]> git.cworth.org Git - apitrace/blob - helpers/spec.py
dd4a5ba95998b61cf3a922972079206f6756fca4
[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         ret_type = 'Void'
151         arg_types = {}
152         category = None
153         line = self.lookahead()
154         while line.startswith('\t'):
155             fields = line.split(None, 2)
156             if fields[0] == 'return':
157                 ret_type = self.parse_type(fields[1])
158             elif fields[0] == 'param':
159                 arg_name, arg_type = fields[1:3]
160                 arg_types[fields[1]] = self.parse_arg(arg_name, arg_type)
161             elif fields[0] == 'category':
162                 category = fields[1]
163             else:
164                 pass
165             self.consume()
166             line = self.lookahead()
167         self.consume()
168         args = [arg_types[arg_name] for arg_name in arg_names]
169
170         if category is not None:
171             if category == self.prefix:
172                 category = self.prefix.upper()
173             else:
174                 category = self.prefix.upper() + '_' + category
175             if category != self.category:
176                 if self.category is not None:
177                     print
178                 print '    # %s' % category
179                 self.category = category
180
181         if self.prefix == 'wgl':
182             constructor = 'StdFunction'
183         else:
184             constructor = 'GlFunction'
185         extra = ''
186         if self.get_function_re.match(function_name):
187             extra += ', sideeffects=False'
188         print '    %s(%s, "%s%s", [%s]%s),' % (constructor, ret_type, self.prefix, function_name, ', '.join(args), extra)
189
190     array_re = re.compile(r'^array\s+\[(.*)\]$')
191
192     string_typemap = {
193         'GLchar': 'GLstring',
194         'GLcharARB': 'GLstringARB',
195     }
196
197     def parse_arg(self, arg_name, arg_type):
198         orig_type, inout, kind = arg_type.split(' ', 2)
199
200         base_type = self.parse_type(orig_type)
201
202         if kind == 'value':
203             arg_type = base_type
204         elif kind == 'reference':
205             arg_type = 'Pointer(%s)' % base_type
206             if inout == 'in':
207                 arg_type = 'Const(%s)' % arg_type
208         elif kind.startswith("array"):
209             arg_type = 'OpaquePointer(%s)' % base_type
210
211             mo = self.array_re.match(kind)
212             if mo:
213                 length = mo.group(1).strip()
214                 if length == '':
215                     try:
216                         arg_type = self.string_typemap[base_type]
217                     except KeyError:
218                         pass
219                 elif length == '1':
220                     arg_type = 'Pointer(%s)' % base_type
221                 elif length.find("COMPSIZE") == -1:
222                     # XXX: Handle COMPSIZE better
223                     arg_type = 'Array(%s, "%s")' % (base_type, length)
224             
225             if inout == 'in':
226                 arg_type = 'Const(%s)' % arg_type
227         else:
228             assert False
229         
230         arg = '(%s, "%s")' % (arg_type, arg_name)
231         if inout == 'out':
232             arg = 'Out' + arg
233         return arg
234
235     semantic_typemap = {
236         'String': 'CString',
237         'Texture': 'GLtexture',
238     }
239
240     post_typemap = {
241         'void': 'Void',
242         'int': 'Int',
243         'float': 'Float',
244     }
245
246     def parse_type(self, type):
247         try:
248             return self.semantic_typemap[type]
249         except KeyError:
250             pass
251         type = self.typemap.get(type, type)
252         type = self.post_typemap.get(type, type)
253         return type
254
255     def match_comment(self):
256         line = self.lookahead()
257         return line.startswith('#')
258
259
260 def main():
261     prefix = sys.argv[1]
262
263     parser = TypemapParser(open(sys.argv[2], 'rt'))
264     typemap = parser.parse()
265
266     for arg in sys.argv[3:]:
267         parser = SpecParser(open(arg, 'rt'), prefix=prefix, typemap=typemap)
268         parser.parse()
269     
270
271 if __name__ == '__main__':
272     main()