blob: 98b1014f3b9aaffaf7406ab1f8830040419a1d24 [file] [log] [blame]
Jack Jansen5ccd8261995-07-17 11:43:20 +00001"""
2gensuitemodule - Generate an AE suite module from an aete/aeut resource
3
4Based on aete.py
5"""
6
7import MacOS
8import os
9import string
10import sys
11import types
12import StringIO
13import macfs
14
15from Res import *
16
17def main():
Jack Jansen9d19a911995-08-14 12:14:55 +000018 fss, ok = macfs.PromptGetFile('Select file with aeut/aete resource:')
Jack Jansen5ccd8261995-07-17 11:43:20 +000019 if not ok:
20 sys.exit(0)
21 process(fss.as_pathname())
22
23def process(fullname):
24 """Process all resources in a single file"""
25 cur = CurResFile()
26 print fullname
27 rf = OpenRFPerm(fullname, 0, 1)
28 try:
29 UseResFile(rf)
30 resources = []
31 for i in range(Count1Resources('aete')):
32 res = Get1IndResource('aete', 1+i)
33 resources.append(res)
34 for i in range(Count1Resources('aeut')):
35 res = Get1IndResource('aeut', 1+i)
36 resources.append(res)
37 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
38 for res in resources:
39 print "decoding", res.GetResInfo(), "..."
40 data = res.data
41 aete = decode(data)
42 compileaete(aete, fullname)
43 finally:
44 if rf <> cur:
45 CloseResFile(rf)
46 UseResFile(cur)
47
48def decode(data):
49 """Decode a resource into a python data structure"""
50 f = StringIO.StringIO(data)
51 aete = generic(getaete, f)
52 aete = simplify(aete)
53 processed = f.tell()
54 unprocessed = len(f.read())
55 total = f.tell()
56 if unprocessed:
57 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
58 (processed, unprocessed, total))
59 return aete
60
61def simplify(item):
62 """Recursively replace singleton tuples by their constituent item"""
63 if type(item) is types.ListType:
64 return map(simplify, item)
65 elif type(item) == types.TupleType and len(item) == 2:
66 return simplify(item[1])
67 else:
68 return item
69
70
71# Here follows the aete resource decoder.
72# It is presented bottom-up instead of top-down because there are direct
73# references to the lower-level part-decoders from the high-level part-decoders.
74
75def getbyte(f, *args):
76 c = f.read(1)
77 if not c:
78 raise EOFError, 'in getbyte' + str(args)
79 return ord(c)
80
81def getword(f, *args):
82 getalign(f)
83 s = f.read(2)
84 if len(s) < 2:
85 raise EOFError, 'in getword' + str(args)
86 return (ord(s[0])<<8) | ord(s[1])
87
88def getlong(f, *args):
89 getalign(f)
90 s = f.read(4)
91 if len(s) < 4:
92 raise EOFError, 'in getlong' + str(args)
93 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
94
95def getostype(f, *args):
96 getalign(f)
97 s = f.read(4)
98 if len(s) < 4:
99 raise EOFError, 'in getostype' + str(args)
100 return s
101
102def getpstr(f, *args):
103 c = f.read(1)
104 if len(c) < 1:
105 raise EOFError, 'in getpstr[1]' + str(args)
106 nbytes = ord(c)
107 if nbytes == 0: return ''
108 s = f.read(nbytes)
109 if len(s) < nbytes:
110 raise EOFError, 'in getpstr[2]' + str(args)
111 return s
112
113def getalign(f):
114 if f.tell() & 1:
115 c = f.read(1)
116 ##if c <> '\0':
117 ## print 'align:', `c`
118
119def getlist(f, description, getitem):
120 count = getword(f)
121 list = []
122 for i in range(count):
123 list.append(generic(getitem, f))
124 getalign(f)
125 return list
126
127def alt_generic(what, f, *args):
128 print "generic", `what`, args
129 res = vageneric(what, f, args)
130 print '->', `res`
131 return res
132
133def generic(what, f, *args):
134 if type(what) == types.FunctionType:
135 return apply(what, (f,) + args)
136 if type(what) == types.ListType:
137 record = []
138 for thing in what:
139 item = apply(generic, thing[:1] + (f,) + thing[1:])
140 record.append((thing[1], item))
141 return record
142 return "BAD GENERIC ARGS: %s" % `what`
143
144getdata = [
145 (getostype, "type"),
146 (getpstr, "description"),
147 (getword, "flags")
148 ]
149getargument = [
150 (getpstr, "name"),
151 (getostype, "keyword"),
152 (getdata, "what")
153 ]
154getevent = [
155 (getpstr, "name"),
156 (getpstr, "description"),
157 (getostype, "suite code"),
158 (getostype, "event code"),
159 (getdata, "returns"),
160 (getdata, "accepts"),
161 (getlist, "optional arguments", getargument)
162 ]
163getproperty = [
164 (getpstr, "name"),
165 (getostype, "code"),
166 (getdata, "what")
167 ]
168getelement = [
169 (getostype, "type"),
170 (getlist, "keyform", getostype)
171 ]
172getclass = [
173 (getpstr, "name"),
174 (getostype, "class code"),
175 (getpstr, "description"),
176 (getlist, "properties", getproperty),
177 (getlist, "elements", getelement)
178 ]
179getcomparison = [
180 (getpstr, "operator name"),
181 (getostype, "operator ID"),
182 (getpstr, "operator comment"),
183 ]
184getenumerator = [
185 (getpstr, "enumerator name"),
186 (getostype, "enumerator ID"),
187 (getpstr, "enumerator comment")
188 ]
189getenumeration = [
190 (getostype, "enumeration ID"),
191 (getlist, "enumerator", getenumerator)
192 ]
193getsuite = [
194 (getpstr, "suite name"),
195 (getpstr, "suite description"),
196 (getostype, "suite ID"),
197 (getword, "suite level"),
198 (getword, "suite version"),
199 (getlist, "events", getevent),
200 (getlist, "classes", getclass),
201 (getlist, "comparisons", getcomparison),
202 (getlist, "enumerations", getenumeration)
203 ]
204getaete = [
205 (getword, "major/minor version in BCD"),
206 (getword, "language code"),
207 (getword, "script code"),
208 (getlist, "suites", getsuite)
209 ]
210
211def compileaete(aete, fname):
212 """Generate code for a full aete resource. fname passed for doc purposes"""
213 [version, language, script, suites] = aete
214 major, minor = divmod(version, 256)
215 for suite in suites:
216 compilesuite(suite, major, minor, language, script, fname)
217
218def compilesuite(suite, major, minor, language, script, fname):
219 """Generate code for a single suite"""
220 [name, desc, code, level, version, events, classes, comps, enums] = suite
221
222 modname = identify(name)
223 fss, ok = macfs.StandardPutFile('Python output file', modname+'.py')
224 if not ok:
225 return
226 fp = open(fss.as_pathname(), 'w')
Jack Jansen18a99f51996-03-18 13:35:00 +0000227 fss.SetCreatorType('Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000228
229 fp.write('"""Suite %s: %s\n' % (name, desc))
230 fp.write("Level %d, version %d\n\n" % (level, version))
231 fp.write("Generated from %s\n"%fname)
232 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
233 (major, minor, language, script))
234 fp.write('"""\n\n')
235 # XXXX Temp?
236 fp.write("import addpack\n")
237 fp.write("addpack.addpack('Tools')\n")
238 fp.write("addpack.addpack('bgen')\n")
239 fp.write("addpack.addpack('ae')\n\n")
240
241 fp.write('import aetools\n')
242 fp.write('import MacOS\n\n')
243 fp.write("_code = %s\n\n"% `code`)
244
245 enum_names = []
246 for enum in enums:
247 name = compileenumeration(fp, enum)
248 enum_names.append(enum)
249
250 compileclassheader(fp, modname)
251 if events:
252 for event in events:
253 compileevent(fp, event)
254 else:
255 fp.write("\tpass\n\n")
256 for cls in classes:
257 compileclass(fp, cls)
258 for comp in comps:
259 compilecomparison(fp, comp)
260
261def compileclassheader(fp, name):
262 """Generate class boilerplate"""
263 fp.write("class %s:\n\n"%name)
264
265def compileevent(fp, event):
266 """Generate code for a single event"""
267 [name, desc, code, subcode, returns, accepts, arguments] = event
268 funcname = identify(name)
269 #
270 # generate name->keyword map
271 #
272 if arguments:
273 fp.write("\t_argmap_%s = {\n"%funcname)
274 for a in arguments:
275 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
276 fp.write("\t}\n\n")
277
278 #
279 # Generate function header
280 #
281 has_arg = (not is_null(accepts))
282 opt_arg = (has_arg and is_optional(accepts))
283
Jack Jansen84264771995-10-03 14:35:58 +0000284 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000285 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000286 if not opt_arg:
287 fp.write("_object, ") # Include direct object, if it has one
288 else:
289 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000290 else:
Jack Jansen84264771995-10-03 14:35:58 +0000291 fp.write("_no_object=None, ") # For argument checking
292 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000293 #
294 # Generate doc string (important, since it may be the only
295 # available documentation, due to our name-remaping)
296 #
297 fp.write('\t\t"""%s: %s\n'%(name, desc))
298 if has_arg:
299 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
300 elif opt_arg:
301 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
302 for arg in arguments:
303 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
304 getdatadoc(arg[2])))
305 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
306 if not is_null(returns):
307 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
308 fp.write('\t\t"""\n')
309 #
310 # Fiddle the args so everything ends up in 'arguments' dictionary
311 #
312 fp.write("\t\t_code = %s\n"% `code`)
313 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000314 #
315 # Do keyword name substitution
316 #
317 if arguments:
318 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
319 else:
320 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
321 #
322 # Stuff required arg (if there is one) into arguments
323 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000324 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000325 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000326 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000327 fp.write("\t\tif _object:\n")
328 fp.write("\t\t\t_arguments['----'] = _object\n")
329 else:
330 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000331 fp.write("\n")
332 #
Jack Jansen73215141995-10-09 23:09:23 +0000333 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000334 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000335 for a in arguments:
336 if is_enum(a[2]):
337 kname = a[1]
338 ename = a[2][0]
Jack Jansen84264771995-10-03 14:35:58 +0000339 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen5ccd8261995-07-17 11:43:20 +0000340 (`kname`, ename))
341 fp.write("\n")
342 #
343 # Do the transaction
344 #
Jack Jansen84264771995-10-03 14:35:58 +0000345 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
346 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000347 #
348 # Error handling
349 #
Jack Jansen84264771995-10-03 14:35:58 +0000350 fp.write("\t\tif _arguments.has_key('errn'):\n")
351 fp.write("\t\t\traise MacOS.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000352 fp.write("\t\t# XXXX Optionally decode result\n")
353 #
354 # Decode result
355 #
Jack Jansen84264771995-10-03 14:35:58 +0000356 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000357 if is_enum(returns):
358 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000359 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000360 fp.write("\n")
361
362# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
363# print "# returns", compiledata(returns)
364# print "# accepts", compiledata(accepts)
365# for arg in arguments:
366# compileargument(arg)
367
368def compileargument(arg):
369 [name, keyword, what] = arg
370 print "# %s (%s)" % (name, `keyword`), compiledata(what)
371
372def compileclass(fp, cls):
373 [name, code, desc, properties, elements] = cls
374 fp.write("\n# Class %s (%s) -- %s\n" % (`name`, `code`, `desc`))
375 for prop in properties:
376 compileproperty(fp, prop)
377 for elem in elements:
378 compileelement(fp, elem)
379
380def compileproperty(fp, prop):
381 [name, code, what] = prop
382 fp.write("# property %s (%s) %s\n" % (`name`, `code`, compiledata(what)))
383
384def compileelement(fp, elem):
385 [code, keyform] = elem
386 fp.write("# element %s as %s\n" % (`code`, keyform))
387
388def compilecomparison(fp, comp):
389 [name, code, comment] = comp
390 fp.write("# comparison %s (%s) -- %s\n" % (`name`, `code`, comment))
391
392def compileenumeration(fp, enum):
393 [code, items] = enum
394 fp.write("_Enum_%s = {\n" % identify(code))
395 for item in items:
396 compileenumerator(fp, item)
397 fp.write("}\n\n")
398 return code
399
400def compileenumerator(fp, item):
401 [name, code, desc] = item
402 fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc))
403
404def compiledata(data):
405 [type, description, flags] = data
406 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
407
408def is_null(data):
409 return data[0] == 'null'
410
411def is_optional(data):
412 return (data[2] & 0x8000)
413
414def is_enum(data):
415 return (data[2] & 0x2000)
416
417def getdatadoc(data):
418 [type, descr, flags] = data
419 if descr:
420 return descr
421 if type == '****':
422 return 'anything'
423 if type == 'obj ':
424 return 'an AE object reference'
425 return "undocumented, typecode %s"%`type`
426
427dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
428def compiledataflags(flags):
429 bits = []
430 for i in range(16):
431 if flags & (1<<i):
432 if i in dataflagdict.keys():
433 bits.append(dataflagdict[i])
434 else:
435 bits.append(`i`)
436 return '[%s]' % string.join(bits)
437
438# XXXX Do we have a set of python keywords somewhere?
439illegal_ids = [ "for", "in", "from", "and", "or", "not", "print" ]
440
441def identify(str):
442 """Turn any string into an identifier:
443 - replace space by _
444 - replace other illegal chars by _xx_ (hex code)
445 - prepend _ if the result is a python keyword
446 """
447 rv = ''
448 ok = string.letters + '_'
449 ok2 = ok + string.digits
450 for c in str:
451 if c in ok:
452 rv = rv + c
453 elif c == ' ':
454 rv = rv + '_'
455 else:
456 rv = rv + '_%02.2x_'%ord(c)
457 ok = ok2
458 if rv in illegal_ids:
459 rv = '_' + rv
460 return rv
461
462# Call the main program
463
464if __name__ == '__main__':
465 main()
466 sys.exit(1)