blob: 02fe8b700c49e24174864ff56305d21d14d8ab82 [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")
Jack Jansen21f67582002-08-07 15:44:53 +0000298 fp.write("\tif hasattr(v, '_superclassnames') and v._superclassnames:\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000299 fp.write("\t\tv._propdict = {}\n")
300 fp.write("\t\tv._elemdict = {}\n")
301 fp.write("\t\tfor superclass in v._superclassnames:\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000302## 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")
Jack Jansen8b777672002-08-07 14:49:00 +0000305 fp.write("\t\tv._propdict.update(v._privpropdict)\n")
306 fp.write("\t\tv._elemdict.update(v._privelemdict)\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000307 fp.write("\t\tv._superclassnames = None\n")
308 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000309 fp.write("import StdSuites\n")
310 if allprecompinfo:
311 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
312 for codenamemapper in allprecompinfo:
313 for k, v in codenamemapper.getall('class'):
314 fp.write("getbaseclasses(%s)\n" % v)
315
316 # Generate a code-to-name mapper for all of the types (classes) declared in this module
317 if allprecompinfo:
318 fp.write("\n#\n# Indices of types declared in this module\n#\n")
319 fp.write("_classdeclarations = {\n")
320 for codenamemapper in allprecompinfo:
321 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000322 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000323 fp.write("}\n")
324
Jack Jansen60762cb2000-08-17 22:11:45 +0000325 if suitelist:
326 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
327 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000328 fp.write(",\n\t\t%s_Events"%modname)
329 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000330 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000331 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000332 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000333
334def precompilesuite(suite, basepackage=None):
335 """Parse a single suite without generating the output. This step is needed
336 so we can resolve recursive references by suites to enums/comps/etc declared
337 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000338 [name, desc, code, level, version, events, classes, comps, enums] = suite
339
340 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000341 if len(modname) > 28:
342 modname = modname[:27]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000343 fss, ok = macfs.StandardPutFile('Python output file', modname+'.py')
344 if not ok:
Jack Jansen12b2b762000-08-20 19:30:56 +0000345 return None, None, None, None, None
346
Jack Jansen60762cb2000-08-17 22:11:45 +0000347 pathname = fss.as_pathname()
348 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000349
350 if basepackage and basepackage._code_to_module.has_key(code):
351 # We are an extension of a baseclass (usually an application extending
352 # Standard_Suite or so). Import everything from our base module
353 basemodule = basepackage._code_to_module[code]
354 else:
355 # We are not an extension.
356 basemodule = None
357
358 enumsneeded = {}
359 for event in events:
360 findenumsinevent(event, enumsneeded)
361
362 objc = ObjectCompiler(None, basemodule)
363 for cls in classes:
364 objc.compileclass(cls)
365 for cls in classes:
366 objc.fillclasspropsandelems(cls)
367 for comp in comps:
368 objc.compilecomparison(comp)
369 for enum in enums:
370 objc.compileenumeration(enum)
371
372 for enum in enumsneeded.keys():
373 objc.checkforenum(enum)
374
375 objc.dumpindex()
376
377 precompinfo = objc.getprecompinfo(modname)
378
379 return code, suite, fss, modname, precompinfo
380
381def compilesuite((suite, fss, modname), major, minor, language, script, fname, basepackage, precompinfo):
382 """Generate code for a single suite"""
383 [name, desc, code, level, version, events, classes, comps, enums] = suite
384
385 pathname = fss.as_pathname()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000386 fp = open(fss.as_pathname(), 'w')
Jack Jansen18a99f51996-03-18 13:35:00 +0000387 fss.SetCreatorType('Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000388
Jack Jansen8b777672002-08-07 14:49:00 +0000389 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000390 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000391 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000392 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
393 (major, minor, language, script))
394 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000395
396 fp.write('import aetools\n')
397 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000398 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000399 if basepackage and basepackage._code_to_module.has_key(code):
400 # We are an extension of a baseclass (usually an application extending
401 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000402 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000403 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000404 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
405 # This is needed by CodeWarrior and some others.
406 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
407 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000408 else:
409 # We are not an extension.
410 basemodule = None
411 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000412
413 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000414 if events:
415 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000416 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000417 else:
418 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000419
Jack Jansen12b2b762000-08-20 19:30:56 +0000420 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000421 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000422 objc.compileclass(cls)
423 for cls in classes:
424 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000425 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000426 objc.compilecomparison(comp)
427 for enum in enums:
428 objc.compileenumeration(enum)
429
430 for enum in enumsneeded.keys():
431 objc.checkforenum(enum)
432
433 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000434
435 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000436
Jack Jansen60762cb2000-08-17 22:11:45 +0000437def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000438 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000439 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000440 if module:
441 modshortname = string.split(module.__name__, '.')[-1]
442 baseclassname = '%s_Events'%modshortname
443 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000444 else:
445 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000446
Jack Jansen66544221997-08-08 14:49:02 +0000447def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000448 """Generate code for a single event"""
449 [name, desc, code, subcode, returns, accepts, arguments] = event
450 funcname = identify(name)
451 #
452 # generate name->keyword map
453 #
454 if arguments:
455 fp.write("\t_argmap_%s = {\n"%funcname)
456 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000457 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000458 fp.write("\t}\n\n")
459
460 #
461 # Generate function header
462 #
463 has_arg = (not is_null(accepts))
464 opt_arg = (has_arg and is_optional(accepts))
465
Jack Jansen84264771995-10-03 14:35:58 +0000466 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000467 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000468 if not opt_arg:
469 fp.write("_object, ") # Include direct object, if it has one
470 else:
471 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000472 else:
Jack Jansen84264771995-10-03 14:35:58 +0000473 fp.write("_no_object=None, ") # For argument checking
474 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000475 #
476 # Generate doc string (important, since it may be the only
477 # available documentation, due to our name-remaping)
478 #
Jack Jansen21f67582002-08-07 15:44:53 +0000479 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000480 if has_arg:
481 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
482 elif opt_arg:
483 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
484 for arg in arguments:
485 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
486 getdatadoc(arg[2])))
487 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
488 if not is_null(returns):
489 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
490 fp.write('\t\t"""\n')
491 #
492 # Fiddle the args so everything ends up in 'arguments' dictionary
493 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000494 fp.write("\t\t_code = %s\n"% `code`)
495 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000496 #
497 # Do keyword name substitution
498 #
499 if arguments:
500 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
501 else:
502 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
503 #
504 # Stuff required arg (if there is one) into arguments
505 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000506 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000507 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000508 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000509 fp.write("\t\tif _object:\n")
510 fp.write("\t\t\t_arguments['----'] = _object\n")
511 else:
512 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000513 fp.write("\n")
514 #
Jack Jansen73215141995-10-09 23:09:23 +0000515 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000516 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000517 for a in arguments:
518 if is_enum(a[2]):
519 kname = a[1]
520 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000521 if ename <> '****':
522 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000523 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000524 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000525 fp.write("\n")
526 #
527 # Do the transaction
528 #
Jack Jansen84264771995-10-03 14:35:58 +0000529 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
530 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000531 #
532 # Error handling
533 #
Jack Jansen18983532002-04-23 21:03:21 +0000534 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000535 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000536 fp.write("\t\t# XXXX Optionally decode result\n")
537 #
538 # Decode result
539 #
Jack Jansen84264771995-10-03 14:35:58 +0000540 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000541 if is_enum(returns):
542 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000543 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000544 fp.write("\n")
545
546# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
547# print "# returns", compiledata(returns)
548# print "# accepts", compiledata(accepts)
549# for arg in arguments:
550# compileargument(arg)
551
552def compileargument(arg):
553 [name, keyword, what] = arg
554 print "# %s (%s)" % (name, `keyword`), compiledata(what)
555
Jack Jansen12b2b762000-08-20 19:30:56 +0000556def findenumsinevent(event, enumsneeded):
557 """Find all enums for a single event"""
558 [name, desc, code, subcode, returns, accepts, arguments] = event
559 for a in arguments:
560 if is_enum(a[2]):
561 ename = a[2][0]
562 if ename <> '****':
563 enumsneeded[ename] = 1
564
565#
566# This class stores the code<->name translations for a single module. It is used
567# to keep the information while we're compiling the module, but we also keep these objects
568# around so if one suite refers to, say, an enum in another suite we know where to
569# find it. Finally, if we really can't find a code, the user can add modules by
570# hand.
571#
572class CodeNameMapper:
573
574 def __init__(self):
575 self.code2name = {
576 "property" : {},
577 "class" : {},
578 "enum" : {},
579 "comparison" : {},
580 }
581 self.name2code = {
582 "property" : {},
583 "class" : {},
584 "enum" : {},
585 "comparison" : {},
586 }
587 self.modulename = None
588 self.star_imported = 0
589
590 def addnamecode(self, type, name, code):
591 self.name2code[type][name] = code
592 if not self.code2name[type].has_key(code):
593 self.code2name[type][code] = name
594
595 def hasname(self, type, name):
596 return self.name2code[type].has_key(name)
597
598 def hascode(self, type, code):
599 return self.code2name[type].has_key(code)
600
601 def findcodename(self, type, code):
602 if not self.hascode(type, code):
603 return None, None, None
604 name = self.code2name[type][code]
605 if self.modulename and not self.star_imported:
606 qualname = '%s.%s'%(self.modulename, name)
607 else:
608 qualname = name
609 return name, qualname, self.modulename
610
611 def getall(self, type):
612 return self.code2name[type].items()
613
614 def addmodule(self, module, name, star_imported):
615 self.modulename = name
616 self.star_imported = star_imported
617 for code, name in module._propdeclarations.items():
618 self.addnamecode('property', name, code)
619 for code, name in module._classdeclarations.items():
620 self.addnamecode('class', name, code)
621 for code in module._enumdeclarations.keys():
622 self.addnamecode('enum', '_Enum_'+identify(code), code)
623 for code, name in module._compdeclarations.items():
624 self.addnamecode('comparison', name, code)
625
626 def prepareforexport(self, name=None):
627 if not self.modulename:
628 self.modulename = name
629 return self
630
Jack Jansen66544221997-08-08 14:49:02 +0000631class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000632 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000633 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000634 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000635 self.namemappers = [CodeNameMapper()]
636 if othernamemappers:
637 self.othernamemappers = othernamemappers[:]
638 else:
639 self.othernamemappers = []
640 if basesuite:
641 basemapper = CodeNameMapper()
642 basemapper.addmodule(basesuite, '', 1)
643 self.namemappers.append(basemapper)
644
645 def getprecompinfo(self, modname):
646 list = []
647 for mapper in self.namemappers:
648 emapper = mapper.prepareforexport(modname)
649 if emapper:
650 list.append(emapper)
651 return list
Jack Jansen66544221997-08-08 14:49:02 +0000652
653 def findcodename(self, type, code):
654 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000655 # First try: check whether we already know about this code.
656 for mapper in self.namemappers:
657 if mapper.hascode(type, code):
658 return mapper.findcodename(type, code)
659 # Second try: maybe one of the other modules knows about it.
660 for mapper in self.othernamemappers:
661 if mapper.hascode(type, code):
662 self.othernamemappers.remove(mapper)
663 self.namemappers.append(mapper)
664 if self.fp:
665 self.fp.write("import %s\n"%mapper.modulename)
666 break
667 else:
668 # If all this has failed we ask the user for a guess on where it could
669 # be and retry.
670 if self.fp:
671 m = self.askdefinitionmodule(type, code)
672 else:
673 m = None
674 if not m: return None, None, None
675 mapper = CodeNameMapper()
676 mapper.addmodule(m, m.__name__, 0)
677 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000678
679 def askdefinitionmodule(self, type, code):
680 fss, ok = macfs.PromptGetFile('Where is %s %s declared?'%(type, code))
681 if not ok: return
682 path, file = os.path.split(fss.as_pathname())
683 modname = os.path.splitext(file)[0]
684 if not path in sys.path:
685 sys.path.insert(0, path)
686 m = __import__(modname)
687 self.fp.write("import %s\n"%modname)
688 return m
689
690 def compileclass(self, cls):
691 [name, code, desc, properties, elements] = cls
692 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000693 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000694 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000695 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
696 if self.fp:
697 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000698 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000699 if self.fp:
700 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000701 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000702 self.fp.write('\twant = %s\n' % `code`)
703 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000704 for prop in properties:
705 self.compileproperty(prop)
706 for elem in elements:
707 self.compileelement(elem)
708
709 def compileproperty(self, prop):
710 [name, code, what] = prop
711 if code == 'c@#!':
712 # Something silly with plurals. Skip it.
713 return
714 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000715 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000716 # plural forms and such
717 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000718 if pname == othername:
719 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000720 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000721 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000722 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000723 if self.fp:
724 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000725 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000726 self.fp.write("\twhich = %s\n" % `code`)
727 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000728 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000729
730 def compileelement(self, elem):
731 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000732 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000733 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000734
Jack Jansen66544221997-08-08 14:49:02 +0000735 def fillclasspropsandelems(self, cls):
736 [name, code, desc, properties, elements] = cls
737 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000738 if self.namemappers[0].hascode('class', code) and \
739 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000740 # This is an other name (plural or so) for something else. Skip.
741 return
742 plist = []
743 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000744 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000745 for prop in properties:
746 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000747 if pcode == "c@#^":
748 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000749 if pcode == 'c@#!':
750 continue
751 pname = identify(pname)
752 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000753
754 superclassnames = []
755 for superclass in superclasses:
756 superId, superDesc, dummy = superclass
757 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
758 superclassnames.append(superclassname)
759
760 if self.fp:
761 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
762
Jack Jansen66544221997-08-08 14:49:02 +0000763 for elem in elements:
764 [ecode, keyform] = elem
765 if ecode == 'c@#!':
766 continue
767 name, ename, module = self.findcodename('class', ecode)
768 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000769 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000770 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000771 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000772 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000773
774 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000775 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000776 for n in plist:
777 self.fp.write("\t'%s' : %s,\n"%(n, n))
778 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000779 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000780 for n, fulln in elist:
781 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
782 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000783
784 def compilecomparison(self, comp):
785 [name, code, comment] = comp
786 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000787 self.namemappers[0].addnamecode('comparison', iname, code)
788 if self.fp:
789 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000790 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000791
792 def compileenumeration(self, enum):
793 [code, items] = enum
794 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000795 if self.fp:
796 self.fp.write("%s = {\n" % name)
797 for item in items:
798 self.compileenumerator(item)
799 self.fp.write("}\n\n")
800 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000801 return code
802
803 def compileenumerator(self, item):
804 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +0000805 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +0000806
807 def checkforenum(self, enum):
808 """This enum code is used by an event. Make sure it's available"""
809 name, fullname, module = self.findcodename('enum', enum)
810 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000811 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000812 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +0000813 return
814 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000815 if self.fp:
816 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000817
818 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000819 if not self.fp:
820 return
Jack Jansen66544221997-08-08 14:49:02 +0000821 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
822 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000823 for k, v in self.namemappers[0].getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000824 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000825 self.fp.write("}\n")
826 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000827 for k, v in self.namemappers[0].getall('property'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000828 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000829 self.fp.write("}\n")
830 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000831 for k, v in self.namemappers[0].getall('comparison'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000832 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000833 self.fp.write("}\n")
834 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000835 for k, v in self.namemappers[0].getall('enum'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000836 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000837 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000838
839def compiledata(data):
840 [type, description, flags] = data
841 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
842
843def is_null(data):
844 return data[0] == 'null'
845
846def is_optional(data):
847 return (data[2] & 0x8000)
848
849def is_enum(data):
850 return (data[2] & 0x2000)
851
852def getdatadoc(data):
853 [type, descr, flags] = data
854 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +0000855 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000856 if type == '****':
857 return 'anything'
858 if type == 'obj ':
859 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000860 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000861
862dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
863def compiledataflags(flags):
864 bits = []
865 for i in range(16):
866 if flags & (1<<i):
867 if i in dataflagdict.keys():
868 bits.append(dataflagdict[i])
869 else:
870 bits.append(`i`)
871 return '[%s]' % string.join(bits)
872
Jack Jansen8b777672002-08-07 14:49:00 +0000873def ascii(str):
874 """Return a string with all non-ascii characters hex-encoded"""
875 if type(str) != type(''):
876 return map(ascii, str)
877 rv = ''
878 for c in str:
879 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
880 rv = rv + c
881 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000882 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +0000883 return rv
884
Jack Jansen5ccd8261995-07-17 11:43:20 +0000885def identify(str):
886 """Turn any string into an identifier:
887 - replace space by _
888 - replace other illegal chars by _xx_ (hex code)
889 - prepend _ if the result is a python keyword
890 """
Jack Jansen66544221997-08-08 14:49:02 +0000891 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +0000892 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000893 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000894 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000895 ok2 = ok + string.digits
896 for c in str:
897 if c in ok:
898 rv = rv + c
899 elif c == ' ':
900 rv = rv + '_'
901 else:
902 rv = rv + '_%02.2x_'%ord(c)
903 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000904 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +0000905 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000906 return rv
907
908# Call the main program
909
910if __name__ == '__main__':
911 main()
912 sys.exit(1)
Jack Jansen8b777672002-08-07 14:49:00 +0000913print identify('for')