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
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.")
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)
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)
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
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
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)
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)
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)
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)]
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)
def number(num: int) -> str: if isinstance(num, int): return str(num) else: raise errs.TplCompileError( "Cannot compile '{}' to number. ".format(num))
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()
def register(num) -> str: if isinstance(num, int): return "%" + str(num) else: raise errs.TplCompileError( "Cannot compile '{}' to register. ".format(num))
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")
def type_name(self): raise errs.TplCompileError(f"Type '{self}' does not have name.")
def memory_length(self): raise errs.TplCompileError( f"Compile time function '{self.name}' does not occupy memory. ")
def address(num: int) -> str: if isinstance(num, int): return "$" + str(num) else: raise errs.TplCompileError( "Cannot compile '{}' to address. ".format(num))