def visit(self, node: ClassDeclarationNode): self.current_type = self.context.get_type(node.id) # check parent if node.parent is not None: if node.parent in ('SELF_TYPE', 'String', 'Int', 'Bool', node.id): self.current_type.set_parent(ErrorType()) self.errors.append(err.CANNOT_INHERIT % (node.parent_pos, node.parent)) else: try: parent_type = self.context.get_type(node.parent) except SemanticError: # the parent type is not defined parent_type = ErrorType() self.errors.append(err.UNDEFINED_TYPE % (node.parent_pos, node.parent)) try: self.current_type.set_parent(parent_type) except SemanticError: # this node already has a parent self.errors.append(err.CANNOT_INHERIT % (node.pos, node.id, node.parent)) else: try: self.current_type.set_parent(self.context.get_type('Object')) except SemanticError: pass # visit features for feature in node.features: self.visit(feature)
def visit(self, node: InstantiateNode, _: Scope): try: return self.context.get_type( node.lex) if node.lex != 'SELF_TYPE' else self.current_type except SemanticError: self.errors.append(err.UNDEFINED_TYPE % (node.pos, node.lex)) return ErrorType()
def visit(self, node: FuncDeclarationNode, scope: Scope): self.current_method = self.current_type.get_method(node.id) # Check method override try: method, method_owner = self.current_type.parent.get_method( node.id, get_owner=True) if method != self.current_method: self.errors.append(err.WRONG_SIGNATURE % (node.pos, node.id, method_owner.name)) except SemanticError: pass scope.define_variable('self', self.current_type, is_param=True) for param_node in node.params: self.visit(param_node, scope) try: ret_type = self.context.get_type( node.return_type ) if node.return_type != 'SELF_TYPE' else self.current_type except SemanticError: # this error is logged by type builder # self.errors.append(err.UNDEFINED_TYPE % (node.pos, node.return_type)) ret_type = ErrorType() expr_type = self.visit(node.body, scope) if not expr_type.conforms_to(ret_type): self.errors.append(err.INCOMPATIBLE_TYPES % (node.pos, expr_type.name, ret_type.name))
def visit(self, node: AttrDeclarationNode, scope: Scope): # Check attribute override try: attr = self.current_type.parent.get_attribute( node.id, self.current_type.name) self.errors.append(err.ATTRIBUTE_DEFINED_IN_PARENT % (node.pos, attr.name)) except SemanticError: pass if node.id == 'self': self.errors.append(err.SELF_INVALID_ID % (node.pos, )) try: attr_type = self.context.get_type( node.type) if node.type != 'SELF_TYPE' else self.current_type except SemanticError: attr_type = ErrorType() self.errors.append(err.UNDEFINED_TYPE % (node.type_pos, node.type)) if node.expr is not None: expr_type = self.visit(node.expr, scope) if not expr_type.conforms_to(attr_type): self.errors.append( err.INCOMPATIBLE_TYPES % (node.expr_pos, expr_type.name, attr_type.name)) self.current_type.get_attribute(node.id).scope = scope
def visit(self, node: AttrDeclarationNode): try: attr_type = self.context.get_type(node.type) except SemanticError: attr_type = ErrorType() self.errors.append(err.UNDEFINED_TYPE % (node.pos, node.type)) # add a default initialization expr to the node if it doesn't have one if node.expr is None: if attr_type == self.context.get_type('Int'): node.expr = IntegerNode('0') elif attr_type == self.context.get_type('String'): node.expr = StringNode('""') elif attr_type == self.context.get_type('Bool'): node.expr = BooleanNode('false') else: node.expr = VariableNode('void') try: self.current_type.define_attribute( node.id, attr_type, node.expr or VariableNode('void'), self.current_type.name) except SemanticError: self.errors.append(err.ATTRIBUTE_ALREADY_DEFINED % (node.pos, node.id, self.current_type.name))
def visit(self, node: CallNode, scope: Scope): if node.obj is None: obj_type = self.current_type else: obj_type = self.visit(node.obj, scope) try: _, owner = obj_type.get_method(node.id, get_owner=True) node.update_obj_dynamic_type(owner.name) except SemanticError: pass if node.type is not None: try: anc_type = self.context.get_type(node.type) except SemanticError: anc_type = ErrorType() self.errors.append(err.UNDEFINED_TYPE % (node.parent_pos, node.type)) if not obj_type.conforms_to(anc_type): self.errors.append(err.INVALID_ANCESTOR % (node.pos, obj_type.name, anc_type.name)) else: anc_type = obj_type try: method = anc_type.get_method(node.id) except SemanticError: self.errors.append(err.UNDEFINED_METHOD % (node.pos, node.id, anc_type.name)) for arg in node.args: self.visit(arg, scope) return ErrorType() if len(node.args) != len(method.param_names): self.errors.append(err.WRONG_SIGNATURE % (node.pos, method.name, obj_type.name)) else: 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.pos, arg_type.name, method.param_types[i].name)) return method.return_type if method.return_type.name != 'SELF_TYPE' else anc_type
def visit(self, node: ParamNode, scope: Scope): if not scope.is_local(node.id): if node.type == 'SELF_TYPE': type_ = ErrorType() self.errors.append(err.SELF_TYPE_INVALID_PARAM_TYPE % (node.type_pos, )) else: try: type_ = self.context.get_type(node.type) except SemanticError: # this error is logged by the type builder # self.errors.append(err.UNDEFINED_TYPE % (node.type_pos, node.type)) type_ = ErrorType() scope.define_variable(node.id, type_, is_param=True) else: self.errors.append(err.LOCAL_ALREADY_DEFINED % (node.pos, node.id, self.current_method.name))
def collect_features(self, node: AttrDeclarationNode, scope: Scope): try: attr_type = self.context.get_type( node.type) if node.type != 'SELF_TYPE' else self.current_type except SemanticError: attr_type = ErrorType() self.errors.append(err.UNDEFINED_TYPE % (node.type_pos, node.type)) scope.define_variable(node.id, attr_type, is_attr=True)
def visit(self, node: ClassDeclarationNode): type_ = self.context.get_type(node.id) try: type_.get_ancestors(node.id) except SemanticError: self.errors.append(err.CYCLIC_INHERITANCE % (node.parent_pos, node.parent)) type_.parent = None type_.set_parent(ErrorType())
def _check_unary_node(self, node: UnaryNode, scope: Scope, oper: str, expected_type: Type): type_ = self.visit(node.expr, scope) if type_ == expected_type: return type_ else: self.errors.append(err.INVALID_UNARY_OPERATOR % (node.pos, oper, type_.name)) return ErrorType()
def visit(self, node: VariableNode, scope: Scope): var = scope.find_variable(node.lex) if var is None: self.errors.append( err.VARIABLE_NOT_DEFINED % (node.pos, node.lex, self.current_method.name if self.current_method is not None else self.current_type.name)) return ErrorType() return var.type
def _check_binary_node(self, node: BinaryNode, scope: Scope, oper: str, ret_type: Type): int_type = self.context.get_type('Int') left_type = self.visit(node.left, scope) right_type = self.visit(node.right, scope) if left_type == right_type == int_type: return ret_type else: self.errors.append( err.INVALID_BINARY_OPERATOR % (node.pos, oper, left_type.name, right_type.name)) return ErrorType()
def visit(self, node: CaseBranchNode, scope: Scope): child_scope = scope.create_child('_case_branch') try: id_type = self.context.get_type(node.type) except SemanticError: self.errors.append(err.UNDEFINED_TYPE % (node.type_pos, node.type)) id_type = ErrorType() if node.id == 'self': self.errors.append(err.SELF_INVALID_ID % (node.pos, )) else: child_scope.define_variable(node.id, id_type) return self.visit(node.expr, child_scope)
def visit(self, node: FuncDeclarationNode): param_names = [] param_types = [] for param_node in node.params: param_names.append(param_node.id) try: param_types.append(self.context.get_type(param_node.type)) except SemanticError: param_types.append(ErrorType()) self.errors.append(err.UNDEFINED_TYPE % (param_node.type_pos, param_node.type)) try: ret_type = self.context.get_type(node.return_type) except SemanticError: self.errors.append(err.UNDEFINED_TYPE % (node.pos, node.return_type)) ret_type = ErrorType() try: self.current_type.define_method(node.id, param_names, param_types, ret_type) except SemanticError: self.errors.append(err.METHOD_ALREADY_DEFINED % (node.pos, node.id, self.current_type.name))
def visit(self, node: LetDeclarationNode, scope: Scope): try: type_ = self.context.get_type( node.type) if node.type != 'SELF_TYPE' else self.current_type except SemanticError: self.errors.append(err.UNDEFINED_TYPE % (node.type_pos, node.type)) type_ = ErrorType() if node.id == 'self': self.errors.append(err.SELF_INVALID_ID % (node.pos, )) else: scope.define_variable(node.id, type_) expr_type: Type = self.visit(node.expr, scope) if node.expr is not None else None if expr_type is not None and not expr_type.conforms_to(type_): self.errors.append(err.INCOMPATIBLE_TYPES % (node.expr_pos, expr_type.name, type_.name)) return type_
def visit(self, node: ast.ProgramNode): self.context = Context() # Default types definition self.context.types['<error>'] = ErrorType() void = self.context.types['Void'] = VoidType() self_ = self.context.create_type('SELF_TYPE') object_ = self.context.create_type('Object') io = self.context.create_type('IO') string = self.context.create_type('String') int_ = self.context.types['Int'] = IntType() bool_ = self.context.create_type('Bool') # Default types inheritance void.set_parent(object_) io.set_parent(object_) string.set_parent(object_) int_.set_parent(object_) bool_.set_parent(object_) # Default types attributes object_.define_attribute('void', void, ast.VariableNode('void')) # Default types methods object_.define_method('abort', [], [], object_) object_.define_method('type_name', [], [], string) object_.define_method('copy', [], [], self_) io.define_method('out_string', ['x'], [string], self_) io.define_method('out_int', ['x'], [int_], self_) io.define_method('in_string', [], [], string) io.define_method('in_int', [], [], int_) string.define_method('length', [], [], int_) string.define_method('concat', ['s'], [string], string) string.define_method('substr', ['i', 'l'], [int_, int_], string) for declaration in node.declarations: self.visit(declaration)
def visit(self, node: BlockNode, scope: Scope): ret_type = ErrorType() for expr in node.expressions: ret_type = self.visit(expr, scope) return ret_type