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 self.constants = {} 68 69 # Wiring within the code. 70 71 self.labels = {} 72 self.label_number = 0 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 self.dispatch(self.module.module) 82 return self.code 83 84 def get_code(self, unit): 85 86 "Return the code for the given 'unit'." 87 88 self.unit = unit 89 self.code = [] 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 print self.module.name, len(self.code) + self.unit.code_location, 124 self.code.append(op) 125 print op 126 127 # Visitor methods. 128 129 def default(self, node, *args): 130 raise TranslateError, node.__class__ 131 132 def dispatch(self, node, *args): 133 return ASTVisitor.dispatch(self, node, *args) 134 135 def _visitName(self, node, classes): 136 name = node.name 137 scope = self.get_scope(name) 138 NameInstruction, AttrInstruction = classes 139 140 if scope == "local": 141 unit = self.unit 142 if isinstance(unit, micropython.inspect.Function): 143 self.new_op(NameInstruction(unit.locals()[name])) 144 elif isinstance(unit, micropython.inspect.Class): 145 self.new_op(AttrInstruction(unit.all_class_attributes()[name])) 146 elif isinstance(unit, micropython.inspect.Module): 147 self.new_op(AttrInstruction(unit.module_attributes()[name])) 148 else: 149 raise TranslateError, "Program unit %r has no local %r" % (unit, name) 150 151 elif scope == "global": 152 globals = self.module.module_attributes() 153 if globals.has_key(name): 154 self.new_op(AttrInstruction(globals[name])) 155 else: 156 raise TranslateError, "Module %r has no attribute %r" % (self.module, name) 157 158 else: 159 builtins = micropython.inspect.builtins.module_attributes() 160 self.new_op(AttrInstruction(builtins[name])) 161 162 def visitAdd(self, node): 163 164 """ 165 _t1 = node.left 166 _t2 = node.right 167 try: 168 _t1.__add__(_t2) 169 except AttributeError: 170 _t2.__radd__(_t1) 171 """ 172 173 def visitAnd(self, node): pass 174 175 def visitAssert(self, node): pass 176 177 def visitAssign(self, node): 178 self.dispatch(node.expr) 179 for n in node.nodes: 180 self.dispatch(n) 181 182 def visitAssAttr(self, node): pass 183 184 def visitAssList(self, node): pass 185 186 def visitAssName(self, node): 187 self._visitName(node, (StoreName, StoreAttr)) 188 189 visitAssTuple = visitAssList 190 191 def visitAugAssign(self, node): pass 192 193 def visitBackquote(self, node): pass 194 195 def visitBitand(self, node): pass 196 197 def visitBitor(self, node): pass 198 199 def visitBitxor(self, node): pass 200 201 def visitBreak(self, node): pass 202 203 def visitCallFunc(self, node): 204 205 """ 206 Evaluate positional arguments, evaluate and store keyword arguments in 207 the correct location, then invoke the function. 208 """ 209 210 # Record the location of the invocation. 211 212 self.new_op(MakeFrame()) # records the start of the frame 213 214 # Evaluate the target. 215 216 self.dispatch(node.node) 217 218 # Evaluate the arguments. 219 220 for i, arg in enumerate(node.args): 221 if isinstance(arg, compiler.ast.Keyword): 222 self.dispatch(arg.expr) 223 224 # Combine the target details with the name to get the location. 225 # See the access method on the Table class. 226 227 paramindex = self.paramtable.get_index(arg.name) 228 self.new_op(StoreFrame(paramindex)) 229 230 # use (callable+0)+paramindex+table 231 # checks embedded offset against (callable+0) 232 # moves the top of stack to frame+position 233 234 else: 235 self.dispatch(arg) 236 237 self.new_op(LoadCallable()) # uses the start of the frame to get the callable 238 self.new_op(Jump()) 239 240 # NOTE: Exception handling required. 241 242 self.new_op(DropFrame()) 243 244 def visitClass(self, node): pass 245 246 def visitCompare(self, node): 247 248 """ 249 self.dispatch(node.expr) 250 for op_name, next_node in compare.ops: 251 methods = self.comparison_methods[op_name] 252 if methods is not None: 253 # Generate method call using evaluated argument and next node. 254 else: 255 # Deal with the special operators. 256 # Provide short-circuiting. 257 """ 258 259 def visitConst(self, node): pass 260 261 def visitContinue(self, node): pass 262 263 def visitDecorators(self, node): pass 264 265 def visitDict(self, node): pass 266 267 def visitDiscard(self, node): 268 self.dispatch(node.expr) 269 270 def visitDiv(self, node): pass 271 272 def visitEllipsis(self, node): pass 273 274 def visitExec(self, node): pass 275 276 def visitExpression(self, node): pass 277 278 def visitFloorDiv(self, node): pass 279 280 def visitFor(self, node): pass 281 282 def visitFrom(self, node): pass 283 284 def visitFunction(self, node): 285 286 # Only store the name when visiting this node from outside. 287 288 if self.unit is self.module: 289 self.new_op(LoadConst(node.function)) 290 self._visitName(node, (StoreName, StoreAttr)) 291 else: 292 self.dispatch(node.code) 293 self.new_op(Return()) 294 295 def visitGenExpr(self, node): pass 296 297 def visitGenExprFor(self, node): pass 298 299 def visitGenExprIf(self, node): pass 300 301 def visitGenExprInner(self, node): pass 302 303 def visitGetattr(self, node): pass 304 305 def visitGlobal(self, node): pass 306 307 def visitIf(self, node): 308 first = 1 309 exit_label = self.new_label() 310 311 for test, body in node.tests + [(None, node.else_)]: 312 if body is None: 313 break 314 if not first: 315 self.set_label(next_label) 316 if test is not None: 317 self.dispatch(test) 318 next_label = self.new_label() 319 self.new_op(Jump(next_label)) 320 self.dispatch(body) 321 self.new_op(Jump(exit_label)) 322 first = 0 323 324 self.set_label(exit_label) 325 326 def visitImport(self, node): pass 327 328 def visitInvert(self, node): pass 329 330 def visitKeyword(self, node): pass 331 332 def visitLambda(self, node): pass 333 334 def visitLeftShift(self, node): pass 335 336 def visitList(self, node): pass 337 338 def visitListComp(self, node): pass 339 340 def visitListCompFor(self, node): pass 341 342 def visitListCompIf(self, node): pass 343 344 def visitMod(self, node): pass 345 346 def visitModule(self, node): 347 self.dispatch(node.node) 348 349 def visitMul(self, node): pass 350 351 def visitName(self, node): 352 self._visitName(node, (LoadName, LoadAttr)) 353 354 def visitNot(self, node): pass 355 356 def visitOr(self, node): pass 357 358 def visitPass(self, node): pass 359 360 def visitPower(self, node): pass 361 362 def visitPrint(self, node): pass 363 364 def visitPrintnl(self, node): pass 365 366 def visitRaise(self, node): pass 367 368 def visitReturn(self, node): 369 if node.value is not None: 370 self.dispatch(node.value) 371 self.new_op(Return()) 372 373 def visitRightShift(self, node): pass 374 375 def visitSlice(self, node): pass 376 377 def visitStmt(self, node): 378 for n in node.nodes: 379 self.dispatch(n) 380 381 def visitSub(self, node): pass 382 383 def visitSubscript(self, node): pass 384 385 def visitTryExcept(self, node): pass 386 387 def visitTryFinally(self, node): pass 388 389 def visitTuple(self, node): pass 390 391 def visitUnaryAdd(self, node): pass 392 393 def visitUnarySub(self, node): pass 394 395 def visitWhile(self, node): pass 396 397 def visitWith(self, node): pass 398 399 def visitYield(self, node): pass 400 401 # Useful data. 402 403 comparison_methods = { 404 "==" : ("__eq__", "__ne__"), 405 "!=" : ("__ne__", "__eq__"), 406 "<" : ("__lt__", "__gt__"), 407 "<=" : ("__le__", "__ge__"), 408 ">=" : ("__ge__", "__le__"), 409 ">" : ("__gt__", "__lt__"), 410 "is" : None, 411 "is not" : None, 412 "in" : None, 413 "not in" : None 414 } 415 416 # vim: tabstop=4 expandtab shiftwidth=4