def visit(self, varDeclarationNode, scope):
        if varDeclarationNode.id == 'self':
            errorText = '\'self\' cannot be bound in a \'let\' expression.'
            self.errors.append(
                SemanticError(errorText, varDeclarationNode.line,
                              varDeclarationNode.col))
            return

        try:
            vType = self.context.get_type(
                varDeclarationNode.type,
                (varDeclarationNode.line, varDeclarationNode.col))
        except:
            errorText = f'Class {varDeclarationNode.type} of let-bound identifier {varDeclarationNode.id} is undefined.'
            self.errors.append(
                TypexError(errorText, varDeclarationNode.typeLine,
                           varDeclarationNode.typeCol))
            vType = ErrorType()

        vType = self._get_type(
            varDeclarationNode.type,
            (varDeclarationNode.typeLine, varDeclarationNode.typeCol))
        varInfo = scope.define_variable(varDeclarationNode.id, vType)

        if varDeclarationNode.expr is not None:
            self.visit(varDeclarationNode.expr, scope)
        else:
            self.define_default_value(vType, varDeclarationNode)
    def visit(self, attrDeclarationNode):
        try:
            attrType = self.context.get_type(
                attrDeclarationNode.type,
                (attrDeclarationNode.line, attrDeclarationNode.col))
        except SemanticError:
            errorText = f'Class {attrDeclarationNode.type} of attribute {attrDeclarationNode.id} is undefined.'
            self.errors.append(
                TypexError(errorText, attrDeclarationNode.typeLine,
                           attrDeclarationNode.typeCol))
            attrType = ErrorType(
                (attrDeclarationNode.line, attrDeclarationNode.col))

        if attrDeclarationNode.id == 'self':
            errorText = f"'self' cannot be the name of an attribute."
            self.errors.append(
                SemanticError(errorText, attrDeclarationNode.line,
                              attrDeclarationNode.col))

        try:
            self.currentType.define_attribute(
                attrDeclarationNode.id, attrType,
                (attrDeclarationNode.line, attrDeclarationNode.col))
        except SemanticError as error:
            self.errors.append(error)
    def visit(self, memberCallNode, scope):
        typex = self.currentType
        method = self._get_method(typex, memberCallNode.id,
                                  (memberCallNode.line, memberCallNode.col))
        if not isinstance(method, MethodError):
            # check the args
            argTypes = [self.visit(arg, scope) for arg in memberCallNode.args]

            if len(argTypes) > len(method.param_types):
                errorText = f'Method {method.name} called with wrong number of arguments.'
                self.errors.append(
                    SemanticError(errorText, memberCallNode.line,
                                  memberCallNode.col))
            elif len(argTypes) < len(method.param_types):
                for arg, argInfo in zip(method.param_names[len(argTypes):],
                                        memberCallNode.args[len(argTypes):]):
                    errorText = f'Method {method.name} called with wrong number of arguments.'
                    self.errors.append(SemanticError(errorText, *argInfo.pos))

            for argType, paramType, paramName in zip(argTypes,
                                                     method.param_types,
                                                     method.param_names):
                if not argType.conforms_to(paramType):
                    errorText = f'In call of method {method.name}, type {argType.name} of parameter {paramName} does not conform to declared type {paramType.name}.'
                    self.errors.append(
                        TypexError(errorText, memberCallNode.line,
                                   memberCallNode.col))

        memberCallNode.static_type = typex
        memberCallNode.computed_type = get_type(method.return_type, typex)
        return memberCallNode.computed_type
    def visit(self, whileNode, scope):
        conditionType = self.visit(whileNode.condition, scope)
        if conditionType.name != 'Bool':
            errorText = 'Loop condition does not have type Bool.'
            self.errors.append(
                TypexError(errorText, whileNode.line, whileNode.col))
        self.visit(whileNode.body, scope)

        whileNode.computed_type = ObjectType()
        return ObjectType()
    def visit(self, lessEq, scope):
        leftType = self.visit(lessEq.lvalue, scope)
        rightType = self.visit(lessEq.rvalue, scope)
        if leftType != IntType() or rightType != IntType():
            errorText = f'non-Int arguments: {leftType.name} <= {rightType.name} .'
            self.errors.append(TypexError(errorText, lessEq.line, lessEq.col))
            return ErrorType()

        lessEq.computed_type = BoolType()
        return BoolType()
    def visit(self, negationNode, scope):
        exprType = self.visit(negationNode.expr, scope)
        if exprType != IntType():
            errorText = f'Argument of \'~\' has type {exprType.name} instead of {IntType().name}.'
            self.errors.append(
                TypexError(errorText, negationNode.line, negationNode.col))
            return ErrorType()

        negationNode.computed_type = IntType()
        return IntType()
    def visit(self, logicNegationNode, scope):
        exprType = self.visit(logicNegationNode.expr, scope)
        if exprType != BoolType():
            errorText = f'Argument of \'not\' has type {exprType.name} instead of {BoolType().name}.'
            self.errors.append(
                TypexError(errorText, logicNegationNode.line,
                           logicNegationNode.col))
            return ErrorType()

        logicNegationNode.computed_type = BoolType()
        return BoolType()
    def visit(self, divNode, scope):
        leftType = self.visit(divNode.lvalue, scope)
        rightType = self.visit(divNode.rvalue, scope)
        if leftType != IntType() or rightType != IntType():
            errorText = f'non-Int arguments: {leftType.name} / {rightType.name} .'
            self.errors.append(TypexError(errorText, divNode.line,
                                          divNode.col))
            return ErrorType()

        divNode.computed_type = IntType()
        return IntType()
    def visit(self, arrobaCallNode, scope):
        objType = self.visit(arrobaCallNode.obj, scope)
        typex = self._get_type(
            arrobaCallNode.type,
            (arrobaCallNode.typeLine, arrobaCallNode.typeCol))

        if not objType.conforms_to(typex):
            errorText = f'Expression type {typex.name} does not conform to declared static dispatch type {objType.name}.'
            self.errors.append(
                TypexError(errorText, arrobaCallNode.typeLine,
                           arrobaCallNode.typeCol))
            return ErrorType()

        method = self._get_method(typex, arrobaCallNode.id,
                                  (arrobaCallNode.line, arrobaCallNode.col))
        if not isinstance(method, MethodError):
            # check the args
            argTypes = [self.visit(arg, scope) for arg in arrobaCallNode.args]

            if len(argTypes) > len(method.param_types):
                errorText = f'Method {method.name} called with wrong number of arguments.'
                self.errors.append(
                    SemanticError(errorText, arrobaCallNode.line,
                                  arrobaCallNode.col))
            elif len(argTypes) < len(method.param_types):
                for arg, argInfo in zip(method.param_names[len(argTypes):],
                                        arrobaCallNode.args[len(argTypes):]):
                    errorText = f'Method {method.name} called with wrong number of arguments.'
                    self.errors.append(SemanticError(errorText, *argInfo.pos))

            for argType, paramType, paramName in zip(argTypes,
                                                     method.param_types,
                                                     method.param_names):
                if not argType.conforms_to(paramType):
                    errorText = f'In call of method {method.name}, type {argType.name} of parameter {paramName} does not conform to declared type {paramType.name}.'
                    self.errors.append(
                        TypexError(errorText, arrobaCallNode.line,
                                   arrobaCallNode.col))

        arrobaCallNode.computed_type = get_type(method.return_type, TypexError)
        return arrobaCallNode.computed_type
Esempio n. 10
0
    def visit(self, assignNode, scope):
        varInfo = self.find_variable(scope, assignNode.id)
        varType = get_type(varInfo.type, self.currentType)
        typex = self.visit(assignNode.expr, scope)

        if not typex.conforms_to(varType):
            errorText = f'Inferred type {typex.name} of initialization of {assignNode.id} does not conform to identifier\'s declared type {varType.name}.'
            self.errors.append(
                TypexError(errorText, assignNode.line, assignNode.col))

        assignNode.computed_type = typex
        return typex
Esempio n. 11
0
    def visit(self, caseOptionNode, scope):
        try:
            typex = self.context.get_type(
                caseOptionNode.type, (caseOptionNode.line, caseOptionNode.col))
        except:
            errorText = f'Class {caseOptionNode.type} of case branch is undefined.'
            self.errors.append(
                TypexError(errorText, caseOptionNode.line, caseOptionNode.col))
            typex = ErrorType()

        scope.define_variable(caseOptionNode.id, typex)
        self.visit(caseOptionNode.expr, scope)
Esempio n. 12
0
    def visit(self, ifThenElseNode, scope):
        conditionType = self.visit(ifThenElseNode.condition, scope)
        if conditionType.name != 'Bool':
            errorText = f'Predicate of \'if\' does not have type Bool.'
            self.errors.append(
                TypexError(errorText, ifThenElseNode.line, ifThenElseNode.col))

        ifBodyType = self.visit(ifThenElseNode.ifBody, scope)
        elseBodyType = self.visit(ifThenElseNode.elseBody, scope)

        ifThenElseNode.computed_type = get_common_basetype(
            [ifBodyType, elseBodyType])
        return ifThenElseNode.computed_type
Esempio n. 13
0
    def visit(self, newNode, scope):
        try:
            typex = self.context.get_type(newNode.id,
                                          (newNode.line, newNode.col))
        except:
            typex = ErrorType()
            errorText = f'\'new\' used with undefined class {newNode.id}.'
            self.errors.append(TypexError(errorText, newNode.line,
                                          newNode.col))

        typex = get_type(typex, self.currentType)
        newNode.computed_type = typex
        return typex
Esempio n. 14
0
    def visit(self, equalNode, scope):
        leftType = self.visit(equalNode.lvalue, scope)
        rightType = self.visit(equalNode.rvalue, scope)
        if (leftType != rightType) and (leftType in [
                IntType(), StringType(), BoolType()
        ] or rightType in [IntType(), StringType(),
                           BoolType()]):
            errorText = 'Illegal comparison with a basic type.'
            self.errors.append(
                TypexError(errorText, equalNode.line, equalNode.col))
            return ErrorType()

        equalNode.computed_type = BoolType()
        return BoolType()
Esempio n. 15
0
    def visit(self, funcDeclarationNode):
        argsNames = []
        argsTypes = []
        for name, typex, line, col in funcDeclarationNode.params:
            if name in argsNames:
                errorText = f'Formal parameter {name} is multiply defined.'
                self.errors.append(SemanticError(errorText, line, col))

            argsNames.append(name)

            try:
                argType = self.context.get_type(typex, (line, col))
            except SemanticError:
                errorText = f'Class {typex} of formal parameter {typex} is undefined.'
                self.errors.append(TypexError(errorText, line, col))
                argType = ErrorType()

            argsTypes.append(argType)

        try:
            returnType = self.context.get_type(
                funcDeclarationNode.type,
                (funcDeclarationNode.typeLine, funcDeclarationNode.typeCol))
        except SemanticError:
            errorText = f'Undefined return type {funcDeclarationNode.type} in method {funcDeclarationNode.id}.'
            self.errors.append(
                TypexError(errorText, funcDeclarationNode.typeLine,
                           funcDeclarationNode.typeCol))
            returnType = ErrorType(
                (funcDeclarationNode.typeLine, funcDeclarationNode.typeCol))

        try:
            self.currentType.define_method(
                funcDeclarationNode.id, argsNames, argsTypes, returnType,
                (funcDeclarationNode.line, funcDeclarationNode.col))
        except SemanticError as error:
            self.errors.append(error)
Esempio n. 16
0
    def visit(self, varDeclarationNode, scope):
        varType = self._get_type(
            varDeclarationNode.type,
            (varDeclarationNode.line, varDeclarationNode.col))
        varType = get_type(varType, self.currentType)

        if varDeclarationNode.expr == None:
            return varType
        else:
            typex = self.visit(varDeclarationNode.expr, scope)
            if not typex.conforms_to(varType):
                errorText = f'Inferred type {typex.name} of initialization of {varDeclarationNode.id} does not conform to identifier\'s declared type {varType.name}.'
                self.errors.append(
                    TypexError(errorText, varDeclarationNode.typeLine,
                               varDeclarationNode.typeCol))
            return typex
Esempio n. 17
0
    def visit(self, attrDeclarationNode, scope):
        attr = self.currentType.get_attribute(
            attrDeclarationNode.id,
            (attrDeclarationNode.line, attrDeclarationNode.col))
        attrType = get_type(attr.type, self.currentType)
        self.currentIndex = attr.index
        typex = self.visit(attrDeclarationNode.expr, scope)
        self.currentIndex = None

        if not typex.conforms_to(attrType):
            errorText = f'Inferred type {typex.name} of initialization of attribute {attr.name} does not conform to declared type {attrType.name}.'
            self.errors.append(
                TypexError(errorText, attrDeclarationNode.line,
                           attrDeclarationNode.col))
            return ErrorType()

        return typex
Esempio n. 18
0
    def visit(self, classDeclarationNode):
        try:
            self.currentType = self.context.get_type(
                classDeclarationNode.id,
                (classDeclarationNode.line, classDeclarationNode.col))
        except SemanticError as error:
            self.currentType = ErrorType()
            self.errors.append(error)

        if classDeclarationNode.parent is not None:
            if classDeclarationNode.parent in ['String', 'Int', 'Bool']:
                errorText = f'Class {classDeclarationNode.id} cannot inherit class {classDeclarationNode.parent}.'
                self.errors.append(
                    SemanticError(errorText, classDeclarationNode.line,
                                  classDeclarationNode.col))
            try:
                parent = self.context.get_type(
                    classDeclarationNode.parent,
                    (classDeclarationNode.parentLine,
                     classDeclarationNode.parentCol))
            except:
                errorText = f'Class {classDeclarationNode.id} inherits from an undefined class {classDeclarationNode.parent}'
                self.errors.append(
                    TypexError(errorText, classDeclarationNode.line,
                               classDeclarationNode.col))
                parent = None
            try:
                current = parent
                while current is not None:
                    if current.name == self.currentType.name:
                        errorText = f'Class {self.currentType.name}, or an ancestor of {self.currentType.name}, is involved in an inheritance cycle.'
                        raise SemanticError(errorText,
                                            classDeclarationNode.line,
                                            classDeclarationNode.col)
                    current = current.parent
            except SemanticError as error:
                parent = ErrorType()
                self.errors.append(error)

            self.currentType.set_parent(parent)

        for feature in classDeclarationNode.features:
            self.visit(feature)
Esempio n. 19
0
    def visit(self, funcDeclarationNode, scope):
        parent = self.currentType.parent
        self.currentMethod = self.currentType.get_method(
            funcDeclarationNode.id,
            (funcDeclarationNode.line, funcDeclarationNode.col))

        method = self.currentMethod
        if parent is not None:
            try:
                oldMethod = parent.get_method(
                    funcDeclarationNode.id,
                    (funcDeclarationNode.line, funcDeclarationNode.col))
                if oldMethod.return_type.name != method.return_type.name:
                    errorText = f'In redefined method {funcDeclarationNode.id}, return type {method.return_type.name} is different from original return type {oldMethod.return_type.name}.'
                    self.errors.append(
                        SemanticError(errorText, funcDeclarationNode.typeLine,
                                      funcDeclarationNode.typeCol))
                if len(method.param_names) != len(oldMethod.param_names):
                    errorText = f'Incompatible number of formal parameters in redefined method {funcDeclarationNode.id}.'
                    self.errors.append(
                        SemanticError(errorText, funcDeclarationNode.line,
                                      funcDeclarationNode.col))
                for (name, ptype, pline,
                     pcol), type1, type2 in zip(funcDeclarationNode.params,
                                                method.param_types,
                                                oldMethod.param_types):
                    if type1.name != type2.name:
                        errorText = f'In redefined method {name}, parameter type {type1.name} is different from original type {type2.name}.'
                        self.errors.append(
                            SemanticError(errorText, pline, pcol))
            except:
                pass

        result = self.visit(funcDeclarationNode.body, scope)
        returnType = get_type(method.return_type, self.currentType)

        if not result.conforms_to(returnType):
            errorText = f'Inferred return type {result.name} of method test does not conform to declared return type {returnType.name}.'
            self.errors.append(
                TypexError(errorText, funcDeclarationNode.typeLine,
                           funcDeclarationNode.typeCol))