Example #1
0
def find_closet_func(poly: util.NaiveDict, arg_types: [Type], name: str,
                     is_method: bool, get_type_func, lf) -> (FuncType, object):
    """
    Returns the closet method.

    The closet means the minimum sum of distance.

    :param poly:
    :param arg_types:
    :param name: the simple name of function
    :param is_method:
    :param get_type_func: a function that extract the function type from a unit in poly dict
    :param lf:
    :return: anything that gets from the poly dict.
        usually (method_key_type, (method_id, method_ptr, method_type)) for method,
        (fn_type, (fn_type, fn_ptr)) for function
    """
    param_begin = 1 if is_method else 0
    matched = {}
    for i in range(len(poly)):
        # fn_t = poly.keys[i]
        tup = poly.values[i]
        fn_t: CallableType = get_type_func(poly, i)
        if len(arg_types) == len(fn_t.param_types):
            sum_dt = 0
            match = True
            for j in range(param_begin, len(arg_types)):
                arg = arg_types[j]
                param = fn_t.param_types[j]
                # print(arg, "=====", param, lf)
                # if isinstance(arg, PointerType) and isinstance(arg.base, Generic):
                #     print("zsda")
                if arg.convertible_to(param, lf, weak=False):
                    # print(arg, param, lf, type_distance(param, arg))
                    sum_dt += type_distance(param, arg)
                else:
                    match = False
                    break
            if match:
                if sum_dt in matched:
                    raise errs.TplCompileError(
                        f"Ambiguous call: {function_poly_name(name, arg_types, is_method)}. ",
                        lf)
                matched[sum_dt] = (fn_t, tup)
    min_dt = 65536
    min_tup = None
    for dt in matched:
        if dt < min_dt:
            min_dt = dt
            min_tup = matched[dt]
    if min_tup is None:
        raise errs.TplCompileError(
            f"Cannot resolve call: {function_poly_name(name, arg_types, is_method)}. ",
            lf)
    return min_tup
Example #2
0
def get_class_type(t) -> ClassType:
    if isinstance(t, ClassType):
        return t
    if isinstance(t, GenericClassType):
        return t.base
    if isinstance(t, Generic):
        return t.max_t
    raise errs.TplCompileError("Not a class type.")
Example #3
0
 def check_convertibility(self,
                          left_tar_type,
                          lf,
                          normal=True,
                          weak=True) -> None:
     if not self.convertible_to(left_tar_type, lf, normal=normal,
                                weak=weak):
         raise errs.TplCompileError(
             f"Cannot convert '{self}' to '{left_tar_type}'. ", lf)
Example #4
0
    def compile(self) -> str:
        manager = prod.Manager(self.literals, self.str_lit_pos,
                               self.optimize_level)
        out = prod.TpaOutput(manager, is_global=True)
        ge = en.GlobalEnvironment()
        _init_compile_time_functions(ge, out)

        env = en.MainEnvironment(ge)

        self.root.compile(env, out)

        # call 'main'
        if not env.has_name("main"):
            raise errs.TplCompileError(
                "Trash program without function 'main' cannot compile as executable. "
            )
        main_fp = env.get_type("main", tl.LF_COMPILER)
        if not isinstance(main_fp, typ.FunctionPlacer):
            raise errs.TplCompileError("Main must be a function")
        main_fn, main_addr = main_fp.get_only()
        if len(main_fn.param_types) == 0:
            out.generate(self.main_path)
        elif len(main_fn.param_types) == 1:
            if main_fn.param_types[0] == typ.TYPE_VOID:
                out.generate(self.main_path)
            elif main_fn.param_types[0] == typ.TYPE_STRING_ARR:
                out.generate(self.main_path, True)
            else:
                raise errs.TplCompileError(MAIN_FN_ERR_MSG)
        else:
            raise errs.TplCompileError(MAIN_FN_ERR_MSG)
        if main_fn.rtype != typ.TYPE_INT and main_fn.rtype != typ.TYPE_VOID:
            raise errs.TplCompileError(MAIN_FN_ERR_MSG)

        res = out.result()
        return "\n".join(res)
Example #5
0
    def process_export(self, parent: tl.CollectiveElement, index: int, builder: ab.AstBuilder, lfp: tl.LineFilePos):
        index += 1
        ele = parent[index]
        if tl.is_brace(ele):
            block = self.parse_as_block(ele)
            builder.add_node(ast.ExportStmt(block, lfp))
        elif isinstance(ele, tl.AtomicElement) and isinstance(ele.atom, tl.IdToken):
            block = ast.BlockStmt(lfp)
            line = ast.Line(lfp)
            line.parts.append(ast.NameNode(ele.atom.identifier, lfp))
            block.lines.append(line)
            builder.add_node(ast.ExportStmt(block, lfp))
        else:
            raise errs.TplCompileError("Invalid export. ", lfp)

        return index
Example #6
0
    def process_for_stmt(self, parent: tl.CollectiveElement, index: int, builder: ab.AstBuilder, lfp: tl.LineFilePos):
        index += 1
        item = parent[index]
        cond_list = tl.CollectiveElement(tl.CE_BRACKET, lfp, None)
        while not (isinstance(item, tl.CollectiveElement) and item.is_brace()):
            cond_list.append(item)
            index += 1
            item = parent[index]

        cond = self.parse_as_block(cond_list)
        body = self.parse_as_block(item)
        if len(cond) == 3:
            builder.add_node(ast.ForStmt(cond[0], cond[1], cond[2], body, lfp))
        elif len(cond) == 1 and len(cond[0]) == 1 and isinstance(cond[0][0], ast.InStmt):
            builder.add_node(ast.ForEachStmt(cond[0][0], body, lfp))
        else:
            raise errs.TplCompileError("For loop title must contains 3 parts. ", lfp)

        return index
Example #7
0
def replace_generic_with_real(t: Type, real_generics: dict, lfp) -> Type:
    if isinstance(t, PointerType):
        return PointerType(
            replace_generic_with_real(t.base, real_generics, lfp))
    elif isinstance(t, ArrayType):
        return ArrayType(
            replace_generic_with_real(t.ele_type, real_generics, lfp))
    elif isinstance(t, Generic):
        # print(t.simple_name(), real_generics)
        if real_generics is not None and t.full_name() in real_generics:
            return real_generics[t.full_name()]
        else:
            # situations in, e.g,
            # lst: *List = new List<Integer>();
            # lst.append(new Integer(42));
            # lst.get(0);
            util.print_warning("Unchecked call.", lfp)
            return t.max_t
    else:
        raise errs.TplCompileError("Unexpected error. ", lfp)
Example #8
0
    def import_one(self, include: tl.Token, result_parent, lf):
        if isinstance(include, tl.IdToken):
            # library import
            file = "{}{}lib{}{}.tp".format(self.pref["tpc_dir"], os.sep,
                                           os.sep, include.identifier)
        elif isinstance(include, tl.StrToken):
            # user import
            file = os.path.join(self.pref["main_dir"], include.value)
        else:
            raise errs.TplCompileError("Invalid include. ", lf)

        if not os.path.exists(file):
            raise errs.TplTokenizeError(f"File '{file}' does not exist. ", lf)

        if file not in self.included_files:
            lexer = tkn.FileTokenizer(file, self.pref["import_lang"])
            tokens = lexer.tokenize()

            self.included_files[
                file] = None  # make 'file' in 'self.included_files'

            module_macros = MacroEnv()
            txt_p = FileTextPreprocessor(tokens, self.pref,
                                         self.included_files, module_macros)
            processed_tks = txt_p.preprocess()

            self.included_files[file] = module_macros

            result_parent.append(
                tl.AtomicElement(tl.IdToken("import", lf), result_parent))
            result_parent.append(
                tl.AtomicElement(tl.StrToken(file, lf), result_parent))
            result_parent.append(processed_tks)

        module_macros = self.included_files[file]
        # print(file, module_macros.export_names)
        for en in module_macros.export_names:
            self.macros.add_macro(en, module_macros.get_macro(en), lf)
Example #9
0
def replace_callee_generic_class(t: Type, caller_class_t: GenericClassType,
                                 lfp) -> Type:
    if isinstance(t, PointerType):
        return PointerType(
            replace_callee_generic_class(t.base, caller_class_t, lfp))
    elif isinstance(t, ArrayType):
        return ArrayType(
            replace_callee_generic_class(t.ele_type, caller_class_t, lfp))
    elif isinstance(t, GenericClassType):
        new_generics = {}
        for key in t.generics:
            value = t.generics[key]
            if is_generic(value):
                replaced = replace_generic_with_real(value,
                                                     caller_class_t.generics,
                                                     lfp)
            else:
                replaced = value
            new_generics[key] = replaced

        return GenericClassType(t.base, new_generics)
    else:
        raise errs.TplCompileError("Unexpected error. ", lfp)
Example #10
0
 def require_regs(self, count):
     if len(self.available_regs) < count:
         raise errs.TplCompileError(
             "Virtual machine does not have enough registers. ")
     return [self.available_regs.pop() for _ in range(count)]
Example #11
0
 def validate_rtype(self, actual_rtype: typ.Type, lfp: tl.LineFilePos):
     if not actual_rtype.convertible_to(self.func_type.rtype, lfp):
         raise errs.TplCompileError(
             "Function '{}' has declared return type '{}', got actual return type '{}'. "
             .format(self.name, self.func_type.rtype, actual_rtype), lfp)
Example #12
0
def number(num: int) -> str:
    if isinstance(num, int):
        return str(num)
    else:
        raise errs.TplCompileError(
            "Cannot compile '{}' to number. ".format(num))
Example #13
0
 def require_reg(self):
     if len(self.available_regs) == 0:
         raise errs.TplCompileError(
             "Virtual machine does not have enough registers. ")
     return self.available_regs.pop()
Example #14
0
def register(num) -> str:
    if isinstance(num, int):
        return "%" + str(num)
    else:
        raise errs.TplCompileError(
            "Cannot compile '{}' to register. ".format(num))
Example #15
0
    def _global_generate(self, main_file_path, main_has_arg):
        # print(self.manager.class_func_order)
        for co in self.manager.class_headers:
            co.compile()

        merged = [
            "version",
            str(util.BYTECODE_VERSION), "bits",
            str(util.VM_BITS), "stack_size",
            str(util.STACK_SIZE), "global_length", "", "literal", "", "classes"
        ]

        class_methods_map = {}

        for co in self.manager.class_headers:
            ct: typ.ClassType = co.class_type
            full_name = util.class_name_with_path(ct.name, ct.file_path)

            class_methods_map[full_name] = co.local_method_full_names

            line = f"class {full_name} ${ct.mro[0].class_ptr}\nmro\n"
            for mro_t in ct.mro[1:]:
                line += "    " + mro_t.full_name() + "\n"
            line += "methods\n"
            # print(ct.method_rank)
            for method_name, base_t in ct.method_rank:
                method_t = ct.methods[method_name][base_t][2]
                poly_name = typ.function_poly_name(
                    util.name_with_path(method_name,
                                        method_t.defined_class.file_path,
                                        method_t.defined_class),
                    method_t.param_types, True)
                line += \
                    f"    {poly_name}\n"

            line += "endclass\n"
            merged.append(line)
        merged.append("")

        compiled_fn_names = set()

        def compile_function(fn_name):
            fo = self.manager.functions_map[fn_name]
            content = fo.compile()
            merged.extend(content)
            compiled_fn_names.add(fn_name)

        for name, t in self.manager.class_func_order:
            if t == 0:  # function
                if name not in compiled_fn_names:
                    compile_function(name)
            else:  # class
                class_methods = class_methods_map[name]
                for method_name in class_methods:
                    if method_name not in compiled_fn_names:
                        compile_function(method_name)

        # process string literals
        # mechanism:
        # 1. simulate 'String' by adding a pointer to class 'String' at <str_pos>
        # 2. simulate 'String.chars' by adding a pointer to literal char array at <str_pos + chars_pos_in_str>
        if len(self.manager.str_lit_pos
               ) != 0 and self.manager.string_class_ptr == 0:
            raise errs.TplCompileError(
                "String literal is not allowed without importing 'lang'.")
        str_class_ptr = util.int_to_bytes(self.manager.string_class_ptr)
        lit_start = util.STACK_SIZE + self.manager.global_length()
        for str_lit in self.manager.str_lit_pos:
            str_pos = self.manager.str_lit_pos[str_lit]
            str_addr = str_pos + lit_start
            char_arr_ptr_pos = str_pos + self.manager.chars_pos_in_str
            self.manager.literal[str_pos: str_pos + util.PTR_LEN] = \
                str_class_ptr
            self.manager.literal[char_arr_ptr_pos: char_arr_ptr_pos + util.PTR_LEN] = \
                util.int_to_bytes(str_addr + self.manager.chars_pos_in_str + util.PTR_LEN)

        merged[merged.index("global_length") + 1] = str(
            self.manager.global_length())
        literal_str = " ".join([str(int(b)) for b in self.manager.literal])
        merged[merged.index("literal") + 1] = literal_str

        self.output = merged + self.output

        if main_has_arg:
            param_types = [typ.TYPE_STRING_ARR]
            self.write_format("main_arg")
        else:
            param_types = []
        self.write_format("aload", "%0", "$1")
        self.write_format("set_ret", "%0")
        self.write_format(
            "call_fn",
            util.name_with_path(
                typ.function_poly_name("main", param_types, False),
                main_file_path, None))
        self.write_format("exit")
Example #16
0
 def type_name(self):
     raise errs.TplCompileError(f"Type '{self}' does not have name.")
Example #17
0
 def memory_length(self):
     raise errs.TplCompileError(
         f"Compile time function '{self.name}' does not occupy memory. ")
Example #18
0
def address(num: int) -> str:
    if isinstance(num, int):
        return "$" + str(num)
    else:
        raise errs.TplCompileError(
            "Cannot compile '{}' to address. ".format(num))