Shadowrun: Awakened 29 September 2011 - Build 871
py2un.py
Go to the documentation of this file.
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.

GNU Lesser General Public License 3 Sourceforge.net