diff --git a/.gitignore b/.gitignore index 4acafde1..4726e6e2 100644 --- a/.gitignore +++ b/.gitignore @@ -408,3 +408,8 @@ dmypy.json # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) +.idea +.vscode +*.cil +*.mips +/src/lexer_parser/parsetab.py \ No newline at end of file diff --git a/Informe Compilador.pdf b/Informe Compilador.pdf new file mode 100644 index 00000000..43ca8431 Binary files /dev/null and b/Informe Compilador.pdf differ diff --git a/doc/Readme.md b/doc/Readme.md index 402477c8..46b4fbf6 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -4,9 +4,9 @@ **Nombre** | **Grupo** | **Github** --|--|-- -Nombre1 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) -Nombre2 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) -Nombre3 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) +Isabella Maria Sierra Ponce | C412 | [@TuquiSierra](https://github.com/TuquiSierra) +Adrian Tubal Paez Ruiz | C412 | [@stdevAdrianPaez](https://github.com/stdevAdrianPaez) +Eric Martin Garcia | C411 | [@ericmg97](https://github.com/ericmg97) ## Readme diff --git a/requirements.txt b/requirements.txt index 9eb0cad1..cba16ee2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pytest pytest-ordering +ply diff --git a/src/code_generation/CIL/__init__.py b/src/code_generation/CIL/__init__.py new file mode 100644 index 00000000..92306e29 --- /dev/null +++ b/src/code_generation/CIL/__init__.py @@ -0,0 +1,7 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .cil import ast_to_cil diff --git a/src/code_generation/CIL/ast.py b/src/code_generation/CIL/ast.py new file mode 100644 index 00000000..e5f547a8 --- /dev/null +++ b/src/code_generation/CIL/ast.py @@ -0,0 +1,471 @@ +class Node: + pass + + +class ProgramNode(Node): + def __init__(self, types, data, code, built_in_code): + self.types = types + self.data = data + self.code = code + self.built_in_code = built_in_code + + def __str__(self): + type_code = '' + data_code = '' + func_code = '' + for t in self.types: + if t.type == 'SELF_TYPE': + continue + type_code += f'{str(t)}\n' + for d in self.data: + data_code += f'{str(d)}\n' + for f in self.built_in_code: + func_code += f'{str(f)}\n' + for f in self.code: + func_code += f'{str(f)}\n' + + return f'.TYPES \n\n{type_code}\n.DATA\n\n{data_code}\n.CODE\n\n{func_code}\n' + + +class TypeNode(Node): + def __init__(self, type): + self.attributes = [] + self.methods = {} + self.type = type + + def __str__(self): + attr_code = '' + method_code = '' + for attr in self.attributes: + attr_code += f'\tattribute {attr};\n' + + for name in self.methods: + method_code += f'\tmethod {name}:{self.methods[name]}_{name};\n' + + return f'type {self.type} {{\n{attr_code}{method_code}}}\n' + + +class DataNode(Node): + def __init__(self, id, val): + self.id = id + self.val = val + + def __str__(self): + return f'{self.id} = \"{self.val}\" ;' + + +class FuncNode(Node): + def __init__(self, name, params, locals, body): + self.name = name + self.params = params + self.locals = locals + self.body = body + + def __str__(self): + params_code = '' + locals_code = '' + body_code = '' + for param in self.params: + params_code += f'\t{str(param)}\n' + + for local in self.locals: + locals_code += f'\tLOCAL {local} ;\n' + + for instruction in self.body: + body_code += f'\t{str(instruction)}\n' + + return f'function {self.name} {{\n{params_code}{locals_code}{body_code}}}\n' + + +class InstructionNode(Node): + def __init__(self): + self.locals = [] + + def check_local(self, var): + if type(var) is LocalNode: + self.locals.append(var) + + +class LocalNode(Node): + def __init__(self, id): + self.id = id + + def __str__(self): + return self.id + + def __repr__(self): + return self.id + + +class ParamNode(Node): + def __init__(self, id): + self.id = id + + def __str__(self): + return f'PARAM {self.id} ;' + + +class AssignNode(InstructionNode): + def __init__(self, result, val): + super().__init__() + self.result = result + self.val = val + self.check_local(result) + self.check_local(val) + + def __str__(self): + return f'{self.result} = {self.val} ;' + + +class ArithNode(InstructionNode): + def __init__(self, left, right, result): + super().__init__() + self.left = left + self.right = right + self.result = result + self.check_local(left) + self.check_local(right) + self.check_local(result) + + +class PlusNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} + {self.right} ;' + + +class MinusNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} - {self.right} ;' + + +class StarNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} * {self.right} ;' + + +class DivNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} / {self.right} ;' + + +class LessEqNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} <= {self.right} ;' + + +class LessNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} < {self.right} ;' + +class EqNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} == {self.right} ;' + +class EqStringNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} == {self.right} ;' + +class NotEqNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} != {self.right} ;' + +class NotEqInstanceNode(ArithNode): + def __str__(self): + return f'{self.result} = {self.left} != {self.right} ;' + + +class NotNode(InstructionNode): + def __init__(self, value, result): + super().__init__() + self.value = value + self.result = result + self.check_local(value) + self.check_local(result) + + def __str__(self): + return f'{self.result} = ~ {self.value}' + + +class GetAttrNode(InstructionNode): + def __init__(self, obj, attr, result, attr_index=0): + super().__init__() + self.attr = attr + self.obj = obj + self.result = result + self.attr_index = attr_index + self.check_local(obj) + self.check_local(result) + + def __str__(self): + return f'{self.result} = GETATTR {self.obj} {self.attr} ;' + + +class SetAttrNode(InstructionNode): + def __init__(self, obj, attr, val, attr_index=0): + super().__init__() + self.val = val + self.obj = obj + self.attr = attr + self.attr_index = attr_index + self.check_local(obj) + self.check_local(val) + + def __str__(self): + return f'SETATTR {self.obj} {self.attr} {self.val} ;' + + +class SetIndexNode(InstructionNode): + def __init__(self, array, index, val): + self.val = val + self.array = array + self.index = index + + def __str__(self): + return f'SETINDEX {self.array} {self.index} {self.val} ;' + + +class GetIndexNode(InstructionNode): + def __init__(self, array, index, result): + self.result = result + self.array = array + self.index = index + + def __str__(self): + return f'{self.result} = GETINDEX {self.array} {self.index} ;' + + +class AllocateNode(InstructionNode): + def __init__(self, _type, result): + super().__init__() + self.type = _type + self.result = result + self.check_local(result) + + def __str__(self): + return f'{self.result} = ALLOCATE {self.type} ;' + + +class AbortNode(InstructionNode): + def __init__(self, type_name: str = None): + self.type_name = type_name + + def __str__(self): + return f'ABORT {self.type_name} ;' + + +class ReadIntNode(InstructionNode): + def __init__(self, result): + self.result = result + + def __str__(self): + return f'{self.result} = READINT ;' + + +class CopyNode(InstructionNode): + def __init__(self, val, result): + self.result = result + self.val = val + + def __str__(self): + return f'{self.result} = COPY {self.val} ;' + + +class TypeOfNode(InstructionNode): + def __init__(self, result, var): + super().__init__() + self.result = result + self.var = var + self.check_local(result) + self.check_local(var) + + def __str__(self): + return f'{self.result} = TYPEOF {self.var} ;' + + +class GetTypeAddrNode(InstructionNode): + def __init__(self, result, var): + super().__init__() + self.result = result + self.var = var + self.check_local(result) + self.check_local(var) + + def __str__(self): + return f'{self.result} = GETTYPEADDR {self.var} ;' + +class GetTypeOrderNode(InstructionNode): + def __init__(self, result, var): + super().__init__() + self.result = result + self.var = var + self.check_local(result) + self.check_local(var) + + def __str__(self): + return f'{self.result} = GETTYPEORDER {self.var} ;' + +class GetTypeMinOrderNode(InstructionNode): + def __init__(self, result, var): + super().__init__() + self.result = result + self.var = var + self.check_local(result) + self.check_local(var) + + def __str__(self): + return f'{self.result} = GETTYPEMINORDER {self.var} ;' + + + + +class ArrayNode(InstructionNode): + def __init__(self, len, result): + self.len = len + self.result = result + + def __str__(self): + return f'{self.result} = ARRAY {self.len} ;' + + +class CallNode(InstructionNode): + def __init__(self, method, result): + self.method = method + self.result = result + + def __str__(self): + return f'{self.result} = CALL {self.method} ;' + + +class VCAllNode(InstructionNode): + def __init__(self, type, method, result): + super().__init__() + self.method = method + self.type = type + self.result = result + self.check_local(result) + self.check_local(type) + + def __str__(self): + return f'{self.result} = VCALL {self.type} {self.method} ;' + + +class ArgNode(InstructionNode): + def __init__(self, val): + super().__init__() + self.val = val + self.check_local(val) + + def __str__(self): + return f'ARG {self.val} ;' + + +class ConditionalGotoNode(InstructionNode): + def __init__(self, predicate, label): + super().__init__() + self.predicate = predicate + self.label = label + self.check_local(predicate) + + def __str__(self): + return f'IF {self.predicate} GOTO {self.label} ;' + + +class GotoNode(InstructionNode): + def __init__(self, label): + super().__init__() + self.label = label + + def __str__(self): + return f'GOTO {self.label} ;' + + +class LabelNode(InstructionNode): + def __init__(self, label_name): + super().__init__() + self.label_name = label_name + + def __str__(self): + return f'LABEL {self.label_name} ;' + + +class ReturnNode(InstructionNode): + def __init__(self, ret_value): + super().__init__() + self.ret_value = ret_value + self.check_local(ret_value) + + def __str__(self): + return f'RETURN {self.ret_value} ;' if self.ret_value else f'RETURN ;' + + +class LoadNode(InstructionNode): + def __init__(self, addr, result): + super().__init__() + self.result = result + self.addr = addr + self.check_local(result) + + def __str__(self): + return f'{self.result} = LOAD {self.addr} ;' + + +class LengthNode(InstructionNode): + def __init__(self, str, result): + self.result = result + self.str = str + + def __str__(self): + return f'{self.result} = LENGTH {self.str} ;' + + +class ConcatNode(InstructionNode): + def __init__(self, str_a, str_b, result): + self.result = result + self.str_a = str_a + self.str_b = str_b + + def __str__(self): + return f'{self.result} = CONCAT {self.str_a} {self.str_b} ;' + + +class SubStringNode(InstructionNode): + def __init__(self, str, i, len, result): + self.result = result + self.i = i + self.len = len + self.str = str + + def __str__(self): + return f'{self.result} = SUBSTRING {self.str} {self.i} {self.len};' + + +class StrNode(InstructionNode): + def __init__(self, val, result): + super().__init__() + self.result = result + self.val = val + self.check_local(val) + self.check_local(result) + + def __str__(self): + return f'{self.result} = STR {self.val} ;' + + +class ReadNode(InstructionNode): + def __init__(self, result): + super().__init__() + self.result = result + self.check_local(result) + + def __str__(self): + return f'{self.result} = READ ;' + + +class PrintNode(InstructionNode): + def __init__(self, str): + super().__init__() + self.str = str + self.check_local(str) + + def __str__(self): + return f'PRINT {self.str} ;' diff --git a/src/code_generation/CIL/cil.py b/src/code_generation/CIL/cil.py new file mode 100644 index 00000000..d316b5f8 --- /dev/null +++ b/src/code_generation/CIL/cil.py @@ -0,0 +1,711 @@ +from . import ast as CilAST +import lexer_parser.ast as CoolAST +import cool_types as CT +from .optimization import optimization_locals, remove_unused_locals + +__DATA__ = {} + + +def add_str_data(data: str): + try: + return __DATA__[data] + except KeyError: + data_count = len(__DATA__) + 1 + __DATA__[data] = f'data_{data_count}' + return __DATA__[data] + + +__LOCALS__ = {} + + +def add_local(id=None): + global __LOCALS__ + if id is None: + id = f'local_{len(__LOCALS__)}' + local = CilAST.LocalNode(id) + __LOCALS__[id] = local + return local + + +labels_count = 0 + + +def add_label(): + global labels_count + labels_count += 1 + return f'label_{labels_count}' + + +def ast_to_cil(ast): + if type(ast) == CoolAST.ProgramNode: + return program_to_cil_visitor(ast) + raise Exception(f'AST root must be program') + + +__DATA_LOCALS__ = {} + + +def add_data_local(string_addr): + try: + return __DATA_LOCALS__[string_addr], False + except KeyError: + local_data = add_local() + __DATA_LOCALS__[string_addr] = local_data + return local_data, True + + +__TYPEOF__ = {} + + +def get_typeof(obj): + try: + return __TYPEOF__[obj], False + except KeyError: + type_local = add_local() + __TYPEOF__[obj] = type_local + return type_local, True + + +__ATTR__ = {} +__CURRENT_TYPE__ = None + + +def program_to_cil_visitor(program): + types = [] + code = [] + built_in_code = [] + + # completing .TYPE section + for t in CT.TypesByName: + _type = CilAST.TypeNode(t) + value = CT.TypesByName[t] + + __ATTR__[t] = [] + for attr in value.get_all_attributes(): + _type.attributes.append(attr.id) + __ATTR__[t].append(attr.id) + + for met in value.get_all_inherited_methods(): + _type.methods[met.id] = met.owner + + for met in value.get_all_self_methods(): + _type.methods[met] = t + + if t not in ('SELF_TYPE', 'Object', 'IO', 'String', 'Bool', 'Int'): + _type.methods['__init__'] = t + + types.append(_type) + + """ + Building main function + """ + main_init = new_to_cil_visitor(CoolAST.NewNode('Main'), 'self') + body = main_init.body + main_result = add_local('main_result') + body.append(CilAST.ArgNode(main_init.value)) + body.append(CilAST.VCAllNode('Main', 'main', main_result)) + body.append(CilAST.ReturnNode(main_result)) + + main_function = CilAST.FuncNode( + 'main', [], [__LOCALS__[k] for k in __LOCALS__.keys()], body) + built_in_code.append(main_function) + + # completing .CODE and .DATA sections + + for c in program.classes: + code.append(init_instance(c.type, c.parent_type)) + for f in c.feature_nodes: + if type(f) == CoolAST.DefFuncNode: + fun = func_to_cil_visitor(c.type, f) + code.append(fun) + + built_in_code += built_in_to_cil() + data = [CilAST.DataNode(__DATA__[data_value], data_value) + for data_value in __DATA__.keys()] + + data.append(CilAST.DataNode('data_abort', 'Abort called from class ')) + cil_program = CilAST.ProgramNode(types, data, code, built_in_code) + # remove_unused_locals(cil_program) + # aqui se esta perdiendo un vcall + # optimization_locals(cil_program) + return cil_program + + +def built_in_to_cil(): + return [out_int_to_cil(), out_string_to_cil(), in_string_to_cil(), in_int_to_cil(), type_name_to_cil(), copy_to_cil(), length_to_cil(), concat_to_cil(), substring_to_cil(), abort_to_cil(), abort_string_to_cil(), abort_int_to_cil(), abort_bool_to_cil(), type_name_bool_to_cil(), type_name_int_to_cil(), type_name_string_to_cil()] + + +def out_string_to_cil(): + return CilAST.FuncNode('IO_out_string', [CilAST.ParamNode('self'), CilAST.ParamNode('str')], [], [CilAST.PrintNode('str'), CilAST.ReturnNode('self')]) + + +def out_int_to_cil(): + return CilAST.FuncNode('IO_out_int', [CilAST.ParamNode('self'), CilAST.ParamNode('int')], [], [CilAST.PrintNode('int'), CilAST.ReturnNode('self')]) + + +def in_string_to_cil(): + _str = CilAST.LocalNode('read_result') + return CilAST.FuncNode('IO_in_string', [CilAST.ParamNode('self')], [_str], [CilAST.ReadNode(_str), CilAST.ReturnNode(_str)]) + + +def in_int_to_cil(): + i = CilAST.LocalNode('int') + return CilAST.FuncNode('IO_in_int', [CilAST.ParamNode('self')], [i], [CilAST.ReadIntNode(i), CilAST.ReturnNode(i)]) + + +def type_name_string_to_cil(): + str_addr = add_str_data('String') + t, need_load = add_data_local(str_addr) + + if need_load: + body = [CilAST.LoadNode(str_addr, t)] + else: + body = [] + + return CilAST.FuncNode('String_type_name', [CilAST.ParamNode('self')], [t], body+[CilAST.ReturnNode(t)]) + + +def type_name_int_to_cil(): + str_addr = add_str_data('Int') + t, need_load = add_data_local(str_addr) + + if need_load: + body = [CilAST.LoadNode(str_addr, t)] + else: + body = [] + return CilAST.FuncNode('Int_type_name', [CilAST.ParamNode('self')], [t], body+[CilAST.ReturnNode(t)]) + + +def type_name_bool_to_cil(): + str_addr = add_str_data('Bool') + t, need_load = add_data_local(str_addr) + + if need_load: + body = [CilAST.LoadNode(str_addr, t)] + else: + body = [] + return CilAST.FuncNode('Bool_type_name', [CilAST.ParamNode('self')], [t], body+[CilAST.ReturnNode(t)]) + + +def type_name_to_cil(): + t = CilAST.LocalNode('type') + return CilAST.FuncNode('Object_type_name', [CilAST.ParamNode('self')], [t], [CilAST.TypeOfNode(t, 'self'), CilAST.ReturnNode(t)]) + + +def copy_to_cil(): + copy = CilAST.LocalNode('copy') + return CilAST.FuncNode('Object_copy', [CilAST.ParamNode('self')], [copy], [CilAST.CopyNode('self', copy), CilAST.ReturnNode(copy)]) + + +def length_to_cil(): + result = CilAST.LocalNode('len_result') + return CilAST.FuncNode('String_length', [CilAST.ParamNode('self')], [result], [CilAST.LengthNode('self', result), CilAST.ReturnNode(result)]) + + +def concat_to_cil(): + result = CilAST.LocalNode('concat_result') + return CilAST.FuncNode('String_concat', [CilAST.ParamNode('self'), CilAST.ParamNode('x')], [result], [CilAST.ConcatNode('self', 'x', result), CilAST.ReturnNode(result)]) + + +def substring_to_cil(): + result = CilAST.LocalNode('substring_result') + return CilAST.FuncNode('String_substr', [CilAST.ParamNode('self'), CilAST.ParamNode('i'), CilAST.ParamNode('l')], [result], [CilAST.SubStringNode('self', 'i', 'l', result), CilAST.ReturnNode(result)]) + + +def abort_to_cil(): + return CilAST.FuncNode('Object_abort', [CilAST.ParamNode('self')], [], [CilAST.AbortNode()]) + + +def abort_string_to_cil(): + return CilAST.FuncNode('String_abort', [CilAST.ParamNode('self')], [], [CilAST.AbortNode('String')]) + + +def abort_bool_to_cil(): + return CilAST.FuncNode('Bool_abort', [CilAST.ParamNode('self')], [], [CilAST.AbortNode('Bool')]) + + +def abort_int_to_cil(): + return CilAST.FuncNode('Int_abort', [CilAST.ParamNode('self')], [], [CilAST.AbortNode('Int')]) + + +def func_to_cil_visitor(type_name, func): + ''' + Converts from FunctionNode in COOL AST to FuncionNode in CIL AST. \n + 1) Builds ParamNodes for each param in FunctionNode.params\n + 2) Builds function body by putting together each instruction's body\n + 3) Creates an array of necessary local variables + + ''' + global __LOCALS__, __DATA_LOCALS__, __TYPEOF__, labels_count, __CURRENT_TYPE__ + name = f'{type_name}_{func.id}' + params = [CilAST.ParamNode('self')] + params += [CilAST.ParamNode(id) for (id, t) in func.params] + __LOCALS__ = {} + __DATA_LOCALS__ = {} + __TYPEOF__ = {} + __CURRENT_TYPE__ = type_name + body = [] + + instruction = expression_to_cil_visitor( + func.expressions) + body += instruction.body + + body.append(CilAST.ReturnNode(instruction.value)) + + _locals = __LOCALS__.copy() + _l_keys = _locals.keys() + for k in _l_keys: + for p in func.params: + if k == p[0]: + __LOCALS__.pop(k) + + return CilAST.FuncNode(name, params, [__LOCALS__[k] for k in __LOCALS__.keys()], body) + + +def expression_to_cil_visitor(expression): + ''' + Selects the appropriate CIL converter for each expression type and calls it. \n + If there is no appropriate CIL converter it throws the exception \'There is no visitor for [type(expression)]\' + ''' + try: + return __visitor__[type(expression)](expression) + except: + raise Exception(f'There is no visitor for {type(expression)}') + + +def order_cases(case_list): + result = [] + while len(case_list): + m = case_list[0] + for branch in case_list[1:]: + if CT.TypesByName[branch.type].order < CT.TypesByName[m.type].order: + m = branch + result.append(m) + case_list.remove(m) + return result + + +def case_to_cil_visitor(case): + ''' + CaseNode CIL converter.\n + 1) Attaches the body of case expression to instruction body.\n + 2) Finds out string repr of case expression dynamic type.\n + 3) For each branch it builts a labeled body in which it is compared branch type to case expression type.\n + 4) Inside the labeled body the branch expression body is attached. + + ''' + body = [] + expr_cil = expression_to_cil_visitor(case.expr) + body += expr_cil.body + t = add_local() + body.append(CilAST.GetTypeOrderNode(t, expr_cil.value)) + t_min = add_local() + + body.append(CilAST.GetTypeMinOrderNode(t_min, expr_cil.value)) + + types = [] + labels = [] + for c in case.case_list: + types.append(c.type) + + for _ in range(len(case.case_list)): + labels.append(add_label()) + + value = add_local() + + l = order_cases(case.case_list) + + for i, branch in enumerate(l): + predicate = add_local() + aux_predicate = add_local() + + order = CT.TypesByName[branch.type].order + min_order = CT.TypesByName[branch.type].min_order + + first_comp_result = add_local() + body.append(CilAST.LessNode(order, t, first_comp_result)) + body.append(CilAST.ConditionalGotoNode(first_comp_result, labels[i])) + + second_comp_result = add_local() + body.append(CilAST.LessNode(t_min, min_order, second_comp_result)) + body.append(CilAST.ConditionalGotoNode(second_comp_result, labels[i])) + + val = add_local(branch.id) + body.append(CilAST.AssignNode(val, expr_cil.value)) + branch_cil = expression_to_cil_visitor( + branch.expr) + body += branch_cil.body + body.append(CilAST.AssignNode(value, branch_cil.value)) + body.append(CilAST.GotoNode(labels[len(l)-1])) + body.append(CilAST.LabelNode(labels[i])) + return CIL_block(body, value) + + +def assign_to_cil_visitor(assign): + ''' + AssignNode CIL converter.\n + 1) Pendiente + ''' + + expr = expression_to_cil_visitor(assign.expr) + if assign.id in __ATTR__[__CURRENT_TYPE__]: + index = __ATTR__[__CURRENT_TYPE__].index(assign.id) + body = expr.body + \ + [CilAST.SetAttrNode('self', assign.id, expr.value, index + 5)] + return CIL_block(body, expr.value) + else: + val = add_local(assign.id) + body = expr.body + [CilAST.AssignNode(val, expr.value)] + return CIL_block(body, val) + + +def arith_to_cil_visitor(arith): + l = expression_to_cil_visitor(arith.lvalue) + r = expression_to_cil_visitor(arith.rvalue) + + cil_result = add_local() + + body = l.body + r.body + + if type(arith) == CoolAST.PlusNode: + body.append(CilAST.PlusNode(l.value, r.value, cil_result)) + elif type(arith) == CoolAST.MinusNode: + body.append(CilAST.MinusNode(l.value, r.value, cil_result)) + elif type(arith) == CoolAST.StarNode: + body.append(CilAST.StarNode(l.value, r.value, cil_result)) + elif type(arith) == CoolAST.DivNode: + body.append(CilAST.DivNode(l.value, r.value, cil_result)) + + return CIL_block(body, cil_result) + + +def if_to_cil_visitor(_if): + predicate = expression_to_cil_visitor( + _if.if_expr) + + then = expression_to_cil_visitor(_if.then_expr) + + else_expression = expression_to_cil_visitor( + _if.else_expr) + + label_1 = add_label() + label_2 = add_label() + value = add_local() + + body = predicate.body + [CilAST.ConditionalGotoNode(predicate.value, label_1)] + else_expression.body + [ + CilAST.AssignNode(value, else_expression.value), CilAST.GotoNode(label_2), CilAST.LabelNode(label_1)] + then.body + [ + CilAST.AssignNode(value, then.value), CilAST.LabelNode(label_2)] + + return CIL_block(body, value) + + +def loop_to_cil_visitor(loop): + predicate = expression_to_cil_visitor(loop.cond) + + loop_block = expression_to_cil_visitor(loop.body) + + value = add_local() + + predicate_label = add_label() + loop_label = add_label() + end_label = add_label() + + body = [CilAST.LabelNode(predicate_label)] + predicate.body + [CilAST.ConditionalGotoNode(predicate.value, loop_label), CilAST.GotoNode(end_label), + CilAST.LabelNode(loop_label)] + loop_block.body + [CilAST.GotoNode(predicate_label), CilAST.LabelNode(end_label), CilAST.AssignNode(value, 0)] + + return CIL_block(body, value) + + +def equal_to_cil_visitor(equal): + l = expression_to_cil_visitor(equal.lvalue) + r = expression_to_cil_visitor(equal.rvalue) + + ret_l = equal.lvalue.returned_type.name + ret_r = equal.rvalue.returned_type.name + + cil_result = add_local() + value = add_local() + + if ret_l == 'String' and ret_r == 'String': + comparison = CilAST.EqStringNode(l.value, r.value, cil_result) + else: + comparison = CilAST.EqNode(l.value, r.value, cil_result) + + body = l.body + r.body + [comparison, + CilAST.AssignNode(value, cil_result)] + + return CIL_block(body, value) + + +def lessthan_to_cil_visitor(lessthan): + l = expression_to_cil_visitor(lessthan.lvalue) + r = expression_to_cil_visitor(lessthan.rvalue) + + value = add_local() + body = l.body + r.body + [CilAST.LessNode(l.value, r.value, value)] + return CIL_block(body, value) + + +def lesseqthan_to_cil_visitor(lessthan): + l = expression_to_cil_visitor(lessthan.lvalue) + r = expression_to_cil_visitor(lessthan.rvalue) + + value = add_local() + body = l.body + r.body + [CilAST.LessEqNode(l.value, r.value, value)] + return CIL_block(body, value) + + +def integer_to_cil_visitor(integer): + return CIL_block([], integer.value) + + +def bool_to_cil_visitor(b: CoolAST.BoolNode): + return CIL_block([], 1) if b.value else CIL_block([], 0) + + +def id_to_cil_visitor(id): + try: + val = __LOCALS__[id.id] + return CIL_block([], val) + except: + if id.id in __ATTR__[__CURRENT_TYPE__]: + result = add_local() + index = __ATTR__[__CURRENT_TYPE__].index(id.id) + return CIL_block([CilAST.GetAttrNode('self', id.id, result, index + 5)], result) + return CIL_block([], id.id) + + +def init_instance(t, parent=None): + global __CURRENT_TYPE__, __LOCALS__, __DATA_LOCALS__, __TYPEOF__ + + __LOCALS__ = {} + __DATA_LOCALS__ = {} + __TYPEOF__ = {} + __CURRENT_TYPE__ = t + body = [] + + if parent and parent not in ('Object', 'IO', 'Int', 'Bool', 'String'): + parent_init = add_local() + body.append(CilAST.ArgNode('self')) + body.append(CilAST.VCAllNode(parent, '__init__', parent_init)) + + all_attr = CT.TypesByName[t].get_all_attributes() + init_attr = CT.TypesByName[t].get_self_attributes() + for index, attr in enumerate(init_attr, len(all_attr)-len(init_attr)+5): + if attr.expression: + attr_cil = expression_to_cil_visitor( + attr.expression) + body += attr_cil.body + body.append(CilAST.SetAttrNode( + 'self', attr.id, attr_cil.value, index)) + elif attr.attrType.name == 'String': + val = add_local() + body.append(CilAST.AllocateNode('String', val)) + body.append(CilAST.SetAttrNode('self', attr.id, val, index)) + elif attr.attrType.name not in ('Bool', 'Int'): + init = new_to_cil_visitor(None, default_type=attr.attrType.name) + body.extend(init.body) + body.append(CilAST.SetAttrNode('self', attr.id, init.value, index)) + else: + body.append(CilAST.SetAttrNode('self', attr.id, 0, index)) + + body.append(CilAST.ReturnNode('self')) + + return CilAST.FuncNode(f'{t}___init__', [CilAST.ParamNode('self')], [__LOCALS__[k] for k in __LOCALS__.keys()], body) + + +def new_to_cil_visitor(new_node, val_id=None, default_type=None): + if val_id: + value = add_local(val_id) + else: + value = add_local() + if default_type: + t = default_type + else: + t = new_node.type + + body = [] + if t == 'SELF_TYPE': + body.append(CilAST.TypeOfNode(t, 'self')) + + body.append(CilAST.AllocateNode(t, value)) + + t_data = add_str_data(t) + t_local = add_local() + size_local = add_local() + order_local = add_local() + min_order_local = add_local() + # + + init_attr = CT.TypesByName[t].get_all_attributes() + + body.append(CilAST.LoadNode(t_data, t_local)) + body.append(CilAST.SetAttrNode(value, '@type', t_local)) + body.append(CilAST.AssignNode(size_local, (len(init_attr)+5)*4)) + body.append(CilAST.SetAttrNode(value, '@size', size_local, 1)) + body.append(CilAST.AssignNode(order_local, CT.TypesByName[t].order)) + body.append(CilAST.SetAttrNode(value, '@order', order_local, 3)) + + body.append(CilAST.AssignNode( + min_order_local, CT.TypesByName[t].min_order)) + body.append(CilAST.SetAttrNode(value, '@min_order', min_order_local, 4)) + + if t not in ('IO', 'Object', 'Int', 'String', 'Bool'): + body.append(CilAST.ArgNode(value)) + allocate_res = add_local() + body.append(CilAST.VCAllNode(t, '__init__', allocate_res)) + return CIL_block(body, allocate_res) + else: + return CIL_block(body, value) + + +def is_void_to_cil_visitor(isvoid): + expr_cil = expression_to_cil_visitor( + isvoid.val) + + body = expr_cil.body + + return CIL_block(body, 1) if not expr_cil.value else CIL_block(body, 0) + + +def string_to_cil_visitor(str): + str_addr = add_str_data(str.value) + str_id, _ = add_data_local(str_addr) + + # if need_load: + body = [CilAST.LoadNode(str_addr, str_id)] + # else: + # body = [] + + return CIL_block(body, str_id) + + +def let_to_cil_visitor(let): + body = [] + for attr in let.let_attrs: + val = add_local(attr.id) + if attr.expr: + attr_cil = expression_to_cil_visitor(attr.expr) + body += attr_cil.body + body.append(CilAST.AssignNode(val, attr_cil.value)) + elif attr.type == 'String': + body.append(CilAST.AllocateNode('String', val)) + elif attr.type not in ('Bool', 'Int'): + init = new_to_cil_visitor(None, default_type=attr.type) + body.extend(init.body) + body.append(CilAST.AssignNode(val, init.value)) + else: + body.append(CilAST.AssignNode(val, 0)) + + expr_cil = expression_to_cil_visitor(let.expr) + body += expr_cil.body + + return CIL_block(body, expr_cil.value) + + +def logic_not_to_cil_visitor(not_node): + expr_cil = expression_to_cil_visitor( + not_node.val) + + value = add_local() + end_label = add_label() + + body = expr_cil.body + [CilAST.AssignNode(value, 0), CilAST.ConditionalGotoNode(expr_cil.value, end_label), + CilAST.AssignNode(value, 1), CilAST.LabelNode(end_label)] + + return CIL_block(body, value) + + +def not_to_cil_visitor(not_node): + expr_cil = expression_to_cil_visitor( + not_node.val) + + value = add_local() + + body = expr_cil.body + [CilAST.NotNode(expr_cil.value, value)] + + return CIL_block(body, value) + + +def block_to_cil_visitor(block): + body = [] + value = None + + for expr in block.expressions: + expr_cil = expression_to_cil_visitor(expr) + body += expr_cil.body + value = expr_cil.value + + return CIL_block(body, value) + + +def func_call_to_cil_visitor(call): + body = [] + t = add_local() + returned = None + if call.object: + obj_cil = expression_to_cil_visitor( + call.object) + body += obj_cil.body + obj = obj_cil.value + returned = call.object.returned_type + if returned and returned.name in ("String", "Int", "Bool"): + call.type = returned.name + else: + body.append(CilAST.GetTypeAddrNode(t, obj)) + else: + obj = 'self' + body.append(CilAST.GetTypeAddrNode(t, obj)) + + arg_values = [] + + for arg in call.args: + arg_cil = expression_to_cil_visitor(arg) + body += arg_cil.body + arg_values.append(arg_cil.value) + + body.append(CilAST.ArgNode(obj)) + + for arg in arg_values: + body.append(CilAST.ArgNode(arg)) + + result = add_local() + + if not call.type: + body.append(CilAST.VCAllNode(t, call.id, result)) + else: + body.append(CilAST.VCAllNode(call.type, call.id, result)) + + return CIL_block(body, result) + + +__visitor__ = { + CoolAST.AssignNode: assign_to_cil_visitor, + CoolAST.BlockNode: block_to_cil_visitor, + CoolAST.BoolNode: bool_to_cil_visitor, + CoolAST.IfNode: if_to_cil_visitor, + CoolAST.WhileNode: loop_to_cil_visitor, + CoolAST.EqNode: equal_to_cil_visitor, + CoolAST.LogicNegationNode: logic_not_to_cil_visitor, + CoolAST.LetNode: let_to_cil_visitor, + CoolAST.NewNode: new_to_cil_visitor, + CoolAST.IntNode: integer_to_cil_visitor, + CoolAST.StringNode: string_to_cil_visitor, + CoolAST.PlusNode: arith_to_cil_visitor, + CoolAST.MinusNode: arith_to_cil_visitor, + CoolAST.StarNode: arith_to_cil_visitor, + CoolAST.DivNode: arith_to_cil_visitor, + CoolAST.VarNode: id_to_cil_visitor, + CoolAST.FuncCallNode: func_call_to_cil_visitor, + CoolAST.IsVoidNode: is_void_to_cil_visitor, + CoolAST.NegationNode: not_to_cil_visitor, + CoolAST.LessThanNode: lessthan_to_cil_visitor, + CoolAST.LessEqNode: lesseqthan_to_cil_visitor, + CoolAST.CaseNode: case_to_cil_visitor, +} + + +class CIL_block: + def __init__(self, body, value): + self.body = body + self.value = value diff --git a/src/code_generation/CIL/optimization.py b/src/code_generation/CIL/optimization.py new file mode 100644 index 00000000..5b2aaa80 --- /dev/null +++ b/src/code_generation/CIL/optimization.py @@ -0,0 +1,84 @@ +from .ast import * + + +def optimization_locals(program: ProgramNode): + # Each local have a start and end index + # Index is the instruction index + for function in program.code: + intervals = {} + unnecessary = [] + for local in function.locals: + # Search initial index of local + start = 0 + for index, instruction in enumerate(function.body): + if local in instruction.locals: + start = index + break + else: + # If local is not in any statement then it is marked as unused + unnecessary.append(local) + continue + # Search end index of local + end = start + for index, instruction in enumerate(function.body[start:]): + if local in instruction.locals: + end = start + index + intervals[local] = (start, end) + + # Build and sort new tuples (start,end,localID) + a = [] + for x in intervals.keys(): + s, e = intervals[x] + a.append((s, e, x)) + # Keep final locals + final_locals = [] + if len(a) != 0: + final_locals.append(a[0]) + for l in a[1:]: + s, e, x = l + for index in range(len(final_locals)): + s2, e2, y = final_locals[index] + if e2 < s: + unnecessary.append(x) + x.id = y.id + final_locals[index] = (s2, e, y) + break + elif e < s2: + unnecessary.append(x) + x.id = y.id + final_locals[index] = (s, e2, y) + break + else: + final_locals.append(l) + # Remove unnnecessary locals + for u in unnecessary: + function.locals.remove(u) + +def remove_unused_locals(program: ProgramNode): + for function in program.code: + used_locals=[] + for instruction in function.body: + for local in instruction.locals: + if local not in used_locals: + try: + if instruction.result.id!=local.id: + used_locals.append(local) + except: + used_locals.append(local) + body=(function.body).copy() + for instruction in function.body: + for local in instruction.locals: + try: + if instruction.result.id==local.id and local not in used_locals and not isinstance(instruction, VCAllNode): + body.remove(instruction) + except: + pass + + function.body=body + function.locals=used_locals + + + + + + \ No newline at end of file diff --git a/src/code_generation/MIPS/__init__.py b/src/code_generation/MIPS/__init__.py new file mode 100644 index 00000000..cf99c9bb --- /dev/null +++ b/src/code_generation/MIPS/__init__.py @@ -0,0 +1,7 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .mips import program_to_mips_visitor diff --git a/src/code_generation/MIPS/ast.py b/src/code_generation/MIPS/ast.py new file mode 100644 index 00000000..ddcc609a --- /dev/null +++ b/src/code_generation/MIPS/ast.py @@ -0,0 +1,471 @@ +import re + + +class MIPSProgram: + def __init__(self, data_section, text_section): + self.data = data_section + self.text = text_section + + def __str__(self): + return f'{self.text}{self.data}' + + +class MIPSTextSection: + def __init__(self, fucntions): + self.fucntions = fucntions + + def __str__(self): + functions_text = '\n'.join([str(f) for f in self.fucntions]) + return f'\n.text\n{functions_text}\n' + + +class MIPSDataSection: + def __init__(self, data): + self.data = data + + def __str__(self): + data_text = '\n'.join([f'\t{str(d)}' for d in self.data]) + return f'\n.data\n{data_text}\n' + + +class MIPSLabel: + def __init__(self, name): + self.name = name + self.registers = set() + + def __str__(self): + return f'{self.name}:\n' + + +class MIPSFunction: + def __init__(self, name, params, locals): + self.name = name + self.init_instructions = [] + self.instructions = [] + self.end_instructions = [] + self.used_regs = set(['ra', 'fp']) + + self.args_count = 0 + self.offset = {} + self.args_code = [] + + + self.init_instructions.append(MoveInstruction('$fp', '$sp')) + + self.params_count = len(params) + + # Save args on the stack + for i, p in enumerate(params): + self.offset[p.id] = (len(params) - i - 1)*4 + + for i, l in enumerate(locals, 1): + self.offset[str(l)] = -i * 4 + + self.init_instructions.append( + SubuInstruction('$sp', '$sp', len(locals)*4)) + + self.end_instructions.append( + AdduInstruction('$sp', '$sp', len(locals)*4)) + if self.name == 'main': + self.end_instructions.append(LiInstruction('$v0', 10)) + self.end_instructions.append(SyscallInstruction()) + else: + self.end_instructions.append(JrInstruction('$ra')) + + def append_instruction(self, instruction): + """ + Add new instruction to function block. For a correct operation, the + instructions must be addes continuisly, in the same order that they + were declared in CIL. + """ + # Update used registers + # self.update_used_reg(instruction.registers) + # Update function instrcutions + self.instructions.append(instruction) + + def update_used_reg(self, registers: set): + self.used_regs.update(registers) + + def __str__(self): + instructions = self.init_instructions + self.instructions + return '\n'.join([f'{self.name}:']+[f'\t{str(i)}' for i in instructions]) + + +class MIPSDataItem: + def __init__(self, label, instruction): + self.label = label + self.instruction = instruction + + def __str__(self): + return f'{self.label}:\n\t\t{str(self.instruction)}' + + +class Instruction: + def __init__(self, name, *arguments): + self.name = name + self.arguments = arguments + self.registers = set() + patterns = [re.compile(r'\$(?P..)'), + re.compile(r'[0-9]+\(\$(?P..)\)'), + re.compile(r'\-[0-9]+\(\$(?P..)\)')] + for a in self.arguments: + for p in patterns: + match = p.match(str(a)) + if match: + reg = match.group('register') + self.registers.add(reg) + break + + def __str__(self): + if len(self.arguments) == 0: + return f"{self.name}" + elif len(self.arguments) == 3: + return f"{'{:10}'.format(self.name)} {self.arguments[0]}, {self.arguments[1]}, {self.arguments[2]}" + elif len(self.arguments) == 2: + return f"{'{:10}'.format(self.name)} {self.arguments[0]}, {self.arguments[1]}" + elif len(self.arguments) == 1: + return f"{'{:10}'.format(self.name)} {self.arguments[0]}" + + +class Comment(Instruction): + def __init__(self, text): + super().__init__('#', (text)) + + +# =============== +# Data Directives +# =============== + +class AsciizInst(Instruction): + def __init__(self, *arguments): + super().__init__(".asciiz", *arguments) + + +class SpaceInst(Instruction): + def __init__(self, *arguments): + super().__init__(".space", *arguments) + + +# ============================= +# Load, Store and Data Movement +# ============================= + +class MoveInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('move', *arguments) + + +class SwInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sw', *arguments) + +class UswInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('usw', *arguments) + +class BreakInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('break', *arguments) + + + + + + +class LwInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('lw', *arguments) + +class UlwInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('ulw', *arguments) + + +class LiInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('li', *arguments) + + +class LaInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('la', *arguments) + + +class LbInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('lb', *arguments) + + +class SbInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sb', *arguments) + +# ======================= +# Arithmetic Instructions +# ======================= + + +class SubInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sub', *arguments) + + +class SubuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('subu', *arguments) + + +class AddInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('add', *arguments) + + +class AdduInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('addu', *arguments) + + +class MulInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('mul', *arguments) + + +class MultInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('mult', *arguments) + + +class MultuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('multu', *arguments) + + +class DivInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('div', *arguments) + + +class DivuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('divu', *arguments) + + +class NegInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('neg', *arguments) + + +class NeguInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('negu', *arguments) + + +class NorInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('nor', *arguments) + + +class NotInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('not', *arguments) + + +class OrInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('or', *arguments) + + +class RemInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('rem', *arguments) + + +class RemuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('remu', *arguments) + + +class RolInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('rol', *arguments) + + +class RorInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('ror', *arguments) + + +class SllInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sll', *arguments) + + +class SraInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sra', *arguments) + + +class SrlInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('srl', *arguments) + + +class XorInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('xor', *arguments) + + +class AbsInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('abs', *arguments) + + +# ======================= +# Comparison Instructions +# ======================= + +class SeqInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('seq', *arguments) + + +class SneInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sne', *arguments) + + +class SgeInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sge', *arguments) + + +class SgeuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sgeu', *arguments) + + +class SgtInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sgt', *arguments) + + +class SgtuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sgtu', *arguments) + + +class SleInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sle', *arguments) + + +class SleuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sleu', *arguments) + + +class SltInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('slt', *arguments) + + +class SltuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('sltu', *arguments) + + +# ============================ +# Branch and Jump Instructions +# ============================ + +class BInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('b', *arguments) + + +class BeqInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('beq', *arguments) + + +class BneInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bne', *arguments) + + +class BgeInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bge', *arguments) + + +class BgeuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bgeu', *arguments) + + +class BgtInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bgt', *arguments) + + +class BgtuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bgtu', *arguments) + + +class BleInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('ble', *arguments) + + +class BleuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bleu', *arguments) + + +class BltInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('blt', *arguments) + + +class BltuInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bltu', *arguments) + + +class BeqzInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('beqz', *arguments) + + +class BnezInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('bnez', *arguments) + + +class JInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('j', *arguments) + + +class JrInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('jr', *arguments) + + +class JalInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('jal', *arguments) + + +class JalrInstruction(Instruction): + def __init__(self, *arguments): + super().__init__('jalr', *arguments) +# ================== +# Exception Handling +# ================== + + +class SyscallInstruction(Instruction): + def __init__(self): + super().__init__('syscall') diff --git a/src/code_generation/MIPS/mips.py b/src/code_generation/MIPS/mips.py new file mode 100644 index 00000000..dd69cedf --- /dev/null +++ b/src/code_generation/MIPS/mips.py @@ -0,0 +1,952 @@ +from code_generation.CIL import ast as cil +from code_generation.MIPS import ast as mips +from .utilities import * + +__BUFFSIZE__ = 1024 +__DATA__ = [] +CURRENT_FUNCTION = None +__VT__ = {} + + +def program_to_mips_visitor(program: cil.ProgramNode): + global __DATA__, __VT__ + + # Build Virtual Table + q = program.types.copy() + t = q.pop(0) + if t.type != 'SELF_TYPE': + raise Exception("unexpected first type") + + while len(q): + t = q.pop(0) + for method, type_impl in t.methods.items(): + if type_impl == t.type: + __VT__[(t.type, method)] = f'{type_impl}_{method}' + else: + try: + __VT__[(t.type, method)] = __VT__[(type_impl, method)] + except KeyError: + q.append(t) + + # Initialize Types + size_vt=init_types(program.types) + + # Build .data section + vt_space_code = reserve_virtual_tables_space(program, size_vt) + __DATA__ = [mips.MIPSDataItem(d.id, mips.AsciizInst(f'"{d.val}"')) + for d in program.data] + __DATA__.append(mips.MIPSDataItem('new_line', mips.AsciizInst(f'"\\n"'))) + __DATA__.extend(vt_space_code) + data_section = mips.MIPSDataSection(__DATA__) + + # Build .text section + functions = [function_to_mips_visitor(f) + for f in program.built_in_code + program.code] + text_section = mips.MIPSTextSection(functions) + return mips.MIPSProgram(data_section, text_section) + +__OFFSET_COUNT__= 0 +__OFFSET__={} + +def get_function_offset(function): + global __OFFSET_COUNT__ + try: + return __OFFSET__[function] + except KeyError: + __OFFSET__[function]=__OFFSET_COUNT__ + __OFFSET_COUNT__+=1 + return __OFFSET__[function] + +def main_instructions(): + instructions=[] + types=get_types() + for t in types.keys(): + __OFFSET_COUNT__=0 + for m in types[t].methods: + instructions.append(mips.LaInstruction('$t0', __VT__[(t, m)])) + instructions.append(mips.UswInstruction('$t0', f'vt_{t}+{get_function_offset(m)*4}')) + return instructions + +def reserve_virtual_tables_space(program: cil.ProgramNode, size_vt): + """ + Each virtual table has a space in the .data section. The + space is 4 bytes for each function, where the address of + the real function is stored. + """ + code = [mips.MIPSDataItem(f'vt_{t.type}', mips.SpaceInst(size_vt)) + for t in program.types[1:]] + + + return code + + + +def function_to_mips_visitor(function): + """ + Convert a CIL function to a block of MIPS code. + 1- Initialize function context + 2- Set CURRENT_FUNCTION for the use of other functions + 3- Add each CIL instruction to function. + 4- Mark as ended the CIL function + """ + global CURRENT_FUNCTION + # 1 + f = mips.MIPSFunction(function.name, function.params, function.locals) + # 2 + + CURRENT_FUNCTION = f + # 3 + if f.name=='main': + for i in main_instructions(): + CURRENT_FUNCTION.append_instruction(i) + for cil_inst in function.body: + for mips_inst in instruction_to_mips_visitor(cil_inst): + CURRENT_FUNCTION.append_instruction(mips_inst) + return f + + +def instruction_to_mips_visitor(inst): + """ + Resolves visitor for each type + """ + try: + return __visitors__[type(inst)](inst) + except KeyError: + print(f'There is no visitor for {type(inst)}') + return [] + + +def print_to_mips_visitor(p: cil.PrintNode): + """ + CIL: + PRINT z; + MIPS if z is str: + lw $a0, shift(z) + li $v0, 4 + syscall + MIPS if z is int: + lw $a0, 4($sp) + li $v0, 4 + syscall + """ + + offset = CURRENT_FUNCTION.offset[str(p.str)] + code = [mips.Comment(str(p)), + mips.LwInstruction('$a0', f'{offset}($fp)')] + if p.str == 'int': + code.append(mips.LiInstruction('$v0', 1)) # li $v0, 1 + elif p.str == 'str': + code.append(mips.LiInstruction('$v0', 4)) # li $v0, 4 + code.append(mips.SyscallInstruction()) # syscall + return code + + +def return_to_mips_visitor(ret: cil.ReturnNode): + """ + CIL: + RETURN x; + MIPS: + lw $v0, shift(x) + """ + code = [mips.Comment(str(ret))] + if isinstance(ret.ret_value, int): + code.append(mips.LiInstruction('$v0', ret.ret_value)) + else: + offset = CURRENT_FUNCTION.offset[str(ret.ret_value)] + code.append(mips.LwInstruction('$v0', f'{offset}($fp)')) + code.extend(CURRENT_FUNCTION.end_instructions) + return code + + +def read_to_mips_visitor(read: cil.ReadNode): + """ + CIL: + x = READ ; + MIPS: + + .data + x: .space 1024 + .text + la $a0, x + li $a1, 1024 + li $v0, 8 + syscall + sw $a0, shift(x) + + + """ + offset = CURRENT_FUNCTION.offset[str(read.result)] + return [ + + mips.LiInstruction('$a0', __BUFFSIZE__), + mips.LiInstruction('$v0', 9), + mips.SyscallInstruction(), + mips.MoveInstruction('$a0', '$v0'), + mips.MoveInstruction('$t3', '$v0'), + + mips.LiInstruction('$a1', __BUFFSIZE__), + mips.LiInstruction('$v0', 8), + mips.SyscallInstruction(), + mips.MIPSLabel('remove_nl_loop'), + mips.LbInstruction('$t0', '($a0)'), + mips.BeqzInstruction('$t0', 'end_loop'), + mips.LaInstruction('$t1', 'new_line'), + mips.LbInstruction('$t2', '($t1)'), + mips.BeqInstruction('$t0', '$t2', 'end_loop'), + mips.AdduInstruction('$a0', '$a0', 1), + mips.BInstruction('remove_nl_loop'), + mips.MIPSLabel('end_loop'), + mips.SbInstruction('$zero', '($a0)'), + mips.SwInstruction('$t3', f'{offset}($fp)') + ] + + +def substring_to_mips_visitor(ss: cil.SubStringNode): + result_offset=CURRENT_FUNCTION.offset[str(ss.result)] + str_offset = CURRENT_FUNCTION.offset[str(ss.str)] + i_offset = CURRENT_FUNCTION.offset[str(ss.i)] + len_offset = CURRENT_FUNCTION.offset[str(ss.len)] + return [ + mips.Comment(str(ss)), + mips.LwInstruction('$t0', f'{str_offset}($fp)'), + + + mips.LwInstruction('$a0', f'{len_offset}($fp)'), + mips.AdduInstruction('$a0', '$a0', 1), + mips.LiInstruction('$v0', 9), + mips.SyscallInstruction(), + mips.MoveInstruction('$t1', '$v0'), + + + mips.LwInstruction('$t4', f'{i_offset}($fp)'), + mips.LwInstruction('$t2', f'{len_offset}($fp)'), + mips.AdduInstruction('$t0', '$t0', '$t4'), + mips.MIPSLabel('substring_loop'), + mips.BeqzInstruction('$t2', 'end_substring_loop'), + mips.LbInstruction('$t3', '($t0)'), + mips.SbInstruction('$t3', '($t1)'), + mips.SubuInstruction('$t2', '$t2', 1), + mips.AdduInstruction('$t0', '$t0', 1), + mips.AdduInstruction('$t1', '$t1', 1), + mips.BInstruction('substring_loop'), + mips.MIPSLabel('end_substring_loop'), + mips.SbInstruction('$zero', '($t1)'), + mips.SwInstruction('$v0', f'{result_offset}($fp)') + ] + + +def read_int_to_mips_visitor(read: cil.ReadIntNode): + addr = CURRENT_FUNCTION.offset[str(read.result)] + code = [ + mips.Comment(str(read)), + mips.LiInstruction('$v0', 5), + mips.SyscallInstruction(), + mips.SwInstruction('$v0', f'{addr}($fp)') + ] + return code + + +def length_to_mips_visitor(length: cil.LengthNode): + val = CURRENT_FUNCTION.offset[str(length.str)] + result_val = CURRENT_FUNCTION.offset[str(length.result)] + + code = [ + mips.Comment(str(length)), + mips.LwInstruction('$t2', f'{val}($fp)'), + mips.LiInstruction('$t1', 0), + mips.MIPSLabel('length_loop'), + mips.LbInstruction('$t0', '($t2)'), + mips.BeqzInstruction('$t0', 'end_length_loop'), + mips.AdduInstruction('$t2', '$t2', 1), + mips.AdduInstruction('$t1', '$t1', 1), + mips.BInstruction('length_loop'), + mips.MIPSLabel('end_length_loop'), + mips.SwInstruction('$t1', f'{result_val}($fp)') + ] + return code + +def concat_to_mips_visitor(concat: cil.ConcatNode): + result_offset = CURRENT_FUNCTION.offset[str(concat.result)] + a_offset = CURRENT_FUNCTION.offset[str(concat.str_a)] + b_offset = CURRENT_FUNCTION.offset[str(concat.str_b)] + + + + + return [ + mips.Comment(str(concat)), + + mips.LwInstruction('$t2', f'{a_offset}($fp)'), + mips.LiInstruction('$t1', 0), + mips.MIPSLabel('concat_a_length_loop'), + mips.LbInstruction('$t0', '($t2)'), + mips.BeqzInstruction('$t0', 'concat_a_end_length_loop'), + mips.AdduInstruction('$t2', '$t2', 1), + mips.AdduInstruction('$t1', '$t1', 1), + mips.BInstruction('concat_a_length_loop'), + mips.MIPSLabel('concat_a_end_length_loop'), + + mips.LwInstruction('$t2', f'{b_offset}($fp)'), + mips.MIPSLabel('concat_b_length_loop'), + mips.LbInstruction('$t0', '($t2)'), + mips.BeqzInstruction('$t0', 'concat_b_end_length_loop'), + mips.AdduInstruction('$t2', '$t2', 1), + mips.AdduInstruction('$t1', '$t1', 1), + mips.BInstruction('concat_b_length_loop'), + mips.MIPSLabel('concat_b_end_length_loop'), + + mips.AdduInstruction('$a0', '$t1', 1), + mips.LiInstruction('$v0', 9), + mips.SyscallInstruction(), + mips.MoveInstruction('$t0', '$v0'), + + mips.LwInstruction('$t1', f'{a_offset}($fp)'), + mips.LwInstruction('$t2', f'{b_offset}($fp)'), + mips.MIPSLabel('concat_loop_a'), + mips.LbInstruction('$a0', '($t1)'), + mips.BeqzInstruction('$a0', 'concat_loop_b'), + mips.SbInstruction('$a0', '($t0)'), + mips.AdduInstruction('$t0', '$t0', 1), + mips.AdduInstruction('$t1', '$t1', 1), + mips.BInstruction('concat_loop_a'), + mips.MIPSLabel('concat_loop_b'), + mips.LbInstruction('$a0', '($t2)'), + mips.BeqzInstruction('$a0', 'end_concat'), + mips.SbInstruction('$a0', '($t0)'), + mips.AdduInstruction('$t0', '$t0', 1), + mips.AdduInstruction('$t2', '$t2', 1), + mips.BInstruction('concat_loop_b'), + mips.MIPSLabel('end_concat'), + mips.SbInstruction('$zero', '($t0)'), + mips.SwInstruction('$v0', f'{result_offset}($fp)') + ] + + +def load_to_mips_visitor(load: cil.LoadNode): + offset = CURRENT_FUNCTION.offset[str(load.result)] + return [ + mips.Comment(str(load)), + mips.LaInstruction('$t0', load.addr), + mips.SwInstruction('$t0', f'{offset}($fp)') + ] + + +def arg_to_mips_visitor(arg: cil.ArgNode): + ''' + Converts an Arg CIL node to a piece of MIPS code:\n + 1) Allocates a 4-bytes space in stack\n + 2) Pushes the arg value in the stack\n + ''' + code = [mips.Comment(str(arg))] + if isinstance(arg.val, int): + code.append(mips.LiInstruction('$t0', arg.val)) + else: + addr = CURRENT_FUNCTION.offset[str(arg.val)] + code.append(mips.LwInstruction('$t0', f'{addr}($fp)')) + CURRENT_FUNCTION.args_code.extend( + code + [mips.SubuInstruction('$sp', '$sp', 4), mips.SwInstruction('$t0', '($sp)')]) + CURRENT_FUNCTION.args_count += 1 + return [] + + +def allocate_to_mips_visitor(allocate: cil.AllocateNode): + """ + CIL: + x = ALLOCATE T + MIPS: + li $a0, [size(T)] + li $v0, 9 + syscall + sw $v0, [addr(x)] + """ + address = CURRENT_FUNCTION.offset[str(allocate.result)] + if allocate.type=='String': + size= __BUFFSIZE__ + code=[ + mips.Comment(str(allocate)), + mips.LiInstruction('$a0', size), + mips.LiInstruction('$v0', 9), + mips.SyscallInstruction(), + mips.SwInstruction('$v0', f'{address}($fp)') + ] + + else: + size = get_type(allocate.type).size_mips + 16 + code = [ + mips.Comment(str(allocate)), + mips.LiInstruction('$a0', size), + mips.LiInstruction('$v0', 9), + mips.SyscallInstruction(), + mips.SwInstruction('$v0', f'{address}($fp)'), + mips.LaInstruction('$t0', f'vt_{allocate.type}'), + mips.SwInstruction('$t0', f'8($v0)') + ] + return code + + +def type_of_to_mips_visitor(typeof: cil.TypeOfNode): + """ + CIL: + t = TYPEOF x + MIPS: + lw $t0, [addr(x)] + sw ($t0), [addr(t)] + """ + x_addr = CURRENT_FUNCTION.offset[str(typeof.var)] + t_addr = CURRENT_FUNCTION.offset[str(typeof.result)] + return [ + mips.Comment(str(typeof)), + mips.LwInstruction('$t0', f'{x_addr}($fp)'), + mips.LwInstruction('$t1', '($t0)'), + mips.SwInstruction('$t1', f'{t_addr}($fp)') + ] + + +def getattr_to_mips_visitor(getattr: cil.GetAttrNode): + """ + CIL: + x = GETATTR y attr + MIPS: + lw $t0, [addr(y)] + lw $t1, [attr_shift($t0)] + sw $t1, [addr(x)] + """ + + x_addr = CURRENT_FUNCTION.offset[str(getattr.result)] + y_addr = CURRENT_FUNCTION.offset[str(getattr.obj)] + attr_shift = getattr.attr_index * 4 + return [ + mips.Comment(str(getattr)), + mips.LwInstruction('$t0', f'{y_addr}($fp)'), + mips.LwInstruction('$t1', f'{attr_shift}($t0)'), + mips.SwInstruction('$t1', f'{x_addr}($fp)') + ] + + +def setattr_to_mips_visitor(setattr: cil.SetAttrNode): + """ + CIL: + SETATTR y attr x + MIPS: + lw $t0, [addr(x)] + lw $t1, [addr(y)] + sw $t0, [attr_shift($t1)] + """ + + code = [mips.Comment(str(setattr))] + if isinstance(setattr.val, int): + code.append(mips.LiInstruction('$t0', setattr.val)) + else: + x_addr = CURRENT_FUNCTION.offset[str(setattr.val)] + code.append(mips.LwInstruction('$t0', f'{x_addr}($fp)')) + + y_addr = CURRENT_FUNCTION.offset[str(setattr.obj)] + attr_shift = setattr.attr_index * 4 + return code + [ + mips.LwInstruction('$t1', f'{y_addr}($fp)'), + mips.SwInstruction('$t0', f'{attr_shift}($t1)') + ] + + +def plus_to_mips_visitor(plus: cil.PlusNode): + """ + CIL: + x = y + z + MIPS: + lw $t1, [addr(y)] + lw $t2, [addr(z)] + add $t0, $t1, $t2 + sw $t0, [addr(x)] + """ + code = [mips.Comment(str(plus))] + if isinstance(plus.left, int): + code.append(mips.LiInstruction('$t0', plus.left)) + else: + x_addr = CURRENT_FUNCTION.offset[str(plus.left)] + code.append(mips.LwInstruction('$t0', f'{x_addr}($fp)')) + + if isinstance(plus.right, int): + code.append(mips.LiInstruction('$t1', plus.right)) + else: + y_addr = CURRENT_FUNCTION.offset[str(plus.right)] + code.append(mips.LwInstruction('$t1', f'{y_addr}($fp)')) + + z_addr = CURRENT_FUNCTION.offset[str(plus.result)] + return code + [mips.AddInstruction('$t0', '$t0', '$t1'), mips.SwInstruction('$t0', f'{z_addr}($fp)')] + + +def minus_to_mips_visitor(minus: cil.MinusNode): + """ + CIL: + x = y - z + MIPS: + lw $t1, [addr(y)] + lw $t2, [addr(z)] + sub $t0, $t1, $t2 + sw $t0, [addr(x)] + """ + code = [mips.Comment(str(minus))] + if isinstance(minus.left, int): + code.append(mips.LiInstruction('$t0', minus.left)) + else: + x_addr = CURRENT_FUNCTION.offset[str(minus.left)] + code.append(mips.LwInstruction('$t0', f'{x_addr}($fp)')) + + if isinstance(minus.right, int): + code.append(mips.LiInstruction('$t1', minus.right)) + else: + y_addr = CURRENT_FUNCTION.offset[str(minus.right)] + code.append(mips.LwInstruction('$t1', f'{y_addr}($fp)')) + + z_addr = CURRENT_FUNCTION.offset[str(minus.result)] + return code + [mips.SubInstruction('$t0', '$t0', '$t1'), mips.SwInstruction('$t0', f'{z_addr}($fp)')] + + +def star_to_mips_visitor(star: cil.StarNode): + """ + CIL: + x = y * z + MIPS: + lw $t1, [addr(y)] + lw $t2, [addr(z)] + mul $t0, $t1, $t2 + sw $t0, [addr(x)] + """ + code = [mips.Comment(str(star))] + if isinstance(star.left, int): + code.append(mips.LiInstruction('$t0', star.left)) + else: + x_addr = CURRENT_FUNCTION.offset[str(star.left)] + code.append(mips.LwInstruction('$t0', f'{x_addr}($fp)')) + + if isinstance(star.right, int): + code.append(mips.LiInstruction('$t1', star.right)) + else: + y_addr = CURRENT_FUNCTION.offset[str(star.right)] + code.append(mips.LwInstruction('$t1', f'{y_addr}($fp)')) + + z_addr = CURRENT_FUNCTION.offset[str(star.result)] + return code + [mips.MulInstruction('$t0', '$t0', '$t1'), mips.SwInstruction('$t0', f'{z_addr}($fp)')] + + +def div_to_mips_visitor(div: cil.DivNode): + """ + CIL: + x = y / z + MIPS: + lw $t1, [addr(y)] + lw $t2, [addr(z)] + div $t0, $t1, $t2 + sw $t0, [addr(x)] + """ + code = [mips.Comment(str(div))] + if isinstance(div.left, int): + code.append(mips.LiInstruction('$t0', div.left)) + else: + x_addr = CURRENT_FUNCTION.offset[str(div.left)] + code.append(mips.LwInstruction('$t0', f'{x_addr}($fp)')) + + if isinstance(div.right, int): + code.append(mips.LiInstruction('$t1', div.right)) + else: + y_addr = CURRENT_FUNCTION.offset[str(div.right)] + code.append(mips.LwInstruction('$t1', f'{y_addr}($fp)')) + + z_addr = CURRENT_FUNCTION.offset[str(div.result)] + return code + [mips.DivInstruction('$t0', '$t0', '$t1'), mips.SwInstruction('$t0', f'{z_addr}($fp)')] + +def eq_to_mips_visitor(eq:cil.EqNode): + instructions = [mips.Comment(str(eq))] + if isinstance(eq.left, int): + instructions.append(mips.LiInstruction('$t0', eq.left)) + else: + y_offset = CURRENT_FUNCTION.offset[str(eq.left)] + instructions.append(mips.LwInstruction('$t0', f'{y_offset}($fp)')) + + if isinstance(eq.right, int): + instructions.append(mips.LiInstruction('$t1', eq.right)) + else: + z_offset = CURRENT_FUNCTION.offset[str(eq.right)] + instructions.append(mips.LwInstruction('$t1', f'{z_offset}($fp)')) + + x_offset = CURRENT_FUNCTION.offset[str(eq.result)] + + return instructions + [ + mips.SeqInstruction('$t0', '$t0', '$t1'), + mips.SwInstruction('$t0', f'{x_offset}($fp)') + ] + +__EQUAL__=0 + +def eq_string_to_mips_visitor(eq:cil.EqNode): + global __EQUAL__ + y_offset = CURRENT_FUNCTION.offset[str(eq.left)] + z_offset = CURRENT_FUNCTION.offset[str(eq.right)] + x_offset = CURRENT_FUNCTION.offset[str(eq.result)] + __EQUAL__+=1 + return [ + mips.Comment(str(eq)), + mips.LwInstruction('$t0', f'{y_offset}($fp)'), + mips.LwInstruction('$t1', f'{z_offset}($fp)'), + mips.LiInstruction('$v0', 1), + mips.SwInstruction('$v0', f'{x_offset}($fp)'), + mips.MIPSLabel(f'equal_loop_{__EQUAL__}'), + mips.LbInstruction('$t2', '($t0)'), + mips.LbInstruction('$t3', '($t1)'), + mips.SeqInstruction('$t4', '$t2', '$t3'), + mips.BeqzInstruction('$t4', f'not_equal_{__EQUAL__}'), + mips.BeqzInstruction('$t2', f'end_loop_{__EQUAL__}'), + mips.AdduInstruction('$t0','$t0', 1), + mips.AdduInstruction('$t1', '$t1', 1), + mips.BInstruction(f'equal_loop_{__EQUAL__}'), + mips.BInstruction(f'end_loop_{__EQUAL__}'), + mips.MIPSLabel(f'not_equal_{__EQUAL__}'), + mips.LiInstruction('$v0', 0), + mips.SwInstruction('$v0', f'{x_offset}($fp)'), + mips.MIPSLabel(f'end_loop_{__EQUAL__}') + ] + + +def not_eq_to_mips_visitor(eq:cil.EqNode): + instructions = [mips.Comment(str(eq))] + if isinstance(eq.left, int): + instructions.append(mips.LiInstruction('$t0', eq.left)) + else: + y_offset = CURRENT_FUNCTION.offset[str(eq.left)] + instructions.append(mips.LwInstruction('$t0', f'{y_offset}($fp)')) + + if isinstance(eq.right, int): + instructions.append(mips.LiInstruction('$t1', eq.right)) + else: + z_offset = CURRENT_FUNCTION.offset[str(eq.right)] + instructions.append(mips.LwInstruction('$t1', f'{z_offset}($fp)')) + + x_offset = CURRENT_FUNCTION.offset[str(eq.result)] + + return instructions + [ + mips.SneInstruction('$t0', '$t0', '$t1'), + mips.SwInstruction('$t0', f'{x_offset}($fp)') + ] + +def not_eq_instance_to_mips_visitor(noteq:cil.NotEqNode): + y_offset = CURRENT_FUNCTION.offset[str(noteq.left)] + z_offset = CURRENT_FUNCTION.offset[str(noteq.right)] + x_offset = CURRENT_FUNCTION.offset[str(noteq.result)] + + return [ + mips.Comment(str(noteq)), + mips.LwInstruction('$t0', f'{y_offset}($fp)'), + mips.LwInstruction('$t1', '($t0)'), + mips.LwInstruction('$t0', f'{z_offset}($fp)'), + mips.LwInstruction('$t2', '($t0)'), + mips.SneInstruction('$t0', '$t1', '$t2'), + mips.SwInstruction('$t0', f'{x_offset}($fp)') + ] + +def lesseq_to_mips_visitor(lesseq: cil.LessEqNode): + """ + CIL: + x = y <= z + MIPS: + lw $t1, [addr(y)] + lw $t2, [addr(z)] + sle $t0, $t1, $t2 + sw $t0, [addr(x)] + """ + instructions = [mips.Comment(str(lesseq))] + if isinstance(lesseq.left, int): + instructions.append(mips.LiInstruction('$t0', lesseq.left)) + else: + y_offset = CURRENT_FUNCTION.offset[str(lesseq.left)] + instructions.append(mips.LwInstruction('$t0', f'{y_offset}($fp)')) + + if isinstance(lesseq.right, int): + instructions.append(mips.LiInstruction('$t1', lesseq.right)) + else: + z_offset = CURRENT_FUNCTION.offset[str(lesseq.right)] + instructions.append(mips.LwInstruction('$t1', f'{z_offset}($fp)')) + + x_offset = CURRENT_FUNCTION.offset[str(lesseq.result)] + + return instructions + [ + mips.SleInstruction('$t0', '$t0', '$t1'), + mips.SwInstruction('$t0', f'{x_offset}($fp)') + ] + + +def less_to_mips_visitor(less: cil.LessNode): + """ + CIL: + x = y < z + MIPS: + lw $t1, [addr(y)] + lw $t2, [addr(z)] + slt $t0, $t1, $t2 + sw $t0, [addr(x)] + """ + instructions = [mips.Comment(str(less))] + if isinstance(less.left, int): + instructions.append(mips.LiInstruction('$t0', less.left)) + else: + y_offset = CURRENT_FUNCTION.offset[str(less.left)] + instructions.append(mips.LwInstruction('$t0', f'{y_offset}($fp)')) + + if isinstance(less.right, int): + instructions.append(mips.LiInstruction('$t1', less.right)) + else: + z_offset = CURRENT_FUNCTION.offset[str(less.right)] + instructions.append(mips.LwInstruction('$t1', f'{z_offset}($fp)')) + + x_offset = CURRENT_FUNCTION.offset[str(less.result)] + + return instructions + [ + mips.SltInstruction('$t0', '$t0', '$t1'), + mips.SwInstruction('$t0', f'{x_offset}($fp)') + ] + + +def not_to_mips_visitor(notn: cil.NotNode): + """ + CIL: + x = ~ y + MIPS: + lw $t1, [addr(y)] + not $t0, $t1 + sw $t0, [addr(x)] + """ + instructions = [mips.Comment(str(notn))] + + if isinstance(notn.value, int): + instructions.append(mips.LiInstruction('$t0', notn.value)) + else: + y_offset = CURRENT_FUNCTION.offset[str(notn.value)] + instructions.append(mips.LwInstruction('$t0', f'{y_offset}($fp)')) + + x_offset = CURRENT_FUNCTION.offset[str(notn.result)] + + return instructions + [ + mips.NegInstruction('$t0', '$t0'), + mips.SwInstruction('$t0', f'{x_offset}($fp)') + ] + + +def vcall_to_mips_visitor(vcall: cil.VCAllNode): + """ + CIL: + result = VCALL [type] [method] + MIPS: + 1 - Save any of the caller-saved registers ($t0 - $t9) which are used by the + caller. + 2 - Execute a jal (or jalr) to jump to the function. + 3 - If any arguments were passed on the stack (instead of in $a0-$a3), pop + them off of the stack. + 4 - Restore the caller-saved registers. + 5 - Extract the return value, if any, from register $v0. + """ + instructions = [] + instructions.append(mips.Comment(str(vcall))) + try: + CURRENT_FUNCTION.used_regs.remove('v0') + except KeyError: + pass + try: + CURRENT_FUNCTION.used_regs.remove('sp') + except KeyError: + pass + save_reg_space = len(CURRENT_FUNCTION.used_regs) * 4 + instructions.append(mips.SubuInstruction('$sp', '$sp', save_reg_space)) + for i, reg in enumerate(CURRENT_FUNCTION.used_regs): + instructions.append(mips.SwInstruction(f'${reg}', f'{i*4}($sp)')) + + instructions.extend(CURRENT_FUNCTION.args_code) + CURRENT_FUNCTION.args_code.clear() + + try: + type_local=CURRENT_FUNCTION.offset[str(vcall.type)] + instructions.append(mips.LwInstruction('$t0', f'{type_local}($fp)')) + instructions.append(mips.UlwInstruction('$t1', f'{__OFFSET__[vcall.method]*4}($t0)')) + instructions.append(mips.JalrInstruction('$t1')) + except KeyError: + instructions.append(mips.JalInstruction(__VT__[(str(vcall.type), str(vcall.method))])) + + instructions.append(mips.AdduInstruction( + '$sp', '$sp', CURRENT_FUNCTION.args_count * 4)) + CURRENT_FUNCTION.args_count = 0 + + for i, reg in enumerate(CURRENT_FUNCTION.used_regs): + instructions.append(mips.LwInstruction(f'${reg}', f'{i*4}($sp)')) + instructions.append(mips.AdduInstruction('$sp', '$sp', save_reg_space)) + + try: + ret_offset = CURRENT_FUNCTION.offset[str(vcall.result)] + instructions.append(mips.SwInstruction('$v0', f'{ret_offset}($fp)')) + except KeyError: + pass + + return instructions + + +def get_type_addr_to_mips_visitor(get_type:cil.GetTypeAddrNode): + x_addr = CURRENT_FUNCTION.offset[str(get_type.var)] + t_addr = CURRENT_FUNCTION.offset[str(get_type.result)] + return [ + mips.Comment(str(get_type)), + mips.LwInstruction('$t1', f'{x_addr}($fp)'), + mips.LwInstruction('$t0', f'8($t1)'), + mips.SwInstruction('$t0', f'{t_addr}($fp)') + ] + +def get_type_order_to_mips_visitor(get_order:cil.GetTypeOrderNode): + x_addr = CURRENT_FUNCTION.offset[str(get_order.var)] + t_addr = CURRENT_FUNCTION.offset[str(get_order.result)] + return [ + mips.Comment(str(get_order)), + mips.LwInstruction('$t1', f'{x_addr}($fp)'), + mips.LwInstruction('$t0', f'12($t1)'), + mips.SwInstruction('$t0', f'{t_addr}($fp)') + ] + +def get_type_order_min_to_mips_visitor(get_order:cil.GetTypeMinOrderNode): + x_addr = CURRENT_FUNCTION.offset[str(get_order.var)] + t_addr = CURRENT_FUNCTION.offset[str(get_order.result)] + return [ + mips.Comment(str(get_order)), + mips.LwInstruction('$t1', f'{x_addr}($fp)'), + mips.LwInstruction('$t0', f'16($t1)'), + mips.SwInstruction('$t0', f'{t_addr}($fp)') + ] + +def assign_to_mips_visitor(assign: cil.AssignNode): + """ + CIL: + x = y + MIPS: + lw $t0, [y_addr] + sw $t0, [x_addr] + """ + + code = [mips.Comment(str(assign))] + x_addr = CURRENT_FUNCTION.offset[str(assign.result)] + + if isinstance(assign.val, int): + code.append(mips.LiInstruction('$t0', assign.val)) + else: + y_addr = CURRENT_FUNCTION.offset[str(assign.val)] + code.append(mips.LwInstruction('$t0', f'{y_addr}($fp)')) + + return code+[ + mips.SwInstruction('$t0', f'{x_addr}($fp)') + ] + + +def copy_to_mips_visitor(copy: cil.CopyNode): + x_addr = CURRENT_FUNCTION.offset[str(copy.val)] + y_addr = CURRENT_FUNCTION.offset[str(copy.result)] + return [ + mips.Comment(str(copy)), + mips.LwInstruction('$a0', f'{x_addr+8}($fp)'), + mips.LiInstruction('$v0', 9), + mips.SyscallInstruction(), + mips.SwInstruction('$v0', f'{y_addr}($fp)'), + mips.AdduInstruction('$t1', '$fp', x_addr), + mips.AdduInstruction('$t2', '$fp', y_addr), + mips.MIPSLabel('copy_loop'), + mips.LwInstruction('$t0', '($t1)'), + mips.SwInstruction('$t0', '($t2)'), + mips.AdduInstruction('$t1', '$t1', 4), + mips.AdduInstruction('$t2', '$t2', 4), + mips.SubuInstruction('$a0', '$a0', 4), + mips.BeqzInstruction('$a0', 'end_copy_loop'), + mips.BInstruction('copy_loop'), + mips.MIPSLabel('end_copy_loop') + ] + + +def conditional_goto_to_mips_visitor(goto: cil.ConditionalGotoNode): + instructions = [mips.Comment(str(goto))] + if isinstance(goto.predicate, int): + instructions.append(mips.LiInstruction('$t0', goto.predicate)) + else: + predicate_offset = CURRENT_FUNCTION.offset[str(goto.predicate)] + instructions.append(mips.LwInstruction( + '$t0', f'{predicate_offset}($fp)')) + return instructions + [mips.BnezInstruction('$t0', goto.label)] + + +def goto_to_mips_visitor(goto: cil.GotoNode): + return [ + mips.Comment(str(goto)), + mips.BInstruction(goto.label) + ] + + +def label_to_mips_visitor(label: cil.LabelNode): + return [ + mips.Comment(str(label)), + mips.MIPSLabel(label.label_name) + ] + + +def abort_to_mips_visitor(abort: cil.AbortNode): + instructions = [ + mips.Comment(str(abort)), + mips.LaInstruction('$a0', 'data_abort'), + mips.LiInstruction('$v0', 4), + mips.SyscallInstruction() + ] + if abort.type_name: + __DATA__.append(mips.MIPSDataItem( + f'abort_{abort.type_name}', mips.AsciizInst(f'"{abort.type_name}"'))) + instructions.append(mips.LaInstruction( + '$a0', f'abort_{abort.type_name}')) + else: + instructions.append(mips.LwInstruction('$a0', f'($fp)')) + + return instructions + [ + mips.LiInstruction('$v0', 4), + mips.SyscallInstruction(), + mips.LaInstruction('$a0', 'new_line'), + mips.LiInstruction('$v0', 4), + mips.SyscallInstruction(), + mips.LiInstruction('$v0', 10), + mips.SyscallInstruction() + ] + + +__visitors__ = { + cil.LabelNode: label_to_mips_visitor, + cil.GotoNode: goto_to_mips_visitor, + cil.ConditionalGotoNode: conditional_goto_to_mips_visitor, + cil.ArgNode: arg_to_mips_visitor, + cil.AllocateNode: allocate_to_mips_visitor, + cil.CopyNode: copy_to_mips_visitor, + cil.GetAttrNode: getattr_to_mips_visitor, + cil.SetAttrNode: setattr_to_mips_visitor, + cil.PlusNode: plus_to_mips_visitor, + cil.MinusNode: minus_to_mips_visitor, + cil.StarNode: star_to_mips_visitor, + cil.DivNode: div_to_mips_visitor, + cil.LessEqNode: lesseq_to_mips_visitor, + cil.LessNode: less_to_mips_visitor, + cil.NotNode: not_to_mips_visitor, + cil.PrintNode: print_to_mips_visitor, + cil.ReturnNode: return_to_mips_visitor, + cil.ReadNode: read_to_mips_visitor, + cil.ReadIntNode: read_int_to_mips_visitor, + cil.LengthNode: length_to_mips_visitor, + cil.ConcatNode: concat_to_mips_visitor, + cil.LoadNode: load_to_mips_visitor, + cil.SubStringNode: substring_to_mips_visitor, + cil.VCAllNode: vcall_to_mips_visitor, + cil.AssignNode: assign_to_mips_visitor, + cil.TypeOfNode: type_of_to_mips_visitor, + cil.AbortNode: abort_to_mips_visitor, + cil.GetTypeAddrNode: get_type_addr_to_mips_visitor, + cil.GetTypeOrderNode:get_type_order_to_mips_visitor, + + cil.GetTypeMinOrderNode:get_type_order_min_to_mips_visitor, + cil.EqNode: eq_to_mips_visitor, + cil.NotEqNode:not_eq_to_mips_visitor, + cil.NotEqInstanceNode:not_eq_instance_to_mips_visitor, + cil.EqStringNode:eq_string_to_mips_visitor +} diff --git a/src/code_generation/MIPS/test.asm b/src/code_generation/MIPS/test.asm new file mode 100644 index 00000000..63a9f949 --- /dev/null +++ b/src/code_generation/MIPS/test.asm @@ -0,0 +1,1987 @@ + +.text +main: + move $fp, $sp + subu $sp, $sp, 24 + la $t0, Object_abort + usw $t0, vt_Object+0 + la $t0, Object_type_name + usw $t0, vt_Object+4 + la $t0, Object_copy + usw $t0, vt_Object+8 + la $t0, Object_abort + usw $t0, vt_IO+0 + la $t0, Object_type_name + usw $t0, vt_IO+4 + la $t0, Object_copy + usw $t0, vt_IO+8 + la $t0, IO_out_string + usw $t0, vt_IO+12 + la $t0, IO_out_int + usw $t0, vt_IO+16 + la $t0, IO_in_string + usw $t0, vt_IO+20 + la $t0, IO_in_int + usw $t0, vt_IO+24 + la $t0, Int_abort + usw $t0, vt_Int+0 + la $t0, Int_type_name + usw $t0, vt_Int+4 + la $t0, Object_copy + usw $t0, vt_Int+8 + la $t0, String_abort + usw $t0, vt_String+0 + la $t0, String_type_name + usw $t0, vt_String+4 + la $t0, Object_copy + usw $t0, vt_String+8 + la $t0, String_length + usw $t0, vt_String+28 + la $t0, String_concat + usw $t0, vt_String+32 + la $t0, String_substr + usw $t0, vt_String+36 + la $t0, Bool_abort + usw $t0, vt_Bool+0 + la $t0, Bool_type_name + usw $t0, vt_Bool+4 + la $t0, Object_copy + usw $t0, vt_Bool+8 + la $t0, Object_abort + usw $t0, vt_List+0 + la $t0, Object_type_name + usw $t0, vt_List+4 + la $t0, Object_copy + usw $t0, vt_List+8 + la $t0, IO_out_string + usw $t0, vt_List+12 + la $t0, IO_out_int + usw $t0, vt_List+16 + la $t0, IO_in_string + usw $t0, vt_List+20 + la $t0, IO_in_int + usw $t0, vt_List+24 + la $t0, List_isNil + usw $t0, vt_List+40 + la $t0, List_cons + usw $t0, vt_List+44 + la $t0, List_car + usw $t0, vt_List+48 + la $t0, List_cdr + usw $t0, vt_List+52 + la $t0, List_rev + usw $t0, vt_List+56 + la $t0, List_sort + usw $t0, vt_List+60 + la $t0, List_insert + usw $t0, vt_List+64 + la $t0, List_rcons + usw $t0, vt_List+68 + la $t0, List_print_list + usw $t0, vt_List+72 + la $t0, List___init__ + usw $t0, vt_List+76 + la $t0, Object_abort + usw $t0, vt_Cons+0 + la $t0, Object_type_name + usw $t0, vt_Cons+4 + la $t0, Object_copy + usw $t0, vt_Cons+8 + la $t0, IO_out_string + usw $t0, vt_Cons+12 + la $t0, IO_out_int + usw $t0, vt_Cons+16 + la $t0, IO_in_string + usw $t0, vt_Cons+20 + la $t0, IO_in_int + usw $t0, vt_Cons+24 + la $t0, Cons_isNil + usw $t0, vt_Cons+40 + la $t0, List_cons + usw $t0, vt_Cons+44 + la $t0, Cons_car + usw $t0, vt_Cons+48 + la $t0, Cons_cdr + usw $t0, vt_Cons+52 + la $t0, Cons_rev + usw $t0, vt_Cons+56 + la $t0, Cons_sort + usw $t0, vt_Cons+60 + la $t0, Cons_insert + usw $t0, vt_Cons+64 + la $t0, Cons_rcons + usw $t0, vt_Cons+68 + la $t0, Cons_print_list + usw $t0, vt_Cons+72 + la $t0, Cons_init + usw $t0, vt_Cons+80 + la $t0, Cons___init__ + usw $t0, vt_Cons+76 + la $t0, Object_abort + usw $t0, vt_Nil+0 + la $t0, Object_type_name + usw $t0, vt_Nil+4 + la $t0, Object_copy + usw $t0, vt_Nil+8 + la $t0, IO_out_string + usw $t0, vt_Nil+12 + la $t0, IO_out_int + usw $t0, vt_Nil+16 + la $t0, IO_in_string + usw $t0, vt_Nil+20 + la $t0, IO_in_int + usw $t0, vt_Nil+24 + la $t0, Nil_isNil + usw $t0, vt_Nil+40 + la $t0, List_cons + usw $t0, vt_Nil+44 + la $t0, List_car + usw $t0, vt_Nil+48 + la $t0, List_cdr + usw $t0, vt_Nil+52 + la $t0, Nil_rev + usw $t0, vt_Nil+56 + la $t0, Nil_sort + usw $t0, vt_Nil+60 + la $t0, Nil_insert + usw $t0, vt_Nil+64 + la $t0, Nil_rcons + usw $t0, vt_Nil+68 + la $t0, Nil_print_list + usw $t0, vt_Nil+72 + la $t0, Nil___init__ + usw $t0, vt_Nil+76 + la $t0, Object_abort + usw $t0, vt_Main+0 + la $t0, Object_type_name + usw $t0, vt_Main+4 + la $t0, Object_copy + usw $t0, vt_Main+8 + la $t0, IO_out_string + usw $t0, vt_Main+12 + la $t0, IO_out_int + usw $t0, vt_Main+16 + la $t0, IO_in_string + usw $t0, vt_Main+20 + la $t0, IO_in_int + usw $t0, vt_Main+24 + la $t0, Main_iota + usw $t0, vt_Main+84 + la $t0, Main_main + usw $t0, vt_Main+88 + la $t0, Main___init__ + usw $t0, vt_Main+76 + # self = ALLOCATE Main ; + li $a0, 24 + li $v0, 9 + syscall + sw $v0, -4($fp) + la $t0, vt_Main + sw $t0, 8($v0) + # local_1 = LOAD data_1 ; + la $t0, data_1 + sw $t0, -8($fp) + # SETATTR self @type local_1 ; + lw $t0, -8($fp) + lw $t1, -4($fp) + sw $t0, 0($t1) + # local_2 = 20 ; + li $t0, 20 + sw $t0, -12($fp) + # SETATTR self @size local_2 ; + lw $t0, -12($fp) + lw $t1, -4($fp) + sw $t0, 4($t1) + # local_3 = -2 ; + li $t0, -2 + sw $t0, -16($fp) + # SETATTR self @order local_3 ; + lw $t0, -16($fp) + lw $t1, -4($fp) + sw $t0, 12($t1) + # local_4 = VCALL Main __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, -4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Main___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -20($fp) + # main_result = VCALL Main main ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_4 ; + lw $t0, -20($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Main_main + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -24($fp) + # RETURN main_result ; + lw $v0, -24($fp) + addu $sp, $sp, 24 + li $v0, 10 + syscall +IO_out_int: + move $fp, $sp + subu $sp, $sp, 0 + # PRINT int ; + lw $a0, 0($fp) + li $v0, 1 + syscall + # RETURN self ; + lw $v0, 4($fp) + addu $sp, $sp, 0 + jr $ra +IO_out_string: + move $fp, $sp + subu $sp, $sp, 0 + # PRINT str ; + lw $a0, 0($fp) + li $v0, 4 + syscall + # RETURN self ; + lw $v0, 4($fp) + addu $sp, $sp, 0 + jr $ra +IO_in_string: + move $fp, $sp + subu $sp, $sp, 4 + la $a0, read_result + lw $t0, 0($fp) + addu $a0, $a0, $t0 + li $a1, 1024 + li $v0, 8 + syscall + remove_nl_loop: + + lb $t0, ($a0) + beqz $t0, end_loop + la $t1, new_line + lb $t2, ($t1) + beq $t0, $t2, end_loop + addu $a0, $a0, 1 + b remove_nl_loop + end_loop: + + sb $zero, ($a0) + la $a0, read_result + lw $t0, 0($fp) + addu $a0, $a0, $t0 + sw $a0, -4($fp) + # RETURN read_result ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +IO_in_int: + move $fp, $sp + subu $sp, $sp, 4 + # int = READINT ; + li $v0, 5 + syscall + sw $v0, -4($fp) + # RETURN int ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +Object_type_name: + move $fp, $sp + subu $sp, $sp, 4 + # type = TYPEOF self ; + lw $t0, 0($fp) + lw $t1, ($t0) + sw $t1, -4($fp) + # RETURN type ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +Object_copy: + move $fp, $sp + subu $sp, $sp, 4 + # copy = COPY self ; + lw $a0, 8($fp) + li $v0, 9 + syscall + sw $v0, -4($fp) + addu $t1, $fp, 0 + addu $t2, $fp, -4 + copy_loop: + + lw $t0, ($t1) + sw $t0, ($t2) + addu $t1, $t1, 4 + addu $t2, $t2, 4 + subu $a0, $a0, 4 + beqz $a0, end_copy_loop + b copy_loop + end_copy_loop: + + # RETURN copy ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +String_length: + move $fp, $sp + subu $sp, $sp, 4 + # len_result = LENGTH self ; + lw $t2, 0($fp) + li $t1, 0 + length_loop: + + lb $t0, ($t2) + beqz $t0, end_length_loop + addu $t2, $t2, 1 + addu $t1, $t1, 1 + b length_loop + end_length_loop: + + sw $t1, -4($fp) + # RETURN len_result ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +String_concat: + move $fp, $sp + subu $sp, $sp, 4 + # concat_result = CONCAT self x ; + la $t0, concat_result + lw $t1, 8($fp) + lw $t2, 4($fp) + concat_loop_a: + + lb $a0, ($t1) + beqz $a0, concat_loop_b + sb $a0, ($t0) + addu $t0, $t0, 1 + addu $t1, $t1, 1 + b concat_loop_a + concat_loop_b: + + lb $a0, ($t2) + beqz $a0, end_concat + sb $a0, ($t0) + addu $t0, $t0, 1 + addu $t2, $t2, 1 + b concat_loop_b + end_concat: + + sb $zero, ($t0) + la $t0, concat_result + sw $t0, -4($fp) + # RETURN concat_result ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +String_substr: + move $fp, $sp + subu $sp, $sp, 4 + # substring_result = SUBSTRING self i l; + lw $t0, 12($fp) + la $t5, substring_result + lw $t1, 0($fp) + addu $t1, $t1, $t5 + lw $t4, 8($fp) + lw $t2, 4($fp) + addu $t0, $t0, $t4 + substring_loop: + + beqz $t2, end_substring_loop + lb $t3, ($t0) + sb $t3, ($t1) + subu $t2, $t2, 1 + addu $t0, $t0, 1 + addu $t1, $t1, 1 + b substring_loop + end_substring_loop: + + sb $zero, ($t1) + la $t5, substring_result + lw $t1, 0($fp) + addu $t1, $t1, $t5 + sw $t1, -4($fp) + # RETURN substring_result ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +Object_abort: + move $fp, $sp + subu $sp, $sp, 0 + # ABORT None ; + la $a0, data_abort + li $v0, 4 + syscall + lw $a0, ($fp) + li $v0, 4 + syscall + la $a0, new_line + li $v0, 4 + syscall + li $v0, 10 + syscall +String_abort: + move $fp, $sp + subu $sp, $sp, 0 + # ABORT String ; + la $a0, data_abort + li $v0, 4 + syscall + la $a0, abort_String + li $v0, 4 + syscall + la $a0, new_line + li $v0, 4 + syscall + li $v0, 10 + syscall +Int_abort: + move $fp, $sp + subu $sp, $sp, 0 + # ABORT Int ; + la $a0, data_abort + li $v0, 4 + syscall + la $a0, abort_Int + li $v0, 4 + syscall + la $a0, new_line + li $v0, 4 + syscall + li $v0, 10 + syscall +Bool_abort: + move $fp, $sp + subu $sp, $sp, 0 + # ABORT Bool ; + la $a0, data_abort + li $v0, 4 + syscall + la $a0, abort_Bool + li $v0, 4 + syscall + la $a0, new_line + li $v0, 4 + syscall + li $v0, 10 + syscall +Bool_type_name: + move $fp, $sp + subu $sp, $sp, 4 + # local_13 = LOAD data_8 ; + la $t0, data_8 + sw $t0, -4($fp) + # RETURN local_13 ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +Int_type_name: + move $fp, $sp + subu $sp, $sp, 4 + # local_14 = LOAD data_3 ; + la $t0, data_3 + sw $t0, -4($fp) + # RETURN local_14 ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +String_type_name: + move $fp, $sp + subu $sp, $sp, 4 + # local_15 = LOAD data_9 ; + la $t0, data_9 + sw $t0, -4($fp) + # RETURN local_15 ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +List___init__: + move $fp, $sp + subu $sp, $sp, 0 + # RETURN self ; + lw $v0, 0($fp) + addu $sp, $sp, 0 + jr $ra +List_isNil: + move $fp, $sp + subu $sp, $sp, 8 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 abort ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 0($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # RETURN 1 ; + li $v0, 1 + addu $sp, $sp, 8 + jr $ra +List_cons: + move $fp, $sp + subu $sp, $sp, 32 + # local_0 = ALLOCATE Cons ; + li $a0, 28 + li $v0, 9 + syscall + sw $v0, -4($fp) + la $t0, vt_Cons + sw $t0, 8($v0) + # local_1 = LOAD data_2 ; + la $t0, data_2 + sw $t0, -8($fp) + # SETATTR local_0 @type local_1 ; + lw $t0, -8($fp) + lw $t1, -4($fp) + sw $t0, 0($t1) + # local_2 = 24 ; + li $t0, 24 + sw $t0, -12($fp) + # SETATTR local_0 @size local_2 ; + lw $t0, -12($fp) + lw $t1, -4($fp) + sw $t0, 4($t1) + # local_3 = -3 ; + li $t0, -3 + sw $t0, -16($fp) + # SETATTR local_0 @order local_3 ; + lw $t0, -16($fp) + lw $t1, -4($fp) + sw $t0, 12($t1) + # local_4 = VCALL Cons __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_0 ; + lw $t0, -4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Cons___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -20($fp) + # new_cell = local_4 ; + lw $t0, -20($fp) + sw $t0, -24($fp) + # local_6 = GETTYPEADDR new_cell ; + lw $t1, -24($fp) + lw $t0, 8($t1) + sw $t0, -28($fp) + # local_7 = VCALL local_6 init ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG new_cell ; + lw $t0, -24($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG hd ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG self ; + lw $t0, 4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -28($fp) + ulw $t1, 80($t0) + jalr $t1 + addu $sp, $sp, 12 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -32($fp) + # RETURN local_7 ; + lw $v0, -32($fp) + addu $sp, $sp, 32 + jr $ra +List_car: + move $fp, $sp + subu $sp, $sp, 24 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 abort ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 0($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # local_2 = ALLOCATE Int ; + li $a0, 20 + li $v0, 9 + syscall + sw $v0, -12($fp) + la $t0, vt_Int + sw $t0, 8($v0) + # local_3 = LOAD data_3 ; + la $t0, data_3 + sw $t0, -16($fp) + # SETATTR local_2 @type local_3 ; + lw $t0, -16($fp) + lw $t1, -12($fp) + sw $t0, 0($t1) + # local_4 = 16 ; + li $t0, 16 + sw $t0, -20($fp) + # SETATTR local_2 @size local_4 ; + lw $t0, -20($fp) + lw $t1, -12($fp) + sw $t0, 4($t1) + # local_5 = -2 ; + li $t0, -2 + sw $t0, -24($fp) + # SETATTR local_2 @order local_5 ; + lw $t0, -24($fp) + lw $t1, -12($fp) + sw $t0, 12($t1) + # RETURN local_2 ; + lw $v0, -12($fp) + addu $sp, $sp, 24 + jr $ra +List_cdr: + move $fp, $sp + subu $sp, $sp, 28 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 abort ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 0($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # local_2 = ALLOCATE List ; + li $a0, 20 + li $v0, 9 + syscall + sw $v0, -12($fp) + la $t0, vt_List + sw $t0, 8($v0) + # local_3 = LOAD data_4 ; + la $t0, data_4 + sw $t0, -16($fp) + # SETATTR local_2 @type local_3 ; + lw $t0, -16($fp) + lw $t1, -12($fp) + sw $t0, 0($t1) + # local_4 = 16 ; + li $t0, 16 + sw $t0, -20($fp) + # SETATTR local_2 @size local_4 ; + lw $t0, -20($fp) + lw $t1, -12($fp) + sw $t0, 4($t1) + # local_5 = -2 ; + li $t0, -2 + sw $t0, -24($fp) + # SETATTR local_2 @order local_5 ; + lw $t0, -24($fp) + lw $t1, -12($fp) + sw $t0, 12($t1) + # local_6 = VCALL List __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_2 ; + lw $t0, -12($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal List___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -28($fp) + # RETURN local_6 ; + lw $v0, -28($fp) + addu $sp, $sp, 28 + jr $ra +List_rev: + move $fp, $sp + subu $sp, $sp, 8 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 cdr ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 52($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # RETURN local_1 ; + lw $v0, -8($fp) + addu $sp, $sp, 8 + jr $ra +List_sort: + move $fp, $sp + subu $sp, $sp, 8 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 cdr ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 52($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # RETURN local_1 ; + lw $v0, -8($fp) + addu $sp, $sp, 8 + jr $ra +List_insert: + move $fp, $sp + subu $sp, $sp, 8 + # local_0 = GETTYPEADDR self ; + lw $t1, 4($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 cdr ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 52($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # RETURN local_1 ; + lw $v0, -8($fp) + addu $sp, $sp, 8 + jr $ra +List_rcons: + move $fp, $sp + subu $sp, $sp, 8 + # local_0 = GETTYPEADDR self ; + lw $t1, 4($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 cdr ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 52($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # RETURN local_1 ; + lw $v0, -8($fp) + addu $sp, $sp, 8 + jr $ra +List_print_list: + move $fp, $sp + subu $sp, $sp, 8 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 abort ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 0($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # RETURN local_1 ; + lw $v0, -8($fp) + addu $sp, $sp, 8 + jr $ra +Cons___init__: + move $fp, $sp + subu $sp, $sp, 4 + # local_0 = VCALL List __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal List___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -4($fp) + # RETURN self ; + lw $v0, 0($fp) + addu $sp, $sp, 4 + jr $ra +Cons_isNil: + move $fp, $sp + subu $sp, $sp, 0 + # RETURN ; + li $v0, 0 + addu $sp, $sp, 0 + jr $ra +Cons_init: + move $fp, $sp + subu $sp, $sp, 0 + # SETATTR self xcar hd ; + lw $t0, 4($fp) + lw $t1, 8($fp) + sw $t0, 16($t1) + # SETATTR self xcdr tl ; + lw $t0, 0($fp) + lw $t1, 8($fp) + sw $t0, 20($t1) + # RETURN self ; + lw $v0, 8($fp) + addu $sp, $sp, 0 + jr $ra +Cons_car: + move $fp, $sp + subu $sp, $sp, 4 + # local_0 = GETATTR self xcar ; + lw $t0, 0($fp) + lw $t1, 16($t0) + sw $t1, -4($fp) + # RETURN local_0 ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +Cons_cdr: + move $fp, $sp + subu $sp, $sp, 4 + # local_0 = GETATTR self xcdr ; + lw $t0, 0($fp) + lw $t1, 20($t0) + sw $t1, -4($fp) + # RETURN local_0 ; + lw $v0, -4($fp) + addu $sp, $sp, 4 + jr $ra +Cons_rev: + move $fp, $sp + subu $sp, $sp, 24 + # local_2 = GETATTR self xcdr ; + lw $t0, 0($fp) + lw $t1, 20($t0) + sw $t1, -12($fp) + # local_1 = GETTYPEADDR local_2 ; + lw $t1, -12($fp) + lw $t0, 8($t1) + sw $t0, -8($fp) + # local_3 = VCALL local_1 rev ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_2 ; + lw $t0, -12($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -8($fp) + ulw $t1, 56($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -16($fp) + # local_0 = GETTYPEADDR local_3 ; + lw $t1, -16($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_4 = GETATTR self xcar ; + lw $t0, 0($fp) + lw $t1, 16($t0) + sw $t1, -20($fp) + # local_5 = VCALL local_0 rcons ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_3 ; + lw $t0, -16($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_4 ; + lw $t0, -20($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 68($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -24($fp) + # RETURN local_5 ; + lw $v0, -24($fp) + addu $sp, $sp, 24 + jr $ra +Cons_sort: + move $fp, $sp + subu $sp, $sp, 24 + # local_2 = GETATTR self xcdr ; + lw $t0, 0($fp) + lw $t1, 20($t0) + sw $t1, -12($fp) + # local_1 = GETTYPEADDR local_2 ; + lw $t1, -12($fp) + lw $t0, 8($t1) + sw $t0, -8($fp) + # local_3 = VCALL local_1 sort ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_2 ; + lw $t0, -12($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -8($fp) + ulw $t1, 60($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -16($fp) + # local_0 = GETTYPEADDR local_3 ; + lw $t1, -16($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_4 = GETATTR self xcar ; + lw $t0, 0($fp) + lw $t1, 16($t0) + sw $t1, -20($fp) + # local_5 = VCALL local_0 insert ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_3 ; + lw $t0, -16($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_4 ; + lw $t0, -20($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 64($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -24($fp) + # RETURN local_5 ; + lw $v0, -24($fp) + addu $sp, $sp, 24 + jr $ra +Cons_insert: + move $fp, $sp + subu $sp, $sp, 84 + # local_0 = GETATTR self xcar ; + lw $t0, 4($fp) + lw $t1, 16($t0) + sw $t1, -4($fp) + # local_1 = i < local_0 ; + lw $t0, 0($fp) + lw $t1, -4($fp) + slt $t0, $t0, $t1 + sw $t0, -8($fp) + # IF local_1 GOTO label_1 ; + lw $t0, -8($fp) + bnez $t0, label_1 + # local_10 = ALLOCATE Cons ; + li $a0, 28 + li $v0, 9 + syscall + sw $v0, -44($fp) + la $t0, vt_Cons + sw $t0, 8($v0) + # local_11 = LOAD data_2 ; + la $t0, data_2 + sw $t0, -48($fp) + # SETATTR local_10 @type local_11 ; + lw $t0, -48($fp) + lw $t1, -44($fp) + sw $t0, 0($t1) + # local_12 = 24 ; + li $t0, 24 + sw $t0, -52($fp) + # SETATTR local_10 @size local_12 ; + lw $t0, -52($fp) + lw $t1, -44($fp) + sw $t0, 4($t1) + # local_13 = -3 ; + li $t0, -3 + sw $t0, -56($fp) + # SETATTR local_10 @order local_13 ; + lw $t0, -56($fp) + lw $t1, -44($fp) + sw $t0, 12($t1) + # local_14 = VCALL Cons __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_10 ; + lw $t0, -44($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Cons___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -60($fp) + # local_9 = GETTYPEADDR local_14 ; + lw $t1, -60($fp) + lw $t0, 8($t1) + sw $t0, -40($fp) + # local_15 = GETATTR self xcar ; + lw $t0, 4($fp) + lw $t1, 16($t0) + sw $t1, -64($fp) + # local_17 = GETATTR self xcdr ; + lw $t0, 4($fp) + lw $t1, 20($t0) + sw $t1, -72($fp) + # local_16 = GETTYPEADDR local_17 ; + lw $t1, -72($fp) + lw $t0, 8($t1) + sw $t0, -68($fp) + # local_18 = VCALL local_16 insert ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_17 ; + lw $t0, -72($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG i ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -68($fp) + ulw $t1, 64($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -76($fp) + # local_19 = VCALL local_9 init ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_14 ; + lw $t0, -60($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_15 ; + lw $t0, -64($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_18 ; + lw $t0, -76($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -40($fp) + ulw $t1, 80($t0) + jalr $t1 + addu $sp, $sp, 12 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -80($fp) + # local_20 = local_19 ; + lw $t0, -80($fp) + sw $t0, -84($fp) + # GOTO label_2 ; + b label_2 + # LABEL label_1 ; + label_1: + + # local_3 = ALLOCATE Cons ; + li $a0, 28 + li $v0, 9 + syscall + sw $v0, -16($fp) + la $t0, vt_Cons + sw $t0, 8($v0) + # local_4 = LOAD data_2 ; + la $t0, data_2 + sw $t0, -20($fp) + # SETATTR local_3 @type local_4 ; + lw $t0, -20($fp) + lw $t1, -16($fp) + sw $t0, 0($t1) + # local_5 = 24 ; + li $t0, 24 + sw $t0, -24($fp) + # SETATTR local_3 @size local_5 ; + lw $t0, -24($fp) + lw $t1, -16($fp) + sw $t0, 4($t1) + # local_6 = -3 ; + li $t0, -3 + sw $t0, -28($fp) + # SETATTR local_3 @order local_6 ; + lw $t0, -28($fp) + lw $t1, -16($fp) + sw $t0, 12($t1) + # local_7 = VCALL Cons __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_3 ; + lw $t0, -16($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Cons___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -32($fp) + # local_2 = GETTYPEADDR local_7 ; + lw $t1, -32($fp) + lw $t0, 8($t1) + sw $t0, -12($fp) + # local_8 = VCALL local_2 init ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_7 ; + lw $t0, -32($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG i ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG self ; + lw $t0, 4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -12($fp) + ulw $t1, 80($t0) + jalr $t1 + addu $sp, $sp, 12 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -36($fp) + # local_20 = local_8 ; + lw $t0, -36($fp) + sw $t0, -84($fp) + # LABEL label_2 ; + label_2: + + # RETURN local_20 ; + lw $v0, -84($fp) + addu $sp, $sp, 84 + jr $ra +Cons_rcons: + move $fp, $sp + subu $sp, $sp, 44 + # local_1 = ALLOCATE Cons ; + li $a0, 28 + li $v0, 9 + syscall + sw $v0, -8($fp) + la $t0, vt_Cons + sw $t0, 8($v0) + # local_2 = LOAD data_2 ; + la $t0, data_2 + sw $t0, -12($fp) + # SETATTR local_1 @type local_2 ; + lw $t0, -12($fp) + lw $t1, -8($fp) + sw $t0, 0($t1) + # local_3 = 24 ; + li $t0, 24 + sw $t0, -16($fp) + # SETATTR local_1 @size local_3 ; + lw $t0, -16($fp) + lw $t1, -8($fp) + sw $t0, 4($t1) + # local_4 = -3 ; + li $t0, -3 + sw $t0, -20($fp) + # SETATTR local_1 @order local_4 ; + lw $t0, -20($fp) + lw $t1, -8($fp) + sw $t0, 12($t1) + # local_5 = VCALL Cons __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_1 ; + lw $t0, -8($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Cons___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -24($fp) + # local_0 = GETTYPEADDR local_5 ; + lw $t1, -24($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_6 = GETATTR self xcar ; + lw $t0, 4($fp) + lw $t1, 16($t0) + sw $t1, -28($fp) + # local_8 = GETATTR self xcdr ; + lw $t0, 4($fp) + lw $t1, 20($t0) + sw $t1, -36($fp) + # local_7 = GETTYPEADDR local_8 ; + lw $t1, -36($fp) + lw $t0, 8($t1) + sw $t0, -32($fp) + # local_9 = VCALL local_7 rcons ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_8 ; + lw $t0, -36($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG i ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -32($fp) + ulw $t1, 68($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -40($fp) + # local_10 = VCALL local_0 init ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_5 ; + lw $t0, -24($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_6 ; + lw $t0, -28($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_9 ; + lw $t0, -40($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 80($t0) + jalr $t1 + addu $sp, $sp, 12 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -44($fp) + # RETURN local_10 ; + lw $v0, -44($fp) + addu $sp, $sp, 44 + jr $ra +Cons_print_list: + move $fp, $sp + subu $sp, $sp, 36 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = GETATTR self xcar ; + lw $t0, 0($fp) + lw $t1, 16($t0) + sw $t1, -8($fp) + # local_2 = VCALL local_0 out_int ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_1 ; + lw $t0, -8($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 16($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -12($fp) + # local_3 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -16($fp) + # local_4 = LOAD data_5 ; + la $t0, data_5 + sw $t0, -20($fp) + # local_5 = VCALL local_3 out_string ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_4 ; + lw $t0, -20($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -16($fp) + ulw $t1, 12($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -24($fp) + # local_7 = GETATTR self xcdr ; + lw $t0, 0($fp) + lw $t1, 20($t0) + sw $t1, -32($fp) + # local_6 = GETTYPEADDR local_7 ; + lw $t1, -32($fp) + lw $t0, 8($t1) + sw $t0, -28($fp) + # local_8 = VCALL local_6 print_list ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_7 ; + lw $t0, -32($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -28($fp) + ulw $t1, 72($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -36($fp) + # RETURN local_8 ; + lw $v0, -36($fp) + addu $sp, $sp, 36 + jr $ra +Nil___init__: + move $fp, $sp + subu $sp, $sp, 4 + # local_0 = VCALL List __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal List___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -4($fp) + # RETURN self ; + lw $v0, 0($fp) + addu $sp, $sp, 4 + jr $ra +Nil_isNil: + move $fp, $sp + subu $sp, $sp, 0 + # RETURN 1 ; + li $v0, 1 + addu $sp, $sp, 0 + jr $ra +Nil_rev: + move $fp, $sp + subu $sp, $sp, 0 + # RETURN self ; + lw $v0, 0($fp) + addu $sp, $sp, 0 + jr $ra +Nil_sort: + move $fp, $sp + subu $sp, $sp, 0 + # RETURN self ; + lw $v0, 0($fp) + addu $sp, $sp, 0 + jr $ra +Nil_insert: + move $fp, $sp + subu $sp, $sp, 8 + # local_0 = GETTYPEADDR self ; + lw $t1, 4($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = VCALL local_0 rcons ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG i ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 68($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -8($fp) + # RETURN local_1 ; + lw $v0, -8($fp) + addu $sp, $sp, 8 + jr $ra +Nil_rcons: + move $fp, $sp + subu $sp, $sp, 28 + # local_1 = ALLOCATE Cons ; + li $a0, 28 + li $v0, 9 + syscall + sw $v0, -8($fp) + la $t0, vt_Cons + sw $t0, 8($v0) + # local_2 = LOAD data_2 ; + la $t0, data_2 + sw $t0, -12($fp) + # SETATTR local_1 @type local_2 ; + lw $t0, -12($fp) + lw $t1, -8($fp) + sw $t0, 0($t1) + # local_3 = 24 ; + li $t0, 24 + sw $t0, -16($fp) + # SETATTR local_1 @size local_3 ; + lw $t0, -16($fp) + lw $t1, -8($fp) + sw $t0, 4($t1) + # local_4 = -3 ; + li $t0, -3 + sw $t0, -20($fp) + # SETATTR local_1 @order local_4 ; + lw $t0, -20($fp) + lw $t1, -8($fp) + sw $t0, 12($t1) + # local_5 = VCALL Cons __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_1 ; + lw $t0, -8($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Cons___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -24($fp) + # local_0 = GETTYPEADDR local_5 ; + lw $t1, -24($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_6 = VCALL local_0 init ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_5 ; + lw $t0, -24($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG i ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG self ; + lw $t0, 4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 80($t0) + jalr $t1 + addu $sp, $sp, 12 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -28($fp) + # RETURN local_6 ; + lw $v0, -28($fp) + addu $sp, $sp, 28 + jr $ra +Nil_print_list: + move $fp, $sp + subu $sp, $sp, 0 + # RETURN 1 ; + li $v0, 1 + addu $sp, $sp, 0 + jr $ra +Main___init__: + move $fp, $sp + subu $sp, $sp, 0 + # RETURN self ; + lw $v0, 0($fp) + addu $sp, $sp, 0 + jr $ra +Main_iota: + move $fp, $sp + subu $sp, $sp, 72 + # local_0 = ALLOCATE Nil ; + li $a0, 20 + li $v0, 9 + syscall + sw $v0, -4($fp) + la $t0, vt_Nil + sw $t0, 8($v0) + # local_1 = LOAD data_6 ; + la $t0, data_6 + sw $t0, -8($fp) + # SETATTR local_0 @type local_1 ; + lw $t0, -8($fp) + lw $t1, -4($fp) + sw $t0, 0($t1) + # local_2 = 16 ; + li $t0, 16 + sw $t0, -12($fp) + # SETATTR local_0 @size local_2 ; + lw $t0, -12($fp) + lw $t1, -4($fp) + sw $t0, 4($t1) + # local_3 = -3 ; + li $t0, -3 + sw $t0, -16($fp) + # SETATTR local_0 @order local_3 ; + lw $t0, -16($fp) + lw $t1, -4($fp) + sw $t0, 12($t1) + # local_4 = VCALL Nil __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_0 ; + lw $t0, -4($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Nil___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -20($fp) + # SETATTR self l local_4 ; + lw $t0, -20($fp) + lw $t1, 4($fp) + sw $t0, 16($t1) + # j = 0 ; + li $t0, 0 + sw $t0, -24($fp) + # LABEL label_3 ; + label_3: + + # local_6 = j < i ; + lw $t0, -24($fp) + lw $t1, 0($fp) + slt $t0, $t0, $t1 + sw $t0, -28($fp) + # IF local_6 GOTO label_4 ; + lw $t0, -28($fp) + bnez $t0, label_4 + # GOTO label_5 ; + b label_5 + # LABEL label_4 ; + label_4: + + # local_8 = ALLOCATE Cons ; + li $a0, 28 + li $v0, 9 + syscall + sw $v0, -36($fp) + la $t0, vt_Cons + sw $t0, 8($v0) + # local_9 = LOAD data_2 ; + la $t0, data_2 + sw $t0, -40($fp) + # SETATTR local_8 @type local_9 ; + lw $t0, -40($fp) + lw $t1, -36($fp) + sw $t0, 0($t1) + # local_10 = 24 ; + li $t0, 24 + sw $t0, -44($fp) + # SETATTR local_8 @size local_10 ; + lw $t0, -44($fp) + lw $t1, -36($fp) + sw $t0, 4($t1) + # local_11 = -3 ; + li $t0, -3 + sw $t0, -48($fp) + # SETATTR local_8 @order local_11 ; + lw $t0, -48($fp) + lw $t1, -36($fp) + sw $t0, 12($t1) + # local_12 = VCALL Cons __init__ ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_8 ; + lw $t0, -36($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + jal Cons___init__ + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -52($fp) + # local_7 = GETTYPEADDR local_12 ; + lw $t1, -52($fp) + lw $t0, 8($t1) + sw $t0, -32($fp) + # local_13 = GETATTR self l ; + lw $t0, 4($fp) + lw $t1, 16($t0) + sw $t1, -56($fp) + # local_14 = VCALL local_7 init ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_12 ; + lw $t0, -52($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG j ; + lw $t0, -24($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_13 ; + lw $t0, -56($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -32($fp) + ulw $t1, 80($t0) + jalr $t1 + addu $sp, $sp, 12 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -60($fp) + # SETATTR self l local_14 ; + lw $t0, -60($fp) + lw $t1, 4($fp) + sw $t0, 16($t1) + # local_15 = j + 1 ; + lw $t0, -24($fp) + li $t1, 1 + add $t0, $t0, $t1 + sw $t0, -64($fp) + # j = local_15 ; + lw $t0, -64($fp) + sw $t0, -24($fp) + # GOTO label_3 ; + b label_3 + # LABEL label_5 ; + label_5: + + # local_16 = 0 ; + li $t0, 0 + sw $t0, -68($fp) + # local_17 = GETATTR self l ; + lw $t0, 4($fp) + lw $t1, 16($t0) + sw $t1, -72($fp) + # RETURN local_17 ; + lw $v0, -72($fp) + addu $sp, $sp, 72 + jr $ra +Main_main: + move $fp, $sp + subu $sp, $sp, 52 + # local_0 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -4($fp) + # local_1 = LOAD data_7 ; + la $t0, data_7 + sw $t0, -8($fp) + # local_2 = VCALL local_0 out_string ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_1 ; + lw $t0, -8($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -4($fp) + ulw $t1, 12($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -12($fp) + # local_6 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -28($fp) + # local_7 = GETTYPEADDR self ; + lw $t1, 0($fp) + lw $t0, 8($t1) + sw $t0, -32($fp) + # local_8 = VCALL local_7 in_int ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -32($fp) + ulw $t1, 24($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -36($fp) + # local_9 = VCALL local_6 iota ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG self ; + lw $t0, 0($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + # ARG local_8 ; + lw $t0, -36($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -28($fp) + ulw $t1, 84($t0) + jalr $t1 + addu $sp, $sp, 8 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -40($fp) + # local_5 = GETTYPEADDR local_9 ; + lw $t1, -40($fp) + lw $t0, 8($t1) + sw $t0, -24($fp) + # local_10 = VCALL local_5 rev ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_9 ; + lw $t0, -40($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -24($fp) + ulw $t1, 56($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -44($fp) + # local_4 = GETTYPEADDR local_10 ; + lw $t1, -44($fp) + lw $t0, 8($t1) + sw $t0, -20($fp) + # local_11 = VCALL local_4 sort ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_10 ; + lw $t0, -44($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -20($fp) + ulw $t1, 60($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -48($fp) + # local_3 = GETTYPEADDR local_11 ; + lw $t1, -48($fp) + lw $t0, 8($t1) + sw $t0, -16($fp) + # local_12 = VCALL local_3 print_list ; + subu $sp, $sp, 8 + sw $fp, 0($sp) + sw $ra, 4($sp) + # ARG local_11 ; + lw $t0, -48($fp) + subu $sp, $sp, 4 + sw $t0, ($sp) + lw $t0, -16($fp) + ulw $t1, 72($t0) + jalr $t1 + addu $sp, $sp, 4 + lw $fp, 0($sp) + lw $ra, 4($sp) + addu $sp, $sp, 8 + sw $v0, -52($fp) + # RETURN local_12 ; + lw $v0, -52($fp) + addu $sp, $sp, 52 + jr $ra + +.data + data_1: + .asciiz "Main" + data_2: + .asciiz "Cons" + data_3: + .asciiz "Int" + data_4: + .asciiz "List" + data_5: + .asciiz "\n" + data_6: + .asciiz "Nil" + data_7: + .asciiz "How many numbers to sort? " + data_8: + .asciiz "Bool" + data_9: + .asciiz "String" + data_abort: + .asciiz "Abort called from class " + new_line: + .asciiz "\n" + concat_result: + .space 0 + substring_result: + .space 0 + read_result: + .space 0 + vt_Object: + .space 376 + vt_IO: + .space 376 + vt_Int: + .space 376 + vt_String: + .space 376 + vt_Bool: + .space 376 + vt_List: + .space 376 + vt_Cons: + .space 376 + vt_Nil: + .space 376 + vt_Main: + .space 376 + abort_String: + .asciiz "String" + abort_Int: + .asciiz "Int" + abort_Bool: + .asciiz "Bool" diff --git a/src/code_generation/MIPS/utilities.py b/src/code_generation/MIPS/utilities.py new file mode 100644 index 00000000..2e724c9f --- /dev/null +++ b/src/code_generation/MIPS/utilities.py @@ -0,0 +1,68 @@ +from code_generation.MIPS import ast as mips +import re + +__TYPES__ = {} +__ADDRS__ = {} + + +def init_types(types): + """ + Objects in MIPS code are memory space (size_mips), therefore we need to know the type + of this object. But we cannot save the type name, therefore, we configure + a unique number for each type which will be identified in MIPS (code_mips)\n + + """ + size_vt=0 + for i, t in enumerate(types): + t.code_mips = i + t.size_mips = (len(t.attributes) + 5) * 4 + size_vt +=(len(t.methods) + 1) * 4 + t.attr_index_mips = {} + for i, a in enumerate(t.attributes): + t.attr_index_mips[a] = i + 1 + __TYPES__[t.type] = t + return size_vt + + +def get_types(): + return __TYPES__ + +def get_type(name): + try: + return __TYPES__[name] + except KeyError: + return None + + +def allocate_stack(bytes): + return [mips.SubuInstruction('$sp', '$sp', bytes)] + + +def free_stack(bytes): + return [mips.AdduInstruction('$sp', '$sp', bytes)] + + +def push_stack(src, pos): + return peek_stack('$t0', src)+[mips.SwInstruction('$t0', pos)] + + +def peek_stack(src, pos): + return [mips.LwInstruction(src, pos)] + + +def save_address(key, value): + + if type(value) is int: + if value: + __ADDRS__[key] = f'{value}($sp)' + else: + __ADDRS__[key] = f'($sp)' + + __ADDRS__[key] = value + + +register_pattern = re.compile(r'\$v[0-1]|\$a[0-3]|\$t[0-9]|\$s[0-7]') + + +def is_register(addr: str): + return register_pattern.match(addr) != None diff --git a/src/code_generation/__init__.py b/src/code_generation/__init__.py new file mode 100644 index 00000000..37fc8e2e --- /dev/null +++ b/src/code_generation/__init__.py @@ -0,0 +1,7 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .code_gen import generate_code diff --git a/src/code_generation/code_gen.py b/src/code_generation/code_gen.py new file mode 100644 index 00000000..87d44432 --- /dev/null +++ b/src/code_generation/code_gen.py @@ -0,0 +1,14 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .CIL import ast_to_cil +from .MIPS import program_to_mips_visitor + + +def generate_code(ast): + cil_ast = ast_to_cil(ast) + mips_program = program_to_mips_visitor(cil_ast) + return str(mips_program) diff --git a/src/cool_types/__init__.py b/src/cool_types/__init__.py new file mode 100644 index 00000000..5cabe365 --- /dev/null +++ b/src/cool_types/__init__.py @@ -0,0 +1,24 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .types import ( + CoolType, + CoolTypeMethod, + CoolTypeMethod, + SelfType, + ObjectType, + BoolType, + IntType, + StringType, + IOType, + TypesByName, + type_by_name, + check_inherits, + check_type_declaration, + check_type_hierarchy, + get_attribute, + pronounced_join +) diff --git a/src/cool_types/types.py b/src/cool_types/types.py new file mode 100644 index 00000000..3021e7aa --- /dev/null +++ b/src/cool_types/types.py @@ -0,0 +1,288 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from errors import * + + +class CoolType: + def __init__(self, name, parent_type, inherit=True): + self.name = name + self.parent = parent_type + self.attributes = {} + self.methods = {} + self.inherit = inherit + self.childs = set() + self.order = 0 + self.min_order = 0 + + # if parent_type: + # self.order=self.parent.order-1 + # else: + # self.order=0 + + def __can_be_define__(self, id): + if id in self.methods.keys(): + return False, f'{ERR_SEMANTIC}: Method {id} is multiply defined.' + return True, None + + def add_method(self, id, arg_types_name, returned_type): + current_type = self + while current_type: + try: + m = current_type.methods[id] + if len(m.args) != len(arg_types_name): + return False, f'{ERR_SEMANTIC}: invalid redefinition of {id}' + for i in range(0, len(arg_types_name)): + current_type = TypesByName[arg_types_name[i]] + if current_type != m.args[i]: + return False, f'{ERR_SEMANTIC}: In redefined method {id}, parameter type {current_type} is different from original type {m.args[i]}.' + _returned_type = type_by_name(returned_type) + if m.returnedType != _returned_type: + return False, f'{ERR_SEMANTIC}: In redefined method {id}, return type {_returned_type} is different from original return type {m.returnedType}.' + break + except KeyError: + current_type = current_type.parent + ok, msg = self.__can_be_define__(id) + if ok: + arg_types = [] + for arg in arg_types_name: + arg_type = type_by_name(arg) + if arg_type is None: + return False, f'{ERR_TYPE}: Class {arg} of formal parameter is undefined.' + arg_types.append(arg_type) + _returned_type = type_by_name(returned_type) + if _returned_type is None: + return False, f'{ERR_TYPE}: Undefined return type {returned_type} in method {id}.' + self.methods[id] = CoolTypeMethod(id, arg_types, _returned_type) + return True, None + else: + return False, msg + + def get_all_attributes(self): + t = self + result = [] + while t: + temp = [] + for attr in t.attributes.values(): + temp.append(attr) + result.append(temp) + t = t.parent + return [elem for sublist in result[::-1] for elem in sublist] + + def get_self_attributes(self): + return self.attributes.values() + + def get_all_self_methods(self): + return self.methods + + def get_all_inherited_methods(self): + t = self.parent + result = [] + while t: + temp = [] + for met in t.methods.values(): + met.owner = t.name + temp.append(met) + result.append(temp) + t = t.parent + return [elem for sublist in result[::-1] for elem in sublist] + + def get_method(self, id, args_types): + try: + return self.get_method_without_hierarchy(id, args_types) + except Exception: + if self.parent: + return self.parent.get_method(id, args_types) + else: + return None, None, f'{ERR_ATTRIBUTE}: Dispatch to undefined method {id}.' + + def get_method_without_hierarchy(self, id, args_types): + try: + method = self.methods[id] + if len(args_types) != len(method.args): + return None, None, f'{ERR_SEMANTIC}: Method {id} called with wrong number of arguments.' + for i, a in enumerate(args_types): + if not check_inherits(a, method.args[i]): + return None, None, f'{ERR_TYPE}: In call of method {id}, type {a} does not conform to declared type {method.args[i]}.' + return method, self, None + except KeyError: + raise Exception(f'type {self.name} don\'t have a method {id}') + + def add_attr(self, id, attr_type, expression): + attribute, owner_type = get_attribute(self, id) + if attribute is not None: + return False, f'{ERR_SEMANTIC}: Attribute {id} is an attribute of an inherited class.' + try: + _ = self.attributes[id] + return False, f'{ERR_SEMANTIC}: Attribute {id} is an attribute of an inherited class.' + except KeyError: + _attr_type = type_by_name(attr_type) + if _attr_type is None: + return False, f'{ERR_TYPE}: Class {attr_type} of attribute {id} is undefined.' + self.attributes[id] = CoolTypeAttribute(id, _attr_type, expression) + return True, None + + def __repr__(self): + return f'{self.name}' + + def __str__(self): + return f'{self.name}' + + +def get_attribute(type_c: CoolType, id: str): + while type_c is not None: + try: + return type_c.attributes[id], type_c + except KeyError: + type_c = type_c.parent + return None, None + + +class CoolTypeAttribute: + def __init__(self, id, attr_type, expression=None): + self.id = id + self.attrType = attr_type + self.expression = expression + + +class CoolTypeMethod: + def __init__(self, id, args, returned_type): + self.returnedType = returned_type + self.id = id + self.args = args + + def __repr__(self): + return f'{self.id}{self.args}:{self.returnedType}' + + +def type_by_name(type_name): + try: + return TypesByName[type_name] + except KeyError: + return None + + +def check_inherits(type_a: CoolType, type_b: CoolType): + """ + Return True if type_a <= typeB + """ + current = type_a + while current != type_b: + if current is None: + return False + current = current.parent + return True + + +def check_type_declaration(node): + for c in node.classes: + try: + _ = TypesByName[c.type] + add_semantic_error(c.lineno, c.colno, + f'{ERR_SEMANTIC}: Redefinition of basic class {c.type}.') + return False + except KeyError: + TypesByName[c.type] = CoolType(c.type, None) + return True + + +def check_type_hierarchy(node): + for c in node.classes: + cType = TypesByName[c.type] + if c.parent_type: + try: + parentType = TypesByName[c.parent_type] + if parentType.inherit: + cType.parent = parentType + parentType.childs.add(cType) + type_x = parentType + while type_x: + if type_x: + if type_x == cType: + add_semantic_error( + c.lineno, c.colno, f'{ERR_SEMANTIC}: Class {cType.name}, or an ancestor of {cType.name}, is involved in an inheritance cycle.') + return False + type_x = type_x.parent + else: + add_semantic_error( + c.lineno, c.colno, f'{ERR_SEMANTIC}: Class {cType} cannot inherit class {parentType.name}.') + return False + except KeyError: + add_semantic_error(c.lineno, c.colno, + f'{ERR_TYPE}: Class {cType} inherits from an undefined class {c.parent_type}.') + return False + else: + cType.parent = ObjectType + ObjectType.childs.add(cType) + set_types_order(ObjectType, 1) + return True + + +def set_types_order(currnet_type: CoolType, current_order: int) -> int: + currnet_type.min_order = current_order + for t in currnet_type.childs: + current_order = set_types_order(t, current_order) + currnet_type.order = current_order + return current_order + 1 + + +def __type_hierarchy__(type_x): + h = [] + while type_x is not None: + h.append(type_x) + type_x = type_x.parent + return h + + +def pronounced_join(type_a, type_b): + h = __type_hierarchy__(type_b) + while type_a is not None: + if type_a in h: + break + type_a = type_a.parent + return type_a + + +SelfType = CoolType('SELF_TYPE', None, False) +ObjectType = CoolType('Object', None) +ObjectType.order = 0 +IOType = CoolType('IO', ObjectType) +IOType.order = -1 +IntType = CoolType('Int', ObjectType, False) +IntType.order = -2 +StringType = CoolType('String', ObjectType, False) +StringType.order = -2 +BoolType = CoolType('Bool', ObjectType, False) +BoolType.order = -2 + +TypesByName = { + 'SELF_TYPE': SelfType, + 'Object': ObjectType, + 'IO': IOType, + 'Int': IntType, + 'String': StringType, + 'Bool': BoolType +} + +ObjectType.childs = set([IOType, IntType, StringType, BoolType]) +ObjectType.add_method('abort', [], 'Object') +ObjectType.add_method('type_name', [], 'String') +ObjectType.add_method('copy', [], 'SELF_TYPE') +IOType.add_method('out_string', ['String'], 'SELF_TYPE') +IOType.add_method('out_int', ['Int'], 'SELF_TYPE') +IOType.add_method('in_string', [], 'String') +IOType.add_method('in_int', [], 'Int') +StringType.add_method('length', [], 'Int') +StringType.add_method('concat', ['String'], 'String') +StringType.add_method('substr', ['Int', 'Int'], 'String') + +IntType.add_method('abort', [], 'Object') +BoolType.add_method('abort', [], 'Object') +StringType.add_method('abort', [], 'Object') + +StringType.add_method('type_name', [], 'String') +IntType.add_method('type_name', [], 'String') +BoolType.add_method('type_name', [], 'String') diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f..eb2814b1 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -4,8 +4,9 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips # Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos +# echo "COOL COMPILER 0.0v" +echo "Copyright (c) 2019: Isabella Maria Sierra Ponce, Adrian Tubal Paez Ruiz, Eric Martin Garcia" # Llamar al compilador echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python3 main.py $INPUT_FILE diff --git a/src/errors/__init__.py b/src/errors/__init__.py new file mode 100644 index 00000000..6320c4b7 --- /dev/null +++ b/src/errors/__init__.py @@ -0,0 +1,7 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .errors import * diff --git a/src/errors/errors.py b/src/errors/errors.py new file mode 100644 index 00000000..bff07d66 --- /dev/null +++ b/src/errors/errors.py @@ -0,0 +1,26 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +LEXER_ERRORS = [] +PARSER_ERRORS = [] +SEMANTIC_ERRORS = [] + +ERR_TYPE = "TypeError" +ERR_SEMANTIC = "SemanticError" +ERR_ATTRIBUTE = "AttributeError" +ERR_NAME = "NameError" + + +def add_lexer_error(line, column, message): + LEXER_ERRORS.append(f'({line}, {column}) - LexicographicError: {message}') + + +def add_parser_error(line, column, message): + PARSER_ERRORS.append(f'({line}, {column}) - SyntacticError: {message}') + + +def add_semantic_error(line, column, message): + SEMANTIC_ERRORS.append(f'({line}, {column}) - {message}') diff --git a/src/lexer_parser/__init__.py b/src/lexer_parser/__init__.py new file mode 100644 index 00000000..6eeb14f4 --- /dev/null +++ b/src/lexer_parser/__init__.py @@ -0,0 +1,9 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .lexer import lexer +from .parser import parser +import ast as CoolAST diff --git a/src/lexer_parser/ast.py b/src/lexer_parser/ast.py new file mode 100644 index 00000000..680e8743 --- /dev/null +++ b/src/lexer_parser/ast.py @@ -0,0 +1,205 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +class AstNode: + def __init__(self): + self.lineno = 0 + self.colno = 0 + + def add_location(self, line, column): + self.lineno = line + self.colno = column + + +class ProgramNode(AstNode): + def __init__(self, classes: list): + super().__init__() + self.classes = classes + + +class DefClassNode(AstNode): + def __init__(self, type, features, parent_type=None): + super().__init__() + self.type = type + self.feature_nodes = features + self.parent_type = parent_type + + +class FeatureNode(AstNode): + def __init__(self, id): + super().__init__() + self.id = id + + +class DefAttrNode(FeatureNode): + def __init__(self, id, type, expr=None): + super().__init__(id) + self.type = type + self.expr = expr + + def __index__(self): + return None + + +class DefFuncNode(FeatureNode): + def __init__(self, id, params, return_type, expressions): + super().__init__(id) + self.params = params + self.return_type = return_type + self.expressions = expressions + + +class ExpressionNode(AstNode): + def __init__(self): + super().__init__() + self.returned_type = None + + +class AssignNode(ExpressionNode): + def __init__(self, id, expr): + super().__init__() + self.id = id + self.expr = expr + + +class FuncCallNode(ExpressionNode): + def __init__(self, id, args, object=None, type=None): + super().__init__() + self.object = object + self.type = type + self.id = id + self.args = args + self.self_type = None + + +class IfNode(ExpressionNode): + def __init__(self, if_expr, then_expr, else_expr): + super().__init__() + self.if_expr = if_expr + self.then_expr = then_expr + self.else_expr = else_expr + + +class WhileNode(ExpressionNode): + def __init__(self, cond, body): + super().__init__() + self.cond = cond + self.body = body + + +class BlockNode(ExpressionNode): + def __init__(self, expressions): + super().__init__() + self.expressions = expressions + + +class LetNode(ExpressionNode): + def __init__(self, let_attrs, expr): + super().__init__() + self.let_attrs = let_attrs + self.expr = expr + + +class CaseNode(ExpressionNode): + def __init__(self, expr, case_list): + super().__init__() + self.expr = expr + self.case_list = case_list + + +class CaseElemNode(AstNode): + def __init__(self, expr, id, type): + super().__init__() + self.expr = expr + self.id = id + self.type = type + + +class BinaryNode(ExpressionNode): + def __init__(self, lvalue, rvalue): + super().__init__() + self.lvalue = lvalue + self.rvalue = rvalue + + +class PlusNode(BinaryNode): + pass + + +class MinusNode(BinaryNode): + pass + + +class StarNode(BinaryNode): + pass + + +class DivNode(BinaryNode): + pass + + +class LessThanNode(BinaryNode): + pass + + +class LessEqNode(BinaryNode): + pass + + +class EqNode(BinaryNode): + pass + + +class UnaryNode(ExpressionNode): + def __init__(self, val): + super().__init__() + self.val = val + + +class NegationNode(UnaryNode): + pass + + +class LogicNegationNode(UnaryNode): + pass + + +class AtomNode(UnaryNode): + pass + + +class IsVoidNode(UnaryNode): + pass + + +class VarNode(ExpressionNode): + def __init__(self, id): + super().__init__() + self.id = id + + +class NewNode(ExpressionNode): + def __init__(self, t): + super().__init__() + self.type = t + + +class ConstantNode(ExpressionNode): + def __init__(self, value): + super().__init__() + self.value = value + + +class IntNode(ConstantNode): + pass + + +class BoolNode(ConstantNode): + pass + + +class StringNode(ConstantNode): + pass diff --git a/src/lexer_parser/lexer.py b/src/lexer_parser/lexer.py new file mode 100644 index 00000000..f9ac7886 --- /dev/null +++ b/src/lexer_parser/lexer.py @@ -0,0 +1,226 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +import ply.lex as lex + +from errors import add_lexer_error +from .utils import * + +states = ( + ('commentLine', 'exclusive'), + ('commentText', 'exclusive'), + ('string', 'exclusive'), +) + +reserved = { + 'if': 'IF', + 'then': 'THEN', + 'else': 'ELSE', + 'fi': 'FI', + 'class': 'CLASS', + 'inherits': 'INHERITS', + 'while': 'WHILE', + 'loop': 'LOOP', + 'pool': 'POOL', + 'let': 'LET', + 'in': 'IN', + 'case': 'CASE', + 'isvoid': 'ISVOID', + 'esac': 'ESAC', + 'new': 'NEW', + 'of': 'OF', + 'not': 'LNOT' +} + +tokens = [ + 'ASSIGN', + 'ARROW', + 'LOWEREQ', + 'INT', + 'STRING', + 'TYPE', + 'ID', + 'SEMICOLON', + 'OBRACKET', + 'CBRACKET', + 'OPAREN', + 'CPAREN', + 'COLON', + 'AT', + 'DOT', + 'LOWER', + 'EQUAL', + 'PLUS', + 'MINUS', + 'STAR', + 'DIV', + 'NOT', + 'COMMA', + 'BOOL' +] + +t_SEMICOLON = r';' +t_OBRACKET = r'{' +t_CBRACKET = r'}' +t_OPAREN = r'\(' +t_CPAREN = r'\)' +t_COLON = r':' +t_AT = r'@' +t_DOT = r'\.' +t_LOWER = r'<' +t_EQUAL = r'=' +t_LOWEREQ = r'<=' +t_ASSIGN = r'<-' +t_ARROW = r'=>' +t_PLUS = r'\+' +t_MINUS = r'-' +t_STAR = r'\*' +t_DIV = r'/' +t_NOT = r'~' +t_COMMA = r',' + + +def t_INT(t): + r'\d+' + t.value = int(t.value) + return t + + +def t_TYPE(t): + r'[A-Z][a-zA-Z_0-9]*' + t.type = reserved.get(t.value.lower(), 'TYPE') + return t + + +def t_BOOL(t): + r'f[Aa][Ll][Ss][Ee]|t[Rr][Uu][Ee]' + t.value = (t.value.lower() == 'true') + return t + + +def t_ID(t): + r'[a-z][a-zA-Z_0-9]*' + t.type = reserved.get(t.value.lower(), 'ID') + return t + + +def t_LINECOMMENT(t): + r'--' + t.lexer.begin('commentLine') + + +def t_TEXTCOMMENT(t): + r'\(\*' + t.lexer.comment_start = t.lexer.lexpos + t.lexer.level = 1 + t.lexer.begin('commentText') + + +def t_STRING(t): + r'"' + t.lexer.string_start = t.lexer.lexpos + t.lexer.begin('string') + + +tokens += list(reserved.values()) + +t_ignore = ' \t' + +t_commentLine_ignore = ' \t' + + +def t_commentLine_error(t): + t.lexer.skip(1) + + +def t_commentLine_newline(t): + r'\n+' + t.lexer.begin('INITIAL') + t.lexer.lineno += len(t.value) + + +t_commentText_ignore = ' \t' + + +def t_commentText_error(t): + t.lexer.skip(1) + + +def t_commentText_OPENTEXT(t): + r'\(\*' + t.lexer.level += 1 + + +def t_commentText_CLOSETEXT(t): + r'\*\)' + t.lexer.level -= 1 + if t.lexer.level == 0: + t.lexer.begin('INITIAL') + + +def t_commentText_newline(t): + r'\n+' + t.lexer.lineno += len(t.value) + + +def t_commentText_eof(t): + add_lexer_error(t.lexer.lineno, find_column(t.lexer.lexdata, t.lexer.lexpos), "EOF in comment") + + +t_string_ignore = '' + + +def t_string_CLOSESTRING(t): + r'"' + t.value = t.lexer.lexdata[t.lexer.string_start:t.lexer.lexpos - 1] + t.type = 'STRING' + t.lexer.begin('INITIAL') + return t + + +def t_string_newline(t): + r'\\\n' + t.lexer.lineno += 1 + + +def t_string_body(t): + r'([^\n\"\\]|\\.)+' + if t.value.rfind('\0') != -1: + add_lexer_error(t.lineno, find_column(t.lexer.lexdata, t.lexpos) + t.value.rfind('\0'), + "String contains null character") + + +def t_string_error(t): + if t.value[0] == '\n': + add_lexer_error(t.lineno, find_column(t.lexer.lexdata, t.lexpos), "Unterminated string constant") + t.lexer.lineno += 1 + t.lexer.skip(1) + t.lexer.begin('INITIAL') + + +def t_string_eof(t): + add_lexer_error(t.lineno, find_column(t.lexer.lexdata, t.lexpos), "Unterminated string constant") + + +def t_error(t): + add_lexer_error(t.lineno, find_column(t.lexer.lexdata, t.lexpos), f'ERROR \"{t.value[0]}\"') + t.lexer.skip(1) + + +def t_newline(t): + r'\n+' + t.lexer.lineno += len(t.value) + + +def test(data): + lexer.input(data) + while True: + tok = lexer.token() + if not tok: + break + + +lexer = lex.lex() diff --git a/src/lexer_parser/parser.py b/src/lexer_parser/parser.py new file mode 100644 index 00000000..27fe948a --- /dev/null +++ b/src/lexer_parser/parser.py @@ -0,0 +1,304 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +import ply.yacc as yacc + +from errors import add_parser_error +from .ast import * +from .lexer import * + +precedence = ( + ('left', 'AT'), + ('left', 'NOT'), + ('left', 'ISVOID'), + ('left', 'LNOT'), + ('left', 'LOWEREQ', 'LOWER', 'EQUAL'), + ('left', 'PLUS', 'MINUS'), + ('left', 'STAR', 'DIV'), + ('left', 'DOT'), +) + + +def p_program(p): + 'program : class_list' + p[0] = ProgramNode(p[1]) + + +def p_empty(p): + 'empty :' + pass + + +def p_class_list(p): + '''class_list : def_class SEMICOLON class_list + | def_class SEMICOLON''' + + try: + p[0] = [p[1]] + p[3] + except IndexError: + p[0] = [p[1]] + + +def p_def_class(p): + '''def_class : CLASS TYPE OBRACKET feature_list CBRACKET + | CLASS TYPE INHERITS TYPE OBRACKET feature_list CBRACKET''' + if len(p) == 8: + p[0] = DefClassNode(p[2], p[6], p[4]) + else: + p[0] = DefClassNode(p[2], p[4]) + + p[0].add_location(p.lineno(2), find_column(p.lexer.lexdata, p.lexpos(2))) + + +def p_feature_list(p): + '''feature_list : def_attr SEMICOLON feature_list + | def_func SEMICOLON feature_list + | empty''' + if len(p) == 4: + p[0] = [p[1]] + p[3] + else: + p[0] = [] + + +def p_def_attr_declaration(p): + '''def_attr : ID COLON TYPE ASSIGN expr + | ID COLON TYPE''' + try: + p[0] = DefAttrNode(p[1], p[3], p[5]) + except IndexError: + p[0] = DefAttrNode(p[1], p[3]) + + p[0].add_location(p.lineno(0), find_column(p.lexer.lexdata, p.lexpos(0))) + + +def p_def_func(p): + '''def_func : ID OPAREN params CPAREN COLON TYPE OBRACKET expr CBRACKET''' + p[0] = DefFuncNode(p[1], p[3], p[6], p[8]) + p[0].add_location(p.lineno(6), find_column(p.lexer.lexdata, p.lexpos(6))) + + +def p_params_ne(p): + '''params : param_list''' + p[0] = p[1] + + +def p_params_e(p): + '''params : empty''' + p[0] = [] + + +def p_param_list(p): + '''param_list : param COMMA param_list + | param empty''' + try: + p[0] = [p[1]] + p[3] + except IndexError: + p[0] = [p[1]] + + +def p_param(p): + # noinspection PySingleQuotedDocstring + '''param : ID COLON TYPE''' + p[0] = (p[1], p[3]) + + +def p_expr_flow(p): + '''expr : LET let_attrs IN expr + | CASE expr OF case_list ESAC + | IF expr THEN expr ELSE expr FI + | WHILE expr LOOP expr POOL''' + + if p[1].lower() == 'let': + p[0] = LetNode(p[2], p[4]) + elif p[1].lower() == 'case': + p[0] = CaseNode(p[2], p[4]) + elif p[1].lower() == 'if': + p[0] = IfNode(p[2], p[4], p[6]) + elif p[1].lower() == 'while': + p[0] = WhileNode(p[2], p[4]) + + p[0].add_location(p.lineno(2), find_column(p.lexer.lexdata, p.lexpos(2))) + + +def p_expr_assign(p): + '''expr : ID ASSIGN expr''' + p[0] = AssignNode(p[1], p[3]) + p[0].add_location(p.lineno(1), find_column(p.lexer.lexdata, p.lexpos(1))) + + +def p_expr_func_all(p): + '''expr : expr AT TYPE DOT ID OPAREN arg_list CPAREN + | expr DOT ID OPAREN arg_list CPAREN + | ID OPAREN arg_list CPAREN''' + if len(p) == 9: + if p[7] is None: + p[7] = [] + p[0] = FuncCallNode(p[5], p[7], p[1], p[3]) + p[0].add_location(p.lineno(5), find_column( + p.lexer.lexdata, p.lexpos(5))) + elif len(p) == 7: + if p[5] is None: + p[5] = [] + p[0] = FuncCallNode(p[3], p[5], p[1]) + p[0].add_location(p.lineno(3), find_column( + p.lexer.lexdata, p.lexpos(3))) + else: + if p[3] is None: + p[3] = [] + p[0] = FuncCallNode(p[1], p[3]) + p[0].add_location(p.lineno(1), find_column( + p.lexer.lexdata, p.lexpos(1))) + + p[0].lineno = p.lineno(0) + + +def p_expr_operators_binary(p): + '''expr : expr PLUS expr + | expr MINUS expr + | expr STAR expr + | expr DIV expr + | expr LOWER expr + | expr LOWEREQ expr + | expr EQUAL expr''' + if p[2] == '+': + p[0] = PlusNode(p[1], p[3]) + elif p[2] == '-': + p[0] = MinusNode(p[1], p[3]) + elif p[2] == '*': + p[0] = StarNode(p[1], p[3]) + elif p[2] == '/': + p[0] = DivNode(p[1], p[3]) + elif p[2] == '<': + p[0] = LessThanNode(p[1], p[3]) + elif p[2] == '<=': + p[0] = LessEqNode(p[1], p[3]) + elif p[2] == '=': + p[0] = EqNode(p[1], p[3]) + + p[0].add_location(p.lineno(0), find_column(p.lexer.lexdata, p.lexpos(0))) + + +def p_expr_operators_unary(p): + '''expr : NOT expr + | ISVOID expr + | LNOT expr''' + if p[1] == '~': + p[0] = NegationNode(p[2]) + elif p[1].lower() == 'isvoid': + p[0] = IsVoidNode(p[2]) + elif p[1].lower() == 'not': + p[0] = LogicNegationNode(p[2]) + + p[0].add_location(p.lineno(2), find_column(p.lexer.lexdata, p.lexpos(2))) + + +def p_expr_group(p): + '''expr : OPAREN expr CPAREN''' + p[0] = p[2] + + +def p_expr_atom(p): + '''expr : atom''' + p[0] = p[1] + + +def p_let_attrs(p): + '''let_attrs : def_attr COMMA let_attrs + | def_attr''' + try: + p[0] = [p[1]] + p[3] + except IndexError: + p[0] = [p[1]] + + +def p_case_list(p): + '''case_list : case_elem SEMICOLON case_list + | case_elem SEMICOLON''' + try: + p[0] = [p[1]] + p[3] + except IndexError: + p[0] = [p[1]] + + +def p_case_elem(p): + '''case_elem : ID COLON TYPE ARROW expr''' + p[0] = CaseElemNode(p[5], p[1], p[3]) + p[0].add_location(p.lineno(3), find_column(p.lexer.lexdata, p.lexpos(3))) + + +def p_arg_list(p): + '''arg_list : arg_list_ne + | empty''' + p[0] = p[1] + + +def p_arg_list_ne(p): + '''arg_list_ne : expr COMMA arg_list_ne + | expr ''' + if len(p) == 4: + p[0] = [p[1]] + p[3] + else: + p[0] = [p[1]] + + +def p_atom_int(p): + '''atom : INT''' + p[0] = IntNode(int(p[1])) + p[0].add_location(p.lineno(1), find_column(p.lexer.lexdata, p.lexpos(1))) + + +def p_atom_id(p): + '''atom : ID''' + p[0] = VarNode(p[1]) + p[0].add_location(p.lineno(1), find_column(p.lexer.lexdata, p.lexpos(1))) + + +def p_atom_new(p): + '''atom : NEW TYPE''' + p[0] = NewNode(p[2]) + p[0].add_location(p.lineno(2), find_column(p.lexer.lexdata, p.lexpos(2))) + + +def p_atom_block(p): + '''atom : block''' + p[0] = p[1] + + +def p_atom_bool(p): + '''atom : BOOL''' + p[0] = BoolNode(p[1]) + p[0].add_location(p.lineno(1), find_column(p.lexer.lexdata, p.lexpos(1))) + + +def p_atom_atring(p): + '''atom : STRING''' + p[0] = StringNode(p[1]) + p[0].add_location(p.lineno(1), find_column(p.lexer.lexdata, p.lexpos(1))) + + +def p_block(p): + '''block : OBRACKET block_list CBRACKET''' + p[0] = p[2] + + +def p_block_list(p): + '''block_list : expr SEMICOLON block_list + | expr SEMICOLON''' + if len(p) == 4: + p[0] = BlockNode([p[1]] + p[3].expressions) + else: + p[0] = BlockNode([p[1]]) + + +def p_error(p): + if p: + add_parser_error(p.lineno, find_column( + p.lexer.lexdata, p.lexpos), f'ERROR at or near \'{p.value}\'') + else: + add_parser_error(0, 0, "ERROR at or near EOF") + + +parser = yacc.yacc() diff --git a/src/lexer_parser/utils.py b/src/lexer_parser/utils.py new file mode 100644 index 00000000..25e652d2 --- /dev/null +++ b/src/lexer_parser/utils.py @@ -0,0 +1,11 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + + +def find_column(text, pos): + line_start = text.rfind('\n', 0, pos) + 1 + tabs = text.count('\t', line_start, pos) + return (pos - line_start) + 1 + 3 * tabs diff --git a/src/main.py b/src/main.py new file mode 100644 index 00000000..a6974aa3 --- /dev/null +++ b/src/main.py @@ -0,0 +1,56 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +import sys + +import errors as err +from code_generation import generate_code +from lexer_parser import lexer, parser +from semantic import semantic_check + + +def exit_with_error(error): + print(f'CompilerError: {error}') + exit(1) + + +def main(): + if len(sys.argv) != 2: + exit_with_error("invalid number of arguments") + + input_data = "" + input_file = sys.argv[1] + try: + with open(input_file) as f: + input_data = f.read() + except FileNotFoundError: + exit_with_error(f'file {sys.argv[1]} not found') + + ast = parser.parse(input_data, lexer, tracking=True) + if err.LEXER_ERRORS: + for e in err.LEXER_ERRORS: + print(e) + exit(1) + if err.PARSER_ERRORS: + for e in err.PARSER_ERRORS: + print(e) + exit(1) + + semantic_check(ast) + if err.SEMANTIC_ERRORS: + for e in err.SEMANTIC_ERRORS: + print(e) + exit(1) + + cil_code = generate_code(ast) + output_file = input_file[0:-2] + 'mips' + + with open(output_file, "w") as output: + output.write(cil_code) + + +if __name__ == "__main__": + main() diff --git a/src/semantic/__init__.py b/src/semantic/__init__.py new file mode 100644 index 00000000..d883ab27 --- /dev/null +++ b/src/semantic/__init__.py @@ -0,0 +1,7 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +from .semantic import semantic_check diff --git a/src/semantic/semantic.py b/src/semantic/semantic.py new file mode 100644 index 00000000..12c46f02 --- /dev/null +++ b/src/semantic/semantic.py @@ -0,0 +1,722 @@ +""" +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +COOL compiler project +""" + +import cool_types as CT +from lexer_parser.ast import * +from errors import * + + +def program_visitor(program: ProgramNode): + ''' + Check semantic for a program. + 1) Check type declaration (check duplicated types...) + 2) Check type hierarchy + 3) Check class Main + 4) Check all methos and attributes for all declared types, and add them to respective type + 5) Check recursive each delared class + ''' + # 1) and 2) + if not (CT.check_type_declaration(program) and CT.check_type_hierarchy(program)): + return + + # 3) + try: + CT.TypesByName['Main'] + except KeyError: + add_semantic_error(0, 0, f'Main class undeclared') + return + + # 4) + types_already_check = [CT.ObjectType, CT.IntType, + CT.StringType, CT.BoolType, CT.IOType] + classes = program.classes.copy() + while len(classes) != 0: + c: DefClassNode = classes.pop() + if c.parent_type is not None: + parent_type = CT.TypesByName[c.parent_type] + if parent_type not in types_already_check: + classes = [c] + classes + continue + classType = CT.TypesByName[c.type] + for f in c.feature_nodes: + if type(f) is DefFuncNode: + param_types = [param[1] for param in f.params] + result, msg = classType.add_method( + f.id, param_types, f.return_type) + if not result: + add_semantic_error(f.lineno, f.colno, msg) + elif type(f) is DefAttrNode: + result, msg = classType.add_attr(f.id, f.type, f.expr) + if not result: + add_semantic_error(f.lineno, f.colno, msg) + types_already_check.append(classType) + + # 5) + for c in program.classes: + class_visitor(c, None, {}) + + +def class_visitor(_class: DefClassNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check class + + class MY_TYPE inherits INHERITS_TYPE{ + FEATURE_LIST + } + + 1) Check each feature in FEATURE_LIST + ''' + current_class = CT.TypesByName[_class.type] + local_scope = local_scope.copy() + local_scope['self'] = current_class + # 1) + for feature in _class.feature_nodes: + if type(feature) is DefAttrNode: + def_attr_class_visitor(feature, current_class, local_scope) + if type(feature) is DefFuncNode: + def_func_visitor(feature, current_class, local_scope) + + +def def_attr_class_visitor(attr: DefAttrNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check class attribute + + ID:TYPE <- EXPR + + If EXPR is not None: + 1) Check EXPR + 2) Check type(EXPR) <= TYPE + ''' + if attr.id == 'self': + add_semantic_error(attr.lineno, attr.colno, + f'{ERR_SEMANTIC}: \'self\' cannot be the name of an attribute.') + if attr.expr: + # 1) + expr_type = expression_visitor(attr.expr, current_class, local_scope) + attr_type = CT.type_by_name(attr.type) + # 2) + if attr_type is not None and not CT.check_inherits(expr_type, attr_type): + add_semantic_error( + attr.lineno, attr.expr.colno, f'{ERR_TYPE}: Inferred type {expr_type} of initialization of attribute d does not conform to declared type {attr_type}.') + else: + return attr_type + + +def def_attribute_visitor(def_attr: DefAttrNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check attribute (except class attribute) + + ID:TYPE <- EXPR + + 1) Check if TYPE exists + 2) Check if EXPR is not None + 2.1) Check EXPR + 2.2) Check type(EXPR) <= TYPE + 3) Type of the attribute is TYPE + ''' + # 1) + id_type = CT.type_by_name(def_attr.type) + if id_type is None: + add_semantic_error(def_attr.lineno, def_attr.colno, + f'{ERR_TYPE}: Class {def_attr.type} of let-bound identifier {def_attr.id} is undefined') + # 2) + if def_attr.expr: + # 2.1) + expr_type = expression_visitor( + def_attr.expr, current_class, local_scope) + # 2.2) + if not CT.check_inherits(expr_type, id_type) and id_type is not None and expr_type is not None: + add_semantic_error(def_attr.lineno, def_attr.colno, + f'{ERR_TYPE}: Inferred type {expr_type} of initialization of {def_attr.id} does not conform to identifier\'s declared type {id_type}.') + # 3) + return id_type + + +def def_func_visitor(function: DefFuncNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check function declaration + + ID (PARAMS): RETURN_TYPE{ + EXPR + } + + 1) Add PARAMS to local_scope. Type of PARAMS already check in program_visitor(4) + 2) Check EXPR + 3) Update RETURN_TYPE with current class type if that is SELFTYPE + 4) Check type(EXPR) <= RETURN_TYPE + ''' + local_scope = local_scope.copy() + # 1) + arg_names = set() + for arg in function.params: + if arg[0] == 'self': + add_semantic_error(function.lineno, function.colno, + f'{ERR_SEMANTIC}: \'self\' cannot be the name of a formal parameter.') + if arg[0] in arg_names: + add_semantic_error( + function.lineno, function.colno, f'{ERR_SEMANTIC}: Formal parameter {arg[0]} is multiply defined.') + return + arg_names.add(arg[0]) + local_scope[arg[0]] = CT.type_by_name(arg[1]) + # 2) + body_type = expression_visitor( + function.expressions, current_class, local_scope) + return_type = CT.type_by_name(function.return_type) + # 3) + if return_type == CT.SelfType: + return_type = current_class + # 4) + if CT.check_inherits(body_type, return_type): + return return_type + elif body_type is not None: + add_semantic_error(function.lineno, function.colno, + f'{ERR_TYPE}: Inferred return type {body_type} of method {function.id} does not conform to declared return type {return_type}.') + + +def assignment_visitor(assign: AssignNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check assigment + + ID <- EXPR + + 1) Check type(ID) + 1.1) First find in local_scope + 1.2) Second find in current class attributes + 2) Check EXPR + 3) Check type(EXPR) <= type(ID) + 4) Type of assigment is type(EXPR) + ''' + if assign.id == 'self': + add_semantic_error(assign.lineno, assign.colno, + f'{ERR_SEMANTIC}: Cannot assign to \'self\'.') + # 1) + try: + # 1.1) + id_type = local_scope[assign.id] + except KeyError: + # 1.2) + attr, _ = CT.get_attribute(current_class, assign.id) + if attr is None: + add_semantic_error(assign.id.lineno, assign.id.colno, + f'{ERR_NAME}: Undeclared identifier {assign.id}.') + id_type = None + else: + id_type = attr.attrType + # 2) + expr_type = expression_visitor(assign.expr, current_class, local_scope) + # 3) + if not CT.check_inherits(expr_type, id_type) and id_type is not None and expr_type is not None: + add_semantic_error(assign.expr.lineno, assign.expr.colno, + f'{ERR_TYPE}: Inferred type {expr_type} of initialization of {assign.id} does not conform to identifier\'s declared type {id_type}.') + # 4) + assign.returned_type = expr_type + return expr_type + + +def func_call_visitor(func_call: FuncCallNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check function call + + Exist three forms of function call: + + 1- EXPR@TYPE.ID(ARGS) + 2- EXPR.ID(ARGS) + 3- ID(ARGS) + + 1) Compute args type, not check yet, because check need wait to find the specific function + 2) EXPR is not None (cases 1 and 2) + 2.1) Check type(EXPR) + 2.2) TYPE is not None + 2.2.1) Check TYPE + 2.2.2) Check type(EXPR) <= TYPE + 2.2.3) If returned type of the funtion is SELFTYPE then returned type is TYPE + 3) If EXPR is None + 3.2) If returned type of the function is SELFTYPE then returned type is current class type + 4) Type of function call is the returned type of function + + ''' + args_types = [] + method = None + msg = None + func_call.self_type = current_class + # 1) + for arg in func_call.args: + arg_type = expression_visitor(arg, current_class, local_scope) + args_types.append(arg_type) + # 2) + if func_call.object: + # 2.1) + object_type = expression_visitor( + func_call.object, current_class, local_scope) + if object_type is None: + return None + # 2.2) + if func_call.type: + # 2.2.1) + specific_type = CT.type_by_name(func_call.type) + if specific_type is None: + add_semantic_error( + func_call.lineno, func_call.colno, f'unknown type \'{func_call.type}\'') + # 2.2.2) + elif CT.check_inherits(object_type, specific_type): + method, _, msg = specific_type.get_method_without_hierarchy( + func_call.id, args_types) + # 2.2.3) + if method is not None and method.returnedType == CT.SelfType: + method.returnedType = specific_type + else: + add_semantic_error(func_call.lineno, func_call.colno, + f'{ERR_TYPE}: Expression type {object_type} does not conform to declared static dispatch type {specific_type}.') + return None + else: + method, _, msg = object_type.get_method(func_call.id, args_types) + if method is not None and method.returnedType == CT.SelfType: + method.returnedType = object_type + # 3) + else: + method, _, msg = current_class.get_method(func_call.id, args_types) + if method is None and msg is not None: + add_semantic_error(func_call.lineno, func_call.colno, msg) + # 3.1) + elif method.returnedType == CT.SelfType: + func_call.returned_type = current_class + else: + # 4) + func_call.returned_type = method.returnedType + return func_call.returned_type + + +def if_visitor(if_struct: IfNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check \"if\" stament + + if IF_EXPR then THEN_EXPR else ELSE_EXPR fi + + 1) Check IF_EXPR. type(IF_EXPR) must be a Bool + 2) Check THEN_EXPR + 3) Check ELSE_EXPR + 4) Type of \"if\" stament is the pronounced join of THEN_EXPR and ELSE_EXPR + ''' + # 1) + predicate_type = expression_visitor( + if_struct.if_expr, current_class, local_scope) + if predicate_type != CT.BoolType and predicate_type is not None: + add_semantic_error(if_struct.if_expr.lineno, if_struct.if_expr.colno, + f'{ERR_TYPE}: Predicate of \'if\' does not have type {CT.BoolType}.') + # 2) + then_type = expression_visitor( + if_struct.then_expr, current_class, local_scope) + # 3) + else_type = expression_visitor( + if_struct.else_expr, current_class, local_scope) + # 4) + if_struct.returned_type = CT.pronounced_join(then_type, else_type) + return if_struct.returned_type + + +def loop_expr_visitor(loop: WhileNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check loop + + while CONDITION_EXPR loop EXPR pool + + 1) Check CONDITION_EXPR. type(CONDITION_EXPR) must be a Bool + 2) Check EXPR + 3) Type of loop is Oject + ''' + # 1) + predicate_type = expression_visitor(loop.cond, current_class, local_scope) + if predicate_type != CT.BoolType and predicate_type is not None: + add_semantic_error(loop.cond.lineno, loop.cond.colno, + f'{ERR_TYPE}: Loop condition does not have type {CT.BoolType}.') + # 2) + expression_visitor(loop.body, current_class, local_scope) + # 3) + loop.returned_type = CT.ObjectType + return CT.ObjectType + + +def block_expr_visitor(block: BlockNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check block + + { EXPR_LIST } + + 1) Check each epression in EXPR_LIST + 2) Type of block is type of the last expression in EXPR_LIST + ''' + final_type = None + # 1) + for expr in block.expressions: + final_type = expression_visitor(expr, current_class, local_scope) + # 2) + block.returned_type = final_type + return final_type + + +def let_visitor(let: LetNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check let + + let LET_ATTRS in EXPR + + 1) Check all attributes in LET_ATTRS + 2) Check EXPR + 3) Type of let is type(EXPR) + + + ''' + local_scope = local_scope.copy() + # 1) + for attribute in let.let_attrs: + if attribute.id == 'self': + add_semantic_error( + attribute.lineno, attribute.colno, f'{ERR_SEMANTIC}: \'self\' cannot be bound in a \'let\' expression.') + attribute_type = expression_visitor( + attribute, current_class, local_scope) + if attribute_type is None: + return None + local_scope[attribute.id] = attribute_type + # 2) and 3) + let.returned_type = expression_visitor( + let.expr, current_class, local_scope) + return let.returned_type + + +def case_expr_visitor(case: CaseNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check case + + case EXPR_0 of + ID_1:TYPE_1 => EXPR_1; + ... + ... + ID_n:TYPE_n => EXPR_n; + esac + + 1) Check EXPR_0 + 2) Check first branch + 2.1) Check TYPE_1 + 2.2) Update local scope with ID_1 + 2.3) Check EXPR_1 and set current type as type(EXPR_1) + 3) Check rest of branches (k=2,...,n) + 3.1) Check TYPE_k + 3.2) Update local scope with ID_k + 3.3) Check EXPR_k and set current type as pronounced join of current type and type(EXPR_k) + 4) Type of case is the pronounced join of all branch expressions types, then is the current type at final of step 3) + ''' + + branch_types = set() + # 1) + expr_0 = expression_visitor(case.expr, current_class, local_scope) + + # 2) + branch_0 = case.case_list[0] + branch_types.add(branch_0.type) + # 2.1) + branch_0_type = CT.type_by_name(branch_0.type) + temp = local_scope.copy() + if branch_0_type is None: + add_semantic_error(branch_0.lineno, branch_0.colno, + f'{ERR_TYPE}: Class {branch_0.type} of case branch is undefined.') + else: + # 2.2) + temp[branch_0.id] = branch_0_type + # 2.3) + current_type = expression_visitor(branch_0.expr, current_class, temp) + + # 3) + for branch in case.case_list[1:]: + if branch.type in branch_types: + add_semantic_error(branch.lineno, branch.colno, + f'{ERR_SEMANTIC}: Duplicate branch {branch.type} in case statement.') + temp = local_scope.copy() + # 3.1) + branch_type = CT.type_by_name(branch.type) + if branch_type is None: + add_semantic_error(branch.lineno, branch.colno, + f'{ERR_TYPE}: Class {branch.type} of case branch is undefined.') + # 3.2) + temp[branch.id] = branch_type + # 3.3) + current_type = CT.pronounced_join( + current_type, expression_visitor(branch.expr, current_class, temp)) + # 4) + case.returned_type = current_type + return case.returned_type + + +def arithmetic_operator_visitor(operator: BinaryNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check arithmetic operator (binary) + + LEXPR operator REXPR + + 1) Check LEXPR, must be Int + 2) Check REXPR, must be a Int + 3) Type of the arithmetic operator (binary) is Int + ''' + # 1) + lvalue_type = expression_visitor( + operator.lvalue, current_class, local_scope) + if lvalue_type != CT.IntType and lvalue_type is not None: + add_semantic_error(operator.lineno, operator.colno, + f'{ERR_TYPE}: non-Int arguments: {lvalue_type} + {CT.IntType}') + # 2) + rvalue_type = expression_visitor( + operator.rvalue, current_class, local_scope) + if rvalue_type != CT.IntType and rvalue_type is not None: + add_semantic_error(operator.lineno, operator.colno, + f'{ERR_TYPE}: non-Int arguments: {CT.IntType} + {rvalue_type}') + # 3) + operator.returned_type = CT.IntType + return CT.IntType + + +def comparison_visitor(cmp: BinaryNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check comparison + + LEXPR operator REXPR + + 1) Check LEXPR, must be Int + 2) Check REXPR, must be Int + 3) Type of the comparison is Bool + ''' + # 1) + lvalue_type = expression_visitor(cmp.lvalue, current_class, local_scope) + if lvalue_type != CT.IntType and lvalue_type is not None: + add_semantic_error(cmp.lvalue.lineno, cmp.lvalue.colno, + f'{ERR_TYPE}: non-{CT.IntType} arguments: {lvalue_type} < {CT.IntType}') + # 2) + rvalue_type = expression_visitor(cmp.rvalue, current_class, local_scope) + if rvalue_type != CT.IntType and rvalue_type is not None: + add_semantic_error(cmp.rvalue.lineno, cmp.rvalue.colno, + f'{ERR_TYPE}: non-{CT.IntType} arguments: {CT.IntType} < {rvalue_type}') + # 3) + cmp.returned_type = CT.BoolType + return CT.BoolType + + +def equal_visitor(equal: EqNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check equal + + LEXPR = REXPR + + 1) Check LEXPR + 2) Check REXPR + 3) Check type(LEXPR) is equal to type(REXPR) and must be Int, Bool or String + 4) Type of the equal is Bool + ''' + # 1) + lvalue_type = expression_visitor(equal.lvalue, current_class, local_scope) + # 2) + rvalue_type = expression_visitor(equal.rvalue, current_class, local_scope) + # 3) + static_types = [CT.IntType, CT.BoolType, CT.StringType] + if (lvalue_type in static_types or rvalue_type in static_types) and lvalue_type != rvalue_type: + add_semantic_error(equal.lineno, equal.colno, + f'{ERR_TYPE}: Illegal comparison with a basic type.') + # 4) + equal.returned_type = CT.BoolType + return CT.BoolType + + +def negation_visitor(negation: NegationNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check binary negation + + ~EXPR + + 1) Check EXPR + 2) Check type(EXPR) must be Int + 3) Type of the binary negation (~) is Int + ''' + # 1) + value_type = expression_visitor(negation.val, current_class, local_scope) + # 2) + if value_type != CT.IntType and value_type is not None: + add_semantic_error(negation.lineno, negation.colno, + f'{ERR_TYPE}: Argument of \'~\' has type {value_type} instead of {CT.IntType}.') + # 3) + negation.returned_type=CT.BoolType + return CT.IntType + + +def logic_negation_visitor(negation: LogicNegationNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check logic negation + + not EXPR + + 1) Check EXPR + 2) Check type(EXPR) must be Bool + 3) Type of the logic negation is Bool + + ''' + # 1) + value_type = expression_visitor(negation.val, current_class, local_scope) + # 2) + if value_type != CT.BoolType and value_type is not None: + add_semantic_error(negation.lineno, negation.colno, + f'{ERR_TYPE}: Argument of \'not\' has type {CT.IntType} instead of {value_type}.') + # 3) + negation.returned_type=CT.BoolType + return CT.BoolType + + +def is_void_expr_visitor(isvoid: IsVoidNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check isvoid + + isvoid EXPR + + 1) Check EXPR + 2) Type of the isvoid is Bool + ''' + # 1) + expression_visitor(isvoid.val, current_class, local_scope) + # 2) + isvoid.returned_type = CT.BoolType + return CT.BoolType + + +def var_visitor(var: VarNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check var + + ID + + 1) Check if ID is in local scope + 2) Check if ID is an class attribute + 3) Type of var is the already funded type + ''' + # 1) + if var.id in local_scope.keys(): + var.returned_type = local_scope[var.id] + else: + # 2) + attribute, _ = CT.get_attribute(current_class, var.id) + if attribute is not None: + var.returned_type = attribute.attrType + else: + add_semantic_error(var.lineno, var.colno, + f'{ERR_NAME}: Undeclared identifier {var.id}.') + # 3) + return var.returned_type + + +def new_expr_visitor(new: NewNode, current_class: CT.CoolType, local_scope: dict): + ''' + Check new + + new TYPE + + 1) Check TYPE exists + 2) Is TYPE is SELFTYPE then TYPE is updated to current class type + 3) Type of new is TYPE + ''' + # 1) + t = CT.type_by_name(new.type) + if not t: + add_semantic_error( + new.lineno, new.colno, f'{ERR_TYPE}: \'new\' used with undefined class {new.type}.') + # 2) + if t == CT.SelfType: + new.returned_type = current_class + else: + new.returned_type = t + # 3) + return new.returned_type + + +def int_visitor(expr: IntNode, current_class, local_scope): + ''' + Check int + + number + + 1) Type os int is Int :) + ''' + # 1) + expr.returned_type = CT.IntType + return CT.IntType + + +def bool_visitor(expr: BoolNode, current_class, local_scope): + ''' + Check bool + + [True|Fsle] + + 1) Type of bool is Bool :) + ''' + # 1) + expr.returned_type = CT.BoolType + return CT.BoolType + + +def string_visitor(expr: StringNode, current_class, local_scope): + ''' + Check string + + "string" + + 1) Type of string is String :) + ''' + expr.returned_type = CT.StringType + return CT.StringType + + +__visitors__ = { + BlockNode: block_expr_visitor, + IntNode: int_visitor, + StringNode: string_visitor, + BoolNode: bool_visitor, + VarNode: var_visitor, + PlusNode: arithmetic_operator_visitor, + MinusNode: arithmetic_operator_visitor, + StarNode: arithmetic_operator_visitor, + DivNode: arithmetic_operator_visitor, + EqNode: equal_visitor, + NegationNode: negation_visitor, + LogicNegationNode: logic_negation_visitor, + LessThanNode: comparison_visitor, + LessEqNode: comparison_visitor, + AssignNode: assignment_visitor, + DefAttrNode: def_attribute_visitor, + LetNode: let_visitor, + IfNode: if_visitor, + FuncCallNode: func_call_visitor, + CaseNode: case_expr_visitor, + IsVoidNode: is_void_expr_visitor, + WhileNode: loop_expr_visitor, + NewNode: new_expr_visitor +} + + +def expression_visitor(expression, current_class: CT.CoolType, local_scope: dict) -> CT.CoolType: + ''' + This is the main function to check any expression. This function search the + right function for check the expression. + + expression: expression to check + current_class: class containing the expression + local_scope: local scope at the moment + ''' + try: + return __visitors__[type(expression)](expression, current_class, local_scope) + except KeyError: + print(f'Not visitor for {expression}') + + +def semantic_check(node): + ''' + Start semantic check for the entire program. It only works if the node type is ProgramNode + ''' + if type(node) is ProgramNode: + program_visitor(node)