def combinations(ones, twos):
def array_type(scalar_type, dimension):
def array_element_type(scalar_type, dimension):
    def cpp_name(self):
    def cpp_name(self):
    def value(self):
    def value(self, new_value):
    def value(self):
    def value(self, new_value):
    def cpp_name(self):
    def has_display(self):
    def has_display(self, value):
    def __create(self):
    def length(self):
    def array(self):
    def value(self):
    def value(self, new_data):
    def at(self, x, y):
    def setAt(self, x, y, value):
  def __init__(self, name):
  def __str__(self):
    def add(self, entry):
    def insert(self, name, entry):
    def remove(self, name):
    def __init__(self, parent=None):
    def __setitem__(self, name, value):
    def __getitem__(self, name):
    def __init__(self):
    def __str__(self):
    def uniqueNumber(self):
    def add(self, entry):
    def insert(self, name, entry):
    def remove(self, name):
    def in_parallel_context(self):
    def createScope(self):
    def createParallelScope(self):
    def removeScope(self):
    def currentScope(self):
    def lookup(self, name):
    def __init__(self, msg):
    def __str__(self):
def main():
class UninitializedException(Exception): pass
class NeutralFunction(namedtuple('NeutralFunction', ['name', 'type', 'parameters', 'node'])):
class ArrayElement(namedtuple('ArrayElement', ['name', 'type', 'array', 'cpp_name'])): pass
class Variable(namedtuple('Variable', ['name', 'type'])):
class Window(namedtuple('Window', ['name', 'number'])):
class Array(namedtuple('Array', ['name', 'type', 'size'])):
class EntryExistsError(Exception):
class CompileTimeScope(dict):
class CompileTimeParallelScope(CompileTimeScope):
class Scope(dict):
class SymbolTable(object):
class SymbolTableError(Exception): def combinations(ones, twos):
def array_type(scalar_type, dimension):
def array_element_type(scalar_type, dimension):
    def cpp_name(self):
    def cpp_name(self):
    def value(self):
    def value(self, new_value):
    def value(self):
    def value(self, new_value):
    def cpp_name(self):
    def has_display(self):
    def has_display(self, value):
    def __create(self):
    def length(self):
    def array(self):
    def value(self):
    def value(self, new_data):
    def at(self, x, y):
    def setAt(self, x, y, value):
  def __init__(self, name):
  def __str__(self):
    def add(self, entry):
    def insert(self, name, entry):
    def remove(self, name):
    def __init__(self, parent=None):
    def __setitem__(self, name, value):
    def __getitem__(self, name):
    def __init__(self):
    def __str__(self):
    def uniqueNumber(self):
    def add(self, entry):
    def insert(self, name, entry):
    def remove(self, name):
    def in_parallel_context(self):
    def createScope(self):
    def createParallelScope(self):
    def removeScope(self):
    def currentScope(self):
    def lookup(self, name):
    def __init__(self, msg):
    def __str__(self):
def main():
class UninitializedException(Exception): pass
class NeutralFunction(namedtuple('NeutralFunction', ['name', 'type', 'parameters', 'node'])):
class ArrayElement(namedtuple('ArrayElement', ['name', 'type', 'array', 'cpp_name'])): pass
class Variable(namedtuple('Variable', ['name', 'type'])):
class Window(namedtuple('Window', ['name', 'number'])):
class Array(namedtuple('Array', ['name', 'type', 'size'])):
class EntryExistsError(Exception):
class CompileTimeScope(dict):
class CompileTimeParallelScope(CompileTimeScope):
class Scope(dict):
class SymbolTable(object):
class SymbolTableError(Exception): def indent(code, indent_first_line=True):
    if indent_first_line:
        code = '  ' + code
    return code.replace('\n', '\n  ') def scoped(function):
    def wrapper(self, *args, **kwargs):
        self.symbols.createScope()

        result = function(self, *args, **kwargs)

        self.symbols.removeScope()
        return result

    return wrapper def parallel_scoped(function):
    def wrapper(self, *args, **kwargs):
        self.symbols.createParallelScope()

        result = function(self, *args, **kwargs)

        self.symbols.removeScope()
        return result

    return wrapper def check_is_symbol(name, environment, context=None):
    if not environment.lookup(name):
        raise CompilerException("Error, the symbol '%s' was used but it hasn't been declared yet" % name, context) def check_type(symbol, *requiredTypes):
    if type(symbol) not in requiredTypes:
        if len(requiredTypes) == 1:
            raise CompilerException("Error, the symbol '%s' is a %s, but a symbol of type %s was expected" % \
                    (symbol.name, type(symbol), requiredTypes[0]))
        else:
            raise CompilerException("Error, the symbol '%s' is a %s, but one of %s was expected" % \
                    (symbol.name, type(symbol), requiredTypes)) def collect_elements_from_list(deep_list, element_type):
    collection = []
    for element in deep_list:
        if type(element) == element_type:
            collection.append(element)
        elif isinstance(element, List):
            collection.extend(collect_elements_from_list(element, element_type))

    return collection def list_contains_type(deep_list, variable_type):
    for element in deep_list:
        if type(element) == variable_type:
            return True
        elif isinstance(element, list):
            if list_contains_type(element, variable_type):
                return True

    return False def list_contains_function(deep_list, function_name):
    for function in collect_elements_from_list(deep_list, FunctionCall):
        if function.name == 'random':
            return True

    return False def indent(code, indent_first_line=True):
def c_type_from_chestnut_type(type_name):
def create_data_like_data(name, like_data):
def release_data(type_, name):
def swap_data(first_data, second_data): class CompilerException(Exception):
    def __init__(self, message, node=None):
        self.message = message
        self.node = node

    def __repr__(self):
        return self.__str__()

    def __str__(self):
        if not self.node or not self.node.context:
            return "Error\n  {message}".format(message=self.message)
        elif self.node.context.start_line == self.node.context.end_line:
            return "Error around line {line_num}\n  {message}".format(line_num=self.node.context.start_line,
                                                                            message=self.message)
        else:
            return "Error around lines {start} and {end}\n  {message}".format(start=self.node.context.start_line,
                                                                            end=self.node.context.end_line,
                                                                            message=self.message)
from ..builtins import built_in_functions
from ..symboltable import scalar_types, array_types
from ..symboltable import SymbolTable, Scope, Keyword, ArrayElement, Variable, Window, \
                          Array, ParallelFunction, SequentialFunction, NeutralFunction, DisplayWindow
from ..nodes import *
from ..node_helpers import indent, scoped, parallel_scoped, check_is_symbol, check_type, \
                           collect_elements_from_list, list_contains_type, list_contains_function
from ..conversions import c_type_from_chestnut_type, color_properties, coordinates, array_to_array_element
from ..exceptions import CompilerException
from collections import namedtuple, defaultdict
from jinja2 import Environment, PackageLoader
def rendered(template_name, **kwargs):
    template = jinja.get_template(template_name)
    return template.render(**kwargs)

# special magic that should probably be in WalnutBackend but I can't figure out
# the magic python incantations to put it there without errors
code_generators = dict()
def generates_code_for(node_type):
    def wrapper(f):
        code_generators[node_type] = f
        return f

    return wrapper

function_code_generators = dict()
def generates_code_for_function(function_name):
    def wrapper(f):
        function_code_generators[function_name] = f
        return f

    return wrapper


class WalnutBackend(object):
    class GeneratorException(Exception): pass

    def __init__(self):
        self.symbols = None
        self.code = None

        # This is hacky and will break if WalnutBackend is not a singleton
        jinja.filters['cpp'] = self.code_for
        jinja.filters['cpp_list'] = self.code_for_nodes

    def code_for(self, node):
        try:
            generate = code_generators[node.__class__]
        except AttributeError:
            raise GeneratorException("No generator exists for nodes with type: {0}".format(type(node)))

        return generate(self, node)

    def code_for_nodes(self, nodes):
        return tuple(map(self.code_for, nodes))

    def code_for_function(self, function_node):
        try:
            generate = function_code_generators[function_node.name]
        except AttributeError:
            raise GeneratorException("No generator exists for function with name: {0}".format(function_node.name))

        return generate(self, function_node)

    def compile(self, ast):
        self.symbols = SymbolTable()
        self.code = defaultdict(list)
        self.ast = ast

        for function in built_in_functions:
            self.symbols.add(function)

        return self.code_for(ast)


    ####### Generators #######
    @generates_code_for(Program)
    def Program(self, node):
        functions = []
        statements = []

        # allow globals to be overridden
        self.symbols.createScope()

        for statement in node:
            if statement.__class__ in [SequentialFunctionDeclaration, ParallelFunctionDeclaration]:
                functions.append(self.code_for(statement))
                statements.append("// function '%s' was declared (see above)" % statement.name)
            else:
                statements.append(self.code_for(statement))

        self.symbols.removeScope()

        return rendered('walnut/base.cpp',
                        symbolTable=self.symbols,
                        main_statements=statements,
                        declarations=self.symbols.parallelFunctionDeclarations + self.symbols.sequentialFunctionDeclarations,
                        functions=functions)

    @generates_code_for(VariableDeclaration)
    def VariableDeclaration(self, node):
        self.symbols.add(Variable(node.name, node.type))


        code = '{type} {name};'.format(type=self.code_for(node.type), name=self.code_for(node.name))
        if node.initialization:
            code += ' ' + self.code_for(Statement([Assignment([node.name, node.initialization.expression])]))

        return code


    @generates_code_for(ArrayDeclaration)
    def ArrayDeclaration(self, node):
        self.symbols.add(Array(node.name, node.type, node.size))

        t = jinja.from_string("Array<{{ type }}> {{ name }} = _allocator.arrayWithSize<{{ type }}>({{ dimensions|join(', ') }});")
        code = t.render(type=self.code_for(node.type),
                        name=self.code_for(node.name),
                        dimensions=node.size.children)


        if node.initialization:
            code += ' ' + self.code_for(Statement([Assignment([node.name, node.initialization.expression])]))

        return code

    @generates_code_for(SequentialFunctionDeclaration)
    def SequentialFunctionDeclaration(self, node):
        template_declaration = jinja.from_string("__host__ {{type}} {{name}}({{parameters|cpp_list|join(', ')}});")
        template_definition = jinja.from_string("__host__ {{type}} {{name}}({{parameters|cpp_list|join(', ')}}) {{block|cpp}}")


        self.symbols.add(SequentialFunction(node.name, node.type, node.parameters, node=node))