blob: ff818fc585cf2db61f590e338ae380b3bfc0a974 [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')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000235
236 fp.write('import aetools\n')
237 fp.write('import MacOS\n\n')
238 fp.write("_code = %s\n\n"% `code`)
239
240 enum_names = []
241 for enum in enums:
242 name = compileenumeration(fp, enum)
243 enum_names.append(enum)
244
245 compileclassheader(fp, modname)
246 if events:
247 for event in events:
248 compileevent(fp, event)
249 else:
250 fp.write("\tpass\n\n")
251 for cls in classes:
252 compileclass(fp, cls)
253 for comp in comps:
254 compilecomparison(fp, comp)
255
256def compileclassheader(fp, name):
257 """Generate class boilerplate"""
258 fp.write("class %s:\n\n"%name)
259
260def compileevent(fp, event):
261 """Generate code for a single event"""
262 [name, desc, code, subcode, returns, accepts, arguments] = event
263 funcname = identify(name)
264 #
265 # generate name->keyword map
266 #
267 if arguments:
268 fp.write("\t_argmap_%s = {\n"%funcname)
269 for a in arguments:
270 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
271 fp.write("\t}\n\n")
272
273 #
274 # Generate function header
275 #
276 has_arg = (not is_null(accepts))
277 opt_arg = (has_arg and is_optional(accepts))
278
Jack Jansen84264771995-10-03 14:35:58 +0000279 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000280 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000281 if not opt_arg:
282 fp.write("_object, ") # Include direct object, if it has one
283 else:
284 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000285 else:
Jack Jansen84264771995-10-03 14:35:58 +0000286 fp.write("_no_object=None, ") # For argument checking
287 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000288 #
289 # Generate doc string (important, since it may be the only
290 # available documentation, due to our name-remaping)
291 #
292 fp.write('\t\t"""%s: %s\n'%(name, desc))
293 if has_arg:
294 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
295 elif opt_arg:
296 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
297 for arg in arguments:
298 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
299 getdatadoc(arg[2])))
300 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
301 if not is_null(returns):
302 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
303 fp.write('\t\t"""\n')
304 #
305 # Fiddle the args so everything ends up in 'arguments' dictionary
306 #
307 fp.write("\t\t_code = %s\n"% `code`)
308 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000309 #
310 # Do keyword name substitution
311 #
312 if arguments:
313 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
314 else:
315 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
316 #
317 # Stuff required arg (if there is one) into arguments
318 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000319 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000320 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000321 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000322 fp.write("\t\tif _object:\n")
323 fp.write("\t\t\t_arguments['----'] = _object\n")
324 else:
325 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000326 fp.write("\n")
327 #
Jack Jansen73215141995-10-09 23:09:23 +0000328 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000329 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000330 for a in arguments:
331 if is_enum(a[2]):
332 kname = a[1]
333 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000334 if ename <> '****':
335 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen5ccd8261995-07-17 11:43:20 +0000336 (`kname`, ename))
337 fp.write("\n")
338 #
339 # Do the transaction
340 #
Jack Jansen84264771995-10-03 14:35:58 +0000341 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
342 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000343 #
344 # Error handling
345 #
Jack Jansen84264771995-10-03 14:35:58 +0000346 fp.write("\t\tif _arguments.has_key('errn'):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000347 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000348 fp.write("\t\t# XXXX Optionally decode result\n")
349 #
350 # Decode result
351 #
Jack Jansen84264771995-10-03 14:35:58 +0000352 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000353 if is_enum(returns):
354 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000355 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000356 fp.write("\n")
357
358# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
359# print "# returns", compiledata(returns)
360# print "# accepts", compiledata(accepts)
361# for arg in arguments:
362# compileargument(arg)
363
364def compileargument(arg):
365 [name, keyword, what] = arg
366 print "# %s (%s)" % (name, `keyword`), compiledata(what)
367
368def compileclass(fp, cls):
369 [name, code, desc, properties, elements] = cls
370 fp.write("\n# Class %s (%s) -- %s\n" % (`name`, `code`, `desc`))
371 for prop in properties:
372 compileproperty(fp, prop)
373 for elem in elements:
374 compileelement(fp, elem)
375
376def compileproperty(fp, prop):
377 [name, code, what] = prop
378 fp.write("# property %s (%s) %s\n" % (`name`, `code`, compiledata(what)))
379
380def compileelement(fp, elem):
381 [code, keyform] = elem
382 fp.write("# element %s as %s\n" % (`code`, keyform))
383
384def compilecomparison(fp, comp):
385 [name, code, comment] = comp
386 fp.write("# comparison %s (%s) -- %s\n" % (`name`, `code`, comment))
387
388def compileenumeration(fp, enum):
389 [code, items] = enum
390 fp.write("_Enum_%s = {\n" % identify(code))
391 for item in items:
392 compileenumerator(fp, item)
393 fp.write("}\n\n")
394 return code
395
396def compileenumerator(fp, item):
397 [name, code, desc] = item
398 fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc))
399
400def compiledata(data):
401 [type, description, flags] = data
402 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
403
404def is_null(data):
405 return data[0] == 'null'
406
407def is_optional(data):
408 return (data[2] & 0x8000)
409
410def is_enum(data):
411 return (data[2] & 0x2000)
412
413def getdatadoc(data):
414 [type, descr, flags] = data
415 if descr:
416 return descr
417 if type == '****':
418 return 'anything'
419 if type == 'obj ':
420 return 'an AE object reference'
421 return "undocumented, typecode %s"%`type`
422
423dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
424def compiledataflags(flags):
425 bits = []
426 for i in range(16):
427 if flags & (1<<i):
428 if i in dataflagdict.keys():
429 bits.append(dataflagdict[i])
430 else:
431 bits.append(`i`)
432 return '[%s]' % string.join(bits)
433
434# XXXX Do we have a set of python keywords somewhere?
435illegal_ids = [ "for", "in", "from", "and", "or", "not", "print" ]
436
437def identify(str):
438 """Turn any string into an identifier:
439 - replace space by _
440 - replace other illegal chars by _xx_ (hex code)
441 - prepend _ if the result is a python keyword
442 """
443 rv = ''
444 ok = string.letters + '_'
445 ok2 = ok + string.digits
446 for c in str:
447 if c in ok:
448 rv = rv + c
449 elif c == ' ':
450 rv = rv + '_'
451 else:
452 rv = rv + '_%02.2x_'%ord(c)
453 ok = ok2
454 if rv in illegal_ids:
455 rv = '_' + rv
456 return rv
457
458# Call the main program
459
460if __name__ == '__main__':
461 main()
462 sys.exit(1)