1 #!/usr/bin/env python 2 3 """ 4 Translate the AST of a Python program into a more interpretable representation. 5 6 Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 import micropython.inspect 23 from micropython.rsvp import * 24 import compiler.ast 25 from compiler.visitor import ASTVisitor 26 try: 27 set 28 except NameError: 29 from sets import Set as set 30 31 class TranslateError(Exception): 32 33 "A translation error." 34 35 pass 36 37 class Label: 38 39 "A reference to a location." 40 41 def __init__(self, number, location=None): 42 self.number = number 43 self.location = location 44 45 def __repr__(self): 46 return "Label(%r, location=%r)" % (self.number, self.location) 47 48 # Program visitors. 49 50 class Translation(ASTVisitor): 51 52 "A translated module." 53 54 def __init__(self, module, objtable, paramtable): 55 56 """ 57 Initialise the translation with an inspected 'module' and an attribute 58 table 'objtable' and parameter table 'paramtable'. 59 """ 60 61 ASTVisitor.__init__(self) 62 self.visitor = self 63 self.module = module 64 self.objtable = objtable 65 self.paramtable = paramtable 66 self.unit = None 67 68 # Wiring within the code. 69 70 self.labels = {} 71 self.label_number = 0 72 self.code = None 73 74 def get_module_code(self): 75 76 "Return the top-level module code." 77 78 self.unit = self.module 79 self.code = [] 80 self.dispatch(self.module.module) 81 return self.code 82 83 def get_code(self, unit): 84 85 "Return the code for the given 'unit'." 86 87 self.unit = unit 88 self.code = [] 89 if unit.node is not None: 90 self.dispatch(unit.node) 91 return self.code 92 93 def __repr__(self): 94 return "Translation(%r)" % self.module 95 96 def get_scope(self, name): 97 if self.unit.has_key(name): 98 return "local" 99 elif self.module.has_key(name): 100 return "global" 101 else: 102 return "builtins" 103 104 # Code writing methods. 105 106 def new_label(self): 107 number = self.label_number 108 label = Label(number) 109 self.labels[label] = label 110 self.label_number += 1 111 return label 112 113 def set_label(self, label): 114 115 """ 116 Set the location of 'label' to that within the entire image: the 117 location within the code combined with location of the code unit. 118 """ 119 120 label.location = len(self.code) + self.unit.code_location 121 122 def new_op(self, op): 123 self.code.append(op) 124 125 def last_op(self): 126 return self.code[-1] 127 128 # Visitor methods. 129 130 def default(self, node, *args): 131 raise TranslateError, node.__class__ 132 133 def dispatch(self, node, *args): 134 return ASTVisitor.dispatch(self, node, *args) 135 136 def _visitName(self, node, classes): 137 name = node.name 138 scope = self.get_scope(name) 139 #print self.module.name, node.lineno, name, scope 140 NameInstruction, AttrInstruction = classes 141 142 if scope == "local": 143 unit = self.unit 144 if isinstance(unit, micropython.inspect.Function): 145 self.new_op(NameInstruction(unit.all_locals()[name])) 146 elif isinstance(unit, micropython.inspect.Class): 147 self.new_op(AttrInstruction(unit.all_class_attributes()[name])) 148 elif isinstance(unit, micropython.inspect.Module): 149 self.new_op(AttrInstruction(unit.module_attributes()[name])) 150 else: 151 raise TranslateError, "Program unit %r has no local %r" % (unit, name) 152 153 elif scope == "global": 154 globals = self.module.module_attributes() 155 if globals.has_key(name): 156 self.new_op(AttrInstruction(globals[name])) 157 else: 158 raise TranslateError, "Module %r has no attribute %r" % (self.module, name) 159 160 else: 161 builtins = micropython.inspect.builtins.module_attributes() 162 self.new_op(AttrInstruction(builtins[name])) 163 164 def visitAdd(self, node): 165 166 """ 167 _t1 = node.left 168 _t2 = node.right 169 try: 170 _t1.__add__(_t2) 171 except AttributeError: 172 _t2.__radd__(_t1) 173 """ 174 175 def visitAnd(self, node): pass 176 177 def visitAssert(self, node): pass 178 179 def visitAssign(self, node): 180 self.dispatch(node.expr) 181 for n in node.nodes: 182 self.dispatch(n) 183 184 def visitAssAttr(self, node): pass 185 186 def visitAssList(self, node): pass 187 188 def visitAssName(self, node): 189 self._visitName(node, (StoreName, StoreAttr)) 190 191 visitAssTuple = visitAssList 192 193 def visitAugAssign(self, node): pass 194 195 def visitBackquote(self, node): pass 196 197 def visitBitand(self, node): pass 198 199 def visitBitor(self, node): pass 200 201 def visitBitxor(self, node): pass 202 203 def visitBreak(self, node): pass 204 205 def visitCallFunc(self, node): 206 207 """ 208 Evaluate positional arguments, evaluate and store keyword arguments in 209 the correct location, then invoke the function. 210 """ 211 212 # Record the location of the invocation. 213 214 self.new_op(MakeFrame()) # records the start of the frame 215 216 # Evaluate the target. 217 218 self.dispatch(node.node) 219 220 # NOTE: Only simple cases are used for optimisations. 221 222 last = self.last_op() 223 if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: 224 target = last.attr.value 225 else: 226 target = None 227 228 # Evaluate the arguments. 229 230 positional = 1 231 232 for i, arg in enumerate(node.args): 233 if isinstance(arg, compiler.ast.Keyword): 234 if positional: 235 self.new_op(ReserveFrame(len(node.args) - i)) 236 positional = 0 237 238 self.dispatch(arg.expr) 239 240 # Optimise where the target is known now. 241 242 if target is not None: 243 pos = self.paramtable.table[target.full_name()][arg.name] 244 self.new_op(StoreFrame(pos)) 245 246 # Otherwise, generate the code needed to obtain the details of 247 # the parameter location. 248 249 else: 250 251 # Combine the target details with the name to get the location. 252 # See the access method on the List class. 253 254 paramindex = self.paramtable.get_index(arg.name) 255 256 self.new_op(StoreFrameIndex(paramindex)) 257 258 # use (callable+0)+paramindex+table 259 # checks embedded offset against (callable+0) 260 # moves the top of stack to frame+position 261 262 else: 263 self.dispatch(arg) 264 265 self.new_op(LoadCallable()) # uses the start of the frame to get the callable 266 self.new_op(Jump()) 267 268 # NOTE: Exception handling required. 269 270 self.new_op(DropFrame()) 271 272 def visitClass(self, node): 273 unit = self.unit 274 self.unit = node.cls 275 self.dispatch(node.code) 276 self.unit = unit 277 278 def visitCompare(self, node): 279 280 """ 281 self.dispatch(node.expr) 282 for op_name, next_node in compare.ops: 283 methods = self.comparison_methods[op_name] 284 if methods is not None: 285 # Generate method call using evaluated argument and next node. 286 else: 287 # Deal with the special operators. 288 # Provide short-circuiting. 289 """ 290 291 def visitConst(self, node): 292 const = self.module.constant_values[node.value] 293 self.new_op(LoadConst(const)) 294 295 def visitContinue(self, node): pass 296 297 def visitDecorators(self, node): pass 298 299 def visitDict(self, node): pass 300 301 def visitDiscard(self, node): 302 self.dispatch(node.expr) 303 304 def visitDiv(self, node): pass 305 306 def visitEllipsis(self, node): pass 307 308 def visitExec(self, node): pass 309 310 def visitExpression(self, node): pass 311 312 def visitFloorDiv(self, node): pass 313 314 def visitFor(self, node): pass 315 316 def visitFrom(self, node): pass 317 318 def visitFunction(self, node): 319 320 # Only store the name when visiting this node from outside. 321 322 if self.unit is self.module: 323 self.new_op(LoadConst(node.function)) 324 self._visitName(node, (StoreName, StoreAttr)) 325 326 # Visiting of the code occurs when get_code is invoked on this node. 327 328 else: 329 self.dispatch(node.code) 330 self.new_op(Return()) 331 332 def visitGenExpr(self, node): pass 333 334 def visitGenExprFor(self, node): pass 335 336 def visitGenExprIf(self, node): pass 337 338 def visitGenExprInner(self, node): pass 339 340 def visitGetattr(self, node): pass 341 342 def visitGlobal(self, node): pass 343 344 def visitIf(self, node): 345 first = 1 346 exit_label = self.new_label() 347 348 for test, body in node.tests + [(None, node.else_)]: 349 if body is None: 350 break 351 if not first: 352 self.set_label(next_label) 353 if test is not None: 354 self.dispatch(test) 355 next_label = self.new_label() 356 self.new_op(Jump(next_label)) 357 self.dispatch(body) 358 self.new_op(Jump(exit_label)) 359 first = 0 360 361 self.set_label(exit_label) 362 363 def visitImport(self, node): pass 364 365 def visitInvert(self, node): pass 366 367 def visitKeyword(self, node): pass 368 369 def visitLambda(self, node): pass 370 371 def visitLeftShift(self, node): pass 372 373 def visitList(self, node): pass 374 375 def visitListComp(self, node): pass 376 377 def visitListCompFor(self, node): pass 378 379 def visitListCompIf(self, node): pass 380 381 def visitMod(self, node): pass 382 383 def visitModule(self, node): 384 self.dispatch(node.node) 385 386 def visitMul(self, node): pass 387 388 def visitName(self, node): 389 self._visitName(node, (LoadName, LoadAttr)) 390 391 def visitNot(self, node): pass 392 393 def visitOr(self, node): pass 394 395 def visitPass(self, node): pass 396 397 def visitPower(self, node): pass 398 399 def visitPrint(self, node): pass 400 401 def visitPrintnl(self, node): pass 402 403 def visitRaise(self, node): pass 404 405 def visitReturn(self, node): 406 if node.value is not None: 407 self.dispatch(node.value) 408 self.new_op(Return()) 409 410 def visitRightShift(self, node): pass 411 412 def visitSlice(self, node): pass 413 414 def visitStmt(self, node): 415 for n in node.nodes: 416 self.dispatch(n) 417 418 def visitSub(self, node): pass 419 420 def visitSubscript(self, node): pass 421 422 def visitTryExcept(self, node): pass 423 424 def visitTryFinally(self, node): pass 425 426 def visitTuple(self, node): pass 427 428 def visitUnaryAdd(self, node): pass 429 430 def visitUnarySub(self, node): pass 431 432 def visitWhile(self, node): pass 433 434 def visitWith(self, node): pass 435 436 def visitYield(self, node): pass 437 438 # Useful data. 439 440 comparison_methods = { 441 "==" : ("__eq__", "__ne__"), 442 "!=" : ("__ne__", "__eq__"), 443 "<" : ("__lt__", "__gt__"), 444 "<=" : ("__le__", "__ge__"), 445 ">=" : ("__ge__", "__le__"), 446 ">" : ("__gt__", "__lt__"), 447 "is" : None, 448 "is not" : None, 449 "in" : None, 450 "not in" : None 451 } 452 453 # vim: tabstop=4 expandtab shiftwidth=4