kugelblitz / kugelblitz / translator /

from kugelblitz.translator.base import ast, BaseTranslator
from kugelblitz.translator.exceptions import CompileError
from kugelblitz.translator.toplevel import ModuleTranslator, FunctionTranslator
from kugelblitz.translator.expressions import ExprTranslator, BinOpTranslator, BoolOpTranslator, UnaryOpTranslator, CompareTranslator
from kugelblitz.translator.values import NumTranslator, ListTranslator, NameTranslator
from kugelblitz.translator.assignment import AssignTranslator, AugAssignTranslator
from kugelblitz.translator.control import IfTranslator, IfExprTranslator

def wrap_old_translator(func):
    class WrappedTranslator(BaseTranslator):
        def translate(self):
            return func(self.node)
    return WrappedTranslator

def translate(node):
    return get_translator(node).translate()

def get_translator(node):
        return {
            # mod
            ast.Module: ModuleTranslator,
            ast.Expression: None,
            # stmt
            ast.FunctionDef: FunctionTranslator,
            ast.ClassDef: wrap_old_translator(translate_class),
            ast.Return: wrap_old_translator(translate_return),
            ast.Delete: wrap_old_translator(translate_delete),
            ast.Assign: AssignTranslator,
            ast.AugAssign: AugAssignTranslator,
            ast.Print: None,
            ast.For: None,
            ast.While: None,
            ast.If: IfTranslator,
            ast.With: None,
            ast.Raise: wrap_old_translator(translate_raise),
            ast.TryExcept: None,
            ast.TryFinally: None,
            ast.Assert: None,
            ast.Import: None,
            ast.ImportFrom: None,
            ast.Exec: None,
            ast.Global: None,
            ast.Expr: ExprTranslator,
            ast.Break: None,
            ast.Continue: None,
            # expr
            ast.BoolOp: BoolOpTranslator,
            ast.BinOp: BinOpTranslator,
            ast.UnaryOp: UnaryOpTranslator,
            ast.Lambda: wrap_old_translator(translate_lambda),
            ast.IfExp: IfExprTranslator,
            ast.Dict: None,
            #ast.Set: None, Not in 2.6
            ast.ListComp: None,
            #ast.SetComp: None, Not in 2.6
            #ast.DictComp: None, Not in 2.6
            ast.GeneratorExp: None,
            ast.Yield: None,
            ast.Compare: CompareTranslator,
            ast.Call: wrap_old_translator(translate_call),
            ast.Repr: None,
            ast.Num: NumTranslator,
            ast.Str: None,
            ast.Attribute: wrap_old_translator(translate_attribute),
            ast.Subscript: wrap_old_translator(translate_subscript),            
            ast.Name: NameTranslator,
            ast.List: ListTranslator,
            ast.Tuple: ListTranslator,
    except TypeError:
        raise CompileError("No translator available for %s." % node.__class__.__name__)

def translate_body(body, line_separator='\n'):
    s = []
    for node in body:
        if isinstance(node, ast.Pass):
        elif isinstance(node, ast.If):
            s.append('%s;' % translate(node))
    return '\n'.join(s)

def translate_function(node, instance_method=False):
    Translates a function. If self_var is not none, it behaves as
    an instance method.
    # Generate argument definition
    if instance_method:
        args_def = ", ".join([ for arg in node.args.args[1:]])
        return "function (%(args_def)s) { %(body_def)s }" % {
            "args_def": args_def,
            "body_def": translate_body(node.body),
        args_def = ", ".join([ for arg in node.args.args])
        return "var %(name)s = function (%(args_def)s) { %(body_def)s }" % {
            "args_def": args_def,
            "body_def": translate_body(node.body),

def translate_class(node):
    # Is there an __init__?
    functions = {}
    assigns = {}
    classes = {}
    for item in node.body:
        if isinstance(item, ast.FunctionDef):
            functions[] = item
        elif isinstance(item, ast.Assign):
            assert len(item.targets) == 1, "You can only assign to a single item."
            assert isinstance(item.targets[0], ast.Name), "You can only assign to simple names in classes"
            assigns[item.targets[0].id] = item.value

    # Make constructor def
    if "__init__" in functions:
        init_def = translate_function(functions['__init__'], instance_method=True)
        init_def = "function () {}"
    # Make other defs
    body = []
    for aname, anode in sorted(assigns.items()):
        body.append("'%s': %s" % (
    # Make method defs
    for fname, fnode in sorted(functions.items()):
        if fname != "__init__":
            body.append("'%s': %s" % (
                translate_function(fnode, instance_method=True),
    return "var %(name)s = %(init_def)s;\n%(name)s.prototype = { %(method_defs)s }" % {
        'init_def': init_def,
        'method_defs': ",\n".join(body),

def translate_return(node):
    return "return %s" % translate(node.value)

def translate_delete(node):
    return ';\n'.join('delete %s' % translate(n) for n in node.targets)

def translate_lambda(node):
    return "function (%(args_def)s) {\nreturn %(body_def)s\n}" % {
        'args_def': ", ".join([ for arg in node.args.args]),
        'body_def': translate_body([node.body]),

def translate_attribute(node):
    return "%(left)s.%(right)s" % {
        "left": translate(node.value),
        "right": node.attr,

def translate_num(node):
    return str(node.n)

def translate_call(node):
    func = translate(node.func)
    if func == 'isinstance':
        if len(node.args) != 2:
            raise TypeError("isinstance expected 2 arguments, got %s" % len(args))
        s = []
        if isinstance(node.args[1], (ast.List, ast.Tuple)):
            for n in node.args[1].elts:
                s.append("%s instanceof %s" % (
            s.append("%s instanceof %s" % tuple(map(translate, node.args)))
        return " || ".join(s)
    elif func == 'len':
        if len(node.args) != 1:
            raise TypeError("len() takes exactly one argument (%s given)" % len(args))
        return "%s.length" % translate(node.args[0])
    args_def = ", ".join(map(translate, node.args))
    return "%(func)s(%(args_def)s)" % {
        "func": func,
        "args_def": args_def,
def translate_subscript(node):
    if isinstance(node.slice, ast.Index):
        return "%(value)s[%(index)s]" % {
            "value": translate(node.value),
            "index": translate(node.slice.value),
    elif isinstance(node.slice, ast.Slice):
        # Translate the endpoints
        if node.slice.lower:
            if isinstance(node.slice.lower, ast.Num):
                lower = node.slice.lower.n
                raise CompileError("Slice arguments must be numeric.")
            lower = 0
        if node.slice.upper:
            if isinstance(node.slice.upper, ast.Num):
                upper = node.slice.upper.n
                raise CompileError("Slice arguments must be numeric.")
            upper = None
        # Different cases for upper/lower
        if node.slice.upper is not None:
            return "%(value)s.slice(%(lower)s, %(upper)s)" % {
                "value": translate(node.value),
                "lower": lower,
                "upper": upper,
            return "%(value)s.slice(%(lower)s)" % {
                "value": translate(node.value),
                "lower": lower,
        raise CompileError("Unknown slice type %s" % type(node.slice))

def translate_raise(node):
    return "throw"

if __name__ == "__main__":
    import sys
    print get_translator(ast.parse("\n")).translate()