blob: 72691b5ec7ed7d996c1978cb74360309e0f3c4e3 [file] [log] [blame]
Tim Peters400cbc32006-02-28 18:44:41 +00001"Usage: unparse.py <path to source file>"
2import sys
3
4class Unparser:
5 """Methods in this class recursively traverse an AST and
6 output source code for the abstract syntax; original formatting
7 is disregarged. """
8
9 def __init__(self, tree, file = sys.stdout):
10 """Unparser(tree, file=sys.stdout) -> None.
11 Print the source for tree to file."""
12 self.f = file
13 self._indent = 0
14 self.dispatch(tree)
15 self.f.flush()
16
17 def fill(self, text = ""):
18 "Indent a piece of text, according to the current indentation level"
19 self.f.write("\n"+" "*self._indent + text)
20
21 def write(self, text):
22 "Append a piece of text to the current line."
23 self.f.write(text)
24
25 def enter(self):
26 "Print ':', and increase the indentation."
27 self.write(":")
28 self._indent += 1
29
30 def leave(self):
31 "Decrease the indentation level."
32 self._indent -= 1
33
34 def dispatch(self, tree):
35 "Dispatcher function, dispatching tree type T to method _T."
36 if isinstance(tree, list):
37 for t in tree:
38 self.dispatch(t)
39 return
40 meth = getattr(self, "_"+tree.__class__.__name__)
41 meth(tree)
42
43
44 ############### Unparsing methods ######################
45 # There should be one method per concrete grammar type #
46 # Constructors should be grouped by sum type. Ideally, #
47 # this would follow the order in the grammar, but #
48 # currently doesn't. #
49 ########################################################
50
51 def _Module(self, tree):
52 for stmt in tree.body:
53 self.dispatch(stmt)
54
55 # stmt
56 def _Expr(self, tree):
57 self.fill()
58 self.dispatch(tree.value)
59
60 def _Import(self, t):
61 self.fill("import ")
62 first = True
63 for a in t.names:
64 if first:
65 first = False
66 else:
67 self.write(", ")
68 self.write(a.name)
69 if a.asname:
70 self.write(" as "+a.asname)
71
72 def _Assign(self, t):
73 self.fill()
74 for target in t.targets:
75 self.dispatch(target)
76 self.write(" = ")
77 self.dispatch(t.value)
78
79 def _ClassDef(self, t):
80 self.write("\n")
81 self.fill("class "+t.name)
82 if t.bases:
83 self.write("(")
84 for a in t.bases:
85 self.dispatch(a)
86 self.write(", ")
87 self.write(")")
88 self.enter()
89 self.dispatch(t.body)
90 self.leave()
91
92 def _FunctionDef(self, t):
93 self.write("\n")
94 self.fill("def "+t.name + "(")
95 self.dispatch(t.args)
96 self.enter()
97 self.dispatch(t.body)
98 self.leave()
99
100 def _If(self, t):
101 self.fill("if ")
102 self.dispatch(t.test)
103 self.enter()
104 # XXX elif?
105 self.dispatch(t.body)
106 self.leave()
107 if t.orelse:
108 self.fill("else")
109 self.enter()
110 self.dispatch(t.orelse)
111 self.leave()
112
113 # expr
114 def _Str(self, tree):
115 self.write(repr(tree.s))
116
117 def _Name(self, t):
118 self.write(t.id)
119
120 def _List(self, t):
121 self.write("[")
122 for e in t.elts:
123 self.dispatch(e)
124 self.write(", ")
125 self.write("]")
126
127 unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"}
128 def _UnaryOp(self, t):
129 self.write(self.unop[t.op.__class__.__name__])
130 self.write("(")
131 self.dispatch(t.operand)
132 self.write(")")
133
134 # others
135 def _arguments(self, t):
136 first = True
137 # XXX t.defaults
138 for a in t.args:
139 if first:first = False
140 else: self.write(", ")
141 self.dispatch(a)
142 if t.vararg:
143 if first:first = False
144 else: self.write(", ")
145 self.write("*"+t.vararg)
146 if t.kwarg:
147 if first:first = False
148 else: self.write(", ")
149 self.write("**"+self.kwarg)
150 self.write(")")
151
152def roundtrip(filename):
153 source = open(filename).read()
154 tree = compile(source, filename, "exec", 0x400)
155 Unparser(tree)
156
157if __name__=='__main__':
158 roundtrip(sys.argv[1])