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