More python2 -> python3 compatibility

This commit is contained in:
rocky
2015-12-11 17:54:30 -05:00
parent d3c732298c
commit b3c8cbb83f
19 changed files with 387 additions and 286 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.pyc *.pyc
*~ *~
/.python-version /.python-version
/uncompyle6.egg-info
build build

56
__pkginfo__.py Normal file
View File

@@ -0,0 +1,56 @@
"""uncompyle6 packaging information"""
# To the extent possible we make this file look more like a
# configuration file rather than code like setup.py. I find putting
# configuration stuff in the middle of a function call in setup.py,
# which for example requires commas in between parameters, is a little
# less elegant than having it here with reduced code, albeit there
# still is some room for improvement.
# Things that change more often go here.
copyright = """
Copyright (C) 2015 Rocky Bernstein <rocky@gnu.org>.
"""
# classifiers = ['Development Status :: 5 - Production/Stable',
# 'Environment :: Console',
# 'Intended Audience :: Developers',
# 'License :: OSI Approved :: GNU General Public License (GPL)',
# 'Operating System :: OS Independent',
# 'Programming Language :: Python',
# 'Topic :: Software Development :: Debuggers',
# 'Topic :: Software Development :: Libraries :: Python Modules',
# ]
# The rest in alphabetic order
author = "Rocky Bernstein"
author_email = "rocky@gnu.org"
ftp_url = None
# license = 'GPL'
mailing_list = 'python-debugger@googlegroups.com'
modname = 'uncompyle6'
packages = ['uncompyle6', 'uncompyle6.opcodes']
py_modules = None
short_desc = 'Python byte-code to source-code converter'
import os
import os.path, sys
def get_srcdir():
filename = os.path.normcase(os.path.dirname(os.path.abspath(__file__)))
return os.path.realpath(filename)
# VERSION.py sets variable VERSION.
ns = {}
version = '2.0'
web = 'https://github.com/rocky/uncompyle6/'
# tracebacks in zip files are funky and not debuggable
zip_safe = False
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
long_description = ( read("README.rst") + '\n' )

View File

@@ -1,15 +1,38 @@
#! python #! python
"""Setup script for the 'uncompyle' distribution.""" """Setup script for the 'uncompyle6' distribution."""
from distutils.core import setup, Extension from distutils.core import setup, Extension
setup (name = "uncompyle6", # Get the package information used in setup().
version = "2.0", # from __pkginfo__ import \
description = "Python byte-code to source-code converter", # author, author_email, classifiers, \
author = "Mysterie", # install_requires, license, long_description, \
author_email = "kajusska@gmail.com", # modname, packages, py_modules, \
url = "http://github.com/Mysterie/uncompyle2", # short_desc, version, web, zip_safe
packages=['uncompyle6', 'uncompyle6.opcode'],
scripts=['scripts/uncompyle6'] from __pkginfo__ import \
) author, author_email, \
long_description, \
modname, packages, py_modules, \
short_desc, version, web, zip_safe
__import__('pkg_resources')
from setuptools import setup
setup(
author = author,
author_email = author_email,
# classifiers = classifiers,
description = short_desc,
# install_requires = install_requires,
# license = license,
long_description = long_description,
py_modules = py_modules,
name = modname,
packages = packages,
test_suite = 'nose.collector',
url = web,
setup_requires = ['nose>=1.0'],
version = version,
zip_safe = zip_safe)

View File

@@ -1,4 +1,6 @@
#! python #!/usr/bin/env python
from __future__ import print_function
""" """
compile_tests -- compile test patterns for the decompyle test suite compile_tests -- compile test patterns for the decompyle test suite
""" """
@@ -20,8 +22,8 @@ for opt, val in opts:
if args: if args:
raise 'This tool does not want any arguments' raise 'This tool does not want any arguments'
print "Using files in dir %s" % src_dir print("Using files in dir %s" % src_dir)
print "Compiling into dir %s" % work_dir print("Compiling into dir %s" % work_dir)
tests = {} tests = {}
@@ -46,6 +48,7 @@ tests['2.3'] = tests['2.2']
tests['2.5'] = tests['2.3'] tests['2.5'] = tests['2.3']
tests['2.6'] = tests['2.5'] tests['2.6'] = tests['2.5']
tests['2.7'] = ['mine'] + tests['2.6'] tests['2.7'] = ['mine'] + tests['2.6']
tests['3.4'] = ['mine']
total_tests = len(tests['2.7']) total_tests = len(tests['2.7'])
#tests['2.2'].sort(); print tests['2.2'] #tests['2.2'].sort(); print tests['2.2']
@@ -68,7 +71,7 @@ try:
except AttributeError: except AttributeError:
version = sys.version[:3] version = sys.version[:3]
print 'Compiling test files for Python', version, print('Compiling test files for Python', version)
print '(%i/%i files)' % (len(tests[version]), total_tests) print('(%i/%i files)' % (len(tests[version]), total_tests))
compile_for_version(version) compile_for_version(version)
print 'Done.' print('Done.')

View File

@@ -1,4 +1,5 @@
#! python #!/usr/bin/env python
from __future__ import print_function
''' '''
test_pythonlib -- uncompyle and verify Python libraries test_pythonlib -- uncompyle and verify Python libraries
@@ -19,7 +20,7 @@ Step 2: Run the test:
test_pythonlib --mylib --verify # decompile verify 'mylib' test_pythonlib --mylib --verify # decompile verify 'mylib'
''' '''
from uncompyle2 import main, verify from uncompyle6 import main, verify
import getopt, sys import getopt, sys
import os, time, shutil import os, time, shutil
from fnmatch import fnmatch from fnmatch import fnmatch
@@ -44,11 +45,11 @@ test_options = {
#----- #-----
def help(): def help():
print 'Usage-Examples:' print('Usage-Examples:')
print 'test_pythonlib --all # decompile all tests (suite + libs)' print('test_pythonlib --all # decompile all tests (suite + libs)')
print 'test_pythonlib --all --verify # decomyile all tests and verify results' print('test_pythonlib --all --verify # decomyile all tests and verify results')
print 'test_pythonlib --test # decompile only the testsuite' print('test_pythonlib --test # decompile only the testsuite')
print 'test_pythonlib --2.2 --verify # decompile and verify python lib 2.2' print('test_pythonlib --2.2 --verify # decompile and verify python lib 2.2')
def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=0): def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=0):
def visitor(files, dirname, names): def visitor(files, dirname, names):
@@ -69,12 +70,12 @@ def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=0):
try: try:
start_with = files.index(start_with) start_with = files.index(start_with)
files = files[start_with:] files = files[start_with:]
print '>>> starting with file', files[0] print('>>> starting with file', files[0])
except ValueError: except ValueError:
pass pass
print time.ctime() print(time.ctime())
print 'Working directory: ', src_dir print('Working directory: ', src_dir)
try: try:
main(src_dir, target_dir, files, [], do_verify=do_verify) main(src_dir, target_dir, files, [], do_verify=do_verify)
except (KeyboardInterrupt, OSError): except (KeyboardInterrupt, OSError):

View File

@@ -1,6 +1,6 @@
import uncompyle2 import uncompyle6
from uncompyle2 import uncompyle, walker, verify, magics from uncompyle6 import uncompyle, walker, verify, magics
from uncompyle2.spark import GenericASTTraversal, GenericASTTraversalPruningException from uncompyle6.spark import GenericASTTraversal, GenericASTTraversalPruningException
import sys, inspect, types, cStringIO import sys, inspect, types, cStringIO
from collections import namedtuple from collections import namedtuple
@@ -137,7 +137,7 @@ def uncompyle_find(version, co, find_offset, out=sys.stdout, showasm=0, showast=
# store final output stream for case of error # store final output stream for case of error
__real_out = out or sys.stdout __real_out = out or sys.stdout
if version == 2.7: if version == 2.7:
import uncompyle2.scanner27 as scan import uncompyle6.scanner27 as scan
scanner = scan.Scanner27() scanner = scan.Scanner27()
elif version == 2.6: elif version == 2.6:
import scanner26 as scan import scanner26 as scan

View File

@@ -1,3 +1,6 @@
from __future__ import print_function
''' '''
Copyright (c) 1999 John Aycock Copyright (c) 1999 John Aycock
Copyright (c) 2000 by hartmut Goebel <h.goebel@crazy-compilers.com> Copyright (c) 2000 by hartmut Goebel <h.goebel@crazy-compilers.com>
@@ -68,9 +71,9 @@ def _load_module(filename):
try: try:
version = float(magics.versions[magic]) version = float(magics.versions[magic])
except KeyError: except KeyError:
raise ImportError, "Unknown magic number %s in %s" % (ord(magic[0])+256*ord(magic[1]), filename) raise ImportError("Unknown magic number %s in %s" % (ord(magic[0])+256*ord(magic[1]), filename))
if (version > 2.7) or (version < 2.5): if (version > 2.7) or (version < 2.5):
raise ImportError, "This is a Python %s file! Only Python 2.5 to 2.7 files are supported." % version raise ImportError("This is a Python %s file! Only Python 2.5 to 2.7 files are supported." % version)
#print version #print version
fp.read(4) # timestamp fp.read(4) # timestamp
co = dis.marshalLoad(fp) co = dis.marshalLoad(fp)
@@ -105,7 +108,7 @@ def uncompyle(version, co, out=None, showasm=0, showast=0):
walk = walker.Walker(out, scanner, showast=showast) walk = walker.Walker(out, scanner, showast=showast)
try: try:
ast = walk.build_ast(tokens, customize) ast = walk.build_ast(tokens, customize)
except walker.ParserError, e : # parser failed, dump disassembly except walker.ParserError as e : # parser failed, dump disassembly
print >>__real_out, e print >>__real_out, e
raise raise
del tokens # save memory del tokens # save memory
@@ -223,9 +226,9 @@ def main(in_base, out_base, files, codes, outfile=None,
if do_verify: if do_verify:
try: try:
verify.compare_code_with_srcfile(infile, outfile) verify.compare_code_with_srcfile(infile, outfile)
if not outfile: print '\n# okay decompyling', infile, __memUsage() if not outfile: print('\n# okay decompyling', infile, __memUsage())
okay_files += 1 okay_files += 1
except verify.VerifyCmpError, e: except verify.VerifyCmpError as e:
verify_failed_files += 1 verify_failed_files += 1
os.rename(outfile, outfile + '_unverified') os.rename(outfile, outfile + '_unverified')
if not outfile: if not outfile:
@@ -233,7 +236,7 @@ def main(in_base, out_base, files, codes, outfile=None,
print >>sys.stderr, e print >>sys.stderr, e
else: else:
okay_files += 1 okay_files += 1
if not outfile: print '\n# okay decompyling', infile, __memUsage() if not outfile: print('\n# okay decompyling', infile, __memUsage())
if outfile: if outfile:
sys.stdout.write("decompiled %i files: %i okay, %i failed, %i verify failed\r" % (tot_files, okay_files, failed_files, verify_failed_files)) sys.stdout.write("decompiled %i files: %i okay, %i failed, %i verify failed\r" % (tot_files, okay_files, failed_files, verify_failed_files))
sys.stdout.flush() sys.stdout.flush()

View File

@@ -1,16 +1,20 @@
from __future__ import print_function from __future__ import print_function
import struct
import struct, sys
__all__ = ['magics', 'versions'] __all__ = ['magics', 'versions']
def __build_magic(magic): def __build_magic(magic):
if (sys.version_info > (3, 0)):
return struct.pack('Hcc', magic, bytes('\r', 'utf-8'), bytes('\n', 'utf-8'))
else:
return struct.pack('Hcc', magic, '\r', '\n') return struct.pack('Hcc', magic, '\r', '\n')
by_magic = {} by_magic = {}
by_version = {} by_version = {}
def __by_version(magics): def __by_version(magics):
for m, v in magics.items(): for m, v in list(magics.items()):
by_magic[m] = v by_magic[m] = v
by_version[v] = m by_version[v] = m
return by_version return by_version
@@ -77,6 +81,7 @@ def test():
magic_20 = magics['2.0'] magic_20 = magics['2.0']
current = imp.get_magic() current = imp.get_magic()
current_version = struct.unpack('HBB', current)[0] current_version = struct.unpack('HBB', current)[0]
from trepan.api import debug; debug()
magic_current = by_magic[ current ] magic_current = by_magic[ current ]
print(type(magic_20), len(magic_20), repr(magic_20)) print(type(magic_20), len(magic_20), repr(magic_20))
print() print()

View File

@@ -1,3 +1,5 @@
from __future__ import print_function
''' '''
Copyright (c) 1999 John Aycock Copyright (c) 1999 John Aycock
Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com> Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
@@ -9,7 +11,12 @@
__all__ = ['parse', 'AST', 'ParserError', 'Parser'] __all__ = ['parse', 'AST', 'ParserError', 'Parser']
from spark import GenericASTBuilder from spark import GenericASTBuilder
import string, exceptions, sys import string, sys
if (sys.version_info > (3, 0)):
intern = sys.intern
from collections import UserList
else:
from UserList import UserList from UserList import UserList
from scanner import Token from scanner import Token
@@ -47,6 +54,7 @@ class ParserError(Exception):
class Parser(GenericASTBuilder): class Parser(GenericASTBuilder):
def __init__(self): def __init__(self):
GenericASTBuilder.__init__(self, AST, 'stmts') GenericASTBuilder.__init__(self, AST, 'stmts')
self.customized = {} self.customized = {}
@@ -57,7 +65,7 @@ class Parser(GenericASTBuilder):
collector to collect this object. collector to collect this object.
""" """
for dict in (self.rule2func, self.rules, self.rule2name): for dict in (self.rule2func, self.rules, self.rule2name):
for i in dict.keys(): for i in list(dict.keys()):
dict[i] = None dict[i] = None
for i in dir(self): for i in dir(self):
setattr(self, i, None) setattr(self, i, None)
@@ -704,7 +712,7 @@ class Parser(GenericASTBuilder):
def __ambiguity(self, children): def __ambiguity(self, children):
# only for debugging! to be removed hG/2000-10-15 # only for debugging! to be removed hG/2000-10-15
print children print(children)
return GenericASTBuilder.ambiguity(self, children) return GenericASTBuilder.ambiguity(self, children)
def resolve(self, list): def resolve(self, list):
@@ -737,9 +745,9 @@ def parse(tokens, customize):
# expr ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP # expr ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP
# #
global p global p
for k, v in customize.items(): for k, v in list(customize.items()):
# avoid adding the same rule twice to this parser # avoid adding the same rule twice to this parser
if p.customized.has_key(k): if k in p.customized:
continue continue
p.customized[k] = None p.customized[k] = None

View File

@@ -12,7 +12,7 @@ from array import array
from operator import itemgetter from operator import itemgetter
from struct import * from struct import *
from uncompyle2.opcode.opcode_25 import * from uncompyle6.opcodes.opcode_25 import *
import disas as dis import disas as dis
import scanner as scan import scanner as scan

View File

@@ -12,7 +12,7 @@ from array import array
from operator import itemgetter from operator import itemgetter
from struct import * from struct import *
from uncompyle2.opcode.opcode_26 import * from uncompyle6.opcodes.opcode_26 import *
import disas as dis import disas as dis
import scanner as scan import scanner as scan
@@ -894,4 +894,3 @@ class Scanner26(scan.Scanner):
label = self.fixed_jumps[i] label = self.fixed_jumps[i]
targets[label] = targets.get(label, []) + [i] targets[label] = targets.get(label, []) + [i]
return targets return targets

View File

@@ -11,7 +11,7 @@ from collections import namedtuple
from array import array from array import array
from operator import itemgetter from operator import itemgetter
from uncompyle2.opcode.opcode_27 import * from uncompyle6.opcodes.opcode_27 import *
import disas as dis import disas as dis
import scanner as scan import scanner as scan

View File

@@ -1,3 +1,4 @@
from __future__ import print_function
''' '''
Copyright (c) 1998-2002 John Aycock Copyright (c) 1998-2002 John Aycock
@@ -28,31 +29,31 @@ def _namelist(instance):
for c in classlist: for c in classlist:
for b in c.__bases__: for b in c.__bases__:
classlist.append(b) classlist.append(b)
for name in c.__dict__.keys(): for name in list(c.__dict__.keys()):
if not namedict.has_key(name): if name not in namedict:
namelist.append(name) namelist.append(name)
namedict[name] = 1 namedict[name] = 1
return namelist return namelist
#
# Extracted from GenericParser and made global so that [un]picking works.
#
class _State: class _State:
'''
Extracted from GenericParser and made global so that [un]picking works.
'''
def __init__(self, stateno, items): def __init__(self, stateno, items):
self.T, self.complete, self.items = [], [], items self.T, self.complete, self.items = [], [], items
self.stateno = stateno self.stateno = stateno
class GenericParser: class GenericParser:
# '''
# An Earley parser, as per J. Earley, "An Efficient Context-Free An Earley parser, as per J. Earley, "An Efficient Context-Free
# Parsing Algorithm", CACM 13(2), pp. 94-102. Also J. C. Earley, Parsing Algorithm", CACM 13(2), pp. 94-102. Also J. C. Earley,
# "An Efficient Context-Free Parsing Algorithm", Ph.D. thesis, "An Efficient Context-Free Parsing Algorithm", Ph.D. thesis,
# Carnegie-Mellon University, August 1968. New formulation of Carnegie-Mellon University, August 1968. New formulation of
# the parser according to J. Aycock, "Practical Earley Parsing the parser according to J. Aycock, "Practical Earley Parsing
# and the SPARK Toolkit", Ph.D. thesis, University of Victoria, and the SPARK Toolkit", Ph.D. thesis, University of Victoria,
# 2001, and J. Aycock and R. N. Horspool, "Practical Earley 2001, and J. Aycock and R. N. Horspool, "Practical Earley
# Parsing", unpublished paper, 2001. Parsing", unpublished paper, 2001.
# '''
def __init__(self, start): def __init__(self, start):
self.rules = {} self.rules = {}
@@ -90,7 +91,7 @@ class GenericParser:
changes = 1 changes = 1
while changes: while changes:
changes = 0 changes = 0
for k, v in self.edges.items(): for k, v in list(self.edges.items()):
if v is None: if v is None:
state, sym = k state, sym = k
if self.states.has_key(state): if self.states.has_key(state):
@@ -127,12 +128,12 @@ class GenericParser:
rules = doc.split() rules = doc.split()
index = [] index = []
for i in xrange(len(rules)): for i in range(len(rules)):
if rules[i] == '::=': if rules[i] == '::=':
index.append(i-1) index.append(i-1)
index.append(len(rules)) index.append(len(rules))
for i in xrange(len(index)-1): for i in range(len(index)-1):
lhs = rules[index[i]] lhs = rules[index[i]]
rhs = rules[index[i]+2:index[i+1]] rhs = rules[index[i]+2:index[i+1]]
rule = (lhs, tuple(rhs)) rule = (lhs, tuple(rhs))
@@ -140,7 +141,7 @@ class GenericParser:
if _preprocess: if _preprocess:
rule, fn = self.preprocess(rule, func) rule, fn = self.preprocess(rule, func)
if self.rules.has_key(lhs): if lhs in self.rules:
self.rules[lhs].append(rule) self.rules[lhs].append(rule)
else: else:
self.rules[lhs] = [ rule ] self.rules[lhs] = [ rule ]
@@ -163,7 +164,7 @@ class GenericParser:
self.nullable = {} self.nullable = {}
tbd = [] tbd = []
for rulelist in self.rules.values(): for rulelist in list(self.rules.values()):
lhs = rulelist[0][0] lhs = rulelist[0][0]
self.nullable[lhs] = 0 self.nullable[lhs] = 0
for rule in rulelist: for rule in rulelist:
@@ -178,7 +179,7 @@ class GenericParser:
# grammars. # grammars.
# #
for sym in rhs: for sym in rhs:
if not self.rules.has_key(sym): if sym not in self.rules:
break break
else: else:
tbd.append(rule) tbd.append(rule)
@@ -212,7 +213,7 @@ class GenericParser:
def makeNewRules(self): def makeNewRules(self):
worklist = [] worklist = []
for rulelist in self.rules.values(): for rulelist in list(self.rules.values()):
for rule in rulelist: for rule in rulelist:
worklist.append((rule, 0, 1, rule)) worklist.append((rule, 0, 1, rule))
@@ -221,7 +222,7 @@ class GenericParser:
n = len(rhs) n = len(rhs)
while i < n: while i < n:
sym = rhs[i] sym = rhs[i]
if not self.rules.has_key(sym) or \ if sym not in self.rules or \
not self.nullable[sym]: not self.nullable[sym]:
candidate = 0 candidate = 0
i = i + 1 i = i + 1
@@ -238,7 +239,7 @@ class GenericParser:
if candidate: if candidate:
lhs = self._NULLABLE+lhs lhs = self._NULLABLE+lhs
rule = (lhs, rhs) rule = (lhs, rhs)
if self.newrules.has_key(lhs): if lhs in self.newrules:
self.newrules[lhs].append(rule) self.newrules[lhs].append(rule)
else: else:
self.newrules[lhs] = [ rule ] self.newrules[lhs] = [ rule ]
@@ -248,7 +249,7 @@ class GenericParser:
return None return None
def error(self, token): def error(self, token):
print "Syntax error at or near `%s' token" % token print("Syntax error at or near `%s' token" % token)
raise SystemExit raise SystemExit
def parse(self, tokens): def parse(self, tokens):
@@ -292,7 +293,8 @@ class GenericParser:
# #
return self._NULLABLE == sym[0:len(self._NULLABLE)] return self._NULLABLE == sym[0:len(self._NULLABLE)]
def skip(self, (lhs, rhs), pos=0): def skip(self, xxx_todo_changeme, pos=0):
(lhs, rhs) = xxx_todo_changeme
n = len(rhs) n = len(rhs)
while pos < n: while pos < n:
if not self.isnullable(rhs[pos]): if not self.isnullable(rhs[pos]):
@@ -551,12 +553,12 @@ class GenericParser:
rule = self.ambiguity(self.newrules[nt]) rule = self.ambiguity(self.newrules[nt])
else: else:
rule = self.newrules[nt][0] rule = self.newrules[nt][0]
#print rule # print(rule)
rhs = rule[1] rhs = rule[1]
attr = [None] * len(rhs) attr = [None] * len(rhs)
for i in xrange(len(rhs)-1, -1, -1): for i in range(len(rhs)-1, -1, -1):
attr[i] = self.deriveEpsilon(rhs[i]) attr[i] = self.deriveEpsilon(rhs[i])
return self.rule2func[self.new2old[rule]](attr) return self.rule2func[self.new2old[rule]](attr)
@@ -570,12 +572,12 @@ class GenericParser:
rule = choices[0] rule = choices[0]
if len(choices) > 1: if len(choices) > 1:
rule = self.ambiguity(choices) rule = self.ambiguity(choices)
#print rule # print(rule)
rhs = rule[1] rhs = rule[1]
attr = [None] * len(rhs) attr = [None] * len(rhs)
for i in xrange(len(rhs)-1, -1, -1): for i in range(len(rhs)-1, -1, -1):
sym = rhs[i] sym = rhs[i]
if not self.newrules.has_key(sym): if not self.newrules.has_key(sym):
if sym != self._BOF: if sym != self._BOF:
@@ -599,24 +601,23 @@ class GenericParser:
# appears in >1 method. Also undefined results if rules # appears in >1 method. Also undefined results if rules
# causing the ambiguity appear in the same method. # causing the ambiguity appear in the same method.
# #
sortlist = [] sortlist = []
name2index = {} name2index = {}
for i in xrange(len(rules)): for i in range(len(rules)):
lhs, rhs = rule = rules[i] lhs, rhs = rule = rules[i]
name = self.rule2name[self.new2old[rule]] name = self.rule2name[self.new2old[rule]]
sortlist.append((len(rhs), name)) sortlist.append((len(rhs), name))
name2index[name] = i name2index[name] = i
sortlist.sort() sortlist.sort()
list = map(lambda (a,b): b, sortlist) list = [a_b[1] for a_b in sortlist]
return rules[name2index[self.resolve(list)]] return rules[name2index[self.resolve(list)]]
def resolve(self, list): def resolve(self, list):
# '''
# Resolve ambiguity in favor of the shortest RHS. Resolve ambiguity in favor of the shortest RHS.
# Since we walk the tree from the top down, this Since we walk the tree from the top down, this
# should effectively resolve in favor of a "shift". should effectively resolve in favor of a "shift".
# '''
return list[0] return list[0]
# #
@@ -645,28 +646,29 @@ class GenericASTBuilder(GenericParser):
if isinstance(arg, self.AST): if isinstance(arg, self.AST):
children.append(arg) children.append(arg)
else: else:
children.append(arg) children.append(self.terminal(arg))
return self.nonterminal(lhs, children) return self.nonterminal(lhs, children)
def terminal(self, token): return token
def nonterminal(self, type, args): def nonterminal(self, type, args):
rv = self.AST(type) rv = self.AST(type)
rv[:len(args)] = args rv[:len(args)] = args
return rv return rv
#
# GenericASTTraversal is a Visitor pattern according to Design Patterns. For
# each node it attempts to invoke the method n_<node type>, falling
# back onto the default() method if the n_* can't be found. The preorder
# traversal also looks for an exit hook named n_<node type>_exit (no default
# routine is called if it's not found). To prematurely halt traversal
# of a subtree, call the prune() method -- this only makes sense for a
# preorder traversal. Node type is determined via the typestring() method.
#
class GenericASTTraversalPruningException: class GenericASTTraversalPruningException:
pass pass
class GenericASTTraversal: class GenericASTTraversal:
'''
GenericASTTraversal is a Visitor pattern according to Design Patterns. For
each node it attempts to invoke the method n_<node type>, falling
back onto the default() method if the n_* can't be found. The preorder
traversal also looks for an exit hook named n_<node type>_exit (no default
routine is called if it's not found). To prematurely halt traversal
of a subtree, call the prune() method -- this only makes sense for a
preorder traversal. Node type is determined via the typestring() method.
'''
def __init__(self, ast): def __init__(self, ast):
self.ast = ast self.ast = ast