def visit(self, node: ast.MethodDeclarationNode): param_names = [] param_types = [] for i, (name, typex) in enumerate(node.params): param_names.append(name) try: param_types.append(self.context.get_type(typex)) except SemanticError: param_types.append(ErrorType()) line, column = node.param_types_positions[i] self.errors.append( err.UNDEFINED_PARAM_TYPE % (line, column, typex, node.id, self.current_type.name)) try: return_type = self.context.get_type(node.return_type) except SemanticError: return_type = ErrorType() line, column = node.return_type_position self.errors.append(err.UNDEFINED_RETURN_TYPE % (line, column, node.return_type, node.id, self.current_type.name)) try: self.current_type.define_method(node.id, param_names, param_types, return_type) except SemanticError: self.errors.append( err.METHOD_ALREADY_DEFINED % (node.line, node.column, node.id, self.current_type.name))
def visit(self, node: ast.MethodCallNode, scope: Scope): if node.obj is None: node.obj = ast.VariableNode("self") obj_type = self.visit(node.obj, scope) if node.type is not None: try: ancestor_type = self.context.get_type(node.type) except SemanticError: ancestor_type = ErrorType() line, column = node.type_position self.errors.append(err.UNDEFINED_TYPE % (line, column, node.type)) if not obj_type.conforms_to(ancestor_type): line, column = node.type_position self.errors.append( err.INVALID_ANCESTOR % (line, column, obj_type.name, ancestor_type.name)) else: ancestor_type = obj_type try: method = ancestor_type.get_method(node.id) except SemanticError: line, column = node.id_position self.errors.append(err.DISPATCH_UNDEFINED_METHOD % (line, column, node.id, obj_type.name)) for arg in node.args: self.visit(arg, scope) return ErrorType() args_count = len(node.args) params_count = len(method.param_names) if args_count != params_count: line, column = node.id_position self.errors.append(err.DISPATCH_WITH_WRONG_NUMBER_OF_ARGS % (line, column, method.name, obj_type.name, params_count, args_count)) number_of_args = min(args_count, params_count) for i, arg in enumerate(node.args[:number_of_args]): arg_type = self.visit(arg, scope) if not arg_type.conforms_to(method.param_types[i]): line, column = node.args_positions[i] self.errors.append(err.INCOMPATIBLE_TYPES % ( line, column, arg_type.name, method.param_types[i].name, )) return (method.return_type if method.return_type.name != "SELF_TYPE" else ancestor_type)
def visit(self, node: ast.SwitchCaseNode, scope: Scope): self.visit(node.expr, scope) types = [] visited = set() for i, (identifier, type_name, expr) in enumerate(node.cases): new_scope = scope.create_child() try: if type_name != "SELF_TYPE": new_scope.define_variable(identifier, self.context.get_type(type_name)) else: self.errors.append(err.INVALID_CASE_TYPE % type_name) except SemanticError: new_scope.define_variable(identifier, ErrorType()) line, column = node.cases_positions[i] self.errors.append(err.UNDEFINED_TYPE_IN_BRANCH % (line, column, type_name)) # Cannot be dublicate Branches types if type_name in visited: line, column = node.cases_positions[i] self.errors.append(err.DUPLICATE_BARNCH_IN_CASE % (line, column, type_name)) visited.add(type_name) types.append(self.visit(expr, new_scope)) return Type.multi_join(types)
def visit(self, node: ast.LetNode, scope: Scope): for i, (_id, _type, _expr) in enumerate(node.declarations): if _id == "self": line, column = node.declaration_names_positions[i] self.errors.append(err.SELF_USED_IN_LET % (line, column)) continue try: var_static_type = (self.context.get_type(_type) if _type != "SELF_TYPE" else self.current_type) except SemanticError: line, column = node.declaration_types_positions[i] self.errors.append(err.UNDEFINED_TYPE % (line, column, _type)) var_static_type = ErrorType() # if scope.is_local(_id): # feature = self.current_method or self.current_attribute # self.errors.append( # err.LOCAL_ALREADY_DEFINED # % (node.line, node.column, _id, feature.name) # ) # else: scope.define_variable(_id, var_static_type) expr_type = (self.visit(_expr, scope.create_child()) if _expr is not None else None) if expr_type is not None and not expr_type.conforms_to( var_static_type): self.errors.append(err.INCOMPATIBLE_TYPES % (node.line, node.column, expr_type.name, var_static_type.name)) return self.visit(node.expr, scope.create_child())
def visit(self, node: ast.LetNode, scope: Scope): for _id, _type, _expr in node.declarations: try: # Define and get the var_info var_info = scope.define_variable(_id, self.context.get_type(_type)) except SemanticError: var_info = scope.define_variable(_id, ErrorType()) var_info_node = self.variables[var_info] = VariableInfoNode( var_info.type, var_info) expr_node = (self.visit(_expr, scope.create_child()) if _expr is not None else None) if var_info.type.name == "AUTO_TYPE": # Create an edge or add an new node only if it is AutoType if expr_node is not None: self.graph.add_edge(expr_node, var_info_node) if expr_node.type.name == "AUTO_TYPE": self.graph.add_edge(var_info_node, expr_node) else: self.graph.add_node(var_info_node) elif expr_node is not None and expr_node.type.name == "AUTO_TYPE": self.graph.add_edge(var_info_node, expr_node) return self.visit(node.expr, scope.create_child())
def visit(self, node: ast.AttrDeclarationNode): try: self.current_type.define_attribute( node.id, self.context.get_type(node.type)) except SemanticError as e: self.errors.append(e.text) self.current_type.define_attribute(node.id, ErrorType())
def visit(self, node: ast.InstantiateNode, scope: Scope): try: return self.context.get_type( node.lex) if node.lex != 'SELF_TYPE' else self.current_type except SemanticError as e: self.errors.append(e.text) return ErrorType()
def visit(self, node: ast.MethodDeclarationNode, scope: Scope): self.current_method = self.current_type.get_method(node.id) # Parameters can hide the attribute declaration, that's why we are not checking if there is defined, # instead we are checking for local declaration. Also it is checked that the static type of a parameter is # different of SELF_TYPE. scope.define_variable('self', self.current_type) for param_name, param_type in zip(self.current_method.param_names, self.current_method.param_types): if not scope.is_local(param_name): if param_type.name == 'SELF_TYPE': self.errors.append(err.INVALID_PARAM_TYPE % 'SELF_TYPE') scope.define_variable(param_name, ErrorType()) else: scope.define_variable( param_name, self.context.get_type(param_type.name)) else: self.errors.append(err.LOCAL_ALREADY_DEFINED % (param_name, self.current_method.name)) return_type = self.context.get_type( node.return_type ) if node.return_type != 'SELF_TYPE' else self.current_type expr_type = self.visit(node.body, scope) if not expr_type.conforms_to(return_type): self.errors.append(err.INCOMPATIBLE_TYPES % (expr_type.name, return_type.name))
def visit(self, node: ast.AttrDeclarationNode, scope: Scope): if node.id == "self": self.errors.append(err.SELF_INVALID_ATTRIBUTE_ID % (node.line, node.column)) try: attr_type = (self.context.get_type(node.type) if node.type != "SELF_TYPE" else self.current_type) except SemanticError: attr_type = ErrorType() scope.define_variable("self", self.current_type) # set the current attribute for analyze the body # and set the self.current_method variable to None self.current_attribute = self.current_type.get_attribute(node.id) self.current_method = None if node.expr is not None: expr_type = self.visit(node.expr, scope.create_child()) if not expr_type.conforms_to(attr_type): line, column = node.expr_position self.errors.append( err.INCOMPATIBLE_TYPES % (line, column, expr_type.name, attr_type.name)) scope.define_variable(node.id, attr_type)
def visit(self, node: ast.VariableNode, scope: Scope): variable = scope.find_variable(node.lex) if variable is None: self.errors.append(err.VARIABLE_NOT_DEFINED % (node.lex, self.current_method.name)) return ErrorType() return variable.type
def _check_unary_operation(self, node: ast.UnaryNode, scope: Scope, operation: str, expected_type: Type): typex = self.visit(node.expr, scope) if typex == expected_type: return typex self.errors.append(err.INVALID_UNARY_OPERATION % (operation, typex.name)) return ErrorType()
def visit(self, node: ast.InstantiateNode, scope: Scope): try: return (self.context.get_type(node.lex) if node.lex != "SELF_TYPE" else self.current_type) except SemanticError as e: line, column = node.type_position self.errors.append(err.UNDEFINED_NEW_TYPE % (line, column, node.lex)) return ErrorType()
def _check_int_binary_operation(self, node: ast.BinaryNode, scope: Scope, operation: str, return_type: Type): left_type = self.visit(node.left, scope) right_type = self.visit(node.right, scope) if left_type == right_type == self.context.get_type('Int'): return return_type self.errors.append(err.INVALID_BINARY_OPERATION % (operation, left_type.name, right_type.name)) return ErrorType()
def visit(self, node: ast.MethodDeclarationNode): param_names = [] param_types = [] for name, typex in node.params: param_names.append(name) try: param_types.append(self.context.get_type(typex)) except SemanticError as e: param_types.append(ErrorType()) self.errors.append(e.text) try: return_type = self.context.get_type(node.return_type) except SemanticError as e: return_type = ErrorType() self.errors.append(e.text) self.current_type.define_method(node.id, param_names, param_types, return_type)
def visit(self, node: ast.VariableNode, scope: Scope): variable = scope.find_variable(node.lex) if variable is None: if self.current_attribute is not None: name = self.current_attribute.name else: name = self.current_method.name self.errors.append(err.UNDEFINED_VARIABLE % (node.line, node.column, node.lex, name)) return ErrorType() return variable.type
def visit(self, node: ast.MethodCallNode, scope: Scope): if node.obj is None: node.obj = ast.VariableNode('self') obj_type = self.visit(node.obj, scope) if node.type is not None: try: ancestor_type = self.context.get_type(node.type) except SemanticError as e: ancestor_type = ErrorType() self.errors.append(e.text) if not obj_type.conforms_to(ancestor_type): self.errors.append(err.INVALID_ANCESTOR % (obj_type.name, ancestor_type.name)) else: ancestor_type = obj_type try: method = ancestor_type.get_method(node.id) except SemanticError as e: self.errors.append(e.text) for arg in node.args: self.visit(arg, scope) return ErrorType() if len(node.args) != len(method.param_names): self.errors.append(err.METHOD_OVERRIDE_ERROR % (method.name, obj_type.name)) for i, arg in enumerate(node.args): arg_type = self.visit(arg, scope) if not arg_type.conforms_to(method.param_types[i]): self.errors.append(err.INCOMPATIBLE_TYPES % (arg_type.name, method.param_types[i].name)) return method.return_type if method.return_type.name != 'SELF_TYPE' else ancestor_type
def visit(self, node: ast.AttrDeclarationNode): try: attr_type = self.context.get_type(node.type) except SemanticError: attr_type = ErrorType() line, column = node.type_position self.errors.append( err.UNDEFINED_ATTRIBUTE_TYPE % (line, column, node.type, node.id, self.current_type.name)) try: self.current_type.define_attribute(node.id, attr_type) except SemanticError: self.errors.append( err.ATTRIBUTE_ALREADY_DEFINED % (node.line, node.column, node.id, self.current_type.name))
def visit(self, node: ast.SwitchCaseNode, scope: Scope): self.visit(node.expr, scope) types = [] for _id, _type, _expr in node.cases: new_scope = scope.create_child() try: if _type != 'SELF_TYPE': new_scope.define_variable(_id, self.context.get_type(_type)) else: self.errors.append(err.INVALID_CASE_TYPE % _type) except SemanticError as e: new_scope.define_variable(_id, ErrorType()) self.errors.append(e.text) types.append(self.visit(_expr, new_scope)) return Type.multi_join(types)
def visit(self, node: ast.LetNode, scope: Scope): for _id, _type, _expr in node.declarations: try: var_static_type = self.context.get_type( _type) if _type != 'SELF_TYPE' else self.current_type except SemanticError as e: self.errors.append(e.text) var_static_type = ErrorType() if scope.is_local(_id): self.errors.append(err.LOCAL_ALREADY_DEFINED % (_id, self.current_method.name)) else: scope.define_variable(_id, var_static_type) expr_type = self.visit( _expr, scope.create_child()) if _expr is not None else None if expr_type is not None and not expr_type.conforms_to( var_static_type): self.errors.append(err.INCOMPATIBLE_TYPES % (expr_type.name, var_static_type.name)) return self.visit(node.expr, scope.create_child())
def visit(self, node: ast.BlockNode, scope: Scope): child_scope = scope.create_child() return_type = ErrorType() for expr in node.expressions: return_type = self.visit(expr, child_scope) return return_type