1 #!/usr/bin/env python 2 3 """ 4 Simplify AST structures for easier type propagation and analysis. 5 6 Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk> 7 8 This software is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2 of 11 the License, or (at your option) any later version. 12 13 This software is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public 19 License along with this library; see the file LICENCE.txt 20 If not, write to the Free Software Foundation, Inc., 21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 22 """ 23 24 from compiler.visitor import ASTVisitor 25 import compiler.ast 26 from simplified import * 27 28 class Simplifier(ASTVisitor): 29 30 """ 31 A simplifying visitor for AST nodes. 32 33 Covered: AssAttr, AssList, AssName, AssTuple, Assign, Break, CallFunc, Class, 34 Const, Continue, Dict, Discard, For, Function, Getattr, If, Keyword, Lambda, 35 List, Module, Name, Pass, Return, Stmt, TryExcept, TryFinally, Tuple, 36 While. 37 38 Missing: Add, And, Assert, AugAssign, Backquote, Bitand, Bitor, Bitxor, 39 Compare, Decorators, Div, Ellipsis, Exec, 40 FloorDiv, From, Global, Import, Invert, LeftShift, 41 ListComp, ListCompFor, ListCompIf, Mod, Mul, Not, Or, Power, 42 Print, Printnl, Raise, RightShift, Slice, Sliceobj, Sub, 43 Subscript, UnaryAdd, UnarySub, Yield. 44 """ 45 46 def __init__(self): 47 ASTVisitor.__init__(self) 48 self.result = None 49 self.subprograms = [] 50 self.current_subprograms = [] 51 52 # Generic visitor methods. 53 54 def default(self, node, *args): 55 raise ValueError, node.__class__ 56 57 def dispatch(self, node, *args): 58 return ASTVisitor.dispatch(self, node, *args) 59 60 def dispatches(self, nodes, *args): 61 results = [] 62 for node in nodes: 63 results.append(self.dispatch(node, *args)) 64 return results 65 66 # Placeholder or deletion transformations. 67 68 def visitStmt(self, stmt): 69 return self.dispatches(stmt.nodes) 70 71 def visitPass(self, pass_): 72 return Pass(pass_) 73 74 def visitDiscard(self, discard): 75 return self.dispatch(discard.expr) 76 77 # Relatively trivial transformations. 78 79 def visitModule(self, module): 80 self.result = Module(module) 81 self.result.code = self.dispatch(module.node) 82 return self.result 83 84 def visitClass(self, class_): 85 result = Class(class_, name=class_.name, bases=class_.bases) 86 result.code = self.dispatch(class_.code) 87 return result 88 89 def visitGetattr(self, getattr): 90 result = LoadAttr(getattr, name=getattr.attrname) 91 result.expr = self.dispatch(getattr.expr) 92 return result 93 94 def visitKeyword(self, keyword): 95 result = Keyword(keyword, name=keyword.name) 96 result.expr = self.dispatch(keyword.expr) 97 return result 98 99 def visitName(self, name): 100 result = LoadName(name, name=name.name) 101 return result 102 103 def visitConst(self, const): 104 result = LoadConst(const, value=const.value) 105 return result 106 107 def visitReturn(self, return_): 108 result = Return(return_) 109 result.expr = self.dispatch(return_.value) 110 return result 111 112 def visitBreak(self, break_): 113 result = Return(break_) 114 return result 115 116 def visitContinue(self, continue_): 117 result = Invoke(same_frame=1, star=None, dstar=None, args=[]) 118 result.expr = LoadRef(ref=self.current_subprograms[-1]) 119 return result 120 121 def visitIf(self, if_): 122 result = If(if_, else_=[]) 123 tests = [] 124 for compare, stmt in if_.tests: 125 test = Conditional(else_=[], test=Invoke( 126 expr=LoadAttr(expr=self.dispatch(compare), name="__true__"), 127 params=[], star=None, dstar=None)) 128 test.body = self.dispatch(stmt) 129 tests.append(test) 130 result.tests = tests 131 if if_.else_ is not None: 132 result.else_ = self.dispatch(if_.else_) 133 return result 134 135 def _visitBuiltin(self, builtin, name): 136 result = Invoke(expr=LoadName(name=name)) 137 args = [] 138 for node in builtin.nodes: 139 args.append(self.dispatch(node)) 140 result.args = args 141 return result 142 143 def visitTuple(self, tuple): 144 return self._visitBuiltin(tuple, "Tuple") 145 146 def visitList(self, list): 147 return self._visitBuiltin(list, "List") 148 149 def visitDict(self, dict): 150 result = Invoke(expr=LoadName(name="Dict")) 151 args = [] 152 for key, value in dict.items: 153 tuple = Invoke(expr=LoadName(name="Tuple"), star=None, dstar=None) 154 tuple.args = [self.dispatch(key), self.dispatch(value)] 155 args.append(tuple) 156 result.args = args 157 return result 158 159 # Assignments. 160 161 def visitAssign(self, assign): 162 result = Assign(assign) 163 store = StoreTemp(expr=self.dispatch(assign.expr)) 164 release = ReleaseTemp() 165 result.code = [store] + self.dispatches(assign.nodes, 0) + [release] 166 return result 167 168 def visitAssList(self, asslist, in_sequence=0): 169 if not in_sequence: 170 expr = LoadTemp(asslist) 171 else: 172 expr = Invoke(asslist, expr=LoadAttr(expr=LoadTemp(), name="next")) 173 result = Assign(asslist) 174 store = StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=expr))) 175 release = ReleaseTemp() 176 result.code = [store] + self.dispatches(asslist.nodes, 1) + [release] 177 return result 178 179 visitAssTuple = visitAssList 180 181 def _visitAssNameOrAttr(self, node, in_sequence): 182 if not in_sequence: 183 return LoadTemp(node) 184 else: 185 return Invoke(node, expr=LoadAttr(expr=LoadTemp(), name="next")) 186 187 def visitAssName(self, assname, in_sequence=0): 188 expr = self._visitAssNameOrAttr(assname, in_sequence) 189 result = StoreName(assname, name=assname.name, expr=expr) 190 return result 191 192 def visitAssAttr(self, assattr, in_sequence=0): 193 expr = self._visitAssNameOrAttr(assattr, in_sequence) 194 lvalue = self.dispatch(assattr.expr) 195 result = StoreAttr(assattr, name=assattr.attrname, lvalue=lvalue, expr=expr) 196 return result 197 198 # Invocation and subprogram transformations. 199 200 def _visitFunction(self, function, subprogram): 201 if function.flags & 4 != 0: has_star = 1 202 else: has_star = 0 203 if function.flags & 8 != 0: has_dstar = 1 204 else: has_dstar = 0 205 ndefaults = len(function.defaults) 206 npositional = len(function.argnames) - has_star - has_dstar 207 if has_star: star = function.argnames[npositional] 208 else: star = None 209 if has_dstar: dstar = function.argnames[npositional + has_star] 210 else: dstar = None 211 212 params = [] 213 for i in range(0, npositional - ndefaults): 214 params.append((function.argnames[i], None)) 215 216 # NOTE: Fix/process defaults. 217 218 for i in range(0, ndefaults): 219 default = function.defaults[i] 220 if default is not None: 221 params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) 222 else: 223 params.append((function.argnames[npositional - ndefaults + i], default)) 224 225 subprogram.params = params 226 subprogram.star = star 227 subprogram.dstar = dstar 228 self.subprograms.append(subprogram) 229 230 def visitFunction(self, function): 231 232 # Make a subprogram for the function and record it outside the main 233 # tree. 234 235 subprogram = Subprogram(function, name=function.name, star=None, dstar=None) 236 self.current_subprograms.append(subprogram) 237 subprogram.code = self.dispatch(function.code) 238 self.current_subprograms.pop() 239 self._visitFunction(function, subprogram) 240 241 # Make a definition of the function associating it with a name. 242 243 result = Assign(function) 244 load = LoadRef(ref=subprogram) 245 store = StoreName(name=function.name) 246 result.code = [load, store] 247 return result 248 249 def visitLambda(self, lambda_): 250 251 # Make a subprogram for the function and record it outside the main 252 # tree. 253 254 subprogram = Subprogram(lambda_, name=hex(id(lambda_)), star=None, dstar=None) 255 self.current_subprograms.append(subprogram) 256 subprogram.code = [Return(expr=self.dispatch(lambda_.code))] 257 self.current_subprograms.pop() 258 self._visitFunction(lambda_, subprogram) 259 260 # Get the subprogram reference to the lambda. 261 262 return LoadRef(ref=subprogram) 263 264 def visitCallFunc(self, callfunc): 265 result = Invoke(callfunc, same_frame=0, star=None, dstar=None) 266 result.args = self.dispatches(callfunc.args) 267 if callfunc.star_args is not None: 268 result.star = self.dispatch(callfunc.star_args) 269 if callfunc.dstar_args is not None: 270 result.dstar = self.dispatch(callfunc.dstar_args) 271 result.expr = self.dispatch(callfunc.node) 272 return result 273 274 def visitWhile(self, while_): 275 276 # Make a subprogram for the block and record it outside the main tree. 277 278 subprogram = Subprogram(while_, name=hex(id(while_)), acquire_locals=1, params=[], star=None, dstar=None) 279 self.current_subprograms.append(subprogram) 280 281 # Include a conditional statement in the subprogram. 282 283 test = Conditional(else_=[]) 284 test.test = Invoke(expr=LoadAttr(expr=self.dispatch(while_.test), name="__true__"), 285 params=[], star=None, dstar=None) 286 287 # Inside the conditional, add a recursive invocation to the subprogram 288 # if the test condition was satisfied. 289 290 continuation = Invoke(same_frame=1, star=None, dstar=None, args=[]) 291 continuation.expr = LoadRef(ref=subprogram) 292 test.body = self.dispatch(while_.body) + [continuation] 293 if while_.else_ is not None: 294 test.else_ = self.dispatch(while_.else_) 295 subprogram.code = [test] 296 297 self.current_subprograms.pop() 298 self.subprograms.append(subprogram) 299 300 # Make an invocation of the subprogram. 301 302 result = Invoke(while_, same_frame=1, star=None, dstar=None, args=[]) 303 result.expr = LoadRef(ref=subprogram) 304 return result 305 306 def visitFor(self, for_): 307 308 # Make a subprogram for the block and record it outside the main tree. 309 310 subprogram = Subprogram(for_, name=hex(id(for_)), acquire_locals=1, params=[], star=None, dstar=None) 311 self.current_subprograms.append(subprogram) 312 313 # Wrap the assignment in a try...except statement. 314 315 try_except = Try(body=[], handlers=[], else_=[], finally_=[]) 316 except_spec = Invoke(expr=LoadName(name="Tuple"), params=[LoadName(name="StopIteration")]) 317 stopiteration = Except(spec=except_spec) 318 stopiteration.code = self.dispatch(for_.else_) 319 try_except.handlers = [stopiteration] 320 321 assign = Assign() 322 assign.code = [ 323 StoreTemp(expr=Invoke(expr=LoadAttr(expr=LoadTemp(), name="next"))), 324 self.dispatch(for_.assign), 325 ReleaseTemp() 326 ] 327 328 # Inside the conditional, add a recursive invocation to the subprogram 329 # if the test condition was satisfied. 330 331 continuation = Invoke(same_frame=1, star=None, dstar=None, args=[]) 332 continuation.expr = LoadRef(ref=subprogram) 333 try_except.body = [assign] + self.dispatch(for_.body) + [continuation] 334 subprogram.code = [try_except] 335 336 self.subprograms.append(subprogram) 337 self.current_subprograms.pop() 338 339 # Obtain an iterator for the sequence involved. 340 # Then, make an invocation of the subprogram. 341 342 result = Assign(for_) 343 result.code = [ 344 StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=self.dispatch(for_.list)))), 345 Invoke(expr=LoadRef(ref=subprogram), same_frame=1, star=None, dstar=None, args=[]), 346 ReleaseTemp() 347 ] 348 return result 349 350 # Exception node transformations. 351 352 def visitTryExcept(self, tryexcept): 353 result = Try(tryexcept, body=[], handlers=[], else_=[], finally_=[]) 354 if tryexcept.body is not None: 355 result.body = self.dispatch(tryexcept.body) 356 if tryexcept.else_ is not None: 357 result.else_ = self.dispatch(tryexcept.else_) 358 handlers = [] 359 for spec, assign, stmt in tryexcept.handlers: 360 get_exc = Assign() 361 get_exc.code = [StoreTemp(expr=LoadExc()), self.dispatch(assign), ReleaseTemp()] 362 handler = Except(spec=self.dispatch(spec)) 363 handler.code = [get_exc] + self.dispatch(stmt) 364 handlers.append(handler) 365 result.handlers = handlers 366 return result 367 368 def visitTryFinally(self, tryfinally): 369 result = Try(tryfinally, body=[], handlers=[], else_=[], finally_=[]) 370 if tryfinally.body is not None: 371 result.body = self.dispatch(tryfinally.body) 372 if tryfinally.final is not None: 373 result.finally_ = self.dispatch(tryfinally.final) 374 return result 375 376 # vim: tabstop=4 expandtab shiftwidth=4