blob: df1e4553ca1879aaeeefe3a29b27150ca3715890 [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')
272 for code, modname in suitelist:
273 fp.write("import %s\n" % modname)
274 fp.write("\n\n_code_to_module = {\n")
275 for code, modname in suitelist:
276 fp.write("\t'%s' : %s,\n"%(code, modname))
277 fp.write("}\n\n")
278 fp.write("\n\n_code_to_fullname = {\n")
279 for code, modname in suitelist:
Jack Jansen12b2b762000-08-20 19:30:56 +0000280 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(code, packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000281 fp.write("}\n\n")
282 for code, modname in suitelist:
283 fp.write("from %s import *\n"%modname)
284 if suitelist:
285 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
286 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000287 fp.write(",\n\t\t%s_Events"%modname)
288 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen60762cb2000-08-17 22:11:45 +0000289 fp.write("\t_signature = '%s'\n\n"%creatorsignature)
290 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000291
292def precompilesuite(suite, basepackage=None):
293 """Parse a single suite without generating the output. This step is needed
294 so we can resolve recursive references by suites to enums/comps/etc declared
295 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000296 [name, desc, code, level, version, events, classes, comps, enums] = suite
297
298 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000299 if len(modname) > 28:
300 modname = modname[:27]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000301 fss, ok = macfs.StandardPutFile('Python output file', modname+'.py')
302 if not ok:
Jack Jansen12b2b762000-08-20 19:30:56 +0000303 return None, None, None, None, None
304
Jack Jansen60762cb2000-08-17 22:11:45 +0000305 pathname = fss.as_pathname()
306 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000307
308 if basepackage and basepackage._code_to_module.has_key(code):
309 # We are an extension of a baseclass (usually an application extending
310 # Standard_Suite or so). Import everything from our base module
311 basemodule = basepackage._code_to_module[code]
312 else:
313 # We are not an extension.
314 basemodule = None
315
316 enumsneeded = {}
317 for event in events:
318 findenumsinevent(event, enumsneeded)
319
320 objc = ObjectCompiler(None, basemodule)
321 for cls in classes:
322 objc.compileclass(cls)
323 for cls in classes:
324 objc.fillclasspropsandelems(cls)
325 for comp in comps:
326 objc.compilecomparison(comp)
327 for enum in enums:
328 objc.compileenumeration(enum)
329
330 for enum in enumsneeded.keys():
331 objc.checkforenum(enum)
332
333 objc.dumpindex()
334
335 precompinfo = objc.getprecompinfo(modname)
336
337 return code, suite, fss, modname, precompinfo
338
339def compilesuite((suite, fss, modname), major, minor, language, script, fname, basepackage, precompinfo):
340 """Generate code for a single suite"""
341 [name, desc, code, level, version, events, classes, comps, enums] = suite
342
343 pathname = fss.as_pathname()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000344 fp = open(fss.as_pathname(), 'w')
Jack Jansen18a99f51996-03-18 13:35:00 +0000345 fss.SetCreatorType('Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000346
347 fp.write('"""Suite %s: %s\n' % (name, desc))
348 fp.write("Level %d, version %d\n\n" % (level, version))
349 fp.write("Generated from %s\n"%fname)
350 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
351 (major, minor, language, script))
352 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000353
354 fp.write('import aetools\n')
355 fp.write('import MacOS\n\n')
356 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000357 if basepackage and basepackage._code_to_module.has_key(code):
358 # We are an extension of a baseclass (usually an application extending
359 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000360 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000361 basemodule = basepackage._code_to_module[code]
362 else:
363 # We are not an extension.
364 basemodule = None
365 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000366
367 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000368 if events:
369 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000370 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000371 else:
372 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000373
Jack Jansen12b2b762000-08-20 19:30:56 +0000374 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000375 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000376 objc.compileclass(cls)
377 for cls in classes:
378 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000379 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000380 objc.compilecomparison(comp)
381 for enum in enums:
382 objc.compileenumeration(enum)
383
384 for enum in enumsneeded.keys():
385 objc.checkforenum(enum)
386
387 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000388
389 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000390
Jack Jansen60762cb2000-08-17 22:11:45 +0000391def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000392 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000393 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000394 if module:
395 modshortname = string.split(module.__name__, '.')[-1]
396 baseclassname = '%s_Events'%modshortname
397 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000398 else:
399 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000400
Jack Jansen66544221997-08-08 14:49:02 +0000401def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000402 """Generate code for a single event"""
403 [name, desc, code, subcode, returns, accepts, arguments] = event
404 funcname = identify(name)
405 #
406 # generate name->keyword map
407 #
408 if arguments:
409 fp.write("\t_argmap_%s = {\n"%funcname)
410 for a in arguments:
411 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
412 fp.write("\t}\n\n")
413
414 #
415 # Generate function header
416 #
417 has_arg = (not is_null(accepts))
418 opt_arg = (has_arg and is_optional(accepts))
419
Jack Jansen84264771995-10-03 14:35:58 +0000420 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000421 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000422 if not opt_arg:
423 fp.write("_object, ") # Include direct object, if it has one
424 else:
425 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000426 else:
Jack Jansen84264771995-10-03 14:35:58 +0000427 fp.write("_no_object=None, ") # For argument checking
428 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000429 #
430 # Generate doc string (important, since it may be the only
431 # available documentation, due to our name-remaping)
432 #
433 fp.write('\t\t"""%s: %s\n'%(name, desc))
434 if has_arg:
435 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
436 elif opt_arg:
437 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
438 for arg in arguments:
439 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
440 getdatadoc(arg[2])))
441 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
442 if not is_null(returns):
443 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
444 fp.write('\t\t"""\n')
445 #
446 # Fiddle the args so everything ends up in 'arguments' dictionary
447 #
448 fp.write("\t\t_code = %s\n"% `code`)
449 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000450 #
451 # Do keyword name substitution
452 #
453 if arguments:
454 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
455 else:
456 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
457 #
458 # Stuff required arg (if there is one) into arguments
459 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000460 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000461 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000462 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000463 fp.write("\t\tif _object:\n")
464 fp.write("\t\t\t_arguments['----'] = _object\n")
465 else:
466 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000467 fp.write("\n")
468 #
Jack Jansen73215141995-10-09 23:09:23 +0000469 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000470 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000471 for a in arguments:
472 if is_enum(a[2]):
473 kname = a[1]
474 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000475 if ename <> '****':
476 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen5ccd8261995-07-17 11:43:20 +0000477 (`kname`, ename))
Jack Jansen66544221997-08-08 14:49:02 +0000478 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000479 fp.write("\n")
480 #
481 # Do the transaction
482 #
Jack Jansen84264771995-10-03 14:35:58 +0000483 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
484 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000485 #
486 # Error handling
487 #
Jack Jansen84264771995-10-03 14:35:58 +0000488 fp.write("\t\tif _arguments.has_key('errn'):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000489 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000490 fp.write("\t\t# XXXX Optionally decode result\n")
491 #
492 # Decode result
493 #
Jack Jansen84264771995-10-03 14:35:58 +0000494 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000495 if is_enum(returns):
496 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000497 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000498 fp.write("\n")
499
500# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
501# print "# returns", compiledata(returns)
502# print "# accepts", compiledata(accepts)
503# for arg in arguments:
504# compileargument(arg)
505
506def compileargument(arg):
507 [name, keyword, what] = arg
508 print "# %s (%s)" % (name, `keyword`), compiledata(what)
509
Jack Jansen12b2b762000-08-20 19:30:56 +0000510def findenumsinevent(event, enumsneeded):
511 """Find all enums for a single event"""
512 [name, desc, code, subcode, returns, accepts, arguments] = event
513 for a in arguments:
514 if is_enum(a[2]):
515 ename = a[2][0]
516 if ename <> '****':
517 enumsneeded[ename] = 1
518
519#
520# This class stores the code<->name translations for a single module. It is used
521# to keep the information while we're compiling the module, but we also keep these objects
522# around so if one suite refers to, say, an enum in another suite we know where to
523# find it. Finally, if we really can't find a code, the user can add modules by
524# hand.
525#
526class CodeNameMapper:
527
528 def __init__(self):
529 self.code2name = {
530 "property" : {},
531 "class" : {},
532 "enum" : {},
533 "comparison" : {},
534 }
535 self.name2code = {
536 "property" : {},
537 "class" : {},
538 "enum" : {},
539 "comparison" : {},
540 }
541 self.modulename = None
542 self.star_imported = 0
543
544 def addnamecode(self, type, name, code):
545 self.name2code[type][name] = code
546 if not self.code2name[type].has_key(code):
547 self.code2name[type][code] = name
548
549 def hasname(self, type, name):
550 return self.name2code[type].has_key(name)
551
552 def hascode(self, type, code):
553 return self.code2name[type].has_key(code)
554
555 def findcodename(self, type, code):
556 if not self.hascode(type, code):
557 return None, None, None
558 name = self.code2name[type][code]
559 if self.modulename and not self.star_imported:
560 qualname = '%s.%s'%(self.modulename, name)
561 else:
562 qualname = name
563 return name, qualname, self.modulename
564
565 def getall(self, type):
566 return self.code2name[type].items()
567
568 def addmodule(self, module, name, star_imported):
569 self.modulename = name
570 self.star_imported = star_imported
571 for code, name in module._propdeclarations.items():
572 self.addnamecode('property', name, code)
573 for code, name in module._classdeclarations.items():
574 self.addnamecode('class', name, code)
575 for code in module._enumdeclarations.keys():
576 self.addnamecode('enum', '_Enum_'+identify(code), code)
577 for code, name in module._compdeclarations.items():
578 self.addnamecode('comparison', name, code)
579
580 def prepareforexport(self, name=None):
581 if not self.modulename:
582 self.modulename = name
583 return self
584
Jack Jansen66544221997-08-08 14:49:02 +0000585class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000586 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000587 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000588 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000589 self.namemappers = [CodeNameMapper()]
590 if othernamemappers:
591 self.othernamemappers = othernamemappers[:]
592 else:
593 self.othernamemappers = []
594 if basesuite:
595 basemapper = CodeNameMapper()
596 basemapper.addmodule(basesuite, '', 1)
597 self.namemappers.append(basemapper)
598
599 def getprecompinfo(self, modname):
600 list = []
601 for mapper in self.namemappers:
602 emapper = mapper.prepareforexport(modname)
603 if emapper:
604 list.append(emapper)
605 return list
Jack Jansen66544221997-08-08 14:49:02 +0000606
607 def findcodename(self, type, code):
608 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000609 # First try: check whether we already know about this code.
610 for mapper in self.namemappers:
611 if mapper.hascode(type, code):
612 return mapper.findcodename(type, code)
613 # Second try: maybe one of the other modules knows about it.
614 for mapper in self.othernamemappers:
615 if mapper.hascode(type, code):
616 self.othernamemappers.remove(mapper)
617 self.namemappers.append(mapper)
618 if self.fp:
619 self.fp.write("import %s\n"%mapper.modulename)
620 break
621 else:
622 # If all this has failed we ask the user for a guess on where it could
623 # be and retry.
624 if self.fp:
625 m = self.askdefinitionmodule(type, code)
626 else:
627 m = None
628 if not m: return None, None, None
629 mapper = CodeNameMapper()
630 mapper.addmodule(m, m.__name__, 0)
631 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000632
633 def askdefinitionmodule(self, type, code):
634 fss, ok = macfs.PromptGetFile('Where is %s %s declared?'%(type, code))
635 if not ok: return
636 path, file = os.path.split(fss.as_pathname())
637 modname = os.path.splitext(file)[0]
638 if not path in sys.path:
639 sys.path.insert(0, path)
640 m = __import__(modname)
641 self.fp.write("import %s\n"%modname)
642 return m
643
644 def compileclass(self, cls):
645 [name, code, desc, properties, elements] = cls
646 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000647 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000648 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000649 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
650 if self.fp:
651 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000652 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000653 if self.fp:
654 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
655 self.fp.write('\t"""%s - %s """\n' % (name, desc))
656 self.fp.write('\twant = %s\n' % `code`)
657 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000658 for prop in properties:
659 self.compileproperty(prop)
660 for elem in elements:
661 self.compileelement(elem)
662
663 def compileproperty(self, prop):
664 [name, code, what] = prop
665 if code == 'c@#!':
666 # Something silly with plurals. Skip it.
667 return
668 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000669 if self.namemappers[0].hascode('property', code):
670 # XXXX Why don't we handle these the same as classes??
671 if self.fp:
672 self.fp.write("# repeated property %s %s\n"%(pname, what[1]))
Jack Jansen66544221997-08-08 14:49:02 +0000673 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000674 if self.fp:
675 self.fp.write("class %s(aetools.NProperty):\n" % pname)
676 self.fp.write('\t"""%s - %s """\n' % (name, what[1]))
677 self.fp.write("\twhich = %s\n" % `code`)
678 self.fp.write("\twant = %s\n" % `what[0]`)
679 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000680
681 def compileelement(self, elem):
682 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000683 if self.fp:
684 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000685
Jack Jansen66544221997-08-08 14:49:02 +0000686 def fillclasspropsandelems(self, cls):
687 [name, code, desc, properties, elements] = cls
688 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000689 if self.namemappers[0].hascode('class', code) and \
690 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000691 # This is an other name (plural or so) for something else. Skip.
692 return
693 plist = []
694 elist = []
695 for prop in properties:
696 [pname, pcode, what] = prop
697 if pcode == 'c@#!':
698 continue
699 pname = identify(pname)
700 plist.append(pname)
701 for elem in elements:
702 [ecode, keyform] = elem
703 if ecode == 'c@#!':
704 continue
705 name, ename, module = self.findcodename('class', ecode)
706 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000707 if self.fp:
708 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000709 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000710 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000711
712 if self.fp:
713 self.fp.write("%s._propdict = {\n"%cname)
714 for n in plist:
715 self.fp.write("\t'%s' : %s,\n"%(n, n))
716 self.fp.write("}\n")
717 self.fp.write("%s._elemdict = {\n"%cname)
718 for n, fulln in elist:
719 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
720 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000721
722 def compilecomparison(self, comp):
723 [name, code, comment] = comp
724 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000725 self.namemappers[0].addnamecode('comparison', iname, code)
726 if self.fp:
727 self.fp.write("class %s(aetools.NComparison):\n" % iname)
728 self.fp.write('\t"""%s - %s """\n' % (name, comment))
Jack Jansen66544221997-08-08 14:49:02 +0000729
730 def compileenumeration(self, enum):
731 [code, items] = enum
732 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000733 if self.fp:
734 self.fp.write("%s = {\n" % name)
735 for item in items:
736 self.compileenumerator(item)
737 self.fp.write("}\n\n")
738 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000739 return code
740
741 def compileenumerator(self, item):
742 [name, code, desc] = item
743 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc))
744
745 def checkforenum(self, enum):
746 """This enum code is used by an event. Make sure it's available"""
747 name, fullname, module = self.findcodename('enum', enum)
748 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000749 if self.fp:
750 self.fp.write("# XXXX enum %s not found!!\n"%(enum))
Jack Jansen66544221997-08-08 14:49:02 +0000751 return
752 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000753 if self.fp:
754 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000755
756 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000757 if not self.fp:
758 return
Jack Jansen66544221997-08-08 14:49:02 +0000759 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
760 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000761 for k, v in self.namemappers[0].getall('class'):
762 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000763 self.fp.write("}\n")
764 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000765 for k, v in self.namemappers[0].getall('property'):
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_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000769 for k, v in self.namemappers[0].getall('comparison'):
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_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000773 for k, v in self.namemappers[0].getall('enum'):
774 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000775 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000776
777def compiledata(data):
778 [type, description, flags] = data
779 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
780
781def is_null(data):
782 return data[0] == 'null'
783
784def is_optional(data):
785 return (data[2] & 0x8000)
786
787def is_enum(data):
788 return (data[2] & 0x2000)
789
790def getdatadoc(data):
791 [type, descr, flags] = data
792 if descr:
793 return descr
794 if type == '****':
795 return 'anything'
796 if type == 'obj ':
797 return 'an AE object reference'
798 return "undocumented, typecode %s"%`type`
799
800dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
801def compiledataflags(flags):
802 bits = []
803 for i in range(16):
804 if flags & (1<<i):
805 if i in dataflagdict.keys():
806 bits.append(dataflagdict[i])
807 else:
808 bits.append(`i`)
809 return '[%s]' % string.join(bits)
810
811# XXXX Do we have a set of python keywords somewhere?
Jack Jansen66544221997-08-08 14:49:02 +0000812illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
813 "def" ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000814
815def identify(str):
816 """Turn any string into an identifier:
817 - replace space by _
818 - replace other illegal chars by _xx_ (hex code)
819 - prepend _ if the result is a python keyword
820 """
Jack Jansen66544221997-08-08 14:49:02 +0000821 if not str:
822 return "_empty_ae_name"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000823 rv = ''
824 ok = string.letters + '_'
825 ok2 = ok + string.digits
826 for c in str:
827 if c in ok:
828 rv = rv + c
829 elif c == ' ':
830 rv = rv + '_'
831 else:
832 rv = rv + '_%02.2x_'%ord(c)
833 ok = ok2
834 if rv in illegal_ids:
835 rv = '_' + rv
836 return rv
837
838# Call the main program
839
840if __name__ == '__main__':
841 main()
842 sys.exit(1)