blob: f08a4be6f3297b81c1d51aa6f2bae9f920d00b30 [file] [log] [blame]
Jack Jansen5ccd8261995-07-17 11:43:20 +00001"""
2gensuitemodule - Generate an AE suite module from an aete/aeut resource
3
Jack Jansen60762cb2000-08-17 22:11:45 +00004Based on aete.py.
5
6Reading and understanding this code is left as an exercise to the reader.
Jack Jansen5ccd8261995-07-17 11:43:20 +00007"""
8
9import MacOS
10import os
11import string
12import sys
13import types
14import StringIO
15import macfs
Jack Jansenb2ecc2c2002-01-24 22:44:07 +000016import keyword
Jack Jansen40926062002-03-30 23:43:04 +000017import macresource
Jack Jansen8b777672002-08-07 14:49:00 +000018from aetools import unpack
Jack Jansen5ccd8261995-07-17 11:43:20 +000019
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000020from Carbon.Res import *
Jack Jansen5ccd8261995-07-17 11:43:20 +000021
Jack Jansen40926062002-03-30 23:43:04 +000022DEFAULT_PACKAGEFOLDER=os.path.join(sys.prefix, 'Mac', 'Lib', 'lib-scriptpackages')
23
Jack Jansen5ccd8261995-07-17 11:43:20 +000024def main():
Jack Jansen40926062002-03-30 23:43:04 +000025 if len(sys.argv) > 1:
26 for filename in sys.argv[1:]:
27 processfile(filename)
28 else:
29 fss, ok = macfs.PromptGetFile('Select file with aeut/aete resource:')
30 if not ok:
31 sys.exit(0)
32 processfile(fss.as_pathname())
Jack Jansen5ccd8261995-07-17 11:43:20 +000033
Jack Jansen60762cb2000-08-17 22:11:45 +000034def processfile(fullname):
Jack Jansen5ccd8261995-07-17 11:43:20 +000035 """Process all resources in a single file"""
36 cur = CurResFile()
Jack Jansen40926062002-03-30 23:43:04 +000037 print "Processing", fullname
38 rf = macresource.open_pathname(fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000039 try:
40 UseResFile(rf)
41 resources = []
42 for i in range(Count1Resources('aete')):
43 res = Get1IndResource('aete', 1+i)
44 resources.append(res)
45 for i in range(Count1Resources('aeut')):
46 res = Get1IndResource('aeut', 1+i)
47 resources.append(res)
48 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +000049 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +000050 for res in resources:
51 print "decoding", res.GetResInfo(), "..."
52 data = res.data
53 aete = decode(data)
Jack Jansen60762cb2000-08-17 22:11:45 +000054 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +000055 finally:
56 if rf <> cur:
57 CloseResFile(rf)
58 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +000059 # switch back (needed for dialogs in Python)
60 UseResFile(cur)
61 compileaetelist(aetelist, fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000062
Jack Jansen60762cb2000-08-17 22:11:45 +000063def compileaetelist(aetelist, fullname):
64 for aete, resinfo in aetelist:
65 compileaete(aete, resinfo, fullname)
66
Jack Jansen5ccd8261995-07-17 11:43:20 +000067def decode(data):
68 """Decode a resource into a python data structure"""
69 f = StringIO.StringIO(data)
70 aete = generic(getaete, f)
71 aete = simplify(aete)
72 processed = f.tell()
73 unprocessed = len(f.read())
74 total = f.tell()
75 if unprocessed:
76 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
77 (processed, unprocessed, total))
78 return aete
79
80def simplify(item):
81 """Recursively replace singleton tuples by their constituent item"""
82 if type(item) is types.ListType:
83 return map(simplify, item)
84 elif type(item) == types.TupleType and len(item) == 2:
85 return simplify(item[1])
86 else:
87 return item
88
89
90# Here follows the aete resource decoder.
91# It is presented bottom-up instead of top-down because there are direct
92# references to the lower-level part-decoders from the high-level part-decoders.
93
94def getbyte(f, *args):
95 c = f.read(1)
96 if not c:
97 raise EOFError, 'in getbyte' + str(args)
98 return ord(c)
99
100def getword(f, *args):
101 getalign(f)
102 s = f.read(2)
103 if len(s) < 2:
104 raise EOFError, 'in getword' + str(args)
105 return (ord(s[0])<<8) | ord(s[1])
106
107def getlong(f, *args):
108 getalign(f)
109 s = f.read(4)
110 if len(s) < 4:
111 raise EOFError, 'in getlong' + str(args)
112 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
113
114def getostype(f, *args):
115 getalign(f)
116 s = f.read(4)
117 if len(s) < 4:
118 raise EOFError, 'in getostype' + str(args)
119 return s
120
121def getpstr(f, *args):
122 c = f.read(1)
123 if len(c) < 1:
124 raise EOFError, 'in getpstr[1]' + str(args)
125 nbytes = ord(c)
126 if nbytes == 0: return ''
127 s = f.read(nbytes)
128 if len(s) < nbytes:
129 raise EOFError, 'in getpstr[2]' + str(args)
130 return s
131
132def getalign(f):
133 if f.tell() & 1:
134 c = f.read(1)
135 ##if c <> '\0':
136 ## print 'align:', `c`
137
138def getlist(f, description, getitem):
139 count = getword(f)
140 list = []
141 for i in range(count):
142 list.append(generic(getitem, f))
143 getalign(f)
144 return list
145
146def alt_generic(what, f, *args):
147 print "generic", `what`, args
148 res = vageneric(what, f, args)
149 print '->', `res`
150 return res
151
152def generic(what, f, *args):
153 if type(what) == types.FunctionType:
154 return apply(what, (f,) + args)
155 if type(what) == types.ListType:
156 record = []
157 for thing in what:
158 item = apply(generic, thing[:1] + (f,) + thing[1:])
159 record.append((thing[1], item))
160 return record
161 return "BAD GENERIC ARGS: %s" % `what`
162
163getdata = [
164 (getostype, "type"),
165 (getpstr, "description"),
166 (getword, "flags")
167 ]
168getargument = [
169 (getpstr, "name"),
170 (getostype, "keyword"),
171 (getdata, "what")
172 ]
173getevent = [
174 (getpstr, "name"),
175 (getpstr, "description"),
176 (getostype, "suite code"),
177 (getostype, "event code"),
178 (getdata, "returns"),
179 (getdata, "accepts"),
180 (getlist, "optional arguments", getargument)
181 ]
182getproperty = [
183 (getpstr, "name"),
184 (getostype, "code"),
185 (getdata, "what")
186 ]
187getelement = [
188 (getostype, "type"),
189 (getlist, "keyform", getostype)
190 ]
191getclass = [
192 (getpstr, "name"),
193 (getostype, "class code"),
194 (getpstr, "description"),
195 (getlist, "properties", getproperty),
196 (getlist, "elements", getelement)
197 ]
198getcomparison = [
199 (getpstr, "operator name"),
200 (getostype, "operator ID"),
201 (getpstr, "operator comment"),
202 ]
203getenumerator = [
204 (getpstr, "enumerator name"),
205 (getostype, "enumerator ID"),
206 (getpstr, "enumerator comment")
207 ]
208getenumeration = [
209 (getostype, "enumeration ID"),
210 (getlist, "enumerator", getenumerator)
211 ]
212getsuite = [
213 (getpstr, "suite name"),
214 (getpstr, "suite description"),
215 (getostype, "suite ID"),
216 (getword, "suite level"),
217 (getword, "suite version"),
218 (getlist, "events", getevent),
219 (getlist, "classes", getclass),
220 (getlist, "comparisons", getcomparison),
221 (getlist, "enumerations", getenumeration)
222 ]
223getaete = [
224 (getword, "major/minor version in BCD"),
225 (getword, "language code"),
226 (getword, "script code"),
227 (getlist, "suites", getsuite)
228 ]
229
Jack Jansen60762cb2000-08-17 22:11:45 +0000230def compileaete(aete, resinfo, fname):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000231 """Generate code for a full aete resource. fname passed for doc purposes"""
232 [version, language, script, suites] = aete
233 major, minor = divmod(version, 256)
Jack Jansen60762cb2000-08-17 22:11:45 +0000234 fss = macfs.FSSpec(fname)
235 creatorsignature, dummy = fss.GetCreatorType()
Jack Jansen40926062002-03-30 23:43:04 +0000236 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000237 if language:
238 packagename = packagename+'_lang%d'%language
239 if script:
240 packagename = packagename+'_script%d'%script
241 if len(packagename) > 27:
242 packagename = packagename[:27]
Jack Jansen40926062002-03-30 23:43:04 +0000243 macfs.SetFolder(DEFAULT_PACKAGEFOLDER)
244 fss, ok = macfs.GetDirectory('Create and select package folder for %s'%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000245 if not ok:
246 return
247 pathname = fss.as_pathname()
248 packagename = os.path.split(os.path.normpath(pathname))[1]
249 fss, ok = macfs.GetDirectory('Package folder for base suite (usually StdSuites)')
250 if ok:
251 dirname, basepkgname = os.path.split(os.path.normpath(fss.as_pathname()))
252 if not dirname in sys.path:
253 sys.path.insert(0, dirname)
254 basepackage = __import__(basepkgname)
255 else:
256 basepackage = None
257 macfs.SetFolder(pathname)
258 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000259 allprecompinfo = []
260 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000261 for suite in suites:
Jack Jansen12b2b762000-08-20 19:30:56 +0000262 code, suite, fss, modname, precompinfo = precompilesuite(suite, basepackage)
263 if not code:
264 continue
265 allprecompinfo = allprecompinfo + precompinfo
266 suiteinfo = suite, fss, modname
267 suitelist.append((code, modname))
268 allsuites.append(suiteinfo)
269 for suiteinfo in allsuites:
270 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage, allprecompinfo)
Jack Jansen60762cb2000-08-17 22:11:45 +0000271 fss, ok = macfs.StandardPutFile('Package module', '__init__.py')
272 if not ok:
273 return
274 fp = open(fss.as_pathname(), 'w')
275 fss.SetCreatorType('Pyth', 'TEXT')
276 fp.write('"""\n')
277 fp.write("Package generated from %s\n"%fname)
Jack Jansen8b777672002-08-07 14:49:00 +0000278 if resinfo:
279 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000280 fp.write('"""\n')
281 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000282 fp.write('Error = aetools.Error\n')
Jack Jansen60762cb2000-08-17 22:11:45 +0000283 for code, modname in suitelist:
284 fp.write("import %s\n" % modname)
285 fp.write("\n\n_code_to_module = {\n")
286 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000287 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000288 fp.write("}\n\n")
289 fp.write("\n\n_code_to_fullname = {\n")
290 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000291 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000292 fp.write("}\n\n")
293 for code, modname in suitelist:
294 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000295
296 # Generate property dicts and element dicts for all types declared in this module
297 fp.write("def getbaseclasses(v):\n")
Jack Jansen7ff034b2002-08-07 15:52:44 +0000298 fp.write("\tif hasattr(v, '_superclassnames') and not hasattr(v, '_propdict'):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000299 fp.write("\t\tv._propdict = {}\n")
300 fp.write("\t\tv._elemdict = {}\n")
301 fp.write("\t\tfor superclass in v._superclassnames:\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000302## fp.write("\t\t\tgetbaseclasses(superclass)\n")
303 fp.write("\t\t\tv._propdict.update(getattr(eval(superclass), '_privpropdict', {}))\n")
304 fp.write("\t\t\tv._elemdict.update(getattr(eval(superclass), '_privelemdict', {}))\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000305 fp.write("\t\tv._propdict.update(v._privpropdict)\n")
306 fp.write("\t\tv._elemdict.update(v._privelemdict)\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000307 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000308 fp.write("import StdSuites\n")
309 if allprecompinfo:
310 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
311 for codenamemapper in allprecompinfo:
312 for k, v in codenamemapper.getall('class'):
313 fp.write("getbaseclasses(%s)\n" % v)
314
315 # Generate a code-to-name mapper for all of the types (classes) declared in this module
316 if allprecompinfo:
317 fp.write("\n#\n# Indices of types declared in this module\n#\n")
318 fp.write("_classdeclarations = {\n")
319 for codenamemapper in allprecompinfo:
320 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000321 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000322 fp.write("}\n")
323
Jack Jansen60762cb2000-08-17 22:11:45 +0000324 if suitelist:
325 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
326 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000327 fp.write(",\n\t\t%s_Events"%modname)
328 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000329 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000330 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000331 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000332
333def precompilesuite(suite, basepackage=None):
334 """Parse a single suite without generating the output. This step is needed
335 so we can resolve recursive references by suites to enums/comps/etc declared
336 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000337 [name, desc, code, level, version, events, classes, comps, enums] = suite
338
339 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000340 if len(modname) > 28:
341 modname = modname[:27]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000342 fss, ok = macfs.StandardPutFile('Python output file', modname+'.py')
343 if not ok:
Jack Jansen12b2b762000-08-20 19:30:56 +0000344 return None, None, None, None, None
345
Jack Jansen60762cb2000-08-17 22:11:45 +0000346 pathname = fss.as_pathname()
347 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000348
349 if basepackage and basepackage._code_to_module.has_key(code):
350 # We are an extension of a baseclass (usually an application extending
351 # Standard_Suite or so). Import everything from our base module
352 basemodule = basepackage._code_to_module[code]
353 else:
354 # We are not an extension.
355 basemodule = None
356
357 enumsneeded = {}
358 for event in events:
359 findenumsinevent(event, enumsneeded)
360
361 objc = ObjectCompiler(None, basemodule)
362 for cls in classes:
363 objc.compileclass(cls)
364 for cls in classes:
365 objc.fillclasspropsandelems(cls)
366 for comp in comps:
367 objc.compilecomparison(comp)
368 for enum in enums:
369 objc.compileenumeration(enum)
370
371 for enum in enumsneeded.keys():
372 objc.checkforenum(enum)
373
374 objc.dumpindex()
375
376 precompinfo = objc.getprecompinfo(modname)
377
378 return code, suite, fss, modname, precompinfo
379
380def compilesuite((suite, fss, modname), major, minor, language, script, fname, basepackage, precompinfo):
381 """Generate code for a single suite"""
382 [name, desc, code, level, version, events, classes, comps, enums] = suite
383
384 pathname = fss.as_pathname()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000385 fp = open(fss.as_pathname(), 'w')
Jack Jansen18a99f51996-03-18 13:35:00 +0000386 fss.SetCreatorType('Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000387
Jack Jansen8b777672002-08-07 14:49:00 +0000388 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000389 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000390 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000391 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
392 (major, minor, language, script))
393 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000394
395 fp.write('import aetools\n')
396 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000397 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000398 if basepackage and basepackage._code_to_module.has_key(code):
399 # We are an extension of a baseclass (usually an application extending
400 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000401 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000402 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000403 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
404 # This is needed by CodeWarrior and some others.
405 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
406 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000407 else:
408 # We are not an extension.
409 basemodule = None
410 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000411
412 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000413 if events:
414 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000415 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000416 else:
417 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000418
Jack Jansen12b2b762000-08-20 19:30:56 +0000419 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000420 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000421 objc.compileclass(cls)
422 for cls in classes:
423 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000424 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000425 objc.compilecomparison(comp)
426 for enum in enums:
427 objc.compileenumeration(enum)
428
429 for enum in enumsneeded.keys():
430 objc.checkforenum(enum)
431
432 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000433
434 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000435
Jack Jansen60762cb2000-08-17 22:11:45 +0000436def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000437 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000438 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000439 if module:
440 modshortname = string.split(module.__name__, '.')[-1]
441 baseclassname = '%s_Events'%modshortname
442 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000443 else:
444 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000445
Jack Jansen66544221997-08-08 14:49:02 +0000446def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000447 """Generate code for a single event"""
448 [name, desc, code, subcode, returns, accepts, arguments] = event
449 funcname = identify(name)
450 #
451 # generate name->keyword map
452 #
453 if arguments:
454 fp.write("\t_argmap_%s = {\n"%funcname)
455 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000456 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000457 fp.write("\t}\n\n")
458
459 #
460 # Generate function header
461 #
462 has_arg = (not is_null(accepts))
463 opt_arg = (has_arg and is_optional(accepts))
464
Jack Jansen84264771995-10-03 14:35:58 +0000465 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000466 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000467 if not opt_arg:
468 fp.write("_object, ") # Include direct object, if it has one
469 else:
470 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000471 else:
Jack Jansen84264771995-10-03 14:35:58 +0000472 fp.write("_no_object=None, ") # For argument checking
473 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000474 #
475 # Generate doc string (important, since it may be the only
476 # available documentation, due to our name-remaping)
477 #
Jack Jansen21f67582002-08-07 15:44:53 +0000478 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000479 if has_arg:
480 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
481 elif opt_arg:
482 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
483 for arg in arguments:
484 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
485 getdatadoc(arg[2])))
486 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
487 if not is_null(returns):
488 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
489 fp.write('\t\t"""\n')
490 #
491 # Fiddle the args so everything ends up in 'arguments' dictionary
492 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000493 fp.write("\t\t_code = %s\n"% `code`)
494 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000495 #
496 # Do keyword name substitution
497 #
498 if arguments:
499 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
500 else:
501 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
502 #
503 # Stuff required arg (if there is one) into arguments
504 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000505 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000506 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000507 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000508 fp.write("\t\tif _object:\n")
509 fp.write("\t\t\t_arguments['----'] = _object\n")
510 else:
511 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000512 fp.write("\n")
513 #
Jack Jansen73215141995-10-09 23:09:23 +0000514 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000515 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000516 for a in arguments:
517 if is_enum(a[2]):
518 kname = a[1]
519 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000520 if ename <> '****':
521 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000522 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000523 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000524 fp.write("\n")
525 #
526 # Do the transaction
527 #
Jack Jansen84264771995-10-03 14:35:58 +0000528 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
529 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000530 #
531 # Error handling
532 #
Jack Jansen18983532002-04-23 21:03:21 +0000533 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000534 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000535 fp.write("\t\t# XXXX Optionally decode result\n")
536 #
537 # Decode result
538 #
Jack Jansen84264771995-10-03 14:35:58 +0000539 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000540 if is_enum(returns):
541 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000542 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000543 fp.write("\n")
544
545# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
546# print "# returns", compiledata(returns)
547# print "# accepts", compiledata(accepts)
548# for arg in arguments:
549# compileargument(arg)
550
551def compileargument(arg):
552 [name, keyword, what] = arg
553 print "# %s (%s)" % (name, `keyword`), compiledata(what)
554
Jack Jansen12b2b762000-08-20 19:30:56 +0000555def findenumsinevent(event, enumsneeded):
556 """Find all enums for a single event"""
557 [name, desc, code, subcode, returns, accepts, arguments] = event
558 for a in arguments:
559 if is_enum(a[2]):
560 ename = a[2][0]
561 if ename <> '****':
562 enumsneeded[ename] = 1
563
564#
565# This class stores the code<->name translations for a single module. It is used
566# to keep the information while we're compiling the module, but we also keep these objects
567# around so if one suite refers to, say, an enum in another suite we know where to
568# find it. Finally, if we really can't find a code, the user can add modules by
569# hand.
570#
571class CodeNameMapper:
572
573 def __init__(self):
574 self.code2name = {
575 "property" : {},
576 "class" : {},
577 "enum" : {},
578 "comparison" : {},
579 }
580 self.name2code = {
581 "property" : {},
582 "class" : {},
583 "enum" : {},
584 "comparison" : {},
585 }
586 self.modulename = None
587 self.star_imported = 0
588
589 def addnamecode(self, type, name, code):
590 self.name2code[type][name] = code
591 if not self.code2name[type].has_key(code):
592 self.code2name[type][code] = name
593
594 def hasname(self, type, name):
595 return self.name2code[type].has_key(name)
596
597 def hascode(self, type, code):
598 return self.code2name[type].has_key(code)
599
600 def findcodename(self, type, code):
601 if not self.hascode(type, code):
602 return None, None, None
603 name = self.code2name[type][code]
604 if self.modulename and not self.star_imported:
605 qualname = '%s.%s'%(self.modulename, name)
606 else:
607 qualname = name
608 return name, qualname, self.modulename
609
610 def getall(self, type):
611 return self.code2name[type].items()
612
613 def addmodule(self, module, name, star_imported):
614 self.modulename = name
615 self.star_imported = star_imported
616 for code, name in module._propdeclarations.items():
617 self.addnamecode('property', name, code)
618 for code, name in module._classdeclarations.items():
619 self.addnamecode('class', name, code)
620 for code in module._enumdeclarations.keys():
621 self.addnamecode('enum', '_Enum_'+identify(code), code)
622 for code, name in module._compdeclarations.items():
623 self.addnamecode('comparison', name, code)
624
625 def prepareforexport(self, name=None):
626 if not self.modulename:
627 self.modulename = name
628 return self
629
Jack Jansen66544221997-08-08 14:49:02 +0000630class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000631 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000632 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000633 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000634 self.namemappers = [CodeNameMapper()]
635 if othernamemappers:
636 self.othernamemappers = othernamemappers[:]
637 else:
638 self.othernamemappers = []
639 if basesuite:
640 basemapper = CodeNameMapper()
641 basemapper.addmodule(basesuite, '', 1)
642 self.namemappers.append(basemapper)
643
644 def getprecompinfo(self, modname):
645 list = []
646 for mapper in self.namemappers:
647 emapper = mapper.prepareforexport(modname)
648 if emapper:
649 list.append(emapper)
650 return list
Jack Jansen66544221997-08-08 14:49:02 +0000651
652 def findcodename(self, type, code):
653 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000654 # First try: check whether we already know about this code.
655 for mapper in self.namemappers:
656 if mapper.hascode(type, code):
657 return mapper.findcodename(type, code)
658 # Second try: maybe one of the other modules knows about it.
659 for mapper in self.othernamemappers:
660 if mapper.hascode(type, code):
661 self.othernamemappers.remove(mapper)
662 self.namemappers.append(mapper)
663 if self.fp:
664 self.fp.write("import %s\n"%mapper.modulename)
665 break
666 else:
667 # If all this has failed we ask the user for a guess on where it could
668 # be and retry.
669 if self.fp:
670 m = self.askdefinitionmodule(type, code)
671 else:
672 m = None
673 if not m: return None, None, None
674 mapper = CodeNameMapper()
675 mapper.addmodule(m, m.__name__, 0)
676 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000677
678 def askdefinitionmodule(self, type, code):
679 fss, ok = macfs.PromptGetFile('Where is %s %s declared?'%(type, code))
680 if not ok: return
681 path, file = os.path.split(fss.as_pathname())
682 modname = os.path.splitext(file)[0]
683 if not path in sys.path:
684 sys.path.insert(0, path)
685 m = __import__(modname)
686 self.fp.write("import %s\n"%modname)
687 return m
688
689 def compileclass(self, cls):
690 [name, code, desc, properties, elements] = cls
691 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000692 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000693 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000694 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
695 if self.fp:
696 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000697 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000698 if self.fp:
699 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000700 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000701 self.fp.write('\twant = %s\n' % `code`)
702 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000703 for prop in properties:
704 self.compileproperty(prop)
705 for elem in elements:
706 self.compileelement(elem)
707
708 def compileproperty(self, prop):
709 [name, code, what] = prop
710 if code == 'c@#!':
711 # Something silly with plurals. Skip it.
712 return
713 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000714 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000715 # plural forms and such
716 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000717 if pname == othername:
718 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000719 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000720 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000721 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000722 if self.fp:
723 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000724 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000725 self.fp.write("\twhich = %s\n" % `code`)
726 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000727 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000728
729 def compileelement(self, elem):
730 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000731 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000732 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000733
Jack Jansen66544221997-08-08 14:49:02 +0000734 def fillclasspropsandelems(self, cls):
735 [name, code, desc, properties, elements] = cls
736 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000737 if self.namemappers[0].hascode('class', code) and \
738 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000739 # This is an other name (plural or so) for something else. Skip.
740 return
741 plist = []
742 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000743 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000744 for prop in properties:
745 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000746 if pcode == "c@#^":
747 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000748 if pcode == 'c@#!':
749 continue
750 pname = identify(pname)
751 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000752
753 superclassnames = []
754 for superclass in superclasses:
755 superId, superDesc, dummy = superclass
756 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
757 superclassnames.append(superclassname)
758
759 if self.fp:
760 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
761
Jack Jansen66544221997-08-08 14:49:02 +0000762 for elem in elements:
763 [ecode, keyform] = elem
764 if ecode == 'c@#!':
765 continue
766 name, ename, module = self.findcodename('class', ecode)
767 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000768 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000769 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000770 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000771 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000772
773 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000774 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000775 for n in plist:
776 self.fp.write("\t'%s' : %s,\n"%(n, n))
777 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000778 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000779 for n, fulln in elist:
780 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
781 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000782
783 def compilecomparison(self, comp):
784 [name, code, comment] = comp
785 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000786 self.namemappers[0].addnamecode('comparison', iname, code)
787 if self.fp:
788 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000789 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000790
791 def compileenumeration(self, enum):
792 [code, items] = enum
793 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000794 if self.fp:
795 self.fp.write("%s = {\n" % name)
796 for item in items:
797 self.compileenumerator(item)
798 self.fp.write("}\n\n")
799 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000800 return code
801
802 def compileenumerator(self, item):
803 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +0000804 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +0000805
806 def checkforenum(self, enum):
807 """This enum code is used by an event. Make sure it's available"""
808 name, fullname, module = self.findcodename('enum', enum)
809 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000810 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000811 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +0000812 return
813 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000814 if self.fp:
815 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000816
817 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000818 if not self.fp:
819 return
Jack Jansen66544221997-08-08 14:49:02 +0000820 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
821 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000822 for k, v in self.namemappers[0].getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000823 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000824 self.fp.write("}\n")
825 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000826 for k, v in self.namemappers[0].getall('property'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000827 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000828 self.fp.write("}\n")
829 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000830 for k, v in self.namemappers[0].getall('comparison'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000831 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000832 self.fp.write("}\n")
833 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000834 for k, v in self.namemappers[0].getall('enum'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000835 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000836 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000837
838def compiledata(data):
839 [type, description, flags] = data
840 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
841
842def is_null(data):
843 return data[0] == 'null'
844
845def is_optional(data):
846 return (data[2] & 0x8000)
847
848def is_enum(data):
849 return (data[2] & 0x2000)
850
851def getdatadoc(data):
852 [type, descr, flags] = data
853 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +0000854 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000855 if type == '****':
856 return 'anything'
857 if type == 'obj ':
858 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000859 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000860
861dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
862def compiledataflags(flags):
863 bits = []
864 for i in range(16):
865 if flags & (1<<i):
866 if i in dataflagdict.keys():
867 bits.append(dataflagdict[i])
868 else:
869 bits.append(`i`)
870 return '[%s]' % string.join(bits)
871
Jack Jansen8b777672002-08-07 14:49:00 +0000872def ascii(str):
873 """Return a string with all non-ascii characters hex-encoded"""
874 if type(str) != type(''):
875 return map(ascii, str)
876 rv = ''
877 for c in str:
878 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
879 rv = rv + c
880 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000881 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +0000882 return rv
883
Jack Jansen5ccd8261995-07-17 11:43:20 +0000884def identify(str):
885 """Turn any string into an identifier:
886 - replace space by _
887 - replace other illegal chars by _xx_ (hex code)
888 - prepend _ if the result is a python keyword
889 """
Jack Jansen66544221997-08-08 14:49:02 +0000890 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +0000891 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000892 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000893 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000894 ok2 = ok + string.digits
895 for c in str:
896 if c in ok:
897 rv = rv + c
898 elif c == ' ':
899 rv = rv + '_'
900 else:
901 rv = rv + '_%02.2x_'%ord(c)
902 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000903 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +0000904 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000905 return rv
906
907# Call the main program
908
909if __name__ == '__main__':
910 main()
911 sys.exit(1)
Jack Jansen8b777672002-08-07 14:49:00 +0000912print identify('for')