Exemplo n.º 1
0
 def visit(self, node:ConditionalNode, scope:Scope):
     cond = self.visit(node.cond, scope)
     if cond.name != 'Bool':
         error_text = TypesError.PREDICATE_ERROR % ('if', 'Bool')
         self.errors.append(TypesError(*node.pos, error_text))
     true_type = self.visit(node.stm, scope)
     false_type = self.visit(node.else_stm, scope)
     return Utils.GetCommonBaseType([false_type, true_type])
Exemplo n.º 2
0
    def visit(self, node: ConditionalNode, scope: Scope):
        cond, _ = self.visit(node.cond, scope)

        true_label = CILLabelNode(f"true__{COUNT}")
        end = CILLabelNode(f"end__{COUNT}")

        local_node = self.generateLocalNode(LOCAL)
        self.saveCilInstruction(CILGotoIfNode(cond, true_label.label))

        false_expr, ftypex = self.visit(node.else_stm, scope)
        self.saveCilInstruction(CILAssignNode(local_node, false_expr))
        self.saveCilInstruction(CILGotoNode(end.label))
        self.saveCilInstruction(true_label)

        true_expr, ttypex = self.visit(node.stm, scope)
        self.saveCilInstruction(CILAssignNode(local_node, true_expr))
        self.saveCilInstruction(end)
        return local_node, Utils.GetCommonBaseType([ttypex, ftypex])
Exemplo n.º 3
0
class TypeChecker:
    def __init__(self, context:Context, errors=[]):
        self.context:Context = context
        self.errors:list = errors
        self.current_type:Type = None
        self.current_method:Method = None

    @visitor.on('node')
    def visit(self, node, scope):
        pass

    @visitor.when(ProgramNode)
    def visit(self, node:ProgramNode, scope:Scope):
        for declaration,new_scope in zip(node.declarations, scope.children):
            self.visit(declaration, new_scope)

    def _get_type(self, ntype:str, pos:tuple):
        try:
            return self.context.get_type(ntype, pos)
        except SemanticError as exception:
            self.errors.append(exception)
            return ErrorType()

    def _get_method(self, typex:Type, name:str, pos:tuple) -> Method:
        try:
            return typex.get_method(name, pos)
        except SemanticError as exception:
            if type(typex) != ErrorType and type(typex) != AutoType:
                error_text = AttributesError.DISPATCH_UNDEFINED % name
                self.errors.append(AttributesError(*pos, error_text))
            return MethodError(name, [], [], ErrorType())

    @visitor.when(ClassDeclarationNode)
    def visit(self, node:ClassDeclarationNode, scope:Scope):
        self.current_type = self.context.get_type(node.id, node.pos)
        func_declaration = [ feature for feature in node.features 
                                if isinstance(feature, FuncDeclarationNode) ]

        for feature in node.features:
            if isinstance(feature, AttrDeclarationNode):
                self.visit(feature, scope)

        for feature,child_scope in zip(func_declaration, scope.functions.values()):
            self.visit(feature, child_scope)

    @visitor.when(AttrDeclarationNode)
    def visit(self, node:AttrDeclarationNode, scope:Scope):
        attr = self.current_type.get_attribute(node.id, node.pos)
        var_type = Utils.GetType(attr.type, self.current_type)
        typex = self.visit(node.expr, scope)

        if not typex.conforms_to(var_type):
            error_text = TypesError.ATTR_TYPE_ERROR % (typex.name, attr.name, var_type.name)
            self.errors.append(TypesError(*node.pos, error_text))
            return ErrorType()
        return typex

    @visitor.when(FuncDeclarationNode)
    def visit(self, node:FuncDeclarationNode, scope:Scope):
        parent = self.current_type.parent
        method = self.current_type.get_method(node.id, node.pos)
        self.current_method = self.current_type.get_method(node.id, node.pos)

        if parent is not None:
            try:
                old_method = parent.get_method(node.id, node.pos)
                if old_method.return_type.name != method.return_type.name:
                    error_text = SemanticError.WRONG_SIGNATURE_RETURN % (node.id, method.return_type.name, old_method.return_type.name)
                    self.errors.append(SemanticError(*node.type_pos, error_text))
                if len(method.param_names) != len(old_method.param_names):
                    error_text = SemanticError.WRONG_NUMBER_PARAM % node.id
                    self.errors.append(SemanticError(*node.pos, error_text))
                for (name,param),type1,type2 in zip(node.params, method.param_types, old_method.param_types):
                    if type1.name != type2.name:
                        error_text = SemanticError.WRONG_SIGNATURE_PARAMETER % (name, type1.name, type2.name)
                        self.errors.append(SemanticError(*param.pos, error_text))
            except SemanticError:
                pass

        result = self.visit(node.body, scope)
        return_type = Utils.GetType(method.return_type, self.current_type)

        if not result.conforms_to(return_type):
            error_text = TypesError.RETURN_TYPE_ERROR % (result.name, return_type.name)
            self.errors.append(TypesError(*node.type_pos, error_text))

    @visitor.when(VarDeclarationNode)
    def visit(self, node:VarDeclarationNode, scope:Scope):
        var_type = self._get_type(node.type, node.type_pos)
        var_type = Utils.GetType(var_type, self.current_type)
        if node.expr != None:
            typex = self.visit(node.expr, scope)
            if not typex.conforms_to(var_type):
                error_text = TypesError.UNCONFORMS_TYPE % (typex.name, node.id, var_type.name)
                self.errors.append(TypesError(*node.type_pos, error_text))
            return typex
        return var_type

    @visitor.when(AssignNode)
    def visit(self, node:AssignNode, scope:Scope):
        var_info = self.find_variable(scope, node.id)
        var_type = Utils.GetType(var_info.type, self.current_type) 
        typex = self.visit(node.expr, scope)
        if not typex.conforms_to(var_type):
            error_text = TypesError.UNCONFORMS_TYPE % (typex.name, node.id, var_type.name)
            self.errors.append(TypesError(*node.pos, error_text))
        return typex

    def check_args(self, method:Method, scope:Scope, args:list, pos:tuple):
        arg_types = [ self.visit(arg, scope) for arg in args ]
        if len(arg_types) > len(method.param_types):
            error_text = SemanticError.ARGUMENT_ERROR % method.name
            self.errors.append(SemanticError(*pos, error_text))
        elif len(arg_types) < len(method.param_types):
            for arg,arg_info in zip(method.param_names[len(arg_types):], args[len(arg_types):]):
                error_text = SemanticError.ARGUMENT_ERROR % (method.name)
                self.errors.append(SemanticError(*arg_info.pos, error_text))
        for arg_type,param_type,param_name in zip(arg_types, method.param_types, method.param_names):
            if not arg_type.conforms_to(param_type):
                error_text = TypesError.INCOSISTENT_ARG_TYPE % (method.name, arg_type.name, param_name, param_type.name)
                self.errors.append(TypesError(*pos, error_text))

    @visitor.when(CallNode)
    def visit(self, node:CallNode, scope:Scope):
        stype = self.visit(node.obj, scope)
        method = self._get_method(stype, node.id, node.pos)
        if not isinstance(method, MethodError):
            self.check_args(method, scope, node.args, node.pos)
        return Utils.GetType(method.return_type, stype)

    @visitor.when(BaseCallNode)
    def visit(self, node:BaseCallNode, scope:Scope):
        obj = self.visit(node.obj, scope)
        typex = self._get_type(node.type, node.type_pos)
        if not obj.conforms_to(typex):
            error_text = TypesError.INCOMPATIBLE_TYPES_DISPATCH % (typex.name, obj.name)
            self.errors.append(TypesError(*node.type_pos, error_text))
            return ErrorType()
        method = self._get_method(typex, node.id, node.pos)
        if not isinstance(method, MethodError):
            self.check_args(method, scope, node.args, node.pos)
        return Utils.GetType(method.return_type, typex)

    @visitor.when(StaticCallNode)
    def visit(self, node:StaticCallNode, scope:Scope):
        typex = self.current_type
        method = self._get_method(typex, node.id, node.pos)
        if not isinstance(method, MethodError):
            self.check_args(method, scope, node.args, node.pos)
        return Utils.GetType(method.return_type, typex)

    @visitor.when(ConstantNumNode)
    def visit(self, node:ConstantNumNode, scope:Scope):
        return IntType(node.pos)

    @visitor.when(ConstantBoolNode)
    def visit(self, node:ConstantBoolNode, scope:Scope):
        return BoolType(node.pos)

    @visitor.when(ConstantStrNode)
    def visit(self, node:ConstantStrNode, scope:Scope):
        return StringType(node.pos)

    @visitor.when(ConstantVoidNode)
    def visit(self, node:ConstantVoidNode, scope:Scope):
        return VoidType(node.pos)

    def find_variable(self, scope, lex):
        var_info = scope.find_local(lex)
        if var_info is None:
            var_info = scope.find_attribute(lex)
        if lex in self.current_type.attributes and var_info is None:
            return VariableInfo(lex, VoidType())
        return var_info

    @visitor.when(VariableNode)
    def visit(self, node:VariableNode, scope:Scope):
        typex = self.find_variable(scope, node.lex).type
        return Utils.GetType(typex, self.current_type)

    @visitor.when(InstantiateNode)
    def visit(self, node:InstantiateNode, scope:Scope):
        try:
            typex = self.context.get_type(node.lex, node.pos)
        except SemanticError:
            typex = ErrorType()
            error_text = TypesError.NEW_UNDEFINED_CLASS % node.lex
            self.errors.append(TypesError(*node.pos, error_text))
        return Utils.GetType(typex, self.current_type)

    @visitor.when(WhileNode)
    def visit(self, node:WhileNode, scope:Scope):
        cond = self.visit(node.cond, scope)
        if cond.name != 'Bool':
            error_text = TypesError.LOOP_CONDITION_ERROR
            self.errors.append(TypesError(*node.pos, error_text))   
        self.visit(node.expr, scope)
        return ObjectType()

    @visitor.when(IsVoidNode)
    def visit(self, node:IsVoidNode, scope:Scope):
        self.visit(node.expr, scope)
        return BoolType()

    @visitor.when(ConditionalNode)
    def visit(self, node:ConditionalNode, scope:Scope):
        cond = self.visit(node.cond, scope)
        if cond.name != 'Bool':
            error_text = TypesError.PREDICATE_ERROR % ('if', 'Bool')
            self.errors.append(TypesError(*node.pos, error_text))
        true_type = self.visit(node.stm, scope)
        false_type = self.visit(node.else_stm, scope)
        return Utils.GetCommonBaseType([false_type, true_type])

    @visitor.when(BlockNode)
    def visit(self, node:BlockNode, scope:Scope):
        value = None
        for exp in node.expr_list:
            value = self.visit(exp, scope)
        return value

    @visitor.when(LetNode)
    def visit(self, node:LetNode, scope:Scope):
        child_scope = scope.expr_dict[node]
        for init in node.init_list:
            self.visit(init, child_scope)
        return self.visit(node.expr, child_scope)

    @visitor.when(CaseNode) 
    def visit(self, node:CaseNode, scope:Scope):
        type_expr = self.visit(node.expr, scope)
        new_scope = scope.expr_dict[node]
        types, var_types = [], []
        for case,case_scope in zip(node.case_list, new_scope.children):
            typex,_ = self.visit(case, case_scope)
            types.append(typex)
            if case.typex in var_types:
                error_text = SemanticError.DUPLICATE_CASE_BRANCH % case.typex
                self.errors.append(SemanticError(*case.type_pos, error_text))
            var_types.append(case.typex)
        return Utils.GetCommonBaseType(types)