def build_del_statement(self, del_statement): for expression in del_statement.expressions: if type(expression) is GetAttrExpression: obj = self.build_expression(expression.target) if obj.type == "None": raise CodegenError( "AttributeError: 'NoneType' object has no attribute '{}'." .format(expression.item)) function_name = obj.type + ".__delattr__" self.make_call(function_name, obj, self.build_string(expression.item)) self.memory_manager.try_free_temporary([obj]) elif type(expression) is GetItemExpression: obj = self.build_expression(expression.target) if obj.type == "None": raise CodegenError( "TypeError: 'NoneType' object does not support item deletion." ) function_name = obj.type + ".__delitem__" args = [obj] for arg_ast in expression.args: arg = self.build_expression(arg_ast) args.append(arg) self.make_call(function_name, *args) self.memory_manager.try_free_temporary(args) elif type(expression) is VariableExpression: self.memory_manager.delete_variable(expression.name) else: raise CodegenError("can not delete {}".format( type(expression)))
def get_attribute(self, obj, attr): if isinstance(attr, StringConstant): attr_name = attr.value elif isinstance(attr, str): attr_name = attr else: raise CodegenError( "get attribute from object dynamically is not supported yet.") t = self.compiler.get_type(obj.type) for i, field in enumerate(t.fields): if field.name == attr_name: field_ptr = self.ir_builder.gep(obj.value, [ self.ir_const(0), self.ir_const(i), ]) # 不太对。。取属性的话应该使用复制构造函数弄个新的值出来。。 if self.builtins_builder.is_primitive_type(field.type): field_value = self.ir_builder.load(field_ptr) return self.primitive(field.type, field_value) else: field_ir_type = self.compiler.get_ir_type(field.type) field_value = Slot(field.type, field_ir_type, is_tmp=False) field_value.value = field_ptr # ir.PointerType(field_ir_type)! field_value.ref = None return field_value else: raise CodegenError("type {} has no field named {}".format( obj.type, attr_name))
def build_return_statement(self, return_statement): assert self.ast_function.rtype if return_statement.expression: if self.ast_function.rtype == "None": raise CodegenError( "function {} must not return a value.".format( self.ast_function.qname)) expr = self.build_expression(return_statement.expression) if self.ir_builder.block is not self.memory_manager.variables.block: print(self.ir_builder.block, self.memory_manager.variables.block) raise CodegenError( "checkpoint: variables.block is not the same as self.ir_builder.block." ) self.retvalue_versions.append([expr.value, self.ir_builder.block]) self.ir_builder.branch(self.clean_block) else: if self.ast_function.rtype == "None": if self.retvalue_versions: raise CodegenError( "function {} must not return any value.".format( self.ast_function.qname)) else: self.ir_builder.branch(self.clean_block) else: raise CodegenError("function must return a value.")
def build_if_statement(self, if_statement): if not if_statement.flow: raise CodegenError( "if statement must have one or more than one suite.") orignal_block = self.ir_builder.block endif_block = None branch_versions = [] for condition, suite in if_statement.flow: true_block = self.ir_builder.append_basic_block( name=orignal_block.name + ".if") false_block = self.ir_builder.append_basic_block( name=orignal_block.name + ".else") expr = self.build_expression(condition) converted_value = self.cast(expr, "bool") condition_version = self.memory_manager.variables self.ir_builder.cbranch(converted_value.value, true_block, false_block) self.ir_builder.position_at_end(true_block) self.memory_manager.branch_version(true_block) self.build_suite(suite) if self.ir_builder.block.terminator is None: # 可能里面又有跳转。。所以 self.builder.block 不一定是 true_block if endif_block is None: endif_block = self.ir_builder.append_basic_block( name=orignal_block.name + ".endif") self.ir_builder.branch(endif_block) branch_versions.append(self.memory_manager.variables) self.ir_builder.position_at_end(false_block) self.memory_manager.variables = condition_version # restore orignal version self.memory_manager.branch_version(false_block) if if_statement.otherwise: self.memory_manager.branch_version(self.ir_builder.block) self.build_suite(if_statement.otherwise) if self.ir_builder.block.terminator is None: if endif_block is None: endif_block = self.ir_builder.append_basic_block( name=orignal_block.name + ".endif") self.ir_builder.branch(endif_block) branch_versions.append(self.memory_manager.variables) if endif_block is not None: if not branch_versions: raise CodegenError( "endif block exists while branch_versions is empty.") self.ir_builder.position_at_end(endif_block) self.memory_manager.variables = self.memory_manager.merge_versions( branch_versions) self.memory_manager.variables.block = endif_block else: if branch_versions: raise CodegenError( "endif block not exists while branch_versions is not empty." )
def call_magic_binary_method(self, operator, lhs, rhs): magic_methods = { "+": "__add__", "-": "__sub__", "*": "__mul__", "/": "__truediv__", "//": "__floordiv__", "%": "__mod__", "**": "__pow__", "<<": "__lshift__", ">>": "__rshift__", "|": "__or__", "&": "__and__", "^": "__xor__", "<": "__lt__", "<=": "__le__", "==": "__eq__", "!=": "__ne__", ">": "__gt__", ">=": "__ge__", "in": "__contains__", } reversed_magic_methods = { "+": "__radd__", "-": "__rsub__", "*": "__rmul__", "/": "__rtruediv__", "//": "__rfloordiv__", "%": "__rmod__", "**": "__rpow__", "<<": "__rlshift__", ">>": "__rrshift__", "|": "__ror__", "&": "__rand__", "^": "__rxor__", } try: magic_function_name = magic_methods.get(operator) if not magic_function_name: raise CodegenError( "unsupported operator {} for type `{}`.".format( operator, lhs.type)) function_name = lhs.type + "." + magic_function_name return self.make_call(function_name, lhs, rhs) except FunctionNotFound: magic_function_name = reversed_magic_methods.get(operator) if not magic_function_name: raise CodegenError( "unsupported operator {} for type `{}`.".format(operator)) function_name = rhs.type + "." + magic_function_name try: return self.make_call(function_name, rhs, lhs) except: raise CodegenError("unsupported operator {}".format(operator))
def cast(self, expr, target_type): if expr.type == target_type: return expr try: target = self.make_call(target_type, expr) if target.type == target_type: return target else: raise CodegenError("{}() must return {} type.".format( target_type, target_type)) except FunctionNotFound: raise CodegenError("can not cast {} to {}.".format( expr.type, target_type))
def make_ir_type(self, compiler): if not self.fields: raise CodegenError("type {} has no defined field.".format( self.name)) else: elems = [] for field in self.fields: try: field_type = compiler.types[field.type] except KeyError: raise CodegenError("unknown field type: {}".format( field.type)) elems.append(field_type.ir_type) self.ir_type = ir.LiteralStructType(elems)
def call_magic_unary_method(self, operator, expr): magic_methods = { "~": "__invert__", "-": "__neg__", "+": "__pos__", } try: magic_function_name = magic_methods.get(operator) if not magic_function_name: raise CodegenError("unsupported operator {}".format(operator)) function_name = expr.type + "." + magic_function_name return self.make_call(function_name, expr) except FunctionNotFound: raise CodegenError("unsupported operator {}".format(operator))
def build_suite(self, suite: Suite): for statement in suite.statements: if type(statement) is ExpressionStatement: self.build_expression_statement(statement) elif type(statement) is DelStatement: self.build_del_statement(statement) elif type(statement) is PassStatement: pass elif type(statement) is ReturnStatement: self.build_return_statement(statement) elif type(statement) is BreakStatement: self.build_break_statement(statement) elif type(statement) is ContinueStatement: self.build_continue_statement(statement) elif type(statement) is FunctionDefinationStatement: self.build_function_statement(statement) elif type(statement) is IfStatement: self.build_if_statement(statement) elif type(statement) is WhileStatement: self.build_while_statement(statement) elif type(statement) is ForStatement: self.build_for_statement(statement) else: raise CodegenError("{} statement is not implemented!".format( type(statement)))
def dot_bool(self, x): if x.type == "bool": return x try: return self.function_builder.make_call(x.type + ".__bool__", x) except FunctionNotFound: raise CodegenError("can not cast {} type to bool.".format(x.type))
def create_type(self, name, ir_type): if name in self.types: raise CodegenError("duplicated type.") t = Type(name) if ir_type: # if ir_type is not provided, use make_ir_type() to generate an ir_type later. t.ir_type = ir_type self.types[name] = t return t
def dot_count(self, x, y): if x.type != "str" or y.type != "str": raise CodegenError( "str.count() require a str argument, but given {}".format( y.type)) aggie_str_count = self.native_invoker.aggie_str_count result = aggie_str_count(self.function_builder, x.value, y.value) self.try_free_temporary([x, y]) return self.primitive("int", result)
def dot_str(self, x=None): if x is not None: result = self.function_builder.make_call(x.type + ".__str__", x) if result.type != "str": raise CodegenError("{}.__str__() do not return str.".format( x.type)) return result else: return self.function_builder.build_constant("")
def dot__add__(self, x, y): if x.type != "str" or y.type != "str": raise CodegenError( "str can only concat with another str, not {}".format(y.type)) result_slot = self.function_builder.memory_manager.allocate_slot("str") aggie_str_concat = self.native_invoker.aggie_str_concat aggie_str_concat(self.function_builder, x.value, y.value, result_slot.value) self.try_free_temporary([x, y]) return result_slot
def delete_variable(self, name: str, version: Variables = None): if version is None: version = self.variables old_value = version.get(name) if old_value: old_value.decref(self.function_builder) else: raise CodegenError( "NameError: name '{}' is not defined.".format(name))
def make_call(self, function_name, *args): try: return self.builtins_builder.try_inline(function_name, *args) except CanNotInline: # TODO use memory_manager to find function by name ast_function = self.ast_function callee = None qname = "" while ast_function: qname = make_qname(ast_function.qname, function_name) callee = self.module.functions.get(qname) if callee: break ast_function = ast_function.upper if not callee: raise FunctionNotFound( "function named {} is not found.".format(qname)) ir_function = self.ir_module.globals.get(qname) if not ir_function: raise FunctionNotFound( "function named {} is not compile yet.".format(qname)) if len(callee.ast.args) != len(args): raise FunctionNotFound( "function named {} require {} arguments, but provide {}.". format(len(callee.ast.args), len(args))) arg_values = [] if callee.ast.rtype != "None" and not self.builtins_builder.is_primitive_type( callee.ast.rtype): result_value = self.memory_manager.allocate_slot( callee.ast.rtype, with_ref=True) arg_values.append(result_value.value) else: result_value = None for i, arg in enumerate(args): arg_name, arg_type = callee.ast.args[i] if arg.type != arg_type: raise CodegenError( "given an unmatched argument {} type {} (wanted {}) while calling {}" .format(i, arg.type, arg_type, callee.name)) arg_values.append(arg.value) result_type = callee.ast.rtype t = self.ir_builder.call(ir_function, arg_values, name=callee.name + ".rvalue") self.memory_manager.try_free_temporary(args) if result_type == "None": return NoneValue() elif self.builtins_builder.is_primitive_type(result_type): return self.primitive(result_type, t) else: return result_value
def ir_const(self, value: (bool, int, float)): if value is True: return ir.Constant(ir.IntType(1), 1) elif value is False: return ir.Constant(ir.IntType(1), 0) elif type(value) is int: return ir.Constant(ir.IntType(64), value) elif type(value) is float: return ir.Constant(ir.FloatType(), value) else: raise CodegenError("unknown constant: {}", repr(value))
def dot__mul__(self, x, y): if y.type != "int": raise CodegenError( "duplicate str require an integer times, but given {}.".format( y.type)) aggie_str_duplicate = self.native_invoker.aggie_str_duplicate ptr_result = self.function_builder.memory_manager.allocate_slot("str") aggie_str_duplicate(self.function_builder, x.value, y.value, ptr_result.value) self.try_free_temporary([x, y]) return ptr_result
def dot_startswith(self, x, y): if x.type != "str" or y.type != "str": raise CodegenError( "str.startswith() require a str argument, but given {}.". format(y.type)) aggie_str_startswith = self.native_invoker.aggie_str_startswith result = aggie_str_startswith(self.function_builder, x.value, y.value) b = self.char_to_bool(result, name="str.startswith") self.try_free_temporary([x, y]) return self.primitive("bool", b)
def allocate_slot(self, slot_type: (str, ir.Type), with_ref=True, init_callback=None): slot_name = "slot_" + str(len(self.slots)) ref_name = "ref_" + str(len(self.slots)) if isinstance(slot_type, str): slot = Slot(slot_type, self.function_builder.compiler.get_ir_type(slot_type), is_tmp=True) else: if with_ref: raise CodegenError( "slot with type {} is not managed by memory manager, must set with_ref = False." .format(slot_type)) slot = Slot("", slot_type, is_tmp=True) ir_builder = self.function_builder.ir_builder current_block = ir_builder.block ir_builder.position_at_end(self.function_builder.vars_block) slot.value = ir_builder.alloca(slot.ir_type, name=slot_name) if with_ref: slot.ref = ir_builder.alloca(MemoryManager.ReferenceCountIrType, name=ref_name) else: slot.ref = None if init_callback: try: init_callback(slot) except: raise CodegenError( "call init_callback failed while allocating slot.") ir_builder.position_at_end(current_block) self.slots.append(slot) return slot
def build_expression_statement(self, expression_statement): value = self.build_expression(expression_statement.expression) if not expression_statement.variable: self.memory_manager.try_free_temporary([value]) return if value.type == "None": raise CodegenError("can not assign None to variable {}.".format( expression_statement.variable)) self.memory_manager.assign_variable(expression_statement.variable, value)
def dot__contains__(self, x, y): if x.type != "str" or y.type != "str": raise CodegenError( "contains operation of str require str, not {}".format(y.type)) aggie_str_contains = self.native_invoker.aggie_str_contains bool_result = aggie_str_contains(self.function_builder, x.value, y.value) b = self.ir_builder.icmp_signed("!=", bool_result, self.ir_const(0), name="booltmp") self.try_free_temporary([x, y]) return self.primitive("bool", b)
def build_constant(self, value: (bool, int, float, str, bytes)): result = self.ir_const(value) if value is True or value is False: return self.primitive("bool", result) elif type(value) is int: return self.primitive("int", result) elif type(value) is float: return self.primitive("float", result) elif type(value) is str: return self.build_string(value) elif type(value) is bytes: return self.build_bytes(value) else: raise CodegenError("unknown constant: {}", repr(value))
def rich_compare(self, x, y, operator, name): if x.type != "str" or y.type != "str": raise CodegenError( "str can only compare to another str, but not {}".format( y.type)) aggie_str_compare = self.native_invoker.aggie_str_compare compare_result = aggie_str_compare(self.function_builder, x.value, y.value) b = self.ir_builder.icmp_signed(operator, compare_result, self.ir_const(0), name=name) self.try_free_temporary([x, y]) return "bool",
def bootstrap(self, module_name): # FIXME this is a very simple libc-amd64 version, do more when porting to windows.. ir_main_function_type = ir.FunctionType(ir.IntType(32), []) function = ir.Function(self.ir_module, ir_main_function_type, "main") entry = function.append_basic_block("entry") builder = ir.IRBuilder(entry) qname = module_name + COMPILE_METHOD callee = self.ir_module.globals.get(qname) if not callee: raise CodegenError("can not find main function.") builder.call(callee, []) builder.ret(ir.Constant(ir.IntType(32), 0))
def compile(self, module_name, module_ast_function, replace=True): function = Function(ast=module_ast_function) module = Module(module_name, function) if module_name in self.modules: if replace: module.functions = self.modules[module_name].functions else: raise CodegenError( "module {} has been defined.".format(module_name)) module_builder = ModuleBuilder(module, self) module_builder.build() self.modules[module_name] = module
def assign_variable(self, name: str, value: Value, version: Variables = None): if version is None: version = self.variables old_value = version.get(name) if old_value: if old_value.type != value.type: raise CodegenError( "can not reassign `{}` from `{}` type to `{}` type.". format(name, old_value.type, value.type)) old_value.decref(self.function_builder) version[name] = value value.incref(self.function_builder)
def __call__(self, function_compiler, *args): ir_builder = function_compiler.ir_builder if len(args) != len(self.arguments): raise CodegenError("native function call `{}` called without match arguments. ") arg_values = [] for expr_value, expected_arg_native_type in zip(args, self.arguments): if expected_arg_native_type is aggie_bool: v = ir_builder.zext(expr_value, aggie_bool) arg_values.append(v) else: arg_values.append(expr_value) if not self.f: self.generator_ir_function(function_compiler.compiler.ir_module) return ir_builder.call(self.f, arg_values, name = "{}.rvalue".format(self.name))
def make_ir_function_type(self, ast_function): param_types = [] if self.builtins_builder.is_primitive_type(ast_function.rtype): ir_rtype = self.compiler.get_ir_type(ast_function.rtype) else: ir_rtype = ir.VoidType() if ast_function.rtype != "None": param_types.append( ir.PointerType( self.compiler.get_ir_type(ast_function.rtype))) for arg_name, arg_type in ast_function.args: if arg_type == "None": raise CodegenError("None is not allow for parameters: {}, {}", ast_function.name, arg_name) if self.builtins_builder.is_primitive_type(arg_type): param_types.append(self.compiler.get_ir_type(arg_type)) else: param_types.append( ir.PointerType(self.compiler.get_ir_type(arg_type))) return ir.FunctionType(ir_rtype, param_types)
def dot__getitem__(self, x, y): if y.type == "int": ptr_char = self.function_builder.memory_manager.allocate_slot( "str") aggie_str_at = self.native_invoker.aggie_str_at aggie_str_at(self.function_builder, x.value, y.value, ptr_char) self.try_free_temporary([x]) return ptr_char elif y.type == "slice": ptr_substr = self.function_builder.memory_manager.allocate_slot( "str") aggie_str_mid = self.native_invoker.aggie_str_mid start = self.function_builder.get_attribute(y, "start") end = self.function_builder.get_attribute(y, "end") step = self.function_builder.get_attribute(y, "step") aggie_str_mid(self.function_builder, x.value, start.value, end.value, step.value, ptr_substr) self.try_free_temporary([x, y]) return ptr_substr else: raise CodegenError( "str indices require int or slice, but not {}".format(y.type))