]> git.tdb.fi Git - gldbg.git/blob - generate.py
Unify array and reference print generation
[gldbg.git] / generate.py
1 #!/usr/bin/python
2 # $Id$
3
4 import sys
5 import os
6 import optparse
7
8 class InputFile:
9         def __init__(self, fn):
10                 self.file = open(fn)
11
12         def __iter__(self):
13                 for l in self.file:
14                         h = l.find("#")
15                         if h==0 or (h>0 and l[h-1].isspace()):
16                                 l = l[:h]
17
18                         l = l.rstrip()
19                         if not l:
20                                 continue
21
22                         yield l
23                 
24
25 def strip_name(name):
26         """Strips any vendor suffix and GL prefix from a name (but not GLX prefix)"""
27
28         suffix = ""
29         if name.endswith(" *"):
30                 suffix = " *"
31                 name = name[:-2]
32         elif name.endswith("Pointer"):
33                 suffix = "Pointer"
34                 name = name[:-7]
35
36         prefix = ""
37         if name.startswith("const "):
38                 prefix = "const "
39                 name = name[6:]
40
41         if name.startswith("GL") and not name.startswith("GLX"):
42                 name = name[2:]
43         if name.endswith(("EXT", "ARB", "SGI", "IBM", "ATI")):
44                 return prefix+name[:-3]+suffix
45         elif name.endswith("SGIX"):
46                 return prefix+name[:-4]+suffix
47         elif name.endswith(("NV", "HP")):
48                 return prefix+name[:-2]+suffix
49         else:
50                 return prefix+name+suffix
51
52
53 class Typemap:
54         def __init__(self, fn):
55                 self.map = {}
56                 for line in InputFile(fn):
57                         parts = [p.strip() for p in line.split(',')]
58                         if parts[3]=="*":
59                                 parts[3] = parts[0]
60                         elif parts[3][-1]=='*' and parts[3][-2]!=' ':
61                                 parts[3] = parts[3][:-1]+" *"
62                         self.map[tuple(parts[0:3])] = tuple(parts[3:6])
63
64         def wildcard_match(self, a, b):
65                 if a=="*" or b=="*":
66                         return True
67                 return a==b
68
69         def __getitem__(self, key):
70                 try:
71                         return self.map[(key[0], "*", "*")]
72                 except KeyError:
73                         for k, v in self.map.iteritems():
74                                 if strip_name(k[0])==strip_name(key[0]) and self.wildcard_match(k[1], key[1]) and self.wildcard_match(k[2], key[2]):
75                                         return v
76                         raise KeyError, key
77
78         def update(self, other):
79                 self.map.update(other.map)
80
81
82 class IOmap:
83         def __init__(self, fn):
84                 self.map = {}
85                 self.map["void"] = None
86                 for line in InputFile(fn):
87                         parts = [p.strip() for p in line.split(',')]
88                         self.map[parts[0]] = tuple(parts[1:])
89
90         def __getitem__(self, key):
91                 return self.map[strip_name(key)]
92
93
94 class Function:
95         class Parameter:
96                 def __init__(self, func, name):
97                         self.func = func
98                         self.name = name
99                         self.type = None
100                         self.direction = "in"
101                         self.kind = "value"
102                         self.size = None
103                         self.ctype = None
104                         self.csize = None
105                         self.io = None
106
107                 def set_type(self, type, dir, kind):
108                         self.type = type
109                         self.direction = dir
110                         self.kind = kind
111
112                 def set_size(self, size):
113                         if type(size)==str and size.isdigit():
114                                 self.size = int(size)
115                         else:
116                                 self.size = size
117
118                 def derive_ctype(self, typemap, iomap):
119                         m = typemap[(self.type, self.direction, self.kind)]
120                         self.ctype = m[0]
121                         if m[1]!="*":
122                                 self.direction = m[1]
123                         if m[2]!="*":
124                                 self.kind = m[2]
125                         self.base_ctype = self.ctype
126                         if self.kind=="value":
127                                 if self.base_ctype.startswith("const "):
128                                         self.base_ctype = self.base_ctype[6:]
129                         else:
130                                 if self.direction=="in":
131                                         self.ctype = "const "+self.ctype
132                                 self.ctype = self.ctype+" *"
133                         self.io = iomap[self.base_ctype]
134
135                 def derive_csize(self):
136                         if self.kind=="array" and self.size is not None:
137                                 self.csize = None
138                                 if type(self.size)==int:
139                                         self.csize = "%d*sizeof(%s)"%(self.size, self.base_ctype)
140                                 elif self.size.startswith("COMPSIZE("):
141                                         self.csize = self.func.compsize(self.size[9:-1], self.base_ctype)
142                                 elif self.size=="pname":
143                                         self.csize = "paramsize(pname)*sizeof(%s)"%self.base_ctype
144                                 else:
145                                         s = self.func.get_param(self.size.split('*')[0])
146                                         if (s.type=="SizeI" or s.type.endswith("Int32") or s.type.startswith("BufferSize")):
147                                                 if s.kind=="value":
148                                                         self.csize = "%s"%self.size
149                                                         if self.func.name.startswith("glUniformMatrix") and self.func.name[16]!='x':
150                                                                 self.csize += "*%d"%(int(self.func.name[15])**2)
151                                                         elif self.func.name.startswith("glUniform") and self.func.name[9].isdigit():
152                                                                 self.csize += "*%s"%self.func.name[9]
153                                                         if strip_name(self.base_ctype)!="void":
154                                                                 self.csize += "*sizeof(%s)"%self.base_ctype
155                                                 elif s.kind=="array" and s.size==1:
156                                                         self.csize = "*%s"%s.name
157                                         if not self.csize:
158                                                 sys.stderr.write("Could not determine size for array parameter '%s[%s]' of function '%s'\n"%(self.name, self.size, self.func.name))
159                         elif self.kind=="reference":
160                                 self.csize = "sizeof(%s)"%self.base_ctype
161
162         def __init__(self, name, pnames):
163                 self.name = name
164                 self.ret = Function.Parameter(self, "ret")
165                 self.params = [Function.Parameter(self, n) for n in pnames]
166                 self.category = None
167
168         def get_param(self, pname):
169                 for p in self.params:
170                         if p.name==pname:
171                                 return p
172                 raise KeyError, pname
173
174         def set_category(self, cat):
175                 self.category = cat
176
177         def compsize(self, size, btype):
178                 if not size:
179                         return
180
181                 res = ""
182                 have_type = False
183                 for c in size.replace(',', '/').split('/'):
184                         param = self.get_param(c)
185                         if not param:
186                                 sys.stderr.write("Compsize '%s' for function '%s' failed: No parameter '%s'\n"%(size, self.name, c))
187                                 return
188
189                         if res:
190                                 res += "*"
191
192                         cn = strip_name(param.type)
193                         if cn.endswith("Type"):
194                                 res += "typesize(%s)"%param.name
195                                 have_type = True
196                         elif cn.endswith("Format"):
197                                 res += "formatsize(%s)"%param.name
198                         elif param.name=="pname" or cn.endswith("Parameter") or (param.name=="target" and cn=="enum"):
199                                 res += "paramsize(%s)"%param.name
200                         elif param.name=="buffer" and cn=="enum":
201                                 res += "buffersize(%s)"%param.name
202                         elif (cn=="SizeI" or cn.endswith("Int32")) and not param.size:
203                                 res += param.name
204                         else:
205                                 sys.stderr.write("Compsize '%s' for function '%s' failed: Parameter '%s' has unknown type '%s'\n"%(size, self.name, param.name, param.type))
206                                 return
207                 if not have_type:
208                         res += "*sizeof(%s)"%btype
209                 return res
210
211         def finalize(self, typemap, iomap):
212                 self.ret.derive_ctype(typemap, iomap)
213                 for p in self.params:
214                         p.derive_ctype(typemap, iomap)
215                 for p in self.params:
216                         p.derive_csize()
217
218
219 class Enum:
220         def __init__(self, name, category, value):
221                 self.name = name
222                 self.category = category
223                 self.value = value
224
225
226 class Template:
227         def __init__(self, fn):
228                 self.mode = "functions"
229                 self.sections = []
230                 self.handcode = []
231
232                 literal = True
233                 text = ""
234                 for line in InputFile(fn):
235                         if line[0]==':':
236                                 if not literal and text:
237                                         self.add_section(text, literal)
238                                         text = ""
239                                 text += line[1:]+"\n"
240                                 literal = True
241                         elif line[0]=='!':
242                                 parts = line[1:].split()
243                                 if parts[0]=="handcode":
244                                         self.handcode.append(parts[1])
245                                 elif parts[0]=="mode":
246                                         self.mode = parts[1]
247                                 else:
248                                         sys.stderr.write("Unknown keyword '%s'\n"%parts[0])
249                         else:
250                                 if literal and text:
251                                         self.add_section(text, literal)
252                                         text = ""
253                                 text += line+"\n"
254                                 literal = False
255                 if text:
256                         self.add_section(text, literal)
257
258         def add_section(self, text, literal):
259                 if literal:
260                         self.sections.append(text)
261                 else:
262                         self.sections.append(compile(text, "-", "exec"))
263
264         def write(self, str, *args):
265                 sys.stdout.write(str%args)
266
267         def writeln(self, str, *args):
268                 sys.stdout.write(str%args+"\n")
269
270         def process_function(self, code, func):
271                 if func.name in self.handcode:
272                         return
273
274                 globals = {
275                         "w": self.write,
276                         "wl": self.writeln,
277                         "func": func,
278                         "ret": func.ret,
279                         "params": func.params
280                 }
281                 eval(code, globals)
282
283         def process_enum(self, code, enum):
284                 if enum.value is None:
285                         return
286
287                 globals = {
288                         "w": self.write,
289                         "wl": self.writeln,
290                         "enum": enum
291                 }
292                 eval(code, globals)
293
294         def process_sections(self, objects, handler):
295                 for sect in self.sections:
296                         if type(sect)==str:
297                                 print sect
298                         else:
299                                 for obj in objects:
300                                         handler(sect, obj)
301
302         def process(self, apis):
303                 if self.mode=="functions":
304                         functions = []
305                         for api in apis:
306                                 typemap = Typemap(api.typemap)
307                                 iomap = IOmap(api.iomap)
308                                 for spec in api.specs:
309                                         funcs = read_spec(spec, api.prefix)
310                                         funcs = [f for f in funcs if f.name not in api.ignore_funcs and f.category not in api.ignore_categs]
311                                         for func in funcs:
312                                                 func.finalize(typemap, iomap)
313                                         names = [f.name for f in funcs]
314                                         functions = [f for f in functions if f.name not in names]+funcs
315                         self.process_sections(functions, self.process_function)
316                 elif self.mode=="enums":
317                         enums = []
318                         for api in apis:
319                                 for spec in api.enumspecs:
320                                         ens = read_enums(spec, api.enumprefix)
321                                         enums += ens
322                         enums.sort(lambda x, y: cmp(x.value, y.value)*2+cmp(x.category, y.category))
323                         self.process_sections(enums, self.process_enum)
324
325
326 class Api:
327         def __init__(self, fn):
328                 self.typemap = None
329                 self.iomap = None
330                 self.specs = []
331                 self.prefix = None
332                 self.enumspecs = []
333                 self.enumprefix = None
334                 self.ignore_categs = []
335                 self.ignore_funcs = []
336
337                 path = os.path.split(fn)[0]
338
339                 for line in InputFile(fn):
340                         parts = line.split()
341                         if parts[0]=="typemap":
342                                 self.typemap = os.path.join(path, parts[1])
343                         elif parts[0]=="iomap":
344                                 self.iomap = os.path.join(path, parts[1])
345                         elif parts[0]=="spec":
346                                 self.specs.append(os.path.join(path, parts[1]))
347                         elif parts[0]=="prefix":
348                                 self.prefix = parts[1]
349                         elif parts[0]=="enumspec":
350                                 self.enumspecs.append(os.path.join(path, parts[1]))
351                         elif parts[0]=="enumprefix":
352                                 self.enumprefix = parts[1]
353                         elif parts[0]=="ignore":
354                                 if parts[1]=="category":
355                                         self.ignore_categs.append(parts[2])
356                                 elif parts[1]=="function":
357                                         self.ignore_funcs.append(parts[2])
358                         else:
359                                 sys.stderr.write("Unknown keyword '%s'\n", parts[0])
360
361
362 def read_spec(fn, prefix):
363         funcs = []
364         cur_func = None
365
366         for line in InputFile(fn):
367                 if line.find(':')>=0:
368                         continue
369                 elif line[0]=='\t' and cur_func:
370                         parts = line.split()
371                         if parts[0]=="return":
372                                 cur_func.ret.set_type(parts[1], "out", "value")
373                         elif parts[0]=="param":
374                                 bracket = parts[4].find('[')
375                                 if bracket>=0:
376                                         parts.insert(5, parts[4][bracket:])
377                                         parts[4] = parts[4][:bracket]
378
379                                 param = cur_func.get_param(parts[1])
380                                 param.set_type(parts[2], parts[3], parts[4])
381                                 if len(parts)==6 or (len(parts)>6 and parts[6]!="retained"):
382                                         param.set_size(parts[5][1:-1])
383                         elif parts[0]=="category":
384                                 cur_func.set_category(parts[1])
385                         elif parts[0]=="glxvendorglx" and cur_func.category=="glx":
386                                 cur_func.set_category("glxext")
387                 else:
388                         paren = line.find('(')
389                         if paren>0:
390                                 cparen = line.rfind(')')
391                                 if cparen>paren+1:
392                                         pnames = [n.strip() for n in line[paren+1:cparen].split(",")]
393                                 else:
394                                         pnames = []
395                                 cur_func = Function(prefix+line[:paren], pnames)
396                                 funcs.append(cur_func)
397
398         return funcs
399
400 def read_enums(fn, prefix):
401         enums = []
402         cur_categ = []
403
404         for line in InputFile(fn):
405                 if ':' in line:
406                         parts = line[:line.find(':')].split()
407                         if len(parts)==2 and parts[1]=="enum":
408                                 cur_categ = parts[0]
409                 elif cur_categ:
410                         parts = line.split()
411                         if parts[0]=="use":
412                                 enums.append(Enum(prefix+parts[2], cur_categ, None))
413                         elif parts[1]=="=":
414                                 try:
415                                         enums.append(Enum(prefix+parts[0], cur_categ, int(parts[2], 0)))
416                                 except ValueError, e:
417                                         sys.stderr.write("Syntax error in %s: %s\n"%(fn, e))
418
419         for e in enums:
420                 if e.value is None:
421                         for n in enums:
422                                 if n.name==e.name and n.value is not None:
423                                         e.value = n.value
424                         if e.value is None:
425                                 sys.stderr.write("Could not find value for enum reference %s in category %s\n"%(e.name, e.category))
426
427         return enums
428
429 parser = optparse.OptionParser()
430 parser.add_option("--depends", dest="depends", default=False)
431 (options, args) = parser.parse_args()
432
433 template = Template(args[0])
434 apis = []
435 for i in args[1:]:
436         apis.append(Api(i))
437
438 if options.depends:
439         deps = args[:]
440         if template.mode=="functions":
441                 for api in apis:
442                         deps += api.specs
443                         deps.append(api.typemap)
444                         deps.append(api.iomap)
445         elif template.mode=="enums":
446                 for api in apis:
447                         deps += api.enumspecs
448         sys.stdout.write("%s: %s\n"%(options.depends, " ".join(deps)))
449 else:
450         template.process(apis)