blob: 6b17a7980860a5587dda58dff42127e22b5ca814 [file] [log] [blame]
Jack Jansen5ccd8261995-07-17 11:43:20 +00001"""
2gensuitemodule - Generate an AE suite module from an aete/aeut resource
3
Jack Jansen60762cb2000-08-17 22:11:45 +00004Based on aete.py.
5
6Reading and understanding this code is left as an exercise to the reader.
Jack Jansen5ccd8261995-07-17 11:43:20 +00007"""
8
9import MacOS
10import os
11import string
12import sys
13import types
14import StringIO
15import macfs
Jack Jansenb2ecc2c2002-01-24 22:44:07 +000016import keyword
Jack Jansen40926062002-03-30 23:43:04 +000017import macresource
Jack Jansen8b777672002-08-07 14:49:00 +000018from aetools import unpack
Jack Jansen5ccd8261995-07-17 11:43:20 +000019
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000020from Carbon.Res import *
Jack Jansen5ccd8261995-07-17 11:43:20 +000021
Jack Jansen40926062002-03-30 23:43:04 +000022DEFAULT_PACKAGEFOLDER=os.path.join(sys.prefix, 'Mac', 'Lib', 'lib-scriptpackages')
23
Jack Jansen5ccd8261995-07-17 11:43:20 +000024def main():
Jack Jansen40926062002-03-30 23:43:04 +000025 if len(sys.argv) > 1:
26 for filename in sys.argv[1:]:
27 processfile(filename)
28 else:
29 fss, ok = macfs.PromptGetFile('Select file with aeut/aete resource:')
30 if not ok:
31 sys.exit(0)
32 processfile(fss.as_pathname())
Jack Jansen5ccd8261995-07-17 11:43:20 +000033
Jack Jansen60762cb2000-08-17 22:11:45 +000034def processfile(fullname):
Jack Jansen5ccd8261995-07-17 11:43:20 +000035 """Process all resources in a single file"""
36 cur = CurResFile()
Jack Jansen40926062002-03-30 23:43:04 +000037 print "Processing", fullname
38 rf = macresource.open_pathname(fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000039 try:
40 UseResFile(rf)
41 resources = []
42 for i in range(Count1Resources('aete')):
43 res = Get1IndResource('aete', 1+i)
44 resources.append(res)
45 for i in range(Count1Resources('aeut')):
46 res = Get1IndResource('aeut', 1+i)
47 resources.append(res)
48 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +000049 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +000050 for res in resources:
51 print "decoding", res.GetResInfo(), "..."
52 data = res.data
53 aete = decode(data)
Jack Jansen60762cb2000-08-17 22:11:45 +000054 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +000055 finally:
56 if rf <> cur:
57 CloseResFile(rf)
58 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +000059 # switch back (needed for dialogs in Python)
60 UseResFile(cur)
61 compileaetelist(aetelist, fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000062
Jack Jansen60762cb2000-08-17 22:11:45 +000063def compileaetelist(aetelist, fullname):
64 for aete, resinfo in aetelist:
65 compileaete(aete, resinfo, fullname)
66
Jack Jansen5ccd8261995-07-17 11:43:20 +000067def decode(data):
68 """Decode a resource into a python data structure"""
69 f = StringIO.StringIO(data)
70 aete = generic(getaete, f)
71 aete = simplify(aete)
72 processed = f.tell()
73 unprocessed = len(f.read())
74 total = f.tell()
75 if unprocessed:
76 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
77 (processed, unprocessed, total))
78 return aete
79
80def simplify(item):
81 """Recursively replace singleton tuples by their constituent item"""
82 if type(item) is types.ListType:
83 return map(simplify, item)
84 elif type(item) == types.TupleType and len(item) == 2:
85 return simplify(item[1])
86 else:
87 return item
88
89
90# Here follows the aete resource decoder.
91# It is presented bottom-up instead of top-down because there are direct
92# references to the lower-level part-decoders from the high-level part-decoders.
93
94def getbyte(f, *args):
95 c = f.read(1)
96 if not c:
97 raise EOFError, 'in getbyte' + str(args)
98 return ord(c)
99
100def getword(f, *args):
101 getalign(f)
102 s = f.read(2)
103 if len(s) < 2:
104 raise EOFError, 'in getword' + str(args)
105 return (ord(s[0])<<8) | ord(s[1])
106
107def getlong(f, *args):
108 getalign(f)
109 s = f.read(4)
110 if len(s) < 4:
111 raise EOFError, 'in getlong' + str(args)
112 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
113
114def getostype(f, *args):
115 getalign(f)
116 s = f.read(4)
117 if len(s) < 4:
118 raise EOFError, 'in getostype' + str(args)
119 return s
120
121def getpstr(f, *args):
122 c = f.read(1)
123 if len(c) < 1:
124 raise EOFError, 'in getpstr[1]' + str(args)
125 nbytes = ord(c)
126 if nbytes == 0: return ''
127 s = f.read(nbytes)
128 if len(s) < nbytes:
129 raise EOFError, 'in getpstr[2]' + str(args)
130 return s
131
132def getalign(f):
133 if f.tell() & 1:
134 c = f.read(1)
135 ##if c <> '\0':
136 ## print 'align:', `c`
137
138def getlist(f, description, getitem):
139 count = getword(f)
140 list = []
141 for i in range(count):
142 list.append(generic(getitem, f))
143 getalign(f)
144 return list
145
146def alt_generic(what, f, *args):
147 print "generic", `what`, args
148 res = vageneric(what, f, args)
149 print '->', `res`
150 return res
151
152def generic(what, f, *args):
153 if type(what) == types.FunctionType:
154 return apply(what, (f,) + args)
155 if type(what) == types.ListType:
156 record = []
157 for thing in what:
158 item = apply(generic, thing[:1] + (f,) + thing[1:])
159 record.append((thing[1], item))
160 return record
161 return "BAD GENERIC ARGS: %s" % `what`
162
163getdata = [
164 (getostype, "type"),
165 (getpstr, "description"),
166 (getword, "flags")
167 ]
168getargument = [
169 (getpstr, "name"),
170 (getostype, "keyword"),
171 (getdata, "what")
172 ]
173getevent = [
174 (getpstr, "name"),
175 (getpstr, "description"),
176 (getostype, "suite code"),
177 (getostype, "event code"),
178 (getdata, "returns"),
179 (getdata, "accepts"),
180 (getlist, "optional arguments", getargument)
181 ]
182getproperty = [
183 (getpstr, "name"),
184 (getostype, "code"),
185 (getdata, "what")
186 ]
187getelement = [
188 (getostype, "type"),
189 (getlist, "keyform", getostype)
190 ]
191getclass = [
192 (getpstr, "name"),
193 (getostype, "class code"),
194 (getpstr, "description"),
195 (getlist, "properties", getproperty),
196 (getlist, "elements", getelement)
197 ]
198getcomparison = [
199 (getpstr, "operator name"),
200 (getostype, "operator ID"),
201 (getpstr, "operator comment"),
202 ]
203getenumerator = [
204 (getpstr, "enumerator name"),
205 (getostype, "enumerator ID"),
206 (getpstr, "enumerator comment")
207 ]
208getenumeration = [
209 (getostype, "enumeration ID"),
210 (getlist, "enumerator", getenumerator)
211 ]
212getsuite = [
213 (getpstr, "suite name"),
214 (getpstr, "suite description"),
215 (getostype, "suite ID"),
216 (getword, "suite level"),
217 (getword, "suite version"),
218 (getlist, "events", getevent),
219 (getlist, "classes", getclass),
220 (getlist, "comparisons", getcomparison),
221 (getlist, "enumerations", getenumeration)
222 ]
223getaete = [
224 (getword, "major/minor version in BCD"),
225 (getword, "language code"),
226 (getword, "script code"),
227 (getlist, "suites", getsuite)
228 ]
229
Jack Jansen60762cb2000-08-17 22:11:45 +0000230def compileaete(aete, resinfo, fname):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000231 """Generate code for a full aete resource. fname passed for doc purposes"""
232 [version, language, script, suites] = aete
233 major, minor = divmod(version, 256)
Jack Jansen60762cb2000-08-17 22:11:45 +0000234 fss = macfs.FSSpec(fname)
235 creatorsignature, dummy = fss.GetCreatorType()
Jack Jansen40926062002-03-30 23:43:04 +0000236 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000237 if language:
238 packagename = packagename+'_lang%d'%language
239 if script:
240 packagename = packagename+'_script%d'%script
241 if len(packagename) > 27:
242 packagename = packagename[:27]
Jack Jansen40926062002-03-30 23:43:04 +0000243 macfs.SetFolder(DEFAULT_PACKAGEFOLDER)
244 fss, ok = macfs.GetDirectory('Create and select package folder for %s'%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000245 if not ok:
246 return
247 pathname = fss.as_pathname()
248 packagename = os.path.split(os.path.normpath(pathname))[1]
249 fss, ok = macfs.GetDirectory('Package folder for base suite (usually StdSuites)')
250 if ok:
251 dirname, basepkgname = os.path.split(os.path.normpath(fss.as_pathname()))
252 if not dirname in sys.path:
253 sys.path.insert(0, dirname)
254 basepackage = __import__(basepkgname)
255 else:
256 basepackage = None
257 macfs.SetFolder(pathname)
258 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000259 allprecompinfo = []
260 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000261 for suite in suites:
Jack Jansen12b2b762000-08-20 19:30:56 +0000262 code, suite, fss, modname, precompinfo = precompilesuite(suite, basepackage)
263 if not code:
264 continue
265 allprecompinfo = allprecompinfo + precompinfo
266 suiteinfo = suite, fss, modname
267 suitelist.append((code, modname))
268 allsuites.append(suiteinfo)
269 for suiteinfo in allsuites:
270 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage, allprecompinfo)
Jack Jansen60762cb2000-08-17 22:11:45 +0000271 fss, ok = macfs.StandardPutFile('Package module', '__init__.py')
272 if not ok:
273 return
274 fp = open(fss.as_pathname(), 'w')
275 fss.SetCreatorType('Pyth', 'TEXT')
276 fp.write('"""\n')
277 fp.write("Package generated from %s\n"%fname)
Jack Jansen8b777672002-08-07 14:49:00 +0000278 if resinfo:
279 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000280 fp.write('"""\n')
281 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000282 fp.write('Error = aetools.Error\n')
Jack Jansen60762cb2000-08-17 22:11:45 +0000283 for code, modname in suitelist:
284 fp.write("import %s\n" % modname)
285 fp.write("\n\n_code_to_module = {\n")
286 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000287 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000288 fp.write("}\n\n")
289 fp.write("\n\n_code_to_fullname = {\n")
290 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000291 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000292 fp.write("}\n\n")
293 for code, modname in suitelist:
294 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000295
296 # Generate property dicts and element dicts for all types declared in this module
297 fp.write("def getbaseclasses(v):\n")
298 fp.write("\tif hasattr(v, '_superclassnames'):\n")
299 fp.write("\t\tv._propdict = {}\n")
300 fp.write("\t\tv._elemdict = {}\n")
301 fp.write("\t\tfor superclass in v._superclassnames:\n")
302 fp.write("\t\t\tgetbaseclasses(superclass)\n")
303 fp.write("\t\t\tv._propdict.update(getattr(eval(superclass), '_privpropdict'))\n")
304 fp.write("\t\t\tv._elemdict.update(getattr(eval(superclass), '_privelemdict'))\n")
305 fp.write("\t\tv._propdict.update(v._privpropdict)\n")
306 fp.write("\t\tv._elemdict.update(v._privelemdict)\n")
307 fp.write("import StdSuites\n")
308 if allprecompinfo:
309 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
310 for codenamemapper in allprecompinfo:
311 for k, v in codenamemapper.getall('class'):
312 fp.write("getbaseclasses(%s)\n" % v)
313
314 # Generate a code-to-name mapper for all of the types (classes) declared in this module
315 if allprecompinfo:
316 fp.write("\n#\n# Indices of types declared in this module\n#\n")
317 fp.write("_classdeclarations = {\n")
318 for codenamemapper in allprecompinfo:
319 for k, v in codenamemapper.getall('class'):
320 fp.write("\t%s : %s,\n" % (`ascii(k)`, v))
321 fp.write("}\n")
322
Jack Jansen60762cb2000-08-17 22:11:45 +0000323 if suitelist:
324 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
325 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000326 fp.write(",\n\t\t%s_Events"%modname)
327 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000328 fp.write("\t_signature = %s\n\n"%`ascii(creatorsignature)`)
329 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000330 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000331
332def precompilesuite(suite, basepackage=None):
333 """Parse a single suite without generating the output. This step is needed
334 so we can resolve recursive references by suites to enums/comps/etc declared
335 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000336 [name, desc, code, level, version, events, classes, comps, enums] = suite
337
338 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000339 if len(modname) > 28:
340 modname = modname[:27]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000341 fss, ok = macfs.StandardPutFile('Python output file', modname+'.py')
342 if not ok:
Jack Jansen12b2b762000-08-20 19:30:56 +0000343 return None, None, None, None, None
344
Jack Jansen60762cb2000-08-17 22:11:45 +0000345 pathname = fss.as_pathname()
346 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000347
348 if basepackage and basepackage._code_to_module.has_key(code):
349 # We are an extension of a baseclass (usually an application extending
350 # Standard_Suite or so). Import everything from our base module
351 basemodule = basepackage._code_to_module[code]
352 else:
353 # We are not an extension.
354 basemodule = None
355
356 enumsneeded = {}
357 for event in events:
358 findenumsinevent(event, enumsneeded)
359
360 objc = ObjectCompiler(None, basemodule)
361 for cls in classes:
362 objc.compileclass(cls)
363 for cls in classes:
364 objc.fillclasspropsandelems(cls)
365 for comp in comps:
366 objc.compilecomparison(comp)
367 for enum in enums:
368 objc.compileenumeration(enum)
369
370 for enum in enumsneeded.keys():
371 objc.checkforenum(enum)
372
373 objc.dumpindex()
374
375 precompinfo = objc.getprecompinfo(modname)
376
377 return code, suite, fss, modname, precompinfo
378
379def compilesuite((suite, fss, modname), major, minor, language, script, fname, basepackage, precompinfo):
380 """Generate code for a single suite"""
381 [name, desc, code, level, version, events, classes, comps, enums] = suite
382
383 pathname = fss.as_pathname()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000384 fp = open(fss.as_pathname(), 'w')
Jack Jansen18a99f51996-03-18 13:35:00 +0000385 fss.SetCreatorType('Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000386
Jack Jansen8b777672002-08-07 14:49:00 +0000387 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000388 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000389 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000390 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
391 (major, minor, language, script))
392 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000393
394 fp.write('import aetools\n')
395 fp.write('import MacOS\n\n')
Jack Jansen8b777672002-08-07 14:49:00 +0000396 fp.write("_code = %s\n\n"% `ascii(code)`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000397 if basepackage and basepackage._code_to_module.has_key(code):
398 # We are an extension of a baseclass (usually an application extending
399 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000400 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000401 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000402 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
403 # This is needed by CodeWarrior and some others.
404 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
405 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000406 else:
407 # We are not an extension.
408 basemodule = None
409 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000410
411 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000412 if events:
413 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000414 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000415 else:
416 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000417
Jack Jansen12b2b762000-08-20 19:30:56 +0000418 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000419 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000420 objc.compileclass(cls)
421 for cls in classes:
422 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000423 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000424 objc.compilecomparison(comp)
425 for enum in enums:
426 objc.compileenumeration(enum)
427
428 for enum in enumsneeded.keys():
429 objc.checkforenum(enum)
430
431 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000432
433 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000434
Jack Jansen60762cb2000-08-17 22:11:45 +0000435def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000436 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000437 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000438 if module:
439 modshortname = string.split(module.__name__, '.')[-1]
440 baseclassname = '%s_Events'%modshortname
441 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000442 else:
443 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000444
Jack Jansen66544221997-08-08 14:49:02 +0000445def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000446 """Generate code for a single event"""
447 [name, desc, code, subcode, returns, accepts, arguments] = event
448 funcname = identify(name)
449 #
450 # generate name->keyword map
451 #
452 if arguments:
453 fp.write("\t_argmap_%s = {\n"%funcname)
454 for a in arguments:
Jack Jansen8b777672002-08-07 14:49:00 +0000455 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `ascii(a[1])`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000456 fp.write("\t}\n\n")
457
458 #
459 # Generate function header
460 #
461 has_arg = (not is_null(accepts))
462 opt_arg = (has_arg and is_optional(accepts))
463
Jack Jansen84264771995-10-03 14:35:58 +0000464 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000465 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000466 if not opt_arg:
467 fp.write("_object, ") # Include direct object, if it has one
468 else:
469 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000470 else:
Jack Jansen84264771995-10-03 14:35:58 +0000471 fp.write("_no_object=None, ") # For argument checking
472 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000473 #
474 # Generate doc string (important, since it may be the only
475 # available documentation, due to our name-remaping)
476 #
Jack Jansen8b777672002-08-07 14:49:00 +0000477 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000478 if has_arg:
479 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
480 elif opt_arg:
481 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
482 for arg in arguments:
483 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
484 getdatadoc(arg[2])))
485 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
486 if not is_null(returns):
487 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
488 fp.write('\t\t"""\n')
489 #
490 # Fiddle the args so everything ends up in 'arguments' dictionary
491 #
Jack Jansen8b777672002-08-07 14:49:00 +0000492 fp.write("\t\t_code = %s\n"% `ascii(code)`)
493 fp.write("\t\t_subcode = %s\n\n"% `ascii(subcode)`)
Jack Jansen73215141995-10-09 23:09:23 +0000494 #
495 # Do keyword name substitution
496 #
497 if arguments:
498 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
499 else:
500 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
501 #
502 # Stuff required arg (if there is one) into arguments
503 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000504 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000505 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000506 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000507 fp.write("\t\tif _object:\n")
508 fp.write("\t\t\t_arguments['----'] = _object\n")
509 else:
510 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000511 fp.write("\n")
512 #
Jack Jansen73215141995-10-09 23:09:23 +0000513 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000514 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000515 for a in arguments:
516 if is_enum(a[2]):
517 kname = a[1]
518 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000519 if ename <> '****':
520 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen8b777672002-08-07 14:49:00 +0000521 (`ascii(kname)`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000522 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000523 fp.write("\n")
524 #
525 # Do the transaction
526 #
Jack Jansen84264771995-10-03 14:35:58 +0000527 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
528 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000529 #
530 # Error handling
531 #
Jack Jansen18983532002-04-23 21:03:21 +0000532 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000533 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000534 fp.write("\t\t# XXXX Optionally decode result\n")
535 #
536 # Decode result
537 #
Jack Jansen84264771995-10-03 14:35:58 +0000538 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000539 if is_enum(returns):
540 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000541 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000542 fp.write("\n")
543
544# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
545# print "# returns", compiledata(returns)
546# print "# accepts", compiledata(accepts)
547# for arg in arguments:
548# compileargument(arg)
549
550def compileargument(arg):
551 [name, keyword, what] = arg
552 print "# %s (%s)" % (name, `keyword`), compiledata(what)
553
Jack Jansen12b2b762000-08-20 19:30:56 +0000554def findenumsinevent(event, enumsneeded):
555 """Find all enums for a single event"""
556 [name, desc, code, subcode, returns, accepts, arguments] = event
557 for a in arguments:
558 if is_enum(a[2]):
559 ename = a[2][0]
560 if ename <> '****':
561 enumsneeded[ename] = 1
562
563#
564# This class stores the code<->name translations for a single module. It is used
565# to keep the information while we're compiling the module, but we also keep these objects
566# around so if one suite refers to, say, an enum in another suite we know where to
567# find it. Finally, if we really can't find a code, the user can add modules by
568# hand.
569#
570class CodeNameMapper:
571
572 def __init__(self):
573 self.code2name = {
574 "property" : {},
575 "class" : {},
576 "enum" : {},
577 "comparison" : {},
578 }
579 self.name2code = {
580 "property" : {},
581 "class" : {},
582 "enum" : {},
583 "comparison" : {},
584 }
585 self.modulename = None
586 self.star_imported = 0
587
588 def addnamecode(self, type, name, code):
589 self.name2code[type][name] = code
590 if not self.code2name[type].has_key(code):
591 self.code2name[type][code] = name
592
593 def hasname(self, type, name):
594 return self.name2code[type].has_key(name)
595
596 def hascode(self, type, code):
597 return self.code2name[type].has_key(code)
598
599 def findcodename(self, type, code):
600 if not self.hascode(type, code):
601 return None, None, None
602 name = self.code2name[type][code]
603 if self.modulename and not self.star_imported:
604 qualname = '%s.%s'%(self.modulename, name)
605 else:
606 qualname = name
607 return name, qualname, self.modulename
608
609 def getall(self, type):
610 return self.code2name[type].items()
611
612 def addmodule(self, module, name, star_imported):
613 self.modulename = name
614 self.star_imported = star_imported
615 for code, name in module._propdeclarations.items():
616 self.addnamecode('property', name, code)
617 for code, name in module._classdeclarations.items():
618 self.addnamecode('class', name, code)
619 for code in module._enumdeclarations.keys():
620 self.addnamecode('enum', '_Enum_'+identify(code), code)
621 for code, name in module._compdeclarations.items():
622 self.addnamecode('comparison', name, code)
623
624 def prepareforexport(self, name=None):
625 if not self.modulename:
626 self.modulename = name
627 return self
628
Jack Jansen66544221997-08-08 14:49:02 +0000629class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000630 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000631 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000632 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000633 self.namemappers = [CodeNameMapper()]
634 if othernamemappers:
635 self.othernamemappers = othernamemappers[:]
636 else:
637 self.othernamemappers = []
638 if basesuite:
639 basemapper = CodeNameMapper()
640 basemapper.addmodule(basesuite, '', 1)
641 self.namemappers.append(basemapper)
642
643 def getprecompinfo(self, modname):
644 list = []
645 for mapper in self.namemappers:
646 emapper = mapper.prepareforexport(modname)
647 if emapper:
648 list.append(emapper)
649 return list
Jack Jansen66544221997-08-08 14:49:02 +0000650
651 def findcodename(self, type, code):
652 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000653 # First try: check whether we already know about this code.
654 for mapper in self.namemappers:
655 if mapper.hascode(type, code):
656 return mapper.findcodename(type, code)
657 # Second try: maybe one of the other modules knows about it.
658 for mapper in self.othernamemappers:
659 if mapper.hascode(type, code):
660 self.othernamemappers.remove(mapper)
661 self.namemappers.append(mapper)
662 if self.fp:
663 self.fp.write("import %s\n"%mapper.modulename)
664 break
665 else:
666 # If all this has failed we ask the user for a guess on where it could
667 # be and retry.
668 if self.fp:
669 m = self.askdefinitionmodule(type, code)
670 else:
671 m = None
672 if not m: return None, None, None
673 mapper = CodeNameMapper()
674 mapper.addmodule(m, m.__name__, 0)
675 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000676
677 def askdefinitionmodule(self, type, code):
678 fss, ok = macfs.PromptGetFile('Where is %s %s declared?'%(type, code))
679 if not ok: return
680 path, file = os.path.split(fss.as_pathname())
681 modname = os.path.splitext(file)[0]
682 if not path in sys.path:
683 sys.path.insert(0, path)
684 m = __import__(modname)
685 self.fp.write("import %s\n"%modname)
686 return m
687
688 def compileclass(self, cls):
689 [name, code, desc, properties, elements] = cls
690 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000691 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000692 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000693 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
694 if self.fp:
695 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000696 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000697 if self.fp:
698 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000699 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000700 self.fp.write('\twant = %s\n' % `code`)
701 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000702 for prop in properties:
703 self.compileproperty(prop)
704 for elem in elements:
705 self.compileelement(elem)
706
707 def compileproperty(self, prop):
708 [name, code, what] = prop
709 if code == 'c@#!':
710 # Something silly with plurals. Skip it.
711 return
712 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000713 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000714 # plural forms and such
715 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000716 if pname == othername:
717 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000718 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000719 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000720 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000721 if self.fp:
722 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000723 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
724 self.fp.write("\twhich = %s\n" % `ascii(code)`)
725 self.fp.write("\twant = %s\n" % `ascii(what[0])`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000726 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000727
728 def compileelement(self, elem):
729 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000730 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000731 self.fp.write("# element %s as %s\n" % (`ascii(code)`, ascii(keyform)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000732
Jack Jansen66544221997-08-08 14:49:02 +0000733 def fillclasspropsandelems(self, cls):
734 [name, code, desc, properties, elements] = cls
735 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000736 if self.namemappers[0].hascode('class', code) and \
737 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000738 # This is an other name (plural or so) for something else. Skip.
739 return
740 plist = []
741 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000742 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000743 for prop in properties:
744 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000745 if pcode == "c@#^":
746 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000747 if pcode == 'c@#!':
748 continue
749 pname = identify(pname)
750 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000751
752 superclassnames = []
753 for superclass in superclasses:
754 superId, superDesc, dummy = superclass
755 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
756 superclassnames.append(superclassname)
757
758 if self.fp:
759 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
760
Jack Jansen66544221997-08-08 14:49:02 +0000761 for elem in elements:
762 [ecode, keyform] = elem
763 if ecode == 'c@#!':
764 continue
765 name, ename, module = self.findcodename('class', ecode)
766 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000767 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000768 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ascii(ecode)`))
Jack Jansen66544221997-08-08 14:49:02 +0000769 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000770 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000771
772 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000773 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000774 for n in plist:
775 self.fp.write("\t'%s' : %s,\n"%(n, n))
776 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000777 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000778 for n, fulln in elist:
779 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
780 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000781
782 def compilecomparison(self, comp):
783 [name, code, comment] = comp
784 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000785 self.namemappers[0].addnamecode('comparison', iname, code)
786 if self.fp:
787 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000788 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000789
790 def compileenumeration(self, enum):
791 [code, items] = enum
792 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000793 if self.fp:
794 self.fp.write("%s = {\n" % name)
795 for item in items:
796 self.compileenumerator(item)
797 self.fp.write("}\n\n")
798 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000799 return code
800
801 def compileenumerator(self, item):
802 [name, code, desc] = item
Jack Jansen8b777672002-08-07 14:49:00 +0000803 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `ascii(code)`, desc))
Jack Jansen66544221997-08-08 14:49:02 +0000804
805 def checkforenum(self, enum):
806 """This enum code is used by an event. Make sure it's available"""
807 name, fullname, module = self.findcodename('enum', enum)
808 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000809 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000810 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +0000811 return
812 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000813 if self.fp:
814 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000815
816 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000817 if not self.fp:
818 return
Jack Jansen66544221997-08-08 14:49:02 +0000819 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
820 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000821 for k, v in self.namemappers[0].getall('class'):
Jack Jansen8b777672002-08-07 14:49:00 +0000822 self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000823 self.fp.write("}\n")
824 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000825 for k, v in self.namemappers[0].getall('property'):
Jack Jansen8b777672002-08-07 14:49:00 +0000826 self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000827 self.fp.write("}\n")
828 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000829 for k, v in self.namemappers[0].getall('comparison'):
Jack Jansen8b777672002-08-07 14:49:00 +0000830 self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000831 self.fp.write("}\n")
832 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000833 for k, v in self.namemappers[0].getall('enum'):
Jack Jansen8b777672002-08-07 14:49:00 +0000834 self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000835 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000836
837def compiledata(data):
838 [type, description, flags] = data
839 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
840
841def is_null(data):
842 return data[0] == 'null'
843
844def is_optional(data):
845 return (data[2] & 0x8000)
846
847def is_enum(data):
848 return (data[2] & 0x2000)
849
850def getdatadoc(data):
851 [type, descr, flags] = data
852 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +0000853 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000854 if type == '****':
855 return 'anything'
856 if type == 'obj ':
857 return 'an AE object reference'
Jack Jansen8b777672002-08-07 14:49:00 +0000858 return "undocumented, typecode %s"%`ascii(type)`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000859
860dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
861def compiledataflags(flags):
862 bits = []
863 for i in range(16):
864 if flags & (1<<i):
865 if i in dataflagdict.keys():
866 bits.append(dataflagdict[i])
867 else:
868 bits.append(`i`)
869 return '[%s]' % string.join(bits)
870
Jack Jansen8b777672002-08-07 14:49:00 +0000871def ascii(str):
872 """Return a string with all non-ascii characters hex-encoded"""
873 if type(str) != type(''):
874 return map(ascii, str)
875 rv = ''
876 for c in str:
877 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
878 rv = rv + c
879 else:
880 rv = rv + '\\x%02.2x' % ord(c)
881 return rv
882
Jack Jansen5ccd8261995-07-17 11:43:20 +0000883def identify(str):
884 """Turn any string into an identifier:
885 - replace space by _
886 - replace other illegal chars by _xx_ (hex code)
887 - prepend _ if the result is a python keyword
888 """
Jack Jansen66544221997-08-08 14:49:02 +0000889 if not str:
890 return "_empty_ae_name"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000891 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000892 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000893 ok2 = ok + string.digits
894 for c in str:
895 if c in ok:
896 rv = rv + c
897 elif c == ' ':
898 rv = rv + '_'
899 else:
900 rv = rv + '_%02.2x_'%ord(c)
901 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000902 if keyword.iskeyword(rv):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000903 rv = '_' + rv
904 return rv
905
906# Call the main program
907
908if __name__ == '__main__':
909 main()
910 sys.exit(1)
Jack Jansen8b777672002-08-07 14:49:00 +0000911print identify('for')