def visit(self, node: cool_ast.CaseNode, scope: Scope): self.visit(node.case_expr, scope.create_child()) case_types = [] var_case_types = [] for option in node.options: child_scope = scope.create_child() try: option_type = self.context.get_type(option.type) except SemanticError as error: self.errors.append(str(error)) option_type = ErrorType() if option_type.name == 'SELF_TYPE': option_type = self.current_type self.errors.append(FORBIDDEN_SELF_TYPE % self.current_type.name) child_scope.define_variable(option.id, option_type) typex = self.visit(option.expr, child_scope) if typex.name == 'SELF_TYPE': typex = self.current_type case_types.append(typex) var_case_types.append(option_type) if len(var_case_types) != len(set(var_case_types)): self.errors.append( 'The variables declared on each branch of a case must all have distinct types. ' ) return Type.join_types(*case_types)
def visit(self, node: cool_ast.MethodDeclarationNode, scope: Scope) -> None: function = self.functions.get_function(self.current_type, self.current_method.name) for param_type, param in zip(function.params_types, node.params): scope.define_variable(param.id, param_type) body_type = self.visit(node.body, scope) self.unify(function.return_type, body_type)
def visit(self, node: cool_ast.LoopNode, scope: Scope): condition_type = self.visit(node.condition, scope.create_child()) bool_type = self.context.get_type('Bool') if condition_type != bool_type: self.errors.append(INCOMPATIBLE_TYPES % (condition_type.name, bool_type.name)) self.visit(node.body, scope.create_child()) return self.context.get_type('Object')
def visit(self, node: cool_ast.ConditionalNode, scope: Scope): condition_type = self.visit(node.condition, scope.create_child()) bool_type = self.context.get_type('Bool') if condition_type != bool_type: self.errors.append(INCOMPATIBLE_TYPES % (condition_type.name, bool_type.name)) then_type = self.visit(node.then_body, scope.create_child()) else_type = self.visit(node.else_body, scope.create_child()) return Type.join_types(then_type, else_type)
def visit(self, node: cool_ast.VarDeclarationNode, scope: Scope) -> Type: try: var_type = self.context.get_type(node.typex) except SemanticError: return TypeVariable() if node.typex == "AUTO_TYPE": var_type = TypeVariable() if node.expr is not None: expr_type = self.visit(node.expr, scope) self.unify(var_type, expr_type) scope.define_variable(node.id, var_type) return var_type
def visit(self, node: cool_ast.VariableNode, scope: Scope): if scope.is_defined(node.lex): var = scope.find_variable(node.lex) return var.type try: att = self.current_type.get_attribute(node.lex) if att.type.name == 'SELF_TYPE': return self.current_type return att.type except SemanticError: self.errors.append(VARIABLE_NOT_DEFINED % (node.lex, self.current_type.name)) return ErrorType()
def visit(self, node: cool_ast.AssignNode, scope: Scope): expr_type = self.visit(node.expr, scope) if scope.is_defined(node.id): var_type = scope.find_variable(node.id).type else: try: att = self.current_type.get_attribute(node.id) var_type = att.type except SemanticError: self.errors.append(VARIABLE_NOT_DEFINED % (node.id, self.current_method.name)) var_type = ErrorType() if not expr_type.conforms_to(var_type): self.errors.append(INCOMPATIBLE_TYPES % (expr_type.name, var_type.name)) return expr_type
def visit(self, node: cool_ast.ClassDeclarationNode, scope: Scope) -> None: attrs = [ feature for feature in node.features if isinstance(feature, cool_ast.AttrDeclarationNode) ] methods = [ feature for feature in node.features if isinstance(feature, cool_ast.MethodDeclarationNode) ] scope.define_variable('self', self.current_type) for attr in attrs: self.visit(attr, scope) for method in methods: self.current_method = self.current_type.get_method(method.id) self.visit(method, scope.create_child())
def visit(self, node: cool_ast.VarDeclarationNode, scope: Scope): try: var_type = self.context.get_type(node.typex) except SemanticError as error: self.errors.append(str(error)) var_type = ErrorType() if scope.is_defined(node.id): self.errors.append(LOCAL_ALREADY_DEFINED % (node.id, self.current_method.name)) else: scope.define_variable(node.id, var_type) expr_type = self.visit(node.expr, scope) if not expr_type.conforms_to(var_type): self.errors.append(INCOMPATIBLE_TYPES % (expr_type.name, var_type.name))
def visit(self, node: cool_ast.VariableNode, scope: Scope) -> Type: var_info = scope.find_variable(node.lex) if var_info is not None: return var_info.type var_type = self.attributes.get_attribute(self.current_type, node.lex) if var_type is None: return TypeVariable() return var_type
def visit(self, node: cool_ast.AssignNode, scope: Scope) -> Type: expr_type = self.visit(node.expr, scope) var_info = scope.find_variable(node.id) if var_info is None: var_type = self.attributes.get_attribute(self.current_type, node.id) if var_type is None: return TypeVariable() else: var_type = var_info.type self.unify(var_type, expr_type) return expr_type
def visit(self, node: cool_ast.MethodDeclarationNode, scope: Scope): try: self.current_method = self.current_type.get_method(node.id) except SemanticError as error: self.errors.append(str(error)) return try: parent_method = self.current_type.get_method(node.id) if parent_method.return_type != self.current_method.return_type: self.errors.append(WRONG_SIGNATURE % (node.id, self.current_type.parent.name)) if len(parent_method.param_types) != len( self.current_method.param_types): self.errors.append(WRONG_SIGNATURE % (node.id, self.current_type.parent.name)) else: for i, j in zip(parent_method.param_types, self.current_method.param_types): if i != j: self.errors.append( WRONG_SIGNATURE % (node.id, self.current_type.parent.name)) except SemanticError: pass child_scope = scope.create_child() for param in node.params: try: typex = self.context.get_type(param.typex) except SemanticError as error: self.errors.append(str(error)) return if typex.name == 'SELF_TYPE': self.errors.append(FORBIDDEN_SELF_TYPE % self.current_type.name) child_scope.define_variable(param.id, typex) expr_type = self.context.get_type('Void') return_type = self.current_method.return_type if return_type.name == 'SELF_TYPE': return_type = self.current_type if node.body is not None: expr_type = self.visit(node.body, child_scope) if self.current_method.return_type != self.context.get_type('Void') \ and not expr_type.conforms_to(return_type): self.errors.append( INCOMPATIBLE_TYPES % (expr_type.name, self.current_method.return_type.name))
def visit(self, node: cool_ast.VarDeclarationNode, scope: Scope, index: int) -> int: if node.typex == 'AUTO_TYPE': var_info = scope.find_variable(node.id) if isinstance(var_info.type, TypeVariable): try: node.typex = self.subst[var_info.type.name].name self.change = True except KeyError: pass else: node.typex = var_info.type.name if node.expr is not None: index = self.visit(node.expr, scope, index) return index
def visit(self, node: cool_ast.MethodCallNode, scope: Scope): object_type = typex = self.current_type if node.expr is not None: object_type = self.visit(node.expr, scope) if object_type.name == 'SELF_TYPE': object_type = self.current_type if node.type is not None: try: node_type = self.context.get_type(node.type) except SemanticError as error: self.errors.append(str(error)) return ErrorType() if node_type.name == 'SELF_TYPE': node_type = object_type if object_type.conforms_to(node_type): object_type = node_type else: self.errors.append(f'Invalid method call') elif node.type is not None: self.errors.append('Invalid method call') try: typex = object_type if object_type.name == 'SELF_TYPE': typex = self.current_type method = typex.get_method(node.id) return_type = method.return_type if return_type.name == 'SELF_TYPE': # and object_type.name != 'SELF_TYPE': return_type = object_type if len(method.param_types) != len(node.args): self.errors.append(UNEXPECTED_NUMBER_OF_ARGUMENT % (self.current_type.name, node.id)) arg_types = [] for arg in node.args: arg_types.append(self.visit(arg, scope.create_child())) for arg_type, typex in zip(arg_types, method.param_types): if not arg_type.conforms_to(typex): self.errors.append( f'Incorrect argument type in method {self.current_type.name}.{node.id}:' + INCOMPATIBLE_TYPES % (arg_type.name, typex.name)) except SemanticError: return_type = ErrorType() self.errors.append(METHOD_NOT_DEFINED % (node.id, typex.name)) return return_type
def visit(self, node: cool_ast.ConditionalNode, scope: Scope) -> Type: bool_type = self.context.get_type('Bool') cond_type = self.visit(node.condition, scope) self.unify(bool_type, cond_type) then_type = self.visit(node.then_body, scope.create_child()) else_type = self.visit(node.else_body, scope.create_child()) if isinstance(then_type, TypeVariable) and isinstance( else_type, TypeVariable): return then_type if isinstance(then_type, TypeVariable): self.unify(then_type, else_type) return else_type if isinstance(else_type, TypeVariable): self.unify(else_type, then_type) return then_type return Type.join_types(then_type, else_type)
def visit(self, node: cool_ast.CaseNode, scope: Scope) -> Type: self.visit(node.case_expr, scope) types = [] for option in node.options: child_scope = scope.create_child() try: option_type = self.context.get_type(option.type) except SemanticError: option_type = TypeVariable() if option_type.name == 'AUTO_TYPE': option_type = TypeVariable() child_scope.define_variable(option.id, option_type) option_expr_type = self.visit(option.expr, child_scope) if option_expr_type.name == 'SELF_TYPE': option_expr_type = self.current_type types.append(option_expr_type) if all(isinstance(typex, TypeVariable) for typex in types): return types[0] known_types = [] unknown_types = [] for case_type in types: if isinstance(case_type, TypeVariable): unknown_types.append(case_type) else: known_types.append(case_type) typex = Type.join_types(*known_types) for case_type in unknown_types: self.unify(case_type, typex) return typex
def visit(self, node: cool_ast.LetNode, scope: Scope): child_scope = scope.create_child() for var in node.var_decl_list: try: var_type = self.context.get_type(var.typex) except SemanticError as error: self.errors.append(str(error)) var_type = ErrorType() if child_scope.is_local(var.id): self.errors.append(LOCAL_ALREADY_DEFINED % (var.id, self.current_method.name)) else: child_scope.define_variable(var.id, var_type) if var.expr is not None: var_expr_type = self.visit(var.expr, child_scope) if not var_expr_type.conforms_to(var_type): self.errors.append(INCOMPATIBLE_TYPES % (var_expr_type.name, var_type.name)) return_type = self.context.get_type('Void') if node.in_expr is not None: return_type = self.visit(node.in_expr, child_scope) return return_type
parse, operations = parser(tokens, get_shift_reduce=True) if show_parsing: st.markdown('### Parsing') st.write([str(prod) + '\n' for prod in reversed(parse)]) ast = evaluate_reverse_parse(parse, operations, tokens) except ParsingException as syntax_error: ast = None st.error(f"{syntax_error}") if ast is not None: formatter = FormatVisitor() errors = [] context = Context() scope = Scope() collector = TypeCollector(context, errors) collector.visit(ast) builder = TypeBuilder(context, errors) builder.visit(ast) if show_ast: st.markdown('### Abstract Syntax Tree') st.text(formatter.visit(ast, 1)) if show_context: st.markdown('### Context') st.text(builder.context) # Types Inference #######################################################
def visit(self, node: cool_ast.BlocksNode, scope: Scope) -> Type: child_scope = scope.create_child() expr_type = self.context.get_type('Void') for expr in node.expr_list: expr_type = self.visit(expr, child_scope) return expr_type
def visit(self, node: cool_ast.LoopNode, scope: Scope) -> Type: cond_type = self.visit(node.condition, scope) body_type = self.visit(node.body, scope.create_child()) self.unify(self.context.get_type('Bool'), cond_type) return self.context.get_type('Object')
def visit(self, node: cool_ast.ClassDeclarationNode, scope: Scope): self.current_type: Type = self.context.get_type(node.id) scope.define_variable('self', self.current_type) for feature in node.features: self.visit(feature, scope)
def visit(self, node: cool_ast.BlocksNode, scope: Scope): return_type = self.context.get_type('Void') for expr in node.expr_list: return_type = self.visit(expr, scope.create_child()) return return_type
def visit(self, node: cool_ast.ProgramNode, scope: Scope) -> None: for class_decl in node.declarations: self.current_type = self.context.get_type(class_decl.id) self.visit(class_decl, scope.create_child())
def visit(self, node: cool_ast.ProgramNode, scope: Scope = None): if scope is None: scope = Scope() for declaration in node.declarations: self.visit(declaration, scope.create_child()) return scope
def visit(self, node: cool_ast.AttrDeclarationNode, scope: Scope) -> None: if node.expression is not None: expr_type = self.visit(node.expression, scope.create_child()) attr_type = self.attributes.get_attribute(self.current_type, node.id) self.unify(attr_type, expr_type)
def visit(self, node: cool_ast.LetNode, scope: Scope): child_scope = scope.create_child() for var in node.var_decl_list: var_type = self.visit(var, child_scope) body_type = self.visit(node.in_expr, child_scope) return body_type