def visit_GeneratorExp(self, node): if not len(node.generators) == 1: raise JSError("Compound generator expressions not supported") if not isinstance(node.generators[0].target, ast.Name): raise JSError("Non-simple targets in generator expressions not supported") return "py_builtins.map(function(%s) {return %s;}, %s)" % (node.generators[0].target.id, self.visit(node.elt), self.visit(node.generators[0].iter))
def visit_ImportFrom(self, node): if node.module == "__future__": if len(node.names) == 1 and node.names[0].name == "division": self.future_division = True else: raise JSError("Unknown import from __future__: %s" % node.names[0].name) else: raise JSError("Import only supports from __future__ import foo") return []
def visit_Num(self, node): if isinstance(node.n, (int, long)): if (0 <= node.n <= 9): return "$c%s" % str(node.n) elif -2**30 < node.n < 2**30: return "int(%s)" % str(node.n) else: raise JSError("Long integer type outside of javascript range") elif isinstance(node.n, float): return "float(%s)" % node.n else: raise JSError("Unknown numeric type: %s" % node.n.__class__.__name__)
def visit_DeleteSimple(self, node): if isinstance(node, ast.Subscript) and isinstance(node.slice, ast.Index): js = "%s.PY$__delitem__(%s);" % (self.visit(node.value), self.visit(node.slice)) elif isinstance(node, ast.Subscript) and isinstance(node.slice, ast.Slice): js = "%s.PY$__delslice__(%s, %s);" % (self.visit(node.value), self.visit(node.slice.lower), self.visit(node.slice.upper)) elif isinstance(node, ast.Attribute): js = '%s.PY$__delattr__("%s");' % (self.visit(node.value), node.attr) elif isinstance(node, ast.Name): raise JSError("Javascript does not support deleting variables. Cannot compile") else: raise JSError("Unsupported delete type: %s" % node) return js
def visit_ListComp(self, node): if not len(node.generators) == 1: raise JSError("Compound list comprehension not supported") if isinstance(node.generators[0].target, ast.Name): prefix = "" name = node.generators[0].target.id elif isinstance(node.generators[0].target, ast.Tuple): name = self.alloc_var() prefix = "".join(["%s = %s.PY$__getitem__(%d); " % (k.id, name, i) for i, k in enumerate(node.generators[0].target.elts)]) else: raise JSError("Non-simple targets in list comprehension not supported") body = self.visit(node.elt) iterexp = self.visit(node.generators[0].iter) return "py_builtins.map(function(%s) {%sreturn %s;}, %s)" % (name, prefix, body, iterexp)
def visit_AssignSimple(self, target, value): if isinstance(target, (ast.Tuple, ast.List)): dummy = self.alloc_var() js = ["var %s = %s;" % (dummy, value)] for i, target in enumerate(target.elts): var = self.visit(target) declare = "" if isinstance(target, ast.Name): if not (var in self._scope): self._scope.append(var) declare = "var " js.append("%s%s = %s.PY$__getitem__(%d);" % (declare, var, dummy, i)) elif isinstance(target, ast.Subscript) and isinstance(target.slice, ast.Index): # found index assignment js = ["%s.PY$__setitem__(%s, %s);" % (self.visit(target.value), self.visit(target.slice), value)] elif isinstance(target, ast.Subscript) and isinstance(target.slice, ast.Slice): # found slice assignmnet js = ["%s.PY$__setslice__(%s, %s, %s);" % (self.visit(target.value), self.visit(target.slice.lower), self.visit(target.slice.upper), value)] else: var = self.visit(target) if isinstance(target, ast.Name): if not (var in self._scope): self._scope.append(var) declare = "var " else: declare = "" js = ["%s%s = %s;" % (declare, var, value)] elif isinstance(target, ast.Attribute): js = ['%s.PY$__setattr__("%s", %s);' % (self.visit(target.value), str(target.attr), value)] else: raise JSError("Unsupported assignment type") return js
def visit_UnaryOp(self, node): if isinstance(node.op, ast.USub ): return "%s.PY$__neg__()" % (self.visit(node.operand)) elif isinstance(node.op, ast.UAdd ): return "%s.PY$__pos__()" % (self.visit(node.operand)) elif isinstance(node.op, ast.Invert): return "%s.PY$__invert__()" % (self.visit(node.operand)) elif isinstance(node.op, ast.Not ): return "py_builtins.__not__(%s)" % (self.visit(node.operand)) else: raise JSError("Unsupported unary op %s" % node.op)
def enter(self, mode): self.modestack.append(mode) if mode == "js": self.visit_current = self.visit_js elif mode == "py": self.visit_current = self.visit_py else: raise JSError("Trying to enter unsupported mode")
def visit_AssignSimple(self, left, right): target = left value = right if isinstance(target, (ast.Tuple, ast.List)): part = self.alloc_var() js = ["var %s = %s;" % (part, value)] for i, target in enumerate(target.elts): var = self.visit(target) declare = "" if isinstance(target, ast.Name): if not (var in self._scope): self._scope.append(var) declare = "var " js.append("%s%s = %s[%d];" % (declare, var, part, i)) elif isinstance(target, ast.Subscript) and isinstance( target.slice, ast.Index): # found index assignment if isinstance(target.slice, ast.Str): i = self.visit(target.slice) else: i = '"%s"' % self.visit(target.slice) js = [ "%s[%s] = %s;" % (self.visit(target.value), self.visit(target.slice), value) ] elif isinstance(target, ast.Subscript) and isinstance( target.slice, ast.Slice): raise JSError("Javascript does not support slice assignments") else: var = self.visit(target) if isinstance(target, ast.Name): if not (var in self._scope): self._scope.append(var) declare = "var " else: declare = "" js = ["%s%s = %s;" % (declare, var, value)] elif isinstance(target, ast.Attribute): js = [ "%s.%s = %s;" % (self.visit(target.value), str(target.attr), value) ] else: raise JSError("Unsupported assignment type") return js
def visit_TryExcept(self, node): if node.orelse: raise JSError("Try-Except with else-clause not supported") js = [] js.append("try {") for n in node.body: js.extend(self.indent(self.visit(n))) err = self.alloc_var() self._exceptions.append(err) js.append("} catch (%s) {" % err) catchall = False for i, n in enumerate(node.handlers): if i > 0: pre = "else " else: pre = "" if n.type: if isinstance(n.type, ast.Name): js.extend(self.indent(["%sif ($PY.isinstance(%s, %s)) {" % (pre, err, self.visit(n.type))])) else: raise JSError("Catching non-simple exceptions not supported") else: catchall = True js.append("%sif (true) {" % (pre)) if n.name: if isinstance(n.name, ast.Name): js.append(self.indent(["var %s = %s;" % (self.visit(n.name), err)])[0]) else: raise JSError("Catching non-simple exceptions not supported") for b in n.body: js.extend(self.indent(self.visit(b))) js.append("}") if not catchall: js.append("else { throw %s; }" % err); js.append("};") self._exceptions.pop() return js
def visit_ClassDef(self, node): js = [] bases = [self.visit(n) for n in node.bases] if not bases: bases = ['object'] if len(bases) == 0: raise JSError("Old-style classes not supported") elif len(bases) > 1: raise JSError("Multiple inheritance not supported") class_name = node.name #self._classes remembers all classes defined self._classes[class_name] = node if len(self._class_name) > 0: js.append("__inherit(%s, \"%s\");" % (bases[0], class_name)) else: js.append("var %s = __inherit(%s, \"%s\");" % (class_name, bases[0], class_name)) self._class_name.append(class_name) heirar = ".PY$".join(self._class_name + []) for stmt in node.body: if isinstance(stmt, ast.Assign): value = self.visit(stmt.value) for t in stmt.targets: var = self.visit(t) js.append("%s.PY$%s = %s;" % (heirar, var, value)) elif isinstance(stmt, ast.FunctionDef): self.heirar = heirar js.append("%s.PY$%s = %s;" % (heirar, stmt.name, "\n".join(self.visit(stmt)))) elif isinstance(stmt, ast.ClassDef): js.append("%s.PY$%s = %s;" % (heirar, stmt.name, "\n".join(self.visit(stmt)))) elif isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Str): js.append("\n".join(["/* %s */" % s for s in stmt.value.s.split("\n")])) elif isinstance(stmt, ast.Pass): # Not required for js pass else: raise JSError("Unsupported class data: %s" % stmt) self._class_name.pop() return js
def visit_Call(self, node): func = self.visit(node.func) if node.keywords: keywords = [] for kw in node.keywords: keywords.append("%s: %s" % (kw.arg, self.visit(kw.value))) keywords = "{" + ", ".join(keywords) + "}" js_args = ", ".join([self.visit(arg) for arg in node.args]) return "%s.args([%s], %s)" % (func, js_args, keywords) else: if node.starargs is not None: raise JSError("star arguments are not supported") if node.kwargs is not None: raise JSError("keyword arguments are not supported") js_args = ", ".join([self.visit(arg) for arg in node.args]) return "%s(%s)" % (func, js_args)
def visit_Raise(self, node): assert node.inst is None assert node.tback is None if not node.type: return ["throw %s;" % self._exceptions[-1]] else: if isinstance(node.type, ast.Name): return ["throw %s();" % self.visit(node.type)] elif isinstance(node.type, ast.Call): return ["throw %s;" % self.visit(node.type)] else: raise JSError("Unknown exception type")
def visit_AugAssign(self, node): target = self.visit(node.target) value = self.visit(node.value) if not self.future_division and isinstance(node.op, ast.Div): node.op = ast.FloorDiv() name = node.op.__class__.__name__ if name in self.ops_augassign: return self.visit_AssignSimple(node.target, "%s.PY$__%s__(%s)" % (target, self.ops_augassign[name], value)) else: raise JSError("Unsupported AugAssign type %s" % node.op)
def visit_comprehension(self, node): if isinstance(node.target, ast.Name): var = self.visit(node.target) elif isinstance(node.target, ast.Tuple): var = self.alloc_var() else: raise JSError("Unsupported target type in list comprehension") iter_var = self.alloc_var() res = "var %s; for (var %s = iter(%s); %s = $PY.next(%s); %s !== null) {\n" % (var, iter_var, self.visit(node.iter), var, iter_var, var) if isinstance(node.target, ast.Tuple): for i, el in enumerate(node.target.elts): if isinstance(el, ast.Name): n = self.visit(el) else: raise JSError("Invalid tuple element in list comprehension") res += "var %s = %s.PY$__getitem__($c%d);\n" % (n, var, i) if node.ifs: ifexp = [] for exp in node.ifs: ifexp.append("bool(%s) === False" % self.visit(exp)) res += "if (%s) { continue; }" % (" || ".join(ifexp)) return res
def visit_Slice(self, node): if node.step: raise JSError("Javascript does not support slicing in steps") if node.lower and node.upper: return ".slice(%s, %s)" % (self.visit( node.lower), self.visit(node.upper)) if node.lower: return "[%s]" % (self.visit(node.lower)) if node.upper: return ".slice(0, %s)" % (self.visit(node.upper)) raise NotImplementedError("Slice")
def visit_BoolOp(self, node): assign = self.stack_destiny(["Assign", "FunctionDef", "Print", "Call"], 1) in ["Assign", "Print", "Call"] if isinstance(node.op, ast.And): op = " && " elif isinstance(node.op, ast.Or): op = " || " else: raise JSError("Unknown boolean operation %s" % node.op) if assign: var = self.alloc_var() return "function() { %s; return %s; }()" % (op.join(["js(%s = %s)" % (var, self.visit(val)) for val in node.values]), var) else: return op.join(["js(%s)" % self.visit(val) for val in node.values])
def visit_BinOp(self, node): left = self.visit(node.left) right = self.visit(node.right) if isinstance(node.op, ast.Mod) and isinstance(node.left, ast.Str): return "%s.PY$__mod__(%s)" % (left, right) if not self.future_division and isinstance(node.op, ast.Div): node.op = ast.FloorDiv() name = node.op.__class__.__name__ if name in self.ops_binop: return "%s.PY$__%s__(%s)" % (left, self.ops_binop[name], right) else: raise JSError("Unknown binary operation type %s" % node.op)
def visit_For(self, node): if not isinstance(node.target, ast.Name): raise JSError( "argument decomposition in 'for' loop is not supported") js = [] for_target = self.visit(node.target) for_iter = self.visit(node.iter) iter_dummy = self.alloc_var() orelse_dummy = self.alloc_var() exc_dummy = self.alloc_var() js.append("var %s = iter(%s);" % (iter_dummy, for_iter)) js.append("var %s = false;" % orelse_dummy) js.append("while (1) {") js.append(" var %s;" % for_target) js.append(" try {") js.append(" %s = %s.next();" % (for_target, iter_dummy)) js.append(" } catch (%s) {" % exc_dummy) js.append( " if (py_builtins.isinstance(%s, py_builtins.StopIteration)) {" % exc_dummy) js.append(" %s = true;" % orelse_dummy) js.append(" break;") js.append(" } else {") js.append(" throw %s;" % exc_dummy) js.append(" }") js.append(" }") for stmt in node.body: js.extend(self.indent(self.visit(stmt))) js.append("}") if node.orelse: js.append("if (%s) {" % orelse_dummy) for stmt in node.orelse: js.extend(self.indent(self.visit(stmt))) js.append("}") return js
def visit_Compare(self, node): assert len(node.ops) == 1 assert len(node.comparators) == 1 op = node.ops[0] comp = node.comparators[0] name = op.__class__.__name__ if name in self.ops_compare: return "%s.PY$__%s__(%s)" % (self.visit(node.left), self.ops_compare[name], self.visit(comp)) elif isinstance(op, ast.In): return "%s.PY$__contains__(%s)" % (self.visit(comp), self.visit(node.left)) elif isinstance(op, ast.Is): return "py_builtins.__is__(%s, %s)" % (self.visit(node.left), self.visit(comp)) elif isinstance(op, ast.NotIn): return "py_builtins.__not__(%s.PY$__contains__(%s))" % (self.visit(comp), self.visit(node.left)) else: raise JSError("Unknown comparison type %s" % node.ops[0])
def visit_FunctionDef(self, node): added = 0 dec = node.decorator_list i = 0 while i < len(dec): if isinstance(dec[i], ast.Call) and isinstance(dec[i].func, ast.Name) and dec[i].func.id == 'JSVar': for a in dec[i].args: if isinstance(a, ast.Str): self.jsvars.append(a.s.split(".")) added += 1 else: raise JSError("JSVar decorator must only be used with string literals") del dec[i] else: i += 1 res = self.visit(node, False) while added: self.jsvars.pop() added -= 1 return res
def visit_For(self, node): if not isinstance(node.target, ast.Name): raise JSError( "argument decomposition in 'for' loop is not supported") js = [] for_target = self.visit(node.target) for_iter = self.visit(node.iter) iter_dummy = self.alloc_var() orelse_dummy = self.alloc_var() exc_dummy = self.alloc_var() if isinstance(node.iter, ast.Call) and isinstance( node.iter.func, ast.Name) and node.iter.func.id == "range" and not node.orelse: counter = self.visit(node.target) end_var = self.alloc_var() assert (len(node.iter.args) in (1, 2, 3)) if len(node.iter.args) == 1: start = "0" end = self.visit(node.iter.args[0]) step = "1" elif len(node.iter.args) == 2: start = self.visit(node.iter.args[0]) end = self.visit(node.iter.args[1]) step = "1" else: start = self.visit(node.iter.args[0]) end = self.visit(node.iter.args[1]) step = self.visit(node.iter.args[2]) js.append("for (%s = %s; %s < %s; %s += %s) {" % (counter, start, counter, end, counter, step)) for stmt in node.body: js.extend(self.indent(self.visit(stmt))) js.append("}") return js js.append("var %s = iter(%s);" % (iter_dummy, for_iter)) js.append("var %s = false;" % orelse_dummy) js.append("while (1) {") js.append(" var %s;" % for_target) js.append(" try {") js.append(" %s = %s.PY$next();" % (for_target, iter_dummy)) js.append(" } catch (%s) {" % exc_dummy) js.append( " if (py_builtins.isinstance(%s, py_builtins.StopIteration)) {" % exc_dummy) js.append(" %s = true;" % orelse_dummy) js.append(" break;") js.append(" } else {") js.append(" throw %s;" % exc_dummy) js.append(" }") js.append(" }") for stmt in node.body: js.extend(self.indent(self.visit(stmt))) js.append("}") if node.orelse: js.append("if (%s) {" % orelse_dummy) for stmt in node.orelse: js.extend(self.indent(self.visit(stmt))) js.append("}") return js
def visit_FunctionDef(self, node): defaults = [None] * (len(node.args.args) - len(node.args.defaults)) + node.args.defaults if node.args.kwarg: kwarg_name = node.args.kwarg else: kwarg_name = "__kwargs" if node.args.vararg: vararg_name = node.args.vararg else: vararg_name = "__varargs" if len(node.args.args) and node.args.args[0].id == "self": offset = 1 else: offset = 0 self._scope = [arg.id for arg in node.args.args] inclass = self.stack_destiny(["ClassDef", "FunctionDef"], 2) in ["ClassDef"] if inclass: js = ["function() {"] else: js = ["var %s = function() {" % (node.name)] if inclass or offset == 1: js.extend(self.indent(["var self = this;"])) newargs = self.alloc_var() js.extend(self.indent(["var %s = __kwargs_get(arguments);" % kwarg_name])) js.extend(self.indent(["var %s = __varargs_get(arguments);" % vararg_name])) js.extend(self.indent(["var %s = Array.prototype.slice.call(arguments).concat(js(%s));" % (newargs, vararg_name)])) for i, arg in enumerate(node.args.args[offset:]): if not isinstance(arg, ast.Name): raise JSError("tuples in argument list are not supported") values = dict(i = i, id = self.visit(arg), kwarg = kwarg_name, newargs = newargs) if defaults[i + offset] == None: js.extend(self.indent(["var %(id)s = ('%(id)s' in %(kwarg)s) ? %(kwarg)s['%(id)s'] : %(newargs)s[%(i)d];" % values])) else: values['default'] = self.visit(defaults[i + offset]) js.extend(self.indent(["var %(id)s = %(newargs)s[%(i)d];" % values])) js.extend(self.indent(["if (%(id)s === undefined) { %(id)s = %(kwarg)s.%(id)s === undefined ? %(default)s : %(kwarg)s.%(id)s; };" % values])) if node.name in ["__getattr__", "__setattr__"]: js.extend(self.indent(["if (typeof %(id)s === 'string') { %(id)s = str(%(id)s); };" % { 'id': node.args.args[1].id }])) if node.args.kwarg: js.extend(self.indent(["%s = dict(%s);" % (node.args.kwarg, node.args.kwarg)])) if node.args.vararg: l = len(node.args.args) if inclass: l -= 1 js.extend(self.indent(["%s = tuple(%s.slice(%s));" % (node.args.vararg, newargs, l)])) for stmt in node.body: js.extend(self.indent(self.visit(stmt))) self._scope = [] if not (node.body and isinstance(node.body[-1], ast.Return)): js.append("return None;") js.append("}") for dec in node.decorator_list: js.extend(["%s.PY$%s = %s(%s.PY$__getattr__('%s'));" % (self.heirar, node.name, dec.id, self.heirar, node.name)]) return js
def visit_For(self, node): if isinstance(node.target, ast.Name): for_target = self.visit(node.target) elif isinstance(node.target, ast.Tuple): for_target = self.alloc_var() else: raise JSError("Advanced for-loop decomposition not supported") js = [] if isinstance(node.iter, ast.Call) and isinstance(node.iter.func, ast.Name) and node.iter.func.id == "range" and not node.orelse: counter = self.visit(node.target) end_var = self.alloc_var() assert(len(node.iter.args) in (1,2,3)) if len(node.iter.args) == 1: start = "$c0" end = self.visit(node.iter.args[0]) step = "$c1" elif len(node.iter.args) == 2: start = self.visit(node.iter.args[0]) end = self.visit(node.iter.args[1]) step = "$c1" else: start = self.visit(node.iter.args[0]) end = self.visit(node.iter.args[1]) step = self.visit(node.iter.args[2]) js.append("%s = %s;" % (end_var, end)) if step <> "$c1": step_var = self.alloc_var() js.append("%s = %s;" % (step_var, step)); else: step_var = step js.append("for (%s = %s; %s.PY$__lt__(%s) == true; %s = %s.PY$__add__(%s)) {" % (counter, start, counter, end_var, counter, counter, step_var)) for stmt in node.body: js.extend(self.indent(self.visit(stmt))) js.append("}") return js for_iter = self.visit(node.iter) iter_dummy = self.alloc_var() exc_dummy = self.alloc_var() js.append("try {") js.append(" var %s;" % for_target) for_init = "var %s = iter(%s)" % (iter_dummy, for_iter) for_iter = "%s = %s.PY$next()" % (for_target, iter_dummy) for_cond = "" js.append(" for (%s; %s; %s) {" % (for_init, for_iter, for_cond)) if isinstance(node.target, ast.Tuple): js.append(" %s;" % "; ".join(["var %s = %s.PY$__getitem__(%s)" % (x.id, for_target, i) for i, x in enumerate(node.target.elts)])) for stmt in node.body: js.extend(self.indent(self.visit(stmt))) js.append(" }") js.append("} catch (%s) {" % exc_dummy) js.append(" if (%s !== $PY.StopIter && !$PY.isinstance(%s, py_builtins.StopIteration))" % (exc_dummy, exc_dummy)) js.append(" throw %s;" % exc_dummy) if node.orelse: js.append(" else {") for stmt in node.orelse: js.extend(self.indent(self.visit(stmt))) js.append(" }") js.append("}") return js
def visit_FunctionDef(self, node): raise JSError( "Javascript compiler does not support function definitions")
def visit_ClassDef(self, node): raise JSError("Javascript compiler does not support class definitions")