From 5fec904f84a40005f824abe295525a1710056be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 27 Feb 2006 21:41:03 +0000 Subject: Start of a source code unparser. --- Demo/parser/README | 6 ++ Demo/parser/unparse.py | 158 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 Demo/parser/unparse.py diff --git a/Demo/parser/README b/Demo/parser/README index e47d4e4..a576d33 100644 --- a/Demo/parser/README +++ b/Demo/parser/README @@ -1,6 +1,8 @@ These files are from the large example of using the `parser' module. Refer to the Python Library Reference for more information. +It also contains examples for the AST parser. + Files: ------ @@ -22,4 +24,8 @@ Files: test_parser.py program to put the parser module through its paces. + unparse.py AST (2.5) based example to recreate source code + from an AST. This is incomplete; contributions + are welcome. + Enjoy! diff --git a/Demo/parser/unparse.py b/Demo/parser/unparse.py new file mode 100644 index 0000000..2455885 --- /dev/null +++ b/Demo/parser/unparse.py @@ -0,0 +1,158 @@ +"Usage: unparse.py " +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) + 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 _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() + + # expr + def _Str(self, tree): + self.write(repr(tree.s)) + + def _Name(self, t): + self.write(t.id) + + def _List(self, t): + 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(")") + + # others + def _arguments(self, t): + first = True + # XXX t.defaults + for a in t.args: + if first:first = False + else: self.write(", ") + self.dispatch(a) + 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]) -- cgit v0.12