2 gensuitemodule - Generate an AE suite module from an aete/aeut resource
18 fss
, ok
= macfs
.PromptGetFile('Select file with aeut/aete resource:')
21 process(fss
.as_pathname())
23 def process(fullname
):
24 """Process all resources in a single file"""
27 rf
= OpenRFPerm(fullname
, 0, 1)
31 for i
in range(Count1Resources('aete')):
32 res
= Get1IndResource('aete', 1+i
)
34 for i
in range(Count1Resources('aeut')):
35 res
= Get1IndResource('aeut', 1+i
)
37 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
39 print "decoding", res
.GetResInfo(), "..."
42 # switch back (needed for dialogs in Python)
44 compileaete(aete
, fullname
)
52 """Decode a resource into a python data structure"""
53 f
= StringIO
.StringIO(data
)
54 aete
= generic(getaete
, f
)
57 unprocessed
= len(f
.read())
60 sys
.stderr
.write("%d processed + %d unprocessed = %d total\n" %
61 (processed
, unprocessed
, total
))
65 """Recursively replace singleton tuples by their constituent item"""
66 if type(item
) is types
.ListType
:
67 return map(simplify
, item
)
68 elif type(item
) == types
.TupleType
and len(item
) == 2:
69 return simplify(item
[1])
74 # Here follows the aete resource decoder.
75 # It is presented bottom-up instead of top-down because there are direct
76 # references to the lower-level part-decoders from the high-level part-decoders.
78 def getbyte(f
, *args
):
81 raise EOFError, 'in getbyte' + str(args
)
84 def getword(f
, *args
):
88 raise EOFError, 'in getword' + str(args
)
89 return (ord(s
[0])<<8) |
ord(s
[1])
91 def getlong(f
, *args
):
95 raise EOFError, 'in getlong' + str(args
)
96 return (ord(s
[0])<<24) |
(ord(s
[1])<<16) |
(ord(s
[2])<<8) |
ord(s
[3])
98 def getostype(f
, *args
):
102 raise EOFError, 'in getostype' + str(args
)
105 def getpstr(f
, *args
):
108 raise EOFError, 'in getpstr[1]' + str(args
)
110 if nbytes
== 0: return ''
113 raise EOFError, 'in getpstr[2]' + str(args
)
120 ## print 'align:', `c`
122 def getlist(f
, description
, getitem
):
125 for i
in range(count
):
126 list.append(generic(getitem
, f
))
130 def alt_generic(what
, f
, *args
):
131 print "generic", `what`
, args
132 res
= vageneric(what
, f
, args
)
136 def generic(what
, f
, *args
):
137 if type(what
) == types
.FunctionType
:
138 return apply(what
, (f
,) + args
)
139 if type(what
) == types
.ListType
:
142 item
= apply(generic
, thing
[:1] + (f
,) + thing
[1:])
143 record
.append((thing
[1], item
))
145 return "BAD GENERIC ARGS: %s" % `what`
149 (getpstr
, "description"),
154 (getostype
, "keyword"),
159 (getpstr
, "description"),
160 (getostype
, "suite code"),
161 (getostype
, "event code"),
162 (getdata
, "returns"),
163 (getdata
, "accepts"),
164 (getlist
, "optional arguments", getargument
)
173 (getlist
, "keyform", getostype
)
177 (getostype
, "class code"),
178 (getpstr
, "description"),
179 (getlist
, "properties", getproperty
),
180 (getlist
, "elements", getelement
)
183 (getpstr
, "operator name"),
184 (getostype
, "operator ID"),
185 (getpstr
, "operator comment"),
188 (getpstr
, "enumerator name"),
189 (getostype
, "enumerator ID"),
190 (getpstr
, "enumerator comment")
193 (getostype
, "enumeration ID"),
194 (getlist
, "enumerator", getenumerator
)
197 (getpstr
, "suite name"),
198 (getpstr
, "suite description"),
199 (getostype
, "suite ID"),
200 (getword
, "suite level"),
201 (getword
, "suite version"),
202 (getlist
, "events", getevent
),
203 (getlist
, "classes", getclass
),
204 (getlist
, "comparisons", getcomparison
),
205 (getlist
, "enumerations", getenumeration
)
208 (getword
, "major/minor version in BCD"),
209 (getword
, "language code"),
210 (getword
, "script code"),
211 (getlist
, "suites", getsuite
)
214 def compileaete(aete
, fname
):
215 """Generate code for a full aete resource. fname passed for doc purposes"""
216 [version
, language
, script
, suites
] = aete
217 major
, minor
= divmod(version
, 256)
219 compilesuite(suite
, major
, minor
, language
, script
, fname
)
221 def compilesuite(suite
, major
, minor
, language
, script
, fname
):
222 """Generate code for a single suite"""
223 [name
, desc
, code
, level
, version
, events
, classes
, comps
, enums
] = suite
225 modname
= identify(name
)
226 fss
, ok
= macfs
.StandardPutFile('Python output file', modname
+'.py')
229 fp
= open(fss
.as_pathname(), 'w')
230 fss
.SetCreatorType('Pyth', 'TEXT')
232 fp
.write('"""Suite %s: %s\n' % (name
, desc
))
233 fp
.write("Level %d, version %d\n\n" % (level
, version
))
234 fp
.write("Generated from %s\n"%fname
)
235 fp
.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
236 (major
, minor
, language
, script
))
239 fp
.write('import aetools\n')
240 fp
.write('import MacOS\n\n')
241 fp
.write("_code = %s\n\n"% `code`
)
243 compileclassheader(fp
, modname
)
248 compileevent(fp
, event
, enumsneeded
)
250 fp
.write("\tpass\n\n")
252 objc
= ObjectCompiler(fp
)
254 objc
.compileclass(cls
)
256 objc
.fillclasspropsandelems(cls
)
258 objc
.compilecomparison(comp
)
260 objc
.compileenumeration(enum
)
262 for enum
in enumsneeded
.keys():
263 objc
.checkforenum(enum
)
267 def compileclassheader(fp
, name
):
268 """Generate class boilerplate"""
269 fp
.write("class %s:\n\n"%name
)
271 def compileevent(fp
, event
, enumsneeded
):
272 """Generate code for a single event"""
273 [name
, desc
, code
, subcode
, returns
, accepts
, arguments
] = event
274 funcname
= identify(name
)
276 # generate name->keyword map
279 fp
.write("\t_argmap_%s = {\n"%funcname
)
281 fp
.write("\t\t%s : %s,\n"%(`
identify(a
[0])`
, `a
[1]`
))
285 # Generate function header
287 has_arg
= (not is_null(accepts
))
288 opt_arg
= (has_arg
and is_optional(accepts
))
290 fp
.write("\tdef %s(self, "%funcname
)
293 fp
.write("_object, ") # Include direct object, if it has one
295 fp
.write("_object=None, ") # Also include if it is optional
297 fp
.write("_no_object=None, ") # For argument checking
298 fp
.write("_attributes={}, **_arguments):\n") # include attribute dict and args
300 # Generate doc string (important, since it may be the only
301 # available documentation, due to our name-remaping)
303 fp
.write('\t\t"""%s: %s\n'%(name
, desc
))
305 fp
.write("\t\tRequired argument: %s\n"%getdatadoc
(accepts
))
307 fp
.write("\t\tOptional argument: %s\n"%getdatadoc
(accepts
))
308 for arg
in arguments
:
309 fp
.write("\t\tKeyword argument %s: %s\n"%(identify(arg
[0]),
311 fp
.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
312 if not is_null(returns
):
313 fp
.write("\t\tReturns: %s\n"%getdatadoc
(returns
))
314 fp
.write('\t\t"""\n')
316 # Fiddle the args so everything ends up in 'arguments' dictionary
318 fp
.write("\t\t_code = %s\n"% `code`
)
319 fp
.write("\t\t_subcode = %s\n\n"% `subcode`
)
321 # Do keyword name substitution
324 fp
.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname
)
326 fp
.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
328 # Stuff required arg (if there is one) into arguments
331 fp
.write("\t\t_arguments['----'] = _object\n")
333 fp
.write("\t\tif _object:\n")
334 fp
.write("\t\t\t_arguments['----'] = _object\n")
336 fp
.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
339 # Do enum-name substitution
346 fp
.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
348 enumsneeded
[ename
] = 1
353 fp
.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
354 fp
.write("\t\t\t\t_arguments, _attributes)\n")
358 fp
.write("\t\tif _arguments.has_key('errn'):\n")
359 fp
.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
360 fp
.write("\t\t# XXXX Optionally decode result\n")
364 fp
.write("\t\tif _arguments.has_key('----'):\n")
366 fp
.write("\t\t\t# XXXX Should do enum remapping here...\n")
367 fp
.write("\t\t\treturn _arguments['----']\n")
370 # print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
371 # print "# returns", compiledata(returns)
372 # print "# accepts", compiledata(accepts)
373 # for arg in arguments:
374 # compileargument(arg)
376 def compileargument(arg
):
377 [name
, keyword
, what
] = arg
378 print "# %s (%s)" % (name
, `keyword`
), compiledata(what
)
380 class ObjectCompiler
:
381 def __init__(self
, fp
):
389 self
.othersuites
= []
391 def findcodename(self
, type, code
):
393 if type == 'property':
394 if self
.propcodes
.has_key(code
):
395 return self
.propcodes
[code
], self
.propcodes
[code
], None
396 for s
in self
.othersuites
:
397 if s
._propdeclarations
.has_key(code
):
398 name
= s
._propdeclarations
[code
].__name
__
399 return name
, '%s.%s' % (s
.__name
__, name
), s
.__name
__
401 if self
.classcodes
.has_key(code
):
402 return self
.classcodes
[code
], self
.classcodes
[code
], None
403 for s
in self
.othersuites
:
404 if s
._classdeclarations
.has_key(code
):
405 name
= s
._classdeclarations
[code
].__name
__
406 return name
, '%s.%s' % (s
.__name
__, name
), s
.__name
__
408 if self
.enumcodes
.has_key(code
):
409 return self
.enumcodes
[code
], self
.enumcodes
[code
], None
410 for s
in self
.othersuites
:
411 if s
._enumdeclarations
.has_key(code
):
412 name
= '_Enum_' + identify(code
)
413 return name
, '%s.%s' % (s
.__name
__, name
), s
.__name
__
414 if type == 'comparison':
415 if self
.compcodes
.has_key(code
):
416 return self
.compcodes
[code
], self
.compcodes
[code
], None
417 for s
in self
.othersuites
:
418 if s
._compdeclarations
.has_key(code
):
419 name
= s
._compdeclarations
[code
].__name
__
420 return name
, '%s.%s' % (s
.__name
__, name
), s
.__name
__
422 m
= self
.askdefinitionmodule(type, code
)
423 if not m
: return None, None, None
424 self
.othersuites
.append(m
)
426 def askdefinitionmodule(self
, type, code
):
427 fss
, ok
= macfs
.PromptGetFile('Where is %s %s declared?'%(type, code
))
429 path
, file = os
.path
.split(fss
.as_pathname())
430 modname
= os
.path
.splitext(file)[0]
431 if not path
in sys
.path
:
432 sys
.path
.insert(0, path
)
433 m
= __import__(modname
)
434 self
.fp
.write("import %s\n"%modname
)
437 def compileclass(self
, cls
):
438 [name
, code
, desc
, properties
, elements
] = cls
439 pname
= identify(name
)
440 if self
.classcodes
.has_key(code
):
441 # plural forms and such
442 self
.fp
.write("\n%s = %s\n"%(pname
, self
.classcodes
[code
]))
443 self
.classnames
[pname
] = code
445 self
.fp
.write('\nclass %s(aetools.ComponentItem):\n' % pname
)
446 self
.fp
.write('\t"""%s - %s"""\n' % (name
, desc
))
447 self
.fp
.write('\twant = %s\n' % `code`
)
448 self
.classnames
[pname
] = code
449 self
.classcodes
[code
] = pname
450 for prop
in properties
:
451 self
.compileproperty(prop
)
452 for elem
in elements
:
453 self
.compileelement(elem
)
455 def compileproperty(self
, prop
):
456 [name
, code
, what
] = prop
458 # Something silly with plurals. Skip it.
460 pname
= identify(name
)
461 if self
.propcodes
.has_key(code
):
462 self
.fp
.write("# repeated property %s %s\n"%(pname
, what
[1]))
464 self
.fp
.write("class %s(aetools.NProperty):\n" % pname
)
465 self
.fp
.write('\t"""%s - %s"""\n' % (name
, what
[1]))
466 self
.fp
.write("\twhich = %s\n" % `code`
)
467 self
.fp
.write("\twant = %s\n" % `what
[0]`
)
468 self
.propnames
[pname
] = code
469 self
.propcodes
[code
] = pname
471 def compileelement(self
, elem
):
472 [code
, keyform
] = elem
473 self
.fp
.write("# element %s as %s\n" % (`code`
, keyform
))
475 def fillclasspropsandelems(self
, cls
):
476 [name
, code
, desc
, properties
, elements
] = cls
477 cname
= identify(name
)
478 if self
.classcodes
[code
] != cname
:
479 # This is an other name (plural or so) for something else. Skip.
483 for prop
in properties
:
484 [pname
, pcode
, what
] = prop
487 pname
= identify(pname
)
489 for elem
in elements
:
490 [ecode
, keyform
] = elem
493 name
, ename
, module
= self
.findcodename('class', ecode
)
495 self
.fp
.write("# XXXX %s element %s not found!!\n"%(cname
, `ecode`
))
497 elist
.append(name
, ename
)
499 self
.fp
.write("%s._propdict = {\n"%cname
)
501 self
.fp
.write("\t'%s' : %s,\n"%(n
, n
))
503 self
.fp
.write("%s._elemdict = {\n"%cname
)
504 for n
, fulln
in elist
:
505 self
.fp
.write("\t'%s' : %s,\n"%(n
, fulln
))
508 def compilecomparison(self
, comp
):
509 [name
, code
, comment
] = comp
510 iname
= identify(name
)
511 self
.compcodes
[code
] = iname
512 self
.fp
.write("class %s(aetools.NComparison):\n" % iname
)
513 self
.fp
.write('\t"""%s - %s"""\n' % (name
, comment
))
515 def compileenumeration(self
, enum
):
517 name
= "_Enum_%s" % identify(code
)
518 self
.fp
.write("%s = {\n" % name
)
520 self
.compileenumerator(item
)
521 self
.fp
.write("}\n\n")
522 self
.enumcodes
[code
] = name
525 def compileenumerator(self
, item
):
526 [name
, code
, desc
] = item
527 self
.fp
.write("\t%s : %s,\t# %s\n" % (`
identify(name
)`
, `code`
, desc
))
529 def checkforenum(self
, enum
):
530 """This enum code is used by an event. Make sure it's available"""
531 name
, fullname
, module
= self
.findcodename('enum', enum
)
533 self
.fp
.write("# XXXX enum %s not found!!\n"%(enum))
536 self
.fp
.write("from %s import %s\n"%(module
, name
))
539 self
.fp
.write("\n#\n# Indices of types declared in this module\n#\n")
540 self
.fp
.write("_classdeclarations = {\n")
541 for k
in self
.classcodes
.keys():
542 self
.fp
.write("\t%s : %s,\n" % (`k`
, self
.classcodes
[k
]))
544 self
.fp
.write("\n_propdeclarations = {\n")
545 for k
in self
.propcodes
.keys():
546 self
.fp
.write("\t%s : %s,\n" % (`k`
, self
.propcodes
[k
]))
548 self
.fp
.write("\n_compdeclarations = {\n")
549 for k
in self
.compcodes
.keys():
550 self
.fp
.write("\t%s : %s,\n" % (`k`
, self
.compcodes
[k
]))
552 self
.fp
.write("\n_enumdeclarations = {\n")
553 for k
in self
.enumcodes
.keys():
554 self
.fp
.write("\t%s : %s,\n" % (`k`
, self
.enumcodes
[k
]))
557 def compiledata(data
):
558 [type, description
, flags
] = data
559 return "%s -- %s %s" % (`
type`
, `description`
, compiledataflags(flags
))
562 return data
[0] == 'null'
564 def is_optional(data
):
565 return (data
[2] & 0x8000)
568 return (data
[2] & 0x2000)
570 def getdatadoc(data
):
571 [type, descr
, flags
] = data
577 return 'an AE object reference'
578 return "undocumented, typecode %s"%`
type`
580 dataflagdict
= {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
581 def compiledataflags(flags
):
585 if i
in dataflagdict
.keys():
586 bits
.append(dataflagdict
[i
])
589 return '[%s]' % string
.join(bits
)
591 # XXXX Do we have a set of python keywords somewhere?
592 illegal_ids
= [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
596 """Turn any string into an identifier:
598 - replace other illegal chars by _xx_ (hex code)
599 - prepend _ if the result is a python keyword
602 return "_empty_ae_name"
604 ok
= string
.letters
+ '_'
605 ok2
= ok
+ string
.digits
612 rv
= rv
+ '_%02.2x_'%ord(c
)
614 if rv
in illegal_ids
:
618 # Call the main program
620 if __name__
== '__main__':