1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/simplify.py Sat Jul 08 21:47:12 2006 +0200
1.3 @@ -0,0 +1,376 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Simplify AST structures for easier type propagation and analysis.
1.8 +
1.9 +Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>
1.10 +
1.11 +This software is free software; you can redistribute it and/or
1.12 +modify it under the terms of the GNU General Public License as
1.13 +published by the Free Software Foundation; either version 2 of
1.14 +the License, or (at your option) any later version.
1.15 +
1.16 +This software is distributed in the hope that it will be useful,
1.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
1.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 +GNU General Public License for more details.
1.20 +
1.21 +You should have received a copy of the GNU General Public
1.22 +License along with this library; see the file LICENCE.txt
1.23 +If not, write to the Free Software Foundation, Inc.,
1.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
1.25 +"""
1.26 +
1.27 +from compiler.visitor import ASTVisitor
1.28 +import compiler.ast
1.29 +from simplified import *
1.30 +
1.31 +class Simplifier(ASTVisitor):
1.32 +
1.33 + """
1.34 + A simplifying visitor for AST nodes.
1.35 +
1.36 + Covered: AssAttr, AssList, AssName, AssTuple, Assign, Break, CallFunc, Class,
1.37 + Const, Continue, Dict, Discard, For, Function, Getattr, If, Keyword, Lambda,
1.38 + List, Module, Name, Pass, Return, Stmt, TryExcept, TryFinally, Tuple,
1.39 + While.
1.40 +
1.41 + Missing: Add, And, Assert, AugAssign, Backquote, Bitand, Bitor, Bitxor,
1.42 + Compare, Decorators, Div, Ellipsis, Exec,
1.43 + FloorDiv, From, Global, Import, Invert, LeftShift,
1.44 + ListComp, ListCompFor, ListCompIf, Mod, Mul, Not, Or, Power,
1.45 + Print, Printnl, Raise, RightShift, Slice, Sliceobj, Sub,
1.46 + Subscript, UnaryAdd, UnarySub, Yield.
1.47 + """
1.48 +
1.49 + def __init__(self):
1.50 + ASTVisitor.__init__(self)
1.51 + self.result = None
1.52 + self.subprograms = []
1.53 + self.current_subprograms = []
1.54 +
1.55 + # Generic visitor methods.
1.56 +
1.57 + def default(self, node, *args):
1.58 + raise ValueError, node.__class__
1.59 +
1.60 + def dispatch(self, node, *args):
1.61 + return ASTVisitor.dispatch(self, node, *args)
1.62 +
1.63 + def dispatches(self, nodes, *args):
1.64 + results = []
1.65 + for node in nodes:
1.66 + results.append(self.dispatch(node, *args))
1.67 + return results
1.68 +
1.69 + # Placeholder or deletion transformations.
1.70 +
1.71 + def visitStmt(self, stmt):
1.72 + return self.dispatches(stmt.nodes)
1.73 +
1.74 + def visitPass(self, pass_):
1.75 + return Pass(pass_)
1.76 +
1.77 + def visitDiscard(self, discard):
1.78 + return self.dispatch(discard.expr)
1.79 +
1.80 + # Relatively trivial transformations.
1.81 +
1.82 + def visitModule(self, module):
1.83 + self.result = Module(module)
1.84 + self.result.code = self.dispatch(module.node)
1.85 + return self.result
1.86 +
1.87 + def visitClass(self, class_):
1.88 + result = Class(class_, name=class_.name, bases=class_.bases)
1.89 + result.code = self.dispatch(class_.code)
1.90 + return result
1.91 +
1.92 + def visitGetattr(self, getattr):
1.93 + result = LoadAttr(getattr, name=getattr.attrname)
1.94 + result.expr = self.dispatch(getattr.expr)
1.95 + return result
1.96 +
1.97 + def visitKeyword(self, keyword):
1.98 + result = Keyword(keyword, name=keyword.name)
1.99 + result.expr = self.dispatch(keyword.expr)
1.100 + return result
1.101 +
1.102 + def visitName(self, name):
1.103 + result = LoadName(name, name=name.name)
1.104 + return result
1.105 +
1.106 + def visitConst(self, const):
1.107 + result = LoadConst(const, value=const.value)
1.108 + return result
1.109 +
1.110 + def visitReturn(self, return_):
1.111 + result = Return(return_)
1.112 + result.expr = self.dispatch(return_.value)
1.113 + return result
1.114 +
1.115 + def visitBreak(self, break_):
1.116 + result = Return(break_)
1.117 + return result
1.118 +
1.119 + def visitContinue(self, continue_):
1.120 + result = Invoke(same_frame=1, star=None, dstar=None, args=[])
1.121 + result.expr = LoadRef(ref=self.current_subprograms[-1])
1.122 + return result
1.123 +
1.124 + def visitIf(self, if_):
1.125 + result = If(if_, else_=[])
1.126 + tests = []
1.127 + for compare, stmt in if_.tests:
1.128 + test = Conditional(else_=[], test=Invoke(
1.129 + expr=LoadAttr(expr=self.dispatch(compare), name="__true__"),
1.130 + params=[], star=None, dstar=None))
1.131 + test.body = self.dispatch(stmt)
1.132 + tests.append(test)
1.133 + result.tests = tests
1.134 + if if_.else_ is not None:
1.135 + result.else_ = self.dispatch(if_.else_)
1.136 + return result
1.137 +
1.138 + def _visitBuiltin(self, builtin, name):
1.139 + result = Invoke(expr=LoadName(name=name))
1.140 + args = []
1.141 + for node in builtin.nodes:
1.142 + args.append(self.dispatch(node))
1.143 + result.args = args
1.144 + return result
1.145 +
1.146 + def visitTuple(self, tuple):
1.147 + return self._visitBuiltin(tuple, "Tuple")
1.148 +
1.149 + def visitList(self, list):
1.150 + return self._visitBuiltin(list, "List")
1.151 +
1.152 + def visitDict(self, dict):
1.153 + result = Invoke(expr=LoadName(name="Dict"))
1.154 + args = []
1.155 + for key, value in dict.items:
1.156 + tuple = Invoke(expr=LoadName(name="Tuple"), star=None, dstar=None)
1.157 + tuple.args = [self.dispatch(key), self.dispatch(value)]
1.158 + args.append(tuple)
1.159 + result.args = args
1.160 + return result
1.161 +
1.162 + # Assignments.
1.163 +
1.164 + def visitAssign(self, assign):
1.165 + result = Assign(assign)
1.166 + store = StoreTemp(expr=self.dispatch(assign.expr))
1.167 + release = ReleaseTemp()
1.168 + result.code = [store] + self.dispatches(assign.nodes, 0) + [release]
1.169 + return result
1.170 +
1.171 + def visitAssList(self, asslist, in_sequence=0):
1.172 + if not in_sequence:
1.173 + expr = LoadTemp(asslist)
1.174 + else:
1.175 + expr = Invoke(asslist, expr=LoadAttr(expr=LoadTemp(), name="next"))
1.176 + result = Assign(asslist)
1.177 + store = StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=expr)))
1.178 + release = ReleaseTemp()
1.179 + result.code = [store] + self.dispatches(asslist.nodes, 1) + [release]
1.180 + return result
1.181 +
1.182 + visitAssTuple = visitAssList
1.183 +
1.184 + def _visitAssNameOrAttr(self, node, in_sequence):
1.185 + if not in_sequence:
1.186 + return LoadTemp(node)
1.187 + else:
1.188 + return Invoke(node, expr=LoadAttr(expr=LoadTemp(), name="next"))
1.189 +
1.190 + def visitAssName(self, assname, in_sequence=0):
1.191 + expr = self._visitAssNameOrAttr(assname, in_sequence)
1.192 + result = StoreName(assname, name=assname.name, expr=expr)
1.193 + return result
1.194 +
1.195 + def visitAssAttr(self, assattr, in_sequence=0):
1.196 + expr = self._visitAssNameOrAttr(assattr, in_sequence)
1.197 + lvalue = self.dispatch(assattr.expr)
1.198 + result = StoreAttr(assattr, name=assattr.attrname, lvalue=lvalue, expr=expr)
1.199 + return result
1.200 +
1.201 + # Invocation and subprogram transformations.
1.202 +
1.203 + def _visitFunction(self, function, subprogram):
1.204 + if function.flags & 4 != 0: has_star = 1
1.205 + else: has_star = 0
1.206 + if function.flags & 8 != 0: has_dstar = 1
1.207 + else: has_dstar = 0
1.208 + ndefaults = len(function.defaults)
1.209 + npositional = len(function.argnames) - has_star - has_dstar
1.210 + if has_star: star = function.argnames[npositional]
1.211 + else: star = None
1.212 + if has_dstar: dstar = function.argnames[npositional + has_star]
1.213 + else: dstar = None
1.214 +
1.215 + params = []
1.216 + for i in range(0, npositional - ndefaults):
1.217 + params.append((function.argnames[i], None))
1.218 +
1.219 + # NOTE: Fix/process defaults.
1.220 +
1.221 + for i in range(0, ndefaults):
1.222 + default = function.defaults[i]
1.223 + if default is not None:
1.224 + params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default)))
1.225 + else:
1.226 + params.append((function.argnames[npositional - ndefaults + i], default))
1.227 +
1.228 + subprogram.params = params
1.229 + subprogram.star = star
1.230 + subprogram.dstar = dstar
1.231 + self.subprograms.append(subprogram)
1.232 +
1.233 + def visitFunction(self, function):
1.234 +
1.235 + # Make a subprogram for the function and record it outside the main
1.236 + # tree.
1.237 +
1.238 + subprogram = Subprogram(function, name=function.name, star=None, dstar=None)
1.239 + self.current_subprograms.append(subprogram)
1.240 + subprogram.code = self.dispatch(function.code)
1.241 + self.current_subprograms.pop()
1.242 + self._visitFunction(function, subprogram)
1.243 +
1.244 + # Make a definition of the function associating it with a name.
1.245 +
1.246 + result = Assign(function)
1.247 + load = LoadRef(ref=subprogram)
1.248 + store = StoreName(name=function.name)
1.249 + result.code = [load, store]
1.250 + return result
1.251 +
1.252 + def visitLambda(self, lambda_):
1.253 +
1.254 + # Make a subprogram for the function and record it outside the main
1.255 + # tree.
1.256 +
1.257 + subprogram = Subprogram(lambda_, name=hex(id(lambda_)), star=None, dstar=None)
1.258 + self.current_subprograms.append(subprogram)
1.259 + subprogram.code = [Return(expr=self.dispatch(lambda_.code))]
1.260 + self.current_subprograms.pop()
1.261 + self._visitFunction(lambda_, subprogram)
1.262 +
1.263 + # Get the subprogram reference to the lambda.
1.264 +
1.265 + return LoadRef(ref=subprogram)
1.266 +
1.267 + def visitCallFunc(self, callfunc):
1.268 + result = Invoke(callfunc, same_frame=0, star=None, dstar=None)
1.269 + result.args = self.dispatches(callfunc.args)
1.270 + if callfunc.star_args is not None:
1.271 + result.star = self.dispatch(callfunc.star_args)
1.272 + if callfunc.dstar_args is not None:
1.273 + result.dstar = self.dispatch(callfunc.dstar_args)
1.274 + result.expr = self.dispatch(callfunc.node)
1.275 + return result
1.276 +
1.277 + def visitWhile(self, while_):
1.278 +
1.279 + # Make a subprogram for the block and record it outside the main tree.
1.280 +
1.281 + subprogram = Subprogram(while_, name=hex(id(while_)), acquire_locals=1, params=[], star=None, dstar=None)
1.282 + self.current_subprograms.append(subprogram)
1.283 +
1.284 + # Include a conditional statement in the subprogram.
1.285 +
1.286 + test = Conditional(else_=[])
1.287 + test.test = Invoke(expr=LoadAttr(expr=self.dispatch(while_.test), name="__true__"),
1.288 + params=[], star=None, dstar=None)
1.289 +
1.290 + # Inside the conditional, add a recursive invocation to the subprogram
1.291 + # if the test condition was satisfied.
1.292 +
1.293 + continuation = Invoke(same_frame=1, star=None, dstar=None, args=[])
1.294 + continuation.expr = LoadRef(ref=subprogram)
1.295 + test.body = self.dispatch(while_.body) + [continuation]
1.296 + if while_.else_ is not None:
1.297 + test.else_ = self.dispatch(while_.else_)
1.298 + subprogram.code = [test]
1.299 +
1.300 + self.current_subprograms.pop()
1.301 + self.subprograms.append(subprogram)
1.302 +
1.303 + # Make an invocation of the subprogram.
1.304 +
1.305 + result = Invoke(while_, same_frame=1, star=None, dstar=None, args=[])
1.306 + result.expr = LoadRef(ref=subprogram)
1.307 + return result
1.308 +
1.309 + def visitFor(self, for_):
1.310 +
1.311 + # Make a subprogram for the block and record it outside the main tree.
1.312 +
1.313 + subprogram = Subprogram(for_, name=hex(id(for_)), acquire_locals=1, params=[], star=None, dstar=None)
1.314 + self.current_subprograms.append(subprogram)
1.315 +
1.316 + # Wrap the assignment in a try...except statement.
1.317 +
1.318 + try_except = Try(body=[], handlers=[], else_=[], finally_=[])
1.319 + except_spec = Invoke(expr=LoadName(name="Tuple"), params=[LoadName(name="StopIteration")])
1.320 + stopiteration = Except(spec=except_spec)
1.321 + stopiteration.code = self.dispatch(for_.else_)
1.322 + try_except.handlers = [stopiteration]
1.323 +
1.324 + assign = Assign()
1.325 + assign.code = [
1.326 + StoreTemp(expr=Invoke(expr=LoadAttr(expr=LoadTemp(), name="next"))),
1.327 + self.dispatch(for_.assign),
1.328 + ReleaseTemp()
1.329 + ]
1.330 +
1.331 + # Inside the conditional, add a recursive invocation to the subprogram
1.332 + # if the test condition was satisfied.
1.333 +
1.334 + continuation = Invoke(same_frame=1, star=None, dstar=None, args=[])
1.335 + continuation.expr = LoadRef(ref=subprogram)
1.336 + try_except.body = [assign] + self.dispatch(for_.body) + [continuation]
1.337 + subprogram.code = [try_except]
1.338 +
1.339 + self.subprograms.append(subprogram)
1.340 + self.current_subprograms.pop()
1.341 +
1.342 + # Obtain an iterator for the sequence involved.
1.343 + # Then, make an invocation of the subprogram.
1.344 +
1.345 + result = Assign(for_)
1.346 + result.code = [
1.347 + StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=self.dispatch(for_.list)))),
1.348 + Invoke(expr=LoadRef(ref=subprogram), same_frame=1, star=None, dstar=None, args=[]),
1.349 + ReleaseTemp()
1.350 + ]
1.351 + return result
1.352 +
1.353 + # Exception node transformations.
1.354 +
1.355 + def visitTryExcept(self, tryexcept):
1.356 + result = Try(tryexcept, body=[], handlers=[], else_=[], finally_=[])
1.357 + if tryexcept.body is not None:
1.358 + result.body = self.dispatch(tryexcept.body)
1.359 + if tryexcept.else_ is not None:
1.360 + result.else_ = self.dispatch(tryexcept.else_)
1.361 + handlers = []
1.362 + for spec, assign, stmt in tryexcept.handlers:
1.363 + get_exc = Assign()
1.364 + get_exc.code = [StoreTemp(expr=LoadExc()), self.dispatch(assign), ReleaseTemp()]
1.365 + handler = Except(spec=self.dispatch(spec))
1.366 + handler.code = [get_exc] + self.dispatch(stmt)
1.367 + handlers.append(handler)
1.368 + result.handlers = handlers
1.369 + return result
1.370 +
1.371 + def visitTryFinally(self, tryfinally):
1.372 + result = Try(tryfinally, body=[], handlers=[], else_=[], finally_=[])
1.373 + if tryfinally.body is not None:
1.374 + result.body = self.dispatch(tryfinally.body)
1.375 + if tryfinally.final is not None:
1.376 + result.finally_ = self.dispatch(tryfinally.final)
1.377 + return result
1.378 +
1.379 +# vim: tabstop=4 expandtab shiftwidth=4