# decompyle3 version 3.3.2
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.8.1 (default, Jan  3 2020, 22:44:00) 
# [GCC 8.3.0]
# Embedded file name: ulang\codegen\ulgen.py
# Size of source mod 2**32: 82682 bytes
"""
    ulgen
    ~~~~~

    Extension to ast that allow ast -> ulang code generation.
"""
from ast import *
BINOP_SYMBOLS = {}
BINOP_SYMBOLS[Add] = '+'
BINOP_SYMBOLS[Sub] = '-'
BINOP_SYMBOLS[Mult] = '*'
BINOP_SYMBOLS[Div] = '/'
BINOP_SYMBOLS[Mod] = '%'
BINOP_SYMBOLS[Pow] = '^'
BINOP_SYMBOLS[LShift] = '<<'
BINOP_SYMBOLS[RShift] = '>>'
BINOP_SYMBOLS[BitOr] = '|'
BINOP_SYMBOLS[BitXor] = '@'
BINOP_SYMBOLS[BitAnd] = '&'
BINOP_SYMBOLS[FloorDiv] = '/'
BOOLOP_SYMBOLS = {}
BOOLOP_SYMBOLS[And] = 'and'
BOOLOP_SYMBOLS[Or] = 'or'
CMPOP_SYMBOLS = {}
CMPOP_SYMBOLS[Eq] = '=='
CMPOP_SYMBOLS[NotEq] = '!='
CMPOP_SYMBOLS[Lt] = '<'
CMPOP_SYMBOLS[LtE] = '<='
CMPOP_SYMBOLS[Gt] = '>'
CMPOP_SYMBOLS[GtE] = '>='
CMPOP_SYMBOLS[Is] = '==='
CMPOP_SYMBOLS[IsNot] = '!=='
CMPOP_SYMBOLS[In] = 'in'
CMPOP_SYMBOLS[NotIn] = 'not in'
UNARYOP_SYMBOLS = {}
UNARYOP_SYMBOLS[Invert] = '~'
UNARYOP_SYMBOLS[Not] = '!'
UNARYOP_SYMBOLS[UAdd] = '+'
UNARYOP_SYMBOLS[USub] = '-'

def to_source(node, indent_with='  '):
    """ 
    This function can covert a node tree back into ulang source code.
    This is useful for implementing a simple python to ulang tool.
    """
    header = '/* This file is auto-generated by the command `ulang -s `. */\n'
    generator = ULangGenerator(indent_with, header=header)
    generator.visit(node)
    return ''.join(generator.result)


class ULangGenerator(NodeVisitor):
    """
    This visitor is able to transform a well formed syntax tree into
    the equivalent ulang source code.
    """

    def __init__(self, indent_with, add_line_info=False, header=None):
        self.result = []
        self.indent_with = indent_with
        self.add_line_info = add_line_info
        self.indentation = 0
        self.new_lines = 0
        self.classes = []
        if header is not None:
            self.result.append(header)

    def write(self, x):
        assert isinstance(x, str)
        if self.new_lines:
            if self.result:
                self.result.append('\n' * self.new_lines)
            self.result.append(self.indent_with * self.indentation)
            self.new_lines = 0
        self.result.append(x)

    def newline(self, node=None, extra=0):
        self.new_lines = max(self.new_lines, 1 + extra)
        if node is not None:
            if self.add_line_info:
                self.write('// line: %s' % node.lineno)
                self.new_lines = 1

    def body(self, statements):
        self.write(' {')
        self.indentation += 1
        for stmt in statements:
            self.visit(stmt)

        self.indentation -= 1
        self.newline()
        self.write('}')

    def class_body(self, statements):
        self.write(' {')
        self.indentation += 1
        stmts = []
        for stmt in statements:
            if isinstance(stmt, FunctionDef) or isinstance(stmt, ClassDef):
                if stmts:
                    self.newline()
                    self.body(stmts)
                    stmts = []
                self.visit(stmt)
            else:
                stmts.append(stmt)

        if stmts:
            self.newline()
            self.body(stmts)
        self.indentation -= 1
        self.newline()
        self.write('}')

    def signature(self, node):
        want_comma = []

        def write_comma():
            if want_comma:
                self.write(', ')
            else:
                want_comma.append(True)

        padding = [None] * (len(node.args) - len(node.defaults))
        for arg, default in zip(node.args, padding + node.defaults):
            write_comma()
            self.visit(arg)
            if default is not None:
                self.write('=')
                self.visit(default)

        if node.vararg is not None:
            write_comma()
            self.write('*' + node.vararg)
        if node.kwarg is not None:
            write_comma()
            self.write('**' + node.kwarg)

    def visit_Assert(self, node):
        self.newline(node)
        self.write('assert (')
        self.visit(node.test)
        if node.msg is not None:
            self.write(', ')
            self.visit(node.msg)

    def visit_Assign(self, node):
        self.newline(node)
        for idx, target in enumerate(node.targets):
            if idx > 0:
                self.write(', ')
            self.visit(target)

        self.write(' = ')
        self.visit(node.value)

    def visit_AugAssign(self, node):
        self.newline(node)
        self.visit(node.target)
        self.write(' ' + BINOP_SYMBOLS[type(node.op)] + '= ')
        self.visit(node.value)

    def visit_ImportFrom(self, node):
        self.newline(node)
        self.write('using ')
        for idx, item in enumerate(node.names):
            if idx:
                self.write(', ')
            self.write(item.name)

        if node.module:
            self.write(' in %s%s' % ('.' * node.level, node.module))
        else:
            self.write(' in %s' % ('.' * node.level))
        for item in node.names:
            self.visit(item)

    def visit_Import(self, node):
        self.newline(node)
        self.write('using ')
        for idx, item in enumerate(node.names):
            if idx:
                self.write(', ')
            self.write(item.name)

        for item in node.names:
            self.visit(item)

    def visit_Expr(self, node):
        self.newline(node)
        self.generic_visit(node)

    def visit_FunctionDef(self, node):
        self.newline(extra=1)
        self.newline(node)
        is_getter = False
        is_setter = False
        if node.decorator_list:
            for deco in decorator_list:
                if isinstance(deco, Name) and deco.id == 'property':
                    is_getter = True
                elif isinstance(deco, Attribute) and deco.attr == 'setter':
                    is_setter = True

        if is_getter:
            assert not is_setter
        if len(self.classes) > 0:
            if len(node.args.args) > 0:
                if node.args.args[0].arg == 'self':
                    node.args.args = node.args.args[1:]
                    if node.name == '__init__':
                        node.name = self.classes[(-1)]
                    node.name = '$' + node.name
        elif is_getter or is_setter:
            self.write('attr ')
        else:
            self.write('func ')
        self.write('%s%s(' % (node.name, ' = ' if is_setter else ''))
        self.visit(node.args)
        self.write(')')
        self.body(node.body)

    def visit_ClassDef(self, node):
        self.classes.append(node.name)
        self.newline(extra=2)
        self.newline(node)
        self.write('type %s' % node.name)
        for base in node.bases:
            if base != node.bases[0]:
                self.write(', ')
            else:
                self.write(' : ')
            self.visit(base)

        self.class_body(node.body)
        self.classes = self.classes[:-2]

    def visit_If(self, node):
        self.newline(node)
        self.write('if ')
        self.visit(node.test)
        self.body(node.body)
        while True:
            orelse = node.orelse
            if len(orelse) == 0:
                break
            elif len(orelse) == 1 and isinstance(orelse[0], If):
                node = orelse[0]
                self.write(' elif ')
                self.visit(node.test)
                self.body(node.body)
            else:
                self.write(' else')
                self.body(orelse)
                break

    def visit_For(self, node):
        self.newline(node)
        self.write('for ')
        self.visit(node.target)
        self.write(' in ')
        self.visit(node.iter)
        self.body(node.body)

    def visit_While(self, node):
        self.newline(node)
        self.write('while ')
        self.visit(node.test)
        self.body(node.body)

    def visit_With(self, node):
        for withitem in node.items:
            self.newline(node.items)
            self.write('try ')
            self.visit(withitem)
            if withitem != node.items[0]:
                self.write(' {')

        self.body(node.body)
        for i in range(len(node.items) - 1):
            self.newline()
            self.write('}')

    def visit_Pass(self, node):
        pass

    def visit_Delete(self, node):
        self.newline(node)
        self.write('unset ')
        for expr in node.targets:
            if expr != node.targets[0]:
                self.write(', ')
            self.visit(expr)

    def visit_Tuple(self, node):
        if isinstance(node.ctx, Load):
            self.write('tuple(')
        for idx, elt in enumerate(node.elts):
            if idx:
                self.write(', ')
            self.visit(elt)

        if isinstance(node.ctx, Load):
            self.write(')')

    def visit_Try(self, node):
        self.newline(node)
        self.write('try')
        self.body(node.body)
        for handler in node.handlers:
            self.visit(handler)

        if node.finalbody:
            self.newline(node)
            self.write('finally')
            self.body(node.finalbody)

    def visit_Global(self, node):
        self.newline(node)
        self.write('extern ' + ', '.join(node.names))

    def visit_Nonlocal(self, node):
        self.newline(node)
        self.write('nonlocal ' + ', '.join(node.names))

    def visit_Return(self, node):
        self.newline(node)
        self.write('return')
        if node.value is not None:
            self.write(' ')
            self.visit(node.value)

    def visit_Break(self, node):
        self.newline(node)
        self.write('break')

    def visit_Continue(self, node):
        self.newline(node)
        self.write('continue')

    def visit_Raise(self, node):
        self.newline(node)
        self.write('throw')
        if node.exc is not None:
            self.write(' ')
            self.visit(node.exc)

    def visit_Attribute(self, node):
        if isinstance(node.value, Name) and node.value.id == 'self':
            self.write('$%s' % node.attr)
        elif isinstance(node.value, Call) and isinstance(node.value.func, Name) and node.value.func.id == 'super':
            self.write('super')
            if node.attr != '__init__':
                self.write('.' + node.attr)
        else:
            self.visit(node.value)
            self.write('.' + node.attr)

    def visit_Call(self, node):
        want_comma = []

        def write_comma():
            if want_comma:
                self.write(', ')
            else:
                want_comma.append(True)

        self.visit(node.func)
        self.write('(')
        for arg in node.args:
            write_comma()
            self.visit(arg)

        for keyword in node.keywords:
            write_comma()
            self.write(keyword.arg + '=')
            self.visit(keyword.value)

        self.write(')')

    def visit_Name(self, node):
        if node.id == 'print':
            self.write('println')
        elif node.id == 'chr':
            self.write('char')
        else:
            self.write(node.id)

    def visit_Str(self, node):
        self.write(repr(node.s))

    def visit_Num(self, node):
        self.write(repr(node.n))

    def sequence_visit(left, right):

        def visit(self, node):
            self.write(left)
            for idx, item in enumerate(node.elts):
                if idx:
                    self.write(', ')
                self.visit(item)

            self.write(right)

        return visit

    visit_List = sequence_visit('[', ']')
    visit_Set = sequence_visit('{', '}')
    del sequence_visit

    def visit_Dict(self, node):
        self.write('{')
        empty = True
        for idx, (key, value) in enumerate(zip(node.keys, node.values)):
            empty = False
            if idx:
                self.write(', ')
            self.visit(key)
            self.write(': ')
            self.visit(value)

        if empty:
            self.write(':')
        self.write('}')

    def visit_BinOp(self, node):
        self.visit(node.left)
        self.write(' %s ' % BINOP_SYMBOLS[type(node.op)])
        self.visit(node.right)

    def visit_BoolOp(self, node):
        self.write('(')
        for idx, value in enumerate(node.values):
            if idx:
                self.write(' %s ' % BOOLOP_SYMBOLS[type(node.op)])
            self.visit(value)

        self.write(')')

    def visit_Compare(self, node):
        self.write('(')
        left = node.left
        emitted = False
        for op, right in zip(node.ops, node.comparators):
            if emitted:
                self.write(' and ')
            else:
                op = CMPOP_SYMBOLS[type(op)]
                if 'in' in op:
                    if 'not' in op:
                        self.write('!')
                    self.visit(right)
                    self.write('.__contains__(')
                    self.visit(left)
                    self.write(')')
                else:
                    self.visit(left)
                    self.write(' %s ' % op)
                    self.visit(right)
            left = right
            emitted = True

        self.write(')')

    def visit_UnaryOp(self, node):
        self.write('(')
        op = UNARYOP_SYMBOLS[type(node.op)]
        self.write(op)
        self.visit(node.operand)
        self.write(')')

    def visit_Subscript(self, node):
        self.visit(node.value)
        self.write('[')
        self.visit(node.slice)
        self.write(']')

    def visit_Slice(self, node):
        if node.lower is not None:
            self.visit(node.lower)
        else:
            self.write(':')
            if node.upper is not None:
                self.visit(node.upper)
            if node.step is not None:
                self.write(':')
                isinstance(node.step, Name) and node.step.id == 'None' or self.visit(node.step)

    def visit_ExtSlice(self, node):
        for idx, item in node.dims:
            if idx:
                self.write(', ')
            self.visit(item)

    def visit_Yield(self, node):
        self.write('yield ')
        self.visit(node.value)

    def visit_Lambda(self, node):
        self.write('(')
        self.visit(node.args)
        self.write(') -> ')
        self.visit(node.body)

    def visit_ComprehensionExp(self, node, init='', append=''):
        self.write('func() { __ = %s; __.%s(' % (init, append))
        self.visit(node.elt)
        self.write(') ')
        for g in node.generators:
            self.visit(g)

        self.write('; return __ }()')

    def visit_ListComp(self, node):
        self.visit_ComprehensionExp(node, '[]', 'append')

    def visit_GeneratorExp(self, node):
        self.write('tuple(')
        self.visit_ComprehensionExp(node, '[]', 'append')
        self.write(')')

    def visit_SetComp(self, node):
        self.visit_ComprehensionExp(node, 'set()', 'add')

    def visit_DictComp(self, node):
        self.write('func() { __ = {;}; __[')
        self.visit(node.key)
        self.write('] = ')
        self.visit(node.value)
        for g in node.generators:
            self.visit(g)

        self.write('; return __ }()')

    def visit_IfExp(self, node):
        self.visit(node.test)
        self.write(' ? ')
        self.visit(node.body)
        self.write(' : ')
        self.visit(node.orelse)

    def visit_Starred(self, node):
        self.write('...')

    def visit_Expr(self, node):
        self.newline()
        if isinstance(node.value, Str):
            self.write('/* %s */' % node.value.s)
        else:
            self.visit(node.value)

    def visit_AnnAssign(self, node):
        self.newline(node)
        self.visit(node.target)
        self.write(' : ')
        self.visit(node.annotation)
        self.write(' = ')
        self.visit(node.value)

    def visit_NameConstant(self, node):
        if node.value == None:
            self.write('nil')
        elif node.value:
            self.write('true')
        else:
            self.write('false')

    def visit_JoinedStr(self, node):
        for id, value in enumerate(node.values):
            if id != 0:
                self.write(' + ')
            self.visit(value)

    def visit_FormattedValue(self, node):
        self.write('str(')
        self.visit(node.value)
        self.write(')')

    def visit_alias(self, node):
        if node.asname is not None:
            self.newline()
            self.write('%s = %s' % (node.asname, node.name))

    def visit_comprehension(self, node):
        if node.ifs:
            for if_ in node.ifs:
                self.write(' if ')
                self.visit(if_)

        self.write(' for ')
        self.visit(node.target)
        self.write(' in ')
        self.visit(node.iter)

    def visit_ExceptHandler(self, node):
        self.newline(node)
        self.write('catch ')
        if node.name is not None:
            self.visit(node.name)
        else:
            self.write('__')
        if node.type is not None:
            self.write(' : ')
            self.visit(node.type)
        self.body(node.body)

    def visit_arguments(self, node):
        self.signature(node)

    def visit_arg(self, node):
        self.write(node.arg)

    def visit_withitem(self, node):
        if node.optional_vars is not None:
            self.visit(node.optional_vars)
            self.write(' = ')
        self.visit(node.context_expr)


def dump(node):
    return to_source(node)