2 ##########################################################################
4 # Copyright 2011 Jose Fonseca
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the "Software"), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 ##########################################################################/
28 '''Script to parse C declarations and spew API definitions.
39 token_re = re.compile(r'(\d[x0-9a-fA-F.UL]*|\w+|\s+|"[^"]*"|.)')
41 multi_comment_re = re.compile(r'/\*.*?\*/', flags = re.DOTALL)
42 single_comment_re = re.compile(r'//.*',)
47 def has_side_effects(self, name):
51 def tokenize(self, s):
52 s = self.multi_comment_re.sub('', s)
53 s = self.single_comment_re.sub('', s)
54 self.tokens = self.token_re.split(s)
55 self.tokens = [token for token in self.tokens if self.filter_token(token)]
57 def filter_token(self, token):
58 if not token or token.isspace():
60 if token.startswith('AVAILABLE_') or token.startswith('DEPRECATED_'):
66 def lookahead(self, index = 0):
68 return self.tokens[index]
72 def match(self, *ref_tokens):
73 return self.lookahead() in ref_tokens
75 def consume(self, *ref_tokens):
77 raise Exception('unexpected EOF')
78 token = self.tokens.pop(0)
79 if ref_tokens and token not in ref_tokens:
80 raise Exception('token mismatch', token, ref_tokens)
84 return not self.tokens
91 #print self.tokens[0:10]
92 self.parse_declaration()
94 def parse_declaration(self):
98 elif self.match('enum'):
100 elif self.match('class', 'interface'):
101 self.parse_interface(self.lookahead())
102 elif self.match('mask'):
103 self.parse_value('mask', 'Flags')
104 elif self.match('struct'):
106 elif self.match('value'):
107 self.parse_value('value', 'FakeEnum')
108 elif self.match('typedef'):
111 self.parse_prototype()
112 if not self.eof() and self.match(';'):
115 def parse_typedef(self):
116 self.consume('typedef')
117 if self.lookahead(2) in (';', ','):
118 base_type = self.consume()
123 type = 'Pointer(%s)' % type
124 name = self.consume()
125 print '%s = Alias("%s", %s)' % (name, name, type)
131 self.parse_declaration()
134 def parse_enum(self):
136 name = self.consume()
139 print '%s = Enum("%s", [' % (name, name)
142 while self.lookahead() != '}':
143 name = self.consume()
146 value = self.consume()
149 tags = self.parse_tags()
150 #print ' "%s",\t# %s' % (name, value)
151 print ' "%s",' % (name,)
158 def parse_value(self, ref_token, constructor):
159 self.consume(ref_token)
160 type = self.consume()
161 name = self.consume()
164 print '%s = %s(%s, [' % (name, constructor, type)
166 while self.lookahead() != '}':
167 name, value = self.parse_define()
173 def parse_define(self):
175 self.consume('define')
176 name = self.consume()
177 value = self.consume()
178 #print ' "%s",\t# %s' % (name, value)
179 print ' "%s",' % (name,)
182 def parse_struct(self):
183 self.consume('struct')
184 name = self.consume()
186 print '%s = Struct("%s", [' % (name, name)
187 for type, name in self.parse_members():
188 print ' (%s, "%s"),' % (type, name)
192 def parse_union(self):
193 self.consume('union')
194 if not self.match('{'):
195 name = self.consume()
198 members = self.parse_members()
199 return 'Union("%s", [%s])' % (name, ', '.join('%s, "%s"' % member for member in members))
201 def parse_members(self):
204 while self.lookahead() != '}':
205 type, name = self.parse_named_type()
214 members.append((type, name))
218 def parse_interface(self, ref_token):
219 self.consume(ref_token)
220 name = self.consume()
224 if self.lookahead() in ('public', 'protected'):
226 base = self.consume()
229 print '%s = Interface("%s", %s)' % (name, name, base)
230 print '%s.methods += [' % (name,)
232 while self.lookahead() != '}':
233 if self.lookahead() in ('public', 'private'):
237 self.parse_prototype('Method')
244 def parse_prototype(self, creator = 'Function'):
245 if self.match('extern', 'virtual'):
248 ret = self.parse_type()
250 if self.match('__stdcall', 'WINAPI'):
252 creator = 'Std' + creator
254 name = self.consume()
256 if not self.has_side_effects(name):
257 extra += ', sideeffects=False'
262 if self.match('void') and self.tokens[1] == ')':
264 while self.lookahead() != ')':
265 arg = self.parse_arg()
270 if self.match('const', 'CONST'):
272 extra = ', const=True' + extra
274 if self.lookahead() == '=':
278 print ' %s(%s, "%s", [%s]%s),' % (creator, ret, name, ', '.join(args), extra)
281 tags = self.parse_tags()
283 type, name = self.parse_named_type()
285 arg = '(%s, "%s")' % (type, name)
286 if 'out' in tags or 'inout' in tags:
291 while not self.match(',', ')'):
296 def parse_tags(self):
300 while not self.match(']'):
304 if tags[0] == 'annotation':
305 assert tags[1] == '('
306 assert tags[3] == ')'
308 assert tags[0] == '"'
309 assert tags[-1] == '"'
311 tags = parse_sal_annotation(tags)
312 token = self.lookahead()
313 if token[0] == '_' and (token[1] == '_' or token[-1] == '_'):
314 # Parse __in, __out, etc tags
317 tag += self.consume()
318 while not self.match(')'):
319 tag += self.consume()
320 tag += self.consume(')')
321 tags.extend(self.parse_sal_annotation(tag))
324 def parse_sal_annotation(self, tags):
326 tags, args = tags.split('(')
329 assert tags[0] == '_'
335 tags = tags.split('_')
338 def parse_named_type(self):
339 type = self.parse_type()
341 if self.match(',', ';', '}', ')'):
344 name = self.consume()
348 while not self.match(']'):
349 length += self.consume()
354 length = '"%s"' % length
355 type = 'Array(%s, %s)' % (type, length)
358 int_tokens = ('unsigned', 'signed', 'int', 'long', 'short', 'char')
366 'uint16_t': 'UInt16',
368 'uint32_t': 'UInt32',
370 'uint64_t': 'UInt64',
373 def parse_type(self):
375 if self.match('const', 'CONST'):
378 if self.match('void'):
381 elif self.match('union'):
382 type = self.parse_union()
383 elif self.match(*self.int_tokens):
389 while self.match(*self.int_tokens):
390 token = self.consume()
391 if token == 'unsigned':
393 if token == 'signed':
414 token = self.consume()
415 type = self.type_table.get(token, token)
417 type = 'Const(%s)' % type
421 type = 'Pointer(%s)' % type
422 elif self.match('const', 'CONST'):
424 type = 'Const(%s)' % type
433 parser = DeclParser()
436 parser.parse(open(arg, 'rt').read())
438 parser.parse(sys.stdin.read())
441 if __name__ == '__main__':