Created
May 11, 2014 19:47
-
-
Save volodymyrsmirnov/81c3a7cbdb297bae411b to your computer and use it in GitHub Desktop.
Proof of concept for Python to JS translator using built-in AST parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var a = 1; | |
var b = a + 1; | |
a += 50; | |
var r = [10, 9, 8, 7]; | |
var c = | |
(function() { | |
var _i, _len, _ref, _results; | |
_results = []; | |
_ref = [1, 5, 10] || []; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
var x = _ref[_i]; | |
if (x >= 10) continue; | |
_results.push(x * 2) | |
} | |
return _results; | |
})(); | |
function sum(a, b, force) { | |
if (force == null) { | |
force = -1; | |
} | |
if (force != -1) { | |
return c; | |
} | |
return a + b; | |
} | |
var t = sum(5, 8); | |
console.log(t); | |
if (((a > 40 && (b * 5) - 10) || 52 in r)) { | |
console.log('oh no'); | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ast | |
source = ast.parse(""" | |
a = 1 | |
b = a + 1 | |
a += 50 | |
r = [10, 9, 8, 7] | |
c = [x * 2 for x in [1, 5, 10] if x >= 10] | |
def sum(a, b, force=-1): | |
if force != -1: | |
return c | |
return a + b | |
t = sum(5, 8) | |
console.log(t) | |
if (a > 40 and (b * 5 - 10) or (52 in r)): | |
console.log("oh no") | |
""") | |
class PJ(ast.NodeVisitor): | |
result = "" | |
current_line = 0 | |
current_level = 0 | |
globals_context = [] | |
BINOP_SYMBOLS = { | |
ast.Add: "+", | |
ast.Sub: "-", | |
ast.Mult: "*", | |
ast.Div: "/", | |
ast.FloorDiv: "//", | |
ast.Mod: "%", | |
ast.LShift: "<<", | |
ast.RShift: ">>", | |
ast.BitOr: "|", | |
ast.BitAnd: "&", | |
ast.BitXor: "^" | |
} | |
BOOLOP_SYMBOLS = { | |
ast.And: "&&", | |
ast.Or: "||" | |
} | |
CMPOP_SYMBOLS = { | |
ast.Eq: "==", | |
ast.Gt: ">", | |
ast.GtE: ">=", | |
ast.In: " in ", | |
ast.Is: "==", | |
ast.IsNot: "!=", | |
ast.Lt: "<", | |
ast.LtE: "<=", | |
ast.NotEq: "!=", | |
} | |
UNARYOP_SYMBOLS = { | |
ast.Invert: "~", | |
ast.Not: "!", | |
ast.UAdd: "+", | |
ast.USub: "-" | |
} | |
TRANSFORM_NAMES = { | |
"None": "undefined", | |
"True": "true", | |
"False": "false" | |
} | |
def __init__(self, isolate=True): | |
self.isolate = isolate | |
def write(self, what): | |
self.result += what | |
def global_variable_exists(self, variable): | |
return any((v for v in self.globals_context if v[0] == variable and v[1] <= self.current_level)) | |
def visit_Attribute(self, node): | |
self.visit(node.value) | |
self.write("." + node.attr) | |
def visit_FunctionDef(self, node): | |
self.write("function " + node.name + "(") | |
for argument in node.args.args: | |
self.visit(argument) | |
if argument != node.args.args[-1]: | |
self.write(",") | |
self.write("){") | |
for idx, default in enumerate(node.args.defaults): | |
variable = node.args.args[(idx + 1) * -1] | |
self.write("if (" + variable.id + " == null){" + variable.id + "=") | |
self.visit(default) | |
self.write(";}") | |
for statement in node.body: | |
self.visit(statement) | |
self.write("}") | |
def visit_Return(self, node): | |
self.write("return ") | |
self.visit(node.value) | |
self.write(";") | |
def visit_Call(self, node): | |
self.visit(node.func) | |
self.write("(") | |
for argument in node.args: | |
self.visit(argument) | |
if argument != node.args[-1]: | |
self.write(",") | |
self.write(")") | |
def visit_GeneratorExp(self, node): | |
self.write(""" | |
(function() {var _i, _len, _ref, _results; | |
_results = []; | |
""") | |
for generator in node.generators: | |
if type(generator) == ast.comprehension: | |
self.write("_ref=") | |
self.visit(generator.iter) | |
self.write("||[];") | |
self.write(""" | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
var """ + generator.target.id + """ = _ref[_i]; | |
""") | |
if len(generator.ifs): | |
for ifgenerator in generator.ifs: | |
self.write("if(") | |
self.visit(ifgenerator) | |
self.write(")continue;") | |
self.write("_results.push("); | |
self.visit(node.elt) | |
self.write(")}"); | |
self.write(""" | |
return _results; | |
})()""") | |
def visit_ListComp(self, node): | |
self.visit_GeneratorExp(node) | |
def visit_IfExp(self, node): | |
self.visit(node.test) | |
self.write("?") | |
self.visit(node.body) | |
self.write(":") | |
self.visit(node.orelse) | |
def visit_If(self, node): | |
self.current_level += 1 | |
self.write("if(") | |
self.visit(node.test) | |
self.write("){") | |
for statement in node.body: | |
self.visit(statement) | |
self.write("}") | |
self.current_level -= 1 | |
if len(node.orelse): | |
else_statements = [] | |
for subnode in node.orelse: | |
if isinstance(subnode, ast.If): | |
self.write("else ") | |
self.visit_If(subnode) | |
else: | |
else_statements.append(subnode) | |
if len(else_statements): | |
self.current_level += 1 | |
self.write("else{") | |
for statement in else_statements: | |
self.visit(statement) | |
self.write("}") | |
self.current_level -= 1 | |
def visit_Name(self, node): | |
self.write(self.TRANSFORM_NAMES.get(node.id, node.id)) | |
def visit_Str(self, node): | |
self.write(repr(node.s)) | |
def visit_Bytes(self, node): | |
self.write(repr(node.s)) | |
def visit_Num(self, node): | |
self.write(repr(node.n)) | |
def visit_Expr(self, node): | |
self.generic_visit(node) | |
self.write(";") | |
def visit_Assign(self, node): | |
for idx, target in enumerate(node.targets): | |
if type(target) in [ast.Tuple, ast.List]: | |
for var_idx, var in enumerate(target.elts): | |
if not self.global_variable_exists(var.id): | |
self.write("var ") | |
self.write("{}=".format(var.id)) | |
self.visit(node.value.elts[var_idx]) | |
self.write(";") | |
self.globals_context.append((var.id, self.current_level)) | |
else: | |
if not self.global_variable_exists(target.id): | |
self.write("var ") | |
self.write("{}=".format(target.id)) | |
self.visit(node.value) | |
self.write(";") | |
self.globals_context.append((target.id, self.current_level)) | |
def visit_AugAssign(self, node): | |
self.visit(node.target) | |
self.write("{}=".format(self.BINOP_SYMBOLS[type(node.op)])) | |
self.visit(node.value) | |
self.write(";") | |
def visit_List(self, node): | |
self.write("[") | |
for value in node.elts: | |
self.visit(value) | |
if value != node.elts[-1]: | |
self.write(",") | |
self.write("]") | |
def visit_BinOp(self, node): | |
if type(node.left) == ast.BinOp: | |
self.write("(") | |
self.visit(node.left) | |
if type(node.left) == ast.BinOp: | |
self.write(")") | |
self.write(self.BINOP_SYMBOLS[type(node.op)]) | |
if type(node.right) == ast.BinOp: | |
self.write("(") | |
self.visit(node.right) | |
if type(node.right) == ast.BinOp: | |
self.write(")") | |
def visit_BoolOp(self, node): | |
self.write("(") | |
for value in node.values: | |
self.visit(value) | |
if value != node.values[-1]: | |
self.write(self.BOOLOP_SYMBOLS[type(node.op)]) | |
self.write(")") | |
def visit_UnaryOp(self, node): | |
self.write("(") | |
self.write(self.UNARYOP_SYMBOLS[type(node.op)]) | |
self.write("(") | |
self.visit(node.operand) | |
self.write("))") | |
def visit_Compare(self, node): | |
for op, right in zip(node.ops, node.comparators): | |
if type(op) == ast.NotIn: | |
self.write("!(") | |
self.visit(node.left) | |
self.write(self.CMPOP_SYMBOLS[ast.In]) | |
self.visit(right) | |
self.write(")") | |
else: | |
self.visit(node.left) | |
self.write(self.CMPOP_SYMBOLS[type(op)]) | |
self.visit(right) | |
def visit_Tuple(self, node): | |
self.visit_List(node) | |
def visit_Module(self, node): | |
if self.isolate: | |
self.write("(function() {") | |
self.generic_visit(node) | |
if self.isolate: | |
self.write("})();") | |
pj = PJ() | |
pj.visit(source) | |
print pj.result |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment