summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin v. Löwis <martin@v.loewis.de>2006-02-27 21:41:03 (GMT)
committerMartin v. Löwis <martin@v.loewis.de>2006-02-27 21:41:03 (GMT)
commit5fec904f84a40005f824abe295525a1710056be0 (patch)
tree1b818998c15ab36b0fc0c17ae564099c1b6bc17e
parent9aaad88393969c3b61609ad93331a743c1dafda7 (diff)
downloadcpython-5fec904f84a40005f824abe295525a1710056be0.zip
cpython-5fec904f84a40005f824abe295525a1710056be0.tar.gz
cpython-5fec904f84a40005f824abe295525a1710056be0.tar.bz2
Start of a source code unparser.
-rw-r--r--Demo/parser/README6
-rw-r--r--Demo/parser/unparse.py158
2 files changed, 164 insertions, 0 deletions
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 <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)
+ 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])