blob: cd9472551f2d10fd8b3e5565802d51b77ca8e692 [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')
227 fss.SetCreatorType('PYTH', 'TEXT')
228
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 Jansen5ccd8261995-07-17 11:43:20 +0000314 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000315 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000316 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000317 fp.write("\t\tif _object:\n")
318 fp.write("\t\t\t_arguments['----'] = _object\n")
319 else:
320 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000321 fp.write("\n")
322 #
323 # Do key substitution
324 #
325 if arguments:
Jack Jansen84264771995-10-03 14:35:58 +0000326 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
327 else:
328 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000329 for a in arguments:
330 if is_enum(a[2]):
331 kname = a[1]
332 ename = a[2][0]
Jack Jansen84264771995-10-03 14:35:58 +0000333 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen5ccd8261995-07-17 11:43:20 +0000334 (`kname`, ename))
335 fp.write("\n")
336 #
337 # Do the transaction
338 #
Jack Jansen84264771995-10-03 14:35:58 +0000339 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
340 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000341 #
342 # Error handling
343 #
Jack Jansen84264771995-10-03 14:35:58 +0000344 fp.write("\t\tif _arguments.has_key('errn'):\n")
345 fp.write("\t\t\traise MacOS.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000346 fp.write("\t\t# XXXX Optionally decode result\n")
347 #
348 # Decode result
349 #
Jack Jansen84264771995-10-03 14:35:58 +0000350 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000351 if is_enum(returns):
352 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000353 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000354 fp.write("\n")
355
356# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
357# print "# returns", compiledata(returns)
358# print "# accepts", compiledata(accepts)
359# for arg in arguments:
360# compileargument(arg)
361
362def compileargument(arg):
363 [name, keyword, what] = arg
364 print "# %s (%s)" % (name, `keyword`), compiledata(what)
365
366def compileclass(fp, cls):
367 [name, code, desc, properties, elements] = cls
368 fp.write("\n# Class %s (%s) -- %s\n" % (`name`, `code`, `desc`))
369 for prop in properties:
370 compileproperty(fp, prop)
371 for elem in elements:
372 compileelement(fp, elem)
373
374def compileproperty(fp, prop):
375 [name, code, what] = prop
376 fp.write("# property %s (%s) %s\n" % (`name`, `code`, compiledata(what)))
377
378def compileelement(fp, elem):
379 [code, keyform] = elem
380 fp.write("# element %s as %s\n" % (`code`, keyform))
381
382def compilecomparison(fp, comp):
383 [name, code, comment] = comp
384 fp.write("# comparison %s (%s) -- %s\n" % (`name`, `code`, comment))
385
386def compileenumeration(fp, enum):
387 [code, items] = enum
388 fp.write("_Enum_%s = {\n" % identify(code))
389 for item in items:
390 compileenumerator(fp, item)
391 fp.write("}\n\n")
392 return code
393
394def compileenumerator(fp, item):
395 [name, code, desc] = item
396 fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc))
397
398def compiledata(data):
399 [type, description, flags] = data
400 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
401
402def is_null(data):
403 return data[0] == 'null'
404
405def is_optional(data):
406 return (data[2] & 0x8000)
407
408def is_enum(data):
409 return (data[2] & 0x2000)
410
411def getdatadoc(data):
412 [type, descr, flags] = data
413 if descr:
414 return descr
415 if type == '****':
416 return 'anything'
417 if type == 'obj ':
418 return 'an AE object reference'
419 return "undocumented, typecode %s"%`type`
420
421dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
422def compiledataflags(flags):
423 bits = []
424 for i in range(16):
425 if flags & (1<<i):
426 if i in dataflagdict.keys():
427 bits.append(dataflagdict[i])
428 else:
429 bits.append(`i`)
430 return '[%s]' % string.join(bits)
431
432# XXXX Do we have a set of python keywords somewhere?
433illegal_ids = [ "for", "in", "from", "and", "or", "not", "print" ]
434
435def identify(str):
436 """Turn any string into an identifier:
437 - replace space by _
438 - replace other illegal chars by _xx_ (hex code)
439 - prepend _ if the result is a python keyword
440 """
441 rv = ''
442 ok = string.letters + '_'
443 ok2 = ok + string.digits
444 for c in str:
445 if c in ok:
446 rv = rv + c
447 elif c == ' ':
448 rv = rv + '_'
449 else:
450 rv = rv + '_%02.2x_'%ord(c)
451 ok = ok2
452 if rv in illegal_ids:
453 rv = '_' + rv
454 return rv
455
456# Call the main program
457
458if __name__ == '__main__':
459 main()
460 sys.exit(1)