]> git.cworth.org Git - apitrace/blob - helpers/spec.py
401c317b064a3825a1dce2ffbb39507af73e9aa9
[apitrace] / helpers / spec.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2010 VMware, Inc.
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 import sys
29 import re
30 import optparse
31
32
33 def stderr(x):
34     sys.stderr.write(str(x) + '\n')
35
36
37 class Parser:
38
39     def __init__(self, stream):
40         pass
41
42
43 class LineParser:
44     """Base class for parsers that read line-based formats."""
45
46     def __init__(self, stream):
47         self._stream = stream
48         self._line = None
49         self._eof = False
50         # read lookahead
51         self.readline()
52     
53     def parse(self):
54         raise NotImplementedError
55
56     def readline(self):
57         line = self._stream.readline()
58         if not line:
59             self._line = ''
60             self._eof = True
61         self._line = line.rstrip('\r\n')
62
63     def lookahead(self):
64         assert self._line is not None
65         return self._line
66
67     def consume(self):
68         assert self._line is not None
69         line = self._line
70         self.readline()
71         return line
72
73     def eof(self):
74         assert self._line is not None
75         return self._eof
76     
77     def skip_whitespace(self):
78         while not self.eof() and self.match_whitespace() or self.match_comment():
79             self.consume()
80
81     def match_whitespace(self):
82         line = self.lookahead()
83         return not line.strip()
84
85     def match_comment(self):
86         return False
87
88
89 class TypemapParser(LineParser):
90
91     def parse(self):
92         typemap = {}
93         self.skip_whitespace()
94         while not self.eof():
95             line = self.consume()
96             fields = [field.strip() for field in line.split(',')]
97             src = fields[0]
98             dst = fields[3]
99             if dst != '*':
100                 typemap[src] = dst
101             self.skip_whitespace()
102         return typemap
103     
104     def match_comment(self):
105         line = self.lookahead()
106         return line.startswith('#')
107
108
109 class SpecParser(LineParser):
110
111     property_re = re.compile(r'^\w+:')
112     prototype_re = re.compile(r'^(\w+)\((.*)\)$')
113
114     def __init__(self, stream, prefix='', typemap = None):
115         LineParser.__init__(self, stream)
116         if typemap is None:
117             self.typemap = {}
118         else:
119             self.typemap = typemap
120         self.prefix = prefix
121         self.category = None
122
123     def parse(self):
124         self.skip_whitespace()
125         while not self.eof():
126             line = self.lookahead()
127             if self.property_re.match(line):
128                 self.parse_property()
129             elif self.prototype_re.match(line):
130                 self.parse_prototype()
131             else:
132                 self.consume()
133             self.skip_whitespace()
134
135     def parse_property(self):
136         line = self.consume()
137         name, value = line.split(':', 1)
138         if name == 'category':
139             values = value.split()
140             #self.prefix = values[0]
141
142     get_function_re = re.compile(r'^Get[A-Z]\w+')
143
144     def parse_prototype(self):
145         line = self.consume()
146         mo = self.prototype_re.match(line)
147         function_name, arg_names = mo.groups()
148         arg_names = [arg_name.strip() for arg_name in arg_names.split(',') if arg_name.strip()]
149         
150         extra = ''
151         if self.get_function_re.match(function_name):
152             extra += ', sideeffects=False'
153         function_name = self.prefix + function_name
154
155         ret_type = 'Void'
156         arg_types = {}
157         category = None
158         line = self.lookahead()
159         while line.startswith('\t'):
160             fields = line.split(None, 2)
161             if fields[0] == 'return':
162                 ret_type = self.parse_type(fields[1])
163             elif fields[0] == 'param':
164                 arg_name, arg_type = fields[1:3]
165                 arg_types[fields[1]] = self.parse_arg(function_name, arg_name, arg_type)
166             elif fields[0] == 'category':
167                 category = fields[1]
168             else:
169                 pass
170             self.consume()
171             line = self.lookahead()
172         self.consume()
173         args = [arg_types[arg_name] for arg_name in arg_names]
174
175         if category is not None:
176             if category == self.prefix:
177                 category = self.prefix.upper()
178             else:
179                 category = self.prefix.upper() + '_' + category
180             if category != self.category:
181                 if self.category is not None:
182                     print
183                 print '    # %s' % category
184                 self.category = category
185
186         if self.prefix == 'wgl':
187             constructor = 'StdFunction'
188         else:
189             constructor = 'GlFunction'
190
191         print '    %s(%s, "%s", [%s]%s),' % (constructor, ret_type, function_name, ', '.join(args), extra)
192
193     array_re = re.compile(r'^array\s+\[(.*)\]$')
194
195     string_typemap = {
196         'GLchar': 'GLstring',
197         'GLcharARB': 'GLstringARB',
198     }
199
200     def parse_arg(self, function_name, arg_name, arg_type):
201         orig_type, inout, kind = arg_type.split(' ', 2)
202
203         base_type = self.parse_type(orig_type)
204
205         if kind == 'value':
206             arg_type = base_type
207         elif kind == 'reference':
208             arg_type = 'Pointer(%s)' % base_type
209             if inout == 'in':
210                 arg_type = 'Const(%s)' % arg_type
211         elif kind.startswith("array"):
212             arg_type = 'OpaquePointer(%s)' % base_type
213
214             mo = self.array_re.match(kind)
215             if mo:
216                 length = mo.group(1).strip()
217                 if length == '':
218                     try:
219                         arg_type = self.string_typemap[base_type]
220                     except KeyError:
221                         pass
222                 elif length == '1':
223                     arg_type = 'Pointer(%s)' % base_type
224                 elif length.find("COMPSIZE") == -1:
225                     arg_type = 'Array(%s, "%s")' % (base_type, length)
226                 else:
227                     # XXX: Handle COMPSIZE better
228                     length = length.replace("COMPSIZE", "__%s_size" % function_name)
229                     length = length.replace("/", ",")
230                     arg_type = 'Array(%s, "%s")' % (base_type, length)
231             if inout == 'in':
232                 arg_type = 'Const(%s)' % arg_type
233         else:
234             assert False
235         
236         arg = '(%s, "%s")' % (arg_type, arg_name)
237         if inout == 'out':
238             arg = 'Out' + arg
239         return arg
240
241     semantic_typemap = {
242         'String': 'CString',
243         'Texture': 'GLtexture',
244     }
245
246     post_typemap = {
247         'void': 'Void',
248         'int': 'Int',
249         'float': 'Float',
250     }
251
252     def parse_type(self, type):
253         try:
254             return self.semantic_typemap[type]
255         except KeyError:
256             pass
257         type = self.typemap.get(type, type)
258         type = self.post_typemap.get(type, type)
259         return type
260
261     def match_comment(self):
262         line = self.lookahead()
263         return line.startswith('#')
264
265
266 def main():
267     prefix = sys.argv[1]
268
269     parser = TypemapParser(open(sys.argv[2], 'rt'))
270     typemap = parser.parse()
271
272     for arg in sys.argv[3:]:
273         parser = SpecParser(open(arg, 'rt'), prefix=prefix, typemap=typemap)
274         parser.parse()
275     
276
277 if __name__ == '__main__':
278     main()