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 # Mark the frame, evaluate the target, generate the call. 282 283 self._startCallFunc() 284 self.dispatch(node.node) 285 self._generateCallFunc(node.args, node) 286 287 def _startCallFunc(self): 288 289 "Record the location of the invocation." 290 291 self.new_op(MakeFrame()) # records the start of the frame 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 context = last.attr.parent 301 else: 302 target = None 303 context = None 304 305 # Where a target is known and has a known context, avoid generating any 306 # first argument. 307 308 if context is not None: 309 pass # NOTE: Class methods should be supported. 310 else: 311 continue_label = self.new_label() 312 self.new_op(LoadContext()) 313 self.new_op(CheckContext()) 314 self.new_op(JumpIfTrue(continue_label)) 315 self.new_op(LoadConst("TypeError")) # NOTE: Do this properly! 316 self.new_op(RaiseException()) 317 self.set_label(continue_label) 318 319 # Evaluate the arguments. 320 321 positional = 1 322 323 for i, arg in enumerate(args): 324 if isinstance(arg, compiler.ast.Keyword): 325 if positional: 326 self.new_op(ReserveFrame(len(args) - i)) 327 positional = 0 328 329 self.dispatch(arg.expr) 330 331 # Optimise where the target is known now. 332 333 if target is not None: 334 335 # Find the parameter table entry for the target. 336 337 target_name = target.full_name() 338 339 # Look for a callable with the precise target name. 340 341 try: 342 table_entry = self.paramtable.table[target_name] 343 344 # Where no callable is present, check to see if it is a 345 # class name and find the initialiser instead. 346 347 except KeyError: 348 if self.objtable.table.has_key(target_name): 349 table_entry = self.paramtable.table[target_name + ".__init__"] 350 else: 351 raise 352 353 pos = table_entry[arg.name] 354 self.new_op(StoreFrame(pos)) 355 356 # Otherwise, generate the code needed to obtain the details of 357 # the parameter location. 358 359 else: 360 361 # Combine the target details with the name to get the location. 362 # See the access method on the List class. 363 364 try: 365 paramindex = self.paramtable.get_index(arg.name) 366 except ValueError: 367 raise TranslateError(self.module.full_name(), node, "No parameter definition exists for %r." % arg.name) 368 369 self.new_op(StoreFrameIndex(paramindex)) 370 371 # use (callable+0)+paramindex+table 372 # checks embedded offset against (callable+0) 373 # moves the top of stack to frame+position 374 375 else: 376 self.dispatch(arg) 377 378 self.new_op(LoadCallable()) # uses the start of the frame to get the callable 379 self.new_op(Jump()) 380 381 # NOTE: Exception handling required. 382 383 self.new_op(DropFrame()) 384 385 def visitClass(self, node): 386 unit = self.unit 387 self.unit = node.unit 388 self.unit.code_location = self.module.code_location # class body code is not independently addressable 389 self.dispatch(node.code) 390 self.unit = unit 391 392 def visitCompare(self, node): 393 394 """ 395 self.dispatch(node.expr) 396 for op_name, next_node in compare.ops: 397 methods = self.comparison_methods[op_name] 398 if methods is not None: 399 # Generate method call using evaluated argument and next node. 400 else: 401 # Deal with the special operators. 402 # Provide short-circuiting. 403 """ 404 405 def visitConst(self, node): 406 const = self.module.constant_values[node.value] 407 self.new_op(LoadConst(const)) 408 409 def visitContinue(self, node): 410 next_label, exit_label = self.loop_labels[-1] 411 self.new_op(Jump(next_label)) 412 413 def visitDecorators(self, node): pass 414 415 def visitDict(self, node): pass 416 417 def visitDiscard(self, node): 418 self.dispatch(node.expr) 419 420 def visitDiv(self, node): pass 421 422 def visitEllipsis(self, node): pass 423 424 def visitExec(self, node): pass 425 426 def visitExpression(self, node): pass 427 428 def visitFloorDiv(self, node): pass 429 430 def visitFor(self, node): 431 exit_label = self.new_label() 432 next_label = self.new_label() 433 else_label = self.new_label() 434 435 # Get the "list" to be iterated over, obtain its iterator. 436 437 self._startCallFunc() 438 self.dispatch(node.list) 439 self._generateAttr("__iter__", (LoadAttr, LoadAttrIndex)) 440 self._generateCallFunc([], node) 441 # Iterator on stack. 442 443 # In the loop... 444 445 self.set_label(next_label) 446 447 # Use the iterator to get the next value. 448 449 self._startCallFunc() 450 self.new_op(Duplicate()) 451 self._generateAttr("next", (LoadAttr, LoadAttrIndex)) 452 self._generateCallFunc([], node) 453 454 # Test for StopIteration. 455 456 self.new_op(CheckException("StopIteration")) # NOTE: To be done properly. 457 if node.else_ is not None: 458 self.new_op(JumpIfTrue(else_label)) 459 else: 460 self.new_op(JumpIfTrue(exit_label)) 461 462 # Assign to the target. 463 464 self.dispatch(node.assign) 465 466 # Process the body with the current next and exit points. 467 468 self.loop_labels.append((next_label, exit_label)) 469 self.dispatch(node.body) 470 self.loop_labels.pop() 471 472 # Repeat the loop. 473 474 self.new_op(Jump(next_label)) 475 476 # Produce the "else" section. 477 478 if node.else_ is not None: 479 self.set_label(exit_label) 480 self.dispatch(node.else_) 481 482 # Pop the iterator. 483 484 self.set_label(exit_label) 485 self.new_op(Pop()) 486 487 def visitFrom(self, node): pass 488 489 def visitFunction(self, node): 490 491 # Only store the name when visiting this node from outside. 492 493 if self.unit is not node.unit: 494 self.new_op(LoadConst(node.unit)) 495 self._visitName(node, (StoreName, StoreAttr)) 496 497 # Visiting of the code occurs when get_code is invoked on this node. 498 499 else: 500 self.dispatch(node.code) 501 self.new_op(Return()) 502 503 def visitGenExpr(self, node): pass 504 505 def visitGenExprFor(self, node): pass 506 507 def visitGenExprIf(self, node): pass 508 509 def visitGenExprInner(self, node): pass 510 511 def visitGetattr(self, node): 512 self._visitAttr(node, (LoadAttr, LoadAttrIndex)) 513 514 def visitGlobal(self, node): pass 515 516 def visitIf(self, node): 517 first = 1 518 exit_label = self.new_label() 519 520 for test, body in node.tests + [(None, node.else_)]: 521 if body is None: 522 break 523 if not first: 524 self.set_label(next_label) 525 if test is not None: 526 self.dispatch(test) 527 next_label = self.new_label() 528 self.new_op(JumpIfFalse(next_label)) 529 self.dispatch(body) 530 self.new_op(Jump(exit_label)) 531 first = 0 532 533 self.set_label(exit_label) 534 535 def visitImport(self, node): pass 536 537 def visitInvert(self, node): pass 538 539 def visitKeyword(self, node): pass 540 541 def visitLambda(self, node): pass 542 543 def visitLeftShift(self, node): pass 544 545 def visitList(self, node): pass 546 547 def visitListComp(self, node): pass 548 549 def visitListCompFor(self, node): pass 550 551 def visitListCompIf(self, node): pass 552 553 def visitMod(self, node): pass 554 555 def visitModule(self, node): 556 self.dispatch(node.node) 557 558 def visitMul(self, node): pass 559 560 def visitName(self, node): 561 self._visitName(node, (LoadName, LoadAttr)) 562 563 def visitNot(self, node): pass 564 565 def visitOr(self, node): pass 566 567 def visitPass(self, node): pass 568 569 def visitPower(self, node): pass 570 571 def visitPrint(self, node): pass 572 573 def visitPrintnl(self, node): pass 574 575 def visitRaise(self, node): pass 576 577 def visitReturn(self, node): 578 if node.value is not None: 579 self.dispatch(node.value) 580 self.new_op(Return()) 581 582 def visitRightShift(self, node): pass 583 584 def visitSlice(self, node): pass 585 586 def visitStmt(self, node): 587 for n in node.nodes: 588 self.dispatch(n) 589 590 def visitSub(self, node): pass 591 592 def visitSubscript(self, node): pass 593 594 def visitTryExcept(self, node): pass 595 596 def visitTryFinally(self, node): pass 597 598 def visitTuple(self, node): pass 599 600 def visitUnaryAdd(self, node): pass 601 602 def visitUnarySub(self, node): pass 603 604 def visitWhile(self, node): 605 exit_label = self.new_label() 606 next_label = self.new_label() 607 else_label = self.new_label() 608 609 self.set_label(next_label) 610 self.dispatch(node.test) 611 if node.else_ is not None: 612 self.new_op(JumpIfFalse(else_label)) 613 else: 614 self.new_op(JumpIfFalse(exit_label)) 615 616 self.loop_labels.append((next_label, exit_label)) 617 618 self.dispatch(node.body) 619 self.new_op(Jump(next_label)) 620 621 if node.else_ is not None: 622 self.set_label(else_label) 623 self.dispatch(node.else_) 624 625 self.set_label(exit_label) 626 self.loop_labels.pop() 627 628 def visitWith(self, node): pass 629 630 def visitYield(self, node): pass 631 632 # Useful data. 633 634 comparison_methods = { 635 "==" : ("__eq__", "__ne__"), 636 "!=" : ("__ne__", "__eq__"), 637 "<" : ("__lt__", "__gt__"), 638 "<=" : ("__le__", "__ge__"), 639 ">=" : ("__ge__", "__le__"), 640 ">" : ("__gt__", "__lt__"), 641 "is" : None, 642 "is not" : None, 643 "in" : None, 644 "not in" : None 645 } 646 647 # vim: tabstop=4 expandtab shiftwidth=4