blob: f72a50b29ba6b9a13509c2c6ad43b0617d762ce5 [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
Jack Jansendf976ca2003-01-26 20:35:47 +000010import EasyDialogs
Jack Jansen5ccd8261995-07-17 11:43:20 +000011import os
12import string
13import sys
14import types
15import StringIO
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 Jansendf976ca2003-01-26 20:35:47 +000022DEFAULT_PACKAGEFOLDER=os.path.join(sys.prefix, 'Lib', 'plat-mac', 'lib-scriptpackages')
Jack Jansen40926062002-03-30 23:43:04 +000023
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:
Jack Jansendf976ca2003-01-26 20:35:47 +000029 filename = EasyDialogs.AskFileForOpen(message='Select file with aeut/aete resource:')
30 if not filename:
Jack Jansen40926062002-03-30 23:43:04 +000031 sys.exit(0)
Jack Jansendf976ca2003-01-26 20:35:47 +000032 processfile(filename)
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 Jansendf976ca2003-01-26 20:35:47 +0000234 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
Jack Jansen40926062002-03-30 23:43:04 +0000235 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000236 if language:
237 packagename = packagename+'_lang%d'%language
238 if script:
239 packagename = packagename+'_script%d'%script
240 if len(packagename) > 27:
241 packagename = packagename[:27]
Jack Jansendf976ca2003-01-26 20:35:47 +0000242 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
243 defaultLocation=DEFAULT_PACKAGEFOLDER)
244 if not pathname:
Jack Jansen60762cb2000-08-17 22:11:45 +0000245 return
Jack Jansen60762cb2000-08-17 22:11:45 +0000246 packagename = os.path.split(os.path.normpath(pathname))[1]
Jack Jansendf976ca2003-01-26 20:35:47 +0000247 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
248 defaultLocation=DEFAULT_PACKAGEFOLDER)
249 if basepkgname:
250 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000251 if not dirname in sys.path:
252 sys.path.insert(0, dirname)
253 basepackage = __import__(basepkgname)
254 else:
255 basepackage = None
Jack Jansen60762cb2000-08-17 22:11:45 +0000256 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000257 allprecompinfo = []
258 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000259 for suite in suites:
Jack Jansendf976ca2003-01-26 20:35:47 +0000260 code, suite, pathname, modname, precompinfo = precompilesuite(suite, basepackage)
Jack Jansen12b2b762000-08-20 19:30:56 +0000261 if not code:
262 continue
263 allprecompinfo = allprecompinfo + precompinfo
Jack Jansendf976ca2003-01-26 20:35:47 +0000264 suiteinfo = suite, pathname, modname
Jack Jansen12b2b762000-08-20 19:30:56 +0000265 suitelist.append((code, modname))
266 allsuites.append(suiteinfo)
267 for suiteinfo in allsuites:
268 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage, allprecompinfo)
Jack Jansendf976ca2003-01-26 20:35:47 +0000269 initfilename = EasyDialogs.AskFileForSave(message='Package module',
270 savedFileName='__init__.py')
271 if not initfilename:
Jack Jansen60762cb2000-08-17 22:11:45 +0000272 return
Jack Jansendf976ca2003-01-26 20:35:47 +0000273 fp = open(initfilename, 'w')
274 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
Jack Jansen60762cb2000-08-17 22:11:45 +0000275 fp.write('"""\n')
276 fp.write("Package generated from %s\n"%fname)
Jack Jansen8b777672002-08-07 14:49:00 +0000277 if resinfo:
278 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000279 fp.write('"""\n')
280 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000281 fp.write('Error = aetools.Error\n')
Jack Jansen60762cb2000-08-17 22:11:45 +0000282 for code, modname in suitelist:
283 fp.write("import %s\n" % modname)
284 fp.write("\n\n_code_to_module = {\n")
285 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000286 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000287 fp.write("}\n\n")
288 fp.write("\n\n_code_to_fullname = {\n")
289 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000290 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000291 fp.write("}\n\n")
292 for code, modname in suitelist:
293 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000294
295 # Generate property dicts and element dicts for all types declared in this module
296 fp.write("def getbaseclasses(v):\n")
Jack Jansen7ff034b2002-08-07 15:52:44 +0000297 fp.write("\tif hasattr(v, '_superclassnames') and not hasattr(v, '_propdict'):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000298 fp.write("\t\tv._propdict = {}\n")
299 fp.write("\t\tv._elemdict = {}\n")
300 fp.write("\t\tfor superclass in v._superclassnames:\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000301## fp.write("\t\t\tgetbaseclasses(superclass)\n")
302 fp.write("\t\t\tv._propdict.update(getattr(eval(superclass), '_privpropdict', {}))\n")
303 fp.write("\t\t\tv._elemdict.update(getattr(eval(superclass), '_privelemdict', {}))\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000304 fp.write("\t\tv._propdict.update(v._privpropdict)\n")
305 fp.write("\t\tv._elemdict.update(v._privelemdict)\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000306 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000307 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'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000320 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000321 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 Jansen2f7f8c42002-08-07 15:05:42 +0000328 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000329 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 Jansendf976ca2003-01-26 20:35:47 +0000341 pathname = EasyDialogs.AskFileForSave(message='Python output file',
342 savedFileName=modname+'.py')
343 if not pathname:
Jack Jansen12b2b762000-08-20 19:30:56 +0000344 return None, None, None, None, None
345
Jack Jansen60762cb2000-08-17 22:11:45 +0000346 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
Jack Jansendf976ca2003-01-26 20:35:47 +0000377 return code, suite, pathname, modname, precompinfo
Jack Jansen12b2b762000-08-20 19:30:56 +0000378
Jack Jansendf976ca2003-01-26 20:35:47 +0000379def compilesuite((suite, pathname, modname), major, minor, language, script, fname, basepackage, precompinfo):
Jack Jansen12b2b762000-08-20 19:30:56 +0000380 """Generate code for a single suite"""
381 [name, desc, code, level, version, events, classes, comps, enums] = suite
382
Jack Jansendf976ca2003-01-26 20:35:47 +0000383 fp = open(pathname, 'w')
384 MacOS.SetCreatorAndType(pathname, 'Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000385
Jack Jansen8b777672002-08-07 14:49:00 +0000386 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000387 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000388 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000389 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
390 (major, minor, language, script))
391 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000392
393 fp.write('import aetools\n')
394 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000395 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000396 if basepackage and basepackage._code_to_module.has_key(code):
397 # We are an extension of a baseclass (usually an application extending
398 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000399 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000400 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000401 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
402 # This is needed by CodeWarrior and some others.
403 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
404 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000405 else:
406 # We are not an extension.
407 basemodule = None
408 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000409
410 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000411 if events:
412 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000413 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000414 else:
415 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000416
Jack Jansen12b2b762000-08-20 19:30:56 +0000417 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000418 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000419 objc.compileclass(cls)
420 for cls in classes:
421 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000422 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000423 objc.compilecomparison(comp)
424 for enum in enums:
425 objc.compileenumeration(enum)
426
427 for enum in enumsneeded.keys():
428 objc.checkforenum(enum)
429
430 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000431
432 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000433
Jack Jansen60762cb2000-08-17 22:11:45 +0000434def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000435 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000436 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000437 if module:
438 modshortname = string.split(module.__name__, '.')[-1]
439 baseclassname = '%s_Events'%modshortname
440 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000441 else:
442 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000443
Jack Jansen66544221997-08-08 14:49:02 +0000444def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000445 """Generate code for a single event"""
446 [name, desc, code, subcode, returns, accepts, arguments] = event
447 funcname = identify(name)
448 #
449 # generate name->keyword map
450 #
451 if arguments:
452 fp.write("\t_argmap_%s = {\n"%funcname)
453 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000454 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000455 fp.write("\t}\n\n")
456
457 #
458 # Generate function header
459 #
460 has_arg = (not is_null(accepts))
461 opt_arg = (has_arg and is_optional(accepts))
462
Jack Jansen84264771995-10-03 14:35:58 +0000463 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000464 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000465 if not opt_arg:
466 fp.write("_object, ") # Include direct object, if it has one
467 else:
468 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000469 else:
Jack Jansen84264771995-10-03 14:35:58 +0000470 fp.write("_no_object=None, ") # For argument checking
471 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000472 #
473 # Generate doc string (important, since it may be the only
474 # available documentation, due to our name-remaping)
475 #
Jack Jansen21f67582002-08-07 15:44:53 +0000476 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000477 if has_arg:
478 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
479 elif opt_arg:
480 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
481 for arg in arguments:
482 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
483 getdatadoc(arg[2])))
484 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
485 if not is_null(returns):
486 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
487 fp.write('\t\t"""\n')
488 #
489 # Fiddle the args so everything ends up in 'arguments' dictionary
490 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000491 fp.write("\t\t_code = %s\n"% `code`)
492 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000493 #
494 # Do keyword name substitution
495 #
496 if arguments:
497 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
498 else:
499 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
500 #
501 # Stuff required arg (if there is one) into arguments
502 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000503 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000504 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000505 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000506 fp.write("\t\tif _object:\n")
507 fp.write("\t\t\t_arguments['----'] = _object\n")
508 else:
509 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000510 fp.write("\n")
511 #
Jack Jansen73215141995-10-09 23:09:23 +0000512 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000513 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000514 for a in arguments:
515 if is_enum(a[2]):
516 kname = a[1]
517 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000518 if ename <> '****':
519 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000520 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000521 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000522 fp.write("\n")
523 #
524 # Do the transaction
525 #
Jack Jansen84264771995-10-03 14:35:58 +0000526 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
527 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000528 #
529 # Error handling
530 #
Jack Jansen18983532002-04-23 21:03:21 +0000531 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000532 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000533 fp.write("\t\t# XXXX Optionally decode result\n")
534 #
535 # Decode result
536 #
Jack Jansen84264771995-10-03 14:35:58 +0000537 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000538 if is_enum(returns):
539 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000540 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000541 fp.write("\n")
542
543# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
544# print "# returns", compiledata(returns)
545# print "# accepts", compiledata(accepts)
546# for arg in arguments:
547# compileargument(arg)
548
549def compileargument(arg):
550 [name, keyword, what] = arg
551 print "# %s (%s)" % (name, `keyword`), compiledata(what)
552
Jack Jansen12b2b762000-08-20 19:30:56 +0000553def findenumsinevent(event, enumsneeded):
554 """Find all enums for a single event"""
555 [name, desc, code, subcode, returns, accepts, arguments] = event
556 for a in arguments:
557 if is_enum(a[2]):
558 ename = a[2][0]
559 if ename <> '****':
560 enumsneeded[ename] = 1
561
562#
563# This class stores the code<->name translations for a single module. It is used
564# to keep the information while we're compiling the module, but we also keep these objects
565# around so if one suite refers to, say, an enum in another suite we know where to
566# find it. Finally, if we really can't find a code, the user can add modules by
567# hand.
568#
569class CodeNameMapper:
570
571 def __init__(self):
572 self.code2name = {
573 "property" : {},
574 "class" : {},
575 "enum" : {},
576 "comparison" : {},
577 }
578 self.name2code = {
579 "property" : {},
580 "class" : {},
581 "enum" : {},
582 "comparison" : {},
583 }
584 self.modulename = None
585 self.star_imported = 0
586
587 def addnamecode(self, type, name, code):
588 self.name2code[type][name] = code
589 if not self.code2name[type].has_key(code):
590 self.code2name[type][code] = name
591
592 def hasname(self, type, name):
593 return self.name2code[type].has_key(name)
594
595 def hascode(self, type, code):
596 return self.code2name[type].has_key(code)
597
598 def findcodename(self, type, code):
599 if not self.hascode(type, code):
600 return None, None, None
601 name = self.code2name[type][code]
602 if self.modulename and not self.star_imported:
603 qualname = '%s.%s'%(self.modulename, name)
604 else:
605 qualname = name
606 return name, qualname, self.modulename
607
608 def getall(self, type):
609 return self.code2name[type].items()
610
611 def addmodule(self, module, name, star_imported):
612 self.modulename = name
613 self.star_imported = star_imported
614 for code, name in module._propdeclarations.items():
615 self.addnamecode('property', name, code)
616 for code, name in module._classdeclarations.items():
617 self.addnamecode('class', name, code)
618 for code in module._enumdeclarations.keys():
619 self.addnamecode('enum', '_Enum_'+identify(code), code)
620 for code, name in module._compdeclarations.items():
621 self.addnamecode('comparison', name, code)
622
623 def prepareforexport(self, name=None):
624 if not self.modulename:
625 self.modulename = name
626 return self
627
Jack Jansen66544221997-08-08 14:49:02 +0000628class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000629 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000630 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000631 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000632 self.namemappers = [CodeNameMapper()]
633 if othernamemappers:
634 self.othernamemappers = othernamemappers[:]
635 else:
636 self.othernamemappers = []
637 if basesuite:
638 basemapper = CodeNameMapper()
639 basemapper.addmodule(basesuite, '', 1)
640 self.namemappers.append(basemapper)
641
642 def getprecompinfo(self, modname):
643 list = []
644 for mapper in self.namemappers:
645 emapper = mapper.prepareforexport(modname)
646 if emapper:
647 list.append(emapper)
648 return list
Jack Jansen66544221997-08-08 14:49:02 +0000649
650 def findcodename(self, type, code):
651 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000652 # First try: check whether we already know about this code.
653 for mapper in self.namemappers:
654 if mapper.hascode(type, code):
655 return mapper.findcodename(type, code)
656 # Second try: maybe one of the other modules knows about it.
657 for mapper in self.othernamemappers:
658 if mapper.hascode(type, code):
659 self.othernamemappers.remove(mapper)
660 self.namemappers.append(mapper)
661 if self.fp:
662 self.fp.write("import %s\n"%mapper.modulename)
663 break
664 else:
665 # If all this has failed we ask the user for a guess on where it could
666 # be and retry.
667 if self.fp:
668 m = self.askdefinitionmodule(type, code)
669 else:
670 m = None
671 if not m: return None, None, None
672 mapper = CodeNameMapper()
673 mapper.addmodule(m, m.__name__, 0)
674 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000675
676 def askdefinitionmodule(self, type, code):
Jack Jansendf976ca2003-01-26 20:35:47 +0000677 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
678 if not path: return
679 path, file = os.path.split(path)
Jack Jansen66544221997-08-08 14:49:02 +0000680 modname = os.path.splitext(file)[0]
681 if not path in sys.path:
682 sys.path.insert(0, path)
683 m = __import__(modname)
684 self.fp.write("import %s\n"%modname)
685 return m
686
687 def compileclass(self, cls):
688 [name, code, desc, properties, elements] = cls
689 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000690 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000691 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000692 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
693 if self.fp:
694 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000695 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000696 if self.fp:
697 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000698 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000699 self.fp.write('\twant = %s\n' % `code`)
700 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000701 for prop in properties:
702 self.compileproperty(prop)
703 for elem in elements:
704 self.compileelement(elem)
705
706 def compileproperty(self, prop):
707 [name, code, what] = prop
708 if code == 'c@#!':
709 # Something silly with plurals. Skip it.
710 return
711 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000712 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000713 # plural forms and such
714 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000715 if pname == othername:
716 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000717 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000718 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000719 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000720 if self.fp:
721 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000722 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000723 self.fp.write("\twhich = %s\n" % `code`)
724 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000725 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000726
727 def compileelement(self, elem):
728 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000729 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000730 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000731
Jack Jansen66544221997-08-08 14:49:02 +0000732 def fillclasspropsandelems(self, cls):
733 [name, code, desc, properties, elements] = cls
734 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000735 if self.namemappers[0].hascode('class', code) and \
736 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000737 # This is an other name (plural or so) for something else. Skip.
738 return
739 plist = []
740 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000741 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000742 for prop in properties:
743 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000744 if pcode == "c@#^":
745 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000746 if pcode == 'c@#!':
747 continue
748 pname = identify(pname)
749 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000750
751 superclassnames = []
752 for superclass in superclasses:
753 superId, superDesc, dummy = superclass
754 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
755 superclassnames.append(superclassname)
756
757 if self.fp:
758 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
759
Jack Jansen66544221997-08-08 14:49:02 +0000760 for elem in elements:
761 [ecode, keyform] = elem
762 if ecode == 'c@#!':
763 continue
764 name, ename, module = self.findcodename('class', ecode)
765 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000766 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000767 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000768 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000769 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000770
771 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000772 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000773 for n in plist:
774 self.fp.write("\t'%s' : %s,\n"%(n, n))
775 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000776 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000777 for n, fulln in elist:
778 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
779 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000780
781 def compilecomparison(self, comp):
782 [name, code, comment] = comp
783 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000784 self.namemappers[0].addnamecode('comparison', iname, code)
785 if self.fp:
786 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000787 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000788
789 def compileenumeration(self, enum):
790 [code, items] = enum
791 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000792 if self.fp:
793 self.fp.write("%s = {\n" % name)
794 for item in items:
795 self.compileenumerator(item)
796 self.fp.write("}\n\n")
797 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000798 return code
799
800 def compileenumerator(self, item):
801 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +0000802 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +0000803
804 def checkforenum(self, enum):
805 """This enum code is used by an event. Make sure it's available"""
806 name, fullname, module = self.findcodename('enum', enum)
807 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000808 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000809 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +0000810 return
811 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000812 if self.fp:
813 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000814
815 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000816 if not self.fp:
817 return
Jack Jansen66544221997-08-08 14:49:02 +0000818 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
819 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000820 for k, v in self.namemappers[0].getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000821 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000822 self.fp.write("}\n")
823 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000824 for k, v in self.namemappers[0].getall('property'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000825 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000826 self.fp.write("}\n")
827 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000828 for k, v in self.namemappers[0].getall('comparison'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000829 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000830 self.fp.write("}\n")
831 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000832 for k, v in self.namemappers[0].getall('enum'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000833 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000834 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000835
836def compiledata(data):
837 [type, description, flags] = data
838 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
839
840def is_null(data):
841 return data[0] == 'null'
842
843def is_optional(data):
844 return (data[2] & 0x8000)
845
846def is_enum(data):
847 return (data[2] & 0x2000)
848
849def getdatadoc(data):
850 [type, descr, flags] = data
851 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +0000852 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000853 if type == '****':
854 return 'anything'
855 if type == 'obj ':
856 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000857 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000858
859dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
860def compiledataflags(flags):
861 bits = []
862 for i in range(16):
863 if flags & (1<<i):
864 if i in dataflagdict.keys():
865 bits.append(dataflagdict[i])
866 else:
867 bits.append(`i`)
868 return '[%s]' % string.join(bits)
869
Jack Jansen8b777672002-08-07 14:49:00 +0000870def ascii(str):
871 """Return a string with all non-ascii characters hex-encoded"""
872 if type(str) != type(''):
873 return map(ascii, str)
874 rv = ''
875 for c in str:
876 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
877 rv = rv + c
878 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000879 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +0000880 return rv
881
Jack Jansen5ccd8261995-07-17 11:43:20 +0000882def identify(str):
883 """Turn any string into an identifier:
884 - replace space by _
885 - replace other illegal chars by _xx_ (hex code)
886 - prepend _ if the result is a python keyword
887 """
Jack Jansen66544221997-08-08 14:49:02 +0000888 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +0000889 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000890 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000891 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000892 ok2 = ok + string.digits
893 for c in str:
894 if c in ok:
895 rv = rv + c
896 elif c == ' ':
897 rv = rv + '_'
898 else:
899 rv = rv + '_%02.2x_'%ord(c)
900 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000901 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +0000902 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000903 return rv
904
905# Call the main program
906
907if __name__ == '__main__':
908 main()
909 sys.exit(1)
Jack Jansen8b777672002-08-07 14:49:00 +0000910print identify('for')