Skip to content

Instantly share code, notes, and snippets.

@volodymyrsmirnov
Created May 11, 2014 19:47
Show Gist options
  • Save volodymyrsmirnov/81c3a7cbdb297bae411b to your computer and use it in GitHub Desktop.
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
(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');
}
})();
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