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, 34 Class, Const, Continue, Dict, Discard, For, Function, Getattr, 35 Global, If, Keyword, Lambda, List, Module, Name, Pass, Return, 36 Stmt, TryExcept, TryFinally, Tuple, While. 37 38 Missing: Add, And, Assert, AugAssign, Backquote, Bitand, Bitor, Bitxor, 39 Compare, Decorators, Div, Ellipsis, Exec, FloorDiv, From, Import, 40 Invert, LeftShift, ListComp, ListCompFor, ListCompIf, Mod, Mul, 41 Not, Or, Power, Print, Printnl, Raise, RightShift, Slice, 42 Sliceobj, Sub, Subscript, UnaryAdd, UnarySub, Yield. 43 """ 44 45 def __init__(self): 46 ASTVisitor.__init__(self) 47 self.result = None # The resulting tree. 48 self.subprograms = [] # Subprograms outside the tree. 49 self.current_subprograms = [] # Current subprograms being processed. 50 51 # Generic visitor methods. 52 53 def default(self, node, *args): 54 raise ValueError, node.__class__ 55 56 def dispatch(self, node, *args): 57 return ASTVisitor.dispatch(self, node, *args) 58 59 def dispatches(self, nodes, *args): 60 results = [] 61 for node in nodes: 62 results.append(self.dispatch(node, *args)) 63 return results 64 65 # Placeholder or deletion transformations. 66 67 def visitStmt(self, stmt): 68 return self.dispatches(stmt.nodes) 69 70 def visitPass(self, pass_): 71 return Pass(pass_) 72 73 def visitDiscard(self, discard): 74 return self.dispatch(discard.expr) 75 76 # Relatively trivial transformations. 77 78 def visitModule(self, module): 79 self.result = Module(module) 80 self.result.code = self.dispatch(module.node) 81 return self.result 82 83 def visitClass(self, class_): 84 result = Class(class_, name=class_.name, bases=class_.bases) 85 result.code = self.dispatch(class_.code) 86 return result 87 88 def visitGetattr(self, getattr): 89 result = LoadAttr(getattr, name=getattr.attrname) 90 result.expr = self.dispatch(getattr.expr) 91 return result 92 93 def visitKeyword(self, keyword): 94 result = Keyword(keyword, name=keyword.name) 95 result.expr = self.dispatch(keyword.expr) 96 return result 97 98 def visitGlobal(self, global_): 99 result = Global(global_, names=global_.names) 100 return result 101 102 def visitName(self, name): 103 result = LoadName(name, name=name.name) 104 return result 105 106 def visitConst(self, const): 107 result = LoadConst(const, value=const.value) 108 return result 109 110 def visitReturn(self, return_): 111 result = Return(return_) 112 result.expr = self.dispatch(return_.value) 113 return result 114 115 def visitBreak(self, break_): 116 result = Return(break_) 117 return result 118 119 def visitContinue(self, continue_): 120 result = Invoke(continue_, same_frame=1, star=None, dstar=None, args=[]) 121 result.expr = LoadRef(ref=self.current_subprograms[-1]) 122 return result 123 124 def visitIf(self, if_): 125 result = If(if_, else_=[]) 126 tests = [] 127 for compare, stmt in if_.tests: 128 # Produce something like... 129 # expr.__true__() ? body 130 test = Conditional(else_=[], test=Invoke( 131 expr=LoadAttr(expr=self.dispatch(compare), name="__true__"), 132 params=[], star=None, dstar=None)) 133 test.body = self.dispatch(stmt) 134 tests.append(test) 135 result.tests = tests 136 if if_.else_ is not None: 137 result.else_ = self.dispatch(if_.else_) 138 return result 139 140 def _visitBuiltin(self, builtin, name): 141 result = Invoke(builtin, expr=LoadName(name=name)) 142 result.args = self.dispatches(builtin.nodes) 143 return result 144 145 def visitTuple(self, tuple): 146 return self._visitBuiltin(tuple, "Tuple") 147 148 def visitList(self, list): 149 return self._visitBuiltin(list, "List") 150 151 def visitDict(self, dict): 152 result = Invoke(expr=LoadName(name="Dict")) 153 args = [] 154 for key, value in dict.items: 155 tuple = Invoke(expr=LoadName(name="Tuple"), star=None, dstar=None) 156 tuple.args = [self.dispatch(key), self.dispatch(value)] 157 args.append(tuple) 158 result.args = args 159 return result 160 161 # Logical operators. 162 163 def visitAnd(self, and_): 164 result = And(and_) 165 nodes = [] 166 for node in and_.nodes: 167 nodes.append(Invoke(expr=LoadAttr(expr=self.dispatch(node), name="__true__"), 168 params=[], star=None, dstar=None)) 169 result.nodes = nodes 170 return result 171 172 def visitOr(self, or_): 173 result = Or(or_) 174 nodes = [] 175 for node in or_.nodes: 176 nodes.append(Invoke(expr=LoadAttr(expr=self.dispatch(node), name="__true__"), 177 params=[], star=None, dstar=None)) 178 result.nodes = nodes 179 return result 180 181 def visitNot(self, not_): 182 result = Not(not_, expr=Invoke(expr=LoadAttr(expr=self.dispatch(not_.expr), name="__true__"), 183 params=[], star=None, dstar=None)) 184 return result 185 186 # Assignments. 187 188 def visitAssign(self, assign): 189 result = Assign(assign) 190 store = StoreTemp(expr=self.dispatch(assign.expr)) 191 release = ReleaseTemp() 192 result.code = [store] + self.dispatches(assign.nodes, 0) + [release] 193 return result 194 195 def visitAssList(self, asslist, in_sequence=0): 196 if not in_sequence: 197 expr = LoadTemp(asslist) 198 else: 199 expr = Invoke(asslist, expr=LoadAttr(expr=LoadTemp(), name="next")) 200 result = Assign(asslist) 201 store = StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=expr))) 202 release = ReleaseTemp() 203 result.code = [store] + self.dispatches(asslist.nodes, 1) + [release] 204 return result 205 206 visitAssTuple = visitAssList 207 208 def _visitAssNameOrAttr(self, node, in_sequence): 209 if not in_sequence: 210 return LoadTemp(node) 211 else: 212 return Invoke(node, expr=LoadAttr(expr=LoadTemp(), name="next")) 213 214 def visitAssName(self, assname, in_sequence=0): 215 expr = self._visitAssNameOrAttr(assname, in_sequence) 216 result = StoreName(assname, name=assname.name, expr=expr) 217 return result 218 219 def visitAssAttr(self, assattr, in_sequence=0): 220 expr = self._visitAssNameOrAttr(assattr, in_sequence) 221 lvalue = self.dispatch(assattr.expr) 222 result = StoreAttr(assattr, name=assattr.attrname, lvalue=lvalue, expr=expr) 223 return result 224 225 # Invocation and subprogram transformations. 226 227 def _visitFunction(self, function, subprogram): 228 if function.flags & 4 != 0: has_star = 1 229 else: has_star = 0 230 if function.flags & 8 != 0: has_dstar = 1 231 else: has_dstar = 0 232 ndefaults = len(function.defaults) 233 npositional = len(function.argnames) - has_star - has_dstar 234 if has_star: star = function.argnames[npositional] 235 else: star = None 236 if has_dstar: dstar = function.argnames[npositional + has_star] 237 else: dstar = None 238 239 params = [] 240 for i in range(0, npositional - ndefaults): 241 params.append((function.argnames[i], None)) 242 243 # NOTE: Fix/process defaults. 244 245 for i in range(0, ndefaults): 246 default = function.defaults[i] 247 if default is not None: 248 params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) 249 else: 250 params.append((function.argnames[npositional - ndefaults + i], default)) 251 252 subprogram.params = params 253 subprogram.star = star 254 subprogram.dstar = dstar 255 self.subprograms.append(subprogram) 256 257 def visitFunction(self, function): 258 259 # Make a subprogram for the function and record it outside the main 260 # tree. 261 262 subprogram = Subprogram(function, name=function.name, star=None, dstar=None) 263 self.current_subprograms.append(subprogram) 264 subprogram.code = self.dispatch(function.code) 265 self.current_subprograms.pop() 266 self._visitFunction(function, subprogram) 267 268 # Make a definition of the function associating it with a name. 269 270 result = Assign(function) 271 load = LoadRef(ref=subprogram) 272 store = StoreName(name=function.name) 273 result.code = [load, store] 274 return result 275 276 def visitLambda(self, lambda_): 277 278 # Make a subprogram for the function and record it outside the main 279 # tree. 280 281 subprogram = Subprogram(lambda_, name=hex(id(lambda_)), star=None, dstar=None) 282 self.current_subprograms.append(subprogram) 283 subprogram.code = [Return(expr=self.dispatch(lambda_.code))] 284 self.current_subprograms.pop() 285 self._visitFunction(lambda_, subprogram) 286 287 # Get the subprogram reference to the lambda. 288 289 return LoadRef(ref=subprogram) 290 291 def visitCallFunc(self, callfunc): 292 result = Invoke(callfunc, same_frame=0, star=None, dstar=None) 293 result.args = self.dispatches(callfunc.args) 294 if callfunc.star_args is not None: 295 result.star = self.dispatch(callfunc.star_args) 296 if callfunc.dstar_args is not None: 297 result.dstar = self.dispatch(callfunc.dstar_args) 298 result.expr = self.dispatch(callfunc.node) 299 return result 300 301 def visitWhile(self, while_): 302 303 # Make a subprogram for the block and record it outside the main tree. 304 305 subprogram = Subprogram(while_, name=hex(id(while_)), acquire_locals=1, params=[], star=None, dstar=None) 306 self.current_subprograms.append(subprogram) 307 308 # Include a conditional statement in the subprogram. 309 310 test = Conditional(else_=[]) 311 test.test = Invoke(expr=LoadAttr(expr=self.dispatch(while_.test), name="__true__"), 312 params=[], star=None, dstar=None) 313 314 # Inside the conditional, add a recursive invocation to the subprogram 315 # if the test condition was satisfied. 316 317 continuation = Invoke(same_frame=1, star=None, dstar=None, args=[]) 318 continuation.expr = LoadRef(ref=subprogram) 319 test.body = self.dispatch(while_.body) + [continuation] 320 if while_.else_ is not None: 321 test.else_ = self.dispatch(while_.else_) 322 subprogram.code = [test] 323 324 self.current_subprograms.pop() 325 self.subprograms.append(subprogram) 326 327 # Make an invocation of the subprogram. 328 329 result = Invoke(while_, same_frame=1, star=None, dstar=None, args=[]) 330 result.expr = LoadRef(ref=subprogram) 331 return result 332 333 def visitFor(self, for_): 334 335 # Make a subprogram for the block and record it outside the main tree. 336 337 subprogram = Subprogram(for_, name=hex(id(for_)), acquire_locals=1, params=[], star=None, dstar=None) 338 self.current_subprograms.append(subprogram) 339 340 # Wrap the assignment in a try...except statement. 341 342 try_except = Try(body=[], handlers=[], else_=[], finally_=[]) 343 except_spec = Invoke(expr=LoadName(name="Tuple"), params=[LoadName(name="StopIteration")]) 344 stopiteration = Except(spec=except_spec) 345 stopiteration.code = self.dispatch(for_.else_) 346 try_except.handlers = [stopiteration] 347 348 assign = Assign() 349 assign.code = [ 350 StoreTemp(expr=Invoke(expr=LoadAttr(expr=LoadTemp(), name="next"))), 351 self.dispatch(for_.assign), 352 ReleaseTemp() 353 ] 354 355 # Inside the conditional, add a recursive invocation to the subprogram 356 # if the test condition was satisfied. 357 358 continuation = Invoke(same_frame=1, star=None, dstar=None, args=[]) 359 continuation.expr = LoadRef(ref=subprogram) 360 try_except.body = [assign] + self.dispatch(for_.body) + [continuation] 361 subprogram.code = [try_except] 362 363 self.subprograms.append(subprogram) 364 self.current_subprograms.pop() 365 366 # Obtain an iterator for the sequence involved. 367 # Then, make an invocation of the subprogram. 368 369 result = Assign(for_) 370 result.code = [ 371 StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=self.dispatch(for_.list)))), 372 Invoke(expr=LoadRef(ref=subprogram), same_frame=1, star=None, dstar=None, args=[]), 373 ReleaseTemp() 374 ] 375 return result 376 377 # Exception node transformations. 378 379 def visitTryExcept(self, tryexcept): 380 result = Try(tryexcept, body=[], handlers=[], else_=[], finally_=[]) 381 if tryexcept.body is not None: 382 result.body = self.dispatch(tryexcept.body) 383 if tryexcept.else_ is not None: 384 result.else_ = self.dispatch(tryexcept.else_) 385 handlers = [] 386 for spec, assign, stmt in tryexcept.handlers: 387 get_exc = Assign() 388 get_exc.code = [StoreTemp(expr=LoadExc()), self.dispatch(assign), ReleaseTemp()] 389 handler = Except(spec=self.dispatch(spec)) 390 handler.code = [get_exc] + self.dispatch(stmt) 391 handlers.append(handler) 392 result.handlers = handlers 393 return result 394 395 def visitTryFinally(self, tryfinally): 396 result = Try(tryfinally, body=[], handlers=[], else_=[], finally_=[]) 397 if tryfinally.body is not None: 398 result.body = self.dispatch(tryfinally.body) 399 if tryfinally.final is not None: 400 result.finally_ = self.dispatch(tryfinally.final) 401 return result 402 403 # vim: tabstop=4 expandtab shiftwidth=4