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, 2008 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 from micropython.common import * 25 import compiler.ast 26 from compiler.visitor import ASTVisitor 27 try: 28 set 29 except NameError: 30 from sets import Set as set 31 32 class TranslateError(ProcessingError): pass 33 34 class Label: 35 36 "A reference to a location." 37 38 def __init__(self, number, location=None): 39 self.number = number 40 self.location = location 41 42 def __repr__(self): 43 return "Label(%r, location=%r)" % (self.number, self.location) 44 45 # Program visitors. 46 47 class Translation(ASTVisitor): 48 49 "A translated module." 50 51 def __init__(self, module, objtable, paramtable): 52 53 """ 54 Initialise the translation with an inspected 'module' and an attribute 55 table 'objtable' and parameter table 'paramtable'. 56 """ 57 58 ASTVisitor.__init__(self) 59 self.visitor = self 60 self.module = module 61 self.objtable = objtable 62 self.paramtable = paramtable 63 self.unit = None 64 65 # Wiring within the code. 66 67 self.labels = {} 68 self.label_number = 0 69 self.loop_labels = [] 70 71 # The code itself. 72 73 self.code = None 74 75 def get_module_code(self): 76 77 "Return the top-level module code." 78 79 self.unit = self.module 80 self.code = [] 81 if self.module.module is not None: 82 self.dispatch(self.module.module) 83 return self.code 84 85 def get_code(self, unit): 86 87 "Return the code for the given 'unit'." 88 89 self.unit = unit 90 self.code = [] 91 if unit.node is not None: 92 self.dispatch(unit.node) 93 return self.code 94 95 def __repr__(self): 96 return "Translation(%r)" % self.module 97 98 def get_scope(self, name): 99 if self.unit.has_key(name): 100 return "local" 101 elif self.module.has_key(name): 102 return "global" 103 else: 104 return "builtins" 105 106 # Code writing methods. 107 108 def new_label(self): 109 110 "Return a new label object for use with set_label." 111 112 number = self.label_number 113 label = Label(number) 114 self.labels[label] = label 115 self.label_number += 1 116 return label 117 118 def set_label(self, label): 119 120 """ 121 Set the location of 'label' to that within the entire image: the 122 location within the code combined with location of the code unit. 123 """ 124 125 label.location = len(self.code) + self.unit.code_location 126 127 def new_op(self, op): 128 129 "Add 'op' to the generated code." 130 131 self.code.append(op) 132 133 def replace_op(self, op): 134 135 "Replace the last added instruction with 'op'." 136 137 self.code[-1] = op 138 139 def last_op(self): 140 141 "Return the last added instruction." 142 143 return self.code[-1] 144 145 # Visitor methods. 146 147 def default(self, node, *args): 148 raise TranslateError(self.module.full_name(), node, "Node class %r is not supported." % node.__class__) 149 150 def dispatch(self, node, *args): 151 return ASTVisitor.dispatch(self, node, *args) 152 153 def _visitAttr(self, node, classes): 154 155 """ 156 Visit the attribute-related 'node', generating instructions based on the 157 given 'classes'. 158 """ 159 160 self.dispatch(node.expr) 161 self._generateAttr(node.attrname, classes) 162 163 def _generateAttr(self, attrname, classes): 164 165 """ 166 Generate code for the access to 'attrname' using the given 'classes'. 167 """ 168 169 AttrInstruction, AttrIndexInstruction = classes 170 # NOTE: Only simple cases are used for optimisations. 171 172 last = self.last_op() 173 if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: 174 175 # Get the details of the access. 176 177 target = last.attr.value 178 target_name = target.full_name() 179 table_entry = self.objtable.table[target_name] 180 pos = table_entry[attrname] 181 self.replace_op(AttrInstruction(pos)) 182 183 else: 184 index = self.objtable.get_index(attrname) 185 self.new_op(AttrIndexInstruction(index)) 186 187 def _visitName(self, node, classes): 188 189 """ 190 Visit the name-related 'node', generating instructions based on the 191 given 'classes'. 192 """ 193 194 name = node.name 195 scope = self.get_scope(name) 196 #print self.module.name, node.lineno, name, scope 197 self._generateName(name, scope, classes, node) 198 199 def _generateName(self, name, scope, classes, node): 200 201 """ 202 Generate code for the access to 'name' in 'scope' using the given 203 'classes', and using the given 'node' as the source of the access. 204 """ 205 206 NameInstruction, AttrInstruction = classes 207 208 if scope == "local": 209 unit = self.unit 210 if isinstance(unit, micropython.inspect.Function): 211 self.new_op(NameInstruction(unit.all_locals()[name])) 212 elif isinstance(unit, micropython.inspect.Class): 213 self.new_op(AttrInstruction(unit.all_class_attributes()[name])) 214 elif isinstance(unit, micropython.inspect.Module): 215 self.new_op(AttrInstruction(unit.module_attributes()[name])) 216 else: 217 raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r" % (unit, name)) 218 219 elif scope == "global": 220 globals = self.module.module_attributes() 221 if globals.has_key(name): 222 self.new_op(AttrInstruction(globals[name])) 223 else: 224 raise TranslateError(self.module.full_name(), node, "Module %r has no attribute %r" % (self.module, name)) 225 226 else: 227 builtins = micropython.inspect.builtins.module_attributes() 228 self.new_op(AttrInstruction(builtins[name])) 229 230 def visitAdd(self, node): 231 232 """ 233 _t1 = node.left 234 _t2 = node.right 235 try: 236 _t1.__add__(_t2) 237 except AttributeError: 238 _t2.__radd__(_t1) 239 """ 240 241 def visitAnd(self, node): pass 242 243 def visitAssert(self, node): pass 244 245 def visitAssign(self, node): 246 self.dispatch(node.expr) 247 for n in node.nodes: 248 self.dispatch(n) 249 250 def visitAssAttr(self, node): 251 self._visitAttr(node, (StoreAttr, StoreAttrIndex)) 252 253 def visitAssList(self, node): pass 254 255 def visitAssName(self, node): 256 self._visitName(node, (StoreName, StoreAttr)) 257 258 visitAssTuple = visitAssList 259 260 def visitAugAssign(self, node): pass 261 262 def visitBackquote(self, node): pass 263 264 def visitBitand(self, node): pass 265 266 def visitBitor(self, node): pass 267 268 def visitBitxor(self, node): pass 269 270 def visitBreak(self, node): 271 next_label, exit_label = self.loop_labels[-1] 272 self.new_op(Jump(exit_label)) 273 274 def visitCallFunc(self, node): 275 276 """ 277 Evaluate positional arguments, evaluate and store keyword arguments in 278 the correct location, then invoke the function. 279 """ 280 281 # Record the location of the invocation. 282 283 self.new_op(MakeFrame()) # records the start of the frame 284 285 # Evaluate the target. 286 287 self.dispatch(node.node) 288 289 # Generate the call. 290 291 self._generateCallFunc(node.args, node) 292 293 def _generateCallFunc(self, args, node): 294 295 # NOTE: Only simple cases are used for optimisations. 296 297 last = self.last_op() 298 if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: 299 target = last.attr.value 300 else: 301 target = None 302 303 # Evaluate the arguments. 304 305 positional = 1 306 307 for i, arg in enumerate(args): 308 if isinstance(arg, compiler.ast.Keyword): 309 if positional: 310 self.new_op(ReserveFrame(len(args) - i)) 311 positional = 0 312 313 self.dispatch(arg.expr) 314 315 # Optimise where the target is known now. 316 317 if target is not None: 318 319 # Find the parameter table entry for the target. 320 321 target_name = target.full_name() 322 323 # Look for a callable with the precise target name. 324 325 try: 326 table_entry = self.paramtable.table[target_name] 327 328 # Where no callable is present, check to see if it is a 329 # class name and find the initialiser instead. 330 331 except KeyError: 332 if self.objtable.table.has_key(target_name): 333 table_entry = self.paramtable.table[target_name + ".__init__"] 334 else: 335 raise 336 337 pos = table_entry[arg.name] 338 self.new_op(StoreFrame(pos)) 339 340 # Otherwise, generate the code needed to obtain the details of 341 # the parameter location. 342 343 else: 344 345 # Combine the target details with the name to get the location. 346 # See the access method on the List class. 347 348 try: 349 paramindex = self.paramtable.get_index(arg.name) 350 except ValueError: 351 raise TranslateError(self.module.full_name(), node, "No parameter definition exists for %r." % arg.name) 352 353 self.new_op(StoreFrameIndex(paramindex)) 354 355 # use (callable+0)+paramindex+table 356 # checks embedded offset against (callable+0) 357 # moves the top of stack to frame+position 358 359 else: 360 self.dispatch(arg) 361 362 self.new_op(LoadCallable()) # uses the start of the frame to get the callable 363 self.new_op(Jump()) 364 365 # NOTE: Exception handling required. 366 367 self.new_op(DropFrame()) 368 369 def visitClass(self, node): 370 unit = self.unit 371 self.unit = node.unit 372 self.unit.code_location = self.module.code_location # class body code is not independently addressable 373 self.dispatch(node.code) 374 self.unit = unit 375 376 def visitCompare(self, node): 377 378 """ 379 self.dispatch(node.expr) 380 for op_name, next_node in compare.ops: 381 methods = self.comparison_methods[op_name] 382 if methods is not None: 383 # Generate method call using evaluated argument and next node. 384 else: 385 # Deal with the special operators. 386 # Provide short-circuiting. 387 """ 388 389 def visitConst(self, node): 390 const = self.module.constant_values[node.value] 391 self.new_op(LoadConst(const)) 392 393 def visitContinue(self, node): 394 next_label, exit_label = self.loop_labels[-1] 395 self.new_op(Jump(next_label)) 396 397 def visitDecorators(self, node): pass 398 399 def visitDict(self, node): pass 400 401 def visitDiscard(self, node): 402 self.dispatch(node.expr) 403 404 def visitDiv(self, node): pass 405 406 def visitEllipsis(self, node): pass 407 408 def visitExec(self, node): pass 409 410 def visitExpression(self, node): pass 411 412 def visitFloorDiv(self, node): pass 413 414 def visitFor(self, node): 415 exit_label = self.new_label() 416 next_label = self.new_label() 417 else_label = self.new_label() 418 419 # Get the "list" to be iterated over, obtain its iterator. 420 421 self.dispatch(node.list) 422 self._generateAttr("__iter__", (LoadAttr, LoadAttrIndex)) 423 self._generateCallFunc([], node) 424 # Iterator on stack. 425 426 # In the loop... 427 428 self.set_label(next_label) 429 430 # Use the iterator to get the next value. 431 432 self.new_op(Duplicate()) 433 self._generateAttr("next", (LoadAttr, LoadAttrIndex)) 434 self._generateCallFunc([], node) 435 436 # Test for StopIteration. 437 438 self.new_op(CheckException("StopIteration")) # NOTE: To be done properly. 439 if node.else_ is not None: 440 self.new_op(JumpIfTrue(else_label)) 441 else: 442 self.new_op(JumpIfTrue(exit_label)) 443 444 # Assign to the target. 445 446 self.dispatch(node.assign) 447 448 # Process the body with the current next and exit points. 449 450 self.loop_labels.append((next_label, exit_label)) 451 self.dispatch(node.body) 452 self.loop_labels.pop() 453 454 # Repeat the loop. 455 456 self.new_op(Jump(next_label)) 457 458 # Produce the "else" section. 459 460 if node.else_ is not None: 461 self.set_label(exit_label) 462 self.dispatch(node.else_) 463 464 # Pop the iterator. 465 466 self.set_label(exit_label) 467 self.new_op(Pop()) 468 469 def visitFrom(self, node): pass 470 471 def visitFunction(self, node): 472 473 # Only store the name when visiting this node from outside. 474 475 if self.unit is not node.unit: 476 self.new_op(LoadConst(node.unit)) 477 self._visitName(node, (StoreName, StoreAttr)) 478 479 # Visiting of the code occurs when get_code is invoked on this node. 480 481 else: 482 self.dispatch(node.code) 483 self.new_op(Return()) 484 485 def visitGenExpr(self, node): pass 486 487 def visitGenExprFor(self, node): pass 488 489 def visitGenExprIf(self, node): pass 490 491 def visitGenExprInner(self, node): pass 492 493 def visitGetattr(self, node): 494 self._visitAttr(node, (LoadAttr, LoadAttrIndex)) 495 496 def visitGlobal(self, node): pass 497 498 def visitIf(self, node): 499 first = 1 500 exit_label = self.new_label() 501 502 for test, body in node.tests + [(None, node.else_)]: 503 if body is None: 504 break 505 if not first: 506 self.set_label(next_label) 507 if test is not None: 508 self.dispatch(test) 509 next_label = self.new_label() 510 self.new_op(JumpIfFalse(next_label)) 511 self.dispatch(body) 512 self.new_op(Jump(exit_label)) 513 first = 0 514 515 self.set_label(exit_label) 516 517 def visitImport(self, node): pass 518 519 def visitInvert(self, node): pass 520 521 def visitKeyword(self, node): pass 522 523 def visitLambda(self, node): pass 524 525 def visitLeftShift(self, node): pass 526 527 def visitList(self, node): pass 528 529 def visitListComp(self, node): pass 530 531 def visitListCompFor(self, node): pass 532 533 def visitListCompIf(self, node): pass 534 535 def visitMod(self, node): pass 536 537 def visitModule(self, node): 538 self.dispatch(node.node) 539 540 def visitMul(self, node): pass 541 542 def visitName(self, node): 543 self._visitName(node, (LoadName, LoadAttr)) 544 545 def visitNot(self, node): pass 546 547 def visitOr(self, node): pass 548 549 def visitPass(self, node): pass 550 551 def visitPower(self, node): pass 552 553 def visitPrint(self, node): pass 554 555 def visitPrintnl(self, node): pass 556 557 def visitRaise(self, node): pass 558 559 def visitReturn(self, node): 560 if node.value is not None: 561 self.dispatch(node.value) 562 self.new_op(Return()) 563 564 def visitRightShift(self, node): pass 565 566 def visitSlice(self, node): pass 567 568 def visitStmt(self, node): 569 for n in node.nodes: 570 self.dispatch(n) 571 572 def visitSub(self, node): pass 573 574 def visitSubscript(self, node): pass 575 576 def visitTryExcept(self, node): pass 577 578 def visitTryFinally(self, node): pass 579 580 def visitTuple(self, node): pass 581 582 def visitUnaryAdd(self, node): pass 583 584 def visitUnarySub(self, node): pass 585 586 def visitWhile(self, node): 587 exit_label = self.new_label() 588 next_label = self.new_label() 589 else_label = self.new_label() 590 591 self.set_label(next_label) 592 self.dispatch(node.test) 593 if node.else_ is not None: 594 self.new_op(JumpIfFalse(else_label)) 595 else: 596 self.new_op(JumpIfFalse(exit_label)) 597 598 self.loop_labels.append((next_label, exit_label)) 599 600 self.dispatch(node.body) 601 self.new_op(Jump(next_label)) 602 603 if node.else_ is not None: 604 self.set_label(else_label) 605 self.dispatch(node.else_) 606 607 self.set_label(exit_label) 608 self.loop_labels.pop() 609 610 def visitWith(self, node): pass 611 612 def visitYield(self, node): pass 613 614 # Useful data. 615 616 comparison_methods = { 617 "==" : ("__eq__", "__ne__"), 618 "!=" : ("__ne__", "__eq__"), 619 "<" : ("__lt__", "__gt__"), 620 "<=" : ("__le__", "__ge__"), 621 ">=" : ("__ge__", "__le__"), 622 ">" : ("__gt__", "__lt__"), 623 "is" : None, 624 "is not" : None, 625 "in" : None, 626 "not in" : None 627 } 628 629 # vim: tabstop=4 expandtab shiftwidth=4