blob: 622016e3effd9707e08c9f83a81e2c194e229b14 [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 Jansenfa1bf1c2003-03-06 23:04:38 +000018import aetools
19import distutils.sysconfig
20import OSATerminology
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000021from Carbon.Res import *
Jack Jansen1269be52003-03-29 22:54:00 +000022import Carbon.Folder
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000023import MacOS
Jack Jansen714caa02003-03-21 16:07:39 +000024import getopt
Jack Jansen1269be52003-03-29 22:54:00 +000025import plistlib
Jack Jansen5ccd8261995-07-17 11:43:20 +000026
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000027_MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
28DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
29DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
Jack Jansen40926062002-03-30 23:43:04 +000030
Jack Jansen714caa02003-03-21 16:07:39 +000031def usage():
Jack Jansen0ae32202003-04-09 13:25:43 +000032 sys.stderr.write("Usage: %s [opts] application-or-resource-file\n" % sys.argv[0])
33 sys.stderr.write("""Options:
Jack Jansen714caa02003-03-21 16:07:39 +000034--output pkgdir Pathname of the output package (short: -o)
35--resource Parse resource file in stead of launching application (-r)
36--base package Use another base package in stead of default StdSuites (-b)
37--edit old=new Edit suite names, use empty new to skip a suite (-e)
Jack Jansen59cdbce2003-03-21 16:28:09 +000038--creator code Set creator code for package (-c)
Jack Jansen18c9b132003-04-01 13:32:17 +000039--dump Dump aete resource to stdout in stead of creating module (-d)
40--verbose Tell us what happens (-v)
Jack Jansen714caa02003-03-21 16:07:39 +000041""")
Jack Jansen0ae32202003-04-09 13:25:43 +000042 sys.exit(1)
Jack Jansen714caa02003-03-21 16:07:39 +000043
Jack Jansen5ccd8261995-07-17 11:43:20 +000044def main():
Jack Jansen0ae32202003-04-09 13:25:43 +000045 if len(sys.argv) > 1:
46 SHORTOPTS = "rb:o:e:c:dv"
47 LONGOPTS = ("resource", "base=", "output=", "edit=", "creator=", "dump", "verbose")
48 try:
49 opts, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
50 except getopt.GetoptError:
51 usage()
52
53 process_func = processfile
54 basepkgname = 'StdSuites'
55 output = None
56 edit_modnames = []
57 creatorsignature = None
58 dump = None
59 verbose = None
60
61 for o, a in opts:
62 if o in ('-r', '--resource'):
63 process_func = processfile_fromresource
64 if o in ('-b', '--base'):
65 basepkgname = a
66 if o in ('-o', '--output'):
67 output = a
68 if o in ('-e', '--edit'):
69 split = a.split('=')
70 if len(split) != 2:
71 usage()
72 edit_modnames.append(split)
73 if o in ('-c', '--creator'):
74 if len(a) != 4:
75 sys.stderr.write("creator must be 4-char string\n")
76 sys.exit(1)
77 creatorsignature = a
78 if o in ('-d', '--dump'):
79 dump = sys.stdout
80 if o in ('-v', '--verbose'):
81 verbose = sys.stderr
82
83
84 if output and len(args) > 1:
85 sys.stderr.write("%s: cannot specify --output with multiple inputs\n" % sys.argv[0])
86 sys.exit(1)
87
88 for filename in args:
89 process_func(filename, output=output, basepkgname=basepkgname,
90 edit_modnames=edit_modnames, creatorsignature=creatorsignature,
91 dump=dump, verbose=verbose)
92 else:
93 main_interactive()
94
Jack Jansen1269be52003-03-29 22:54:00 +000095def main_interactive(interact=0, basepkgname='StdSuites'):
Jack Jansen0ae32202003-04-09 13:25:43 +000096 if interact:
97 # Ask for save-filename for each module
98 edit_modnames = None
99 else:
100 # Use default filenames for each module
101 edit_modnames = []
102 appsfolder = Carbon.Folder.FSFindFolder(-32765, 'apps', 0)
103 filename = EasyDialogs.AskFileForOpen(
104 message='Select scriptable application',
105 dialogOptionFlags=0x1056, # allow selection of .app bundles
106 defaultLocation=appsfolder)
107 if not filename:
108 return
109 if not is_scriptable(filename):
110 if EasyDialogs.AskYesNoCancel(
111 "Warning: application does not seem scriptable",
112 yes="Continue", default=2, no="") <= 0:
113 return
114 try:
115 processfile(filename, edit_modnames=edit_modnames, basepkgname=basepkgname,
116 verbose=sys.stderr)
117 except MacOS.Error, arg:
118 print "Error getting terminology:", arg
119 print "Retry, manually parsing resources"
120 processfile_fromresource(filename, edit_modnames=edit_modnames,
121 basepkgname=basepkgname, verbose=sys.stderr)
122
Jack Jansen1269be52003-03-29 22:54:00 +0000123def is_scriptable(application):
Jack Jansen0ae32202003-04-09 13:25:43 +0000124 """Return true if the application is scriptable"""
125 if os.path.isdir(application):
126 plistfile = os.path.join(application, 'Contents', 'Info.plist')
127 if not os.path.exists(plistfile):
128 return False
129 plist = plistlib.Plist.fromFile(plistfile)
130 return plist.get('NSAppleScriptEnabled', False)
131 # If it is a file test for an aete/aeut resource.
132 currf = CurResFile()
133 try:
134 refno = macresource.open_pathname(application)
135 except MacOS.Error:
136 return False
137 UseResFile(refno)
138 n_terminology = Count1Resources('aete') + Count1Resources('aeut') + \
139 Count1Resources('scsz') + Count1Resources('osiz')
140 CloseResFile(refno)
141 UseResFile(currf)
142 return n_terminology > 0
Jack Jansen5ccd8261995-07-17 11:43:20 +0000143
Jack Jansen59cdbce2003-03-21 16:28:09 +0000144def processfile_fromresource(fullname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000145 edit_modnames=None, creatorsignature=None, dump=None, verbose=None):
146 """Process all resources in a single file"""
147 if not is_scriptable(fullname) and verbose:
148 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
149 cur = CurResFile()
150 if verbose:
151 print >>verbose, "Processing", fullname
152 rf = macresource.open_pathname(fullname)
153 try:
154 UseResFile(rf)
155 resources = []
156 for i in range(Count1Resources('aete')):
157 res = Get1IndResource('aete', 1+i)
158 resources.append(res)
159 for i in range(Count1Resources('aeut')):
160 res = Get1IndResource('aeut', 1+i)
161 resources.append(res)
162 if verbose:
163 print >>verbose, "\nLISTING aete+aeut RESOURCES IN", `fullname`
164 aetelist = []
165 for res in resources:
166 if verbose:
167 print >>verbose, "decoding", res.GetResInfo(), "..."
168 data = res.data
169 aete = decode(data, verbose)
170 aetelist.append((aete, res.GetResInfo()))
171 finally:
172 if rf <> cur:
173 CloseResFile(rf)
174 UseResFile(cur)
175 # switch back (needed for dialogs in Python)
176 UseResFile(cur)
177 if dump:
178 dumpaetelist(aetelist, dump)
179 compileaetelist(aetelist, fullname, output=output,
180 basepkgname=basepkgname, edit_modnames=edit_modnames,
181 creatorsignature=creatorsignature, verbose=verbose)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000182
Jack Jansen59cdbce2003-03-21 16:28:09 +0000183def processfile(fullname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000184 edit_modnames=None, creatorsignature=None, dump=None,
185 verbose=None):
186 """Ask an application for its terminology and process that"""
187 if not is_scriptable(fullname) and verbose:
188 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
189 if verbose:
190 print >>verbose, "\nASKING FOR aete DICTIONARY IN", `fullname`
191 try:
192 aedescobj, launched = OSATerminology.GetAppTerminology(fullname)
193 except MacOS.Error, arg:
194 if arg[0] in (-1701, -192): # errAEDescNotFound, resNotFound
195 if verbose:
196 print >>verbose, "GetAppTerminology failed with errAEDescNotFound/resNotFound, trying manually"
197 aedata, sig = getappterminology(fullname, verbose=verbose)
198 if not creatorsignature:
199 creatorsignature = sig
200 else:
201 raise
202 else:
203 if launched:
204 if verbose:
205 print >>verbose, "Launched", fullname
206 raw = aetools.unpack(aedescobj)
207 if not raw:
208 if verbose:
209 print >>verbose, 'Unpack returned empty value:', raw
210 return
211 if not raw[0].data:
212 if verbose:
213 print >>verbose, 'Unpack returned value without data:', raw
214 return
215 aedata = raw[0]
216 aete = decode(aedata.data, verbose)
217 if dump:
218 dumpaetelist([aete], dump)
219 return
220 compileaete(aete, None, fullname, output=output, basepkgname=basepkgname,
221 creatorsignature=creatorsignature, edit_modnames=edit_modnames,
222 verbose=verbose)
223
Jack Jansen18c9b132003-04-01 13:32:17 +0000224def getappterminology(fullname, verbose=None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000225 """Get application terminology by sending an AppleEvent"""
226 # First check that we actually can send AppleEvents
227 if not MacOS.WMAvailable():
228 raise RuntimeError, "Cannot send AppleEvents, no access to window manager"
229 # Next, a workaround for a bug in MacOS 10.2: sending events will hang unless
230 # you have created an event loop first.
231 import Carbon.Evt
232 Carbon.Evt.WaitNextEvent(0,0)
233 if os.path.isdir(fullname):
234 # Now get the signature of the application, hoping it is a bundle
235 pkginfo = os.path.join(fullname, 'Contents', 'PkgInfo')
236 if not os.path.exists(pkginfo):
237 raise RuntimeError, "No PkgInfo file found"
238 tp_cr = open(pkginfo, 'rb').read()
239 cr = tp_cr[4:8]
240 else:
241 # Assume it is a file
242 cr, tp = MacOS.GetCreatorAndType(fullname)
243 # Let's talk to it and ask for its AETE
244 talker = aetools.TalkTo(cr)
245 try:
246 talker._start()
247 except (MacOS.Error, aetools.Error), arg:
248 if verbose:
249 print >>verbose, 'Warning: start() failed, continuing anyway:', arg
250 reply = talker.send("ascr", "gdte")
251 #reply2 = talker.send("ascr", "gdut")
252 # Now pick the bits out of the return that we need.
253 return reply[1]['----'], cr
254
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000255
Jack Jansen59cdbce2003-03-21 16:28:09 +0000256def compileaetelist(aetelist, fullname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000257 edit_modnames=None, creatorsignature=None, verbose=None):
258 for aete, resinfo in aetelist:
259 compileaete(aete, resinfo, fullname, output=output,
260 basepkgname=basepkgname, edit_modnames=edit_modnames,
261 creatorsignature=creatorsignature, verbose=verbose)
Jack Jansen18c9b132003-04-01 13:32:17 +0000262
263def dumpaetelist(aetelist, output):
Jack Jansen0ae32202003-04-09 13:25:43 +0000264 import pprint
265 pprint.pprint(aetelist, output)
266
Jack Jansen18c9b132003-04-01 13:32:17 +0000267def decode(data, verbose=None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000268 """Decode a resource into a python data structure"""
269 f = StringIO.StringIO(data)
270 aete = generic(getaete, f)
271 aete = simplify(aete)
272 processed = f.tell()
273 unprocessed = len(f.read())
274 total = f.tell()
275 if unprocessed and verbose:
276 verbose.write("%d processed + %d unprocessed = %d total\n" %
277 (processed, unprocessed, total))
278 return aete
Jack Jansen5ccd8261995-07-17 11:43:20 +0000279
280def simplify(item):
Jack Jansen0ae32202003-04-09 13:25:43 +0000281 """Recursively replace singleton tuples by their constituent item"""
282 if type(item) is types.ListType:
283 return map(simplify, item)
284 elif type(item) == types.TupleType and len(item) == 2:
285 return simplify(item[1])
286 else:
287 return item
Jack Jansen5ccd8261995-07-17 11:43:20 +0000288
289
290# Here follows the aete resource decoder.
291# It is presented bottom-up instead of top-down because there are direct
292# references to the lower-level part-decoders from the high-level part-decoders.
293
294def getbyte(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000295 c = f.read(1)
296 if not c:
297 raise EOFError, 'in getbyte' + str(args)
298 return ord(c)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000299
300def getword(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000301 getalign(f)
302 s = f.read(2)
303 if len(s) < 2:
304 raise EOFError, 'in getword' + str(args)
305 return (ord(s[0])<<8) | ord(s[1])
Jack Jansen5ccd8261995-07-17 11:43:20 +0000306
307def getlong(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000308 getalign(f)
309 s = f.read(4)
310 if len(s) < 4:
311 raise EOFError, 'in getlong' + str(args)
312 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
Jack Jansen5ccd8261995-07-17 11:43:20 +0000313
314def getostype(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000315 getalign(f)
316 s = f.read(4)
317 if len(s) < 4:
318 raise EOFError, 'in getostype' + str(args)
319 return s
Jack Jansen5ccd8261995-07-17 11:43:20 +0000320
321def getpstr(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000322 c = f.read(1)
323 if len(c) < 1:
324 raise EOFError, 'in getpstr[1]' + str(args)
325 nbytes = ord(c)
326 if nbytes == 0: return ''
327 s = f.read(nbytes)
328 if len(s) < nbytes:
329 raise EOFError, 'in getpstr[2]' + str(args)
330 return s
Jack Jansen5ccd8261995-07-17 11:43:20 +0000331
332def getalign(f):
Jack Jansen0ae32202003-04-09 13:25:43 +0000333 if f.tell() & 1:
334 c = f.read(1)
335 ##if c <> '\0':
336 ## print 'align:', `c`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000337
338def getlist(f, description, getitem):
Jack Jansen0ae32202003-04-09 13:25:43 +0000339 count = getword(f)
340 list = []
341 for i in range(count):
342 list.append(generic(getitem, f))
343 getalign(f)
344 return list
Jack Jansen5ccd8261995-07-17 11:43:20 +0000345
346def alt_generic(what, f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000347 print "generic", `what`, args
348 res = vageneric(what, f, args)
349 print '->', `res`
350 return res
Jack Jansen5ccd8261995-07-17 11:43:20 +0000351
352def generic(what, f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000353 if type(what) == types.FunctionType:
354 return apply(what, (f,) + args)
355 if type(what) == types.ListType:
356 record = []
357 for thing in what:
358 item = apply(generic, thing[:1] + (f,) + thing[1:])
359 record.append((thing[1], item))
360 return record
361 return "BAD GENERIC ARGS: %s" % `what`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000362
363getdata = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000364 (getostype, "type"),
365 (getpstr, "description"),
366 (getword, "flags")
367 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000368getargument = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000369 (getpstr, "name"),
370 (getostype, "keyword"),
371 (getdata, "what")
372 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000373getevent = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000374 (getpstr, "name"),
375 (getpstr, "description"),
376 (getostype, "suite code"),
377 (getostype, "event code"),
378 (getdata, "returns"),
379 (getdata, "accepts"),
380 (getlist, "optional arguments", getargument)
381 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000382getproperty = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000383 (getpstr, "name"),
384 (getostype, "code"),
385 (getdata, "what")
386 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000387getelement = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000388 (getostype, "type"),
389 (getlist, "keyform", getostype)
390 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000391getclass = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000392 (getpstr, "name"),
393 (getostype, "class code"),
394 (getpstr, "description"),
395 (getlist, "properties", getproperty),
396 (getlist, "elements", getelement)
397 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000398getcomparison = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000399 (getpstr, "operator name"),
400 (getostype, "operator ID"),
401 (getpstr, "operator comment"),
402 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000403getenumerator = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000404 (getpstr, "enumerator name"),
405 (getostype, "enumerator ID"),
406 (getpstr, "enumerator comment")
407 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000408getenumeration = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000409 (getostype, "enumeration ID"),
410 (getlist, "enumerator", getenumerator)
411 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000412getsuite = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000413 (getpstr, "suite name"),
414 (getpstr, "suite description"),
415 (getostype, "suite ID"),
416 (getword, "suite level"),
417 (getword, "suite version"),
418 (getlist, "events", getevent),
419 (getlist, "classes", getclass),
420 (getlist, "comparisons", getcomparison),
421 (getlist, "enumerations", getenumeration)
422 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000423getaete = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000424 (getword, "major/minor version in BCD"),
425 (getword, "language code"),
426 (getword, "script code"),
427 (getlist, "suites", getsuite)
428 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000429
Jack Jansen59cdbce2003-03-21 16:28:09 +0000430def compileaete(aete, resinfo, fname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000431 edit_modnames=None, creatorsignature=None, verbose=None):
432 """Generate code for a full aete resource. fname passed for doc purposes"""
433 [version, language, script, suites] = aete
434 major, minor = divmod(version, 256)
435 if not creatorsignature:
436 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
437 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
438 if language:
439 packagename = packagename+'_lang%d'%language
440 if script:
441 packagename = packagename+'_script%d'%script
442 if len(packagename) > 27:
443 packagename = packagename[:27]
444 if output:
445 # XXXX Put this in site-packages if it isn't a full pathname?
446 if not os.path.exists(output):
447 os.mkdir(output)
448 pathname = output
449 else:
450 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
451 defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
452 output = pathname
453 if not pathname:
454 return
455 packagename = os.path.split(os.path.normpath(pathname))[1]
456 if not basepkgname:
457 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
458 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
459 if basepkgname:
460 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
461 if dirname and not dirname in sys.path:
462 sys.path.insert(0, dirname)
463 basepackage = __import__(basepkgname)
464 else:
465 basepackage = None
466 suitelist = []
467 allprecompinfo = []
468 allsuites = []
469 for suite in suites:
470 compiler = SuiteCompiler(suite, basepackage, output, edit_modnames, verbose)
471 code, modname, precompinfo = compiler.precompilesuite()
472 if not code:
473 continue
474 allprecompinfo = allprecompinfo + precompinfo
475 suiteinfo = suite, pathname, modname
476 suitelist.append((code, modname))
477 allsuites.append(compiler)
478 for compiler in allsuites:
479 compiler.compilesuite(major, minor, language, script, fname, allprecompinfo)
480 initfilename = os.path.join(output, '__init__.py')
481 fp = open(initfilename, 'w')
482 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
483 fp.write('"""\n')
484 fp.write("Package generated from %s\n"%ascii(fname))
485 if resinfo:
486 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
487 fp.write('"""\n')
488 fp.write('import aetools\n')
489 fp.write('Error = aetools.Error\n')
490 suitelist.sort()
491 for code, modname in suitelist:
492 fp.write("import %s\n" % modname)
493 fp.write("\n\n_code_to_module = {\n")
494 for code, modname in suitelist:
495 fp.write(" '%s' : %s,\n"%(ascii(code), modname))
496 fp.write("}\n\n")
497 fp.write("\n\n_code_to_fullname = {\n")
498 for code, modname in suitelist:
499 fp.write(" '%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
500 fp.write("}\n\n")
501 for code, modname in suitelist:
502 fp.write("from %s import *\n"%modname)
503
504 # Generate property dicts and element dicts for all types declared in this module
505 fp.write("\ndef getbaseclasses(v):\n")
506 fp.write(" if not getattr(v, '_propdict', None):\n")
507 fp.write(" v._propdict = {}\n")
508 fp.write(" v._elemdict = {}\n")
509 fp.write(" for superclassname in getattr(v, '_superclassnames', []):\n")
510 fp.write(" superclass = eval(superclassname)\n")
511 fp.write(" getbaseclasses(superclass)\n")
512 fp.write(" v._propdict.update(getattr(superclass, '_propdict', {}))\n")
513 fp.write(" v._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
514 fp.write(" v._propdict.update(getattr(v, '_privpropdict', {}))\n")
515 fp.write(" v._elemdict.update(getattr(v, '_privelemdict', {}))\n")
516 fp.write("\n")
517 fp.write("import StdSuites\n")
518 allprecompinfo.sort()
519 if allprecompinfo:
520 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
521 for codenamemapper in allprecompinfo:
522 for k, v in codenamemapper.getall('class'):
523 fp.write("getbaseclasses(%s)\n" % v)
Jack Jansen8b777672002-08-07 14:49:00 +0000524
Jack Jansen0ae32202003-04-09 13:25:43 +0000525 # Generate a code-to-name mapper for all of the types (classes) declared in this module
526 if allprecompinfo:
527 fp.write("\n#\n# Indices of types declared in this module\n#\n")
528 fp.write("_classdeclarations = {\n")
529 for codenamemapper in allprecompinfo:
530 for k, v in codenamemapper.getall('class'):
531 fp.write(" %s : %s,\n" % (`k`, v))
532 fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000533
Jack Jansen0ae32202003-04-09 13:25:43 +0000534 if suitelist:
535 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
536 for code, modname in suitelist[1:]:
537 fp.write(",\n %s_Events"%modname)
538 fp.write(",\n aetools.TalkTo):\n")
539 fp.write(" _signature = %s\n\n"%`creatorsignature`)
540 fp.write(" _moduleName = '%s'\n\n"%packagename)
541 fp.close()
Jack Jansen3279cb02003-04-01 14:25:49 +0000542
543class SuiteCompiler:
Jack Jansen0ae32202003-04-09 13:25:43 +0000544 def __init__(self, suite, basepackage, output, edit_modnames, verbose):
545 self.suite = suite
546 self.basepackage = basepackage
547 self.edit_modnames = edit_modnames
548 self.output = output
549 self.verbose = verbose
550
551 # Set by precompilesuite
552 self.pathname = None
553 self.modname = None
554
555 # Set by compilesuite
556 self.fp = None
557 self.basemodule = None
558 self.enumsneeded = {}
559
560 def precompilesuite(self):
561 """Parse a single suite without generating the output. This step is needed
562 so we can resolve recursive references by suites to enums/comps/etc declared
563 in other suites"""
564 [name, desc, code, level, version, events, classes, comps, enums] = self.suite
565
566 modname = identify(name)
567 if len(modname) > 28:
568 modname = modname[:27]
569 if self.edit_modnames is None:
570 self.pathname = EasyDialogs.AskFileForSave(message='Python output file',
571 savedFileName=modname+'.py')
572 else:
573 for old, new in self.edit_modnames:
574 if old == modname:
575 modname = new
576 if modname:
577 self.pathname = os.path.join(self.output, modname + '.py')
578 else:
579 self.pathname = None
580 if not self.pathname:
581 return None, None, None
582
583 self.modname = os.path.splitext(os.path.split(self.pathname)[1])[0]
584
585 if self.basepackage and self.basepackage._code_to_module.has_key(code):
586 # We are an extension of a baseclass (usually an application extending
587 # Standard_Suite or so). Import everything from our base module
588 basemodule = self.basepackage._code_to_module[code]
589 else:
590 # We are not an extension.
591 basemodule = None
592
593 self.enumsneeded = {}
594 for event in events:
595 self.findenumsinevent(event)
596
597 objc = ObjectCompiler(None, self.modname, basemodule, interact=(self.edit_modnames is None),
598 verbose=self.verbose)
599 for cls in classes:
600 objc.compileclass(cls)
601 for cls in classes:
602 objc.fillclasspropsandelems(cls)
603 for comp in comps:
604 objc.compilecomparison(comp)
605 for enum in enums:
606 objc.compileenumeration(enum)
607
608 for enum in self.enumsneeded.keys():
609 objc.checkforenum(enum)
610
611 objc.dumpindex()
612
613 precompinfo = objc.getprecompinfo(self.modname)
614
615 return code, self.modname, precompinfo
616
617 def compilesuite(self, major, minor, language, script, fname, precompinfo):
618 """Generate code for a single suite"""
619 [name, desc, code, level, version, events, classes, comps, enums] = self.suite
620 # Sort various lists, so re-generated source is easier compared
621 def class_sorter(k1, k2):
622 """Sort classes by code, and make sure main class sorts before synonyms"""
623 # [name, code, desc, properties, elements] = cls
624 if k1[1] < k2[1]: return -1
625 if k1[1] > k2[1]: return 1
626 if not k2[3] or k2[3][0][1] == 'c@#!':
627 # This is a synonym, the other one is better
628 return -1
629 if not k1[3] or k1[3][0][1] == 'c@#!':
630 # This is a synonym, the other one is better
631 return 1
632 return 0
633
634 events.sort()
635 classes.sort(class_sorter)
636 comps.sort()
637 enums.sort()
638
639 self.fp = fp = open(self.pathname, 'w')
640 MacOS.SetCreatorAndType(self.pathname, 'Pyth', 'TEXT')
641
642 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
643 fp.write("Level %d, version %d\n\n" % (level, version))
644 fp.write("Generated from %s\n"%ascii(fname))
645 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
646 (major, minor, language, script))
647 fp.write('"""\n\n')
648
649 fp.write('import aetools\n')
650 fp.write('import MacOS\n\n')
651 fp.write("_code = %s\n\n"% `code`)
652 if self.basepackage and self.basepackage._code_to_module.has_key(code):
653 # We are an extension of a baseclass (usually an application extending
654 # Standard_Suite or so). Import everything from our base module
655 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code][0])
656 basemodule = self.basepackage._code_to_module[code]
657 elif self.basepackage and self.basepackage._code_to_module.has_key(code.lower()):
658 # This is needed by CodeWarrior and some others.
659 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code.lower()][0])
660 basemodule = self.basepackage._code_to_module[code.lower()]
661 else:
662 # We are not an extension.
663 basemodule = None
664 self.basemodule = basemodule
665 self.compileclassheader()
666
667 self.enumsneeded = {}
668 if events:
669 for event in events:
670 self.compileevent(event)
671 else:
672 fp.write(" pass\n\n")
673
674 objc = ObjectCompiler(fp, self.modname, basemodule, precompinfo, interact=(self.edit_modnames is None),
675 verbose=self.verbose)
676 for cls in classes:
677 objc.compileclass(cls)
678 for cls in classes:
679 objc.fillclasspropsandelems(cls)
680 for comp in comps:
681 objc.compilecomparison(comp)
682 for enum in enums:
683 objc.compileenumeration(enum)
684
685 for enum in self.enumsneeded.keys():
686 objc.checkforenum(enum)
687
688 objc.dumpindex()
689
690 def compileclassheader(self):
691 """Generate class boilerplate"""
692 classname = '%s_Events'%self.modname
693 if self.basemodule:
694 modshortname = string.split(self.basemodule.__name__, '.')[-1]
695 baseclassname = '%s_Events'%modshortname
696 self.fp.write("class %s(%s):\n\n"%(classname, baseclassname))
697 else:
698 self.fp.write("class %s:\n\n"%classname)
699
700 def compileevent(self, event):
701 """Generate code for a single event"""
702 [name, desc, code, subcode, returns, accepts, arguments] = event
703 fp = self.fp
704 funcname = identify(name)
705 #
706 # generate name->keyword map
707 #
708 if arguments:
709 fp.write(" _argmap_%s = {\n"%funcname)
710 for a in arguments:
711 fp.write(" %s : %s,\n"%(`identify(a[0])`, `a[1]`))
712 fp.write(" }\n\n")
713
714 #
715 # Generate function header
716 #
717 has_arg = (not is_null(accepts))
718 opt_arg = (has_arg and is_optional(accepts))
719
720 fp.write(" def %s(self, "%funcname)
721 if has_arg:
722 if not opt_arg:
723 fp.write("_object, ") # Include direct object, if it has one
724 else:
725 fp.write("_object=None, ") # Also include if it is optional
726 else:
727 fp.write("_no_object=None, ") # For argument checking
728 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
729 #
730 # Generate doc string (important, since it may be the only
731 # available documentation, due to our name-remaping)
732 #
733 fp.write(' """%s: %s\n'%(ascii(name), ascii(desc)))
734 if has_arg:
735 fp.write(" Required argument: %s\n"%getdatadoc(accepts))
736 elif opt_arg:
737 fp.write(" Optional argument: %s\n"%getdatadoc(accepts))
738 for arg in arguments:
739 fp.write(" Keyword argument %s: %s\n"%(identify(arg[0]),
740 getdatadoc(arg[2])))
741 fp.write(" Keyword argument _attributes: AppleEvent attribute dictionary\n")
742 if not is_null(returns):
743 fp.write(" Returns: %s\n"%getdatadoc(returns))
744 fp.write(' """\n')
745 #
746 # Fiddle the args so everything ends up in 'arguments' dictionary
747 #
748 fp.write(" _code = %s\n"% `code`)
749 fp.write(" _subcode = %s\n\n"% `subcode`)
750 #
751 # Do keyword name substitution
752 #
753 if arguments:
754 fp.write(" aetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
755 else:
756 fp.write(" if _arguments: raise TypeError, 'No optional args expected'\n")
757 #
758 # Stuff required arg (if there is one) into arguments
759 #
760 if has_arg:
761 fp.write(" _arguments['----'] = _object\n")
762 elif opt_arg:
763 fp.write(" if _object:\n")
764 fp.write(" _arguments['----'] = _object\n")
765 else:
766 fp.write(" if _no_object != None: raise TypeError, 'No direct arg expected'\n")
767 fp.write("\n")
768 #
769 # Do enum-name substitution
770 #
771 for a in arguments:
772 if is_enum(a[2]):
773 kname = a[1]
774 ename = a[2][0]
775 if ename <> '****':
776 fp.write(" aetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
777 (`kname`, identify(ename)))
778 self.enumsneeded[ename] = 1
779 fp.write("\n")
780 #
781 # Do the transaction
782 #
783 fp.write(" _reply, _arguments, _attributes = self.send(_code, _subcode,\n")
784 fp.write(" _arguments, _attributes)\n")
785 #
786 # Error handling
787 #
788 fp.write(" if _arguments.get('errn', 0):\n")
789 fp.write(" raise aetools.Error, aetools.decodeerror(_arguments)\n")
790 fp.write(" # XXXX Optionally decode result\n")
791 #
792 # Decode result
793 #
794 fp.write(" if _arguments.has_key('----'):\n")
795 if is_enum(returns):
796 fp.write(" # XXXX Should do enum remapping here...\n")
797 fp.write(" return _arguments['----']\n")
798 fp.write("\n")
799
800 def findenumsinevent(self, event):
801 """Find all enums for a single event"""
802 [name, desc, code, subcode, returns, accepts, arguments] = event
803 for a in arguments:
804 if is_enum(a[2]):
805 ename = a[2][0]
806 if ename <> '****':
807 self.enumsneeded[ename] = 1
808
Jack Jansen12b2b762000-08-20 19:30:56 +0000809#
810# This class stores the code<->name translations for a single module. It is used
811# to keep the information while we're compiling the module, but we also keep these objects
812# around so if one suite refers to, say, an enum in another suite we know where to
813# find it. Finally, if we really can't find a code, the user can add modules by
814# hand.
815#
816class CodeNameMapper:
Jack Jansen0ae32202003-04-09 13:25:43 +0000817
818 def __init__(self, interact=1, verbose=None):
819 self.code2name = {
820 "property" : {},
821 "class" : {},
822 "enum" : {},
823 "comparison" : {},
824 }
825 self.name2code = {
826 "property" : {},
827 "class" : {},
828 "enum" : {},
829 "comparison" : {},
830 }
831 self.modulename = None
832 self.star_imported = 0
833 self.can_interact = interact
834 self.verbose = verbose
835
836 def addnamecode(self, type, name, code):
837 self.name2code[type][name] = code
838 if not self.code2name[type].has_key(code):
839 self.code2name[type][code] = name
840
841 def hasname(self, name):
842 for dict in self.name2code.values():
843 if dict.has_key(name):
844 return True
845 return False
846
847 def hascode(self, type, code):
848 return self.code2name[type].has_key(code)
849
850 def findcodename(self, type, code):
851 if not self.hascode(type, code):
852 return None, None, None
853 name = self.code2name[type][code]
854 if self.modulename and not self.star_imported:
855 qualname = '%s.%s'%(self.modulename, name)
856 else:
857 qualname = name
858 return name, qualname, self.modulename
859
860 def getall(self, type):
861 return self.code2name[type].items()
862
863 def addmodule(self, module, name, star_imported):
864 self.modulename = name
865 self.star_imported = star_imported
866 for code, name in module._propdeclarations.items():
867 self.addnamecode('property', name, code)
868 for code, name in module._classdeclarations.items():
869 self.addnamecode('class', name, code)
870 for code in module._enumdeclarations.keys():
871 self.addnamecode('enum', '_Enum_'+identify(code), code)
872 for code, name in module._compdeclarations.items():
873 self.addnamecode('comparison', name, code)
874
875 def prepareforexport(self, name=None):
876 if not self.modulename:
877 self.modulename = name
878 return self
879
Jack Jansen66544221997-08-08 14:49:02 +0000880class ObjectCompiler:
Jack Jansen0ae32202003-04-09 13:25:43 +0000881 def __init__(self, fp, modname, basesuite, othernamemappers=None, interact=1,
882 verbose=None):
883 self.fp = fp
884 self.verbose = verbose
885 self.basesuite = basesuite
886 self.can_interact = interact
887 self.modulename = modname
888 self.namemappers = [CodeNameMapper(self.can_interact, self.verbose)]
889 if othernamemappers:
890 self.othernamemappers = othernamemappers[:]
891 else:
892 self.othernamemappers = []
893 if basesuite:
894 basemapper = CodeNameMapper(self.can_interact, self.verbose)
895 basemapper.addmodule(basesuite, '', 1)
896 self.namemappers.append(basemapper)
897
898 def getprecompinfo(self, modname):
899 list = []
900 for mapper in self.namemappers:
901 emapper = mapper.prepareforexport(modname)
902 if emapper:
903 list.append(emapper)
904 return list
905
906 def findcodename(self, type, code):
907 while 1:
908 # First try: check whether we already know about this code.
909 for mapper in self.namemappers:
910 if mapper.hascode(type, code):
911 return mapper.findcodename(type, code)
912 # Second try: maybe one of the other modules knows about it.
913 for mapper in self.othernamemappers:
914 if mapper.hascode(type, code):
915 self.othernamemappers.remove(mapper)
916 self.namemappers.append(mapper)
917 if self.fp:
918 self.fp.write("import %s\n"%mapper.modulename)
919 break
920 else:
921 # If all this has failed we ask the user for a guess on where it could
922 # be and retry.
923 if self.fp:
924 m = self.askdefinitionmodule(type, code)
925 else:
926 m = None
927 if not m: return None, None, None
928 mapper = CodeNameMapper(self.can_interact, self.verbose)
929 mapper.addmodule(m, m.__name__, 0)
930 self.namemappers.append(mapper)
931
932 def hasname(self, name):
933 for mapper in self.othernamemappers:
934 if mapper.hasname(name) and mapper.modulename != self.modulename:
935 if self.verbose:
936 print >>self.verbose, "Duplicate Python identifier:", name, self.modulename, mapper.modulename
937 return True
938 return False
939
940 def askdefinitionmodule(self, type, code):
941 if not self.can_interact:
942 if self.verbose:
943 print >>self.verbose, "** No definition for %s '%s' found" % (type, code)
944 return None
945 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
946 if not path: return
947 path, file = os.path.split(path)
948 modname = os.path.splitext(file)[0]
949 if not path in sys.path:
950 sys.path.insert(0, path)
951 m = __import__(modname)
952 self.fp.write("import %s\n"%modname)
953 return m
954
955 def compileclass(self, cls):
956 [name, code, desc, properties, elements] = cls
957 pname = identify(name)
958 if self.namemappers[0].hascode('class', code):
959 # plural forms and such
960 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
961 if self.fp:
962 self.fp.write("\n%s = %s\n"%(pname, othername))
963 else:
964 if self.fp:
965 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
966 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(desc)))
967 self.fp.write(' want = %s\n' % `code`)
968 self.namemappers[0].addnamecode('class', pname, code)
969 properties.sort()
970 for prop in properties:
971 self.compileproperty(prop)
972 elements.sort()
973 for elem in elements:
974 self.compileelement(elem)
975
976 def compileproperty(self, prop):
977 [name, code, what] = prop
978 if code == 'c@#!':
979 # Something silly with plurals. Skip it.
980 return
981 pname = identify(name)
982 if self.namemappers[0].hascode('property', code):
983 # plural forms and such
984 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
985 if pname == othername:
986 return
987 if self.fp:
988 self.fp.write("\n_Prop_%s = _Prop_%s\n"%(pname, othername))
989 else:
990 if self.hasname(pname):
991 pass
992 if self.fp:
993 self.fp.write("class _Prop_%s(aetools.NProperty):\n" % pname)
994 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(what[1])))
995 self.fp.write(" which = %s\n" % `code`)
996 self.fp.write(" want = %s\n" % `what[0]`)
997 self.namemappers[0].addnamecode('property', pname, code)
998
999 def compileelement(self, elem):
1000 [code, keyform] = elem
1001 if self.fp:
1002 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +00001003
Jack Jansen0ae32202003-04-09 13:25:43 +00001004 def fillclasspropsandelems(self, cls):
1005 [name, code, desc, properties, elements] = cls
1006 cname = identify(name)
1007 if self.namemappers[0].hascode('class', code) and \
1008 self.namemappers[0].findcodename('class', code)[0] != cname:
1009 # This is an other name (plural or so) for something else. Skip.
1010 if self.fp and (elements or len(properties) > 1 or (len(properties) == 1 and
1011 properties[0][1] != 'c@#!')):
1012 if self.verbose:
1013 print >>self.verbose, '** Skip multiple %s of %s (code %s)' % (cname, self.namemappers[0].findcodename('class', code)[0], `code`)
1014 raise RuntimeError, "About to skip non-empty class"
1015 return
1016 plist = []
1017 elist = []
1018 superclasses = []
1019 for prop in properties:
1020 [pname, pcode, what] = prop
1021 if pcode == "c@#^":
1022 superclasses.append(what)
1023 if pcode == 'c@#!':
1024 continue
1025 pname = identify(pname)
1026 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +00001027
Jack Jansen0ae32202003-04-09 13:25:43 +00001028 superclassnames = []
1029 for superclass in superclasses:
1030 superId, superDesc, dummy = superclass
1031 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
1032 # I don't think this is correct:
1033 if superclassname == cname:
1034 pass # superclassnames.append(fullyqualifiedname)
1035 else:
1036 superclassnames.append(superclassname)
Jack Jansen8b777672002-08-07 14:49:00 +00001037
Jack Jansen0ae32202003-04-09 13:25:43 +00001038 if self.fp:
1039 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
Jack Jansen8b777672002-08-07 14:49:00 +00001040
Jack Jansen0ae32202003-04-09 13:25:43 +00001041 for elem in elements:
1042 [ecode, keyform] = elem
1043 if ecode == 'c@#!':
1044 continue
1045 name, ename, module = self.findcodename('class', ecode)
1046 if not name:
1047 if self.fp:
1048 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
1049 else:
1050 elist.append((name, ename))
1051
1052 plist.sort()
1053 elist.sort()
1054
1055 if self.fp:
1056 self.fp.write("%s._privpropdict = {\n"%cname)
1057 for n in plist:
1058 self.fp.write(" '%s' : _Prop_%s,\n"%(n, n))
1059 self.fp.write("}\n")
1060 self.fp.write("%s._privelemdict = {\n"%cname)
1061 for n, fulln in elist:
1062 self.fp.write(" '%s' : %s,\n"%(n, fulln))
1063 self.fp.write("}\n")
1064
1065 def compilecomparison(self, comp):
1066 [name, code, comment] = comp
1067 iname = identify(name)
1068 self.namemappers[0].addnamecode('comparison', iname, code)
1069 if self.fp:
1070 self.fp.write("class %s(aetools.NComparison):\n" % iname)
1071 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(comment)))
1072
1073 def compileenumeration(self, enum):
1074 [code, items] = enum
1075 name = "_Enum_%s" % identify(code)
1076 if self.fp:
1077 self.fp.write("%s = {\n" % name)
1078 for item in items:
1079 self.compileenumerator(item)
1080 self.fp.write("}\n\n")
1081 self.namemappers[0].addnamecode('enum', name, code)
1082 return code
1083
1084 def compileenumerator(self, item):
1085 [name, code, desc] = item
1086 self.fp.write(" %s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
1087
1088 def checkforenum(self, enum):
1089 """This enum code is used by an event. Make sure it's available"""
1090 name, fullname, module = self.findcodename('enum', enum)
1091 if not name:
1092 if self.fp:
1093 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
1094 return
1095 if module:
1096 if self.fp:
1097 self.fp.write("from %s import %s\n"%(module, name))
1098
1099 def dumpindex(self):
1100 if not self.fp:
1101 return
1102 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
1103
1104 self.fp.write("_classdeclarations = {\n")
1105 classlist = self.namemappers[0].getall('class')
1106 classlist.sort()
1107 for k, v in classlist:
1108 self.fp.write(" %s : %s,\n" % (`k`, v))
1109 self.fp.write("}\n")
1110
1111## self.fp.write("\n_propdeclarations = {\n")
1112## proplist = self.namemappers[0].getall('property')
1113## proplist.sort()
1114## for k, v in proplist:
1115## self.fp.write(" %s : _Prop_%s,\n" % (`k`, v))
1116## self.fp.write("}\n")
1117##
1118## self.fp.write("\n_compdeclarations = {\n")
1119## complist = self.namemappers[0].getall('comparison')
1120## complist.sort()
1121## for k, v in complist:
1122## self.fp.write(" %s : %s,\n" % (`k`, v))
1123## self.fp.write("}\n")
1124##
1125## self.fp.write("\n_enumdeclarations = {\n")
1126## enumlist = self.namemappers[0].getall('enum')
1127## enumlist.sort()
1128## for k, v in enumlist:
1129## self.fp.write(" %s : %s,\n" % (`k`, v))
1130## self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +00001131
1132def compiledata(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001133 [type, description, flags] = data
1134 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
1135
Jack Jansen5ccd8261995-07-17 11:43:20 +00001136def is_null(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001137 return data[0] == 'null'
1138
Jack Jansen5ccd8261995-07-17 11:43:20 +00001139def is_optional(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001140 return (data[2] & 0x8000)
1141
Jack Jansen5ccd8261995-07-17 11:43:20 +00001142def is_enum(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001143 return (data[2] & 0x2000)
1144
Jack Jansen5ccd8261995-07-17 11:43:20 +00001145def getdatadoc(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001146 [type, descr, flags] = data
1147 if descr:
1148 return ascii(descr)
1149 if type == '****':
1150 return 'anything'
1151 if type == 'obj ':
1152 return 'an AE object reference'
1153 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +00001154
1155dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
1156def compiledataflags(flags):
Jack Jansen0ae32202003-04-09 13:25:43 +00001157 bits = []
1158 for i in range(16):
1159 if flags & (1<<i):
1160 if i in dataflagdict.keys():
1161 bits.append(dataflagdict[i])
1162 else:
1163 bits.append(`i`)
1164 return '[%s]' % string.join(bits)
1165
Jack Jansen8b777672002-08-07 14:49:00 +00001166def ascii(str):
Jack Jansen0ae32202003-04-09 13:25:43 +00001167 """Return a string with all non-ascii characters hex-encoded"""
1168 if type(str) != type(''):
1169 return map(ascii, str)
1170 rv = ''
1171 for c in str:
1172 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
1173 rv = rv + c
1174 else:
1175 rv = rv + '\\' + 'x%02.2x' % ord(c)
1176 return rv
1177
Jack Jansen5ccd8261995-07-17 11:43:20 +00001178def identify(str):
Jack Jansen0ae32202003-04-09 13:25:43 +00001179 """Turn any string into an identifier:
1180 - replace space by _
1181 - replace other illegal chars by _xx_ (hex code)
1182 - prepend _ if the result is a python keyword
1183 """
1184 if not str:
1185 return "empty_ae_name_"
1186 rv = ''
1187 ok = string.ascii_letters + '_'
1188 ok2 = ok + string.digits
1189 for c in str:
1190 if c in ok:
1191 rv = rv + c
1192 elif c == ' ':
1193 rv = rv + '_'
1194 else:
1195 rv = rv + '_%02.2x_'%ord(c)
1196 ok = ok2
1197 if keyword.iskeyword(rv):
1198 rv = rv + '_'
1199 return rv
Jack Jansen5ccd8261995-07-17 11:43:20 +00001200
1201# Call the main program
1202
1203if __name__ == '__main__':
Jack Jansen0ae32202003-04-09 13:25:43 +00001204 main()
1205 sys.exit(1)