blob: b68def78d06297014906337d01b8324ef196f44b [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
16
17from Res import *
18
19def main():
Jack Jansen9d19a911995-08-14 12:14:55 +000020 fss, ok = macfs.PromptGetFile('Select file with aeut/aete resource:')
Jack Jansen5ccd8261995-07-17 11:43:20 +000021 if not ok:
22 sys.exit(0)
Jack Jansen60762cb2000-08-17 22:11:45 +000023 processfile(fss.as_pathname())
Jack Jansen5ccd8261995-07-17 11:43:20 +000024
Jack Jansen60762cb2000-08-17 22:11:45 +000025def processfile(fullname):
Jack Jansen5ccd8261995-07-17 11:43:20 +000026 """Process all resources in a single file"""
27 cur = CurResFile()
28 print fullname
29 rf = OpenRFPerm(fullname, 0, 1)
30 try:
31 UseResFile(rf)
32 resources = []
33 for i in range(Count1Resources('aete')):
34 res = Get1IndResource('aete', 1+i)
35 resources.append(res)
36 for i in range(Count1Resources('aeut')):
37 res = Get1IndResource('aeut', 1+i)
38 resources.append(res)
39 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +000040 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +000041 for res in resources:
42 print "decoding", res.GetResInfo(), "..."
43 data = res.data
44 aete = decode(data)
Jack Jansen60762cb2000-08-17 22:11:45 +000045 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +000046 finally:
47 if rf <> cur:
48 CloseResFile(rf)
49 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +000050 # switch back (needed for dialogs in Python)
51 UseResFile(cur)
52 compileaetelist(aetelist, fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000053
Jack Jansen60762cb2000-08-17 22:11:45 +000054def compileaetelist(aetelist, fullname):
55 for aete, resinfo in aetelist:
56 compileaete(aete, resinfo, fullname)
57
Jack Jansen5ccd8261995-07-17 11:43:20 +000058def decode(data):
59 """Decode a resource into a python data structure"""
60 f = StringIO.StringIO(data)
61 aete = generic(getaete, f)
62 aete = simplify(aete)
63 processed = f.tell()
64 unprocessed = len(f.read())
65 total = f.tell()
66 if unprocessed:
67 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
68 (processed, unprocessed, total))
69 return aete
70
71def simplify(item):
72 """Recursively replace singleton tuples by their constituent item"""
73 if type(item) is types.ListType:
74 return map(simplify, item)
75 elif type(item) == types.TupleType and len(item) == 2:
76 return simplify(item[1])
77 else:
78 return item
79
80
81# Here follows the aete resource decoder.
82# It is presented bottom-up instead of top-down because there are direct
83# references to the lower-level part-decoders from the high-level part-decoders.
84
85def getbyte(f, *args):
86 c = f.read(1)
87 if not c:
88 raise EOFError, 'in getbyte' + str(args)
89 return ord(c)
90
91def getword(f, *args):
92 getalign(f)
93 s = f.read(2)
94 if len(s) < 2:
95 raise EOFError, 'in getword' + str(args)
96 return (ord(s[0])<<8) | ord(s[1])
97
98def getlong(f, *args):
99 getalign(f)
100 s = f.read(4)
101 if len(s) < 4:
102 raise EOFError, 'in getlong' + str(args)
103 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
104
105def getostype(f, *args):
106 getalign(f)
107 s = f.read(4)
108 if len(s) < 4:
109 raise EOFError, 'in getostype' + str(args)
110 return s
111
112def getpstr(f, *args):
113 c = f.read(1)
114 if len(c) < 1:
115 raise EOFError, 'in getpstr[1]' + str(args)
116 nbytes = ord(c)
117 if nbytes == 0: return ''
118 s = f.read(nbytes)
119 if len(s) < nbytes:
120 raise EOFError, 'in getpstr[2]' + str(args)
121 return s
122
123def getalign(f):
124 if f.tell() & 1:
125 c = f.read(1)
126 ##if c <> '\0':
127 ## print 'align:', `c`
128
129def getlist(f, description, getitem):
130 count = getword(f)
131 list = []
132 for i in range(count):
133 list.append(generic(getitem, f))
134 getalign(f)
135 return list
136
137def alt_generic(what, f, *args):
138 print "generic", `what`, args
139 res = vageneric(what, f, args)
140 print '->', `res`
141 return res
142
143def generic(what, f, *args):
144 if type(what) == types.FunctionType:
145 return apply(what, (f,) + args)
146 if type(what) == types.ListType:
147 record = []
148 for thing in what:
149 item = apply(generic, thing[:1] + (f,) + thing[1:])
150 record.append((thing[1], item))
151 return record
152 return "BAD GENERIC ARGS: %s" % `what`
153
154getdata = [
155 (getostype, "type"),
156 (getpstr, "description"),
157 (getword, "flags")
158 ]
159getargument = [
160 (getpstr, "name"),
161 (getostype, "keyword"),
162 (getdata, "what")
163 ]
164getevent = [
165 (getpstr, "name"),
166 (getpstr, "description"),
167 (getostype, "suite code"),
168 (getostype, "event code"),
169 (getdata, "returns"),
170 (getdata, "accepts"),
171 (getlist, "optional arguments", getargument)
172 ]
173getproperty = [
174 (getpstr, "name"),
175 (getostype, "code"),
176 (getdata, "what")
177 ]
178getelement = [
179 (getostype, "type"),
180 (getlist, "keyform", getostype)
181 ]
182getclass = [
183 (getpstr, "name"),
184 (getostype, "class code"),
185 (getpstr, "description"),
186 (getlist, "properties", getproperty),
187 (getlist, "elements", getelement)
188 ]
189getcomparison = [
190 (getpstr, "operator name"),
191 (getostype, "operator ID"),
192 (getpstr, "operator comment"),
193 ]
194getenumerator = [
195 (getpstr, "enumerator name"),
196 (getostype, "enumerator ID"),
197 (getpstr, "enumerator comment")
198 ]
199getenumeration = [
200 (getostype, "enumeration ID"),
201 (getlist, "enumerator", getenumerator)
202 ]
203getsuite = [
204 (getpstr, "suite name"),
205 (getpstr, "suite description"),
206 (getostype, "suite ID"),
207 (getword, "suite level"),
208 (getword, "suite version"),
209 (getlist, "events", getevent),
210 (getlist, "classes", getclass),
211 (getlist, "comparisons", getcomparison),
212 (getlist, "enumerations", getenumeration)
213 ]
214getaete = [
215 (getword, "major/minor version in BCD"),
216 (getword, "language code"),
217 (getword, "script code"),
218 (getlist, "suites", getsuite)
219 ]
220
Jack Jansen60762cb2000-08-17 22:11:45 +0000221def compileaete(aete, resinfo, fname):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000222 """Generate code for a full aete resource. fname passed for doc purposes"""
223 [version, language, script, suites] = aete
224 major, minor = divmod(version, 256)
Jack Jansen60762cb2000-08-17 22:11:45 +0000225 fss = macfs.FSSpec(fname)
226 creatorsignature, dummy = fss.GetCreatorType()
227 packagename = identify(os.path.basename(fname))
228 if language:
229 packagename = packagename+'_lang%d'%language
230 if script:
231 packagename = packagename+'_script%d'%script
232 if len(packagename) > 27:
233 packagename = packagename[:27]
234 macfs.SetFolder(os.path.join(sys.prefix, ':Mac:Lib:lib-scriptpackages'))
235 fss, ok = macfs.GetDirectory('Package folder for %s'%packagename)
236 if not ok:
237 return
238 pathname = fss.as_pathname()
239 packagename = os.path.split(os.path.normpath(pathname))[1]
240 fss, ok = macfs.GetDirectory('Package folder for base suite (usually StdSuites)')
241 if ok:
242 dirname, basepkgname = os.path.split(os.path.normpath(fss.as_pathname()))
243 if not dirname in sys.path:
244 sys.path.insert(0, dirname)
245 basepackage = __import__(basepkgname)
246 else:
247 basepackage = None
248 macfs.SetFolder(pathname)
249 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000250 allprecompinfo = []
251 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000252 for suite in suites:
Jack Jansen12b2b762000-08-20 19:30:56 +0000253 code, suite, fss, modname, precompinfo = precompilesuite(suite, basepackage)
254 if not code:
255 continue
256 allprecompinfo = allprecompinfo + precompinfo
257 suiteinfo = suite, fss, modname
258 suitelist.append((code, modname))
259 allsuites.append(suiteinfo)
260 for suiteinfo in allsuites:
261 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage, allprecompinfo)
Jack Jansen60762cb2000-08-17 22:11:45 +0000262 fss, ok = macfs.StandardPutFile('Package module', '__init__.py')
263 if not ok:
264 return
265 fp = open(fss.as_pathname(), 'w')
266 fss.SetCreatorType('Pyth', 'TEXT')
267 fp.write('"""\n')
268 fp.write("Package generated from %s\n"%fname)
269 fp.write("Resource %s resid %d %s\n"%(resinfo[1], resinfo[0], resinfo[2]))
270 fp.write('"""\n')
271 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000272 fp.write('Error = aetools.Error\n')
Jack Jansen60762cb2000-08-17 22:11:45 +0000273 for code, modname in suitelist:
274 fp.write("import %s\n" % modname)
275 fp.write("\n\n_code_to_module = {\n")
276 for code, modname in suitelist:
277 fp.write("\t'%s' : %s,\n"%(code, modname))
278 fp.write("}\n\n")
279 fp.write("\n\n_code_to_fullname = {\n")
280 for code, modname in suitelist:
Jack Jansen12b2b762000-08-20 19:30:56 +0000281 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(code, packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000282 fp.write("}\n\n")
283 for code, modname in suitelist:
284 fp.write("from %s import *\n"%modname)
285 if suitelist:
286 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
287 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000288 fp.write(",\n\t\t%s_Events"%modname)
289 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen60762cb2000-08-17 22:11:45 +0000290 fp.write("\t_signature = '%s'\n\n"%creatorsignature)
291 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000292
293def precompilesuite(suite, basepackage=None):
294 """Parse a single suite without generating the output. This step is needed
295 so we can resolve recursive references by suites to enums/comps/etc declared
296 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000297 [name, desc, code, level, version, events, classes, comps, enums] = suite
298
299 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000300 if len(modname) > 28:
301 modname = modname[:27]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000302 fss, ok = macfs.StandardPutFile('Python output file', modname+'.py')
303 if not ok:
Jack Jansen12b2b762000-08-20 19:30:56 +0000304 return None, None, None, None, None
305
Jack Jansen60762cb2000-08-17 22:11:45 +0000306 pathname = fss.as_pathname()
307 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000308
309 if basepackage and basepackage._code_to_module.has_key(code):
310 # We are an extension of a baseclass (usually an application extending
311 # Standard_Suite or so). Import everything from our base module
312 basemodule = basepackage._code_to_module[code]
313 else:
314 # We are not an extension.
315 basemodule = None
316
317 enumsneeded = {}
318 for event in events:
319 findenumsinevent(event, enumsneeded)
320
321 objc = ObjectCompiler(None, basemodule)
322 for cls in classes:
323 objc.compileclass(cls)
324 for cls in classes:
325 objc.fillclasspropsandelems(cls)
326 for comp in comps:
327 objc.compilecomparison(comp)
328 for enum in enums:
329 objc.compileenumeration(enum)
330
331 for enum in enumsneeded.keys():
332 objc.checkforenum(enum)
333
334 objc.dumpindex()
335
336 precompinfo = objc.getprecompinfo(modname)
337
338 return code, suite, fss, modname, precompinfo
339
340def compilesuite((suite, fss, modname), major, minor, language, script, fname, basepackage, precompinfo):
341 """Generate code for a single suite"""
342 [name, desc, code, level, version, events, classes, comps, enums] = suite
343
344 pathname = fss.as_pathname()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000345 fp = open(fss.as_pathname(), 'w')
Jack Jansen18a99f51996-03-18 13:35:00 +0000346 fss.SetCreatorType('Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000347
348 fp.write('"""Suite %s: %s\n' % (name, desc))
349 fp.write("Level %d, version %d\n\n" % (level, version))
350 fp.write("Generated from %s\n"%fname)
351 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
352 (major, minor, language, script))
353 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000354
355 fp.write('import aetools\n')
356 fp.write('import MacOS\n\n')
357 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000358 if basepackage and basepackage._code_to_module.has_key(code):
359 # We are an extension of a baseclass (usually an application extending
360 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000361 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000362 basemodule = basepackage._code_to_module[code]
363 else:
364 # We are not an extension.
365 basemodule = None
366 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000367
368 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000369 if events:
370 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000371 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000372 else:
373 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000374
Jack Jansen12b2b762000-08-20 19:30:56 +0000375 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000376 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000377 objc.compileclass(cls)
378 for cls in classes:
379 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000380 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000381 objc.compilecomparison(comp)
382 for enum in enums:
383 objc.compileenumeration(enum)
384
385 for enum in enumsneeded.keys():
386 objc.checkforenum(enum)
387
388 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000389
390 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000391
Jack Jansen60762cb2000-08-17 22:11:45 +0000392def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000393 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000394 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000395 if module:
396 modshortname = string.split(module.__name__, '.')[-1]
397 baseclassname = '%s_Events'%modshortname
398 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000399 else:
400 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000401
Jack Jansen66544221997-08-08 14:49:02 +0000402def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000403 """Generate code for a single event"""
404 [name, desc, code, subcode, returns, accepts, arguments] = event
405 funcname = identify(name)
406 #
407 # generate name->keyword map
408 #
409 if arguments:
410 fp.write("\t_argmap_%s = {\n"%funcname)
411 for a in arguments:
412 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
413 fp.write("\t}\n\n")
414
415 #
416 # Generate function header
417 #
418 has_arg = (not is_null(accepts))
419 opt_arg = (has_arg and is_optional(accepts))
420
Jack Jansen84264771995-10-03 14:35:58 +0000421 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000422 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000423 if not opt_arg:
424 fp.write("_object, ") # Include direct object, if it has one
425 else:
426 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000427 else:
Jack Jansen84264771995-10-03 14:35:58 +0000428 fp.write("_no_object=None, ") # For argument checking
429 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000430 #
431 # Generate doc string (important, since it may be the only
432 # available documentation, due to our name-remaping)
433 #
434 fp.write('\t\t"""%s: %s\n'%(name, desc))
435 if has_arg:
436 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
437 elif opt_arg:
438 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
439 for arg in arguments:
440 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
441 getdatadoc(arg[2])))
442 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
443 if not is_null(returns):
444 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
445 fp.write('\t\t"""\n')
446 #
447 # Fiddle the args so everything ends up in 'arguments' dictionary
448 #
449 fp.write("\t\t_code = %s\n"% `code`)
450 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000451 #
452 # Do keyword name substitution
453 #
454 if arguments:
455 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
456 else:
457 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
458 #
459 # Stuff required arg (if there is one) into arguments
460 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000461 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000462 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000463 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000464 fp.write("\t\tif _object:\n")
465 fp.write("\t\t\t_arguments['----'] = _object\n")
466 else:
467 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000468 fp.write("\n")
469 #
Jack Jansen73215141995-10-09 23:09:23 +0000470 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000471 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000472 for a in arguments:
473 if is_enum(a[2]):
474 kname = a[1]
475 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000476 if ename <> '****':
477 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen6aa92c52000-08-20 21:59:03 +0000478 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000479 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000480 fp.write("\n")
481 #
482 # Do the transaction
483 #
Jack Jansen84264771995-10-03 14:35:58 +0000484 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
485 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000486 #
487 # Error handling
488 #
Jack Jansen84264771995-10-03 14:35:58 +0000489 fp.write("\t\tif _arguments.has_key('errn'):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000490 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000491 fp.write("\t\t# XXXX Optionally decode result\n")
492 #
493 # Decode result
494 #
Jack Jansen84264771995-10-03 14:35:58 +0000495 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000496 if is_enum(returns):
497 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000498 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000499 fp.write("\n")
500
501# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
502# print "# returns", compiledata(returns)
503# print "# accepts", compiledata(accepts)
504# for arg in arguments:
505# compileargument(arg)
506
507def compileargument(arg):
508 [name, keyword, what] = arg
509 print "# %s (%s)" % (name, `keyword`), compiledata(what)
510
Jack Jansen12b2b762000-08-20 19:30:56 +0000511def findenumsinevent(event, enumsneeded):
512 """Find all enums for a single event"""
513 [name, desc, code, subcode, returns, accepts, arguments] = event
514 for a in arguments:
515 if is_enum(a[2]):
516 ename = a[2][0]
517 if ename <> '****':
518 enumsneeded[ename] = 1
519
520#
521# This class stores the code<->name translations for a single module. It is used
522# to keep the information while we're compiling the module, but we also keep these objects
523# around so if one suite refers to, say, an enum in another suite we know where to
524# find it. Finally, if we really can't find a code, the user can add modules by
525# hand.
526#
527class CodeNameMapper:
528
529 def __init__(self):
530 self.code2name = {
531 "property" : {},
532 "class" : {},
533 "enum" : {},
534 "comparison" : {},
535 }
536 self.name2code = {
537 "property" : {},
538 "class" : {},
539 "enum" : {},
540 "comparison" : {},
541 }
542 self.modulename = None
543 self.star_imported = 0
544
545 def addnamecode(self, type, name, code):
546 self.name2code[type][name] = code
547 if not self.code2name[type].has_key(code):
548 self.code2name[type][code] = name
549
550 def hasname(self, type, name):
551 return self.name2code[type].has_key(name)
552
553 def hascode(self, type, code):
554 return self.code2name[type].has_key(code)
555
556 def findcodename(self, type, code):
557 if not self.hascode(type, code):
558 return None, None, None
559 name = self.code2name[type][code]
560 if self.modulename and not self.star_imported:
561 qualname = '%s.%s'%(self.modulename, name)
562 else:
563 qualname = name
564 return name, qualname, self.modulename
565
566 def getall(self, type):
567 return self.code2name[type].items()
568
569 def addmodule(self, module, name, star_imported):
570 self.modulename = name
571 self.star_imported = star_imported
572 for code, name in module._propdeclarations.items():
573 self.addnamecode('property', name, code)
574 for code, name in module._classdeclarations.items():
575 self.addnamecode('class', name, code)
576 for code in module._enumdeclarations.keys():
577 self.addnamecode('enum', '_Enum_'+identify(code), code)
578 for code, name in module._compdeclarations.items():
579 self.addnamecode('comparison', name, code)
580
581 def prepareforexport(self, name=None):
582 if not self.modulename:
583 self.modulename = name
584 return self
585
Jack Jansen66544221997-08-08 14:49:02 +0000586class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000587 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000588 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000589 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000590 self.namemappers = [CodeNameMapper()]
591 if othernamemappers:
592 self.othernamemappers = othernamemappers[:]
593 else:
594 self.othernamemappers = []
595 if basesuite:
596 basemapper = CodeNameMapper()
597 basemapper.addmodule(basesuite, '', 1)
598 self.namemappers.append(basemapper)
599
600 def getprecompinfo(self, modname):
601 list = []
602 for mapper in self.namemappers:
603 emapper = mapper.prepareforexport(modname)
604 if emapper:
605 list.append(emapper)
606 return list
Jack Jansen66544221997-08-08 14:49:02 +0000607
608 def findcodename(self, type, code):
609 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000610 # First try: check whether we already know about this code.
611 for mapper in self.namemappers:
612 if mapper.hascode(type, code):
613 return mapper.findcodename(type, code)
614 # Second try: maybe one of the other modules knows about it.
615 for mapper in self.othernamemappers:
616 if mapper.hascode(type, code):
617 self.othernamemappers.remove(mapper)
618 self.namemappers.append(mapper)
619 if self.fp:
620 self.fp.write("import %s\n"%mapper.modulename)
621 break
622 else:
623 # If all this has failed we ask the user for a guess on where it could
624 # be and retry.
625 if self.fp:
626 m = self.askdefinitionmodule(type, code)
627 else:
628 m = None
629 if not m: return None, None, None
630 mapper = CodeNameMapper()
631 mapper.addmodule(m, m.__name__, 0)
632 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000633
634 def askdefinitionmodule(self, type, code):
635 fss, ok = macfs.PromptGetFile('Where is %s %s declared?'%(type, code))
636 if not ok: return
637 path, file = os.path.split(fss.as_pathname())
638 modname = os.path.splitext(file)[0]
639 if not path in sys.path:
640 sys.path.insert(0, path)
641 m = __import__(modname)
642 self.fp.write("import %s\n"%modname)
643 return m
644
645 def compileclass(self, cls):
646 [name, code, desc, properties, elements] = cls
647 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000648 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000649 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000650 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
651 if self.fp:
652 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000653 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000654 if self.fp:
655 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
656 self.fp.write('\t"""%s - %s """\n' % (name, desc))
657 self.fp.write('\twant = %s\n' % `code`)
658 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000659 for prop in properties:
660 self.compileproperty(prop)
661 for elem in elements:
662 self.compileelement(elem)
663
664 def compileproperty(self, prop):
665 [name, code, what] = prop
666 if code == 'c@#!':
667 # Something silly with plurals. Skip it.
668 return
669 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000670 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000671 # plural forms and such
672 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000673 if pname == othername:
674 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000675 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000676 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000677 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000678 if self.fp:
679 self.fp.write("class %s(aetools.NProperty):\n" % pname)
680 self.fp.write('\t"""%s - %s """\n' % (name, what[1]))
681 self.fp.write("\twhich = %s\n" % `code`)
682 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000683 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000684
685 def compileelement(self, elem):
686 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000687 if self.fp:
688 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000689
Jack Jansen66544221997-08-08 14:49:02 +0000690 def fillclasspropsandelems(self, cls):
691 [name, code, desc, properties, elements] = cls
692 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000693 if self.namemappers[0].hascode('class', code) and \
694 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000695 # This is an other name (plural or so) for something else. Skip.
696 return
697 plist = []
698 elist = []
699 for prop in properties:
700 [pname, pcode, what] = prop
701 if pcode == 'c@#!':
702 continue
703 pname = identify(pname)
704 plist.append(pname)
705 for elem in elements:
706 [ecode, keyform] = elem
707 if ecode == 'c@#!':
708 continue
709 name, ename, module = self.findcodename('class', ecode)
710 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000711 if self.fp:
712 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000713 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000714 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000715
716 if self.fp:
717 self.fp.write("%s._propdict = {\n"%cname)
718 for n in plist:
719 self.fp.write("\t'%s' : %s,\n"%(n, n))
720 self.fp.write("}\n")
721 self.fp.write("%s._elemdict = {\n"%cname)
722 for n, fulln in elist:
723 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
724 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000725
726 def compilecomparison(self, comp):
727 [name, code, comment] = comp
728 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000729 self.namemappers[0].addnamecode('comparison', iname, code)
730 if self.fp:
731 self.fp.write("class %s(aetools.NComparison):\n" % iname)
732 self.fp.write('\t"""%s - %s """\n' % (name, comment))
Jack Jansen66544221997-08-08 14:49:02 +0000733
734 def compileenumeration(self, enum):
735 [code, items] = enum
736 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000737 if self.fp:
738 self.fp.write("%s = {\n" % name)
739 for item in items:
740 self.compileenumerator(item)
741 self.fp.write("}\n\n")
742 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000743 return code
744
745 def compileenumerator(self, item):
746 [name, code, desc] = item
Jack Jansen6aa92c52000-08-20 21:59:03 +0000747 self.fp.write("\t%s : %s,\t# %s\n" % (`name`, `code`, desc))
Jack Jansen66544221997-08-08 14:49:02 +0000748
749 def checkforenum(self, enum):
750 """This enum code is used by an event. Make sure it's available"""
751 name, fullname, module = self.findcodename('enum', enum)
752 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000753 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000754 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), enum))
Jack Jansen66544221997-08-08 14:49:02 +0000755 return
756 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000757 if self.fp:
758 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000759
760 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000761 if not self.fp:
762 return
Jack Jansen66544221997-08-08 14:49:02 +0000763 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
764 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000765 for k, v in self.namemappers[0].getall('class'):
766 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000767 self.fp.write("}\n")
768 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000769 for k, v in self.namemappers[0].getall('property'):
770 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000771 self.fp.write("}\n")
772 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000773 for k, v in self.namemappers[0].getall('comparison'):
774 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000775 self.fp.write("}\n")
776 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000777 for k, v in self.namemappers[0].getall('enum'):
778 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000779 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000780
781def compiledata(data):
782 [type, description, flags] = data
783 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
784
785def is_null(data):
786 return data[0] == 'null'
787
788def is_optional(data):
789 return (data[2] & 0x8000)
790
791def is_enum(data):
792 return (data[2] & 0x2000)
793
794def getdatadoc(data):
795 [type, descr, flags] = data
796 if descr:
797 return descr
798 if type == '****':
799 return 'anything'
800 if type == 'obj ':
801 return 'an AE object reference'
802 return "undocumented, typecode %s"%`type`
803
804dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
805def compiledataflags(flags):
806 bits = []
807 for i in range(16):
808 if flags & (1<<i):
809 if i in dataflagdict.keys():
810 bits.append(dataflagdict[i])
811 else:
812 bits.append(`i`)
813 return '[%s]' % string.join(bits)
814
815# XXXX Do we have a set of python keywords somewhere?
Jack Jansen66544221997-08-08 14:49:02 +0000816illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
817 "def" ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000818
819def identify(str):
820 """Turn any string into an identifier:
821 - replace space by _
822 - replace other illegal chars by _xx_ (hex code)
823 - prepend _ if the result is a python keyword
824 """
Jack Jansen66544221997-08-08 14:49:02 +0000825 if not str:
826 return "_empty_ae_name"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000827 rv = ''
828 ok = string.letters + '_'
829 ok2 = ok + string.digits
830 for c in str:
831 if c in ok:
832 rv = rv + c
833 elif c == ' ':
834 rv = rv + '_'
835 else:
836 rv = rv + '_%02.2x_'%ord(c)
837 ok = ok2
838 if rv in illegal_ids:
839 rv = '_' + rv
840 return rv
841
842# Call the main program
843
844if __name__ == '__main__':
845 main()
846 sys.exit(1)