]> git.cworth.org Git - apitrace/blob - specs/scripts/gltxt.py
scripts: Make gltxt.py parse EGL specs correctly (issue #134).
[apitrace] / specs / scripts / gltxt.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 """Parser for OpenGL .txt extensions specification."""
29
30
31 import sys
32 import re
33 import optparse
34 from urllib2 import urlopen
35
36
37 def stderr(x):
38     sys.stderr.write(str(x) + '\n')
39
40
41 class Parser:
42
43     def __init__(self, stream):
44         pass
45
46
47 class LineParser:
48     """Base class for parsers that read line-based formats."""
49
50     def __init__(self, stream):
51         self._stream = stream
52         self._line = None
53         self._eof = False
54         # read lookahead
55         self.readline()
56     
57     def parse(self):
58         raise NotImplementedError
59
60     def readline(self):
61         line = self._stream.readline()
62         if not line:
63             self._line = ''
64             self._eof = True
65         self._line = line.rstrip('\r\n')
66
67     def lookahead(self):
68         assert self._line is not None
69         return self._line
70
71     def consume(self):
72         assert self._line is not None
73         line = self._line
74         self.readline()
75         return line
76
77     def eof(self):
78         assert self._line is not None
79         return self._eof
80     
81     def skip_whitespace(self):
82         while not self.eof() and self.match_whitespace() or self.match_comment():
83             self.consume()
84
85     def match_whitespace(self):
86         line = self.lookahead()
87         return not line.strip()
88
89     def match_comment(self):
90         return False
91
92
93 class TxtParser(LineParser):
94
95     section_re = re.compile(r'^([A-Z]\w+)( \w+)*$')
96
97     property_re = re.compile(r'^\w+:')
98     prototype_re = re.compile(r'^(\w+)\((.*)\)$')
99
100     comment_start_re = re.compile(r'^/\*')
101     comment_end_re = re.compile(r'.*\*/$')
102
103     def __init__(self, stream, prefix=''):
104         LineParser.__init__(self, stream)
105         self.prefix = prefix
106
107     def parse(self):
108         while  not self.eof():
109             while not self.eof():
110                 line = self.lookahead()
111                 if self.eof():
112                     return
113                 mo = self.section_re.match(line)
114                 if mo:
115                     break
116                 self.consume()
117             line = self.consume()
118             self.parse_section(line)
119         print
120
121     def parse_section(self, name):
122         if name == 'Name Strings':
123             self.parse_strings()
124         if name == 'New Procedures and Functions':
125             self.parse_procs()
126
127     def parse_strings(self):
128         while not self.eof():
129             line = self.lookahead()
130             if not line.strip():
131                 self.consume()
132                 continue
133             if not line.startswith(' '):
134                 break
135             self.consume()
136             name = line.strip()
137             if name.startswith('EGL_'):
138                 self.prefix = ''
139             print '    # %s' % name
140
141     def skip_c_comments(self):
142         while not self.eof():
143             line = self.lookahead().strip()
144             mo = self.comment_start_re.match(line)
145             if not mo:
146                 return
147             while not self.eof():
148                 self.consume()
149                 mo = self.comment_end_re.match(line)
150                 if mo:
151                     return
152                 line = self.lookahead().strip()
153
154     def parse_procs(self):
155         lines = []
156         while not self.eof():
157             self.skip_c_comments()
158             line = self.lookahead()
159             if not line.strip():
160                 self.consume()
161                 continue
162             if not line[0].isspace():
163                 break
164             self.consume()
165             lines.append(line.strip())
166             if line[-1] in (';', ')'):
167                 prototype = ' '.join(lines)
168                 self.parse_proc(prototype)
169                 lines = []
170
171     token_re = re.compile(r'(\w+|\s+|.)')
172     get_function_re = re.compile(r'^Get[A-Z]\w+')
173
174     def parse_proc(self, prototype):
175         #print prototype
176         tokens = self.token_re.split(prototype)
177         self.tokens = [token for token in tokens if token.strip()]
178         #print self.tokens
179
180         ret = self.parse_type()
181
182         name = self.tokens.pop(0)
183         extra = ''
184         if self.get_function_re.match(name):
185             extra += ', sideeffects=False'
186         name = self.prefix + name
187
188         assert self.tokens.pop(0) == '('
189         args = []
190         while self.tokens[0] != ')':
191             arg = self.parse_arg()
192             args.append(arg)
193             if self.tokens[0] == ',':
194                 self.tokens.pop(0)
195         print '    GlFunction(%s, "%s", [%s]%s),' % (ret, name, ', '.join(args), extra)
196
197     def parse_arg(self):
198         type = self.parse_type()
199         if self.tokens[0] == ')':
200             assert type == 'Void'
201             return ''
202         name = self.tokens.pop(0)
203         if self.tokens[0] == '[':
204             self.tokens.pop(0)
205             n = int(self.tokens.pop(0))
206             assert self.tokens.pop(0) == ']'
207             type = 'Array(%s, %d)' % (type, n)
208         return '(%s, "%s")' % (type, name)
209
210     def parse_type(self):
211         token = self.tokens.pop(0)
212         if token == 'const':
213             return 'Const(%s)' % self.parse_type()
214         if token == 'void':
215             type = 'Void'
216         else:
217             type = self.prefix.upper() + token
218         while self.tokens[0] == '*':
219             type = 'OpaquePointer(%s)' % type
220             self.tokens.pop(0)
221         return type
222
223
224 def main():
225     optparser = optparse.OptionParser(
226         usage="\n\t%prog [options] [URL|TXT] ...")
227     optparser.add_option(
228         '-p', '--prefix', metavar='STRING',
229         type="string", dest="prefix", default='gl',
230         help="function prefix [default: %default]")
231
232     (options, args) = optparser.parse_args(sys.argv[1:])
233
234     for arg in args:
235         if arg.startswith('http://'):
236             stream = urlopen(arg, 'rt')
237         else:
238             stream = open(arg, 'rt')
239         parser = TxtParser(stream, prefix = options.prefix)
240         parser.parse()
241     
242
243 if __name__ == '__main__':
244     main()