blob: d9bcdc6edcdf724434c72e7ec0079d679ab34806 [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
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000017from Carbon.Res import *
Jack Jansen5ccd8261995-07-17 11:43:20 +000018
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]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000363 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
364 # This is needed by CodeWarrior and some others.
365 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
366 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000367 else:
368 # We are not an extension.
369 basemodule = None
370 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000371
372 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000373 if events:
374 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000375 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000376 else:
377 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000378
Jack Jansen12b2b762000-08-20 19:30:56 +0000379 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000380 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000381 objc.compileclass(cls)
382 for cls in classes:
383 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000384 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000385 objc.compilecomparison(comp)
386 for enum in enums:
387 objc.compileenumeration(enum)
388
389 for enum in enumsneeded.keys():
390 objc.checkforenum(enum)
391
392 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000393
394 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000395
Jack Jansen60762cb2000-08-17 22:11:45 +0000396def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000397 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000398 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000399 if module:
400 modshortname = string.split(module.__name__, '.')[-1]
401 baseclassname = '%s_Events'%modshortname
402 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000403 else:
404 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000405
Jack Jansen66544221997-08-08 14:49:02 +0000406def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000407 """Generate code for a single event"""
408 [name, desc, code, subcode, returns, accepts, arguments] = event
409 funcname = identify(name)
410 #
411 # generate name->keyword map
412 #
413 if arguments:
414 fp.write("\t_argmap_%s = {\n"%funcname)
415 for a in arguments:
416 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
417 fp.write("\t}\n\n")
418
419 #
420 # Generate function header
421 #
422 has_arg = (not is_null(accepts))
423 opt_arg = (has_arg and is_optional(accepts))
424
Jack Jansen84264771995-10-03 14:35:58 +0000425 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000426 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000427 if not opt_arg:
428 fp.write("_object, ") # Include direct object, if it has one
429 else:
430 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000431 else:
Jack Jansen84264771995-10-03 14:35:58 +0000432 fp.write("_no_object=None, ") # For argument checking
433 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000434 #
435 # Generate doc string (important, since it may be the only
436 # available documentation, due to our name-remaping)
437 #
438 fp.write('\t\t"""%s: %s\n'%(name, desc))
439 if has_arg:
440 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
441 elif opt_arg:
442 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
443 for arg in arguments:
444 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
445 getdatadoc(arg[2])))
446 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
447 if not is_null(returns):
448 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
449 fp.write('\t\t"""\n')
450 #
451 # Fiddle the args so everything ends up in 'arguments' dictionary
452 #
453 fp.write("\t\t_code = %s\n"% `code`)
454 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000455 #
456 # Do keyword name substitution
457 #
458 if arguments:
459 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
460 else:
461 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
462 #
463 # Stuff required arg (if there is one) into arguments
464 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000465 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000466 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000467 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000468 fp.write("\t\tif _object:\n")
469 fp.write("\t\t\t_arguments['----'] = _object\n")
470 else:
471 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000472 fp.write("\n")
473 #
Jack Jansen73215141995-10-09 23:09:23 +0000474 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000475 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000476 for a in arguments:
477 if is_enum(a[2]):
478 kname = a[1]
479 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000480 if ename <> '****':
481 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen6aa92c52000-08-20 21:59:03 +0000482 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000483 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000484 fp.write("\n")
485 #
486 # Do the transaction
487 #
Jack Jansen84264771995-10-03 14:35:58 +0000488 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
489 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000490 #
491 # Error handling
492 #
Jack Jansen84264771995-10-03 14:35:58 +0000493 fp.write("\t\tif _arguments.has_key('errn'):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000494 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000495 fp.write("\t\t# XXXX Optionally decode result\n")
496 #
497 # Decode result
498 #
Jack Jansen84264771995-10-03 14:35:58 +0000499 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000500 if is_enum(returns):
501 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000502 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000503 fp.write("\n")
504
505# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
506# print "# returns", compiledata(returns)
507# print "# accepts", compiledata(accepts)
508# for arg in arguments:
509# compileargument(arg)
510
511def compileargument(arg):
512 [name, keyword, what] = arg
513 print "# %s (%s)" % (name, `keyword`), compiledata(what)
514
Jack Jansen12b2b762000-08-20 19:30:56 +0000515def findenumsinevent(event, enumsneeded):
516 """Find all enums for a single event"""
517 [name, desc, code, subcode, returns, accepts, arguments] = event
518 for a in arguments:
519 if is_enum(a[2]):
520 ename = a[2][0]
521 if ename <> '****':
522 enumsneeded[ename] = 1
523
524#
525# This class stores the code<->name translations for a single module. It is used
526# to keep the information while we're compiling the module, but we also keep these objects
527# around so if one suite refers to, say, an enum in another suite we know where to
528# find it. Finally, if we really can't find a code, the user can add modules by
529# hand.
530#
531class CodeNameMapper:
532
533 def __init__(self):
534 self.code2name = {
535 "property" : {},
536 "class" : {},
537 "enum" : {},
538 "comparison" : {},
539 }
540 self.name2code = {
541 "property" : {},
542 "class" : {},
543 "enum" : {},
544 "comparison" : {},
545 }
546 self.modulename = None
547 self.star_imported = 0
548
549 def addnamecode(self, type, name, code):
550 self.name2code[type][name] = code
551 if not self.code2name[type].has_key(code):
552 self.code2name[type][code] = name
553
554 def hasname(self, type, name):
555 return self.name2code[type].has_key(name)
556
557 def hascode(self, type, code):
558 return self.code2name[type].has_key(code)
559
560 def findcodename(self, type, code):
561 if not self.hascode(type, code):
562 return None, None, None
563 name = self.code2name[type][code]
564 if self.modulename and not self.star_imported:
565 qualname = '%s.%s'%(self.modulename, name)
566 else:
567 qualname = name
568 return name, qualname, self.modulename
569
570 def getall(self, type):
571 return self.code2name[type].items()
572
573 def addmodule(self, module, name, star_imported):
574 self.modulename = name
575 self.star_imported = star_imported
576 for code, name in module._propdeclarations.items():
577 self.addnamecode('property', name, code)
578 for code, name in module._classdeclarations.items():
579 self.addnamecode('class', name, code)
580 for code in module._enumdeclarations.keys():
581 self.addnamecode('enum', '_Enum_'+identify(code), code)
582 for code, name in module._compdeclarations.items():
583 self.addnamecode('comparison', name, code)
584
585 def prepareforexport(self, name=None):
586 if not self.modulename:
587 self.modulename = name
588 return self
589
Jack Jansen66544221997-08-08 14:49:02 +0000590class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000591 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000592 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000593 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000594 self.namemappers = [CodeNameMapper()]
595 if othernamemappers:
596 self.othernamemappers = othernamemappers[:]
597 else:
598 self.othernamemappers = []
599 if basesuite:
600 basemapper = CodeNameMapper()
601 basemapper.addmodule(basesuite, '', 1)
602 self.namemappers.append(basemapper)
603
604 def getprecompinfo(self, modname):
605 list = []
606 for mapper in self.namemappers:
607 emapper = mapper.prepareforexport(modname)
608 if emapper:
609 list.append(emapper)
610 return list
Jack Jansen66544221997-08-08 14:49:02 +0000611
612 def findcodename(self, type, code):
613 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000614 # First try: check whether we already know about this code.
615 for mapper in self.namemappers:
616 if mapper.hascode(type, code):
617 return mapper.findcodename(type, code)
618 # Second try: maybe one of the other modules knows about it.
619 for mapper in self.othernamemappers:
620 if mapper.hascode(type, code):
621 self.othernamemappers.remove(mapper)
622 self.namemappers.append(mapper)
623 if self.fp:
624 self.fp.write("import %s\n"%mapper.modulename)
625 break
626 else:
627 # If all this has failed we ask the user for a guess on where it could
628 # be and retry.
629 if self.fp:
630 m = self.askdefinitionmodule(type, code)
631 else:
632 m = None
633 if not m: return None, None, None
634 mapper = CodeNameMapper()
635 mapper.addmodule(m, m.__name__, 0)
636 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000637
638 def askdefinitionmodule(self, type, code):
639 fss, ok = macfs.PromptGetFile('Where is %s %s declared?'%(type, code))
640 if not ok: return
641 path, file = os.path.split(fss.as_pathname())
642 modname = os.path.splitext(file)[0]
643 if not path in sys.path:
644 sys.path.insert(0, path)
645 m = __import__(modname)
646 self.fp.write("import %s\n"%modname)
647 return m
648
649 def compileclass(self, cls):
650 [name, code, desc, properties, elements] = cls
651 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000652 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000653 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000654 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
655 if self.fp:
656 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000657 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000658 if self.fp:
659 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
660 self.fp.write('\t"""%s - %s """\n' % (name, desc))
661 self.fp.write('\twant = %s\n' % `code`)
662 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000663 for prop in properties:
664 self.compileproperty(prop)
665 for elem in elements:
666 self.compileelement(elem)
667
668 def compileproperty(self, prop):
669 [name, code, what] = prop
670 if code == 'c@#!':
671 # Something silly with plurals. Skip it.
672 return
673 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000674 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000675 # plural forms and such
676 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000677 if pname == othername:
678 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000679 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000680 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000681 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000682 if self.fp:
683 self.fp.write("class %s(aetools.NProperty):\n" % pname)
684 self.fp.write('\t"""%s - %s """\n' % (name, what[1]))
685 self.fp.write("\twhich = %s\n" % `code`)
686 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000687 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000688
689 def compileelement(self, elem):
690 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000691 if self.fp:
692 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000693
Jack Jansen66544221997-08-08 14:49:02 +0000694 def fillclasspropsandelems(self, cls):
695 [name, code, desc, properties, elements] = cls
696 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000697 if self.namemappers[0].hascode('class', code) and \
698 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000699 # This is an other name (plural or so) for something else. Skip.
700 return
701 plist = []
702 elist = []
703 for prop in properties:
704 [pname, pcode, what] = prop
705 if pcode == 'c@#!':
706 continue
707 pname = identify(pname)
708 plist.append(pname)
709 for elem in elements:
710 [ecode, keyform] = elem
711 if ecode == 'c@#!':
712 continue
713 name, ename, module = self.findcodename('class', ecode)
714 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000715 if self.fp:
716 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000717 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000718 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000719
720 if self.fp:
721 self.fp.write("%s._propdict = {\n"%cname)
722 for n in plist:
723 self.fp.write("\t'%s' : %s,\n"%(n, n))
724 self.fp.write("}\n")
725 self.fp.write("%s._elemdict = {\n"%cname)
726 for n, fulln in elist:
727 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
728 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000729
730 def compilecomparison(self, comp):
731 [name, code, comment] = comp
732 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000733 self.namemappers[0].addnamecode('comparison', iname, code)
734 if self.fp:
735 self.fp.write("class %s(aetools.NComparison):\n" % iname)
736 self.fp.write('\t"""%s - %s """\n' % (name, comment))
Jack Jansen66544221997-08-08 14:49:02 +0000737
738 def compileenumeration(self, enum):
739 [code, items] = enum
740 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000741 if self.fp:
742 self.fp.write("%s = {\n" % name)
743 for item in items:
744 self.compileenumerator(item)
745 self.fp.write("}\n\n")
746 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000747 return code
748
749 def compileenumerator(self, item):
750 [name, code, desc] = item
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000751 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc))
Jack Jansen66544221997-08-08 14:49:02 +0000752
753 def checkforenum(self, enum):
754 """This enum code is used by an event. Make sure it's available"""
755 name, fullname, module = self.findcodename('enum', enum)
756 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000757 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000758 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), enum))
Jack Jansen66544221997-08-08 14:49:02 +0000759 return
760 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000761 if self.fp:
762 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000763
764 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000765 if not self.fp:
766 return
Jack Jansen66544221997-08-08 14:49:02 +0000767 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
768 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000769 for k, v in self.namemappers[0].getall('class'):
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_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000773 for k, v in self.namemappers[0].getall('property'):
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_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000777 for k, v in self.namemappers[0].getall('comparison'):
778 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000779 self.fp.write("}\n")
780 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000781 for k, v in self.namemappers[0].getall('enum'):
782 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000783 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000784
785def compiledata(data):
786 [type, description, flags] = data
787 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
788
789def is_null(data):
790 return data[0] == 'null'
791
792def is_optional(data):
793 return (data[2] & 0x8000)
794
795def is_enum(data):
796 return (data[2] & 0x2000)
797
798def getdatadoc(data):
799 [type, descr, flags] = data
800 if descr:
801 return descr
802 if type == '****':
803 return 'anything'
804 if type == 'obj ':
805 return 'an AE object reference'
806 return "undocumented, typecode %s"%`type`
807
808dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
809def compiledataflags(flags):
810 bits = []
811 for i in range(16):
812 if flags & (1<<i):
813 if i in dataflagdict.keys():
814 bits.append(dataflagdict[i])
815 else:
816 bits.append(`i`)
817 return '[%s]' % string.join(bits)
818
819# XXXX Do we have a set of python keywords somewhere?
Jack Jansen66544221997-08-08 14:49:02 +0000820illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
821 "def" ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000822
823def identify(str):
824 """Turn any string into an identifier:
825 - replace space by _
826 - replace other illegal chars by _xx_ (hex code)
827 - prepend _ if the result is a python keyword
828 """
Jack Jansen66544221997-08-08 14:49:02 +0000829 if not str:
830 return "_empty_ae_name"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000831 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000832 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000833 ok2 = ok + string.digits
834 for c in str:
835 if c in ok:
836 rv = rv + c
837 elif c == ' ':
838 rv = rv + '_'
839 else:
840 rv = rv + '_%02.2x_'%ord(c)
841 ok = ok2
842 if rv in illegal_ids:
843 rv = '_' + rv
844 return rv
845
846# Call the main program
847
848if __name__ == '__main__':
849 main()
850 sys.exit(1)