def visit(self, node, enviroment): child_enviroment = enviroment.create_child_enviroment() child_enviroment.define_symbol('self', 'SELF_TYPE' + self.current_type.name, 0, 0) for formal_param in node.formal_parameter_list: child_enviroment.define_symbol(formal_param.name, formal_param.type_parameter, formal_param.line, formal_param.column) self.visit(node.expression, child_enviroment) if node.return_type_method == 'SELF_TYPE': _type = Type('SELF_TYPE', self.current_type.name) else: # type_builder guarantee that _type exists _type = enviroment.get_type(node.return_type_method) if not enviroment.conforms(node.expression.computed_type, _type): errors.throw_error( errors.TypeError( text= f"Body expression type for method '{node.name}' does not conforms with its return type.", line=node.line, column=node.column)) node.computed_type = node.expression.computed_type
def p_error(self, parse): if parse: errors.throw_error( errors.SyntacticError(text=f"Token error '{parse.value}'.", line=parse.lineno, column=parse.lexpos)) else: errors.throw_error(errors.SyntacticError(text='EOF error.'))
def visit(self, node, enviroment): node_type = enviroment.get_symbol_type(node.name) if node_type is None: errors.throw_error( errors.NameError( text=f"Variable '{node.name}' is not defined.", line=node.line, column=node.column)) node.computed_type = node_type
def detect_cycles(self): #self.build_types_graph() self.visited = [False]*len(self.types_list) for u in range(len(self.types_list)): if not self.visited[u]: if self.dfs_cycles(u): errors.throw_error(errors.SemanticError(text="The inheritance graph contains cycles.", line=0, column=0)) return 0 return 1
def t_error(self, token): """ Error Handling and Reporting Rule. """ token.lexpos = (token.lexpos - self.lexer.line_start) errors.throw_error( errors.LexicographicError(text=f'Illegal character {token.value}', line=token.lineno, column=token.lexpos))
def t_string_newline(self, token): token.lexer.lineno += 1 if not token.lexer.backslashEscaped: errors.throw_error( errors.LexicographicError(text='String \'\\n\' not scaped.', line=token.lineno, column=token.lexpos)) else: token.lexer.backslashEscaped = False token.lexer.string_buf += '\n'
def visit(self, node, enviroment): self.visit(node.expression, enviroment) if node.expression.computed_type.name != "Int": errors.throw_error( errors.TypeError( text= f"The expression for unary operation 'Neg' must have type 'Int'.", line=node.line, column=node.column)) node.computed_type = enviroment.get_type("Int")
def visit(self, node, enviroment): self.visit(node.while_expression, enviroment) self.visit(node.loop_expression, enviroment) if node.while_expression.computed_type.name != "Bool": errors.throw_error( errors.TypeError( text=f"The predicate of a loop must have type 'Bool'.", line=node.line, column=node.column)) node.computed_type = enviroment.get_type("Object")
def visit(self, node, enviroment): self.visit(node.left_expression, enviroment) self.visit(node.right_expression, enviroment) if node.left_expression.computed_type.name != 'Int' or node.right_expression.computed_type.name != 'Int': errors.throw_error( errors.TypeError( text= f"In arithmetic operation type of letf and right expressions must be 'Int'.", line=node.line, column=node.column)) node.computed_type = enviroment.get_type("Int")
def visit(self, node, enviroment): if node.new_type == 'SELF_TYPE': node.computed_type = Type('SELF_TYPE', self.current_type.name) else: _type = enviroment.get_type(node.new_type) if _type is None: errors.throw_error( errors.TypeError( text= f"In new expression type '{node.new_type}' does not exists.", line=node.line, column=node.column)) node.computed_type = _type
def visit(self, node, enviroment): self.visit(node.if_expression, enviroment) self.visit(node.then_expression, enviroment) self.visit(node.else_expression, enviroment) if node.if_expression.computed_type.name != "Bool": errors.throw_error( errors.TypeError(text=f"If expression type must be 'Bool'.", line=node.line, column=node.column)) node.computed_type = enviroment.lca([ node.then_expression.computed_type, node.else_expression.computed_type ])
def t_string_end(self, token): if not token.lexer.backslashEscaped: token.value = token.lexer.string_buf token.type = 'STRING' token.lineno = token.lexer.string_lineno token.lexpos = token.lexer.string_lexpos if len(token.lexer.string_buf) > 1024: errors.throw_error( errors.LexicographicError( text="Strings may be at most 1024 characters long.", line=token.lineno, column=token.lexpos)) token.lexer.pop_state() return token else: token.lexer.string_buf += '"' token.lexer.backslashEscaped = False
def visit(self, node, enviroment): self.visit(node.left_expression, enviroment) self.visit(node.right_expression, enviroment) basic_types = ['Int', 'String', 'Bool'] if node.left_expression.computed_type.name in basic_types \ or node.right_expression.computed_type.name in basic_types: if node.left_expression.computed_type.name != node.right_expression.computed_type.name: errors.throw_error( errors.TypeError( text= f"In an equal operation, both expressions must have the same basic type.", line=node.line, column=node.column)) node.computed_type = enviroment.get_type('Bool')
def visit(self, node, enviroment): if node.instance.name == 'self': errors.throw_error( errors.SemanticError(text=f"It is an error to assign to self.", line=node.line, column=node.column)) self.visit(node.instance, enviroment) self.visit(node.expression, enviroment) if not enviroment.conforms(node.expression.computed_type, node.instance.computed_type): errors.throw_error( errors.TypeError( text= f"In assign expression type does not conforms with variable '{node.instance.name}' declared type.", line=node.line, column=node.column)) node.computed_type = node.expression.computed_type
def visit(self, node): # node.type_attribute can be SELF_TYPE if node.type_attribute == 'SELF_TYPE': attribute_type = Type('SELF_TYPE', self.current_type.name) else: attribute_type = self.enviroment.get_type(node.type_attribute) if attribute_type is not None: if node.name == "self": errors.throw_error( errors.SemanticError( text=f"Name attribute can not be self.", line=node.line, column=node.column)) else: ans = self.current_type.define_attribute( node.name, node.type_attribute, node.line, node.column) if not ans: errors.throw_error( errors.SemanticError( text= f"In class '{self.current_type.name}' attribute '{node.name}' is defined multiple times.", line=node.line, column=node.column)) else: errors.throw_error( errors.TypeError( text= f"The type '{node.type_attribute}' of attribute '{node.name}' is missing.", line=node.line, column=node.column))
def visit(self, node, enviroment): # [Attr-No-Init] node.computed_type = enviroment.get_symbol_type(node.name) # check if attribute has an initialization expression if node.expression is not None: # [Attr-Init] child_enviroment = enviroment.create_child_enviroment() child_enviroment.define_symbol( 'self', 'SELF_TYPE' + self.current_type.name, 0, 0) self.visit(node.expression, child_enviroment) if not enviroment.conforms(node.expression.computed_type, node.computed_type): errors.throw_error( errors.TypeError( text= f"Initialization expression type for attribute '{node.name}' does not conforms with its declared type.", line=node.line, column=node.column)) node.computed_type = node.expression.computed_type
def visit(self, node): self.current_type = self.enviroment.get_type(node.name) parent_type = self.enviroment.get_type(node.parent) if parent_type is None and node.name != "Object": errors.throw_error( errors.TypeError( text= f"In class '{self.current_type.name}' parent type '{node.parent}' is missing.", line=node.line, column=node.column)) if parent_type.name in ['Int', 'String', 'Bool']: errors.throw_error( errors.SemanticError( text= f"In class '{self.current_type.name}' it is an error to inherit from basic class '{node.parent}'.", line=node.line, column=node.column)) for attribute in node.attribute_list: self.visit(attribute) for method in node.method_list: self.visit(method)
def visit(self, node, enviroment): self.visit(node.case_expression, enviroment) variable_types = [] for _branch in node.branch_list: if _branch.name == 'self': errors.throw_error( errors.SemanticError( text= f"It is an error to bind self in a case expression.", line=_branch.line, column=_branch.column)) if _branch.type_branch == 'SELF_TYPE': errors.throw_error( errors.SemanticError( text=f"Branch declared type cannot be 'SELF_TYPE'.", line=_branch.line, column=_branch.column)) _type = enviroment.get_type(_branch.type_branch) if _type is None: errors.throw_error( errors.TypeError( text= f"Type '{_branch.type_branch}' declared for variable '{_branch.name}' is not defined.", line=_branch.line, column=_branch.column)) if _type in variable_types: errors.throw_error( errors.SemanticError( text= f"In case expression all types declared type must be different.", line=node.line, column=node.column)) variable_types.append(_type) static_types = [] for _branch in node.branch_list: child_enviroment = enviroment.create_child_enviroment() child_enviroment.define_symbol(_branch.name, _branch.type_branch, _branch.line, _branch.column) self.visit(_branch.expression, child_enviroment) _branch.computed_type = _branch.expression.computed_type # in a BranchNode computed_type means the computed type of its expression static_types.append(_branch.computed_type) node.computed_type = enviroment.lca(static_types)
def dfs_inheritance_features(self, u, inherited_scope): # scope represent the scope inherited for the current type self.visited[u] = True valid = 1 # take the current type _type = self.types_list[u] # check my attributes and methods first for attr, attr_obj in _type.attribute_dict.items(): if inherited_scope.isdefined_attr(attr): valid = 0 errors.throw_error(errors.SemanticError(text=f"In class '{_type.name}' attribute '{attr}' is redefined.", line=attr_obj.line, column=attr_obj.column)) else: inherited_scope.define_attr(attr, _type.attribute_dict[attr]._type, attr_obj.line, attr_obj.column) # setting inherited attributes to the type for attr, (attr_type, attr_line, attr_column) in inherited_scope.attribute_dict.items(): _type.define_attribute(attr, attr_type, attr_line, attr_column) # working on error for meth in _type.method_dict.values(): arg_list = [attr._type for attr in meth.attribute_list] arg_list.append(meth.return_type) if inherited_scope.isdefined_meth(meth.name): arg_def_method = inherited_scope.get_method(meth.name) if len(arg_def_method) != len(arg_list): valid = 0 errors.throw_error(errors.SemanticError(text=f"In class '{_type.name}' method '{meth.name}' is redefined with a different number of arguments.", line=meth.line, column=meth.column)) else: if arg_def_method[len(arg_def_method)-1] != arg_list[len(arg_list)-1]: valid = 0 errors.throw_error(errors.SemanticError(text=f"In class '{_type.name}' method '{meth.name}' is redefined with a different return type.", line=meth.line, column=meth.column)) for i in range(len(arg_list)-1): if arg_def_method[i] != arg_list[i]: valid = 0 errors.throw_error(errors.SemanticError(text=f"In class '{_type.name}' method '{meth.name}' is redefined with argument at position {i} with different type.", line=meth.line, column=meth.column)) else: inherited_scope.define_meth(meth.name, arg_list) for v in self.inheritance_graph[u]: if not self.visited[v]: valid &= self.dfs_inheritance_features(v, Scope(inherited_scope)) return valid
def check_main(self): _type = self.get_type("Main") if _type is not None: # checking if there is the program has a class Main method = _type.get_method("main") # the method main is not inherited because enviroment is None if method is None: # checking if the a class Main has a method named main errors.throw_error(errors.SemanticError(text="Main class must have a method main.", line=0, column=0)) return 0 if len(method.attribute_list) > 0: # checking if the method main takes no formal parameters errors.throw_error(errors.SemanticError(text="Method main must not take formal parameters.", line=0, column=0)) return 0 return 1 else: errors.throw_error(errors.SemanticError(text="Program must have a class Main.", line=0, column=0)) return 0
def visit(self, node, enviroment): current_enviroment = enviroment for _decl in node.declaration_list: if _decl.name == 'self': errors.throw_error( errors.SemanticError( text= f"It is an error to bind self in a let expression.", line=_decl.line, column=_decl.column)) if _decl._type == 'SELF_TYPE': _type = Type('SELF_TYPE', self.current_type.name) else: _type = current_enviroment.get_type(_decl._type) if _type is None: errors.throw_error( errors.TypeError( text= f"Type '{_decl._type}' declared for variable '{_decl.name}' is not defined.", line=_decl.line, column=_decl.column)) # check if _decl has an init expression if _decl.expression is not None: self.visit(_decl.expression, current_enviroment) if not enviroment.conforms(_decl.expression.computed_type, _type): errors.throw_error( errors.TypeError( text= f"Type of initialization expression for variable '{_decl.name}' does not conform with its declared type '{_decl._type}'.", line=_decl.line, column=_decl.column)) new_child_enviroment = current_enviroment.create_child_enviroment() if _type.name == 'SELF_TYPE': new_child_enviroment.define_symbol(_decl.name, 'SELF_TYPE' + _type.parent, _decl.line, _decl.column) else: new_child_enviroment.define_symbol(_decl.name, _type.name, _decl.line, _decl.column) current_enviroment = new_child_enviroment self.visit(node.expression, current_enviroment) node.computed_type = node.expression.computed_type
def visit(self, node, enviroment): self.visit(node.instance, enviroment) for _expr in node.arguments: self.visit(_expr, enviroment) instance_type = node.instance.computed_type if node.instance.computed_type.name != 'SELF_TYPE' else enviroment.get_type( node.instance.computed_type.parent) meth = instance_type.get_method( node.method, enviroment) # enviroment is used to search for inherited methods if meth is None: errors.throw_error( errors.AttributeError( text= f"Class '{instance_type.name}' does not contain a method named '{node.method}'.", line=node.line, column=node.column)) # check if the number of arguments is correct if len(node.arguments) != len(meth.attribute_list): errors.throw_error( errors.SemanticError( text= f"Dynamic dispatch does not match with the signature of method '{node.method}'.", line=node.line, column=node.column)) # check if each arg type conforms with the formal parameter type in the method signautre for i in range(len(node.arguments)): if not enviroment.conforms( node.arguments[i].computed_type, enviroment.get_type(meth.attribute_list[i]._type)): errors.throw_error( errors.TypeError( text= f"In dynamic dispatch of method '{node.method}' the type of the argument at index {i} does not conforms with the type of the formal parameter in the method signature.", line=node.line, column=node.column)) # The return type of a method always exits, its garanty in the type_builder if meth.return_type == 'SELF_TYPE': node.computed_type = node.instance.computed_type else: node.computed_type = enviroment.get_type(meth.return_type)
def visit(self, node): ans = self.enviroment.create_type(node.name, node.parent) if ans is None: errors.throw_error(errors.SemanticError(text=f"Class '{node.name}' may not be redefined", line=node.line, column=node.column))
def t_string_eof(self, token): errors.throw_error( errors.LexicographicError(text="String can't end with EOF.", line=token.lineno, column=token.lexpos))
def visit(self, node): # node.return_type_method can be SELF_TYPE if node.return_type_method == 'SELF_TYPE': return_type = Type('SELF_TYPE', self.current_type.name) else: return_type = self.enviroment.get_type(node.return_type_method) if return_type is not None: # formal_parameter_list argument_list = [] for parameter in node.formal_parameter_list: if parameter.name == 'self': errors.throw_error( errors.SemanticError( text= f"In method '{node.name}' it is an error to bind self as a formal parameter.", line=node.line, column=node.column)) if parameter.name in argument_list: errors.throw_error( errors.SemanticError( text= f"In method '{node.name}' the argument '{parameter.name}' is defined multiple times.", line=node.line, column=node.column)) argument_list.append(parameter.name) argument_types = [] for parameter in node.formal_parameter_list: if parameter.type_parameter == 'SELF_TYPE': errors.throw_error( errors.TypeError( text= f"In method '{node.name}' the type of argument '{parameter.name}' cannot be SELF_TYPE.", line=node.line, column=node.column)) _type = self.enviroment.get_type(parameter.type_parameter) if _type is not None: argument_types.append(parameter.type_parameter) else: errors.throw_error( errors.TypeError( text= f"The type of the parameter '{parameter.name}' in method '{node.name}' is missing.", line=node.line, column=node.column)) ans = self.current_type.define_method(node.name, node.return_type_method, argument_list, argument_types, node.line, node.column) if not ans: errors.throw_error( errors.SemanticError( text= f"In class '{self.current_type.name}' method '{node.name}' is defined multiple times.", line=node.line, column=node.column)) else: errors.throw_error( errors.TypeError( text= f"In class '{self.current_type.name}' return type of method '{node.name}' is missing.", line=node.line, column=node.column))
def t_comment2_eof(self, token): errors.throw_error( errors.LexicographicError( text="Comment (* ... *) can't end with EOF.", line=token.lineno, column=token.lexpos))
def t_string_error(self, token): token.lexpos = (token.lexpos - self.lexer.line_start) errors.throw_error( errors.LexicographicError(text=f'Illegal character {token.value}', line=token.lineno, column=token.lexpos))
def main(): # TAKE THE INPUT programs = sys.argv[1:] # CHECK IF AT LEAST ONE FILE IS GIVEN if len(programs) == 0: errors.throw_error(errors.CompilerError(text="No file is given to coolc compiler.")) # CHECK IF FILEOUT IS GIVEN if programs[0] == '-o': if len(programs) == 1: errors.throw_error(errors.CompilerError(text="No fileout is given to coolc compiler.")) fileout = programs[1] if not str(fileout).endswith(".asm"): errors.throw_error(errors.CompilerError(text="Fileout must end with .asm extension.")) if len(programs) == 2: errors.throw_error(errors.CompilerError(text="No file is given to coolc compiler.")) programs = programs[2:] else: fileout = programs[0].split(".cl")[0] + ".asm" # Check all programs have the *.cl extension. for program in programs: if not str(program).endswith(".cl"): errors.throw_error(errors.CompilerError(text="Cool program files must end with a .cl extension.")) code = "" # Read all program source codes. for program in programs: try: with open(program, encoding="utf-8") as file: code += file.read() + '\n' except (IOError, FileNotFoundError): errors.throw_error(errors.CompilerError(text=f'File "{program}" was not found.')) except Exception: errors.throw_error(errors.CompilerError(text="An unexpected error occurred!")) print(f"Compiling file '{fileout}'...") # =============================================================== # ==================ANALISIS-LEXICOGRAFICO======================= # =============================================================== from lexicography.lexer_rules import CoolLex # BUILD THE LEXER lexer = CoolLex() lexer.build() # =============================================================== # =============================================================== # =====================ANALISIS-SINTACTICO======================= # =============================================================== from lexicography.grammar_rules import CoolParse # BUILD THE PARSER parser = CoolParse(lexer) parser.build() program_ast = parser.parse(code) # =============================================================== # =============================================================== # ======================ANALISIS-SEMANTICO======================= # =============================================================== from semantic.type_collector import TypeCollectorVisitor from semantic.type_builder import TypeBuilderVisitor from semantic.type_checker import TypeCheckerVisitor # from semantic.ast_types_painter import Painter typeCollector = TypeCollectorVisitor() typeCollector.visit(program_ast) typeBuilder = TypeBuilderVisitor(typeCollector.enviroment) typeBuilder.visit(program_ast) ## CHECK SEMANTIC ERRORS IN THE ENVIROMENT(check_main, cycles and inheritance rules) final_enviroment = typeBuilder.enviroment final_enviroment.build_types_graph() type_checker = TypeCheckerVisitor() type_checker.visit(program_ast, typeBuilder.enviroment) typed_ast = program_ast # ast_painter = Painter() # print(ast_painter.visit(typed_ast, 0)) # =============================================================== # =============================================================== # ========================CODE-GENERATION======================== # =============================================================== # COOL --> CIL from generation.cil.cil_generator import CilGeneratorVisitor # from general.cil_hierarchy import get_formatter cil_code_generator = CilGeneratorVisitor(typed_ast, typeBuilder.enviroment) ast_cil = cil_code_generator.generate_code() # cil_painter = get_formatter() # print(cil_painter(ast_cil)) # CIL --> MIPS from generation.mips.mips_writer import MIPSWriterVisitor from operator import itemgetter types_ids = typeBuilder.enviroment.types_dict hierarchy = [0]*len(types_ids) for _type in typeBuilder.enviroment.types_list[1:]: hierarchy[types_ids[_type.name]] = types_ids[_type.parent] # tag_names = sorted(types_ids.items(), key=itemgetter(1)) ast_cil.typesHierarchy = hierarchy # ast_cil.tag_names = tag_names mips_code_generator = MIPSWriterVisitor(ast_cil, fileout) mips_code_generator.generate_Mips()