]> git.cworth.org Git - apitrace/blob - apigen/cdecl.py
More complete D310 API spec.
[apitrace] / apigen / 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 Parser:
38
39     token_re = re.compile(r'(\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('enum'):
97             self.parse_enum()
98         elif self.match('interface'):
99             self.parse_interface()
100         elif self.match('mask'):
101             self.parse_value('mask', 'Flags')
102         elif self.match('struct'):
103             self.parse_struct()
104         elif self.match('value'):
105             self.parse_value('value', 'FakeEnum')
106         elif self.match('typedef'):
107             self.parse_typedef()
108         else:
109             self.parse_prototype()
110         if not self.eof() and self.match(';'):
111             self.consume(';')
112
113     def parse_typedef(self):
114         self.consume('typedef')
115         if self.lookahead(2) in (';', ','):
116             base_type = self.consume()
117             while True:
118                 type = base_type
119                 if self.match('*'):
120                     self.consume()
121                     type = 'Pointer(%s)' % type
122                 name = self.consume()
123                 print '%s = Alias("%s", %s)' % (name, name, type)
124                 if self.match(','):
125                     self.consume()
126                 else:
127                     break
128         else:
129             self.parse_declaration()
130             self.consume()
131
132     def parse_enum(self):
133         self.consume('enum')
134         name = self.consume()
135         self.consume('{')
136
137         print '%s = Enum("%s", [' % (name, name)
138
139         #value = 0
140         while self.lookahead() != '}':
141             name = self.consume()
142             if self.match('='):
143                 self.consume('=')
144                 value = self.consume()
145             if self.match(','):
146                 self.consume(',')
147             tags = self.parse_tags()
148             #print '    "%s",\t# %s' % (name, value) 
149             print '    "%s",' % (name,) 
150             #value += 1
151         self.consume('}')
152
153         print '])'
154         print
155
156     def parse_value(self, ref_token, constructor):
157         self.consume(ref_token)
158         type = self.consume()
159         name = self.consume()
160         self.consume('{')
161
162         print '%s = %s(%s, [' % (name, constructor, type)
163
164         while self.lookahead() != '}':
165             self.consume('#')
166             self.consume('define')
167             name = self.consume()
168             value = self.consume()
169             #print '    "%s",\t# %s' % (name, value) 
170             print '    "%s",' % (name,) 
171         self.consume('}')
172
173         print '])'
174         print
175
176     def parse_struct(self):
177         self.consume('struct')
178         name = self.consume()
179         self.consume('{')
180
181         print '%s = Struct("%s", [' % (name, name)
182
183         value = 0
184         while self.lookahead() != '}':
185             type, name = self.parse_named_type()
186             if self.match(','):
187                 self.consume(',')
188             self.consume(';')
189             print '    (%s, "%s"),' % (type, name) 
190             value += 1
191         self.consume('}')
192
193         print '])'
194         print
195
196     def parse_interface(self):
197         self.consume('interface')
198         name = self.consume()
199         if self.match(';'):
200             return
201         self.consume(':')
202         base = self.consume()
203         self.consume('{')
204
205         print '%s = Interface("%s", %s)' % (name, name, base)
206         print '%s.methods += [' % (name,)
207
208         while self.lookahead() != '}':
209             self.parse_prototype('Method')
210             self.consume(';')
211         self.consume('}')
212
213         print ']'
214         print
215
216     def parse_prototype(self, creator = 'Function'):
217         if self.match('extern'):
218             self.consume()
219
220         ret = self.parse_type()
221
222         if self.match('__stdcall'):
223             self.consume()
224             creator = 'StdFunction'
225
226         name = self.consume()
227         extra = ''
228         if not self.has_side_effects(name):
229             extra += ', sideeffects=False'
230         name = name
231
232         self.consume('(')
233         args = []
234         if self.match('void') and self.tokens[1] == ')':
235             self.consume()
236         while self.lookahead() != ')':
237             arg = self.parse_arg()
238             args.append(arg)
239             if self.match(','):
240                 self.consume()
241         self.consume() == ')'
242         
243         print '    %s(%s, "%s", [%s]%s),' % (creator, ret, name, ', '.join(args), extra)
244
245     def parse_arg(self):
246         tags = self.parse_tags()
247
248         type, name = self.parse_named_type()
249
250         arg = '(%s, "%s")' % (type, name)
251         if 'out' in tags:
252             arg = 'Out' + arg
253         return arg
254
255     def parse_tags(self):
256         tags = []
257         if self.match('['):
258             self.consume()
259             while not self.match(']'):
260                 tag = self.consume()
261                 tags.append(tag)
262             self.consume(']')
263         return tags
264
265     def parse_named_type(self):
266         type = self.parse_type()
267         name = self.consume()
268         if self.match('['):
269             self.consume()
270             length = self.consume()
271             self.consume(']')
272             type = 'Array(%s, "%s")' % (type, length)
273         return type, name
274
275     int_tokens = ('unsigned', 'signed', 'int', 'long', 'short', 'char')
276
277     type_table = {
278         'float':    'Float',
279         'double':   'Double',
280         'int8_t':   'Int8',
281         'uint8_t':  'UInt8',
282         'int16_t':  'Int16',
283         'uint16_t': 'UInt16',
284         'int32_t':  'Int32',
285         'uint32_t': 'UInt32',
286         'int64_t' : 'Int64',
287         'uint64_t': 'UInt64',
288     }
289
290     def parse_type(self):
291         token = self.consume()
292         if token == 'const':
293             return 'Const(%s)' % self.parse_type()
294         if token == 'void':
295             type = 'Void'
296         elif token in self.int_tokens:
297             unsigned = False
298             signed = False
299             long = 0
300             short = 0
301             char = False
302             while token in self.int_tokens:
303                 if token == 'unsigned':
304                     unsigned = True
305                 if token == 'signed':
306                     signed = True
307                 if token == 'long':
308                     long += 1
309                 if token == 'short':
310                     short += 1
311                 if token == 'char':
312                     char = False
313                 if self.lookahead() in self.int_tokens:
314                     token = self.consume()
315                 else:
316                     token = None
317             if char:
318                 type = 'Char'
319                 if signed:
320                     type = 'S' + type
321             elif short:
322                 type = 'Short'
323             elif long:
324                 type = 'Long' * long
325             else:
326                 type = 'Int'
327             if unsigned:
328                 type = 'U' + type
329         else:
330             type = self.type_table.get(token, token)
331         while True:
332             if self.match('*'):
333                 self.consume('*')
334                 type = 'OpaquePointer(%s)' % type
335             elif self.match('const'):
336                 self.consume('const')
337                 type = 'Const(%s)' % type
338             else:
339                 break
340         return type
341
342
343
344         
345
346
347 def main():
348     parser = Parser()
349     for arg in sys.argv[1:]:
350         parser.parse(open(arg, 'rt').read())
351     
352
353 if __name__ == '__main__':
354     main()