def create_type(self, name, pos): if name in self.types: error_text = 'Classes may not be redefined.' raise SemanticError(error_text, *pos) typex = self.types[name] = Type(name, pos) return typex
def define_attribute(self, name: str, typex, pos): try: self.attributes[name] except KeyError: try: self.get_attribute(name, pos) except: self.attributes[name] = attribute = Attribute( name, typex, len(self.attributes)) return attribute else: error_msg = f'Attribute {name} is an attribute of an inherit class.' raise SemanticError(error_msg, *pos) else: error_msg = f'Attribute {name} is multiply defined in class.' raise SemanticError(error_msg, *pos)
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, node: FuncDeclarationNode): args_names = [] args_types = [] for name, type_ in node.params: if name in args_names: error_text = SemanticError.PARAMETER_MULTY_DEFINED % name self.errors.append(SemanticError(error_text, *type_.pos)) args_names.append(name) try: arg_type = self.context.get_type(type_.value, type_.pos) except SemanticError: error_text = TypesError.PARAMETER_UNDEFINED % (type_.value, type_.value) self.errors.append(TypesError(error_text, *type_.pos)) arg_type = ErrorType() args_types.append(arg_type) try: return_type = self.context.get_type(node.type, node.type_pos) except SemanticError as e: error_text = TypesError.RETURN_TYPE_UNDEFINED % (node.type, node.id) self.errors.append(TypesError(error_text, *node.type_pos)) return_type = ErrorType(node.type_pos) try: self.current_type.define_method(node.id, args_names, args_types, return_type, node.pos) except SemanticError as e: self.errors.append(e)
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 _check_args(self, meth: Method, scope: Scope, args, pos): arg_types = [self.visit(arg, scope) for arg in args] if len(arg_types) > len(meth.param_types): error_text = SemanticError.ARGUMENT_ERROR % meth.name self.errors.append(SemanticError(error_text, *pos)) elif len(arg_types) < len(meth.param_types): for arg, arg_info in zip(meth.param_names[len(arg_types):], args[len(arg_types):]): error_text = SemanticError.ARGUMENT_ERROR % (meth.name) self.errors.append(SemanticError(error_text, *arg_info.pos)) for atype, ptype, param_name in zip(arg_types, meth.param_types, meth.param_names): if not atype.conforms_to(ptype): error_text = TypesError.INCOSISTENT_ARG_TYPE % ( meth.name, atype.name, param_name, ptype.name) self.errors.append(TypesError(error_text, *pos))
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, node: ClassDeclarationNode): if node.id in ['String', 'Int', 'Object', 'Bool', 'SELF_TYPE', 'IO']: error = SemanticError.REDEFINITION_ERROR % node.id self.errors.append(SemanticError(error, *node.pos)) try: self.context.create_type(node.id, node.pos) except SemanticError as e: self.errors.append(e) # añade un como padre Object si este no tiene if not node.parent: node.parent = 'Object'
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
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))
def visit(self, node: ClassDeclarationNode): if Utils.IsBasicType(node.id): error_text = SemanticError.REDEFINITION_ERROR % node.id self.errors.append(SemanticError(*node.pos, error_text)) try: self.context.create_type(node.id, node.pos) except SemanticError as exception: self.errors.append(exception) if not node.parent: node.parent = 'Object'
def define_method(self, name: str, param_names: list, param_types: list, return_type, pos=(0, 0)): if name in self.methods: error_msg = f'Method {name} is multiply defined' raise SemanticError(error_msg, *pos) method = self.methods[name] = Method(name, param_names, param_types, return_type) return method
def visit(self, node: FuncDeclarationNode, scope: Scope): parent = self.current_type.parent self.current_method = method = self.current_type.get_method( node.id, node.pos) if parent is not None: try: old_meth = parent.get_method(node.id, node.pos) if old_meth.return_type.name != method.return_type.name: error_text = SemanticError.WRONG_SIGNATURE_RETURN % ( node.id, method.return_type.name, old_meth.return_type.name) self.errors.append( SemanticError(error_text, *node.type_pos)) if len(method.param_names) != len(old_meth.param_names): error_text = SemanticError.WRONG_NUMBER_PARAM % node.id self.errors.append(SemanticError(error_text, *node.pos)) for (name, param), type1, type2 in zip(node.params, method.param_types, old_meth.param_types): if type1.name != type2.name: error_text = SemanticError.WRONG_SIGNATURE_PARAMETER % ( name, type1.name, type2.name) self.errors.append( SemanticError(error_text, *param.pos)) except SemanticError: pass result = self.visit(node.body, scope) return_type = get_type(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(error_text, *node.type_pos))
def visit(self, caseNode, scope): exprType = self.visit(caseNode.expr, scope) newScope = scope.expr_dict[caseNode] types = [] checkDuplicate = [] for option, optionScope in zip(caseNode.optionList, newScope.children): optionType = self.visit(option, optionScope) types.append(optionType) if option.type in checkDuplicate: errorText = f'Duplicate branch {option.type} in case statement.' self.errors.append( SemanticError(errorText, option.typeLine, option.typeCol)) checkDuplicate.append(option.type) caseNode.computed_type = get_common_basetype(types) return caseNode.computed_type
def visit(self, node: AttrDeclarationNode): try: attr_type = self.context.get_type(node.type, node.pos) except SemanticError as e: error_text = TypesError.ATTR_TYPE_UNDEFINED % (node.type, node.id) attr_type = ErrorType(node.type_pos) self.errors.append(TypesError(error_text, *node.type_pos)) if node.id == 'self': self.errors.append( SemanticError(SemanticError.SELF_ATTR, *node.pos)) try: self.current_type.define_attribute(node.id, attr_type, node.pos) except SemanticError as e: self.errors.append(e)
def visit(self, classDeclarationNode): if classDeclarationNode.id in [ 'Int', 'String', 'Bool', 'Object', 'SELF_TYPE', 'IO' ]: errorText = f'Redefinition of basic class {classDeclarationNode.id}' self.errors.append( SemanticError(errorText, classDeclarationNode.line, classDeclarationNode.col)) try: self.context.create_type( classDeclarationNode.id, (classDeclarationNode.line, classDeclarationNode.col)) except SemanticError as error: self.errors.append(error) if not classDeclarationNode.parent: classDeclarationNode.parent = 'Object'
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, funcDeclarationNode, scope): parent = self.currentType.parent pnames = [param[0] for param in funcDeclarationNode.params] ptypes = [param[1] for param in funcDeclarationNode.params] self.currentMethod = self.currentType.get_method( funcDeclarationNode.id, (funcDeclarationNode.line, funcDeclarationNode.col)) newScope = scope.create_child() scope.functions[funcDeclarationNode.id] = newScope for pname, ptype, pline, pcol in funcDeclarationNode.params: if pname == 'self': errorText = "'self' cannot be the name of a formal parameter." self.errors.append(SemanticError(errorText, pline, pcol)) newScope.define_variable(pname, self._get_type(ptype, (pline, pcol))) self.visit(funcDeclarationNode.body, newScope)
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 set_parent(self, parent): if type(self.parent) != ObjectType and self.parent is not None: error_msg = f'Parent type is already set for {self.name}.' raise SemanticError(error_msg, *self.pos) self.parent = parent
def get_type(self, name: str, pos=None): try: return self.types[name] except KeyError: error_text = f'Type {name} is not defined.' raise SemanticError(error_text, *pos)
def create_type(self, name: str, pos) -> Type: if name in self.types: error_text = SemanticError.TYPE_ALREADY_DEFINED raise SemanticError(error_text, *pos) typex = self.types[name] = Type(name, pos) return typex