def visit(self, node, scope): if node.id == "self": node_row, node_col = node.token.location self.errors.append( SemanticError( node_row, node_col, "'self' cannot be bound in a 'let' expression. " + SELF_IS_READONLY)) static_type = None try: static_type = self.context.get_type(node.type.lex) if static_type.name == "SELF_TYPE": static_type = self.current_type except SError as e: node_row, node_col = node.type.location self.errors.append(TypeError(node_row, node_col, e.text)) static_type = ErrorType() if node.expr != None: typex, node_exp = self.visit(node.expr, scope) if not typex.conforms_to(static_type): line, col = node.expr.token.location self.errors.append( TypeError( line, col, INCOMPATIBLE_TYPES % (typex.name, static_type.name))) else: node_exp = None scope.define_variable(node.id, static_type) return (static_type, cool_type_nodes.VarDeclarationNode(node.id, node.type.lex, node_exp, static_type))
def get_type(self, ntype, comp_error_mesg): try: return self.context.get_type(ntype.lex) except SError as error: node_row, node_col = ntype.location self.errors.append(TypeError(node_row, node_col, f"Type {ntype.lex} " + comp_error_mesg + " is not defined.")) return ErrorType()
def visit(self, node, scope): if node.id.lex == "self": node_row, node_col = node.token.location self.errors.append( SemanticError(node_row, node_col, "Cannot assign to 'self'. " + SELF_IS_READONLY)) var_type = None if not scope.is_defined(node.id.lex): node_row, node_col = node.id.location self.errors.append( NameError( node_row, node_col, VARIABLE_NOT_DEFINED % (node.id.lex, self.current_method.name))) var_type = ErrorType() else: var_type = scope.find_variable(node.id.lex).type expr_type, exp_node = self.visit(node.expr, scope) if not expr_type.conforms_to(var_type): node_row, node_col = node.token.location self.errors.append( TypeError( node_row, node_col, f"Inferred type {expr_type.name} of assigned expression does not conforms to type {var_type.name} of variable '{node.id.lex}'" )) return (expr_type, cool_type_nodes.AssignNode(node.id.lex, exp_node, expr_type))
def visit(self, node, scope): type_not_found = False try: typex = self.context.get_type(node.type.lex) if typex.name == "SELF_TYPE": typex = self.current_type except SError as e: # ERROR already reported in type builder type_not_found = True if node.init_exp != None: init_expr_type, init_exp = self.visit(node.init_exp, scope) if type_not_found: return cool_type_nodes.AttrDeclarationNode( node.id.lex, node.type.lex, init_exp) if not init_expr_type.conforms_to(typex): line, col = node.token.location self.errors.append( TypeError( line, col, INCOMPATIBLE_TYPES % (init_expr_type.name, typex.name))) else: init_exp = None return cool_type_nodes.AttrDeclarationNode(node.id.lex, node.type.lex, init_exp)
def visit(self, node, scope): int_type = self.context.get_type("Int") typex, exp_node = self.visit(node.expr, scope) if typex != int_type and not typex.name == "AUTO_TYPE": node_row, node_col = node.expr.token.location self.errors.append( TypeError( node_row, node_col, f"Expression after '~' must be Int, current is {typex.name}" )) return (ErrorType(), cool_type_nodes.NegNode(exp_node, ErrorType())) return (int_type, cool_type_nodes.NegNode(exp_node, int_type))
def visit(self, node, scope): bool_type = self.context.get_type("Bool") typex, exp_node = self.visit(node.expr, scope) if typex != bool_type and not typex.name == "AUTO_TYPE": line, col = node.expr.token.location self.errors.append( TypeError( line, col, f"Expression after 'not' must be Bool, current is {typex.name}" )) return (ErrorType(), cool_type_nodes.NotNode(exp_node, ErrorType())) return (bool_type, cool_type_nodes.NotNode(exp_node, bool_type))
def visit(self, node, scope): int_type = self.context.get_type("Int") left_type, left_exp_node = self.visit(node.left, scope) right_type, right_exp_node = self.visit(node.right, scope) if (left_type != int_type and left_type.name != "AUTO_TYPE") or ( right_type != int_type and right_type.name != "AUTO_TYPE"): node_row, node_col = node.token.location self.errors.append( TypeError( node_row, node_col, INVALID_OPERATION % (left_type.name, right_type.name))) return (int_type, cool_type_nodes.DivNode(left_exp_node, right_exp_node, int_type))
def visit(self, node, scope): try: typex = self.context.get_type(node.lex.lex) if typex.name == "SELF_TYPE": return self.current_type return (typex, cool_type_nodes.InstantiateNode(node.lex.lex, typex)) except SError as error: node_row, node_col = node.lex.location self.errors.append( TypeError( node_row, node_col, f"Type {node.lex.lex} of 'new' expression is not defined.") ) return (ErrorType(), cool_type_nodes.InstantiateNode(node.lex.lex, ErrorType()))
def visit(self, node, scope): condition_type, condition_node = self.visit(node.condition, scope) body_type, body_node = self.visit(node.body, scope) bool_type = self.context.get_type("Bool") if condition_type != bool_type and condition_type.name != "AUTO_TYPE": node_row, node_col = node.condition.token.location self.errors.append( TypeError( node_row, node_col, f"Expression in 'while' condition must be bool, current type is {condition_type.name}" )) return (ErrorType(), cool_type_nodes.WhileNode(condition_node, body_node, ErrorType())) obj_type = self.context.get_type("Object") return (obj_type, cool_type_nodes.WhileNode(condition_node, body_node, obj_type))
def visit(self, node, scope): predicate_type, if_node = self.visit(node.if_expr, scope) then_type, then_node = self.visit(node.then_expr, scope) else_type, else_node = self.visit(node.else_expr, scope) if predicate_type.name != "Bool" and predicate_type.name != "AUTO_TYPE": node_row, node_col = node.if_expr.token.location self.errors.append( TypeError( node_row, node_col, f"Expression after 'if' must be Bool, current type is {predicate_type.name}" )) return (ErrorType(), cool_type_nodes.IfNode(if_node, then_node, else_node, ErrorType())) least_type = find_least_type(then_type, else_type, self.context) return (least_type, cool_type_nodes.IfNode(if_node, then_node, else_node, least_type))
def visit(self, node, scope): int_type = self.context.get_type("Int") string_type = self.context.get_type("String") bool_type = self.context.get_type("Bool") built_in_types = [int_type, string_type, bool_type] left_type, left_exp_node = self.visit(node.left, scope) right_type, right_exp_node = self.visit(node.right, scope) if left_type in built_in_types or right_type in built_in_types: if (left_type != right_type and left_type.name != "AUTO_TYPE" and right_type.name != "AUTO_TYPE"): node_row, node_col = node.token.location self.errors.append( TypeError( node_row, node_col, f"One of the expressions of '=' operator is of type Int, String or Bool, the other must have the same static type. Left type: {left_type.name}. Right type: {right_type.name}" )) return (bool_type, cool_type_nodes.EqualNode(left_exp_node, right_exp_node, bool_type))
def visit(self, node, scope): if node.id.lex == "self": node_row, node_col = node.id.location self.errors.append( SemanticError( node_row, node_col, "'self' cannot be bound in a 'case' expression. " + SELF_IS_READONLY)) try: static_type = self.context.get_type(node.type.lex) scope.define_variable(node.id.lex, static_type) except SError as e: node_row, node_col = node.type.location self.errors.append( TypeError( node_row, node_col, f"Type {node.type.lex} of case branch is undefined.")) typex, node_exp = self.visit(node.expr, scope) return (typex, cool_type_nodes.CaseItemNode(node.id.lex, node.type.lex, node_exp, typex))
def visit(self, node, scope): auto_type = self.context.get_type("AUTO_TYPE") typex = None if node.obj is not None: typex, obj_exp = self.visit(node.obj, scope) if typex == auto_type: return auto_type else: typex = self.current_type obj_exp = None new_args = [] arg_types = [] for arg in node.args: # visiting arguments in case of earlier return arg_type, arg_node = self.visit(arg, scope) new_args.append(arg_node) arg_types.append(arg_type) method = None try: if not (node.at_type is None): at_type = node.at_type.lex node_at_type = self.context.get_type(node.at_type.lex) method = node_at_type.get_method(node.id.lex) if not typex.conforms_to(node_at_type): node_row, node_col = node.at_type.location # maybe in node.obj self.errors.append( TypeError( node_row, node_col, f"Expression type {typex.name} does not conform to declared static dispatch type {node_at_type.name}." )) return (ErrorType(), cool_type_nodes.CallNode(node.id.lex, new_args, obj_exp, at_type, typex, ErrorType())) else: at_type = None method = typex.get_method(node.id.lex) except SError as error: node_col, node_row = node.token.location self.errors.append(AttributeError(node_col, node_row, error.text)) return (ErrorType(), cool_type_nodes.CallNode(node.id.lex, new_args, obj_exp, at_type, typex, ErrorType())) if len(method.param_names) != len(node.args): node_row, node_col = node.id.location self.errors.append( SemanticError( node_row, node_col, f"There is no definition of {method.name} that takes {len(node.args)} arguments " )) n_method_args = len(method.param_names) for i, arg in enumerate(node.args): if n_method_args == i: break arg_type = arg_types[i] ptype = method.param_types[i] if not arg_type.conforms_to(ptype): node_row, node_col = arg.token.location self.errors.append( TypeError( node_row, node_col, f"In call of method {node.id.lex} parameter of type {arg_type.name} does not conforms to declared type {ptype.name}" )) if method.return_type == self.context.get_type("SELF_TYPE"): return (typex, cool_type_nodes.CallNode(node.id.lex, new_args, obj_exp, at_type, typex, typex)) return (method.return_type, cool_type_nodes.CallNode(node.id.lex, new_args, obj_exp, at_type, typex, method.return_type))
def visit(self, node, scope): self.current_method = self.current_type.get_method(node.id.lex) method_return_type = self.current_method.return_type if method_return_type.name == "SELF_TYPE": method_return_type = self.current_type child_scope = scope.create_child() # ------------parameters most have differente names------------ param_names = self.current_method.param_names param_types = self.current_method.param_types param_used = {} new_params = [] for i, param_name in enumerate(param_names): param_n, param_t = node.params[i] new_params.append((param_n.lex, param_t.lex)) if param_name == "self": node_row, node_col = param_n.location # location of param name self.errors.append( SemanticError( node_row, node_col, f"'self' cannot be the name of a formal parameter.")) try: param_used[param_name] node_row, node_col = param_n.location self.errors.append( SemanticError( node_row, node_col, f"Formal parameter '{param_name}' multiply defined in method '{node.id.lex}'" )) except: param_used[param_name] = True child_scope.define_variable(param_name, param_types[i]) # ------------------------------------------------------------- body_type, body_exp = self.visit(node.body, child_scope) if not body_type.conforms_to(method_return_type): node_row, node_col = node.body.token.location self.errors.append( TypeError( node_row, node_col, f"Inferred return type '{body_type.name}' of method '{node.id.lex}' (the type of the last expression) does not conform to declared return type '{method_return_type.name}'." )) if self.current_type.parent is not None: try: parent_method = self.current_type.parent.get_method( self.current_method.name) # ensure same return type of redefined method if parent_method.return_type != self.current_method.return_type: node_row, node_col = node.type.location self.errors.append( SemanticError( node_row, node_col, f"In redefined method '{node.id.lex}', return type {self.current_method.return_type.name} is different from original return type {parent_method.return_type.name}." )) # redefined method most have same number of parameters if len(parent_method.param_names) != len( self.current_method.param_names): node_row, node_col = node.id.location self.errors.append( SemanticError( node_row, node_col, f"Incompatible number of formal parameters in redefined method '{node.id.lex}'." )) len_parent_params = len(parent_method.param_names) len_current_params = len(self.current_method.param_names) if len_current_params >= len_parent_params: max_len = len_parent_params else: max_len = len_current_params else: max_len = len(parent_method.param_names) # check that each param has the same type as in the original method for i in range(0, max_len): if self.current_method.param_types[ i] != parent_method.param_types[i]: param_i_name, param_i_type = node.params[i] node_row, node_col = param_i_name.location self.errors.append( SemanticError( node_row, node_col, f"In redefined method '{node.id.lex}', type {self.current_method.param_types[i].name} of parameter {param_i_name.lex} is different from original type {parent_method.param_types[i].name}." )) except SError: pass # parent has no method named like this return cool_type_nodes.FuncDeclarationNode(node.id.lex, new_params, node.type.lex, body_exp)