![]() |
Shadowrun: Awakened 29 September 2011 - Build 871
|
00001 import ast 00002 import types 00003 import sys 00004 00005 def translate_file(fpath, outpath, pretty=False): 00006 if not fpath: 00007 print 'No filepath supplied. Exiting.' 00008 return 00009 f = open(fpath, 'r') 00010 if not f: 00011 print 'Could not open file "' + fpath + '". Exiting.' 00012 return 00013 source = f.read() 00014 f.close() 00015 tree = ast.parse(source) 00016 translator = Py2Un() 00017 src = translator.walk_tree(tree, pretty) 00018 if src and outpath: 00019 f = open(outpath, 'w') 00020 if f: 00021 f.write(src) 00022 f.close() 00023 00024 opTransDict = { 00025 #Bool Ops 00026 'And': '&&', 00027 'Or': '||', 00028 #Binary Ops 00029 'Add': '+', 00030 'Sub': '-', 00031 'Mult': '*', 00032 'Div': '/', 00033 'Mod': '%', 00034 'Pow': '**', 00035 'LShift': '<<', 00036 'RShift': '>>', 00037 'BitOr': '|', 00038 'BitXor': '^', 00039 'BitAnd': '&', 00040 'FloorDiv': '//', 00041 #Unary Ops 00042 'Invert': '~', 00043 'Not': '!', 00044 'UAdd': '+', 00045 'USub': '-', 00046 #Compare Ops 00047 'Eq': '==', 00048 'NotEq': '!=', 00049 'Lt': '<', 00050 'LtE': '<=', 00051 'Gt': '>', 00052 'GtE': '>=', 00053 'Is': 'is', 00054 'IsNot': 'isnot', 00055 'In': 'in', 00056 'NotIn': 'notin', 00057 } 00058 00059 builtins = [ 00060 'abs', 00061 'bool', 00062 'chr', 00063 'cmp', 00064 'dict', 00065 'divmod', 00066 'hasattr', 00067 'getattr', 00068 'globals', 00069 'iter', 00070 'len', 00071 'list', 00072 'locals', 00073 'max', 00074 'min', 00075 'next', 00076 'object', 00077 'ord', 00078 'range', 00079 'repr', 00080 'round', 00081 'setattr', 00082 'slice', 00083 'sorted', 00084 'str', 00085 'struct', 00086 'sum', 00087 'tuple', 00088 'type', 00089 'zip', 00090 ] 00091 00092 def transOp(op): 00093 if op in opTransDict: 00094 return opTransDict[op] 00095 return 'NotImplemented' 00096 00097 class Py2Un(ast.NodeVisitor): 00098 def __init__(self): 00099 self.ind = 0 00100 self.opnum = 0 00101 self.pretty = False 00102 self.tokens = [] 00103 self.globalStack = [] 00104 self.overrideMode = None 00105 def walk_tree(self, node, pretty): 00106 self.ind = 0 00107 self.opnum = 0 00108 self.pretty = pretty 00109 self.overrideMode = None 00110 self.visit(node) 00111 i = 0 00112 for t in self.tokens: 00113 if self.pretty: 00114 print str(i) + ' ' + t 00115 else: 00116 print t, 00117 i += 1 00118 return ' '.join(self.tokens) 00119 def comment(self, text): 00120 if self.pretty: 00121 self.tokens.append( '#' + text ) 00122 def output(self, text): 00123 if self.pretty: 00124 self.tokens.append( self.getTab() + text ) 00125 else: 00126 self.tokens.append( text ) 00127 self.opnum += 1 00128 def update(self, tokInd, txt): 00129 if tokInd < len(self.tokens) and tokInd >= 0: 00130 if self.pretty: 00131 self.tokens[tokInd] = self.getTab() + txt 00132 else: 00133 self.tokens[tokInd] = txt 00134 def getTab(self, off=0): 00135 return (self.ind + off) * ' ' 00136 def indent(self, amt=4): 00137 self.ind += amt 00138 def dedent(self, amt=4): 00139 self.ind -= amt 00140 def generic_visit(self, node): 00141 self.output('Undef: ' + str(type(node))) 00142 self.indent() 00143 self.output('Fields: ' + str(node._fields)) 00144 ast.NodeVisitor.generic_visit(self, node) 00145 self.dedent() 00146 00147 def pushGlobalScope(self): 00148 self.globalStack.append([]) 00149 self.comment('StartScope,' + str(len(self.globalStack))) 00150 def popGlobalScope(self): 00151 if len(self.globalStack) > 0: 00152 self.comment('EndScope,' + str(len(self.globalStack))) 00153 del self.globalStack[-1] 00154 def addGlobal(self, name): 00155 if len(self.globalStack) > 0: 00156 if not name in self.globalStack[-1]: 00157 self.globalStack[-1].append(name) 00158 def isGlobal(self, name): 00159 for l in self.globalStack: 00160 if name in l: 00161 return True 00162 return False 00163 00164 #================= Module level ===================== 00165 def visit_Module(self, node): 00166 self.pushGlobalScope() 00167 ast.NodeVisitor.generic_visit(self, node) 00168 self.popGlobalScope() 00169 00170 #================= Statements ===================== 00171 def visit_FunctionDef(self, node): 00172 self.comment('FunctionDef') 00173 self.indent() 00174 #Regular param names 00175 for n in node.args.args: 00176 ast.NodeVisitor.visit(self,n) 00177 self.output('BUILDLIST,' + str(len(node.args.args))) 00178 #Default values if any 00179 for n in node.args.defaults: 00180 ast.NodeVisitor.visit(self,n) 00181 self.output('BUILDLIST,' + str(len(node.args.defaults))) 00182 self.pushGlobalScope() 00183 self.output('DEFCODE,' + node.name) 00184 for n in node.body: 00185 ast.NodeVisitor.visit(self,n) 00186 self.output('BUILDCODE,' + node.name) 00187 self.popGlobalScope() 00188 self.output('SETLOC,' + node.name) 00189 self.dedent() 00190 00191 def visit_Return(self, node): 00192 self.comment('Return') 00193 self.indent() 00194 if node.value: 00195 ast.NodeVisitor.visit(self,node.value) 00196 self.output('RETURN') 00197 self.dedent() 00198 00199 def visit_Delete(self, node): 00200 self.comment('Delete') 00201 self.indent() 00202 for n in node.targets: 00203 ast.NodeVisitor.visit(self,n) 00204 self.dedent() 00205 00206 def visit_Assign(self, node): 00207 self.comment('Assign') 00208 self.indent() 00209 ast.NodeVisitor.visit(self,node.value) 00210 for n in node.targets: 00211 ast.NodeVisitor.visit(self,n) 00212 self.dedent() 00213 00214 def visit_AugAssign(self, node): 00215 self.comment('AugAssign') 00216 self.indent() 00217 prevOverride = self.overrideMode 00218 self.overrideMode = 'Load' 00219 ast.NodeVisitor.visit(self,node.target) 00220 self.overrideMode = prevOverride 00221 ast.NodeVisitor.visit(self,node.value) 00222 self.output('CMP,' + transOp(type(node.op).__name__)) 00223 ast.NodeVisitor.visit(self,node.target) 00224 self.dedent() 00225 00226 def visit_Print(self, node): 00227 self.comment('Print') 00228 self.indent() 00229 for n in node.values: 00230 ast.NodeVisitor.visit(self,n) 00231 self.output('PRINT,' + str(len(node.values))) 00232 self.dedent() 00233 00234 def visit_For(self, node): 00235 self.comment('For') 00236 self.indent() 00237 ast.NodeVisitor.visit(self,node.iter) 00238 self.output('GET_ITER') 00239 self.output('WHILE') 00240 self.output('') 00241 plcHolder = len(self.tokens) - 1 00242 plcIndex = self.opnum 00243 ast.NodeVisitor.visit(self,node.target) 00244 self.indent() 00245 for n in node.body: 00246 ast.NodeVisitor.visit(self,n) 00247 self.dedent() 00248 self.output('END') 00249 self.update(plcHolder, 'FOR_ITER,' + str(self.opnum - plcIndex + 1)) 00250 for n in node.orelse: 00251 ast.NodeVisitor.visit(self,n) 00252 self.dedent() 00253 00254 def visit_While(self, node): 00255 self.comment('While') 00256 self.indent() 00257 self.output('WHILE') 00258 ast.NodeVisitor.visit(self.test) 00259 self.output('TEST') 00260 self.indent() 00261 for n in node.body: 00262 ast.NodeVisitor.visit(self,n) 00263 self.dedent() 00264 self.output('END') 00265 for n in node.orelse: 00266 ast.NodeVisitor.visit(self,n) 00267 self.dedent() 00268 00269 def visit_If(self, node): 00270 self.comment('If') 00271 self.indent() 00272 self.output('IF') 00273 ast.NodeVisitor.visit(self,node.test) 00274 self.output('TEST') 00275 for n in node.body: 00276 ast.NodeVisitor.visit(self,n) 00277 if node.orelse: 00278 self.output('ELSE') 00279 for n in node.orelse: 00280 ast.NodeVisitor.visit(self,n) 00281 self.output('END') 00282 self.dedent() 00283 00284 def visit_TryExcept(self, node): 00285 self.comment('TryExcept') 00286 self.indent() 00287 self.output('TRY') 00288 plcHolder = len(self.tokens) - 1 00289 plcIndex = self.opnum 00290 for n in node.body: 00291 ast.NodeVisitor.visit(self,n) 00292 for n in node.handlers: 00293 ast.NodeVisitor.visit(self,n) 00294 if node.orelse: 00295 self.output('ELSE') 00296 for n in node.orelse: 00297 ast.NodeVisitor.visit(self,n) 00298 self.output('END') 00299 self.update(plcHolder, 'TRY,' + str(self.opnum - plcIndex + 1)) 00300 self.dedent() 00301 00302 def visit_TryFinally(self, node): 00303 self.comment('TryFinally') 00304 self.indent() 00305 self.output('') 00306 plcHolder = len(self.tokens) - 1 00307 plcIndex = self.opnum 00308 for n in node.body: 00309 ast.NodeVisitor.visit(self,n) 00310 self.output('FINALLY') 00311 for n in node.finalbody: 00312 ast.NodeVisitor.visit(self,n) 00313 self.output('END') 00314 self.update(plcHolder, 'TRY,' + str(self.opnum - plcIndex + 1)) 00315 self.dedent() 00316 00317 def visit_Global(self, node): 00318 self.comment('Global') 00319 self.indent() 00320 for n in node.names: 00321 self.addGlobal(n) 00322 self.dedent() 00323 00324 def visit_Expr(self, node): 00325 self.comment('Expr') 00326 self.indent() 00327 ast.NodeVisitor.visit(self,node.value) 00328 self.output('POP') 00329 self.dedent() 00330 00331 def visit_Pass(self, none): 00332 self.comment('Pass') 00333 00334 #================= Expressions ===================== 00335 def visit_BoolOp(self, node): 00336 self.comment('BoolOp') 00337 self.indent() 00338 #self.comment('Need_to_resolve_short_circuit_logic') 00339 op = transOp(type(node.op).__name__) 00340 if len(node.values) != 2: 00341 self.comment('BoolOp_with_' + str(len(node.values)) + '_values_not_anticipated') 00342 self.dedent() 00343 return 00344 #Output left side of bool operation 00345 ast.NodeVisitor.visit(self,node.values[0]) 00346 #Output placeholder which will contain skip logic for short-circuit 00347 self.output('') 00348 #Save index of placeholder 00349 plcHolder = len(self.tokens) - 1 00350 plcIndex = self.opnum 00351 #Output right side of bool operation 00352 ast.NodeVisitor.visit(self,node.values[1]) 00353 #Update skip logic with next op to skip to 00354 if op == '&&': 00355 self.update(plcHolder, 'JMPF_OR_POP,' + str(self.opnum - plcIndex + 1)) 00356 elif op == '||': 00357 self.update(plcHolder, 'JMPT_OR_POP,' + str(self.opnum - plcIndex + 1)) 00358 #self.comment('CurrentLoc:' + str(self.opnum+1)) 00359 self.dedent() 00360 00361 def visit_BinOp(self, node): 00362 self.comment('BinOp') 00363 self.indent() 00364 ast.NodeVisitor.visit(self,node.left) 00365 ast.NodeVisitor.visit(self,node.right) 00366 self.output('CMP,' + transOp(type(node.op).__name__)) 00367 self.dedent() 00368 00369 def visit_UnaryOp(self, node): 00370 self.comment('UnaryOp') 00371 self.indent() 00372 ast.NodeVisitor.visit(self,node.operand) 00373 self.output('UNARY,' + transOp(type(node.op).__name__)) 00374 self.dedent() 00375 00376 def visit_Dict(self, node): 00377 self.comment('Dict') 00378 self.indent() 00379 for n in node.keys: 00380 ast.NodeVisitor.visit(self,n) 00381 self.output('BUILDLIST,' + str(len(node.keys))) 00382 for n in node.values: 00383 ast.NodeVisitor.visit(self,n) 00384 self.output('BUILDLIST,' + str(len(node.values))) 00385 self.output('BUILDDICT,' + str(len(node.keys))) 00386 self.dedent() 00387 00388 def visit_Compare(self, node): 00389 self.comment('Compare') 00390 self.indent() 00391 #self.comment('Compare_Not_Implemented_Yet') 00392 ast.NodeVisitor.visit(self,node.left) 00393 #List of tuples for tokens index and opnum, used to set offsets to jump point 00394 replacements = [] 00395 doComplex = len(node.ops) > 1 00396 if len(node.ops) > 0: 00397 for i in range(len(node.ops)): 00398 if i < len(node.comparators): 00399 if i > 0: 00400 self.output('') 00401 replacements.append((len(self.tokens) - 1, self.opnum )) 00402 ast.NodeVisitor.visit(self,node.comparators[i]) 00403 #Duplicate top op and put behind top 2 to avoid 00404 if doComplex and i + 1 < len(node.ops): 00405 self.output('DUP') 00406 self.output('ROT_3') 00407 self.output('CMP,' + transOp(type(node.ops[i]).__name__)) 00408 #Jump past end ROT_2 and POP on the last op 00409 if doComplex and i + 1 == len(node.ops): 00410 self.output('JMP_REL,3') 00411 else: 00412 self.comment('More_ops_than_comparators!') 00413 for r in replacements: 00414 self.update(r[0], 'JMPF_OR_POP,' + str(self.opnum - r[1] + 1)) 00415 if doComplex: 00416 self.output('ROT_2') 00417 self.output('POP') 00418 self.dedent() 00419 00420 def visit_Call(self, node): 00421 self.comment('Call') 00422 self.indent() 00423 #self.comment('builtins:' + repr(builtins)) 00424 if type(node.func) is ast.Name: 00425 if node.func.id in builtins: 00426 self.output('FUNC,' + node.func.id) 00427 else: 00428 ast.NodeVisitor.visit(self,node.func) 00429 else: 00430 ast.NodeVisitor.visit(self,node.func) 00431 for n in node.args: 00432 ast.NodeVisitor.visit(self,n) 00433 self.output('CALL,' + str(len(node.args))) 00434 self.dedent() 00435 00436 def visit_Num(self, node): 00437 self.comment('Num') 00438 self.indent() 00439 if (type(node.n) == types.IntType): 00440 self.output('PUSHI,' + str(node.n)) 00441 elif (type(node.n) == types.FloatType): 00442 self.output('PUSHF,' + str(node.n)) 00443 self.dedent() 00444 00445 def visit_Str(self, node): 00446 self.comment('Str') 00447 self.indent() 00448 rep = node.s.replace(' ','\\_').replace(',','\\.') 00449 self.output('PUSHS,' + rep) 00450 self.dedent() 00451 00452 def visit_Attribute(self, node): 00453 self.comment('Attribute') 00454 name = self.overrideMode or type(node.ctx).__name__ 00455 self.comment(name) 00456 self.indent() 00457 00458 #TOS - object 00459 ast.NodeVisitor.visit(self,node.value) 00460 00461 if name == 'Store': 00462 self.output('SETATTR,' + node.attr) 00463 elif name == 'Load': 00464 self.output('GETATTR,' + node.attr) 00465 elif name == 'Del': 00466 self.output('DELATTR,' + node.attr) 00467 00468 self.dedent() 00469 00470 def visit_Subscript(self, node): 00471 self.comment('Subscript') 00472 00473 name = self.overrideMode or type(node.ctx).__name__ 00474 00475 self.indent() 00476 #ast.NodeVisitor.visit(self,node.value) 00477 ast.NodeVisitor.visit(self,node.value) 00478 ast.NodeVisitor.visit(self,node.slice) 00479 00480 if name == 'Store': 00481 self.output('SETIND') 00482 elif name == 'Load': 00483 self.output('GETIND') 00484 elif name == 'Del': 00485 self.output('DELIND') 00486 else: 00487 self.output('????') 00488 00489 self.dedent() 00490 00491 def visit_Name(self, node): 00492 self.comment('Name') 00493 name = self.overrideMode or type(node.ctx).__name__ 00494 self.indent() 00495 glb = self.isGlobal(node.id) 00496 if name == 'Store': 00497 if glb: 00498 self.output('SETGLB,' + node.id) 00499 else: 00500 self.output('SETLOC,' + node.id) 00501 elif name == 'Load': 00502 #Push contant values if applicable 00503 if node.id == 'True': 00504 self.output('PUSHB,True') 00505 elif node.id == 'False': 00506 self.output('PUSHB,False') 00507 elif node.id == 'None': 00508 self.output('PUSHN') 00509 #otherwise assume it is a variable name 00510 else: 00511 if glb: 00512 self.output('GETGLB,' + node.id) 00513 else: 00514 self.output('GETLOC,' + node.id) 00515 elif name == 'Del': 00516 if glb: 00517 self.output('DELGLB,' + node.id) 00518 else: 00519 self.output('DELLOC,' + node.id) 00520 elif name == 'Param': 00521 self.output('PUSHS,' + node.id) 00522 else: 00523 self.output('????,' + node.id) 00524 self.dedent() 00525 00526 def visit_List(self, node): 00527 self.comment('List') 00528 name = self.overrideMode or type(node.ctx).__name__ 00529 self.indent() 00530 if name == 'Store': 00531 lst = [] 00532 for n in node.elts: 00533 self.buildNameList(n, lst) 00534 self.output('SETLOC,' + ','.join(lst)) 00535 else: 00536 for n in node.elts: 00537 ast.NodeVisitor.visit(self,n) 00538 self.output('BUILDLIST,' + str(len(node.elts))) 00539 self.dedent() 00540 00541 def visit_Tuple(self, node): 00542 self.comment('Tuple') 00543 name = self.overrideMode or type(node.ctx).__name__ 00544 self.indent() 00545 if name == 'Store': 00546 lst = [] 00547 for n in node.elts: 00548 self.buildNameList(n, lst) 00549 self.output('SETLOC,' + ','.join(lst)) 00550 #Otherwise just build a tuple 00551 else: 00552 for n in node.elts: 00553 ast.NodeVisitor.visit(self,n) 00554 self.output('BUILDTUPLE,' + str(len(node.elts))) 00555 self.dedent() 00556 00557 def buildNameList(self, node, lst): 00558 if type(node).__name__ == 'Tuple' or type(node).__name__ == 'List': 00559 for n in node.elts: 00560 self.buildNameList(n, lst) 00561 elif type(node).__name__ == 'Name': 00562 lst.append(node.id) 00563 else: 00564 self.comment('Unhandled type in buildNameList: ' + type(node).__name__) 00565 #================== Slice expressions ===================== 00566 def visit_Slice(self, node): 00567 self.comment('Slice') 00568 self.indent() 00569 x = 0 00570 if node.lower: 00571 x |= 1 00572 ast.NodeVisitor.visit(self,node.lower) 00573 if node.upper: 00574 x |= 2 00575 ast.NodeVisitor.visit(self,node.upper) 00576 if node.step: 00577 x |= 4 00578 ast.NodeVisitor.visit(self,node.step) 00579 self.output('BUILDSLICE,' + str(x)) 00580 self.dedent() 00581 00582 def visit_Index(self, node): 00583 self.comment('Index') 00584 self.indent() 00585 ast.NodeVisitor.visit(self,node.value) 00586 self.dedent() 00587 00588 def visit_ExceptHandler(self, node): 00589 self.comment('ExceptHandler') 00590 self.indent() 00591 opArgs = '' 00592 if node.type: 00593 if type(node.type).__name__ == 'Tuple': 00594 for e in node.type.elts: 00595 if type(e).__name__ == 'Name': 00596 opArgs += ',' + e.id 00597 else: 00598 self.comment('Unhandled exception type in tuple: ' + type(node.type).__name__) 00599 elif type(node.type).__name__ == 'Name': 00600 opArgs += ',' + node.type.id 00601 else: 00602 self.comment('Unhandled exception type: ' + type(node.type).__name__) 00603 self.output('') 00604 #Save index of placeholder 00605 plcHolder = len(self.tokens) - 1 00606 plcIndex = self.opnum 00607 00608 if node.name: 00609 self.output('GETEXC') 00610 if type(node.name).__name__ == 'Name': 00611 self.output('SETLOC,' + node.name.id) 00612 else: 00613 self.comment('Unhandled exception var name type: ' + type(node.name).__name__) 00614 00615 for n in node.body: 00616 ast.NodeVisitor.visit(self,n) 00617 00618 self.update(plcHolder, 'EXCEPT' + opArgs + ',' + str(self.opnum - plcIndex + 1)) 00619 self.dedent() 00620 00621 def main(): 00622 if (len(sys.argv) != 3): 00623 print 'py2un takes 2 parameters source and destination filepath' 00624 sys.exit(2) 00625 translate_file(sys.argv[1], sys.argv[2]) 00626 00627 if __name__ == '__main__': 00628 main()
Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.