Пример #1
0
def split_typedefs_and_code(toplevel_declarations):
    """Separates type definitions from actual code.
    
    :param toplevel_declarations: declarations made at the toplevel
    :return:                      a tuple (typedefs, code)
    """
    # separate type definitions from code
    typedefs = [t for t in toplevel_declarations
                if isinstance(t, NewTypeStatement)]
    code     = [t for t in toplevel_declarations
                if not (isinstance(t, NewTypeStatement))]

    return typedefs, code

desugar = get_registry_function()

@desugar.register(Module)
def module_desugar(node):
    return desugar(node.body)

@desugar.register(ProgramBody)
def program_body_desugar(node):
    # NOTE: imports should already be in the parse tree by now, so no need to
    #       do anything with import statements.

    typedefs, code = split_typedefs_and_code(node.toplevel_declarations)

    typedefs = [desugar(t) for t in typedefs]
    toplevel_declarations = [desugar(t) for t in code]
Пример #2
0
class HaskellCodeGenerator(CodeGenerator):

    comment = "-- {}".format

    def __init__(self):
        super().__init__("Haskell", "-- {}".format)
        self.runtime = HaskellRuntime()

        # this set contains the names that are known to be constructors so that
        # we do not convert them to lowercase when compiling CoreVariables.
        self.constructor_names = set()

    def funky_type_to_haskell(self, t, adt_names):
        if isinstance(t, FunctionType):
            return "({} -> {})".format(
                self.funky_type_to_haskell(t.input_type, adt_names),
                self.funky_type_to_haskell(t.output_type, adt_names))
        else:
            ident = str(t)
            if ident in adt_names:
                ident = "{}{}".format(self.ADT_PREFIX, ident)
            return ident

    ADT_PREFIX = "ADT"

    def make_adts(self, typedefs):
        adts = CodeSection("algebraic data types")

        adt_names = set()
        for typedef in typedefs:
            adt = typedef.typ
            adt_names.add(adt.type_name)

            # If true, this ADT contains a function type somewhere, and
            # therefore cannot derive Show or Eq.
            has_function = False

            ind = 0
            for i, constructor in enumerate(adt.constructors):

                params = []
                for p in constructor.parameters:
                    pstring = self.funky_type_to_haskell(p, adt_names)
                    if "->" in pstring: has_function = True
                    params.append(pstring)
                params = " ".join(params)

                if i == 0:
                    definition = "data {}{}".format(self.ADT_PREFIX,
                                                    adt.type_name)
                    ind = len(definition) + 1
                    line = "{} = {}{} {}".format(definition, self.ADT_PREFIX,
                                                 constructor.identifier,
                                                 params)
                    adts.emit(line)
                else:
                    line = "| {}{} {}".format(self.ADT_PREFIX,
                                              constructor.identifier, params)
                    adts.emit(line, d=ind)
                self.constructor_names.add(constructor.identifier)

            if not has_function:
                adts.emit("deriving (Show, Eq)", d=ind)

            adts.newline()

        return adts

    def make_used_runtime(self):
        return self.runtime.get_runtime()

    hs_compile = get_registry_function(registered_index=1)  # 1 to skip self

    @hs_compile.register(CoreBind)
    def hs_compile_bind(self, node):
        return "{} = {}".format(node.identifier, self.hs_compile(node.bindee))

    @hs_compile.register(CoreCons)
    def hs_compile_cons(self, node):
        params = " ".join(self.hs_compile(p) for p in node.parameters)
        return "{}{} {}".format(self.ADT_PREFIX, node.constructor, params)

    @hs_compile.register(CoreVariable)
    def hs_compile_variable(self, node):
        ident = node.identifier
        try:
            return self.runtime.runtime_method(ident)
        except KeyError:
            if node.identifier not in self.constructor_names:
                ident = ident.lower()
            else:
                ident = "{}{}".format(self.ADT_PREFIX, ident)
            return ident

    @hs_compile.register(CoreLiteral)
    def hs_compile_literal(self, node):
        if node.inferred_type == String:
            return "\"{}\"".format(node.value)
        else:
            return str(node.value)

    @hs_compile.register(CoreApplication)
    def hs_compile_application(self, node):
        f = self.hs_compile(node.expr)
        return "({})({})".format(f, self.hs_compile(node.arg))

    @hs_compile.register(CoreLambda)
    def hs_compile_lambda(self, node):
        return "\{} -> {}".format(node.param.identifier,
                                  self.hs_compile(node.expr))

    @hs_compile.register(CoreLet)
    def hs_compile_let(self, node):
        binds = "; ".join(self.hs_compile(bind) for bind in node.binds)
        expr = self.hs_compile(node.expr)
        return "let {{ {} }} in {}".format(binds, expr)

    @hs_compile.register(CoreMatch)
    def hs_compile_match(self, node):
        scrutinee = self.hs_compile(node.scrutinee)

        alts = []
        for alt in node.alts:
            compiled_altcon = self.hs_compile(alt.altcon)
            if not alt.expr:
                alts.append("{} -> undefined".format(compiled_altcon))
                continue
            compiled_expr = self.hs_compile(alt.expr)
            alts.append("{} -> {}".format(compiled_altcon, compiled_expr))

        return "case {} of {{ {} }}".format(scrutinee, "; ".join(alts))

    def make_core_section(self, core_tree):
        core_section = CodeSection("core section")
        for bind in core_tree.binds:
            compiled_bind = self.hs_compile(bind)
            core_section.emit(compiled_bind)
        core_section.newline()
        return core_section

    def make_main_section(self, expr):
        main_section = CodeSection("main")
        main_section.emit("main = do")
        main_section.emit("       print ({})".format(self.hs_compile(expr)))
        return main_section

    def do_generate_code(self, core_tree, typedefs):
        """Generates Haskell code from the core tree and type definitions.
        
        :param core_tree: the type-checked core tree from the desugarer
        :param typedefs:  the typedefs from the desugarer
        :return:          the generated Haskell code as a string
        :rtype:           str
        """

        import funky.globals
        funky.globals.USE_UNICODE = False
        funky.globals.USE_COLORS = False

        super().do_generate_code(core_tree, typedefs)

        log.info("Generating {} code...".format(self.lang_name))
        self.program.reset()
        self.runtime.reset()

        header_section = self.code_header()

        log.info("Creating user-defined data structres...")
        adts = self.make_adts(typedefs)
        log.info("Done.")

        log.info("Compiling core tree...")
        core_section = self.make_core_section(core_tree)
        log.info("Done.")

        log.info("Creating main method...")
        main_section = self.make_main_section(core_tree.expr)
        log.info("Done.")

        log.info("Creating used runtime section...")
        used_runtime_section = self.make_used_runtime()
        log.info("Done.")

        for i, section in enumerate([
                header_section, used_runtime_section, adts, core_section,
                main_section
        ]):
            self.program.add_section(section, i)

        log.info("Done generating {} code.".format(self.lang_name))
        return self.program.get_code()
Пример #3
0
class StrictPythonCodeGenerator(CodeGenerator):

    def __init__(self):
        super().__init__("Python", "# {}".format)
        self.runtime = StrictPythonRuntime()

    def make_base_runtime(self):
        runtime_section = CodeSection("base runtime")
        runtime_section.emit(base_runtime)
        runtime_section.newline()
        return runtime_section

    def make_used_runtime(self):
        return self.runtime.get_runtime()

    def make_adts(self, typedefs):
        adts = CodeSection("algebraic data types")
        for typedef in typedefs:
            adt = typedef.typ
            superclass_name = "ADT{}".format(adt.type_name)
            adts.emit("class {}(ADT):".format(superclass_name))
            adts.emit("    pass")
            adts.newline()

            for constructor in adt.constructors:
                constructor_name = "ADT{}".format(constructor.identifier)
                adts.emit("class {}({}):".format(constructor_name,
                                                 superclass_name))
                varnames = ["v{}".format(i)
                            for i, _ in enumerate(constructor.parameters)]

                adts.newline()
                if varnames:
                    adts.emit("    def __init__(self, {}):".format(", ".join(varnames)))
                    adts.emit("        super().__init__([{}])".format(", ".join(v for v in varnames)))
                else:
                    adts.emit("    def __init__(self):".format(", ".join(varnames)))
                    adts.emit("        super().__init__([])")

                adts.newline()

                s = ""
                for var in varnames:
                    s += "lambda {}: ".format(var)

                if constructor.identifier in kwlist:
                    py_name = "__{}".format(constructor.identifier)
                else:
                    py_name = constructor.identifier
                adts.emit("{} = {}{}({})".format(py_name,
                                                 s,
                                                 constructor_name,
                                                 ", ".join(varnames)))
                adts.newline()

        return adts

    py_compile = get_registry_function(registered_index=1) # 1 to skip self

    @py_compile.register(CoreBind)
    def py_compile_bind(self, node, sect, indent):
        if isinstance(node.bindee, CoreLambda):
            lam = node.bindee
            sect.emit("def {}({}):".format(node.identifier,
                                           self.py_compile(lam.param, sect, indent)),
                      d=indent)
            return_statement = self.py_compile(lam.expr, sect, indent+4)
            sect.emit("return {}".format(return_statement), d=indent+4)
            sect.newline()
        else:
            # if it's not a function bind, it must be a value bind.
            val = node.bindee
            sect.emit("{} = {}".format(node.identifier,
                                       self.py_compile(val, sect, indent)),
                      d=indent)

    @py_compile.register(CoreCons)
    def py_compile_cons(self, node, sect, indent):
        return "ADT{}".format(node.constructor)

    @py_compile.register(CoreVariable)
    def py_compile_variable(self, node, sect, indent):
        try:
            return self.runtime.runtime_method(node.identifier)
        except KeyError:
            if node.identifier in kwlist:
                return "__{}".format(node.identifier)
            return node.identifier

    @py_compile.register(CoreLiteral)
    def py_compile_literal(self, node, sect, indent):
        if node.inferred_type == String:
            return "\"{}\"".format(node.value)
        else:
            return str(node.value)

    @py_compile.register(CoreApplication)
    def py_compile_application(self, node, sect, indent):
        f = self.py_compile(node.expr, sect, indent)
        return "({})({})".format(f, self.py_compile(node.arg, sect, indent))

    @py_compile.register(CoreLambda)
    def py_compile_lambda(self, node, sect, indent):
        param = self.py_compile(node.param, sect, indent)
        differentiator = str(global_counter())
        lam_name = "lam{}".format(differentiator)
        sect.emit("def {}({}):".format(lam_name, param), d=indent)
        expr = self.py_compile(node.expr, sect, indent + 4)
        sect.emit("return {}".format(expr), d=indent+4)
        return lam_name

    @py_compile.register(CoreLet)
    def py_compile_let(self, node, sect, indent):
        for bind in node.binds:
            self.py_compile(bind, sect, indent)
        return self.py_compile(node.expr, sect, indent)

    @py_compile.register(CoreMatch)
    def py_compile_match(self, node, sect, indent):
        scrutinee = self.py_compile(node.scrutinee, sect, indent)
        d = {}
        default = None
        for i, alt in enumerate(node.alts):
            if not alt.expr: continue
            fname = "m{}".format(i)

            if isinstance(alt.altcon, CoreCons):
                params = []
                for i, v in enumerate(alt.altcon.parameters):
                    name = v.identifier
                    if name == "_":
                        name += str(i)
                    params.append(name)
                sect.emit("def {}({}):".format(fname, ", ".join(params)),
                          d=indent)
            else:
                sect.emit("def {}():".format(fname), d=indent)
                if isinstance(alt.altcon, CoreVariable) and \
                   alt.altcon.identifier != "_":
                    sect.emit("{} = {}".format(alt.altcon.identifier, scrutinee),
                              d=indent+4)

            k = self.py_compile(alt.altcon, sect, indent+4)
            v = self.py_compile(alt.expr, sect, indent=indent+4)

            if isinstance(alt.altcon, CoreVariable):
                default = fname
            else:
                d[k] = fname
            sect.emit("return {}".format(v), d=indent+4)

        return "__match({}, {{{}}}, {})".format(
            scrutinee,
            ", ".join("{} : {}".format(k, v) for k, v in d.items()),
            default,
        )

    def make_main(self, main):
        main_section = CodeSection("main method")

        main_section.emit("def result():")
        main_section.emit("return {}".format(main), d=4)
        main_section.emit("")

        main_section.emit("def main():")

        # are we in the REPL?  if so, we want to show the representation of an
        # object.  this has the nice benefit of wrapping it in quotes, etc, if
        # need be
        if funky.globals.CURRENT_MODE == funky.globals.Mode.REPL:
            main_section.emit("print(repr(result()))", d=4)
        else:
            main_section.emit("print(result())", d=4)

        main_section.newline()
        main_section.emit("if __name__ == \"__main__\":")
        main_section.emit("    main()")

        return main_section

    def do_generate_code(self, core_tree, typedefs):
        """Generates Python code from the core tree and type definitions.

        :param core_tree: the type-checked core tree from the desugarer
        :param typedefs:  the typedefs from the desugarer
        :return:          the generated Python code as a string
        :rtype:           str
        """
        super().do_generate_code(core_tree, typedefs)

        log.info("Generating {} code...".format(self.lang_name))
        self.program.reset()
        self.runtime.reset()

        header_section = self.code_header()
        base_runtime_section = self.make_base_runtime()

        log.info("Creating user-defined data structres...")
        adts_section = self.make_adts(typedefs)
        log.info("Done.")
        log.info("Compiling core tree...")
        core_section = CodeSection("core code")
        main = self.py_compile(core_tree, core_section,  0)
        log.info("Done.")

        log.info("Creating main method...")
        main_section = self.make_main(main)
        log.info("Done.")

        log.info("Creating used runtime section...")
        used_runtime_section = self.make_used_runtime()
        log.info("Done.")

        for i, section in enumerate([header_section, base_runtime_section,
                                     used_runtime_section, adts_section,
                                     core_section, main_section]):
            self.program.add_section(section, i)

        log.info("Done generating {} code.".format(self.lang_name))
        return self.program.get_code()
Пример #4
0
from funky.util import get_registry_function, global_counter

from funky.rename import FunkyRenamingError

log = logging.getLogger(__name__)

get_unique_varname = lambda: "v" + str(global_counter())
MAIN = "_main"


def get_parameter_name(*args):
    """Gets an underscore-separated parameter name."""
    return "_".join(str(a) for a in args if a is not None)


rename = get_registry_function()


@rename.register(Module)
def module_rename(node, scope=Scope()):
    rename(node.body, scope)


@rename.register(ProgramBody)
def program_body_rename(node, scope):
    for decl in node.toplevel_declarations:
        rename(decl, scope)


@rename.register(NewTypeStatement)
def new_cons_statement_rename(node, scope):
Пример #5
0
"""Type inference."""

import logging
from funky.infer import FunkyTypeError
from funky.util import get_registry_function, flatten
from funky.ds import Graph
from funky.corelang.coretree import *
from funky.corelang.builtins import *
from funky.corelang.types import *

from funky.infer.tarjan import reorder_bindings

log = logging.getLogger(__name__)

infer = get_registry_function()

@infer.register(CoreVariable)
def infer_variable(node, ctx, non_generic):
    """Infer the type for a core variable. If there is no such variable in the
    current context, we raise an exception. Otherwise, we produce a copy of the
    type expression with get_fresh().
    """
    try:
        node.inferred_type = get_fresh(ctx[node.identifier], non_generic)
    except KeyError:
        raise FunkyTypeError("Undefined symbol '{}'.".format(node.identifier))

@infer.register(CoreLiteral)
def infer_literal(node, ctx, non_generic):
    """Infer the type for a core literal. Simply use the mapping from Python
    types to function types to infer the type of the literal.
Пример #6
0
    def __init__(self, altcon, expr):
        super().__init__()
        self.altcon   =  altcon
        self.expr     =  expr

    def pprint(self, indent=0):
        if not self.expr:
            expr_str = cyellow(sunderline(sbold("undefined")))
        else:
            expr_str = self.expr.pprint(indent=indent)

        return "{} {} {}".format(self.altcon.pprint(indent=indent),
                                 COLOR_OPERATOR(chars.C_RIGHTARROW),
                                 expr_str)

add_edges = get_registry_function()

@add_edges.register(CoreVariable)
def add_edges_variable(bindee, graph, current, ids):
    # We are only concerned about dependencies in our isolated set of bindings
    # -- ignore anything that we're not looking for.
    if bindee.identifier in ids:
        graph.add(current, bindee.identifier)

@add_edges.register(CoreBind)
def add_edges_bind(bindee, graph, current, ids):
    add_edges(bindee.bindee, graph, current, ids)

@add_edges.register(CoreCons)
def add_edges_cons(bindee, graph, current, ids):
    for param in bindee.parameters: