blob: 5a4c806ab18ca89d027c8435deb140f50140af7f [file] [log] [blame]
Jack Jansen5ccd8261995-07-17 11:43:20 +00001"""
2gensuitemodule - Generate an AE suite module from an aete/aeut resource
3
Jack Jansen60762cb2000-08-17 22:11:45 +00004Based on aete.py.
5
6Reading and understanding this code is left as an exercise to the reader.
Jack Jansen5ccd8261995-07-17 11:43:20 +00007"""
8
9import MacOS
10import os
11import string
12import sys
13import types
14import StringIO
15import macfs
Jack Jansenb2ecc2c2002-01-24 22:44:07 +000016import keyword
Jack Jansen5ccd8261995-07-17 11:43:20 +000017
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000018from Carbon.Res import *
Jack Jansen5ccd8261995-07-17 11:43:20 +000019
20def main():
Jack Jansen9d19a911995-08-14 12:14:55 +000021 fss, ok = macfs.PromptGetFile('Select file with aeut/aete resource:')
Jack Jansen5ccd8261995-07-17 11:43:20 +000022 if not ok:
23 sys.exit(0)
Jack Jansen60762cb2000-08-17 22:11:45 +000024 processfile(fss.as_pathname())
Jack Jansen5ccd8261995-07-17 11:43:20 +000025
Jack Jansen60762cb2000-08-17 22:11:45 +000026def processfile(fullname):
Jack Jansen5ccd8261995-07-17 11:43:20 +000027 """Process all resources in a single file"""
28 cur = CurResFile()
29 print fullname
30 rf = OpenRFPerm(fullname, 0, 1)
31 try:
32 UseResFile(rf)
33 resources = []
34 for i in range(Count1Resources('aete')):
35 res = Get1IndResource('aete', 1+i)
36 resources.append(res)
37 for i in range(Count1Resources('aeut')):
38 res = Get1IndResource('aeut', 1+i)
39 resources.append(res)
40 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +000041 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +000042 for res in resources:
43 print "decoding", res.GetResInfo(), "..."
44 data = res.data
45 aete = decode(data)
Jack Jansen60762cb2000-08-17 22:11:45 +000046 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +000047 finally:
48 if rf <> cur:
49 CloseResFile(rf)
50 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +000051 # switch back (needed for dialogs in Python)
52 UseResFile(cur)
53 compileaetelist(aetelist, fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000054
Jack Jansen60762cb2000-08-17 22:11:45 +000055def compileaetelist(aetelist, fullname):
56 for aete, resinfo in aetelist:
57 compileaete(aete, resinfo, fullname)
58
Jack Jansen5ccd8261995-07-17 11:43:20 +000059def decode(data):
60 """Decode a resource into a python data structure"""
61 f = StringIO.StringIO(data)
62 aete = generic(getaete, f)
63 aete = simplify(aete)
64 processed = f.tell()
65 unprocessed = len(f.read())
66 total = f.tell()
67 if unprocessed:
68 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
69 (processed, unprocessed, total))
70 return aete
71
72def simplify(item):
73 """Recursively replace singleton tuples by their constituent item"""
74 if type(item) is types.ListType:
75 return map(simplify, item)
76 elif type(item) == types.TupleType and len(item) == 2:
77 return simplify(item[1])
78 else:
79 return item
80
81
82# Here follows the aete resource decoder.
83# It is presented bottom-up instead of top-down because there are direct
84# references to the lower-level part-decoders from the high-level part-decoders.
85
86def getbyte(f, *args):
87 c = f.read(1)
88 if not c:
89 raise EOFError, 'in getbyte' + str(args)
90 return ord(c)
91
92def getword(f, *args):
93 getalign(f)
94 s = f.read(2)
95 if len(s) < 2:
96 raise EOFError, 'in getword' + str(args)
97 return (ord(s[0])<<8) | ord(s[1])
98
99def getlong(f, *args):
100 getalign(f)
101 s = f.read(4)
102 if len(s) < 4:
103 raise EOFError, 'in getlong' + str(args)
104 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
105
106def getostype(f, *args):
107 getalign(f)
108 s = f.read(4)
109 if len(s) < 4:
110 raise EOFError, 'in getostype' + str(args)
111 return s
112
113def getpstr(f, *args):
114 c = f.read(1)
115 if len(c) < 1:
116 raise EOFError, 'in getpstr[1]' + str(args)
117 nbytes = ord(c)
118 if nbytes == 0: return ''
119 s = f.read(nbytes)
120 if len(s) < nbytes:
121 raise EOFError, 'in getpstr[2]' + str(args)
122 return s
123
124def getalign(f):
125 if f.tell() & 1:
126 c = f.read(1)
127 ##if c <> '\0':
128 ## print 'align:', `c`
129
130def getlist(f, description, getitem):
131 count = getword(f)
132 list = []
133 for i in range(count):
134 list.append(generic(getitem, f))
135 getalign(f)
136 return list
137
138def alt_generic(what, f, *args):
139 print "generic", `what`, args
140 res = vageneric(what, f, args)
141 print '->', `res`
142 return res
143
144def generic(what, f, *args):
145 if type(what) == types.FunctionType:
146 return apply(what, (f,) + args)
147 if type(what) == types.ListType:
148 record = []
149 for thing in what:
150 item = apply(generic, thing[:1] + (f,) + thing[1:])
151 record.append((thing[1], item))
152 return record
153 return "BAD GENERIC ARGS: %s" % `what`
154
155getdata = [
156 (getostype, "type"),
157 (getpstr, "description"),
158 (getword, "flags")
159 ]
160getargument = [
161 (getpstr, "name"),
162 (getostype, "keyword"),
163 (getdata, "what")
164 ]
165getevent = [
166 (getpstr, "name"),
167 (getpstr, "description"),
168 (getostype, "suite code"),
169 (getostype, "event code"),
170 (getdata, "returns"),
171 (getdata, "accepts"),
172 (getlist, "optional arguments", getargument)
173 ]
174getproperty = [
175 (getpstr, "name"),
176 (getostype, "code"),
177 (getdata, "what")
178 ]
179getelement = [
180 (getostype, "type"),
181 (getlist, "keyform", getostype)
182 ]
183getclass = [
184 (getpstr, "name"),
185 (getostype, "class code"),
186 (getpstr, "description"),
187 (getlist, "properties", getproperty),
188 (getlist, "elements", getelement)
189 ]
190getcomparison = [
191 (getpstr, "operator name"),
192 (getostype, "operator ID"),
193 (getpstr, "operator comment"),
194 ]
195getenumerator = [
196 (getpstr, "enumerator name"),
197 (getostype, "enumerator ID"),
198 (getpstr, "enumerator comment")
199 ]
200getenumeration = [
201 (getostype, "enumeration ID"),
202 (getlist, "enumerator", getenumerator)
203 ]
204getsuite = [
205 (getpstr, "suite name"),
206 (getpstr, "suite description"),
207 (getostype, "suite ID"),
208 (getword, "suite level"),
209 (getword, "suite version"),
210 (getlist, "events", getevent),
211 (getlist, "classes", getclass),
212 (getlist, "comparisons", getcomparison),
213 (getlist, "enumerations", getenumeration)
214 ]
215getaete = [
216 (getword, "major/minor version in BCD"),
217 (getword, "language code"),
218 (getword, "script code"),
219 (getlist, "suites", getsuite)
220 ]
221
Jack Jansen60762cb2000-08-17 22:11:45 +0000222def compileaete(aete, resinfo, fname):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000223 """Generate code for a full aete resource. fname passed for doc purposes"""
224 [version, language, script, suites] = aete
225 major, minor = divmod(version, 256)
Jack Jansen60762cb2000-08-17 22:11:45 +0000226 fss = macfs.FSSpec(fname)
227 creatorsignature, dummy = fss.GetCreatorType()
228 packagename = identify(os.path.basename(fname))
229 if language:
230 packagename = packagename+'_lang%d'%language
231 if script:
232 packagename = packagename+'_script%d'%script
233 if len(packagename) > 27:
234 packagename = packagename[:27]
235 macfs.SetFolder(os.path.join(sys.prefix, ':Mac:Lib:lib-scriptpackages'))
236 fss, ok = macfs.GetDirectory('Package folder for %s'%packagename)
237 if not ok:
238 return
239 pathname = fss.as_pathname()
240 packagename = os.path.split(os.path.normpath(pathname))[1]
241 fss, ok = macfs.GetDirectory('Package folder for base suite (usually StdSuites)')
242 if ok:
243 dirname, basepkgname = os.path.split(os.path.normpath(fss.as_pathname()))
244 if not dirname in sys.path:
245 sys.path.insert(0, dirname)
246 basepackage = __import__(basepkgname)
247 else:
248 basepackage = None
249 macfs.SetFolder(pathname)
250 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000251 allprecompinfo = []
252 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000253 for suite in suites:
Jack Jansen12b2b762000-08-20 19:30:56 +0000254 code, suite, fss, modname, precompinfo = precompilesuite(suite, basepackage)
255 if not code:
256 continue
257 allprecompinfo = allprecompinfo + precompinfo
258 suiteinfo = suite, fss, modname
259 suitelist.append((code, modname))
260 allsuites.append(suiteinfo)
261 for suiteinfo in allsuites:
262 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage, allprecompinfo)
Jack Jansen60762cb2000-08-17 22:11:45 +0000263 fss, ok = macfs.StandardPutFile('Package module', '__init__.py')
264 if not ok:
265 return
266 fp = open(fss.as_pathname(), 'w')
267 fss.SetCreatorType('Pyth', 'TEXT')
268 fp.write('"""\n')
269 fp.write("Package generated from %s\n"%fname)
270 fp.write("Resource %s resid %d %s\n"%(resinfo[1], resinfo[0], resinfo[2]))
271 fp.write('"""\n')
272 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000273 fp.write('Error = aetools.Error\n')
Jack Jansen60762cb2000-08-17 22:11:45 +0000274 for code, modname in suitelist:
275 fp.write("import %s\n" % modname)
276 fp.write("\n\n_code_to_module = {\n")
277 for code, modname in suitelist:
278 fp.write("\t'%s' : %s,\n"%(code, modname))
279 fp.write("}\n\n")
280 fp.write("\n\n_code_to_fullname = {\n")
281 for code, modname in suitelist:
Jack Jansen12b2b762000-08-20 19:30:56 +0000282 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(code, packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000283 fp.write("}\n\n")
284 for code, modname in suitelist:
285 fp.write("from %s import *\n"%modname)
286 if suitelist:
287 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
288 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000289 fp.write(",\n\t\t%s_Events"%modname)
290 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen60762cb2000-08-17 22:11:45 +0000291 fp.write("\t_signature = '%s'\n\n"%creatorsignature)
292 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000293
294def precompilesuite(suite, basepackage=None):
295 """Parse a single suite without generating the output. This step is needed
296 so we can resolve recursive references by suites to enums/comps/etc declared
297 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000298 [name, desc, code, level, version, events, classes, comps, enums] = suite
299
300 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000301 if len(modname) > 28:
302 modname = modname[:27]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000303 fss, ok = macfs.StandardPutFile('Python output file', modname+'.py')
304 if not ok:
Jack Jansen12b2b762000-08-20 19:30:56 +0000305 return None, None, None, None, None
306
Jack Jansen60762cb2000-08-17 22:11:45 +0000307 pathname = fss.as_pathname()
308 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000309
310 if basepackage and basepackage._code_to_module.has_key(code):
311 # We are an extension of a baseclass (usually an application extending
312 # Standard_Suite or so). Import everything from our base module
313 basemodule = basepackage._code_to_module[code]
314 else:
315 # We are not an extension.
316 basemodule = None
317
318 enumsneeded = {}
319 for event in events:
320 findenumsinevent(event, enumsneeded)
321
322 objc = ObjectCompiler(None, basemodule)
323 for cls in classes:
324 objc.compileclass(cls)
325 for cls in classes:
326 objc.fillclasspropsandelems(cls)
327 for comp in comps:
328 objc.compilecomparison(comp)
329 for enum in enums:
330 objc.compileenumeration(enum)
331
332 for enum in enumsneeded.keys():
333 objc.checkforenum(enum)
334
335 objc.dumpindex()
336
337 precompinfo = objc.getprecompinfo(modname)
338
339 return code, suite, fss, modname, precompinfo
340
341def compilesuite((suite, fss, modname), major, minor, language, script, fname, basepackage, precompinfo):
342 """Generate code for a single suite"""
343 [name, desc, code, level, version, events, classes, comps, enums] = suite
344
345 pathname = fss.as_pathname()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000346 fp = open(fss.as_pathname(), 'w')
Jack Jansen18a99f51996-03-18 13:35:00 +0000347 fss.SetCreatorType('Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000348
349 fp.write('"""Suite %s: %s\n' % (name, desc))
350 fp.write("Level %d, version %d\n\n" % (level, version))
351 fp.write("Generated from %s\n"%fname)
352 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
353 (major, minor, language, script))
354 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000355
356 fp.write('import aetools\n')
357 fp.write('import MacOS\n\n')
358 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000359 if basepackage and basepackage._code_to_module.has_key(code):
360 # We are an extension of a baseclass (usually an application extending
361 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000362 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000363 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000364 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
365 # This is needed by CodeWarrior and some others.
366 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
367 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000368 else:
369 # We are not an extension.
370 basemodule = None
371 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000372
373 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000374 if events:
375 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000376 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000377 else:
378 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000379
Jack Jansen12b2b762000-08-20 19:30:56 +0000380 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000381 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000382 objc.compileclass(cls)
383 for cls in classes:
384 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000385 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000386 objc.compilecomparison(comp)
387 for enum in enums:
388 objc.compileenumeration(enum)
389
390 for enum in enumsneeded.keys():
391 objc.checkforenum(enum)
392
393 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000394
395 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000396
Jack Jansen60762cb2000-08-17 22:11:45 +0000397def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000398 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000399 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000400 if module:
401 modshortname = string.split(module.__name__, '.')[-1]
402 baseclassname = '%s_Events'%modshortname
403 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000404 else:
405 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000406
Jack Jansen66544221997-08-08 14:49:02 +0000407def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000408 """Generate code for a single event"""
409 [name, desc, code, subcode, returns, accepts, arguments] = event
410 funcname = identify(name)
411 #
412 # generate name->keyword map
413 #
414 if arguments:
415 fp.write("\t_argmap_%s = {\n"%funcname)
416 for a in arguments:
417 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
418 fp.write("\t}\n\n")
419
420 #
421 # Generate function header
422 #
423 has_arg = (not is_null(accepts))
424 opt_arg = (has_arg and is_optional(accepts))
425
Jack Jansen84264771995-10-03 14:35:58 +0000426 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000427 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000428 if not opt_arg:
429 fp.write("_object, ") # Include direct object, if it has one
430 else:
431 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000432 else:
Jack Jansen84264771995-10-03 14:35:58 +0000433 fp.write("_no_object=None, ") # For argument checking
434 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000435 #
436 # Generate doc string (important, since it may be the only
437 # available documentation, due to our name-remaping)
438 #
439 fp.write('\t\t"""%s: %s\n'%(name, desc))
440 if has_arg:
441 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
442 elif opt_arg:
443 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
444 for arg in arguments:
445 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
446 getdatadoc(arg[2])))
447 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
448 if not is_null(returns):
449 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
450 fp.write('\t\t"""\n')
451 #
452 # Fiddle the args so everything ends up in 'arguments' dictionary
453 #
454 fp.write("\t\t_code = %s\n"% `code`)
455 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000456 #
457 # Do keyword name substitution
458 #
459 if arguments:
460 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
461 else:
462 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
463 #
464 # Stuff required arg (if there is one) into arguments
465 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000466 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000467 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000468 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000469 fp.write("\t\tif _object:\n")
470 fp.write("\t\t\t_arguments['----'] = _object\n")
471 else:
472 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000473 fp.write("\n")
474 #
Jack Jansen73215141995-10-09 23:09:23 +0000475 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000476 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000477 for a in arguments:
478 if is_enum(a[2]):
479 kname = a[1]
480 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000481 if ename <> '****':
482 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen6aa92c52000-08-20 21:59:03 +0000483 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000484 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000485 fp.write("\n")
486 #
487 # Do the transaction
488 #
Jack Jansen84264771995-10-03 14:35:58 +0000489 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
490 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000491 #
492 # Error handling
493 #
Jack Jansen84264771995-10-03 14:35:58 +0000494 fp.write("\t\tif _arguments.has_key('errn'):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000495 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000496 fp.write("\t\t# XXXX Optionally decode result\n")
497 #
498 # Decode result
499 #
Jack Jansen84264771995-10-03 14:35:58 +0000500 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000501 if is_enum(returns):
502 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000503 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000504 fp.write("\n")
505
506# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
507# print "# returns", compiledata(returns)
508# print "# accepts", compiledata(accepts)
509# for arg in arguments:
510# compileargument(arg)
511
512def compileargument(arg):
513 [name, keyword, what] = arg
514 print "# %s (%s)" % (name, `keyword`), compiledata(what)
515
Jack Jansen12b2b762000-08-20 19:30:56 +0000516def findenumsinevent(event, enumsneeded):
517 """Find all enums for a single event"""
518 [name, desc, code, subcode, returns, accepts, arguments] = event
519 for a in arguments:
520 if is_enum(a[2]):
521 ename = a[2][0]
522 if ename <> '****':
523 enumsneeded[ename] = 1
524
525#
526# This class stores the code<->name translations for a single module. It is used
527# to keep the information while we're compiling the module, but we also keep these objects
528# around so if one suite refers to, say, an enum in another suite we know where to
529# find it. Finally, if we really can't find a code, the user can add modules by
530# hand.
531#
532class CodeNameMapper:
533
534 def __init__(self):
535 self.code2name = {
536 "property" : {},
537 "class" : {},
538 "enum" : {},
539 "comparison" : {},
540 }
541 self.name2code = {
542 "property" : {},
543 "class" : {},
544 "enum" : {},
545 "comparison" : {},
546 }
547 self.modulename = None
548 self.star_imported = 0
549
550 def addnamecode(self, type, name, code):
551 self.name2code[type][name] = code
552 if not self.code2name[type].has_key(code):
553 self.code2name[type][code] = name
554
555 def hasname(self, type, name):
556 return self.name2code[type].has_key(name)
557
558 def hascode(self, type, code):
559 return self.code2name[type].has_key(code)
560
561 def findcodename(self, type, code):
562 if not self.hascode(type, code):
563 return None, None, None
564 name = self.code2name[type][code]
565 if self.modulename and not self.star_imported:
566 qualname = '%s.%s'%(self.modulename, name)
567 else:
568 qualname = name
569 return name, qualname, self.modulename
570
571 def getall(self, type):
572 return self.code2name[type].items()
573
574 def addmodule(self, module, name, star_imported):
575 self.modulename = name
576 self.star_imported = star_imported
577 for code, name in module._propdeclarations.items():
578 self.addnamecode('property', name, code)
579 for code, name in module._classdeclarations.items():
580 self.addnamecode('class', name, code)
581 for code in module._enumdeclarations.keys():
582 self.addnamecode('enum', '_Enum_'+identify(code), code)
583 for code, name in module._compdeclarations.items():
584 self.addnamecode('comparison', name, code)
585
586 def prepareforexport(self, name=None):
587 if not self.modulename:
588 self.modulename = name
589 return self
590
Jack Jansen66544221997-08-08 14:49:02 +0000591class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000592 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000593 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000594 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000595 self.namemappers = [CodeNameMapper()]
596 if othernamemappers:
597 self.othernamemappers = othernamemappers[:]
598 else:
599 self.othernamemappers = []
600 if basesuite:
601 basemapper = CodeNameMapper()
602 basemapper.addmodule(basesuite, '', 1)
603 self.namemappers.append(basemapper)
604
605 def getprecompinfo(self, modname):
606 list = []
607 for mapper in self.namemappers:
608 emapper = mapper.prepareforexport(modname)
609 if emapper:
610 list.append(emapper)
611 return list
Jack Jansen66544221997-08-08 14:49:02 +0000612
613 def findcodename(self, type, code):
614 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000615 # First try: check whether we already know about this code.
616 for mapper in self.namemappers:
617 if mapper.hascode(type, code):
618 return mapper.findcodename(type, code)
619 # Second try: maybe one of the other modules knows about it.
620 for mapper in self.othernamemappers:
621 if mapper.hascode(type, code):
622 self.othernamemappers.remove(mapper)
623 self.namemappers.append(mapper)
624 if self.fp:
625 self.fp.write("import %s\n"%mapper.modulename)
626 break
627 else:
628 # If all this has failed we ask the user for a guess on where it could
629 # be and retry.
630 if self.fp:
631 m = self.askdefinitionmodule(type, code)
632 else:
633 m = None
634 if not m: return None, None, None
635 mapper = CodeNameMapper()
636 mapper.addmodule(m, m.__name__, 0)
637 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000638
639 def askdefinitionmodule(self, type, code):
640 fss, ok = macfs.PromptGetFile('Where is %s %s declared?'%(type, code))
641 if not ok: return
642 path, file = os.path.split(fss.as_pathname())
643 modname = os.path.splitext(file)[0]
644 if not path in sys.path:
645 sys.path.insert(0, path)
646 m = __import__(modname)
647 self.fp.write("import %s\n"%modname)
648 return m
649
650 def compileclass(self, cls):
651 [name, code, desc, properties, elements] = cls
652 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000653 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000654 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000655 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
656 if self.fp:
657 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000658 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000659 if self.fp:
660 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
661 self.fp.write('\t"""%s - %s """\n' % (name, desc))
662 self.fp.write('\twant = %s\n' % `code`)
663 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000664 for prop in properties:
665 self.compileproperty(prop)
666 for elem in elements:
667 self.compileelement(elem)
668
669 def compileproperty(self, prop):
670 [name, code, what] = prop
671 if code == 'c@#!':
672 # Something silly with plurals. Skip it.
673 return
674 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000675 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000676 # plural forms and such
677 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000678 if pname == othername:
679 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000680 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000681 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000682 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000683 if self.fp:
684 self.fp.write("class %s(aetools.NProperty):\n" % pname)
685 self.fp.write('\t"""%s - %s """\n' % (name, what[1]))
686 self.fp.write("\twhich = %s\n" % `code`)
687 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000688 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000689
690 def compileelement(self, elem):
691 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000692 if self.fp:
693 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000694
Jack Jansen66544221997-08-08 14:49:02 +0000695 def fillclasspropsandelems(self, cls):
696 [name, code, desc, properties, elements] = cls
697 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000698 if self.namemappers[0].hascode('class', code) and \
699 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000700 # This is an other name (plural or so) for something else. Skip.
701 return
702 plist = []
703 elist = []
704 for prop in properties:
705 [pname, pcode, what] = prop
706 if pcode == 'c@#!':
707 continue
708 pname = identify(pname)
709 plist.append(pname)
710 for elem in elements:
711 [ecode, keyform] = elem
712 if ecode == 'c@#!':
713 continue
714 name, ename, module = self.findcodename('class', ecode)
715 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000716 if self.fp:
717 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000718 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000719 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000720
721 if self.fp:
722 self.fp.write("%s._propdict = {\n"%cname)
723 for n in plist:
724 self.fp.write("\t'%s' : %s,\n"%(n, n))
725 self.fp.write("}\n")
726 self.fp.write("%s._elemdict = {\n"%cname)
727 for n, fulln in elist:
728 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
729 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000730
731 def compilecomparison(self, comp):
732 [name, code, comment] = comp
733 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000734 self.namemappers[0].addnamecode('comparison', iname, code)
735 if self.fp:
736 self.fp.write("class %s(aetools.NComparison):\n" % iname)
737 self.fp.write('\t"""%s - %s """\n' % (name, comment))
Jack Jansen66544221997-08-08 14:49:02 +0000738
739 def compileenumeration(self, enum):
740 [code, items] = enum
741 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000742 if self.fp:
743 self.fp.write("%s = {\n" % name)
744 for item in items:
745 self.compileenumerator(item)
746 self.fp.write("}\n\n")
747 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000748 return code
749
750 def compileenumerator(self, item):
751 [name, code, desc] = item
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000752 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc))
Jack Jansen66544221997-08-08 14:49:02 +0000753
754 def checkforenum(self, enum):
755 """This enum code is used by an event. Make sure it's available"""
756 name, fullname, module = self.findcodename('enum', enum)
757 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000758 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000759 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), enum))
Jack Jansen66544221997-08-08 14:49:02 +0000760 return
761 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000762 if self.fp:
763 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000764
765 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000766 if not self.fp:
767 return
Jack Jansen66544221997-08-08 14:49:02 +0000768 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
769 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000770 for k, v in self.namemappers[0].getall('class'):
771 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000772 self.fp.write("}\n")
773 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000774 for k, v in self.namemappers[0].getall('property'):
775 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000776 self.fp.write("}\n")
777 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000778 for k, v in self.namemappers[0].getall('comparison'):
779 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000780 self.fp.write("}\n")
781 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000782 for k, v in self.namemappers[0].getall('enum'):
783 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000784 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000785
786def compiledata(data):
787 [type, description, flags] = data
788 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
789
790def is_null(data):
791 return data[0] == 'null'
792
793def is_optional(data):
794 return (data[2] & 0x8000)
795
796def is_enum(data):
797 return (data[2] & 0x2000)
798
799def getdatadoc(data):
800 [type, descr, flags] = data
801 if descr:
802 return descr
803 if type == '****':
804 return 'anything'
805 if type == 'obj ':
806 return 'an AE object reference'
807 return "undocumented, typecode %s"%`type`
808
809dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
810def compiledataflags(flags):
811 bits = []
812 for i in range(16):
813 if flags & (1<<i):
814 if i in dataflagdict.keys():
815 bits.append(dataflagdict[i])
816 else:
817 bits.append(`i`)
818 return '[%s]' % string.join(bits)
819
Jack Jansen5ccd8261995-07-17 11:43:20 +0000820def identify(str):
821 """Turn any string into an identifier:
822 - replace space by _
823 - replace other illegal chars by _xx_ (hex code)
824 - prepend _ if the result is a python keyword
825 """
Jack Jansen66544221997-08-08 14:49:02 +0000826 if not str:
827 return "_empty_ae_name"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000828 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000829 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000830 ok2 = ok + string.digits
831 for c in str:
832 if c in ok:
833 rv = rv + c
834 elif c == ' ':
835 rv = rv + '_'
836 else:
837 rv = rv + '_%02.2x_'%ord(c)
838 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000839 if keyword.iskeyword(rv):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000840 rv = '_' + rv
841 return rv
842
843# Call the main program
844
845if __name__ == '__main__':
846 main()
847 sys.exit(1)
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000848print identify('for')