296 lines
8.0 KiB
Python
296 lines
8.0 KiB
Python
"Usage: unparse.py <path to source file>"
|
|
import sys
|
|
|
|
class Unparser:
|
|
"""Methods in this class recursively traverse an AST and
|
|
output source code for the abstract syntax; original formatting
|
|
is disregarged. """
|
|
|
|
def __init__(self, tree, file = sys.stdout):
|
|
"""Unparser(tree, file=sys.stdout) -> None.
|
|
Print the source for tree to file."""
|
|
self.f = file
|
|
self._indent = 0
|
|
self.dispatch(tree)
|
|
print >>self.f,""
|
|
self.f.flush()
|
|
|
|
def fill(self, text = ""):
|
|
"Indent a piece of text, according to the current indentation level"
|
|
self.f.write("\n"+" "*self._indent + text)
|
|
|
|
def write(self, text):
|
|
"Append a piece of text to the current line."
|
|
self.f.write(text)
|
|
|
|
def enter(self):
|
|
"Print ':', and increase the indentation."
|
|
self.write(":")
|
|
self._indent += 1
|
|
|
|
def leave(self):
|
|
"Decrease the indentation level."
|
|
self._indent -= 1
|
|
|
|
def dispatch(self, tree):
|
|
"Dispatcher function, dispatching tree type T to method _T."
|
|
if isinstance(tree, list):
|
|
for t in tree:
|
|
self.dispatch(t)
|
|
return
|
|
meth = getattr(self, "_"+tree.__class__.__name__)
|
|
meth(tree)
|
|
|
|
|
|
############### Unparsing methods ######################
|
|
# There should be one method per concrete grammar type #
|
|
# Constructors should be grouped by sum type. Ideally, #
|
|
# this would follow the order in the grammar, but #
|
|
# currently doesn't. #
|
|
########################################################
|
|
|
|
def _Module(self, tree):
|
|
for stmt in tree.body:
|
|
self.dispatch(stmt)
|
|
|
|
# stmt
|
|
def _Expr(self, tree):
|
|
self.fill()
|
|
self.dispatch(tree.value)
|
|
|
|
def _Import(self, t):
|
|
self.fill("import ")
|
|
first = True
|
|
for a in t.names:
|
|
if first:
|
|
first = False
|
|
else:
|
|
self.write(", ")
|
|
self.write(a.name)
|
|
if a.asname:
|
|
self.write(" as "+a.asname)
|
|
|
|
def _Assign(self, t):
|
|
self.fill()
|
|
for target in t.targets:
|
|
self.dispatch(target)
|
|
self.write(" = ")
|
|
self.dispatch(t.value)
|
|
|
|
def _AugAssign(self, t):
|
|
self.fill()
|
|
self.dispatch(t.target)
|
|
self.write(" "+self.binop[t.op.__class__.__name__]+"= ")
|
|
self.dispatch(t.value)
|
|
|
|
def _Return(self, t):
|
|
self.fill("return ")
|
|
if t.value:
|
|
self.dispatch(t.value)
|
|
|
|
def _Print(self, t):
|
|
self.fill("print ")
|
|
do_comma = False
|
|
if t.dest:
|
|
self.write(">>")
|
|
self.dispatch(t.dest)
|
|
do_comma = True
|
|
for e in t.values:
|
|
if do_comma:self.write(", ")
|
|
else:do_comma=True
|
|
self.dispatch(e)
|
|
if not t.nl:
|
|
self.write(",")
|
|
|
|
def _ClassDef(self, t):
|
|
self.write("\n")
|
|
self.fill("class "+t.name)
|
|
if t.bases:
|
|
self.write("(")
|
|
for a in t.bases:
|
|
self.dispatch(a)
|
|
self.write(", ")
|
|
self.write(")")
|
|
self.enter()
|
|
self.dispatch(t.body)
|
|
self.leave()
|
|
|
|
def _FunctionDef(self, t):
|
|
self.write("\n")
|
|
self.fill("def "+t.name + "(")
|
|
self.dispatch(t.args)
|
|
self.enter()
|
|
self.dispatch(t.body)
|
|
self.leave()
|
|
|
|
def _If(self, t):
|
|
self.fill("if ")
|
|
self.dispatch(t.test)
|
|
self.enter()
|
|
# XXX elif?
|
|
self.dispatch(t.body)
|
|
self.leave()
|
|
if t.orelse:
|
|
self.fill("else")
|
|
self.enter()
|
|
self.dispatch(t.orelse)
|
|
self.leave()
|
|
|
|
def _For(self, t):
|
|
self.fill("for ")
|
|
self.dispatch(t.target)
|
|
self.write(" in ")
|
|
self.dispatch(t.iter)
|
|
self.enter()
|
|
self.dispatch(t.body)
|
|
self.leave()
|
|
if t.orelse:
|
|
self.fill("else")
|
|
self.enter()
|
|
self.dispatch(t.orelse)
|
|
self.leave
|
|
|
|
# expr
|
|
def _Str(self, tree):
|
|
self.write(repr(tree.s))
|
|
|
|
def _Name(self, t):
|
|
self.write(t.id)
|
|
|
|
def _Num(self, t):
|
|
self.write(repr(t.n))
|
|
|
|
def _List(self, t):
|
|
self.write("[")
|
|
for e in t.elts:
|
|
self.dispatch(e)
|
|
self.write(", ")
|
|
self.write("]")
|
|
|
|
def _Dict(self, t):
|
|
self.write("{")
|
|
for k,v in zip(t.keys, t.values):
|
|
self.dispatch(k)
|
|
self.write(" : ")
|
|
self.dispatch(v)
|
|
self.write(", ")
|
|
self.write("}")
|
|
|
|
def _Tuple(self, t):
|
|
if not t.elts:
|
|
self.write("()")
|
|
return
|
|
self.write("(")
|
|
for e in t.elts:
|
|
self.dispatch(e)
|
|
self.write(", ")
|
|
self.write(")")
|
|
|
|
unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"}
|
|
def _UnaryOp(self, t):
|
|
self.write(self.unop[t.op.__class__.__name__])
|
|
self.write("(")
|
|
self.dispatch(t.operand)
|
|
self.write(")")
|
|
|
|
binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%",
|
|
"RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&",
|
|
"FloorDiv":"//"}
|
|
def _BinOp(self, t):
|
|
self.write("(")
|
|
self.dispatch(t.left)
|
|
self.write(")" + self.binop[t.op.__class__.__name__] + "(")
|
|
self.dispatch(t.right)
|
|
self.write(")")
|
|
|
|
cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=",
|
|
"Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"}
|
|
def _Compare(self, t):
|
|
self.write("(")
|
|
self.dispatch(t.left)
|
|
for o, e in zip(t.ops, t.comparators):
|
|
self.write(") " +self.cmpops[o.__class__.__name__] + " (")
|
|
self.dispatch(e)
|
|
self.write(")")
|
|
|
|
def _Attribute(self,t):
|
|
self.dispatch(t.value)
|
|
self.write(".")
|
|
self.write(t.attr)
|
|
|
|
def _Call(self, t):
|
|
self.dispatch(t.func)
|
|
self.write("(")
|
|
comma = False
|
|
for e in t.args:
|
|
if comma: self.write(", ")
|
|
else: comma = True
|
|
self.dispatch(e)
|
|
for e in t.keywords:
|
|
if comma: self.write(", ")
|
|
else: comma = True
|
|
self.dispatch(e)
|
|
if t.starargs:
|
|
if comma: self.write(", ")
|
|
else: comma = True
|
|
self.write("*")
|
|
self.dispatch(t.stararg)
|
|
if t.kwargs:
|
|
if comma: self.write(", ")
|
|
else: comma = True
|
|
self.write("**")
|
|
self.dispatch(t.stararg)
|
|
self.write(")")
|
|
|
|
def _Subscript(self, t):
|
|
self.dispatch(t.value)
|
|
self.write("[")
|
|
self.dispatch(t.slice)
|
|
self.write("]")
|
|
|
|
# slice
|
|
def _Index(self, t):
|
|
self.dispatch(t.value)
|
|
|
|
def _Slice(self, t):
|
|
if t.lower:
|
|
self.dispatch(t.lower)
|
|
self.write(":")
|
|
if t.upper:
|
|
self.dispatch(t.upper)
|
|
if t.step:
|
|
self.write(":")
|
|
self.dispatch(t.step)
|
|
|
|
# others
|
|
def _arguments(self, t):
|
|
first = True
|
|
nonDef = len(t.args)-len(t.defaults)
|
|
for a in t.args[0:nonDef]:
|
|
if first:first = False
|
|
else: self.write(", ")
|
|
self.dispatch(a)
|
|
for a,d in zip(t.args[nonDef:], t.defaults):
|
|
if first:first = False
|
|
else: self.write(", ")
|
|
self.dispatch(a),
|
|
self.write("=")
|
|
self.dispatch(d)
|
|
if t.vararg:
|
|
if first:first = False
|
|
else: self.write(", ")
|
|
self.write("*"+t.vararg)
|
|
if t.kwarg:
|
|
if first:first = False
|
|
else: self.write(", ")
|
|
self.write("**"+self.kwarg)
|
|
self.write(")")
|
|
|
|
def roundtrip(filename):
|
|
source = open(filename).read()
|
|
tree = compile(source, filename, "exec", 0x400)
|
|
Unparser(tree)
|
|
|
|
if __name__=='__main__':
|
|
roundtrip(sys.argv[1])
|