#!/usr/bin/env python # . # A Python implementation that uses AST to turn Python into Javascript code. Hurray! # TODO: # Fix constructor names DONE # Add stuff like xrange range map etc # try..except # Properly indent...maybe use a code beautifyer? # Port small bit of python core lib? os, sys, string? # use expando modifyer to do special stuff with __dict__ # vars must be declared!? # escape strings from compiler import ast import compiler import os import string import time import sys import unittest pylib = """ function len(s) { return s.length; } function dict() { var arr = new Array(); for (var i=0;i 1: print "Warning: only supports single inheritance" closechar = "}" + os.linesep current_class = node.name classes.append(node.name) jsbuf = jsbuf + "class " + node.name + " " if len(node.bases) > 0: lst = [] for base in node.bases: lst.append(python2js(base)) jsbuf = jsbuf + "extends " + string.join(lst,",") + " " jsbuf = jsbuf + "{" + os.linesep + python2js(node.code) elif nodetype == ast.Continue: closechar = ";" + os.linesep jsbuf = jsbuf + "continue" elif nodetype == ast.Dict: #print "I can't do dicts, how much does that suck :(" parsechildren = False jsbuf = jsbuf + "dict( " for item in node.items: jsbuf = jsbuf + python2js(item[0]) + "," + python2js(item[1]) + "," jsbuf = jsbuf[:-1] + ")" elif nodetype == ast.Discard: parsechildren = False closechar = ";" + os.linesep jsbuf = jsbuf + python2js(node.expr) elif nodetype == ast.Exec: parsechildren = False closechar = ";" + os.linesep jsbuf = jsbuf + "eval('" + python2js(node.expr).replace("'","\'") + "')" elif nodetype == ast.For: parsechildren = False closechar = "}" + os.linesep tmpvar = getuniquename("foriter") listvar = getuniquename("forlist") jsbuf = jsbuf + listvar + " = " + python2js(node.list) + ";" + os.linesep jsbuf = jsbuf + "for (var " + tmpvar + " = 0; " + tmpvar + " < " + listvar + ".length; " + tmpvar + " += 1) {" + os.linesep jsbuf = jsbuf + python2js(node.assign) + " = " + listvar + "[" + tmpvar + "];" + os.linesep jsbuf = jsbuf + python2js(node.body) elif nodetype == ast.ListCompFor: parsechildren = False tmpvar = getuniquename("foriter") listvar = getuniquename("forlist") jsbuf = jsbuf + listvar + " = " + python2js(node.list) + ";" jsbuf = jsbuf + "for (var " + tmpvar + " = 0; " + tmpvar + " < " + listvar + ".length; " + tmpvar + " += 1) {" jsbuf = jsbuf + python2js(node.assign) + " = " + listvar + "[" + tmpvar + "];" # jsbuf = jsbuf + "for (var " + python2js(node.assign) + " in " + python2js(node.list) + ")\n{ " elif nodetype == ast.ListComp: parsechildren = False closechar = "" jsbuf = jsbuf + "function() { " tmpvar = getuniquename("listcomp") jsbuf += " var " + tmpvar + " = new Array(); " quals = [] for qual in node.quals: quals.insert(0, qual) for qual in quals: jsbuf += python2js(qual) closechar += "} " closechar = tmpvar + ".push(" + python2js(node.expr) + "); " + closechar + " ; return " + tmpvar + "; }()" elif nodetype == ast.Function: parsechildren = False closechar = "}" + os.linesep if node.name == "__init__": if current_class != "": node.name = current_class if current_class != "": # we're in a class, assign the first param to this and remove it from arg list selfarg = node.argnames[0] del node.argnames[0] else: selfarg = None #print "Warning: method on line " + str(node.lineno) + " is a constructor. You must make a change in the JS source..." jsbuf = jsbuf + "function " + node.name + "(" + string.join(node.argnames,",") + ") {" + os.linesep if selfarg != None: jsbuf = jsbuf + selfarg + " = this;" + os.linesep jsbuf = jsbuf + python2js(node.code) #{" + pyt elif nodetype == ast.Getattr: parsechildren = False jsbuf = jsbuf + python2js(node.expr) + "." + node.attrname #print "Getattr is not implemented yet. Perhaps we'll have a JScript object model that implements stuff like __dict__?" elif nodetype == ast.Global: parsechildren = False print "Global is not supported yet. Maybe stick at beginning of file?" elif nodetype == ast.List or nodetype == ast.Tuple: parsechildren = False jsbuf = jsbuf + "new Array(" lst = [] for n in node.nodes: lst.append(python2js(n)) jsbuf = jsbuf + string.join(lst,",") + ")" elif nodetype == ast.Mod: parsechildren = False jsbuf = jsbuf + python2js(node.left) + " % " + python2js(node.right) elif nodetype == ast.LeftShift: parsechildren = False jsbuf = jsbuf + python2js(node.left) + "<<" + python2js(node.right) elif nodetype == ast.RightShift: parsechildren = False jsbuf = jsbuf + python2js(node.left) + ">>" + python2js(node.right) elif nodetype == ast.Mul: parsechildren = False jsbuf = jsbuf + python2js(node.left) + " * " + python2js(node.right) elif nodetype == ast.Pass: jsbuf = jsbuf + "//" + os.linesep elif nodetype == ast.Power: parsechildren = False jsbuf = jsbuf + "pow(" + python2js(node.left) + ", " + python2js(node.right) + ")" elif nodetype == ast.Raise: parsechildren = False print "Warning: raise only supports 1st arg right now" jsbuf = jsbuf + "throw " + python2js(node.expr1) + ";" + os.linesep elif nodetype == ast.Slice: parsechildren = False if node.lower == None: nodelower = 0 else: nodelower = python2js(node.lower) jsbuf = jsbuf + python2js(node.expr) + ".substr(" + str(nodelower) if node.upper != None: jsbuf = jsbuf + "," + python2js(node.upper) jsbuf = jsbuf + ")" elif nodetype == ast.Sub: parsechildren = False jsbuf = jsbuf + python2js(node.left) + " - " + python2js(node.right) elif nodetype == ast.Subscript: parsechildren = False lst = [] for sub in node.subs: lst.append(python2js(sub)) jsbuf = jsbuf + python2js(node.expr) + "[" + string.join(lst,",") + "]" elif nodetype == ast.UnaryAdd: parsechildren = False jsbuf = jsbuf + "+" + python2js(node.expr) elif nodetype == ast.UnarySub: parsechildren = False jsbuf = jsbuf + "-" + python2js(node.expr) elif nodetype == ast.While: parsechildren = False closechar = "}" + os.linesep jsbuf = jsbuf + "while (" + python2js(node.test) + ") {" + os.linesep + python2js(node.body) elif nodetype == ast.Yield: print "Warning: using yield, it wont work right" parsechildren = False closechar = ";" + os.linesep jsbuf = jsbuf + "return " + python2js(node.value) elif nodetype == ast.Return: parsechildren = False closechar = ";" + os.linesep jsbuf = jsbuf + "return " + python2js(node.value) elif nodetype == ast.Lambda: parsechildren = False closechar = ";};" + os.linesep jsbuf = jsbuf + "function(" + string.join(node.argnames,",") + ") {" + python2js(node.code) elif nodetype == ast.AssTuple or nodetype == ast.AssList: parsechildren = False # this is handled by process_assign else: error = True #elif nodetype == if parsechildren: for child in node.getChildNodes(): jsbuf = jsbuf + python2js(child) if jsbuf != "": jsbuf = jsbuf + closechar if nodetype == ast.Class: current_class = "" if error: error = "// Syntax error for " + nodetype.__name__ + ": " + repr(node) + " having " + ",".join(dir(node)) + os.linesep jsbuf += os.linesep + error + os.linesep return jsbuf def compilefile(infile,outfile=""): jssrc = python2js(compiler.parseFile(infile)) jssrc = pylib + jssrc if outfile: f = open(outfile,"w") f.write(jssrc) f.close() else: sys.stdout.write(jssrc) if __name__=="__main__": compilefile(sys.argv[1]) #unittest.main()