]> git.cworth.org Git - apitrace/blob - specs/scripts/cdecl.py
dxgi: Support tracng DWM process.
[apitrace] / specs / scripts / cdecl.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2011 Jose Fonseca
5 # All Rights Reserved.
6 #
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:
13 #
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
16 #
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
23 # THE SOFTWARE.
24 #
25 ##########################################################################/
26
27
28 '''Script to parse C declarations and spew API definitions.
29 '''
30
31
32 import sys
33 import re
34 import optparse
35
36
37 class DeclParser:
38
39     token_re = re.compile(r'(\d[x0-9a-fA-F.UL]*|\w+|\s+|"[^"]*"|.)')
40
41     multi_comment_re = re.compile(r'/\*.*?\*/', flags = re.DOTALL)
42     single_comment_re = re.compile(r'//.*',)
43
44     def __init__(self):
45         self.tokens = []
46
47     def has_side_effects(self, name):
48         return True
49
50
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)]
56
57     def filter_token(self, token):
58         if not token or token.isspace():
59             return False
60         if token.startswith('AVAILABLE_') or token.startswith('DEPRECATED_'):
61             return False
62         if token in ['FAR']:
63             return False
64         return True
65
66     def lookahead(self, index = 0):
67         try:
68             return self.tokens[index]
69         except KeyError:
70             return None
71
72     def match(self, *ref_tokens):
73         return self.lookahead() in ref_tokens
74
75     def consume(self, *ref_tokens):
76         if not self.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)
81         return token
82
83     def eof(self):
84         return not self.tokens
85
86
87     def parse(self, s):
88         self.tokenize(s)
89
90         while not self.eof():
91             #print self.tokens[0:10]
92             self.parse_declaration()
93
94     def parse_declaration(self):
95         self.parse_tags()
96         if self.match('#'):
97             self.parse_define()
98         elif self.match('enum'):
99             self.parse_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'):
105             self.parse_struct()
106         elif self.match('value'):
107             self.parse_value('value', 'FakeEnum')
108         elif self.match('typedef'):
109             self.parse_typedef()
110         else:
111             self.parse_prototype()
112         if not self.eof() and self.match(';'):
113             self.consume(';')
114
115     def parse_typedef(self):
116         self.consume('typedef')
117         if self.lookahead(2) in (';', ','):
118             base_type = self.consume()
119             while True:
120                 type = base_type
121                 if self.match('*'):
122                     self.consume()
123                     type = 'Pointer(%s)' % type
124                 name = self.consume()
125                 print '%s = Alias("%s", %s)' % (name, name, type)
126                 if self.match(','):
127                     self.consume()
128                 else:
129                     break
130         else:
131             self.parse_declaration()
132             self.consume()
133
134     def parse_enum(self):
135         self.consume('enum')
136         name = self.consume()
137         self.consume('{')
138
139         print '%s = Enum("%s", [' % (name, name)
140
141         #value = 0
142         while self.lookahead() != '}':
143             name = self.consume()
144             if self.match('='):
145                 self.consume('=')
146                 value = self.consume()
147             if self.match(','):
148                 self.consume(',')
149             tags = self.parse_tags()
150             #print '    "%s",\t# %s' % (name, value) 
151             print '    "%s",' % (name,) 
152             #value += 1
153         self.consume('}')
154
155         print '])'
156         print
157
158     def parse_value(self, ref_token, constructor):
159         self.consume(ref_token)
160         type = self.consume()
161         name = self.consume()
162         self.consume('{')
163
164         print '%s = %s(%s, [' % (name, constructor, type)
165
166         while self.lookahead() != '}':
167             name, value = self.parse_define()
168         self.consume('}')
169
170         print '])'
171         print
172
173     def parse_define(self):
174         self.consume('#')
175         self.consume('define')
176         name = self.consume()
177         value = self.consume()
178         #print '    "%s",\t# %s' % (name, value) 
179         print '    "%s",' % (name,) 
180         return name, value
181
182     def parse_struct(self):
183         self.consume('struct')
184         name = self.consume()
185
186         print '%s = Struct("%s", [' % (name, name)
187         for type, name in self.parse_members():
188             print '    (%s, "%s"),' % (type, name)
189         print '])'
190         print
191
192     def parse_union(self):
193         self.consume('union')
194         if not self.match('{'):
195             name = self.consume()
196         else:
197             name = None
198         members = self.parse_members()
199         return 'Union("%s", [%s])' % (name, ', '.join('%s, "%s"' % member for member in members))
200
201     def parse_members(self):
202         members = []
203         self.consume('{')
204         while self.lookahead() != '}':
205             type, name = self.parse_named_type()
206
207             if self.match(':'):
208                 self.consume()
209                 self.consume()
210
211             if self.match(','):
212                 self.consume(',')
213             self.consume(';')
214             members.append((type, name))
215         self.consume('}')
216         return members
217
218     def parse_interface(self, ref_token):
219         self.consume(ref_token)
220         name = self.consume()
221         if self.match(';'):
222             return
223         self.consume(':')
224         if self.lookahead() in ('public', 'protected'):
225             self.consume()
226         base = self.consume()
227         self.consume('{')
228
229         print '%s = Interface("%s", %s)' % (name, name, base)
230         print '%s.methods += [' % (name,)
231
232         while self.lookahead() != '}':
233             if self.lookahead() in ('public', 'private'):
234                 self.consume()
235                 self.consume(':')
236             else:
237                 self.parse_prototype('Method')
238                 self.consume(';')
239         self.consume('}')
240
241         print ']'
242         print
243
244     def parse_prototype(self, creator = 'Function'):
245         if self.match('extern', 'virtual'):
246             self.consume()
247
248         ret = self.parse_type()
249
250         if self.match('__stdcall', 'WINAPI', 'STDMETHODCALLTYPE'):
251             self.consume()
252             creator = 'Std' + creator
253
254         name = self.consume()
255         extra = ''
256         if not self.has_side_effects(name):
257             extra += ', sideeffects=False'
258         name = name
259
260         self.consume('(')
261         args = []
262         if self.match('void') and self.tokens[1] == ')':
263             self.consume()
264         while self.lookahead() != ')':
265             arg = self.parse_arg()
266             args.append(arg)
267             if self.match(','):
268                 self.consume()
269         self.consume(')')
270         if self.match('const', 'CONST'):
271             self.consume()
272             extra = ', const=True' + extra
273
274         if self.lookahead() == '=':
275             self.consume()
276             self.consume('0')
277         
278         print '    %s(%s, "%s", [%s]%s),' % (creator, ret, name, ', '.join(args), extra)
279
280     def parse_arg(self):
281         tags = self.parse_tags()
282
283         type, name = self.parse_named_type()
284
285         arg = '(%s, "%s")' % (type, name)
286         if 'out' in tags or 'inout' in tags:
287             arg = 'Out' + arg
288
289         if self.match('='):
290             self.consume()
291             while not self.match(',', ')'):
292                 self.consume()
293
294         return arg
295
296     def parse_tags(self):
297         tags = []
298         if self.match('['):
299             self.consume()
300             while not self.match(']'):
301                 tag = self.consume()
302                 tags.append(tag)
303             self.consume(']')
304             if tags[0] == 'annotation':
305                 assert tags[1] == '('
306                 assert tags[3] == ')'
307                 tags = tags[2]
308                 assert tags[0] == '"'
309                 assert tags[-1] == '"'
310                 tags = tags[1:-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
315             tag = self.consume()
316             if self.match('('):
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))
322         return tags
323
324     def parse_sal_annotation(self, tags):
325         try:
326             tags, args = tags.split('(')
327         except ValueError:
328             pass
329         assert tags[0] == '_'
330         if tags[1] == '_':
331             tags = tags[2:]
332         if tags[-1] == '_':
333             tags = tags[1:-1]
334         tags = tags.lower()
335         tags = tags.split('_')
336         return tags
337
338     def parse_named_type(self):
339         type = self.parse_type()
340         
341         if self.match(',', ';', '}', ')'):
342             name = None
343         else:
344             name = self.consume()
345             if self.match('['):
346                 self.consume()
347                 length = ''
348                 while not self.match(']'):
349                     length += self.consume()
350                 self.consume(']')
351                 try:
352                     int(length)
353                 except ValueError:
354                     length = '"%s"' % length
355                 type = 'Array(%s, %s)' % (type, length)
356         return type, name
357
358     int_tokens = ('unsigned', 'signed', 'int', 'long', 'short', 'char')
359
360     type_table = {
361         'float':    'Float',
362         'double':   'Double',
363         'int8_t':   'Int8',
364         'uint8_t':  'UInt8',
365         'int16_t':  'Int16',
366         'uint16_t': 'UInt16',
367         'int32_t':  'Int32',
368         'uint32_t': 'UInt32',
369         'int64_t' : 'Int64',
370         'uint64_t': 'UInt64',
371     }
372
373     def parse_type(self):
374         const = False
375         if self.match('const', 'CONST'):
376             self.consume()
377             const = True
378         if self.match('void'):
379             self.consume()
380             type = 'Void'
381         elif self.match('union'):
382             type = self.parse_union()
383         elif self.match(*self.int_tokens):
384             unsigned = False
385             signed = False
386             long = 0
387             short = 0
388             char = False
389             while self.match(*self.int_tokens):
390                 token = self.consume()
391                 if token == 'unsigned':
392                     unsigned = True
393                 if token == 'signed':
394                     signed = True
395                 if token == 'long':
396                     long += 1
397                 if token == 'short':
398                     short += 1
399                 if token == 'char':
400                     char = False
401             if char:
402                 type = 'Char'
403                 if signed:
404                     type = 'S' + type
405             elif short:
406                 type = 'Short'
407             elif long:
408                 type = 'Long' * long
409             else:
410                 type = 'Int'
411             if unsigned:
412                 type = 'U' + type
413         else:
414             token = self.consume()
415             type = self.type_table.get(token, token)
416         if const:
417             type = 'Const(%s)' % type
418         while True:
419             if self.match('*'):
420                 self.consume('*')
421                 type = 'Pointer(%s)' % type
422             elif self.match('const', 'CONST'):
423                 self.consume()
424                 type = 'Const(%s)' % type
425             else:
426                 break
427         return type
428
429
430 def main():
431     args = sys.argv[1:]
432
433     parser = DeclParser()
434     if args:
435         for arg in args:
436             parser.parse(open(arg, 'rt').read())
437     else:
438         parser.parse(sys.stdin.read())
439     
440
441 if __name__ == '__main__':
442     main()