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, 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 _get_method(self, typex, name, pos): typex = self.context.get_type(typex.name) try: return typex.get_method(name, pos) except SemanticError: if type(typex) != ErrorType and type(typex) != AutoType: errorText = f'Dispatch to undefined method {name}.' self.errors.append(AttributexError(errorText, *pos)) return MethodError(name, [], [], ErrorType())
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, 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, 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)
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, 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)
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
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()
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)
def visit(self, idNode, scope): try: return self.currentType.get_attribute( idNode.id, (idNode.line, idNode.col)).type except: if not scope.is_defined(idNode.id): errorText = f'Undeclared identifier {idNode.id}.' self.errors.append( NamexError(errorText, idNode.line, idNode.col)) vInfo = scope.define_variable( idNode.id, ErrorType((idNode.line, idNode.col))) else: vInfo = scope.find_variable(idNode.id) return vInfo.type
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
def visit(self, assignNode, scope): if assignNode.id == 'self': errorText = 'Cannot assign to \'self\'.' self.errors.append( SemanticError(errorText, assignNode.line, assignNode.col)) return vInfo = scope.find_variable(assignNode.id) if vInfo is None: varInfo = scope.find_attribute(assignNode.id) if varInfo is None: errorText = f'Undeclared identifier {assignNode.id}.' self.errors.append( NamexError(errorText, assignNode.line, assignNode.col)) vType = ErrorType() scope.define_variable(assignNode.id, vType) self.visit(assignNode.expr, scope)
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
class TypeBuilder: def __init__(self, context, errors): self.context = context self.currentType = None self.errors = errors @visitor.on('node') def visit(self, node): pass @visitor.when(ProgramNode) def visit(self, programNode): for declaration in programNode.declarations: self.visit(declaration) @visitor.when(ClassDeclarationNode) 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) @visitor.when(FuncDeclarationNode) 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) @visitor.when(AttrDeclarationNode) 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 _get_type(self, ntype, pos): try: return self.context.get_type(ntype, pos) except SemanticError as e: self.errors.append(e) return ErrorType()