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 174 # Where the last operation yields a constant... 175 176 if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: 177 178 # Get the details of the access. 179 180 target = last.attr.value 181 target_name = target.full_name() 182 table_entry = self.objtable.table[target_name] 183 pos = table_entry[attrname] 184 self.replace_op(AttrInstruction(pos + target.location)) 185 186 # Where the last operation involves the special 'self' name, check to 187 # see if the attribute is acceptably positioned. 188 189 elif isinstance(last, LoadName) and last.attr.name == "self" and not self.unit.is_relocated(attrname): 190 attr = self.unit.parent.all_attributes()[attrname] 191 self.new_op(AttrInstruction(attr)) 192 193 # Otherwise, perform a normal operation. 194 195 else: 196 index = self.objtable.get_index(attrname) 197 self.new_op(AttrIndexInstruction(index)) 198 199 def _visitName(self, node, classes): 200 201 """ 202 Visit the name-related 'node', generating instructions based on the 203 given 'classes'. 204 """ 205 206 name = node.name 207 scope = self.get_scope(name) 208 #print self.module.name, node.lineno, name, scope 209 self._generateName(name, scope, classes, node) 210 211 def _generateName(self, name, scope, classes, node): 212 213 """ 214 Generate code for the access to 'name' in 'scope' using the given 215 'classes', and using the given 'node' as the source of the access. 216 """ 217 218 NameInstruction, AttrInstruction = classes 219 220 if scope == "local": 221 unit = self.unit 222 if isinstance(unit, micropython.inspect.Function): 223 self.new_op(NameInstruction(unit.all_locals()[name])) 224 elif isinstance(unit, micropython.inspect.Class): 225 self.new_op(AttrInstruction(unit.all_class_attributes()[name])) 226 elif isinstance(unit, micropython.inspect.Module): 227 self.new_op(AttrInstruction(unit.module_attributes()[name])) 228 else: 229 raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r" % (unit, name)) 230 231 elif scope == "global": 232 globals = self.module.module_attributes() 233 if globals.has_key(name): 234 self.new_op(AttrInstruction(globals[name])) 235 else: 236 raise TranslateError(self.module.full_name(), node, "Module %r has no attribute %r" % (self.module, name)) 237 238 else: 239 builtins = micropython.inspect.builtins.module_attributes() 240 self.new_op(AttrInstruction(builtins[name])) 241 242 def visitAdd(self, node): 243 244 """ 245 _t1 = node.left 246 _t2 = node.right 247 try: 248 _t1.__add__(_t2) 249 except AttributeError: 250 _t2.__radd__(_t1) 251 """ 252 253 def visitAnd(self, node): pass 254 255 def visitAssert(self, node): pass 256 257 def visitAssign(self, node): 258 self.dispatch(node.expr) 259 for n in node.nodes: 260 self.dispatch(n) 261 262 def visitAssAttr(self, node): 263 self._visitAttr(node, (StoreAttr, StoreAttrIndex)) 264 265 def visitAssList(self, node): pass 266 267 def visitAssName(self, node): 268 self._visitName(node, (StoreName, StoreAttr)) 269 270 visitAssTuple = visitAssList 271 272 def visitAugAssign(self, node): pass 273 274 def visitBackquote(self, node): pass 275 276 def visitBitand(self, node): pass 277 278 def visitBitor(self, node): pass 279 280 def visitBitxor(self, node): pass 281 282 def visitBreak(self, node): 283 next_label, exit_label = self.loop_labels[-1] 284 self.new_op(Jump(exit_label)) 285 286 def visitCallFunc(self, node): 287 288 """ 289 Evaluate positional arguments, evaluate and store keyword arguments in 290 the correct location, then invoke the function. 291 """ 292 293 # Mark the frame, evaluate the target, generate the call. 294 295 self._startCallFunc() 296 self.dispatch(node.node) 297 self._generateCallFunc(node.args, node) 298 299 def _startCallFunc(self): 300 301 "Record the location of the invocation." 302 303 self.new_op(MakeFrame()) # records the start of the frame 304 305 def _generateCallFunc(self, args, node): 306 307 # NOTE: Only simple cases are used for optimisations. 308 309 last = self.last_op() 310 if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: 311 target = last.attr.value 312 context = last.attr.parent 313 else: 314 target = None 315 context = None 316 317 # Where a target is known and has a known context, avoid generating any 318 # first argument. 319 320 if context is not None: 321 pass # NOTE: Class methods should be supported. 322 else: 323 continue_label = self.new_label() 324 self.new_op(LoadContext()) 325 self.new_op(CheckContext()) 326 self.new_op(JumpIfTrue(continue_label)) 327 self.new_op(LoadConst("TypeError")) # NOTE: Do this properly! 328 self.new_op(RaiseException()) 329 self.set_label(continue_label) 330 331 # Evaluate the arguments. 332 333 positional = 1 334 335 for i, arg in enumerate(args): 336 if isinstance(arg, compiler.ast.Keyword): 337 if positional: 338 self.new_op(ReserveFrame(len(args) - i)) 339 positional = 0 340 341 self.dispatch(arg.expr) 342 343 # Optimise where the target is known now. 344 345 if target is not None: 346 347 # Find the parameter table entry for the target. 348 349 target_name = target.full_name() 350 351 # Look for a callable with the precise target name. 352 353 try: 354 table_entry = self.paramtable.table[target_name] 355 356 # Where no callable is present, check to see if it is a 357 # class name and find the initialiser instead. 358 359 except KeyError: 360 if self.objtable.table.has_key(target_name): 361 table_entry = self.paramtable.table[target_name + ".__init__"] 362 else: 363 raise 364 365 pos = table_entry[arg.name] 366 self.new_op(StoreFrame(pos)) 367 368 # Otherwise, generate the code needed to obtain the details of 369 # the parameter location. 370 371 else: 372 373 # Combine the target details with the name to get the location. 374 # See the access method on the List class. 375 376 try: 377 paramindex = self.paramtable.get_index(arg.name) 378 except ValueError: 379 raise TranslateError(self.module.full_name(), node, "No parameter definition exists for %r." % arg.name) 380 381 self.new_op(StoreFrameIndex(paramindex)) 382 383 # use (callable+0)+paramindex+table 384 # checks embedded offset against (callable+0) 385 # moves the top of stack to frame+position 386 387 else: 388 self.dispatch(arg) 389 390 self.new_op(LoadCallable()) # uses the start of the frame to get the callable 391 self.new_op(Jump()) 392 393 # NOTE: Exception handling required. 394 395 self.new_op(DropFrame()) 396 397 def visitClass(self, node): 398 unit = self.unit 399 self.unit = node.unit 400 self.unit.code_location = self.module.code_location # class body code is not independently addressable 401 self.dispatch(node.code) 402 self.unit = unit 403 404 def visitCompare(self, node): 405 406 """ 407 self.dispatch(node.expr) 408 for op_name, next_node in compare.ops: 409 methods = self.comparison_methods[op_name] 410 if methods is not None: 411 # Generate method call using evaluated argument and next node. 412 else: 413 # Deal with the special operators. 414 # Provide short-circuiting. 415 """ 416 417 def visitConst(self, node): 418 const = self.module.constant_values[node.value] 419 self.new_op(LoadConst(const)) 420 421 def visitContinue(self, node): 422 next_label, exit_label = self.loop_labels[-1] 423 self.new_op(Jump(next_label)) 424 425 def visitDecorators(self, node): pass 426 427 def visitDict(self, node): pass 428 429 def visitDiscard(self, node): 430 self.dispatch(node.expr) 431 432 def visitDiv(self, node): pass 433 434 def visitEllipsis(self, node): pass 435 436 def visitExec(self, node): pass 437 438 def visitExpression(self, node): pass 439 440 def visitFloorDiv(self, node): pass 441 442 def visitFor(self, node): 443 exit_label = self.new_label() 444 next_label = self.new_label() 445 else_label = self.new_label() 446 447 # Get the "list" to be iterated over, obtain its iterator. 448 449 self._startCallFunc() 450 self.dispatch(node.list) 451 self._generateAttr("__iter__", (LoadAttr, LoadAttrIndex)) 452 self._generateCallFunc([], node) 453 # Iterator on stack. 454 455 # In the loop... 456 457 self.set_label(next_label) 458 459 # Use the iterator to get the next value. 460 461 self._startCallFunc() 462 self.new_op(Duplicate()) 463 self._generateAttr("next", (LoadAttr, LoadAttrIndex)) 464 self._generateCallFunc([], node) 465 466 # Test for StopIteration. 467 468 self.new_op(CheckException("StopIteration")) # NOTE: To be done properly. 469 if node.else_ is not None: 470 self.new_op(JumpIfTrue(else_label)) 471 else: 472 self.new_op(JumpIfTrue(exit_label)) 473 474 # Assign to the target. 475 476 self.dispatch(node.assign) 477 478 # Process the body with the current next and exit points. 479 480 self.loop_labels.append((next_label, exit_label)) 481 self.dispatch(node.body) 482 self.loop_labels.pop() 483 484 # Repeat the loop. 485 486 self.new_op(Jump(next_label)) 487 488 # Produce the "else" section. 489 490 if node.else_ is not None: 491 self.set_label(exit_label) 492 self.dispatch(node.else_) 493 494 # Pop the iterator. 495 496 self.set_label(exit_label) 497 self.new_op(Pop()) 498 499 def visitFrom(self, node): pass 500 501 def visitFunction(self, node): 502 503 # Only store the name when visiting this node from outside. 504 505 if self.unit is not node.unit: 506 self.new_op(LoadConst(Const(node.unit))) 507 self._visitName(node, (StoreName, StoreAttr)) 508 509 # Visiting of the code occurs when get_code is invoked on this node. 510 511 else: 512 self.dispatch(node.code) 513 self.new_op(Return()) 514 515 def visitGenExpr(self, node): pass 516 517 def visitGenExprFor(self, node): pass 518 519 def visitGenExprIf(self, node): pass 520 521 def visitGenExprInner(self, node): pass 522 523 def visitGetattr(self, node): 524 self._visitAttr(node, (LoadAttr, LoadAttrIndex)) 525 526 def visitGlobal(self, node): pass 527 528 def visitIf(self, node): 529 first = 1 530 exit_label = self.new_label() 531 532 for test, body in node.tests + [(None, node.else_)]: 533 if body is None: 534 break 535 if not first: 536 self.set_label(next_label) 537 if test is not None: 538 self.dispatch(test) 539 next_label = self.new_label() 540 self.new_op(JumpIfFalse(next_label)) 541 self.dispatch(body) 542 self.new_op(Jump(exit_label)) 543 first = 0 544 545 self.set_label(exit_label) 546 547 def visitImport(self, node): pass 548 549 def visitInvert(self, node): pass 550 551 def visitKeyword(self, node): pass 552 553 def visitLambda(self, node): pass 554 555 def visitLeftShift(self, node): pass 556 557 def visitList(self, node): pass 558 559 def visitListComp(self, node): pass 560 561 def visitListCompFor(self, node): pass 562 563 def visitListCompIf(self, node): pass 564 565 def visitMod(self, node): pass 566 567 def visitModule(self, node): 568 self.dispatch(node.node) 569 570 def visitMul(self, node): pass 571 572 def visitName(self, node): 573 self._visitName(node, (LoadName, LoadAttr)) 574 575 def visitNot(self, node): pass 576 577 def visitOr(self, node): pass 578 579 def visitPass(self, node): pass 580 581 def visitPower(self, node): pass 582 583 def visitPrint(self, node): pass 584 585 def visitPrintnl(self, node): pass 586 587 def visitRaise(self, node): pass 588 589 def visitReturn(self, node): 590 if node.value is not None: 591 self.dispatch(node.value) 592 self.new_op(Return()) 593 594 def visitRightShift(self, node): pass 595 596 def visitSlice(self, node): pass 597 598 def visitStmt(self, node): 599 for n in node.nodes: 600 self.dispatch(n) 601 602 def visitSub(self, node): pass 603 604 def visitSubscript(self, node): pass 605 606 def visitTryExcept(self, node): pass 607 608 def visitTryFinally(self, node): pass 609 610 def visitTuple(self, node): pass 611 612 def visitUnaryAdd(self, node): pass 613 614 def visitUnarySub(self, node): pass 615 616 def visitWhile(self, node): 617 exit_label = self.new_label() 618 next_label = self.new_label() 619 else_label = self.new_label() 620 621 self.set_label(next_label) 622 self.dispatch(node.test) 623 if node.else_ is not None: 624 self.new_op(JumpIfFalse(else_label)) 625 else: 626 self.new_op(JumpIfFalse(exit_label)) 627 628 self.loop_labels.append((next_label, exit_label)) 629 630 self.dispatch(node.body) 631 self.new_op(Jump(next_label)) 632 633 if node.else_ is not None: 634 self.set_label(else_label) 635 self.dispatch(node.else_) 636 637 self.set_label(exit_label) 638 self.loop_labels.pop() 639 640 def visitWith(self, node): pass 641 642 def visitYield(self, node): pass 643 644 # Useful data. 645 646 comparison_methods = { 647 "==" : ("__eq__", "__ne__"), 648 "!=" : ("__ne__", "__eq__"), 649 "<" : ("__lt__", "__gt__"), 650 "<=" : ("__le__", "__ge__"), 651 ">=" : ("__ge__", "__le__"), 652 ">" : ("__gt__", "__lt__"), 653 "is" : None, 654 "is not" : None, 655 "in" : None, 656 "not in" : None 657 } 658 659 # vim: tabstop=4 expandtab shiftwidth=4