From 54aa1f4ca644f977cf5fd8755047c0e3ffb8ff77 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 11 Feb 2020 18:02:26 -0500 Subject: [PATCH 001/520] Populating the project Added files developed in previous Type-Inferencer project --- .../core/cmp/CoolUtils.py | 359 +++++ .../core/cmp/__init__.py | 0 src/Files_from_TypeInferencer/core/cmp/ast.py | 62 + .../core/cmp/automata.py | 207 +++ .../core/cmp/evaluation.py | 33 + .../core/cmp/functions.py | 261 ++++ .../core/cmp/grammartools.py | 753 ++++++++++ .../core/cmp/languages.py | 228 +++ .../core/cmp/nbpackage.py | 87 ++ .../core/cmp/pycompiler.py | 512 +++++++ .../core/cmp/semantic.py | 246 ++++ .../core/cmp/utils.py | 276 ++++ .../core/cmp/visitor.py | 80 ++ .../core/cmp/visitors.py | 1253 +++++++++++++++++ src/Files_from_TypeInferencer/main.py | 112 ++ 15 files changed, 4469 insertions(+) create mode 100644 src/Files_from_TypeInferencer/core/cmp/CoolUtils.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/__init__.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/ast.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/automata.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/evaluation.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/functions.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/grammartools.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/languages.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/nbpackage.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/pycompiler.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/semantic.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/utils.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/visitor.py create mode 100644 src/Files_from_TypeInferencer/core/cmp/visitors.py create mode 100644 src/Files_from_TypeInferencer/main.py diff --git a/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py b/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py new file mode 100644 index 00000000..fb4e25b3 --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py @@ -0,0 +1,359 @@ +from core.cmp.pycompiler import Grammar +from core.cmp.functions import LR1Parser +from core.cmp.utils import Token, tokenizer + +# AST Classes +class Node: + pass + +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations + +class DeclarationNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent='Object'): + self.id = idx + self.parent = parent + self.features = features + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr=None): + self.id = idx + self.type = typex + self.expr = expr + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body): + self.id = idx + self.params = params + self.type = return_type + self.body = body + +class ExpressionNode(Node): + pass + +class IfThenElseNode(ExpressionNode): + def __init__(self, condition, if_body, else_body): + self.condition = condition + self.if_body = if_body + self.else_body = else_body + +class WhileLoopNode(ExpressionNode): + def __init__(self, condition, body): + self.condition = condition + self.body = body + +class BlockNode(ExpressionNode): + def __init__(self, exprs): + self.exprs = exprs + +class LetInNode(ExpressionNode): + def __init__(self, let_body, in_body): + self.let_body = let_body + self.in_body = in_body + +class CaseOfNode(ExpressionNode): + def __init__(self, expr, branches): + self.expr = expr + self.branches = branches + +class CaseExpressionNode(AttrDeclarationNode): + pass + +class LetAttributeNode(AttrDeclarationNode): + pass + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr): + self.id = idx + self.expr= expr + +class UnaryNode(ExpressionNode): + def __init__(self, expr): + self.expr = expr + +class NotNode(UnaryNode): + pass + +class BinaryNode(ExpressionNode): + def __init__(self, left, right): + self.left = left + self.right = right + +class LessEqualNode(BinaryNode): + pass + +class LessNode(BinaryNode): + pass + +class EqualNode(BinaryNode): + pass + +class ArithmeticNode(BinaryNode): + pass + +class PlusNode(ArithmeticNode): + pass + +class MinusNode(ArithmeticNode): + pass + +class StarNode(ArithmeticNode): + pass + +class DivNode(ArithmeticNode): + pass + +class IsVoidNode(UnaryNode): + pass + +class ComplementNode(UnaryNode): + pass + +class FunctionCallNode(ExpressionNode): + def __init__(self, obj, idx, args, typex=None): + self.obj = obj + self.id = idx + self.args = args + self.type = typex + +class MemberCallNode(ExpressionNode): + def __init__(self, idx, args): + self.id = idx + self.args = args + +class NewNode(ExpressionNode): + def __init__(self, typex): + self.type = typex + +class AtomicNode(ExpressionNode): + def __init__(self, lex): + self.lex = lex + +class IntegerNode(AtomicNode): + pass + +class IdNode(AtomicNode): + pass + +class StringNode(AtomicNode): + pass + +class BoolNode(AtomicNode): + pass + + +# Grammar + +CoolGrammar = Grammar() + +# non-terminals +program = CoolGrammar.NonTerminal('', startSymbol=True) +class_list, def_class = CoolGrammar.NonTerminals(' ') +feature_list, feature = CoolGrammar.NonTerminals(' ') +param_list, param = CoolGrammar.NonTerminals(' ') +expr, member_call, expr_list, let_list, case_list = CoolGrammar.NonTerminals(' ') +truth_expr, comp_expr = CoolGrammar.NonTerminals(' ') +arith, term, factor, factor_2, factor_3 = CoolGrammar.NonTerminals(' ') +atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') + +# terminals +classx, inherits = CoolGrammar.Terminals('class inherits') +ifx, then, elsex, fi = CoolGrammar.Terminals('if then else fi') +whilex, loop, pool = CoolGrammar.Terminals('while loop pool') +let, inx = CoolGrammar.Terminals('let in') +case, of, esac = CoolGrammar.Terminals('case of esac') +semi, colon, comma, dot, at, opar, cpar, ocur, ccur, larrow, rarrow = CoolGrammar.Terminals('; : , . @ ( ) { } <- =>') +plus, minus, star, div, isvoid, compl = CoolGrammar.Terminals('+ - * / isvoid ~') +notx, less, leq, equal = CoolGrammar.Terminals('not < <= =') +new, idx, typex, integer, string, boolx = CoolGrammar.Terminals('new id type integer string bool') + +# productions +program %= class_list, lambda h, s: ProgramNode(s[1]) + +# +class_list %= def_class + class_list, lambda h, s: [s[1]] + s[2] +class_list %= def_class, lambda h, s: [s[1]] + +# +def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[4]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[6], s[4]) + +# +feature_list %= feature + feature_list, lambda h, s: [s[1]] + s[2] +feature_list %= CoolGrammar.Epsilon, lambda h, s: [] + +# +feature %= idx + colon + typex + semi, lambda h, s: AttrDeclarationNode(s[1], s[3]) +feature %= idx + colon + typex + larrow + expr + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]) + +# +feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr_list + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) +feature %= idx + opar + cpar + colon + typex + ocur + expr_list + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], s[5], s[7]) + +# +param_list %= param, lambda h, s: [s[1]] +param_list %= param + comma + param_list, lambda h, s: [s[1]] + s[3] + +# +param %= idx + colon + typex, lambda h, s: (s[1], s[3]) + +# +expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) +expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) +expr %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) +expr %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2]) +expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +expr %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) +expr %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) +expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +expr %= truth_expr, lambda h, s: s[1] + +# +expr_list %= expr + semi, lambda h, s: [s[1]] +expr_list %= expr + semi + expr_list, lambda h, s: [s[1]] + s[3] + +# +let_list %= idx + colon + typex, lambda h, s: [LetAttributeNode(s[1], s[3])] +let_list %= idx + colon + typex + larrow + expr, lambda h, s: [LetAttributeNode(s[1], s[3], s[5])] +let_list %= idx + colon + typex + comma + let_list, lambda h, s: [LetAttributeNode(s[1], s[3])] + s[5] +let_list %= idx + colon + typex + larrow + expr + comma + let_list, lambda h, s: [LetAttributeNode(s[1], s[3], s[5])] + s[7] + +# +case_list %= idx + colon + typex + rarrow + expr + semi, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] +case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] + +# +truth_expr %= notx + truth_expr, lambda h, s: NotNode(s[2]) +truth_expr %= comp_expr, lambda h, s: s[1] + +# +comp_expr %= comp_expr + leq + arith, lambda h, s: LessEqualNode(s[1], s[3]) +comp_expr %= comp_expr + less + arith, lambda h, s: LessNode(s[1], s[3]) +comp_expr %= comp_expr + equal + arith, lambda h, s: EqualNode(s[1], s[3]) +comp_expr %= arith, lambda h, s: s[1] + +# +arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3]) +arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3]) +arith %= term, lambda h, s: s[1] + +# +term %= term + star + factor, lambda h, s: StarNode(s[1], s[3]) +term %= term + div + factor, lambda h, s: DivNode(s[1], s[3]) +term %= factor, lambda h, s: s[1] + +# +factor %= isvoid + factor_2, lambda h, s: IsVoidNode(s[2]) +factor %= factor_2, lambda h, s: s[1] + +# +factor_2 %= compl + factor_3, lambda h, s: ComplementNode(s[2]) +factor_2 %= factor_3, lambda h, s: s[1] + +# +factor_3 %= atom, lambda h, s: s[1] +factor_3 %= atom + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) + +# +func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) +func_call %= dot + idx + opar + cpar, lambda h, s: (s[2], []) +func_call %= at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: (s[4], s[6], s[2]) +func_call %= at + typex + dot + idx + opar + cpar, lambda h, s: (s[4], [], s[2]) + +# +arg_list %= expr, lambda h, s: [s[1]] +arg_list %= expr + comma + arg_list, lambda h, s: [s[1]] + s[3] + +# +atom %= member_call, lambda h, s: s[1] +atom %= new + typex, lambda h, s: NewNode(s[2]) +atom %= opar + expr + cpar, lambda h, s: s[2] +atom %= idx, lambda h, s: IdNode(s[1]) +atom %= integer, lambda h, s: IntegerNode(s[1]) +atom %= string, lambda h, s: StringNode(s[1]) +atom %= boolx, lambda h, s: BoolNode(s[1]) + +# +member_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3]) +member_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) + +# Parser +CoolParser = LR1Parser(CoolGrammar) + +# Tokenizer + +fixed_tokens = { t.Name: Token(t.Name, t) for t in CoolGrammar.terminals if t not in { idx, integer, typex, string, boolx}} +booleans = ['false', 'true'] +previous = [new, colon, classx, inherits] + +@tokenizer(CoolGrammar, fixed_tokens) +def tokenize_text(token): + lex = token.lex + try: + float(lex) + return token.transform_to(integer) + except ValueError: + if lex[0].islower() and lex.lower() in booleans: + return token.transform_to(boolx) + if lex.count('"') >= 2 and lex[0] == '"' and lex[-1] == '"': + return token.transform_to(string) + return token.transform_to(idx) + +deprecated_tokenize_text = tokenize_text + +def tokenize_text(text): + tokens = deprecated_tokenize_text(text) + if tokens: + for i in range(1, len(tokens)): + if tokens[i].token_type == idx and tokens[i-1].token_type in previous: + tokens[i] = Token(tokens[i].lex, typex) + return tokens + +def pprint_tokens(tokens): + indent = 0 + pending = [] + for token in tokens: + pending.append(token) + if token.token_type in { ocur, ccur, semi }: + if token.token_type == ccur: + indent -= 1 + print(' '*indent + ' '.join(str(t.token_type) for t in pending)) + pending.clear() + if token.token_type == ocur: + indent += 1 + print(' '.join([str(t.token_type) for t in pending])) + +def format_tokens(tokens): + indent = 0 + pending = [] + txt = '' + for token in tokens: + pending.append(token) + if token.token_type in { ocur, ccur, semi }: + if token.token_type == ccur: + indent -= 1 + txt += ' '*indent + ' '.join(str(t.token_type) for t in pending) + '\n' + pending.clear() + if token.token_type == ocur: + indent += 1 + txt += ' '.join([str(t.token_type) for t in pending]) + return txt + +# Example text +_text = ''' +class Main { + main ( console : IO ) : AUTO_TYPE { + let x : AUTO_TYPE <- 3 + 2 in { + case a of { + x : Int => 3 ; + p : string => "OK" ; + } esac ; + } ; + } ; +} ; +''' + diff --git a/src/Files_from_TypeInferencer/core/cmp/__init__.py b/src/Files_from_TypeInferencer/core/cmp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/Files_from_TypeInferencer/core/cmp/ast.py b/src/Files_from_TypeInferencer/core/cmp/ast.py new file mode 100644 index 00000000..3d5b3b9f --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/ast.py @@ -0,0 +1,62 @@ +import core.cmp.visitor as visitor + +class Node: + def evaluate(self): + raise NotImplementedError() + +class AtomicNode(Node): + def __init__(self, lex): + self.lex = lex + +class UnaryNode(Node): + def __init__(self, node): + self.node = node + + def evaluate(self): + value = self.node.evaluate() + return self.operate(value) + + @staticmethod + def operate(value): + raise NotImplementedError() + +class BinaryNode(Node): + def __init__(self, left, right): + self.left = left + self.right = right + + def evaluate(self): + lvalue = self.left.evaluate() + rvalue = self.right.evaluate() + return self.operate(lvalue, rvalue) + + @staticmethod + def operate(lvalue, rvalue): + raise NotImplementedError() + +def get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode, ): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__}' + child = self.visit(node.node, tabs + 1) + return f'{ans}\n{child}' + + @visitor.when(BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + left = self.visit(node.left, tabs + 1) + right = self.visit(node.right, tabs + 1) + return f'{ans}\n{left}\n{right}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/automata.py b/src/Files_from_TypeInferencer/core/cmp/automata.py new file mode 100644 index 00000000..f42b6f00 --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/automata.py @@ -0,0 +1,207 @@ +try: + import pydot +except: + pass + +class State: + def __init__(self, state, final=False, formatter=lambda x: str(x), shape='circle'): + self.state = state + self.final = final + self.transitions = {} + self.epsilon_transitions = set() + self.tag = None + self.formatter = formatter + self.shape = shape + + # The method name is set this way from compatibility issues. + def set_formatter(self, value, attr='formatter', visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + self.__setattr__(attr, value) + for destinations in self.transitions.values(): + for node in destinations: + node.set_formatter(value, attr, visited) + for node in self.epsilon_transitions: + node.set_formatter(value, attr, visited) + return self + + def has_transition(self, symbol): + return symbol in self.transitions + + def add_transition(self, symbol, state): + try: + self.transitions[symbol].append(state) + except: + self.transitions[symbol] = [state] + return self + + def add_epsilon_transition(self, state): + self.epsilon_transitions.add(state) + return self + + def recognize(self, string): + states = self.epsilon_closure + for symbol in string: + states = self.move_by_state(symbol, *states) + states = self.epsilon_closure_by_state(*states) + return any(s.final for s in states) + + def to_deterministic(self, formatter=lambda x: str(x)): + closure = self.epsilon_closure + start = State(tuple(closure), any(s.final for s in closure), formatter) + + closures = [ closure ] + states = [ start ] + pending = [ start ] + + while pending: + state = pending.pop() + symbols = { symbol for s in state.state for symbol in s.transitions } + + for symbol in symbols: + move = self.move_by_state(symbol, *state.state) + closure = self.epsilon_closure_by_state(*move) + + if closure not in closures: + new_state = State(tuple(closure), any(s.final for s in closure), formatter) + closures.append(closure) + states.append(new_state) + pending.append(new_state) + else: + index = closures.index(closure) + new_state = states[index] + + state.add_transition(symbol, new_state) + + return start + + @staticmethod + def from_nfa(nfa, get_states=False): + states = [] + for n in range(nfa.states): + state = State(n, n in nfa.finals) + states.append(state) + + for (origin, symbol), destinations in nfa.map.items(): + origin = states[origin] + origin[symbol] = [ states[d] for d in destinations ] + + if get_states: + return states[nfa.start], states + return states[nfa.start] + + @staticmethod + def move_by_state(symbol, *states): + return { s for state in states if state.has_transition(symbol) for s in state[symbol]} + + @staticmethod + def epsilon_closure_by_state(*states): + closure = { state for state in states } + + l = 0 + while l != len(closure): + l = len(closure) + tmp = [s for s in closure] + for s in tmp: + for epsilon_state in s.epsilon_transitions: + closure.add(epsilon_state) + return closure + + @property + def epsilon_closure(self): + return self.epsilon_closure_by_state(self) + + @property + def name(self): + return self.formatter(self.state) + + def get(self, symbol): + target = self.transitions[symbol] + assert len(target) == 1 + return target[0] + + def __getitem__(self, symbol): + if symbol == '': + return self.epsilon_transitions + try: + return self.transitions[symbol] + except KeyError: + return None + + def __setitem__(self, symbol, value): + if symbol == '': + self.epsilon_transitions = value + else: + self.transitions[symbol] = value + + def __repr__(self): + return str(self) + + def __str__(self): + return str(self.state) + + def __hash__(self): + return hash(self.state) + + def __iter__(self): + yield from self._visit() + + def _visit(self, visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + yield self + + for destinations in self.transitions.values(): + for node in destinations: + yield from node._visit(visited) + for node in self.epsilon_transitions: + yield from node._visit(visited) + + def graph(self): + G = pydot.Dot(rankdir='LR', margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + visited = set() + def visit(start): + ids = id(start) + if ids not in visited: + visited.add(ids) + G.add_node(pydot.Node(ids, label=start.name, shape=self.shape, style='bold' if start.final else '')) + for tran, destinations in start.transitions.items(): + for end in destinations: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label=tran, labeldistance=2)) + for end in start.epsilon_transitions: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label='ε', labeldistance=2)) + + visit(self) + G.add_edge(pydot.Edge('start', id(self), label='', style='dashed')) + + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + + def write_to(self, fname): + return self.graph().write_svg(fname) + +def multiline_formatter(state): + return '\n'.join(str(item) for item in state) + +def lr0_formatter(state): + try: + return '\n'.join(str(item)[:-4] for item in state) + except TypeError: + return str(state)[:-4] \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/evaluation.py b/src/Files_from_TypeInferencer/core/cmp/evaluation.py new file mode 100644 index 00000000..1ac49378 --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/evaluation.py @@ -0,0 +1,33 @@ +from core.cmp.pycompiler import EOF +from core.cmp.utils import ShiftReduceParser + +def evaluate_reverse_parse(right_parse, operations, tokens): + if not right_parse or not operations or not tokens: + return + + right_parse = iter(right_parse) + tokens = iter(tokens) + stack = [] + for operation in operations: + if operation == ShiftReduceParser.SHIFT: + token = next(tokens) + stack.append(token.lex) + elif operation == ShiftReduceParser.REDUCE: + production = next(right_parse) + head, body = production + attributes = production.attributes + assert all(rule is None for rule in attributes[1:]), 'There must be only synteticed attributes.' + rule = attributes[0] + + if len(body): + synteticed = [None] + stack[-len(body):] + value = rule(None, synteticed) + stack[-len(body):] = [value] + else: + stack.append(rule(None, None)) + else: + raise Exception('Invalid action!!!') + + assert len(stack) == 1 + assert isinstance(next(tokens).token_type, EOF) + return stack[0] \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/functions.py b/src/Files_from_TypeInferencer/core/cmp/functions.py new file mode 100644 index 00000000..03b4d66d --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/functions.py @@ -0,0 +1,261 @@ +from core.cmp.pycompiler import * +from core.cmp.automata import * +from core.cmp.utils import * + + +def compute_local_first(firsts, alpha): + first_alpha = ContainerSet() + + try: + alpha_is_epsilon = alpha.IsEpsilon + except: + alpha_is_epsilon = False + + if alpha_is_epsilon: + first_alpha.set_epsilon() + else: + for symbol in alpha: + first_alpha.update(firsts[symbol]) + if not firsts[symbol].contains_epsilon: + break + else: + first_alpha.set_epsilon() + return first_alpha + + +def compute_firsts(G): + firsts = {} + change = True + + for terminal in G.terminals: + firsts[terminal] = ContainerSet(terminal) + + for nonterminal in G.nonTerminals: + firsts[nonterminal] = ContainerSet() + + while change: + change = False + + for production in G.Productions: + X = production.Left + alpha = production.Right + + first_X = firsts[X] + + try: + first_alpha = firsts[alpha] + except: + first_alpha = firsts[alpha] = ContainerSet() + + local_first = compute_local_first(firsts, alpha) + + change |= first_alpha.hard_update(local_first) + change |= first_X.hard_update(local_first) + return firsts + + +def compute_follows(G, firsts): + follows = {} + change = True + + local_firsts = {} + + for nonterminal in G.nonTerminals: + follows[nonterminal] = ContainerSet() + follows[G.startSymbol] = ContainerSet(G.EOF) + + while change: + change = False + + for production in G.Productions: + X = production.Left + alpha = production.Right + + follow_X = follows[X] + + for i, Y in enumerate(alpha): + if Y.IsTerminal: + continue + beta = alpha[i + 1:] + try: + beta_f = local_firsts[beta] + except KeyError: + beta_f = local_firsts[beta] = compute_local_first(firsts, beta) + change |= follows[Y].update(beta_f) + if beta_f.contains_epsilon: + change |= follows[Y].update(follow_X) + + return follows + + +def upd_table(table, symbol, trans, val): + if symbol not in table: + table[symbol] = {} + if trans not in table[symbol]: + table[symbol][trans] = set() + table[symbol][trans].update([val]) + ans = (len(table[symbol][trans]) == 1) + return ans + + +def build_LR0_automaton(G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0) + + automaton = State(start_item, True) + + pending = [start_item] + visited = {start_item: automaton} + + while pending: + current_item = pending.pop() + if current_item.IsReduceItem: + continue + + # Your code here!!! (Decide which transitions to add) + transitions = [] + + next_item = current_item.NextItem() + if next_item not in visited: + visited[next_item] = State(next_item, True) + pending.append(next_item) + transitions.append(visited[next_item]) + + symbol = current_item.NextSymbol + if symbol.IsNonTerminal: + for prod in symbol.productions: + item = Item(prod, 0) + if item not in visited: + visited[item] = State(item, True) + pending.append(item) + transitions.append(visited[item]) + + current_state = visited[current_item] + # Your code here!!! (Add the decided transitions) + current_state.add_transition(current_item.NextSymbol.Name, transitions[0]) + for item in transitions[1:]: + current_state.add_epsilon_transition(item) + return automaton + + +def expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() + # Your code here!!! (Compute lookahead for child items) + for preview in item.Preview(): + lookaheads.hard_update(compute_local_first(firsts, preview)) + + assert not lookaheads.contains_epsilon + # Your code here!!! (Build and return child items) + return [Item(prod, 0, lookaheads) for prod in next_symbol.productions] + + +def compress(items): + centers = {} + + for item in items: + center = item.Center() + try: + lookaheads = centers[center] + except KeyError: + centers[center] = lookaheads = set() + lookaheads.update(item.lookaheads) + + return {Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items()} + + +def closure_lr1(items, firsts): + closure = ContainerSet(*items) + + changed = True + while changed: + changed = False + + new_items = ContainerSet() + for item in closure: + new_items.extend(expand(item, firsts)) + + changed = closure.update(new_items) + + return compress(closure) + + +def goto_lr1(items, symbol, firsts=None, just_kernel=False): + assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' + items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) + return items if just_kernel else closure_lr1(items, firsts) + + +def build_LR1_automaton(G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = compute_firsts(G) + firsts[G.EOF] = ContainerSet(G.EOF) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0, lookaheads=(G.EOF,)) + start = frozenset([start_item]) + + closure = closure_lr1(start, firsts) + automaton = State(frozenset(closure), True) + + pending = [start] + visited = {start: automaton} + + while pending: + current = pending.pop() + current_state = visited[current] + + for symbol in G.terminals + G.nonTerminals: + # Your code here!!! (Get/Build `next_state`) + items = current_state.state + kernel = goto_lr1(items, symbol, just_kernel=True) + if not kernel: + continue + try: + next_state = visited[kernel] + except KeyError: + closure = goto_lr1(items, symbol, firsts) + next_state = visited[kernel] = State(frozenset(closure), True) + pending.append(kernel) + + current_state.add_transition(symbol.Name, next_state) + + automaton.set_formatter(lambda x: "") + return automaton + + +class LR1Parser(ShiftReduceParser): + def _build_parsing_table(self): + self.ok = True + G = self.Augmented = self.G.AugmentedGrammar(True) + + automaton = self.automaton = build_LR1_automaton(G) + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + node.tag = f'I{i}' + + for node in automaton: + idx = node.idx + for item in node.state: + if item.IsReduceItem: + prod = item.production + if prod.Left == G.startSymbol: + self.ok &= upd_table(self.action, idx, G.EOF, (ShiftReduceParser.OK, '')) + else: + for lookahead in item.lookaheads: + self.ok &= upd_table(self.action, idx, lookahead, (ShiftReduceParser.REDUCE, prod)) + else: + next_symbol = item.NextSymbol + if next_symbol.IsTerminal: + self.ok &= upd_table(self.action, idx, next_symbol, (ShiftReduceParser.SHIFT, node[next_symbol.Name][0].idx)) + else: + self.ok &= upd_table(self.goto, idx, next_symbol, node[next_symbol.Name][0].idx) + + diff --git a/src/Files_from_TypeInferencer/core/cmp/grammartools.py b/src/Files_from_TypeInferencer/core/cmp/grammartools.py new file mode 100644 index 00000000..dfc076dd --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/grammartools.py @@ -0,0 +1,753 @@ +from core.cmp.pycompiler import * +from core.cmp.automata import * +from core.cmp.utils import * + +class BadTextFormatException(Exception): + """ + Class for wrong format in texts + used to convert to a grammar + """ + pass + +class GrammarTools: + @staticmethod + def grammar_from_text(text: str): + """ + Transform a string in this format: + + S --> A B + A --> a A | epsilon + B --> b B | epsilon + + to a Grammar object + """ + terminals, nonTerminals, productions = [], [], [] + + try: + lines = text.split('\n') + + for line in lines: + head, bodies = line.split('-->') + + head, = head.split() + nonTerminals.append(head) + + for body in bodies.split('|'): + productions.append({'Head': head, 'Body': list(body.split())}) + terminals.extend(productions[-1]['Body']) + + except: + raise BadTextFormatException() + + sterminals, snonTerminals = set(terminals).difference(nonTerminals + ['epsilon']), set(nonTerminals) + + data = json.dumps({ + 'NonTerminals': [nt for nt in nonTerminals if nt in snonTerminals and snonTerminals.discard(nt) is None], + 'Terminals': [t for t in terminals if t in sterminals and sterminals.discard(t) is None], + 'Productions': productions + }) + + return Grammar.from_json(data) + + @staticmethod + def is_not_null(G: Grammar): + """ + Check if the given grammar genere + the empty language + """ + accepted = set() + visited = set() + + def dfs(symbol): + visited.add(symbol) + acc = False + + if isinstance(symbol, Terminal): + acc = True + else: + for production in symbol.productions: + for s in production.Right: + if s not in visited: + dfs(s) + acc |= all(s in accepted for s in production.Right) + + if acc: + accepted.add(symbol) + + dfs(G.startSymbol) + + return G.startSymbol in accepted + + @staticmethod + def clone_grammar(G: Grammar): + NG = Grammar() + symbols = {nonTerminal: NG.NonTerminal(nonTerminal.Name, nonTerminal == G.startSymbol) for nonTerminal in G.nonTerminals} + symbols.update({terminal: NG.Terminal(terminal.Name) for terminal in G.terminals}) + for p in G.Productions: + x = symbols[p.Left] + if isinstance(p.Right, Epsilon): + x %= NG.Epsilon + else: + x %= Sentence(*[symbols[symbol] for symbol in p.Right]) + + return NG + + @staticmethod + def remove_left_recursion(G: Grammar): + """ + Transform G for remove inmediate + left recursion + """ + G.Productions = [] + + for nonTerminal in G.nonTerminals: + recursion = [p.Right[1:] for p in nonTerminal.productions if len(p.Right) > 0 and p.Right[0] == nonTerminal] + no_recursion = [p.Right for p in nonTerminal.productions if len(p.Right) == 0 or p.Right[0] != nonTerminal] + + if len(recursion) > 0: + nonTerminal.productions = [] + aux = G.NonTerminal(f'{nonTerminal.Name}0') + + for p in no_recursion: + nonTerminal %= Sentence(*p) + aux + + for p in recursion: + aux %= Sentence(*p) + aux + + aux %= G.Epsilon + else: + G.Productions.extend(nonTerminal.productions) + + @staticmethod + def factorize_grammar(G: Grammar): + """ + Transform G for remove common + prefixes + """ + G.Productions = [] + + pending = G.nonTerminals.copy() + + while pending: + nonTerminal = pending.pop() + + productions = nonTerminal.productions.copy() + nonTerminal.productions = [] + + visited = set() + + for i, p in enumerate(productions): + if p not in visited: + n = len(p.Right) + same_prefix = [] + + for p2 in productions[i:]: + m = 0 + + for s1, s2 in zip(p.Right, p2.Right): + if s1 == s2: + m += 1 + else: + break + + if m > 0: + same_prefix.append(p2) + n = min(n, m) + + if len(same_prefix) > 1: + visited.update(same_prefix) + aux = G.NonTerminal(f'{nonTerminal.Name}{i + 1}') + + nonTerminal %= Sentence(*p.Right[:n]) + aux + for p2 in same_prefix: + if n == len(p2.Right): + aux %= G.Epsilon + else: + aux %= Sentence(*p2.Right[n:]) + + pending.append(aux) + else: + visited.add(p) + nonTerminal %= p.Right + + @staticmethod + def compute_local_first(firsts, alpha): + """ + Computes First(alpha), given First(Vt) and First(Vn) + alpha in (Vt U Vn)* + """ + first_alpha = ContainerSet() + + try: + alpha_is_epsilon = alpha.IsEpsilon + except: + alpha_is_epsilon = False + + # alpha == epsilon ? First(alpha) = { epsilon } + if alpha_is_epsilon: + first_alpha.set_epsilon() + + # alpha = X1 ... XN + # First(Xi) subset of First(alpha) + # epsilon in First(X1)...First(Xi) ? First(Xi+1) subset of First(X) & First(alpha) + # epsilon in First(X1)...First(XN) ? epsilon in First(X) & First(alpha) + else: + for symbol in alpha: + first_symbol = firsts[symbol] + first_alpha.update(first_symbol) + if not first_symbol.contains_epsilon: + break + else: + first_alpha.set_epsilon() + + return first_alpha + + @staticmethod + def compute_firsts(G: Grammar): + """ + Computes First(Vt) U First(Vn) U First(alpha) + P: X -> alpha + """ + firsts = {} + change = True + + # init First(Vt) + for terminal in G.terminals: + firsts[terminal] = ContainerSet(terminal) + + # init First(Vn) + for nonterminal in G.nonTerminals: + firsts[nonterminal] = ContainerSet() + + while change: + change = False + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + # get current First(X) + first_X = firsts[X] + + # init First(alpha) + try: + first_alpha = firsts[alpha] + except: + first_alpha = firsts[alpha] = ContainerSet() + + # CurrentFirst(alpha)??? + local_first = GrammarTools.compute_local_first(firsts, alpha) + + # update First(X) and First(alpha) from CurrentFirst(alpha) + change |= first_alpha.hard_update(local_first) + change |= first_X.hard_update(local_first) + + # First(Vt) + First(Vt) + First(RightSides) + return firsts + + @staticmethod + def compute_follows(G: Grammar, firsts): + """ + Computes Follow(Vn) + """ + follows = { } + change = True + + local_firsts = {} + + # init Follow(Vn) + for nonterminal in G.nonTerminals: + follows[nonterminal] = ContainerSet() + follows[G.startSymbol] = ContainerSet(G.EOF) + + while change: + change = False + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + follow_X = follows[X] + + # X -> zeta Y beta + # First(beta) - { epsilon } subset of Follow(Y) + # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) + for i, symbol in enumerate(alpha): + if symbol.IsNonTerminal: + follow_symbol = follows[symbol] + beta = alpha[i + 1:] + try: + first_beta = local_firsts[beta] + except KeyError: + first_beta = local_firsts[beta] = GrammarTools.compute_local_first(firsts, beta) + change |= follow_symbol.update(first_beta) + if first_beta.contains_epsilon or len(beta) == 0: + change |= follow_symbol.update(follow_X) + + return follows + + @staticmethod + def _register(table, state, symbol, value): + if state not in table: + table[state] = dict() + + row = table[state] + + if symbol not in row: + row[symbol] = set() + + cell = row[symbol] + + cell.add(value) + + return len(cell) == 1 + + @staticmethod + def build_ll1_table(G: Grammar, firsts, follows): + """ + Computes Parsing Table for a + Parser LL(1) + """ + # init parsing table + M = {} + is_ll1 = True + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + # working with symbols on First(alpha) ... + first_alpha = firsts[alpha] + for symbol in first_alpha: + is_ll1 &= GrammarTools._register(M, X, symbol, production) + + # working with epsilon... + if first_alpha.contains_epsilon: + for symbol in follows[X]: + is_ll1 &= GrammarTools._register(M, X, symbol, production) + + # parsing table is ready!!! + return M, is_ll1 + + @staticmethod + def build_automaton(G: Grammar): + """ + Build the finite automaton for + a regular grammar + """ + states = { nonTerminal: State(nonTerminal.Name) for nonTerminal in G.nonTerminals } + final_state = State('F\'', True) + + start_in_right = False + epsilon_production = False + + for nonTerminal in G.nonTerminals: + for production in nonTerminal.productions: + right = production.Right + + # Start Symbol produces epsilon + if isinstance(right, Epsilon) and nonTerminal == G.startSymbol: + epsilon_production = True + continue + + start_in_right |= G.startSymbol in right + n = len(right) + + # X --> w + if n == 1 and isinstance(right[0], Terminal): + states[nonTerminal].add_transition(right[0].Name, final_state) + continue + + # X --> w Y + if n == 2 and isinstance(right[0], Terminal) and isinstance(right[1], NonTerminal): + states[nonTerminal].add_transition(right[0].Name, states[right[1]]) + continue + + return states[G.startSymbol], False + + states[G.startSymbol].final = epsilon_production + return states[G.startSymbol], not (start_in_right and epsilon_production) + + epsilon = 'ε' + + @staticmethod + def regex_union(regex, other): + if regex is None: + return other + + if other is None: + return regex + + if regex == other: + return regex + + return f'({regex}|{other})' + + @staticmethod + def regex_concat(regex, other): + if regex is None or other is None: + return None + + if regex is GrammarTools.epsilon: + return other + + if other is GrammarTools.epsilon: + return regex + + return f'{regex}{other}' + + @staticmethod + def regex_star(regex): + if regex is None or regex is GrammarTools.epsilon: + return regex + + return f'({regex})*' + + @staticmethod + def regexp_from_automaton(automaton): + """ + Build the regular expresion for + a NFA + """ + states = list(automaton) + states_index = {state: i for i, state in enumerate(states)} + n = len(states) + + R = [[[None for k in range(n + 1)] for j in range(n)] for i in range(n)] + + for i in range(n): + R[i][i][0] = GrammarTools.epsilon + + for i, state in enumerate(states): + for symbol, transitions in state.transitions.items(): + for state2 in transitions: + j = states_index[state2] + R[i][j][0] = GrammarTools.regex_union(R[i][j][0], symbol) + + for k in range(n): + for i in range(n): + for j in range(n): + R[i][j][k + 1] = GrammarTools.regex_union(R[i][j][k], GrammarTools.regex_concat(R[i][k][k], GrammarTools.regex_concat(GrammarTools.regex_star(R[k][k][k]), R[k][j][k]))) + + e = None + for i in range(n): + if states[i].final: + e = GrammarTools.regex_union(e, R[0][i][n]) + + return e + + +class Action(tuple): + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + + def __str__(self): + try: + action, tag = self + return f"{'S' if action == Action.SHIFT else 'OK' if action == Action.OK else ''}{tag}" + except: + return str(tuple(self)) + + __repr__ = __str__ + +class ShiftReduceParser: + def __init__(self, G, verbose=False): + self.G = G + self.verbose = verbose + self.action = {} + self.goto = {} + self._build_parsing_table() + + def _build_parsing_table(self): + raise NotImplementedError() + + def __call__(self, w): + stack = [ 0 ] + cursor = 0 + output = [] + + while True: + state = stack[-1] + lookahead = w[cursor] + if self.verbose: print(stack, w[cursor:]) + + # (Detect error) + try: + print(type(state), type(lookahead)) + action, tag = list(self.action[state][lookahead])[0] + # (Shift case) + if action == Action.SHIFT: + stack.append(tag) + cursor += 1 + # (Reduce case) + elif action == Action.REDUCE: + for _ in range(len(tag.Right)): stack.pop() + stack.append(self.goto[stack[-1]][tag.Left]) + output.append(tag) + # (OK case) + elif action == Action.OK: + return output + # (Invalid case) + else: + assert False, 'Must be something wrong!' + except KeyError: + raise Exception('Aborting parsing, item is not viable.') + +class SLR1Parser(ShiftReduceParser): + def build_LR0_automaton(self): + G = self.augmentedG = self.G.AugmentedGrammar(True) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0) + + automaton = State(start_item, True) + + pending = [ start_item ] + visited = { start_item: automaton } + + while pending: + current_item = pending.pop() + if current_item.IsReduceItem: + continue + + # (Decide which transitions to add) + next_symbol = current_item.NextSymbol + + next_item = current_item.NextItem() + if not next_item in visited: + pending.append(next_item) + visited[next_item] = State(next_item, True) + + if next_symbol.IsNonTerminal: + for prod in next_symbol.productions: + next_item = Item(prod, 0) + if not next_item in visited: + pending.append(next_item) + visited[next_item] = State(next_item, True) + + current_state = visited[current_item] + + # (Add the decided transitions) + current_state.add_transition(next_symbol.Name, visited[current_item.NextItem()]) + + if next_symbol.IsNonTerminal: + for prod in next_symbol.productions: + current_state.add_epsilon_transition(visited[Item(prod, 0)]) + + self.automaton = automaton.to_deterministic() + + def _build_parsing_table(self): + self.is_slr1 = True + self.build_LR0_automaton() + + firsts = GrammarTools.compute_firsts(self.augmentedG) + follows = GrammarTools.compute_follows(self.augmentedG, firsts) + + for i, node in enumerate(self.automaton): + if self.verbose: print(i, node) + node.idx = i + node.tag = f'I{i}' + + for node in self.automaton: + idx = node.idx + for state in node.state: + item = state.state + + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + prod = item.production + if prod.Left == self.augmentedG.startSymbol: + self.is_slr1 &= GrammarTools._register(self.action, idx, self.augmentedG.EOF, + Action((Action.OK, ''))) + else: + for symbol in follows[prod.Left]: + self.is_slr1 &= GrammarTools._register(self.action, idx, symbol, + Action((Action.REDUCE, prod))) + else: + next_symbol = item.NextSymbol + if next_symbol.IsTerminal: + self.is_slr1 &= GrammarTools._register(self.action, idx, next_symbol, + Action((Action.SHIFT, node[next_symbol.Name][0].idx))) + else: + self.is_slr1 &= GrammarTools._register(self.goto, idx, next_symbol, + node[next_symbol.Name][0].idx) + +class LR1Parser(ShiftReduceParser): + @staticmethod + def expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() + # (Compute lookahead for child items) + for preview in item.Preview(): + lookaheads.hard_update(GrammarTools.compute_local_first(firsts, preview)) + + assert not lookaheads.contains_epsilon + # (Build and return child items) + return [Item(prod, 0, lookaheads) for prod in next_symbol.productions] + + @staticmethod + def compress(items): + centers = {} + + for item in items: + center = item.Center() + try: + lookaheads = centers[center] + except KeyError: + centers[center] = lookaheads = set() + lookaheads.update(item.lookaheads) + + return { Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items() } + + @staticmethod + def closure_lr1(items, firsts): + closure = ContainerSet(*items) + + changed = True + while changed: + changed = False + + new_items = ContainerSet() + for item in closure: + new_items.extend(LR1Parser.expand(item, firsts)) + + changed = closure.update(new_items) + + return LR1Parser.compress(closure) + + @staticmethod + def goto_lr1(items, symbol, firsts=None, just_kernel=False): + assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' + items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) + return items if just_kernel else LR1Parser.closure_lr1(items, firsts) + + + def build_LR1_automaton(self): + G = self.augmentedG = self.G.AugmentedGrammar(True) + + firsts = GrammarTools.compute_firsts(G) + firsts[G.EOF] = ContainerSet(G.EOF) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0, lookaheads=(G.EOF,)) + start = frozenset([start_item]) + + closure = LR1Parser.closure_lr1(start, firsts) + automaton = State(frozenset(closure), True) + + pending = [ start ] + visited = { start: automaton } + + while pending: + current = pending.pop() + current_state = visited[current] + + for symbol in G.terminals + G.nonTerminals: + # (Get/Build `next_state`) + kernels = LR1Parser.goto_lr1(current_state.state, symbol, just_kernel=True) + + if not kernels: + continue + + try: + next_state = visited[kernels] + except KeyError: + pending.append(kernels) + visited[pending[-1]] = next_state = State(frozenset(LR1Parser.goto_lr1(current_state.state, symbol, firsts)), True) + + current_state.add_transition(symbol.Name, next_state) + + self.automaton = automaton + + def _build_parsing_table(self): + self.is_lr1 = True + self.build_LR1_automaton() + + for i, node in enumerate(self.automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + node.tag = f'I{i}' + + for node in self.automaton: + idx = node.idx + for item in node.state: + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + prod = item.production + if prod.Left == self.augmentedG.startSymbol: + self.is_lr1 &= GrammarTools._register(self.action, idx, self.augmentedG.EOF, + Action((Action.OK, ''))) + else: + for lookahead in item.lookaheads: + self.is_lr1 &= GrammarTools._register(self.action, idx, lookahead, + Action((Action.REDUCE, prod))) + else: + next_symbol = item.NextSymbol + if next_symbol.IsTerminal: + self.is_lr1 &= GrammarTools._register(self.action, idx, next_symbol, + Action((Action.SHIFT, node[next_symbol.Name][0].idx))) + else: + self.is_lr1 &= GrammarTools._register(self.goto, idx, next_symbol, + node[next_symbol.Name][0].idx) + pass + +class LALR1Parser(LR1Parser): + @staticmethod + def mergue_items_lookaheads(items, others): + if len(items) != len(others): + return False + + new_lookaheads = [] + for item in items: + for item2 in others: + if item.Center() == item2.Center(): + new_lookaheads.append(item2.lookaheads) + break + else: + return False + + for item, new_lookahead in zip(items, new_lookaheads): + item.lookaheads = item.lookaheads.union(new_lookahead) + + return True + + def build_LR1_automaton(self): + super().build_LR1_automaton() + + states = list(self.automaton) + new_states = [] + visited = {} + + for i, state in enumerate(states): + if state not in visited: + # creates items + items = [item.Center() for item in state.state] + + # check for states with same center + for state2 in states[i:]: + if LALR1Parser.mergue_items_lookaheads(items, state2.state): + visited[state2] = len(new_states) + + # add new state + new_states.append(State(frozenset(items), True)) + + # making transitions + for state in states: + new_state = new_states[visited[state]] + for symbol, transitions in state.transitions.items(): + for state2 in transitions: + new_state2 = new_states[visited[state2]] + # check if the transition already exists + if symbol not in new_state.transitions or new_state2 not in new_state.transitions[symbol]: + new_state.add_transition(symbol, new_state2) + + self.automaton = new_states[0] diff --git a/src/Files_from_TypeInferencer/core/cmp/languages.py b/src/Files_from_TypeInferencer/core/cmp/languages.py new file mode 100644 index 00000000..f53cb0ef --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/languages.py @@ -0,0 +1,228 @@ +from core.cmp.pycompiler import Sentence, Production +from core.cmp.utils import ContainerSet, Token, UnknownToken +from core.cmp.tools.parsing import build_parsing_table, metodo_predictivo_no_recursivo + +class BasicXCool: + def __init__(self, G): + self.G = G + self.fixed_tokens = { lex: Token(lex, G[lex]) for lex in '+ - * / ( )'.split() } + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G.EOF, G['*'], G['/'], G[')'], G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['num'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['+'], ): [ Production(G['X'], Sentence(G['+'], G['T'], G['X'])), ], + ( G['X'], G['-'], ): [ Production(G['X'], Sentence(G['-'], G['T'], G['X'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['num'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['*'], ): [ Production(G['Y'], Sentence(G['*'], G['F'], G['Y'])), ], + ( G['Y'], G['/'], ): [ Production(G['Y'], Sentence(G['/'], G['F'], G['Y'])), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['-'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['+'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['num'], ): [ Production(G['F'], Sentence(G['num'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def tokenizer(self): + G = self.G + fixed_tokens = self.fixed_tokens + + def tokenize_text(text): + tokens = [] + for item in text.split(): + try: + float(item) + token = Token(item, G['num']) + except ValueError: + try: + token = fixed_tokens[item] + except: + token = UnknownToken(item) + tokens.append(token) + eof = Token('$', G.EOF) + tokens.append(eof) + return tokens + + return tokenize_text + +class PowXCool: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['^']: ContainerSet(G['^'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + G['Z']: ContainerSet(G['^'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['^'], G['F']): ContainerSet(G['^'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['A']: ContainerSet(G['-'], G['*'], G['/'], G['^'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['Z']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False) + } + +class Regex: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['|']: ContainerSet(G['|'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['symbol']: ContainerSet(G['symbol'] , contains_epsilon=False), + G['ε']: ContainerSet(G['ε'] , contains_epsilon=False), + G['E']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['ε'], G['symbol'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['|'] , contains_epsilon=True), + G['Y']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=True), + G['Z']: ContainerSet(G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['|'], G['E']): ContainerSet(G['|'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['T']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['*']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['symbol']): ContainerSet(G['symbol'] , contains_epsilon=False), + Sentence(G['ε']): ContainerSet(G['ε'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), + G['F']: ContainerSet(G[')'], G.EOF, G['symbol'], G['|'], G['ε'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G.EOF, G['|'], G['*'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), + G['Z']: ContainerSet(G.EOF, G['|'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['symbol'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['ε'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['|'], ): [ Production(G['X'], Sentence(G['|'], G['E'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['symbol'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['ε'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['symbol'], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['ε'], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['('], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['|'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['symbol'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['F'], G['ε'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['Z'], G['*'], ): [ Production(G['Z'], Sentence(G['*'])), ], + ( G['Z'], G.EOF, ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['|'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['('], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G[')'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['symbol'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['ε'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['A'], G['symbol'], ): [ Production(G['A'], Sentence(G['symbol'])), ], + ( G['A'], G['ε'], ): [ Production(G['A'], Sentence(G['ε'])), ], + ( G['A'], G['('], ): [ Production(G['A'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def parser(self): + firsts = self.firsts + follows = self.follows + M = build_parsing_table(self.G, firsts, follows) + parser = metodo_predictivo_no_recursivo(self.G, M) + return parser \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/nbpackage.py b/src/Files_from_TypeInferencer/core/cmp/nbpackage.py new file mode 100644 index 00000000..e89c62ad --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/nbpackage.py @@ -0,0 +1,87 @@ +import io, os, sys, types + +from IPython import get_ipython +from nbformat import read +from IPython.core.interactiveshell import InteractiveShell + +def find_notebook(fullname, path=None): + """find a notebook, given its fully qualified name and an optional path + + This turns "foo.bar" into "foo/bar.ipynb" + and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar + does not exist. + """ + name = fullname.rsplit('.', 1)[-1] + if not path: + path = [''] + for d in path: + nb_path = os.path.join(d, name + ".ipynb") + if os.path.isfile(nb_path): + return nb_path + # let import Notebook_Name find "Notebook Name.ipynb" + nb_path = nb_path.replace("_", " ") + if os.path.isfile(nb_path): + return nb_path + +class NotebookLoader(object): + """Module Loader for Jupyter Notebooks""" + def __init__(self, path=None): + self.shell = InteractiveShell.instance() + self.path = path + + def load_module(self, fullname): + """import a notebook as a module""" + path = find_notebook(fullname, self.path) + + print ("importing Jupyter notebook from %s" % path) + + # load the notebook object + with io.open(path, 'r', encoding='utf-8') as f: + nb = read(f, 4) + + + # create the module and add it to sys.modules + # if name in sys.modules: + # return sys.modules[name] + mod = types.ModuleType(fullname) + mod.__file__ = path + mod.__loader__ = self + mod.__dict__['get_ipython'] = get_ipython + sys.modules[fullname] = mod + + # extra work to ensure that magics that would affect the user_ns + # actually affect the notebook module's ns + save_user_ns = self.shell.user_ns + self.shell.user_ns = mod.__dict__ + + try: + for cell in nb.cells: + if cell.cell_type == 'code': + # transform the input to executable Python + code = self.shell.input_transformer_manager.transform_cell(cell.source) + # run the code in themodule + exec(code, mod.__dict__) + finally: + self.shell.user_ns = save_user_ns + return mod + +class NotebookFinder(object): + """Module finder that locates Jupyter Notebooks""" + def __init__(self): + self.loaders = {} + + def find_module(self, fullname, path=None): + nb_path = find_notebook(fullname, path) + if not nb_path: + return + + key = path + if path: + # lists aren't hashable + key = os.path.sep.join(path) + + if key not in self.loaders: + self.loaders[key] = NotebookLoader(path) + return self.loaders[key] + +sys.meta_path.append(NotebookFinder()) \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/pycompiler.py b/src/Files_from_TypeInferencer/core/cmp/pycompiler.py new file mode 100644 index 00000000..3ef02dee --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/pycompiler.py @@ -0,0 +1,512 @@ +import json + +class Symbol(object): + + def __init__(self, name, grammar): + self.Name = name + self.Grammar = grammar + + def __str__(self): + return self.Name + + def __repr__(self): + return repr(self.Name) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + raise TypeError(other) + + def __or__(self, other): + + if isinstance(other, (Sentence)): + return SentenceList(Sentence(self), other) + + raise TypeError(other) + + @property + def IsEpsilon(self): + return False + + def __len__(self): + return 1 + +class NonTerminal(Symbol): + + + def __init__(self, name, grammar): + super().__init__(name, grammar) + self.productions = [] + + + def __imod__(self, other): + + if isinstance(other, (Sentence)): + p = Production(self, other) + self.Grammar.Add_Production(p) + return self + + if isinstance(other, tuple): + assert len(other) > 1 + + if len(other) == 2: + other += (None,) * len(other[0]) + + assert len(other) == len(other[0]) + 2, "Debe definirse una, y solo una, regla por cada símbolo de la producción" + # assert len(other) == 2, "Tiene que ser una Tupla de 2 elementos (sentence, attribute)" + + if isinstance(other[0], Symbol) or isinstance(other[0], Sentence): + p = AttributeProduction(self, other[0], other[1:]) + else: + raise Exception("") + + self.Grammar.Add_Production(p) + return self + + if isinstance(other, Symbol): + p = Production(self, Sentence(other)) + self.Grammar.Add_Production(p) + return self + + if isinstance(other, SentenceList): + + for s in other: + p = Production(self, s) + self.Grammar.Add_Production(p) + + return self + + raise TypeError(other) + + @property + def IsTerminal(self): + return False + + @property + def IsNonTerminal(self): + return True + + @property + def IsEpsilon(self): + return False + +class Terminal(Symbol): + + def __init__(self, name, grammar): + super().__init__(name, grammar) + + @property + def IsTerminal(self): + return True + + @property + def IsNonTerminal(self): + return False + + @property + def IsEpsilon(self): + return False + +class EOF(Terminal): + + def __init__(self, Grammar): + super().__init__('$', Grammar) + +class Sentence(object): + + def __init__(self, *args): + self._symbols = tuple(x for x in args if not x.IsEpsilon) + self.hash = hash(self._symbols) + + def __len__(self): + return len(self._symbols) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(*(self._symbols + (other,))) + + if isinstance(other, Sentence): + return Sentence(*(self._symbols + other._symbols)) + + raise TypeError(other) + + def __or__(self, other): + if isinstance(other, Sentence): + return SentenceList(self, other) + + if isinstance(other, Symbol): + return SentenceList(self, Sentence(other)) + + raise TypeError(other) + + def __repr__(self): + return str(self) + + def __str__(self): + return ("%s " * len(self._symbols) % tuple(self._symbols)).strip() + + def __iter__(self): + return iter(self._symbols) + + def __getitem__(self, index): + return self._symbols[index] + + def __eq__(self, other): + return self._symbols == other._symbols + + def __hash__(self): + return self.hash + + @property + def IsEpsilon(self): + return False + +class SentenceList(object): + + def __init__(self, *args): + self._sentences = list(args) + + def Add(self, symbol): + if not symbol and (symbol is None or not symbol.IsEpsilon): + raise ValueError(symbol) + + self._sentences.append(symbol) + + def __iter__(self): + return iter(self._sentences) + + def __or__(self, other): + if isinstance(other, Sentence): + self.Add(other) + return self + + if isinstance(other, Symbol): + return self | Sentence(other) + + +class Epsilon(Terminal, Sentence): + + def __init__(self, grammar): + super().__init__('epsilon', grammar) + + + def __str__(self): + return "e" + + def __repr__(self): + return 'epsilon' + + def __iter__(self): + yield from () + + def __len__(self): + return 0 + + def __add__(self, other): + return other + + def __eq__(self, other): + return isinstance(other, (Epsilon,)) + + def __hash__(self): + return hash("") + + @property + def IsEpsilon(self): + return True + +class Production(object): + + def __init__(self, nonTerminal, sentence): + + self.Left = nonTerminal + self.Right = sentence + + def __str__(self): + + return '%s := %s' % (self.Left, self.Right) + + def __repr__(self): + return '%s -> %s' % (self.Left, self.Right) + + def __iter__(self): + yield self.Left + yield self.Right + + def __eq__(self, other): + return isinstance(other, Production) and self.Left == other.Left and self.Right == other.Right + + def __hash__(self): + return hash((self.Left, self.Right)) + + @property + def IsEpsilon(self): + return self.Right.IsEpsilon + +class AttributeProduction(Production): + + def __init__(self, nonTerminal, sentence, attributes): + if not isinstance(sentence, Sentence) and isinstance(sentence, Symbol): + sentence = Sentence(sentence) + super(AttributeProduction, self).__init__(nonTerminal, sentence) + + self.attributes = attributes + + def __str__(self): + return '%s := %s' % (self.Left, self.Right) + + def __repr__(self): + return '%s -> %s' % (self.Left, self.Right) + + def __iter__(self): + yield self.Left + yield self.Right + + + @property + def IsEpsilon(self): + return self.Right.IsEpsilon + + # sintetizar en ingles??????, pending aggrement + def syntetice(self): + pass + +class Grammar(): + + def __init__(self): + + self.Productions = [] + self.nonTerminals = [] + self.terminals = [] + self.startSymbol = None + # production type + self.pType = None + self.Epsilon = Epsilon(self) + self.EOF = EOF(self) + + self.symbDict = { '$': self.EOF } + + def NonTerminal(self, name, startSymbol = False): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = NonTerminal(name,self) + + if startSymbol: + + if self.startSymbol is None: + self.startSymbol = term + else: + raise Exception("Cannot define more than one start symbol.") + + self.nonTerminals.append(term) + self.symbDict[name] = term + return term + + def NonTerminals(self, names): + + ans = tuple((self.NonTerminal(x) for x in names.strip().split())) + + return ans + + + def Add_Production(self, production): + + if len(self.Productions) == 0: + self.pType = type(production) + + assert type(production) == self.pType, "The Productions most be of only 1 type." + + production.Left.productions.append(production) + self.Productions.append(production) + + + def Terminal(self, name): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = Terminal(name, self) + self.terminals.append(term) + self.symbDict[name] = term + return term + + def Terminals(self, names): + + ans = tuple((self.Terminal(x) for x in names.strip().split())) + + return ans + + + def __str__(self): + + mul = '%s, ' + + ans = 'Non-Terminals:\n\t' + + nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' + + ans += nonterminals % tuple(self.nonTerminals) + + ans += 'Terminals:\n\t' + + terminals = mul * (len(self.terminals)-1) + '%s\n' + + ans += terminals % tuple(self.terminals) + + ans += 'Productions:\n\t' + + ans += str(self.Productions) + + return ans + + def __getitem__(self, name): + try: + return self.symbDict[name] + except KeyError: + return None + + @property + def to_json(self): + + productions = [] + + for p in self.Productions: + head = p.Left.Name + + body = [] + + for s in p.Right: + body.append(s.Name) + + productions.append({'Head':head, 'Body':body}) + + d={'NonTerminals':[symb.Name for symb in self.nonTerminals], 'Terminals': [symb.Name for symb in self.terminals],\ + 'Productions':productions} + + # [{'Head':p.Left.Name, "Body": [s.Name for s in p.Right]} for p in self.Productions] + return json.dumps(d) + + @staticmethod + def from_json(data): + data = json.loads(data) + + G = Grammar() + dic = {'epsilon':G.Epsilon} + + for term in data['Terminals']: + dic[term] = G.Terminal(term) + + for noTerm in data['NonTerminals']: + dic[noTerm] = G.NonTerminal(noTerm) + + for p in data['Productions']: + head = p['Head'] + dic[head] %= Sentence(*[dic[term] for term in p['Body']]) + + return G + + def copy(self): + G = Grammar() + G.Productions = self.Productions.copy() + G.nonTerminals = self.nonTerminals.copy() + G.terminals = self.terminals.copy() + G.pType = self.pType + G.startSymbol = self.startSymbol + G.Epsilon = self.Epsilon + G.EOF = self.EOF + G.symbDict = self.symbDict.copy() + + return G + + @property + def IsAugmentedGrammar(self): + augmented = 0 + for left, right in self.Productions: + if self.startSymbol == left: + augmented += 1 + if augmented <= 1: + return True + else: + return False + + def AugmentedGrammar(self, force=False): + if not self.IsAugmentedGrammar or force: + + G = self.copy() + # S, self.startSymbol, SS = self.startSymbol, None, self.NonTerminal('S\'', True) + S = G.startSymbol + G.startSymbol = None + SS = G.NonTerminal('S\'', True) + if G.pType is AttributeProduction: + SS %= S + G.Epsilon, lambda x : x + else: + SS %= S + G.Epsilon + + return G + else: + return self.copy() + #endchange + +class Item: + + def __init__(self, production, pos, lookaheads=[]): + self.production = production + self.pos = pos + self.lookaheads = frozenset(look for look in lookaheads) + + def __str__(self): + s = str(self.production.Left) + " -> " + if len(self.production.Right) > 0: + for i,c in enumerate(self.production.Right): + if i == self.pos: + s += "." + s += str(self.production.Right[i]) + if self.pos == len(self.production.Right): + s += "." + else: + s += "." + s += ", " + str(self.lookaheads)[10:-1] + return s + + def __repr__(self): + return str(self) + + + def __eq__(self, other): + return ( + (self.pos == other.pos) and + (self.production == other.production) and + (set(self.lookaheads) == set(other.lookaheads)) + ) + + def __hash__(self): + return hash((self.production,self.pos,self.lookaheads)) + + @property + def IsReduceItem(self): + return len(self.production.Right) == self.pos + + @property + def NextSymbol(self): + if self.pos < len(self.production.Right): + return self.production.Right[self.pos] + else: + return None + + def NextItem(self): + if self.pos < len(self.production.Right): + return Item(self.production,self.pos+1,self.lookaheads) + else: + return None + + def Preview(self, skip=1): + unseen = self.production.Right[self.pos+skip:] + return [ unseen + (lookahead,) for lookahead in self.lookaheads ] + + def Center(self): + return Item(self.production, self.pos) \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/semantic.py b/src/Files_from_TypeInferencer/core/cmp/semantic.py new file mode 100644 index 00000000..6613abd0 --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/semantic.py @@ -0,0 +1,246 @@ +import itertools as itt + +class SemanticError(Exception): + @property + def text(self): + return self.args[0] + +class Attribute: + def __init__(self, name, typex): + self.name = name + self.type = typex + + def __str__(self): + return f'[attrib] {self.name} : {self.type.name};' + + def __repr__(self): + return str(self) + +class Method: + def __init__(self, name, param_names, params_types, return_type): + self.name = name + self.param_names = param_names + self.param_types = params_types + self.return_type = return_type + + def __str__(self): + params = ', '.join(f'{n}:{t.name}' for n,t in zip(self.param_names, self.param_types)) + return f'[method] {self.name}({params}): {self.return_type.name};' + + def __eq__(self, other): + return other.name == self.name and \ + other.return_type == self.return_type and \ + other.param_types == self.param_types + +class Type: + def __init__(self, name:str='Object'): + self.name = name + self.attributes = [] + self.methods = {} + self.parent = None + + def set_parent(self, parent): + if self.parent is not None: + raise SemanticError(f'Parent type is already set for {self.name}.') + self.parent = parent + + def get_attribute(self, name:str): + try: + return next(attr for attr in self.attributes if attr.name == name) + except StopIteration: + if self.parent is None: + raise SemanticError(f'Attribute "{name}" is not defined in {self.name}.') + try: + return self.parent.get_attribute(name) + except SemanticError: + raise SemanticError(f'Attribute "{name}" is not defined in {self.name}.') + + def define_attribute(self, name:str, typex): + try: + self.get_attribute(name) + except SemanticError: + attribute = Attribute(name, typex) + self.attributes.append(attribute) + return attribute + else: + raise SemanticError(f'Attribute "{name}" is already defined in {self.name}.') + + def get_method(self, name:str): + try: + return self.methods[name] + except KeyError: + if self.parent is None: + raise SemanticError(f'Method "{name}" is not defined in {self.name}.') + try: + return self.parent.get_method(name) + except SemanticError: + raise SemanticError(f'Method "{name}" is not defined in {self.name}.') + + def define_method(self, name:str, param_names:list, param_types:list, return_type): + if name in self.methods: + raise SemanticError(f'Method "{name}" already defined in {self.name}') + # raise SemanticError(f'Method "{name}" already defined in {self.name} with a different signature.') + + method = self.methods[name] = Method(name, param_names, param_types, return_type) + return method + + # my method, change it in future + def define_method(self, name:str, param_names:list, param_types:list, return_type): + try: + method = self.get_method(name) + except SemanticError: + pass + else: + if method.return_type != return_type or method.param_types != param_types: + raise SemanticError(f'Method "{name}" already defined in {self.name} with a different signature.') + + method = self.methods[name] = Method(name, param_names, param_types, return_type) + return method + + def conforms_to(self, other): + return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) + + def bypass(self): + if self.name == 'Object' or self.name == 'AUTO_TYPE': + return True + return False + + def __str__(self): + output = f'type {self.name}' + parent = '' if self.parent is None else f' : {self.parent.name}' + output += parent + output += ' {' + output += '\n\t' if self.attributes or self.methods else '' + output += '\n\t'.join(str(x) for x in self.attributes) + output += '\n\t' if self.attributes else '' + output += '\n\t'.join(str(x) for x in self.methods.values()) + output += '\n' if self.methods else '' + output += '}\n' + return output + + def __repr__(self): + return str(self) + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, 'void') + + def conforms_to(self, other): + if other.name in [ 'Int', 'String', 'Bool', 'IO']: + return False + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, VoidType) + +class IntType(Type): + def __init__(self): + Type.__init__(self, 'Int') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, IntType) + +class StringType(Type): + def __init__(self): + Type.__init__(self, 'String') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, StringType) + +class BoolType(Type): + def __init__(self): + Type.__init__(self, 'Bool') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, BoolType) + +class IOType(Type): + def __init__(self): + Type.__init__(self, 'IO') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, IOType) + + +class Context: + def __init__(self): + self.types = {} + + def create_type(self, name:str): + if name in self.types: + raise SemanticError(f'Type with the same name ({name}) already in context.') + typex = self.types[name] = Type(name) + return typex + + def get_type(self, name:str): + try: + return self.types[name] + except KeyError: + raise SemanticError(f'Type "{name}" is not defined.') + + def __str__(self): + return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' + + def __repr__(self): + return str(self) + +class VariableInfo: + def __init__(self, name, vtype): + self.name = name + self.type = vtype + +class Scope: + def __init__(self, parent=None): + self.locals = [] + self.parent = parent + self.children = [] + self.index = 0 if parent is None else len(parent) + + def __len__(self): + return len(self.locals) + + def create_child(self): + child = Scope(self) + self.children.append(child) + return child + + def define_variable(self, vname, vtype): + info = VariableInfo(vname, vtype) + self.locals.append(info) + return info + + def find_variable(self, vname, index=None): + locals = self.locals if index is None else itt.islice(self.locals, index) + try: + return next(x for x in locals if x.name == vname) + except StopIteration: + return self.parent.find_variable(vname, self.index) if self.parent else None + + def is_defined(self, vname): + return self.find_variable(vname) is not None + + def is_local(self, vname): + return any(True for x in self.locals if x.name == vname) + + def count_auto(self): + num = 0 + for var in self.locals: + if var.type.name == 'AUTO_TYPE': + num += 1 + return num + sum([scp.count_auto() for scp in self.children]) \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/utils.py b/src/Files_from_TypeInferencer/core/cmp/utils.py new file mode 100644 index 00000000..8b45057f --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/utils.py @@ -0,0 +1,276 @@ +from core.cmp.pycompiler import Production, Sentence, Symbol, EOF, Epsilon + +class ContainerSet: + def __init__(self, *values, contains_epsilon=False): + self.set = set(values) + self.contains_epsilon = contains_epsilon + + def add(self, value): + n = len(self.set) + self.set.add(value) + return n != len(self.set) + + def extend(self, values): + change = False + for value in values: + change |= self.add(value) + return change + + def set_epsilon(self, value=True): + last = self.contains_epsilon + self.contains_epsilon = value + return last != self.contains_epsilon + + def update(self, other): + n = len(self.set) + self.set.update(other.set) + return n != len(self.set) + + def epsilon_update(self, other): + return self.set_epsilon(self.contains_epsilon | other.contains_epsilon) + + def hard_update(self, other): + return self.update(other) | self.epsilon_update(other) + + def find_match(self, match): + for item in self.set: + if item == match: + return item + return None + + def __len__(self): + return len(self.set) + int(self.contains_epsilon) + + def __str__(self): + return '%s-%s' % (str(self.set), self.contains_epsilon) + + def __repr__(self): + return str(self) + + def __iter__(self): + return iter(self.set) + + def __nonzero__(self): + return len(self) > 0 + + def __eq__(self, other): + if isinstance(other, set): + return self.set == other + return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon + + +def inspect(item, grammar_name='G', mapper=None): + try: + return mapper[item] + except (TypeError, KeyError ): + if isinstance(item, dict): + items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) + return f'{{\n {items} \n}}' + elif isinstance(item, ContainerSet): + args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' + return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' + elif isinstance(item, EOF): + return f'{grammar_name}.EOF' + elif isinstance(item, Epsilon): + return f'{grammar_name}.Epsilon' + elif isinstance(item, Symbol): + return f"G['{item.Name}']" + elif isinstance(item, Sentence): + items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) + return f'Sentence({items})' + elif isinstance(item, Production): + left = inspect(item.Left, grammar_name, mapper) + right = inspect(item.Right, grammar_name, mapper) + return f'Production({left}, {right})' + elif isinstance(item, tuple) or isinstance(item, list): + ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') + return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' + else: + raise ValueError(f'Invalid: {item}') + +def pprint(item, header=""): + if header: + print(header) + + if isinstance(item, dict): + for key, value in item.items(): + print(f'{key} ---> {value}') + elif isinstance(item, list): + print('[') + for x in item: + print(f' {repr(x)}') + print(']') + else: + print(item) + +class Token: + """ + Basic token class. + + Parameters + ---------- + lex : str + Token's lexeme. + token_type : Enum + Token's type. + """ + + def __init__(self, lex, token_type): + self.lex = lex + self.token_type = token_type + + def __str__(self): + return f'{self.token_type}: {self.lex}' + + def __repr__(self): + return str(self) + + @property + def is_valid(self): + return True + +class UnknownToken(Token): + def __init__(self, lex): + Token.__init__(self, lex, None) + + def transform_to(self, token_type): + return Token(self.lex, token_type) + + @property + def is_valid(self): + return False + +def tokenizer(G, fixed_tokens): + def decorate(func): + def tokenize_text(text): + tokens = [] + for lex in text.split(): + try: + token = fixed_tokens[lex] + except KeyError: + token = UnknownToken(lex) + try: + token = func(token) + except TypeError: + pass + tokens.append(token) + tokens.append(Token('$', G.EOF)) + return tokens + + if hasattr(func, '__call__'): + return tokenize_text + elif isinstance(func, str): + return tokenize_text(func) + else: + raise TypeError('Argument must be "str" or a callable object.') + return decorate + +class DisjointSet: + def __init__(self, *items): + self.nodes = { x: DisjointNode(x) for x in items } + + def merge(self, items): + items = (self.nodes[x] for x in items) + try: + head, *others = items + for other in others: + head.merge(other) + except ValueError: + pass + + @property + def representatives(self): + return { n.representative for n in self.nodes.values() } + + @property + def groups(self): + return [[n for n in self.nodes.values() if n.representative == r] for r in self.representatives] + + def __len__(self): + return len(self.representatives) + + def __getitem__(self, item): + return self.nodes[item] + + def __str__(self): + return str(self.groups) + + def __repr__(self): + return str(self) + +class DisjointNode: + def __init__(self, value): + self.value = value + self.parent = self + + @property + def representative(self): + if self.parent != self: + self.parent = self.parent.representative + return self.parent + + def merge(self, other): + other.representative.parent = self.representative + + def __str__(self): + return str(self.value) + + def __repr__(self): + return str(self) + + +class ShiftReduceParser: + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + + def __init__(self, G, verbose=False): + self.G = G + self.verbose = verbose + self.action = {} + self.goto = {} + self._build_parsing_table() + + def _build_parsing_table(self): + raise NotImplementedError() + + def __call__(self, w, get_shift_reduce=False): + stack = [0] + cursor = 0 + output = [] + operations = [] + + while True: + state = stack[-1] + lookahead = w[cursor] + if self.verbose: print(stack, w[cursor:]) + + # Your code here!!! (Detect error) + try: + if state not in self.action or lookahead not in self.action[state]: + return None + except: + print(state) + print(self.action) + print(lookahead) + return None + + action, tag = list(self.action[state][lookahead])[0] + # Your code here!!! (Shift case) + if action is ShiftReduceParser.SHIFT: + operations.append(ShiftReduceParser.SHIFT) + stack.append(tag) + cursor += 1 + # Your code here!!! (Reduce case) + elif action is ShiftReduceParser.REDUCE: + operations.append(ShiftReduceParser.REDUCE) + if len(tag.Right): + stack = stack[:-len(tag.Right)] + stack.append(list(self.goto[stack[-1]][tag.Left])[0]) + output.append(tag) + # Your code here!!! (OK case) + elif action is ShiftReduceParser.OK: + return output if not get_shift_reduce else(output,operations) + # Your code here!!! (Invalid case) + else: + raise ValueError \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/core/cmp/visitor.py b/src/Files_from_TypeInferencer/core/cmp/visitor.py new file mode 100644 index 00000000..96484283 --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/visitor.py @@ -0,0 +1,80 @@ +# The MIT License (MIT) +# +# Copyright (c) 2013 Curtis Schlak +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import inspect + +__all__ = ['on', 'when'] + +def on(param_name): + def f(fn): + dispatcher = Dispatcher(param_name, fn) + return dispatcher + return f + + +def when(param_type): + def f(fn): + frame = inspect.currentframe().f_back + func_name = fn.func_name if 'func_name' in dir(fn) else fn.__name__ + dispatcher = frame.f_locals[func_name] + if not isinstance(dispatcher, Dispatcher): + dispatcher = dispatcher.dispatcher + dispatcher.add_target(param_type, fn) + def ff(*args, **kw): + return dispatcher(*args, **kw) + ff.dispatcher = dispatcher + return ff + return f + + +class Dispatcher(object): + def __init__(self, param_name, fn): + frame = inspect.currentframe().f_back.f_back + top_level = frame.f_locals == frame.f_globals + self.param_index = self.__argspec(fn).args.index(param_name) + self.param_name = param_name + self.targets = {} + + def __call__(self, *args, **kw): + typ = args[self.param_index].__class__ + d = self.targets.get(typ) + if d is not None: + return d(*args, **kw) + else: + issub = issubclass + t = self.targets + ks = t.keys() + ans = [t[k](*args, **kw) for k in ks if issub(typ, k)] + if len(ans) == 1: + return ans.pop() + return ans + + def add_target(self, typ, target): + self.targets[typ] = target + + @staticmethod + def __argspec(fn): + # Support for Python 3 type hints requires inspect.getfullargspec + if hasattr(inspect, 'getfullargspec'): + return inspect.getfullargspec(fn) + else: + return inspect.getargspec(fn) diff --git a/src/Files_from_TypeInferencer/core/cmp/visitors.py b/src/Files_from_TypeInferencer/core/cmp/visitors.py new file mode 100644 index 00000000..cf6e9dcf --- /dev/null +++ b/src/Files_from_TypeInferencer/core/cmp/visitors.py @@ -0,0 +1,1253 @@ +import core.cmp.visitor as visitor +from core.cmp.CoolUtils import * +from core.cmp.semantic import SemanticError +from core.cmp.semantic import Attribute, Method, Type +from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType +from core.cmp.semantic import Context, Scope + +WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' +SELF_IS_READONLY = 'Variable "self" is read-only.' +LOCAL_ALREADY_DEFINED = 'Variable "%s" is already defined in method "%s".' +INCOMPATIBLE_TYPES = 'Cannot convert "%s" into "%s".' +VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined in "%s".' +INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' +CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' + +build_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE' ] + +#AST Printer +class FormatVisitor(object): + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(ProgramNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' + statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) + return f'{ans}\n{statements}' + + @visitor.when(ClassDeclarationNode) + def visit(self, node, tabs=0): + parent = '' if node.parent is None else f"inherits {node.parent}" + ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' + features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) + return f'{ans}\n{features}' + + @visitor.when(AttrDeclarationNode) + def visit(self, node, tabs=0): + sons = [node.expr] if node.expr else [] + text = '<- ' if node.expr else '' + ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {node.type} {text}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' if body else f'{ans}' + + @visitor.when(FuncDeclarationNode) + def visit(self, node, tabs=0): + params = ', '.join(':'.join(param) for param in node.params) + ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {node.type} {{}}' + body = '\n'.join(self.visit(child, tabs + 1) for child in node.body) + return f'{ans}\n{body}' + + @visitor.when(IfThenElseNode) + def visit(self, node, tabs=0): + sons = [node.condition, node.if_body] + text = '' + if node.else_body: + sons.append(node.else_body) + text += 'else ' + ans = '\t' * tabs + f'\\__IfThenElseNode: if then {text} fi' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(WhileLoopNode) + def visit(self, node, tabs=0): + sons = [node.condition, node.body] + ans = '\t' * tabs + f'\\__WhileLoopNode: while loop pool' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(BlockNode) + def visit(self, node, tabs=0): + sons = node.exprs + ans = '\t' * tabs + f'\\__BlockNode: {{ ... }}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(LetInNode) + def visit(self, node, tabs=0): + sons = node.let_body + [node.in_body] + ans = '\t' * tabs + f'\\__LetInNode: let {{ ... }} in ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(CaseOfNode) + def visit(self, node, tabs=0): + sons = [node.expr] + node.branches + ans = '\t' * tabs + f'\\__CaseOfNode: case of {{ ... }} esac' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(CaseExpressionNode) + def visit(self, node, tabs=0): + sons = [node.expr] + ans = '\t' * tabs + f'\\__CaseExpressionNode: {node.id} : {node.type} => ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(LetAttributeNode) + def visit(self, node, tabs=0): + sons = [node.expr] if node.expr else [] + text = '<- ' if node.expr else '' + ans = '\t' * tabs + f'\\__LetAttributeNode: {node.id} : {node.type} {text}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' if body else f'{ans}' + + @visitor.when(AssignNode) + def visit(self, node, tabs=0): + sons = [node.expr] + ans = '\t' * tabs + f'\\__AssignNode: {node.id} = ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' + right = self.visit(node.expr, tabs + 1) + return f'{ans}\n{right}' + + @visitor.when(BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + left = self.visit(node.left, tabs + 1) + right = self.visit(node.right, tabs + 1) + return f'{ans}\n{left}\n{right}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' + + @visitor.when(FunctionCallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + ans = '\t' * tabs + f'\\__FunctionCallNode: .{node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{obj}\n{args}' + + @visitor.when(MemberCallNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__MemberCallNode: {node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{args}' + + @visitor.when(NewNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__NewNode: new {node.type}()' + +# Type Collector +class TypeCollector(object): + def __init__(self, errors=[]): + self.context = None + self.errors = errors + self.type_level = {} + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + self.context = Context() + obj = self.context.create_type('Object') + self.context.create_type('Int').set_parent(obj) + self.context.create_type('String').set_parent(obj) + self.context.create_type('Bool').set_parent(obj) + self.context.create_type('IO').set_parent(obj) + #self.context.create_type('SELF_TYPE') + self.context.create_type('AUTO_TYPE') + + for def_class in node.declarations: + self.visit(def_class) + + # comparison for sort node.declarations + def get_type_level(typex): + try: + parent = self.type_level[typex] + except KeyError: + return 0 + + if parent == 0: + self.errors.append('Cyclic heritage.') + elif type(parent) is not int: + self.type_level[typex] = 0 if parent else 1 + if type(parent) is str: + self.type_level[typex] = get_type_level(parent) + 1 + + return self.type_level[typex] + + node.declarations.sort(key = lambda node: get_type_level(node.id)) + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + if node.id not in ['AUTO_TYPE', 'SELF_TYPE', 'self']: + try: + self.context.create_type(node.id) + self.type_level[node.id] = node.parent + except SemanticError as ex: + self.errors.append(ex.text) + else: + self.errors.append(f'{node.id} is a keyword and cannot be a class name') + +# Type Builder +class TypeBuilder: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + for def_class in node.declarations: + self.visit(def_class) + + try: + main = self.context.get_type('Main') + main.get_method('main') + if main.parent.name != 'Object': + self.errors.append('The class "Main" cannot inherits from any type.') + except SemanticError: + self.errors.append('The class "Main" and his method "main" are needed.') + + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + + if node.parent: + if node.parent in build_in_types: + self.errors.append(f'Is not possible to inherits from "{node.parent}"') + else: + try: + parent_type = self.context.get_type(node.parent) + self.current_type.set_parent(parent_type) + except SemanticError as ex: + self.errors.append(ex.text) + + for feature in node.features: + self.visit(feature) + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + try: + attr_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError as ex: + self.errors.append(ex.text) + attr_type = ErrorType() + + try: + self.current_type.define_attribute(node.id, attr_type) + except SemanticError as ex: + self.errors.append(ex.text) + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + arg_names, arg_types = [], [] + for idx, typex in node.params: + try: + arg_type = self.context.get_type(typex) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError as ex: + self.errors.append(ex.text) + arg_type = ErrorType() + + arg_names.append(idx) + arg_types.append(arg_type) + + try: + ret_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError as ex: + self.errors.append(ex.text) + ret_type = ErrorType() + + try: + self.current_type.define_method(node.id, arg_names, arg_types, ret_type) + except SemanticError as ex: + self.errors.append(ex.text) + +# Compute the Lowest Common Ancestor in +# the type hierarchy tree +def LCA(type_list, context): + known_types = set() + counter = {} + + type_list = [ context.get_type(tp.name) for tp in type_list ] + for typex in type_list: + node = typex + while True: + try: + counter[node.name] += 1 + if counter[node.name] == len(type_list): + return [t for t in known_types if t.name == node.name][0] + except KeyError: + counter[node.name] = 1 + known_types.add(node) + if node.parent: + node = node.parent + else: + break + + raise Exception('El LCA se partio') + +def IsAuto(name): + return name == 'AUTO_TYPE' or IsVoid(name) + +def IsVoid(name): + return name == 'void' + +# Type Checker +class TypeChecker: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.current_method = None + self.errors = errors + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope=None): + scope = Scope() + for declaration in node.declarations: + self.visit(declaration, scope.create_child()) + return scope + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + self.current_type = self.context.get_type(node.id) + + scope.define_variable('self', self.current_type) + for attr in self.current_type.attributes: + scope.define_variable(attr.name, attr.type) + + for feature in node.features: + self.visit(feature, scope.create_child()) + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + try: + node_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + if not node.expr: + node.computed_type = node_type + return + + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + + if not (IsAuto(expr_type.name) or expr_type.conforms_to(node_type)): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + + node.computed_type = node_type + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + self.current_method = self.current_type.get_method(node.id) + + for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): + scope.define_variable(pname, ptype) + + for expr in node.body: + self.visit(expr, scope) + + last_expr = node.body[-1] + last_expr_type = last_expr.computed_type + method_rtn_type = self.current_method.return_type + + if not last_expr_type.conforms_to(method_rtn_type): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', last_expr_type.name, 1).replace('%s', method_rtn_type.name, 1)) + + @visitor.when(AssignNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + + if scope.is_defined(node.id): + var = scope.find_variable(node.id) + node_type = var.type + + if var.name == 'self': + self.errors.append(SELF_IS_READONLY) + elif not (IsAuto(expr_type.name) or expr_type.conforms_to(node_type)): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + else: + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(CaseOfNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + has_auto = has_error = False + + types_list = [] + for case in node.branches: + self.visit(case.expr, scope) + has_auto |= IsAuto(case.expr.computed_type.name) + has_error |= case.expr.computed_type.name == '' + types_list.append(case.expr.computed_type) + + if has_error: + node.computed_type = ErrorType() + elif has_auto: + node.computed_type = self.context.get_type('AUTO_TYPE') + else: + node.computed_type = LCA(types_list, self.context) + + @visitor.when(CaseExpressionNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + node.computed_type = node.expr.computed_type + + @visitor.when(LetInNode) + def visit(self, node, scope): + child = scope.create_child() + node.scope = child + + for expr in node.let_body: + self.visit(expr, child) + + self.visit(node.in_body, child) + node.computed_type = node.in_body.computed_type + + @visitor.when(LetAttributeNode) + def visit(self, node, scope): + try: + node_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + if not scope.is_local(node.id): + scope.define_variable(node.id, node_type) + else: + self.errors.append(LOCAL_ALREADY_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) + + if not node.expr: + node.computed_type = node.type + return + + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + + if not(IsAuto(expr_type.name) or expr_type.conforms_to(node_type)): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + + + node.computed_type = node_type + + @visitor.when(IfThenElseNode) + def visit(self, node, scope): + self.visit(node.condition, scope) + expr_type = node.condition.computed_type + + if not expr_type.name in ['Bool', 'AUTO_TYPE']: + self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'If', 1).replace('%s', expr_type.name, 1)) + + self.visit(node.if_body, scope) + node.computed_type = node.if_body.computed_type + + if node.else_body: + self.visit(node.else_body, scope) + if IsAuto(node.if_body.computed_type.name) or IsAuto(node.else_body.computed_type.name): + node.computed_type = self.context.get_type('AUTO_TYPE') + elif '' in [node.if_body.computed_type.name, node.else_body.computed_type.name]: + node.computed_type = ErrorType() + else: + node.computed_type = LCA([node.if_body.computed_type, node.else_body.computed_type], self.context) + + @visitor.when(BlockNode) + def visit(self, node, scope): + for expr in node.exprs: + self.visit(expr, scope) + + last_expr = node.exprs[-1] + node.computed_type = last_expr.computed_type + + @visitor.when(WhileLoopNode) + def visit(self, node, scope): + self.visit(node.condition, scope) + expr_type = node.condition.computed_type + + if not expr_type.name in ['Bool', 'AUTO_TYPE']: + self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'While', 1).replace('%s', expr_type.name, 1)) + + self.visit(node.body, scope) + node.computed_type = VoidType() + + @visitor.when(FunctionCallNode) + def visit(self, node, scope): + self.visit(node.obj, scope) + obj_type = node.obj.computed_type + + if node.type: + try: + if IsAuto(node.type): + raise SemanticError('Is not possible to use AUTO_TYPE in a cast') + if not obj_type.conforms_to(self.context.get_type(node.type)): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', obj_type.name, 1).replace('%s', node.type, 1)) + except SemanticError as ex: + self.errors.append(ex.text) + + try: + if node.type: + obj_method = self.context.get_type(node.type).get_method(node.id) + else: + obj_method = obj_type.get_method(node.id) + + if len(node.args) == len(obj_method.param_types): + for arg, param_type in zip(node.args, obj_method.param_types): + self.visit(arg, scope) + arg_type = arg.computed_type + + if not (IsAuto(arg_type.name) or arg_type.conforms_to(param_type)): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) + else: + self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + + node_type = obj_method.return_type + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(MemberCallNode) + def visit(self, node, scope): + obj_type = self.current_type + + try: + obj_method = obj_type.get_method(node.id) + + if len(node.args) == len(obj_method.param_types): + for arg, param_type in zip(node.args, obj_method.param_types): + self.visit(arg, scope) + arg_type = arg.computed_type + + if not (IsAuto(arg_type.name) or arg_type.conforms_to(param_type)): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) + else: + self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + + node_type = obj_method.return_type + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(BinaryNode) + def visit(self, node, scope): + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + + if not (IsAuto(left_type.name) or left_type.conforms_to(IntType())) or not (IsAuto(right_type.name) or right_type.conforms_to(IntType())): + self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) + node_type = ErrorType() + else: + node_type = IntType() + + node.computed_type = node_type + + @visitor.when(IntegerNode) + def visit(self, node, scope): + node.computed_type = IntType() + + @visitor.when(StringNode) + def visit(self, node, scope): + node.computed_type = StringType() + + @visitor.when(BoolNode) + def visit(self, node, scope): + node.computed_type = BoolType() + + @visitor.when(IdNode) + def visit(self, node, scope): + if scope.is_defined(node.lex): + var = scope.find_variable(node.lex) + node_type = var.type + else: + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1).replace('%s', self.current_method.name, 1)) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(NewNode) + def visit(self, node, scope): + if node.type in build_in_types: + self.errors.append(f'It cannot be initialized a {node.type} with the new keyword') + node.computed_type = ErrorType() + else: + try: + node_type = self.context.get_type(node.type) + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(IsVoidNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + node.computed_type = self.context.get_type('Bool') + + @visitor.when(ComplementNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + if not (IsAuto(node.expr.computed_type.name) or node.expr.computed_type.name != 'Int'): + self.errors.append("Complement works only for Int") + node.computed_type = ErrorType() + else: + node.computed_type = self.context.get_type('Int') + + @visitor.when(NotNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + if not (IsAuto(node.expr.computed_type.name) or node.expr.computed_type.name != 'Bool'): + self.errors.append("Not operator works only for Bool") + node.computed_type = ErrorType() + else: + node.computed_type = self.context.get_type('Bool') + +# Type Inference Visitor +class InferenceVisitor(object): + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.current_method = None + self.errors = errors + + @visitor.on('node') + def update(self, node, scope, ntype): + pass + + @visitor.when(Node) + def update(self, node, scope, ntype): + pass + + @visitor.when(FunctionCallNode) + def update(self, node, scope, ntype): + obj_type = node.obj.computed_type + + obj_type.get_method(node.id).return_type = ntype + node.computed_type = ntype + + @visitor.when(MemberCallNode) + def update(self, node, scope, ntype): + obj_type = self.current_type + + obj_type.get_method(node.id).return_type = ntype + node.computed_type = ntype + + @visitor.when(AttrDeclarationNode) + def update(self, node, scope, ntype): + scope.find_variable(node.id).type = ntype + node.computed_type = ntype + + @visitor.when(IdNode) + def update(self, node, scope, ntype): + scope.find_variable(node.lex).type = ntype + node.computed_type = ntype + + @visitor.when(IfThenElseNode) + def update(self, node, scope, ntype): + if IsAuto(node.if_body.computed_type.name): + self.update(node.if_body, scope, ntype) + + node.computed_type = node.if_body.computed_type + + if node.else_body: + if IsAuto(node.else_body.computed_type.name): + self.update(node.else_body, scope, ntype) + + names = [node.if_body.computed_type.name, node.else_body.computed_type.name] + if 'AUTO_TYPE' not in names and '' not in names: + node.computed_type = LCA([node.if_body.computed_type, node.else_body.computed_type], self.context) + else: + if '' in names: + node.computed_type = ErrorType() + else: + node.computed_type = self.context.get_type('AUTO_TYPE') + + @visitor.when(CaseOfNode) + def update(self, node, scope, ntype): + types_list = [] + has_auto = has_error = False + + for case in node.branches: + if IsAuto(case.computed_type.name): + self.update(branch, scope, ntype) + has_auto |= IsAuto(case.expr.computed_type.name) + has_error |= case.expr.computed_type.name == '' + types_list.append(case.expr.computed_type) + + if has_error: + node.computed_type = ErrorType() + elif has_auto: + node.computed_type = self.context.get_type('AUTO_TYPE') + else: + node.computed_type = LCA(types_list) + + @visitor.when(CaseExpressionNode) + def update(self, node, scope, ntype): + self.update(node.expr, scope, ntype) + node.computed_type = node.expr.computed_type + + @visitor.when(LetInNode) + def update(self, node, scope, ntype): + self.update(node.in_body, node.scope, ntype) + node.computed_type = node.in_body.computed_type + + @visitor.when(BlockNode) + def update(self, node, scope, ntype): + self.update(node.exprs[-1], scope, ntype) + node.computed_type = node.exprs[-1].computed_type + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope=None): + scope = Scope() + for declaration in node.declarations: + self.visit(declaration, scope.create_child()) + return scope + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + self.current_type = self.context.get_type(node.id) + + scope.define_variable('self', self.current_type) + for attr in self.current_type.attributes: + scope.define_variable(attr.name, attr.type) + + for feature in node.features: + self.visit(feature, scope.create_child()) + + for idx, attr in enumerate(self.current_type.attributes): + actual_type = scope.find_variable(attr.name).type + if IsAuto(attr.type.name): + self.current_type.attributes[idx].type = actual_type + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + node.scope = scope + if node.expr: + self.visit(node.expr, scope) + if IsAuto(node.type): + if not IsAuto(node.expr.computed_type.name): + scope.find_variable(node.id).type = node.expr.computed_type + else: + if IsAuto(node.expr.computed_type.name): + self.update(node.expr, scope, node.type) + else: + if not node.expr.computed_type.conforms_to(node.computed_type): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', node.expr.computed_type.name, 1).replace('%s', node.computed_type.name, 1)) + node.computed_type = ErrorType() + return + + node.computed_type = node.expr.computed_type + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + self.current_method = self.current_type.get_method(node.id) + node.method = self.current_method + + for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): + scope.define_variable(pname, ptype) + + for expr in node.body: + self.visit(expr, scope) + + last_expr = node.body[-1] + last_expr_type = last_expr.computed_type + method_rtn_type = self.current_method.return_type + + if IsAuto(method_rtn_type.name): + if not IsAuto(last_expr_type.name): + self.current_method.return_type = last_expr_type + method_rtn_type = last_expr_type + else: + if IsAuto(last_expr_type.name): + self.update(last_expr, scope, method_rtn_type) + else: + if not last_expr_type.conforms_to(method_rtn_type): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', last_expr_type.name, 1).replace('%s', method_rtn_type.name, 1)) + + for idx, pname in enumerate(self.current_method.param_names): + actual_type = scope.find_variable(pname).type + if self.current_method.param_types[idx].name != actual_type.name: + self.current_method.param_types[idx] = actual_type + + @visitor.when(IfThenElseNode) + def visit(self, node, scope): + self.visit(node.condition, scope) + expr_type = node.condition.computed_type + + if IsAuto(expr_type.name): + self.update(node.condition, scope, self.context.get_type('Bool')) + expr_type = node.condition.computed_type + if expr_type.name not in ['Bool', 'AUTO_TYPE']: + self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'If', 1).replace('%s', expr_type.name, 1)) + + self.visit(node.if_body, scope) + node.computed_type = node.if_body.computed_type + + if node.else_body: + self.visit(node.else_body, scope) + names = [node.if_body.computed_type.name, node.else_body.computed_type.name] + if 'AUTO_TYPE' not in names and '' not in names: + node.computed_type = LCA([node.if_body.computed_type, node.else_body.computed_type], self.context) + else: + if '' in names: + node.computed_type = ErrorType() + else: + node.computed_type = self.context.get_type('AUTO_TYPE') + + @visitor.when(WhileLoopNode) + def visit(self, node, scope): + self.visit(node.condition, scope) + expr_type = node.condition.computed_type + if IsAuto(expr_type.name): + self.update(node.condition, scope, self.context.get_type('Bool')) + expr_type = node.condition.computed_type + + if expr_type.name not in ['Bool', 'AUTO_TYPE']: + self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'If', 1).replace('%s', expr_type.name, 1)) + + self.visit(node.body, scope) + node.computed_type = VoidType() + + @visitor.when(BlockNode) + def visit(self, node, scope): + for expr in node.exprs: + self.visit(expr, scope) + + last_expr = node.exprs[-1] + node.computed_type = last_expr.computed_type + + @visitor.when(LetInNode) + def visit(self, node, scope): + child = scope.create_child() + node.scope = child + + for attr in node.let_body: + self.visit(attr, child) + + self.visit(node.in_body, child) + node.computed_type = node.in_body.computed_type + + for attr in node.let_body: + type_name = attr.type + if attr.computed_type.name == '': + continue + actual_type = child.find_variable(attr.id).type + if type_name != actual_type.name: + attr.type = actual_type.name + + @visitor.when(LetAttributeNode) + def visit(self, node, scope): + node.scope = scope + try: + node_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + if not scope.is_local(node.id): + scope.define_variable(node.id, node_type) + else: + self.errors.append(LOCAL_ALREADY_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) + + if not node.expr: + node.computed_type = node_type + return + + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + + if IsAuto(node_type.name): + if not IsAuto(expr_type.name): + node.type = expr_type.name + scope.find_variable(node.id).type = expr_type + node.computed_type = expr_type + else: + if not IsAuto(expr_type.name): + if not expr_type.conforms_to(node_type): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + else: + self.update(node.expr, scope, node_type) + node.computed_type = node.expr.computed_type + + @visitor.when(CaseOfNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + has_auto = has_error = False + + types_list = [] + for case in node.branches: + self.visit(case.expr, scope) + has_auto |= IsAuto(case.expr.computed_type.name) + has_error |= case.expr.computed_type.name == '' + types_list.append(case.expr.computed_type) + + if has_error: + node.computed_type = ErrorType() + elif has_auto: + node.computed_type = self.context.get_type('AUTO_TYPE') + else: + node.computed_type = LCA(types_list, self.context) + + @visitor.when(CaseExpressionNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + node.computed_type = node.expr.computed_type + + @visitor.when(AssignNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + + if scope.is_defined(node.id): + var = scope.find_variable(node.id) + node_type = var.type + + if var.name == 'self': + self.errors.append(SELF_IS_READONLY) + else: + if IsAuto(node_type.name): + if not IsAuto(expr_type.name): + node.type = expr_type.name + scope.find_variable(node.id).type = expr_type + node.computed_type = expr_type + else: + if not IsAuto(expr_type.name): + if not expr_type.conforms_to(node_type): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + else: + self.update(node.expr, scope, node_type) + node.computed_type = node.expr.computed_type + else: + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) + node.computed_type = ErrorType() + + @visitor.when(IsVoidNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + node.computed_type = self.context.get_type('Bool') + + @visitor.when(ComplementNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + if IsAuto(node.expr.computed_type.name): + self.update(node.expr, scope, self.context.get_type('Int')) + node.computed_type = node.expr.computed_type + else: + if node.expr.computed_type.name != 'Int': + self.errors.append("Complement works only for Int") + node.computed_type = ErrorType() + else: + node.computed_type = self.context.get_type('Int') + + @visitor.when(NotNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + if IsAuto(node.expr.computed_type): + self.update(node.expr, scope, self.context.get_type('Bool')) + node.computed_type = node.expr.computed_type + else: + if node.expr.computed_type.name != 'Bool': + self.errors.append("Not operator works only for Bool") + node.computed_type = ErrorType() + else: + node.computed_type = self.context.get_type('Bool') + + @visitor.when(BinaryNode) + def visit(self, node, scope): + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + + if IsAuto(left_type.name): + self.update(node.left, scope, self.context.get_type('Int')) + left_type = node.left.computed_type + + if IsAuto(right_type.name): + self.update(node.right, scope, self.context.get_type('Int')) + right_type = node.right.computed_type + + if not (IsAuto(left_type.name) or left_type.conforms_to(IntType())) or not (IsAuto(right_type.name) or right_type.conforms_to(IntType())): + self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) + node_type = ErrorType() + else: + node_type = IntType() + + node.computed_type = node_type + + @visitor.when(FunctionCallNode) + def visit(self, node, scope): + self.visit(node.obj, scope) + obj_type = node.obj.computed_type + + if node.type: + try: + if IsAuto(node.type): + raise SemanticError('Is not possible to use AUTO_TYPE in a cast') + if not obj_type.conforms_to(self.context.get_type(node.type)): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', obj_type.name, 1).replace('%s', node.type, 1)) + except SemanticError as ex: + self.errors.append(ex.text) + + try: + if node.type: + obj_method = self.context.get_type(node.type).get_method(node.id) + else: + obj_method = obj_type.get_method(node.id) + + if len(node.args) == len(obj_method.param_types): + for idx, arg in enumerate(node.args): + self.visit(arg, scope) + arg_type = arg.computed_type + param_type = obj_method.param_types[idx] + + if IsAuto(param_type.name): + if not IsAuto(arg_type.name): + obj_method.param_types[idx] = arg_type + else: + if IsAuto(arg_type.name): + self.update(arg, scope, param_type) + else: + if not arg_type.conforms_to(param_type): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) + else: + self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + + node_type = obj_method.return_type + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(MemberCallNode) + def visit(self, node, scope): + obj_type = self.current_type + + try: + obj_method = obj_type.get_method(node.id) + + if len(node.args) == len(obj_method.param_types): + for idx, arg in enumerate(node.args): + self.visit(arg, scope) + arg_type = arg.computed_type + param_type = obj_method.param_types[idx] + + if IsAuto(param_type.name): + if not IsAuto(arg_type.name): + obj_method.param_types[idx] = arg_type + else: + if IsAuto(arg_type.name): + self.update(arg, scope, param_type) + else: + if not arg_type.conforms_to(param_type): + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) + else: + self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + + node_type = obj_method.return_type + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(IntegerNode) + def visit(self, node, scope): + node.computed_type = IntType() + + @visitor.when(StringNode) + def visit(self, node, scope): + node.computed_type = StringType() + + @visitor.when(BoolNode) + def visit(self, node, scope): + node.computed_type = BoolType() + + @visitor.when(IdNode) + def visit(self, node, scope): + if scope.is_defined(node.lex): + var = scope.find_variable(node.lex) + node_type = var.type + else: + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1).replace('%s', self.current_method.name, 1)) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(NewNode) + def visit(self, node, scope): + try: + node_type = self.context.get_type(node.type) + except SemanticError as ex: + self.errors.append(ex.text) + node_type = ErrorType() + + node.computed_type = node_type + +class ComputedVisitor(FormatVisitor): + def replace_auto(self, name): + return 'Object' if IsAuto(name) else name + + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(ProgramNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' + statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) + return f'{ans}\n{statements}' + + @visitor.when(ClassDeclarationNode) + def visit(self, node, tabs=0): + parent = '' if node.parent is None else f"inherits {node.parent}" + ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' + features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) + return f'{ans}\n{features}' + + @visitor.when(AttrDeclarationNode) + def visit(self, node, tabs=0): + sons = [node.expr] if node.expr else [] + text = '<- ' if node.expr else '' + real_type = self.replace_auto(node.scope.find_variable(node.id).type.name) + ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {real_type} {text}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' if body else f'{ans}' + + @visitor.when(FuncDeclarationNode) + def visit(self, node, tabs=0): + params = ', '.join(':'.join(param) for param in node.params) + real_type = self.replace_auto(node.method.return_type.name) + ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {real_type} {{}}' + body = '\n'.join(self.visit(child, tabs + 1) for child in node.body) + return f'{ans}\n{body}' + + @visitor.when(IfThenElseNode) + def visit(self, node, tabs=0): + sons = [node.condition, node.if_body] + text = '' + if node.else_body: + sons.append(node.else_body) + text += 'else ' + ans = '\t' * tabs + f'\\__IfThenElseNode: if then {text} fi' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(WhileLoopNode) + def visit(self, node, tabs=0): + sons = [node.condition, node.body] + ans = '\t' * tabs + f'\\__WhileLoopNode: while loop pool' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(BlockNode) + def visit(self, node, tabs=0): + sons = node.exprs + ans = '\t' * tabs + f'\\__BlockNode: {{ ... }}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(LetInNode) + def visit(self, node, tabs=0): + sons = node.let_body + [node.in_body] + ans = '\t' * tabs + f'\\__LetInNode: let {{ ... }} in ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(CaseOfNode) + def visit(self, node, tabs=0): + sons = [node.expr] + node.branches + ans = '\t' * tabs + f'\\__CaseOfNode: case of {{ ... }} esac' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(CaseExpressionNode) + def visit(self, node, tabs=0): + sons = [node.expr] + ans = '\t' * tabs + f'\\__CaseExpressionNode: {node.id} : {node.type} => ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(LetAttributeNode) + def visit(self, node, tabs=0): + sons = [node.expr] if node.expr else [] + text = '<- ' if node.expr else '' + real_type = self.replace_auto(node.scope.find_variable(node.id).type.name) + ans = '\t' * tabs + f'\\__LetAttributeNode: {node.id} : {real_type} {text}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' if body else f'{ans}' + + @visitor.when(AssignNode) + def visit(self, node, tabs=0): + sons = [node.expr] + ans = '\t' * tabs + f'\\__AssignNode: {node.id} <- ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' + right = self.visit(node.expr, tabs + 1) + return f'{ans}\n{right}' + + @visitor.when(BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + left = self.visit(node.left, tabs + 1) + right = self.visit(node.right, tabs + 1) + return f'{ans}\n{left}\n{right}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' + + @visitor.when(FunctionCallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + ans = '\t' * tabs + f'\\__FunctionCallNode: .{node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{obj}\n{args}' + + @visitor.when(MemberCallNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__MemberCallNode: {node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{args}' + + @visitor.when(NewNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__NewNode: new {node.type}()' diff --git a/src/Files_from_TypeInferencer/main.py b/src/Files_from_TypeInferencer/main.py new file mode 100644 index 00000000..d30dfee3 --- /dev/null +++ b/src/Files_from_TypeInferencer/main.py @@ -0,0 +1,112 @@ +import eel +import logging +from time import sleep +from core.cmp.visitors import * +from core.cmp.evaluation import * + + +def build_AST(G, text): + data, err = [], False + ast = None + txt = '================== TOKENS =====================\n' + tokens = tokenize_text(text) + txt += format_tokens(tokens) + data.append(txt) + txt = '=================== PARSE =====================\n' + #print(parser([t.token_type for t in tokens], get_shift_reduce=True)) + try: + parse, operations = CoolParser([t.token_type for t in tokens], get_shift_reduce=True) + except: + err = True + txt += 'Impossible to parse\n' + #print('\n'.join(repr(x) for x in parse)) + data.append(txt) + if not err: + txt = '==================== AST ======================\n' + ast = evaluate_reverse_parse(parse, operations, tokens) + formatter = FormatVisitor() + tree = formatter.visit(ast) + txt += str(tree) + data.append(txt) + return ast, '\n\n'.join(data) + +def error_formatter(errors): + txt = 'Errors: [\n' + for error in errors: + txt += f'\t{error}\n' + txt += ']\n' + return txt + +def run_pipeline(G, text): + data, err = [], False + ast, txt = build_AST(G, text) + errors = context = scope = None + data.append(txt) + if ast: + txt = '============== COLLECTING TYPES ===============\n' + errors = [] + collector = TypeCollector(errors) + collector.visit(ast) + context = collector.context + if len(errors): + txt += error_formatter(errors) + err = True + txt += 'Context:\n' + txt += str(context) + data.append(txt) + errors.clear() + txt = '=============== BUILDING TYPES ================\n' + builder = TypeBuilder(context, errors) + builder.visit(ast) + if len(errors): + txt += error_formatter(errors) + err = True + errors.clear() + data.append(txt) + txt = '=============== CHECKING TYPES ================\n' + checker = TypeChecker(context, errors) + scope = checker.visit(ast) + if len(errors): + txt += error_formatter(errors) + err = True + errors.clear() + data.append(txt) + txt = '=============== INFERING TYPES ================\n' + inferer = InferenceVisitor(context, errors) + while True: + old = scope.count_auto() + scope = inferer.visit(ast) + if old == scope.count_auto(): + break + errors.clear() + scope = inferer.visit(ast) + if len(errors): + txt += error_formatter(errors) + err = True + errors.clear() + txt += 'Context:\n' + txt += str(context) + '\n' + formatter = ComputedVisitor() + if not err: + tree = formatter.visit(ast) + txt += 'AST:\n' + str(tree) + data.append(txt) + return '\n\n'.join(data) + +@eel.expose +def compile(text): + sleep(2) + return run_pipeline(CoolGrammar, text) + +def main(): + eel.init('web') + + eel_options = {'port': 8045} + eel.start('index.html', size=(1000, 860), options=eel_options, block=False) + + while True: + eel.sleep(0.1) + + +if __name__ == '__main__': + main() \ No newline at end of file From 0bc444e20b3c47559b7c9397108c816188e8e683 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 11 Feb 2020 18:24:51 -0500 Subject: [PATCH 002/520] Add personal info to `doc/README.md` --- doc/Readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index 402477c8..b35a5682 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/) +Lázaro Raúl Iglesias Vera | C412 | [@github_user](https://github.com/stdevRulo) +Miguel Tenorio Potrony | C4xx | [@github_user](https://github.com/stdevAntiD2ta) +Mauricio Lázaro Perdomo Cortés | C4xx | [@github_user](https://github.com/stdevMauricio1802) ## Readme From 5179a8344e163ae36b44ca6a87e59a2f4323c804 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 20:44:01 -0500 Subject: [PATCH 003/520] Add lex.py file for lexer component --- src/Files_from_TypeInferencer/core/cmp/lex.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/Files_from_TypeInferencer/core/cmp/lex.py diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py new file mode 100644 index 00000000..e69de29b From a4c683f301f56a8d0bb14bb82691a68ba681ff22 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 20:49:31 -0500 Subject: [PATCH 004/520] Add tokens name for language --- src/Files_from_TypeInferencer/core/cmp/lex.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index e69de29b..59a92b34 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -0,0 +1,49 @@ +import ply.lex as lex +import re +from .utils import Token +from .CoolUtils import * + + +reserved = { + "class": "CLASS", + "else": "ELSE", + "fi": "FI", + "if": "IF", + "in": "IN", + "inherits": "INHERITS", + "isvoid": "ISVOID", + "let": "LET", + "loop": "LOOP", + "pool": "POOL", + "then": "THEN", + "while": "WHILE", + "case": "CASE", + "esac": "ESAC", + "new": "NEW", + "of": "OF", + "not": "NOT", + "true": "TRUE", + "false": "FALSE", +} + + +tokens = [ + 'NUMBER', + 'TYPEIDENTIFIER', + 'OBJECTIDENTIFIER', + 'EQUALS', + 'PLUS', + 'MINUS', + 'TIMES', + 'DIVIDE', + 'LPAREN', + 'RPAREN', + 'STRING', + 'LESS', + 'LESSEQ', + 'LCBRA', + 'RCBRA', + 'COLON', + 'SEMICOLON', + +] + list(reserved.values()) \ No newline at end of file From 455a88fdffcac951057028935c2e2e3edb35e185 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 20:50:03 -0500 Subject: [PATCH 005/520] Add tokens definition --- src/Files_from_TypeInferencer/core/cmp/lex.py | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index 59a92b34..fbf05ef5 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -46,4 +46,56 @@ 'COLON', 'SEMICOLON', -] + list(reserved.values()) \ No newline at end of file +] + list(reserved.values()) + +t_EQUALS = r'=' +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_LESS = r'<' +t_LESSEQ = r'<=' +t_LCBRA = r'{' +t_RCBRA = r'}' +t_COLON = r':' +t_SEMICOLON = r';' +t_ignore = ' \t\f\r\t\v' + + + +def t_TYPEIDENTIFIER(t): + r'[A-Z][a-zA-Z0-9|_]*' + l_value = t.value.lower() + if l_value == "false" or l_value == "true": + if t.value[0] != "f" and t.value[0] != 't': + return t + t.type = reserved.get(l_value, "TYPEIDENTIFIER") + return t + +def t_OBJECTIDENTIFIER(t): + r'[a-z][a-zA-Z0-9|_]*' + l_value = t.value.lower() + if l_value == "false" or l_value == "true": + if t.value[0] != "f" and t.value[0] != 't': + return t + t.type = reserved.get(l_value, "OBJECTIDENTIFIER") + return t + +def t_NUMBER(t): + r'\d+' + t.value = int(t.value) + return t + +def t_newline(t): + r'\n+' + t.lexer.lineno += len(t.value) + +def t_LINECOMMENT(t): + r'--.*\n' +def t_COMMENT(t): + r'\*.*\*' + +def t_eof(t): + return None \ No newline at end of file From bacc13c21c7cc16beb8d2d4b89829cd807b6d22a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 20:54:30 -0500 Subject: [PATCH 006/520] Add tokenize function for tokenize text an convert the tokens returned by ply.lexer to tokens used by the parser --- src/Files_from_TypeInferencer/core/cmp/lex.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index fbf05ef5..c7cc02dd 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -3,6 +3,8 @@ from .utils import Token from .CoolUtils import * +__lexer__ = None +__text__ = None reserved = { "class": "CLASS", @@ -98,4 +100,16 @@ def t_COMMENT(t): r'\*.*\*' def t_eof(t): - return None \ No newline at end of file + return None + +def tokenize(text): + global __text__ + global __lexer__ + __text__ = text + if __lexer__ is None: + __lexer__ = lex.lex() + __lexer__.input(text) + original_tokens = [token for token in __lexer__] + tokens = [ Token(token.value, tokenType[token.type]) for token in original_tokens] + EOF = Token('$', eof) + return tokens + [EOF] \ No newline at end of file From f3d28fbaebcb3d0300048fc998c47199d2776bd1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 21:08:07 -0500 Subject: [PATCH 007/520] Add error handling to lexer stage --- src/Files_from_TypeInferencer/core/cmp/lex.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index c7cc02dd..bd2c1a07 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -5,6 +5,7 @@ __lexer__ = None __text__ = None +__errors__ = None reserved = { "class": "CLASS", @@ -102,9 +103,24 @@ def t_COMMENT(t): def t_eof(t): return None +def compute_column(token): + line_start = __lexer__.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + +def t_error(t): + global __errors__ + line = t.lexer.lineno + column = compute_column(t) + error_text = t.value[0] + __errors__.append(f"({line},{column}) - LexicographicError: \"{error_text}\"") + t.lexer.skip(1) + + def tokenize(text): global __text__ global __lexer__ + global __errors__ + __errors__ = [] __text__ = text if __lexer__ is None: __lexer__ = lex.lex() @@ -112,4 +128,4 @@ def tokenize(text): original_tokens = [token for token in __lexer__] tokens = [ Token(token.value, tokenType[token.type]) for token in original_tokens] EOF = Token('$', eof) - return tokens + [EOF] \ No newline at end of file + return (tokens + [EOF], __errors__) \ No newline at end of file From cd91abedd809f0ded2ee6d6516a223b57d0941a3 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 21:37:33 -0500 Subject: [PATCH 008/520] Add dict for conver to tokens used by parser --- src/Files_from_TypeInferencer/core/cmp/lex.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index bd2c1a07..b315f15d 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -29,6 +29,38 @@ "false": "FALSE", } +tokenType = { + "CLASS": classx, + "ELSE": elsex, + "FI": fi, + "IF": ifx, + "IN": inx, + "INHERITS": inherits, + "ISVOID": isvoid, + "LET": let, + "LOOP": loop, + "POOL": pool, + "THEN": then, + "WHILE": whilex, + "CASE": case, + "ESAC": esac, + "NEW": new, + "OF": of, + "NOT": notx, + "OBJECTIDENTIFIER": idx, + "TYPEIDENTIFIER": typex, + "LCBRA": ocur, + "RCBRA": ccur, + "LPAREN": opar, + "RPAREN": cpar, + "COLON": colon, + "SEMICOLON": semi, + "NUMBER": integer, + "eof": eof, + #"TRUE": + #"FALSE", +} + tokens = [ 'NUMBER', From 34220a6c948e732583d4a4f6d2b0f2146ef585cc Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 21:40:15 -0500 Subject: [PATCH 009/520] Add eof variable to CoolUtils.py for accesing EOF token --- src/Files_from_TypeInferencer/core/cmp/CoolUtils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py b/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py index fb4e25b3..95ce8ecd 100644 --- a/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py +++ b/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py @@ -170,7 +170,7 @@ class BoolNode(AtomicNode): plus, minus, star, div, isvoid, compl = CoolGrammar.Terminals('+ - * / isvoid ~') notx, less, leq, equal = CoolGrammar.Terminals('not < <= =') new, idx, typex, integer, string, boolx = CoolGrammar.Terminals('new id type integer string bool') - +eof = CoolGrammar.EOF # productions program %= class_list, lambda h, s: ProgramNode(s[1]) From 7286216666d83e13b0f15eea837bb42a5ff12399 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 21:41:21 -0500 Subject: [PATCH 010/520] Fix error in compute_column function --- src/Files_from_TypeInferencer/core/cmp/lex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index b315f15d..83d1f029 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -136,7 +136,7 @@ def t_eof(t): return None def compute_column(token): - line_start = __lexer__.rfind('\n', 0, token.lexpos) + 1 + line_start = __text__.rfind('\n', 0, token.lexpos) + 1 return (token.lexpos - line_start) + 1 def t_error(t): From 45f5a78fe41809a4dd3a8ce88cfff32bdefba2d8 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 21:43:37 -0500 Subject: [PATCH 011/520] Add missing token types --- src/Files_from_TypeInferencer/core/cmp/lex.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index 83d1f029..92467648 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -57,6 +57,8 @@ "SEMICOLON": semi, "NUMBER": integer, "eof": eof, + "PLUS": plus, + "MINUS": minus, #"TRUE": #"FALSE", } From ccf9a2fa57d885f9332d52c96dc1d3a0d7225dd1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 21:45:55 -0500 Subject: [PATCH 012/520] Add missing token types --- src/Files_from_TypeInferencer/core/cmp/lex.py | 4 ++++ src/Files_from_TypeInferencer/main.py | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index 92467648..6c08b3ad 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -59,6 +59,10 @@ "eof": eof, "PLUS": plus, "MINUS": minus, + "DIVIDE": div, + "LESS": less, + "LESSEQ": leq, + "EQUALS": equal, #"TRUE": #"FALSE", } diff --git a/src/Files_from_TypeInferencer/main.py b/src/Files_from_TypeInferencer/main.py index d30dfee3..bea6d3b5 100644 --- a/src/Files_from_TypeInferencer/main.py +++ b/src/Files_from_TypeInferencer/main.py @@ -3,6 +3,7 @@ from time import sleep from core.cmp.visitors import * from core.cmp.evaluation import * +from core.cmp.lex import tokenize def build_AST(G, text): @@ -109,4 +110,12 @@ def main(): if __name__ == '__main__': - main() \ No newline at end of file + # main() + test_data = '''0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ +class Class if then else fi testing Testing ~007agent_bond james_007bones___ + + +new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) +loop pool while tRuE or noT faLsE let in case of ESAC''' + tokens, errors = tokenize(test_data) + print(errors) \ No newline at end of file From d970e67d23102f3f761b3fae478ba1800eb70fbf Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 22 Feb 2020 23:12:19 -0500 Subject: [PATCH 013/520] Add missing tokens --- src/Files_from_TypeInferencer/core/cmp/lex.py | 63 +++++++++++++----- src/Files_from_TypeInferencer/main.py | 65 +++++++++++++++++-- 2 files changed, 106 insertions(+), 22 deletions(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index 6c08b3ad..c2c4bc35 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -60,11 +60,18 @@ "PLUS": plus, "MINUS": minus, "DIVIDE": div, + "TIMES": star, "LESS": less, "LESSEQ": leq, "EQUALS": equal, - #"TRUE": - #"FALSE", + "TRUE": boolx, + "FALSE": boolx, + "COMPLEMENT": compl, + "RARROW": rarrow, + "LARROW": larrow, + "COMMA": comma, + "DOT": dot, + "AT": at, } @@ -86,22 +93,35 @@ 'RCBRA', 'COLON', 'SEMICOLON', + 'COMPLEMENT', + 'RARROW', + 'LARROW', + 'COMMA', + 'DOT', + 'AT' ] + list(reserved.values()) -t_EQUALS = r'=' -t_PLUS = r'\+' -t_MINUS = r'-' -t_TIMES = r'\*' -t_DIVIDE = r'/' -t_LPAREN = r'\(' -t_RPAREN = r'\)' -t_LESS = r'<' -t_LESSEQ = r'<=' -t_LCBRA = r'{' -t_RCBRA = r'}' -t_COLON = r':' -t_SEMICOLON = r';' +t_EQUALS = r'=' +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_LESS = r'<' +t_LESSEQ = r'<=' +t_LCBRA = r'{' +t_RCBRA = r'}' +t_COLON = r':' +t_SEMICOLON = r';' +t_COMPLEMENT = r'~' +t_RARROW = r'=>' +t_LARROW = r'<-' +t_COMMA = r',' +t_DOT = r'\.' +t_AT = r'@' + t_ignore = ' \t\f\r\t\v' @@ -136,11 +156,21 @@ def t_newline(t): def t_LINECOMMENT(t): r'--.*\n' def t_COMMENT(t): - r'\*.*\*' + r'\(\*(.|\n)*\*\)' def t_eof(t): + print("HERE") + print(t.lexer.lineno) + t.lexer.eof =(t.lexer.lineno, compute_column(t)) return None +def t_ERROREOFCOMMENTS(t): + r'\(\*(.|\n)*$' + global __errors__ + line = t.lexer.lineno + column = compute_column(t) + __errors__.append(f"({t.lexer.eof[0]},{t.lexer.eof[1]}) - LexicographicError: EOF IN COMMENTS") + def compute_column(token): line_start = __text__.rfind('\n', 0, token.lexpos) + 1 return (token.lexpos - line_start) + 1 @@ -162,6 +192,7 @@ def tokenize(text): __text__ = text if __lexer__ is None: __lexer__ = lex.lex() + __lexer__.eof = (1,1) __lexer__.input(text) original_tokens = [token for token in __lexer__] tokens = [ Token(token.value, tokenType[token.type]) for token in original_tokens] diff --git a/src/Files_from_TypeInferencer/main.py b/src/Files_from_TypeInferencer/main.py index bea6d3b5..d279ab7f 100644 --- a/src/Files_from_TypeInferencer/main.py +++ b/src/Files_from_TypeInferencer/main.py @@ -111,11 +111,64 @@ def main(): if __name__ == '__main__': # main() - test_data = '''0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ -class Class if then else fi testing Testing ~007agent_bond james_007bones___ + test_data = '''--Any characters between two dashes “--” and the next newline +--(or EOF, if there is no next newline) are treated as comments +(*(*(* +Comments may also be written by enclosing +text in (∗ . . . ∗). The latter form of comment may be nested. +Comments cannot cross file boundaries. +*)*)*) -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) -loop pool while tRuE or noT faLsE let in case of ESAC''' - tokens, errors = tokenize(test_data) - print(errors) \ No newline at end of file +class Error() { + + (* There was once a comment, + that was quite long. + But, the reader soon discovered that + the comment was indeed longer than + previously assumed. Now, the reader + was in a real dilemma; is the comment + ever gonna end? If I stop reading, will + it end? + He started imagining all sorts of things. + He thought about heisenberg's cat and how + how that relates to the end of the sentence. + He thought to himself "I'm gonna stop reading". + "If I keep reading this comment, I'm gonna know + the fate of this sentence; That will be disastorous." + He knew that such a comment was gonna extend to + another file. It was too awesome to be contained in + a single file. And he would have kept reading too... + if only... + cool wasn't a super-duper-fab-awesomest language; + but cool is that language; + "This comment shall go not cross this file" said cool. + Alas! The reader could read no more. + There was once a comment, + that was quite long. + But, the reader soon discovered that + the comment was indeed longer than + previously assumed. Now, the reader + was in a real dilemma; is the comment + ever gonna end? If I stop reading, will + it end? + He started imagining all sorts of things. + He thought about heisenberg's cat and how + how that relates to the end of the sentence. + He thought to himself "I'm gonna stop reading". + "If I keep reading this comment, I'm gonna know + the fate of this sentence; That will be disastorous." + He knew that such a comment was gonna extend to + another file. It was too awesome to be contained in + a single file. And he would have kept reading too... + if only... + cool wasn't a super-duper-fab-awesomest language; + but cool is that language; + "This comment shall go not cross this file" said cool. + Alas! The reader could read no more.''' + test_data2 = '''(*(*sdfaerlkj + asdf*)''' + tok, errors = tokenize(test_data) + print(errors) + print(tok) + \ No newline at end of file From 23ac924356e4da9efe573a3f33495b1d5a92f4eb Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 24 Feb 2020 18:10:54 -0500 Subject: [PATCH 014/520] Change lexer to be a type and add states to it for comments --- src/Files_from_TypeInferencer/core/cmp/lex.py | 463 ++++++++++-------- src/Files_from_TypeInferencer/main.py | 73 +-- 2 files changed, 282 insertions(+), 254 deletions(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index c2c4bc35..d747efe3 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -3,198 +3,275 @@ from .utils import Token from .CoolUtils import * -__lexer__ = None -__text__ = None -__errors__ = None - -reserved = { - "class": "CLASS", - "else": "ELSE", - "fi": "FI", - "if": "IF", - "in": "IN", - "inherits": "INHERITS", - "isvoid": "ISVOID", - "let": "LET", - "loop": "LOOP", - "pool": "POOL", - "then": "THEN", - "while": "WHILE", - "case": "CASE", - "esac": "ESAC", - "new": "NEW", - "of": "OF", - "not": "NOT", - "true": "TRUE", - "false": "FALSE", -} - -tokenType = { - "CLASS": classx, - "ELSE": elsex, - "FI": fi, - "IF": ifx, - "IN": inx, - "INHERITS": inherits, - "ISVOID": isvoid, - "LET": let, - "LOOP": loop, - "POOL": pool, - "THEN": then, - "WHILE": whilex, - "CASE": case, - "ESAC": esac, - "NEW": new, - "OF": of, - "NOT": notx, - "OBJECTIDENTIFIER": idx, - "TYPEIDENTIFIER": typex, - "LCBRA": ocur, - "RCBRA": ccur, - "LPAREN": opar, - "RPAREN": cpar, - "COLON": colon, - "SEMICOLON": semi, - "NUMBER": integer, - "eof": eof, - "PLUS": plus, - "MINUS": minus, - "DIVIDE": div, - "TIMES": star, - "LESS": less, - "LESSEQ": leq, - "EQUALS": equal, - "TRUE": boolx, - "FALSE": boolx, - "COMPLEMENT": compl, - "RARROW": rarrow, - "LARROW": larrow, - "COMMA": comma, - "DOT": dot, - "AT": at, -} - - -tokens = [ - 'NUMBER', - 'TYPEIDENTIFIER', - 'OBJECTIDENTIFIER', - 'EQUALS', - 'PLUS', - 'MINUS', - 'TIMES', - 'DIVIDE', - 'LPAREN', - 'RPAREN', - 'STRING', - 'LESS', - 'LESSEQ', - 'LCBRA', - 'RCBRA', - 'COLON', - 'SEMICOLON', - 'COMPLEMENT', - 'RARROW', - 'LARROW', - 'COMMA', - 'DOT', - 'AT' +class CoolLexer: + + states = ( + ('comments', 'inclusive'), + ) + + reserved = { + "class": "CLASS", + "else": "ELSE", + "fi": "FI", + "if": "IF", + "in": "IN", + "inherits": "INHERITS", + "isvoid": "ISVOID", + "let": "LET", + "loop": "LOOP", + "pool": "POOL", + "then": "THEN", + "while": "WHILE", + "case": "CASE", + "esac": "ESAC", + "new": "NEW", + "of": "OF", + "not": "NOT", + "true": "TRUE", + "false": "FALSE", + } + + tokenType = { + "CLASS": classx, + "ELSE": elsex, + "FI": fi, + "IF": ifx, + "IN": inx, + "INHERITS": inherits, + "ISVOID": isvoid, + "LET": let, + "LOOP": loop, + "POOL": pool, + "THEN": then, + "WHILE": whilex, + "CASE": case, + "ESAC": esac, + "NEW": new, + "OF": of, + "NOT": notx, + "OBJECTIDENTIFIER": idx, + "TYPEIDENTIFIER": typex, + "LCBRA": ocur, + "RCBRA": ccur, + "LPAREN": opar, + "RPAREN": cpar, + "COLON": colon, + "SEMICOLON": semi, + "NUMBER": integer, + "eof": eof, + "PLUS": plus, + "MINUS": minus, + "DIVIDE": div, + "TIMES": star, + "LESS": less, + "LESSEQ": leq, + "EQUALS": equal, + "TRUE": boolx, + "FALSE": boolx, + "COMPLEMENT": compl, + "RARROW": rarrow, + "LARROW": larrow, + "COMMA": comma, + "DOT": dot, + "AT": at, + } + + + tokens = [ + 'NUMBER', + 'TYPEIDENTIFIER', + 'OBJECTIDENTIFIER', + 'EQUALS', + 'PLUS', + 'MINUS', + 'TIMES', + 'DIVIDE', + 'LPAREN', + 'RPAREN', + 'STRING', + 'LESS', + 'LESSEQ', + 'LCBRA', + 'RCBRA', + 'COLON', + 'SEMICOLON', + 'COMPLEMENT', + 'RARROW', + 'LARROW', + 'COMMA', + 'DOT', + 'AT' + + ] + list(reserved.values()) + + t_EQUALS = r'=' + t_PLUS = r'\+' + t_MINUS = r'-' + t_TIMES = r'\*' + t_DIVIDE = r'/' + t_LPAREN = r'\(' + t_RPAREN = r'\)' + t_LESS = r'<' + t_LESSEQ = r'<=' + t_LCBRA = r'{' + t_RCBRA = r'}' + t_COLON = r':' + t_SEMICOLON = r';' + t_COMPLEMENT = r'~' + t_RARROW = r'=>' + t_LARROW = r'<-' + t_COMMA = r',' + t_DOT = r'\.' + t_AT = r'@' + + t_ignore = ' \t\f\r\t\v' + + def build(self, **kwargs): + self.lexer = lex.lex(module=self, **kwargs) + self.lexer.eof= (1,1) + self.lexer.comment_level = 0 + self.errors = [] + + # def t_comments_EQUALS(self, t): + # r'=' + + # def t_comments_PLUS(self, t): + # r'\+' + + # def t_comments_MINUS(self, t): + # r'-' + + # def t_comments_TIMES(self, t): + # r'\*' + + # def t_comments_DIVIDE(self, t): + # r'/' + + # def t_comments_LPAREN(self, t): + # r'\(' + + # def t_comments_RPAREN(self, t): + # r'\)' + + # def t_comments_LESS(self, t): + # r'<' + + # def t_comments_LESSEQ(self, t): + # r'<=' + + # def t_comments_LCBRA(self, t): + # r'{' + + # def t_comments_RCBRA(self, t): + # r'}' + + # def t_comments_COLON(self, t): + # r':' + + # def t_comments_SEMICOLON(self, t): + # r';' + + # def t_comments_COMPLEMENT(self, t): + # r'~' + + # def t_comments_RARROW(self, t): + # r'=>' + + # def t_comments_LARROW(self, t): + # r'<-' + + # def t_comments_COMMA(self, t): + # r',' + + # def t_comments_DOT(self, t): + # r'\.' + + # def t_comments_AT(self, t): + # r'@' + + # def t_comments_TYPEIDENTIFIER(self, t): + # r'[A-Z][a-zA-Z0-9|_]*' + + # def t_comments_OBJECTIDENTIFIER(self, t): + # r'[a-z][a-zA-Z0-9|_]*' + + # def t_comments_anything(self, t): + # r'.*' + + def t_TYPEIDENTIFIER(self, t): + r'[A-Z][a-zA-Z0-9|_]*' + l_value = t.value.lower() + if l_value == "false" or l_value == "true": + if t.value[0] != "f" and t.value[0] != 't': + return t + t.type = CoolLexer.reserved.get(l_value, "TYPEIDENTIFIER") + return t + + def t_OBJECTIDENTIFIER(self,t): + r'[a-z][a-zA-Z0-9|_]*' + l_value = t.value.lower() + if l_value == "false" or l_value == "true": + if t.value[0] != "f" and t.value[0] != 't': + return t + t.type = CoolLexer.reserved.get(l_value, "OBJECTIDENTIFIER") + return t + + def t_NUMBER(self, t): + r'\d+' + t.value = int(t.value) + return t + + def t_newline(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + + def t_LINECOMMENT(self, t): + r'--.*' + + def t_COMMENTIN(self, t): + r'\(\*' + self.lexer.comment_level = 1 + self.lexer.begin('comments') + + def t_comments_COMMENTIN(self, t): + r'\(\*' + self.lexer.comment_level += 1 + + def t_comments_COMMENTOUT(self, t): + r'\*\)' + if self.lexer.comment_level == 1: + self.lexer.begin('INITIAL') + self.lexer.comment_level -= 1 + + + def t_eof(self, t): + t.lexer.eof =(t.lexer.lineno, self.compute_column(t)) + return None + + def t_comments_eof(self, t): + line = t.lexer.lineno + column = self.compute_column(t) + self.errors.append(f"({line},{column}) - LexicographicError: EOF in comment") + + def compute_column(self, token): + line_start = self.text.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + + def t_error(self, t): + line = t.lexer.lineno + column = self.compute_column(t) + error_text = t.value[0] + self.errors.append(f"({line},{column}) - LexicographicError: \"{error_text}\"") + t.lexer.skip(1) -] + list(reserved.values()) - -t_EQUALS = r'=' -t_PLUS = r'\+' -t_MINUS = r'-' -t_TIMES = r'\*' -t_DIVIDE = r'/' -t_LPAREN = r'\(' -t_RPAREN = r'\)' -t_LESS = r'<' -t_LESSEQ = r'<=' -t_LCBRA = r'{' -t_RCBRA = r'}' -t_COLON = r':' -t_SEMICOLON = r';' -t_COMPLEMENT = r'~' -t_RARROW = r'=>' -t_LARROW = r'<-' -t_COMMA = r',' -t_DOT = r'\.' -t_AT = r'@' - -t_ignore = ' \t\f\r\t\v' - - - -def t_TYPEIDENTIFIER(t): - r'[A-Z][a-zA-Z0-9|_]*' - l_value = t.value.lower() - if l_value == "false" or l_value == "true": - if t.value[0] != "f" and t.value[0] != 't': - return t - t.type = reserved.get(l_value, "TYPEIDENTIFIER") - return t - -def t_OBJECTIDENTIFIER(t): - r'[a-z][a-zA-Z0-9|_]*' - l_value = t.value.lower() - if l_value == "false" or l_value == "true": - if t.value[0] != "f" and t.value[0] != 't': - return t - t.type = reserved.get(l_value, "OBJECTIDENTIFIER") - return t - -def t_NUMBER(t): - r'\d+' - t.value = int(t.value) - return t - -def t_newline(t): - r'\n+' - t.lexer.lineno += len(t.value) - -def t_LINECOMMENT(t): - r'--.*\n' -def t_COMMENT(t): - r'\(\*(.|\n)*\*\)' - -def t_eof(t): - print("HERE") - print(t.lexer.lineno) - t.lexer.eof =(t.lexer.lineno, compute_column(t)) - return None - -def t_ERROREOFCOMMENTS(t): - r'\(\*(.|\n)*$' - global __errors__ - line = t.lexer.lineno - column = compute_column(t) - __errors__.append(f"({t.lexer.eof[0]},{t.lexer.eof[1]}) - LexicographicError: EOF IN COMMENTS") + def t_comments_error(self, t): + t.lexer.skip(1) -def compute_column(token): - line_start = __text__.rfind('\n', 0, token.lexpos) + 1 - return (token.lexpos - line_start) + 1 - -def t_error(t): - global __errors__ - line = t.lexer.lineno - column = compute_column(t) - error_text = t.value[0] - __errors__.append(f"({line},{column}) - LexicographicError: \"{error_text}\"") - t.lexer.skip(1) - - -def tokenize(text): - global __text__ - global __lexer__ - global __errors__ - __errors__ = [] - __text__ = text - if __lexer__ is None: - __lexer__ = lex.lex() - __lexer__.eof = (1,1) - __lexer__.input(text) - original_tokens = [token for token in __lexer__] - tokens = [ Token(token.value, tokenType[token.type]) for token in original_tokens] - EOF = Token('$', eof) - return (tokens + [EOF], __errors__) \ No newline at end of file + def tokenize(self, text): + self.errors = [] + self.text = text + self.lexer.input(text) + original_tokens = [token for token in self.lexer] + tokens = [ Token(token.value, self.tokenType[token.type]) for token in original_tokens] + EOF = Token('$', eof) + return (tokens + [EOF], self.errors) \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/main.py b/src/Files_from_TypeInferencer/main.py index d279ab7f..b73d99df 100644 --- a/src/Files_from_TypeInferencer/main.py +++ b/src/Files_from_TypeInferencer/main.py @@ -3,7 +3,9 @@ from time import sleep from core.cmp.visitors import * from core.cmp.evaluation import * -from core.cmp.lex import tokenize +from core.cmp.lex import CoolLexer +import sys +from os.path import basename def build_AST(G, text): @@ -111,64 +113,13 @@ def main(): if __name__ == '__main__': # main() - test_data = '''--Any characters between two dashes “--” and the next newline ---(or EOF, if there is no next newline) are treated as comments - -(*(*(* -Comments may also be written by enclosing -text in (∗ . . . ∗). The latter form of comment may be nested. -Comments cannot cross file boundaries. -*)*)*) - -class Error() { - - (* There was once a comment, - that was quite long. - But, the reader soon discovered that - the comment was indeed longer than - previously assumed. Now, the reader - was in a real dilemma; is the comment - ever gonna end? If I stop reading, will - it end? - He started imagining all sorts of things. - He thought about heisenberg's cat and how - how that relates to the end of the sentence. - He thought to himself "I'm gonna stop reading". - "If I keep reading this comment, I'm gonna know - the fate of this sentence; That will be disastorous." - He knew that such a comment was gonna extend to - another file. It was too awesome to be contained in - a single file. And he would have kept reading too... - if only... - cool wasn't a super-duper-fab-awesomest language; - but cool is that language; - "This comment shall go not cross this file" said cool. - Alas! The reader could read no more. - There was once a comment, - that was quite long. - But, the reader soon discovered that - the comment was indeed longer than - previously assumed. Now, the reader - was in a real dilemma; is the comment - ever gonna end? If I stop reading, will - it end? - He started imagining all sorts of things. - He thought about heisenberg's cat and how - how that relates to the end of the sentence. - He thought to himself "I'm gonna stop reading". - "If I keep reading this comment, I'm gonna know - the fate of this sentence; That will be disastorous." - He knew that such a comment was gonna extend to - another file. It was too awesome to be contained in - a single file. And he would have kept reading too... - if only... - cool wasn't a super-duper-fab-awesomest language; - but cool is that language; - "This comment shall go not cross this file" said cool. - Alas! The reader could read no more.''' - test_data2 = '''(*(*sdfaerlkj - asdf*)''' - tok, errors = tokenize(test_data) + input_file = sys.argv[1] + text = "" + with open(input_file, 'r') as f: + text = f.read() + lex = CoolLexer() + lex.build() + toks, errors = lex.tokenize(text) + print(f"TEST {basename(input_file)}") print(errors) - print(tok) - \ No newline at end of file + \ No newline at end of file From cdfa68ffeffe8bc2e7620cdf86d93b4ddec2dd1d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 24 Feb 2020 21:06:08 -0500 Subject: [PATCH 015/520] Add strings state to the lexer for processing strings --- src/Files_from_TypeInferencer/core/cmp/lex.py | 90 +++++++++++++++---- src/Files_from_TypeInferencer/main.py | 2 + 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index d747efe3..6c0e281a 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -6,7 +6,9 @@ class CoolLexer: states = ( - ('comments', 'inclusive'), + ('comments', 'exclusive'), + ('strings', 'exclusive'), + ) reserved = { @@ -74,6 +76,7 @@ class CoolLexer: "COMMA": comma, "DOT": dot, "AT": at, + "STRING": string, } @@ -100,7 +103,8 @@ class CoolLexer: 'LARROW', 'COMMA', 'DOT', - 'AT' + 'AT', + ] + list(reserved.values()) @@ -125,12 +129,22 @@ class CoolLexer: t_AT = r'@' t_ignore = ' \t\f\r\t\v' + t_comments_ignore = '' def build(self, **kwargs): self.lexer = lex.lex(module=self, **kwargs) self.lexer.eof= (1,1) - self.lexer.comment_level = 0 + self.comment_level = 0 self.errors = [] + self.string = "" + + def t_comments_COMMENTOUT(self, t): + r'\*\)' + if self.comment_level == 0: + self.lexer.begin('INITIAL') + else: + self.comment_level -= 1 + # def t_comments_EQUALS(self, t): # r'=' @@ -195,9 +209,61 @@ def build(self, **kwargs): # def t_comments_OBJECTIDENTIFIER(self, t): # r'[a-z][a-zA-Z0-9|_]*' - # def t_comments_anything(self, t): - # r'.*' - + def t_STRINGIN(self, t): + r'"' + self.string = "" + t.lexer.begin('strings') + + def t_strings_NULL(self, t): + r'\0' + line = t.lexer.lineno + column = self.compute_column(t) + self.errors.append(f"({line},{column}) - LexicographicError: Null caracter in string") + + def t_strings_newline(self, t): + r'\\\n' + t.lexer.lineno+=1 + self.string += '\n' + + def t_strings_invalid_new_line(self, t): + r'\n' + line = t.lexer.lineno + t.lexer.lineno+=1 + column = self.compute_column(t) + self.errors.append(f"({line},{column}) - LexicographicError: Unterminated string constant") + t.lexer.begin("INITIAL") + + # def t_string + + def t_strings_escaped_special_character(self, t): + r'\\(b|t|f)' + self.string+= t.value + + def t_strings_escaped_character(self, t): + r'\\.' + self.string+= t.value[1] + + def t_strings_STRINGOUT(self, t): + r'"' + t.lexer.begin('INITIAL') + t.type = 'STRING' + t.value = self.string + return t + + def t_strings_character(self, t): + r'.' + self.string += t.value + + def t_strings_eof(self, t): + line = t.lexer.lineno + column = self.compute_column(t) + self.errors.append(f"({line},{column}) - LexicographicError: EOF in string constant") + + + + + + def t_TYPEIDENTIFIER(self, t): r'[A-Z][a-zA-Z0-9|_]*' l_value = t.value.lower() @@ -221,7 +287,7 @@ def t_NUMBER(self, t): t.value = int(t.value) return t - def t_newline(self, t): + def t_ANY_newline(self, t): r'\n+' t.lexer.lineno += len(t.value) @@ -230,20 +296,12 @@ def t_LINECOMMENT(self, t): def t_COMMENTIN(self, t): r'\(\*' - self.lexer.comment_level = 1 self.lexer.begin('comments') def t_comments_COMMENTIN(self, t): r'\(\*' - self.lexer.comment_level += 1 - - def t_comments_COMMENTOUT(self, t): - r'\*\)' - if self.lexer.comment_level == 1: - self.lexer.begin('INITIAL') - self.lexer.comment_level -= 1 + self.comment_level += 1 - def t_eof(self, t): t.lexer.eof =(t.lexer.lineno, self.compute_column(t)) return None diff --git a/src/Files_from_TypeInferencer/main.py b/src/Files_from_TypeInferencer/main.py index b73d99df..e206c782 100644 --- a/src/Files_from_TypeInferencer/main.py +++ b/src/Files_from_TypeInferencer/main.py @@ -122,4 +122,6 @@ def main(): toks, errors = lex.tokenize(text) print(f"TEST {basename(input_file)}") print(errors) + for token in toks: + print(token) \ No newline at end of file From 75f050474a11dbbf57d51ffacb50ddb9340c84d3 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 24 Feb 2020 22:08:50 -0500 Subject: [PATCH 016/520] Add errors as tokens --- src/Files_from_TypeInferencer/core/cmp/lex.py | 98 +++++-------------- src/Files_from_TypeInferencer/main.py | 6 +- 2 files changed, 27 insertions(+), 77 deletions(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index 6c0e281a..cb6f51bf 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -104,6 +104,7 @@ class CoolLexer: 'COMMA', 'DOT', 'AT', + 'ERROR' ] + list(reserved.values()) @@ -135,7 +136,6 @@ def build(self, **kwargs): self.lexer = lex.lex(module=self, **kwargs) self.lexer.eof= (1,1) self.comment_level = 0 - self.errors = [] self.string = "" def t_comments_COMMENTOUT(self, t): @@ -145,70 +145,6 @@ def t_comments_COMMENTOUT(self, t): else: self.comment_level -= 1 - - # def t_comments_EQUALS(self, t): - # r'=' - - # def t_comments_PLUS(self, t): - # r'\+' - - # def t_comments_MINUS(self, t): - # r'-' - - # def t_comments_TIMES(self, t): - # r'\*' - - # def t_comments_DIVIDE(self, t): - # r'/' - - # def t_comments_LPAREN(self, t): - # r'\(' - - # def t_comments_RPAREN(self, t): - # r'\)' - - # def t_comments_LESS(self, t): - # r'<' - - # def t_comments_LESSEQ(self, t): - # r'<=' - - # def t_comments_LCBRA(self, t): - # r'{' - - # def t_comments_RCBRA(self, t): - # r'}' - - # def t_comments_COLON(self, t): - # r':' - - # def t_comments_SEMICOLON(self, t): - # r';' - - # def t_comments_COMPLEMENT(self, t): - # r'~' - - # def t_comments_RARROW(self, t): - # r'=>' - - # def t_comments_LARROW(self, t): - # r'<-' - - # def t_comments_COMMA(self, t): - # r',' - - # def t_comments_DOT(self, t): - # r'\.' - - # def t_comments_AT(self, t): - # r'@' - - # def t_comments_TYPEIDENTIFIER(self, t): - # r'[A-Z][a-zA-Z0-9|_]*' - - # def t_comments_OBJECTIDENTIFIER(self, t): - # r'[a-z][a-zA-Z0-9|_]*' - def t_STRINGIN(self, t): r'"' self.string = "" @@ -218,7 +154,10 @@ def t_strings_NULL(self, t): r'\0' line = t.lexer.lineno column = self.compute_column(t) - self.errors.append(f"({line},{column}) - LexicographicError: Null caracter in string") + t.type = "ERROR" + t.value = f"({line},{column}) - LexicographicError: Null caracter in string" + return t + def t_strings_newline(self, t): r'\\\n' @@ -230,10 +169,12 @@ def t_strings_invalid_new_line(self, t): line = t.lexer.lineno t.lexer.lineno+=1 column = self.compute_column(t) - self.errors.append(f"({line},{column}) - LexicographicError: Unterminated string constant") t.lexer.begin("INITIAL") + t.type = "ERROR" + t.value = f"({line},{column}) - LexicographicError: Unterminated string constant" + return t + - # def t_string def t_strings_escaped_special_character(self, t): r'\\(b|t|f)' @@ -257,7 +198,9 @@ def t_strings_character(self, t): def t_strings_eof(self, t): line = t.lexer.lineno column = self.compute_column(t) - self.errors.append(f"({line},{column}) - LexicographicError: EOF in string constant") + t.type = "ERROR" + t.value = f"({line},{column}) - LexicographicError: EOF in string constant" + return t @@ -309,7 +252,8 @@ def t_eof(self, t): def t_comments_eof(self, t): line = t.lexer.lineno column = self.compute_column(t) - self.errors.append(f"({line},{column}) - LexicographicError: EOF in comment") + t.type = "ERROR" + t.value = f"({line},{column}) - LexicographicError: EOF in comment" def compute_column(self, token): line_start = self.text.rfind('\n', 0, token.lexpos) + 1 @@ -319,17 +263,23 @@ def t_error(self, t): line = t.lexer.lineno column = self.compute_column(t) error_text = t.value[0] - self.errors.append(f"({line},{column}) - LexicographicError: \"{error_text}\"") t.lexer.skip(1) + t.type = "ERROR" + t.value = f"({line},{column}) - LexicographicError: \"{error_text}\"" + return t def t_comments_error(self, t): t.lexer.skip(1) def tokenize(self, text): - self.errors = [] self.text = text self.lexer.input(text) + tokens = [] original_tokens = [token for token in self.lexer] - tokens = [ Token(token.value, self.tokenType[token.type]) for token in original_tokens] + for token in original_tokens: + if token.type == "ERROR": + tokens.append(Token(token.value, "ERROR")) + else: + tokens.append(Token(token.value, self.tokenType[token.type])) EOF = Token('$', eof) - return (tokens + [EOF], self.errors) \ No newline at end of file + return tokens + [EOF] \ No newline at end of file diff --git a/src/Files_from_TypeInferencer/main.py b/src/Files_from_TypeInferencer/main.py index e206c782..44a03b58 100644 --- a/src/Files_from_TypeInferencer/main.py +++ b/src/Files_from_TypeInferencer/main.py @@ -120,8 +120,8 @@ def main(): lex = CoolLexer() lex.build() toks, errors = lex.tokenize(text) - print(f"TEST {basename(input_file)}") - print(errors) + for token in toks: - print(token) + if token.token_type == "ERROR": + print(token.lex) \ No newline at end of file From 70c4d12075b2f642bf67f50223009ac65bf45103 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 24 Feb 2020 22:11:45 -0500 Subject: [PATCH 017/520] [makefile] Some changes Added: - `.DEFAULT_GOAL` set to `main`. Since this variable exists it is possible to use `make` instead of `make main`, and that is the expected behaivior. - Rule `help` - Rule `info`. It shows the project details - rules documentation to display in `make help` --- src/Makefile | 26 ++++++++++++++++++++++++++ src/makefile | 11 ----------- 2 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 src/Makefile delete mode 100644 src/makefile diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..3514ece9 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,26 @@ +.DEFAULT_GOAL := main +.PHONY: clean, info + +ORG_NAME := 2kodevs +PROJECT_NAME := CoolCompiler +APP_VERSION := v0.1 +APP_DESCRIPTION := $(ORG_NAME) - $(PROJECT_NAME)$(APP_VERSION) +DEVELOPERS := Lázaro Raúl Iglesias Vera, Miguel Tenorio Potrony, Mauricio Lázaro Perdomo Cortéz +COPYRIGHT := Copyright © 2020: $(DEVELOPERS) + +main: ## Compiling the compiler :) + # Compiling the compiler :) + +clean: ## Remove temporary files + rm -rf build/* + +test: ## Run testsuit with name TAG + pytest ../tests -v --tb=short -m=${TAG} + +info: ## Display project description + @echo "$(APP_DESCRIPTION)" + @echo "$(COPYRIGHT)" + +help: ## Show this help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + diff --git a/src/makefile b/src/makefile deleted file mode 100644 index 021189d6..00000000 --- a/src/makefile +++ /dev/null @@ -1,11 +0,0 @@ -.PHONY: clean - -main: - # Compiling the compiler :) - -clean: - rm -rf build/* - -test: - pytest ../tests -v --tb=short -m=${TAG} - From f021b7f3e175352bb0792a9673287e946557229d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 24 Feb 2020 22:12:32 -0500 Subject: [PATCH 018/520] [coolc.sh] Initial script body --- src/coolc.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f..a656a456 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,11 +1,11 @@ -# Incluya aquí las instrucciones necesarias para ejecutar su compilador +# Execution details 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 +# Display project descripton here +make info # TODO: Ensure that this rule is executed a single time -# Llamar al compilador +# Compile and Run echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python main.py -f $INPUT_FILE From a4187bd26cc453ffc77f10e9b7e64dcb27c859e8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 24 Feb 2020 22:14:46 -0500 Subject: [PATCH 019/520] [inferencer] Modifying parser to recive the list of tokens instead of its types, and clean errors logs --- src/Files_from_TypeInferencer/core/cmp/utils.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Files_from_TypeInferencer/core/cmp/utils.py b/src/Files_from_TypeInferencer/core/cmp/utils.py index 8b45057f..4e93321c 100644 --- a/src/Files_from_TypeInferencer/core/cmp/utils.py +++ b/src/Files_from_TypeInferencer/core/cmp/utils.py @@ -242,18 +242,12 @@ def __call__(self, w, get_shift_reduce=False): while True: state = stack[-1] - lookahead = w[cursor] + lookahead = w[cursor].token_type if self.verbose: print(stack, w[cursor:]) # Your code here!!! (Detect error) - try: - if state not in self.action or lookahead not in self.action[state]: - return None - except: - print(state) - print(self.action) - print(lookahead) - return None + if state not in self.action or lookahead not in self.action[state]: + return None, (True, w[cursor]) #TODO: Build the correct error using `w[cursor]` action, tag = list(self.action[state][lookahead])[0] # Your code here!!! (Shift case) @@ -270,7 +264,7 @@ def __call__(self, w, get_shift_reduce=False): output.append(tag) # Your code here!!! (OK case) elif action is ShiftReduceParser.OK: - return output if not get_shift_reduce else(output,operations) + return (output if not get_shift_reduce else(output,operations)), (False, None) # Your code here!!! (Invalid case) else: raise ValueError \ No newline at end of file From 1b89d92d9f8749d493cdfa5b8c01f89a1f57c61a Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 24 Feb 2020 22:15:06 -0500 Subject: [PATCH 020/520] [main] Initial pipeline structure --- src/main.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/main.py diff --git a/src/main.py b/src/main.py new file mode 100644 index 00000000..b48748a2 --- /dev/null +++ b/src/main.py @@ -0,0 +1,40 @@ +from Files_from_TypeInferencer.core.cmp.CoolUtils import tokenize_text, CoolParser +from missing_files import lexer + +def main(args): + # Read code + try: + with open(args.file, 'w') as fd: + code = fd.read() + except: + print(f"(0,0) - CompilerError: file {args.file} not found") #TODO: Customize errors + exit(1) + + # Lexer + text = lexer(code) + + # Tokenize + tokens = tokenize_text(text) + + # Parse + parse, (failure, token) = CoolParser([t.token_type for t in tokens]) + if failure: + print(f"(0,0) - SemanticError: Unexpected token {token}") #TODO: Use correct line and column + exit(1) + + # Comming soon pipeline steps + print(parse) + + + exit(0) + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='CoolCompiler pipeline') + parser.add_argument('-f', '--file', type=str, default='code.cl', help='node address') + + args = parser.parse_args() + main(args) + From 8534bddb20ec1ecc54892031843e1d00a09c2f28 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 24 Feb 2020 22:56:34 -0500 Subject: [PATCH 021/520] Fix eof of strings and comments states for avoid endless loop --- src/Files_from_TypeInferencer/core/cmp/lex.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/Files_from_TypeInferencer/core/cmp/lex.py index cb6f51bf..ddce226a 100644 --- a/src/Files_from_TypeInferencer/core/cmp/lex.py +++ b/src/Files_from_TypeInferencer/core/cmp/lex.py @@ -200,6 +200,7 @@ def t_strings_eof(self, t): column = self.compute_column(t) t.type = "ERROR" t.value = f"({line},{column}) - LexicographicError: EOF in string constant" + t.lexer.begin("INITIAL") return t @@ -254,6 +255,8 @@ def t_comments_eof(self, t): column = self.compute_column(t) t.type = "ERROR" t.value = f"({line},{column}) - LexicographicError: EOF in comment" + t.lexer.begin("INITIAL") + return t def compute_column(self, token): line_start = self.text.rfind('\n', 0, token.lexpos) + 1 From 210b315fe53ff047ab6e781508b115032a4298df Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 25 Feb 2020 10:25:47 -0500 Subject: [PATCH 022/520] Restructure project files --- src/{Files_from_TypeInferencer => }/core/cmp/CoolUtils.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/__init__.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/ast.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/automata.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/evaluation.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/functions.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/grammartools.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/languages.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/lex.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/nbpackage.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/pycompiler.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/semantic.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/utils.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/visitor.py | 0 src/{Files_from_TypeInferencer => }/core/cmp/visitors.py | 0 src/main.py | 5 +++-- src/{Files_from_TypeInferencer/main.py => older_main.py} | 0 17 files changed, 3 insertions(+), 2 deletions(-) rename src/{Files_from_TypeInferencer => }/core/cmp/CoolUtils.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/__init__.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/ast.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/automata.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/evaluation.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/functions.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/grammartools.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/languages.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/lex.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/nbpackage.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/pycompiler.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/semantic.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/utils.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/visitor.py (100%) rename src/{Files_from_TypeInferencer => }/core/cmp/visitors.py (100%) rename src/{Files_from_TypeInferencer/main.py => older_main.py} (100%) diff --git a/src/Files_from_TypeInferencer/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/CoolUtils.py rename to src/core/cmp/CoolUtils.py diff --git a/src/Files_from_TypeInferencer/core/cmp/__init__.py b/src/core/cmp/__init__.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/__init__.py rename to src/core/cmp/__init__.py diff --git a/src/Files_from_TypeInferencer/core/cmp/ast.py b/src/core/cmp/ast.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/ast.py rename to src/core/cmp/ast.py diff --git a/src/Files_from_TypeInferencer/core/cmp/automata.py b/src/core/cmp/automata.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/automata.py rename to src/core/cmp/automata.py diff --git a/src/Files_from_TypeInferencer/core/cmp/evaluation.py b/src/core/cmp/evaluation.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/evaluation.py rename to src/core/cmp/evaluation.py diff --git a/src/Files_from_TypeInferencer/core/cmp/functions.py b/src/core/cmp/functions.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/functions.py rename to src/core/cmp/functions.py diff --git a/src/Files_from_TypeInferencer/core/cmp/grammartools.py b/src/core/cmp/grammartools.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/grammartools.py rename to src/core/cmp/grammartools.py diff --git a/src/Files_from_TypeInferencer/core/cmp/languages.py b/src/core/cmp/languages.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/languages.py rename to src/core/cmp/languages.py diff --git a/src/Files_from_TypeInferencer/core/cmp/lex.py b/src/core/cmp/lex.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/lex.py rename to src/core/cmp/lex.py diff --git a/src/Files_from_TypeInferencer/core/cmp/nbpackage.py b/src/core/cmp/nbpackage.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/nbpackage.py rename to src/core/cmp/nbpackage.py diff --git a/src/Files_from_TypeInferencer/core/cmp/pycompiler.py b/src/core/cmp/pycompiler.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/pycompiler.py rename to src/core/cmp/pycompiler.py diff --git a/src/Files_from_TypeInferencer/core/cmp/semantic.py b/src/core/cmp/semantic.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/semantic.py rename to src/core/cmp/semantic.py diff --git a/src/Files_from_TypeInferencer/core/cmp/utils.py b/src/core/cmp/utils.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/utils.py rename to src/core/cmp/utils.py diff --git a/src/Files_from_TypeInferencer/core/cmp/visitor.py b/src/core/cmp/visitor.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/visitor.py rename to src/core/cmp/visitor.py diff --git a/src/Files_from_TypeInferencer/core/cmp/visitors.py b/src/core/cmp/visitors.py similarity index 100% rename from src/Files_from_TypeInferencer/core/cmp/visitors.py rename to src/core/cmp/visitors.py diff --git a/src/main.py b/src/main.py index b48748a2..086c7f30 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,6 @@ -from Files_from_TypeInferencer.core.cmp.CoolUtils import tokenize_text, CoolParser -from missing_files import lexer +from core.cmp.CoolUtils import tokenize_text, CoolParser +from core.cmp.lex import CoolLexer + def main(args): # Read code diff --git a/src/Files_from_TypeInferencer/main.py b/src/older_main.py similarity index 100% rename from src/Files_from_TypeInferencer/main.py rename to src/older_main.py From 69fc76edbfe36e3c1b03f44852f526c415bf210d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 25 Feb 2020 10:42:09 -0500 Subject: [PATCH 023/520] Fix pipeline in main.py --- src/core/cmp/lex.py | 3 +++ src/main.py | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/lex.py b/src/core/cmp/lex.py index ddce226a..13265729 100644 --- a/src/core/cmp/lex.py +++ b/src/core/cmp/lex.py @@ -132,6 +132,9 @@ class CoolLexer: t_ignore = ' \t\f\r\t\v' t_comments_ignore = '' + def __init__(self): + self.build() + def build(self, **kwargs): self.lexer = lex.lex(module=self, **kwargs) self.lexer.eof= (1,1) diff --git a/src/main.py b/src/main.py index 086c7f30..7a3f9b2a 100644 --- a/src/main.py +++ b/src/main.py @@ -5,18 +5,26 @@ def main(args): # Read code try: - with open(args.file, 'w') as fd: + with open(args.file, 'r') as fd: code = fd.read() except: print(f"(0,0) - CompilerError: file {args.file} not found") #TODO: Customize errors exit(1) # Lexer - text = lexer(code) - + lexer = CoolLexer() + # Tokenize - tokens = tokenize_text(text) + tokens = lexer.tokenize(code) + lexer_err = False + for token in tokens: + if token.token_type == "ERROR": + lexer_err = True + print(token.lex) + if lexer_err: + exit(1) + # Parse parse, (failure, token) = CoolParser([t.token_type for t in tokens]) if failure: From e79a0f79bfa6a69d3ffff7e6d530dc829d9a9c52 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 25 Feb 2020 11:23:18 -0500 Subject: [PATCH 024/520] Add row and column to returned tokens --- src/core/cmp/lex.py | 160 ++++++++++++++++++++++++++++++++++++++------ src/main.py | 10 ++- 2 files changed, 147 insertions(+), 23 deletions(-) diff --git a/src/core/cmp/lex.py b/src/core/cmp/lex.py index 13265729..acb025b7 100644 --- a/src/core/cmp/lex.py +++ b/src/core/cmp/lex.py @@ -109,25 +109,25 @@ class CoolLexer: ] + list(reserved.values()) - t_EQUALS = r'=' - t_PLUS = r'\+' - t_MINUS = r'-' - t_TIMES = r'\*' - t_DIVIDE = r'/' - t_LPAREN = r'\(' - t_RPAREN = r'\)' - t_LESS = r'<' - t_LESSEQ = r'<=' - t_LCBRA = r'{' - t_RCBRA = r'}' - t_COLON = r':' - t_SEMICOLON = r';' - t_COMPLEMENT = r'~' - t_RARROW = r'=>' - t_LARROW = r'<-' - t_COMMA = r',' - t_DOT = r'\.' - t_AT = r'@' + # t_EQUALS = r'=' + # t_PLUS = r'\+' + # t_MINUS = r'-' + # t_TIMES = r'\*' + # t_DIVIDE = r'/' + # t_LPAREN = r'\(' + # t_RPAREN = r'\)' + # t_LESS = r'<' + # t_LESSEQ = r'<=' + # t_LCBRA = r'{' + # t_RCBRA = r'}' + # t_COLON = r':' + # t_SEMICOLON = r';' + # t_COMPLEMENT = r'~' + # t_RARROW = r'=>' + # t_LARROW = r'<-' + # t_COMMA = r',' + # t_DOT = r'\.' + # t_AT = r'@' t_ignore = ' \t\f\r\t\v' t_comments_ignore = '' @@ -141,6 +141,7 @@ def build(self, **kwargs): self.comment_level = 0 self.string = "" + def t_comments_COMMENTOUT(self, t): r'\*\)' if self.comment_level == 0: @@ -159,6 +160,7 @@ def t_strings_NULL(self, t): column = self.compute_column(t) t.type = "ERROR" t.value = f"({line},{column}) - LexicographicError: Null caracter in string" + self.add_line_column(t) return t @@ -175,6 +177,7 @@ def t_strings_invalid_new_line(self, t): t.lexer.begin("INITIAL") t.type = "ERROR" t.value = f"({line},{column}) - LexicographicError: Unterminated string constant" + self.add_line_column(t) return t @@ -192,6 +195,7 @@ def t_strings_STRINGOUT(self, t): t.lexer.begin('INITIAL') t.type = 'STRING' t.value = self.string + self.add_line_column(t) return t def t_strings_character(self, t): @@ -204,6 +208,7 @@ def t_strings_eof(self, t): t.type = "ERROR" t.value = f"({line},{column}) - LexicographicError: EOF in string constant" t.lexer.begin("INITIAL") + self.add_line_column(t) return t @@ -216,8 +221,10 @@ def t_TYPEIDENTIFIER(self, t): l_value = t.value.lower() if l_value == "false" or l_value == "true": if t.value[0] != "f" and t.value[0] != 't': + self.add_line_column(t) return t t.type = CoolLexer.reserved.get(l_value, "TYPEIDENTIFIER") + self.add_line_column(t) return t def t_OBJECTIDENTIFIER(self,t): @@ -225,13 +232,16 @@ def t_OBJECTIDENTIFIER(self,t): l_value = t.value.lower() if l_value == "false" or l_value == "true": if t.value[0] != "f" and t.value[0] != 't': + self.add_line_column(t) return t t.type = CoolLexer.reserved.get(l_value, "OBJECTIDENTIFIER") + self.add_line_column(t) return t def t_NUMBER(self, t): r'\d+' t.value = int(t.value) + self.add_line_column(t) return t def t_ANY_newline(self, t): @@ -259,12 +269,113 @@ def t_comments_eof(self, t): t.type = "ERROR" t.value = f"({line},{column}) - LexicographicError: EOF in comment" t.lexer.begin("INITIAL") + self.add_line_column(t) return t def compute_column(self, token): line_start = self.text.rfind('\n', 0, token.lexpos) + 1 return (token.lexpos - line_start) + 1 + + def t_LARROW(self, t): + r'<-' + self.add_line_column(t) + return t + + def t_LESSEQ(self, t): + r'<=' + self.add_line_column(t) + return t + + def t_RARROW(self, t): + r'=>' + self.add_line_column(t) + return t + + def t_EQUALS(self, t): + r'=' + self.add_line_column(t) + return t + + def t_PLUS(self, t): + r'\+' + self.add_line_column(t) + return t + + def t_MINUS(self, t): + r'-' + self.add_line_column(t) + return t + + def t_TIMES(self, t): + r'\*' + self.add_line_column(t) + return t + + def t_DIVIDE(self, t): + r'/' + self.add_line_column(t) + return t + + def t_LPAREN(self, t): + r'\(' + self.add_line_column(t) + return t + + def t_RPAREN(self, t): + r'\)' + self.add_line_column(t) + return t + + + + def t_LESS(self, t): + r'<' + self.add_line_column(t) + return t + + + def t_LCBRA(self, t): + r'{' + self.add_line_column(t) + return t + + def t_RCBRA(self, t): + r'}' + self.add_line_column(t) + return t + + def t_COLON(self, t): + r':' + self.add_line_column(t) + return t + + def t_SEMICOLON(self, t): + r';' + self.add_line_column(t) + return t + + def t_COMPLEMENT(self, t): + r'~' + self.add_line_column(t) + return t + + + + def t_COMMA(self, t): + r',' + self.add_line_column(t) + return t + + def t_DOT(self, t): + r'\.' + self.add_line_column(t) + return t + + def t_AT(self, t): + r'@' + self.add_line_column(t) + return t def t_error(self, t): line = t.lexer.lineno column = self.compute_column(t) @@ -272,11 +383,14 @@ def t_error(self, t): t.lexer.skip(1) t.type = "ERROR" t.value = f"({line},{column}) - LexicographicError: \"{error_text}\"" + self.add_line_column(t) return t + def t_comments_error(self, t): t.lexer.skip(1) + def tokenize(self, text): self.text = text self.lexer.input(text) @@ -287,5 +401,11 @@ def tokenize(self, text): tokens.append(Token(token.value, "ERROR")) else: tokens.append(Token(token.value, self.tokenType[token.type])) + tokens[-1].row = token.row + tokens[-1].column = token.column EOF = Token('$', eof) - return tokens + [EOF] \ No newline at end of file + return tokens + [EOF] + + def add_line_column(self, t): + t.row = t.lexer.lineno + t.column = self.compute_column(t) \ No newline at end of file diff --git a/src/main.py b/src/main.py index 7a3f9b2a..49ca1990 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,6 @@ from core.cmp.CoolUtils import tokenize_text, CoolParser from core.cmp.lex import CoolLexer +from pprint import pprint def main(args): @@ -24,11 +25,14 @@ def main(args): if lexer_err: exit(1) - + pprint(tokens) + # for t in tokens: + # print(t.token_type) # Parse - parse, (failure, token) = CoolParser([t.token_type for t in tokens]) + parse, (failure, token) = CoolParser(tokens) + if failure: - print(f"(0,0) - SemanticError: Unexpected token {token}") #TODO: Use correct line and column + print(f"({token.row},{token.column}) - SemanticError: Unexpected token {token}") #TODO: Use correct line and column exit(1) # Comming soon pipeline steps From 12d3424f7981d61a21c80b06fbb28582a46495da Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 25 Feb 2020 11:37:43 -0500 Subject: [PATCH 025/520] Fix Grammar to add block non-terminal in feature-list --- src/core/cmp/CoolUtils.py | 11 ++++++++--- src/main.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 95ce8ecd..11ebcec7 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -155,7 +155,7 @@ class BoolNode(AtomicNode): class_list, def_class = CoolGrammar.NonTerminals(' ') feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') -expr, member_call, expr_list, let_list, case_list = CoolGrammar.NonTerminals(' ') +expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') truth_expr, comp_expr = CoolGrammar.NonTerminals(' ') arith, term, factor, factor_2, factor_3 = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') @@ -213,8 +213,13 @@ class BoolNode(AtomicNode): expr %= truth_expr, lambda h, s: s[1] # -expr_list %= expr + semi, lambda h, s: [s[1]] -expr_list %= expr + semi + expr_list, lambda h, s: [s[1]] + s[3] +# expr_list %= expr + semi, lambda h, s: [s[1]] +# expr_list %= expr + semi + expr_list, lambda h, s: [s[1]] + s[3] +expr_list %= expr, lambda h, s: [s[1]] +expr_list %= ocur + block + ccur, lambda h, s: s[2] + +block %= expr + semi, lambda h, s: [s[1]] +block %= expr + semi + block, lambda h, s: [s[1]] + s[3] # let_list %= idx + colon + typex, lambda h, s: [LetAttributeNode(s[1], s[3])] diff --git a/src/main.py b/src/main.py index 49ca1990..4e9d59e3 100644 --- a/src/main.py +++ b/src/main.py @@ -32,7 +32,7 @@ def main(args): parse, (failure, token) = CoolParser(tokens) if failure: - print(f"({token.row},{token.column}) - SemanticError: Unexpected token {token}") #TODO: Use correct line and column + print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token}") #TODO: Use correct line and column exit(1) # Comming soon pipeline steps From 0d2574cffc7a22026b84890b477aa509ec4e837c Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 25 Feb 2020 11:46:24 -0500 Subject: [PATCH 026/520] Add ply module to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) 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 From c808a5a302f0cd1ed9f0fe45974b3a2438f5831e Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 25 Feb 2020 22:42:41 -0500 Subject: [PATCH 027/520] Fix grammar to allow cadenced function calls --- src/core/cmp/CoolUtils.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 11ebcec7..976f7fc8 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -157,7 +157,7 @@ class BoolNode(AtomicNode): param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') truth_expr, comp_expr = CoolGrammar.NonTerminals(' ') -arith, term, factor, factor_2, factor_3 = CoolGrammar.NonTerminals(' ') +arith, term, factor, factor_2, factor_3, invocation = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') # terminals @@ -204,17 +204,15 @@ class BoolNode(AtomicNode): # expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) -expr %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) +expr %= whilex + expr + loop + expr_list + pool, lambda h, s: WhileLoopNode(s[2], s[4]) expr %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2]) -expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +expr %= let + let_list + inx + expr_list, lambda h, s: LetInNode(s[2], s[4]) expr %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) expr %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) expr %= truth_expr, lambda h, s: s[1] # -# expr_list %= expr + semi, lambda h, s: [s[1]] -# expr_list %= expr + semi + expr_list, lambda h, s: [s[1]] + s[3] expr_list %= expr, lambda h, s: [s[1]] expr_list %= ocur + block + ccur, lambda h, s: s[2] @@ -262,6 +260,11 @@ class BoolNode(AtomicNode): # factor_3 %= atom, lambda h, s: s[1] factor_3 %= atom + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) +factor_3 %= atom + func_call + invocation, lambda h, s: FunctionCallNode(s[1], *s[2]) + +# +invocation %= func_call + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) +invocation %= func_call + invocation, lambda h, s: FunctionCallNode(s[1], *s[2]) # func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) From 19527e306d38bb3ed1e991ad02a41aaa2c88a6c4 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 26 Feb 2020 10:18:33 -0500 Subject: [PATCH 028/520] Fix attributes of `factor_3` and `invocation` rules of Grammar --- src/core/cmp/CoolUtils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 976f7fc8..8e29b011 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -260,11 +260,11 @@ class BoolNode(AtomicNode): # factor_3 %= atom, lambda h, s: s[1] factor_3 %= atom + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) -factor_3 %= atom + func_call + invocation, lambda h, s: FunctionCallNode(s[1], *s[2]) +factor_3 %= atom + func_call + invocation, lambda h, s: s[3], None, None, lambda h, s: FunctionCallNode(s[1], *s[2]) # -invocation %= func_call + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) -invocation %= func_call + invocation, lambda h, s: FunctionCallNode(s[1], *s[2]) +invocation %= func_call, lambda h, s: s[1] +invocation %= func_call + invocation, lambda h, s: s[2], None, lambda h, s: FunctionCallNode(h[0], *s[1]) # func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) From d52b96533903738dac9b2fec244ffcf56b554536 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 26 Feb 2020 10:22:21 -0500 Subject: [PATCH 029/520] [makefile] - Comment `make info` rule and "Compiling" echo make info adds two lines to stdout that cause malfunction in test checker --- src/coolc.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coolc.sh b/src/coolc.sh index a656a456..2e3ec93d 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -4,8 +4,10 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips # Display project descripton here -make info # TODO: Ensure that this rule is executed a single time +#make info # TODO: Ensure that this rule is executed a single time +echo "2kodevs - CoolCompilerv0.1" +echo "Copyright © 2020: Lázaro Raúl Iglesias Vera, Miguel Tenorio Potrony, Mauricio Lázaro Perdomo Cortéz" # Compile and Run -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +#echo "Compiling $INPUT_FILE into $OUTPUT_FILE" python main.py -f $INPUT_FILE From e1e127451dc4a7d43862d36d3858fba35ac6a80a Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 26 Feb 2020 10:27:22 -0500 Subject: [PATCH 030/520] [main] - Remove unnecessary prints --- src/main.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main.py b/src/main.py index 4e9d59e3..f6d4a0c7 100644 --- a/src/main.py +++ b/src/main.py @@ -1,3 +1,4 @@ +from sys import exit from core.cmp.CoolUtils import tokenize_text, CoolParser from core.cmp.lex import CoolLexer from pprint import pprint @@ -21,13 +22,10 @@ def main(args): for token in tokens: if token.token_type == "ERROR": lexer_err = True - print(token.lex) if lexer_err: exit(1) - pprint(tokens) - # for t in tokens: - # print(t.token_type) + # Parse parse, (failure, token) = CoolParser(tokens) @@ -36,7 +34,7 @@ def main(args): exit(1) # Comming soon pipeline steps - print(parse) + #print(parse) exit(0) From 09194d2f09e52e21c74a64edc65e84046590b050 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 26 Feb 2020 11:22:45 -0500 Subject: [PATCH 031/520] [main] - Raise error if program is empty --- src/main.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.py b/src/main.py index f6d4a0c7..df0b2fbf 100644 --- a/src/main.py +++ b/src/main.py @@ -18,6 +18,11 @@ def main(args): # Tokenize tokens = lexer.tokenize(code) + + if len(tokens) == 1 and tokens[0].lex == '$': + print("(0, 0) - SyntacticError: Unexpected token EOF") + exit(1) + lexer_err = False for token in tokens: if token.token_type == "ERROR": From 67e3ab0ae8a7e0d2dd0bb2753748ebad49b89f97 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 26 Feb 2020 11:27:55 -0500 Subject: [PATCH 032/520] [Main] - Print lexer errors --- src/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.py b/src/main.py index df0b2fbf..9b8a8ac0 100644 --- a/src/main.py +++ b/src/main.py @@ -27,6 +27,7 @@ def main(args): for token in tokens: if token.token_type == "ERROR": lexer_err = True + print(token.lex) if lexer_err: exit(1) From f768dff973838307a5f25bd90c91dc47ff4c33a2 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 26 Feb 2020 11:31:31 -0500 Subject: [PATCH 033/520] Fix Grammar `expr` Changes: - Replace `comp_expr` for `expr` - Remove `expr-list` - Move productions of `expr` to `atom` --- src/core/cmp/CoolUtils.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 8e29b011..d90268c2 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -191,8 +191,8 @@ class BoolNode(AtomicNode): feature %= idx + colon + typex + larrow + expr + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]) # -feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr_list + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) -feature %= idx + opar + cpar + colon + typex + ocur + expr_list + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], s[5], s[7]) +feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) +feature %= idx + opar + cpar + colon + typex + ocur + expr + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], s[5], s[7]) # param_list %= param, lambda h, s: [s[1]] @@ -201,21 +201,13 @@ class BoolNode(AtomicNode): # param %= idx + colon + typex, lambda h, s: (s[1], s[3]) +#TODO: Check precedence # -expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) -expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) -expr %= whilex + expr + loop + expr_list + pool, lambda h, s: WhileLoopNode(s[2], s[4]) -expr %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2]) -expr %= let + let_list + inx + expr_list, lambda h, s: LetInNode(s[2], s[4]) -expr %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) -expr %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) -expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +expr %= expr + leq + truth_expr, lambda h, s: LessEqualNode(s[1], s[3]) +expr %= expr + less + truth_expr, lambda h, s: LessNode(s[1], s[3]) +expr %= expr + equal + truth_expr, lambda h, s: EqualNode(s[1], s[3]) expr %= truth_expr, lambda h, s: s[1] -# -expr_list %= expr, lambda h, s: [s[1]] -expr_list %= ocur + block + ccur, lambda h, s: s[2] - block %= expr + semi, lambda h, s: [s[1]] block %= expr + semi + block, lambda h, s: [s[1]] + s[3] @@ -231,13 +223,10 @@ class BoolNode(AtomicNode): # truth_expr %= notx + truth_expr, lambda h, s: NotNode(s[2]) -truth_expr %= comp_expr, lambda h, s: s[1] +truth_expr %= arith, lambda h, s: s[1] # -comp_expr %= comp_expr + leq + arith, lambda h, s: LessEqualNode(s[1], s[3]) -comp_expr %= comp_expr + less + arith, lambda h, s: LessNode(s[1], s[3]) -comp_expr %= comp_expr + equal + arith, lambda h, s: EqualNode(s[1], s[3]) -comp_expr %= arith, lambda h, s: s[1] + # arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3]) @@ -284,6 +273,15 @@ class BoolNode(AtomicNode): atom %= integer, lambda h, s: IntegerNode(s[1]) atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) +atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) +atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) +atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) +atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) +atom %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) +atom %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) +atom %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) + # member_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3]) From 94e885dbfb93581e72ad622db13ea38469ec6cab Mon Sep 17 00:00:00 2001 From: Lazaro Raul <47638426+stdevRulo@users.noreply.github.com> Date: Sat, 29 Feb 2020 07:10:11 -0500 Subject: [PATCH 034/520] Change missing groups --- doc/Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index b35a5682..2840ea1c 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -5,8 +5,8 @@ **Nombre** | **Grupo** | **Github** --|--|-- Lázaro Raúl Iglesias Vera | C412 | [@github_user](https://github.com/stdevRulo) -Miguel Tenorio Potrony | C4xx | [@github_user](https://github.com/stdevAntiD2ta) -Mauricio Lázaro Perdomo Cortés | C4xx | [@github_user](https://github.com/stdevMauricio1802) +Miguel Tenorio Potrony | C412 | [@github_user](https://github.com/stdevAntiD2ta) +Mauricio Lázaro Perdomo Cortés | C412 | [@github_user](https://github.com/stdevMauricio1802) ## Readme From 74b323d436d2bd42cf98fb538c6750adcd60409c Mon Sep 17 00:00:00 2001 From: Lazaro Raul <47638426+stdevRulo@users.noreply.github.com> Date: Sat, 29 Feb 2020 07:58:56 -0500 Subject: [PATCH 035/520] [doc] Change github handlers --- doc/Readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index 2840ea1c..a0450f53 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -4,9 +4,9 @@ **Nombre** | **Grupo** | **Github** --|--|-- -Lázaro Raúl Iglesias Vera | C412 | [@github_user](https://github.com/stdevRulo) -Miguel Tenorio Potrony | C412 | [@github_user](https://github.com/stdevAntiD2ta) -Mauricio Lázaro Perdomo Cortés | C412 | [@github_user](https://github.com/stdevMauricio1802) +Lázaro Raúl Iglesias Vera | C412 | [@stdevRulo](https://github.com/stdevRulo) +Miguel Tenorio Potrony | C412 | [@stdevAntiD2ta](https://github.com/stdevAntiD2ta) +Mauricio Lázaro Perdomo Cortés | C412 | [@stdevMauricio1802](https://github.com/stdevMauricio1802) ## Readme From 5ac039b4cf34fee77ff59030573437905d118d28 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 18 Apr 2020 16:20:26 -0400 Subject: [PATCH 036/520] [grammar] - Fix precedence error in grammar related with `not` operator --- src/core/cmp/CoolUtils.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index d90268c2..fb18d013 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -156,7 +156,6 @@ class BoolNode(AtomicNode): feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') -truth_expr, comp_expr = CoolGrammar.NonTerminals(' ') arith, term, factor, factor_2, factor_3, invocation = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') @@ -203,10 +202,11 @@ class BoolNode(AtomicNode): #TODO: Check precedence # -expr %= expr + leq + truth_expr, lambda h, s: LessEqualNode(s[1], s[3]) -expr %= expr + less + truth_expr, lambda h, s: LessNode(s[1], s[3]) -expr %= expr + equal + truth_expr, lambda h, s: EqualNode(s[1], s[3]) -expr %= truth_expr, lambda h, s: s[1] +expr %= notx + expr, lambda h, s: NotNode(s[2]) +expr %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3]) +expr %= expr + less + expr, lambda h, s: LessNode(s[1], s[3]) +expr %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3]) +expr %= arith, lambda h, s: s[1] block %= expr + semi, lambda h, s: [s[1]] block %= expr + semi + block, lambda h, s: [s[1]] + s[3] @@ -221,13 +221,6 @@ class BoolNode(AtomicNode): case_list %= idx + colon + typex + rarrow + expr + semi, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] -# -truth_expr %= notx + truth_expr, lambda h, s: NotNode(s[2]) -truth_expr %= arith, lambda h, s: s[1] - -# - - # arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3]) arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3]) From 69e0c66ac04f67606de381987124e8efbd6eae8f Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 18 Apr 2020 16:22:42 -0400 Subject: [PATCH 037/520] [grammar] - Delete the TODO related to operators precedence --- src/core/cmp/CoolUtils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index fb18d013..89448a26 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -200,7 +200,6 @@ class BoolNode(AtomicNode): # param %= idx + colon + typex, lambda h, s: (s[1], s[3]) -#TODO: Check precedence # expr %= notx + expr, lambda h, s: NotNode(s[2]) expr %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3]) From c1e7e39292c987023486ac11acd7da7071d7ad2e Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 20 Apr 2020 17:01:56 -0400 Subject: [PATCH 038/520] [cil] - Add cil.py to core with AST nodes --- src/core/cmp/cil.py | 232 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100755 src/core/cmp/cil.py diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py new file mode 100755 index 00000000..c1f86113 --- /dev/null +++ b/src/core/cmp/cil.py @@ -0,0 +1,232 @@ +import core.cmp.visitor as visitor + +#AST +class Node: + pass + +class ProgramNode(Node): + def __init__(self, dottypes, dotdata, dotcode): + self.dottypes = dottypes + self.dotdata = dotdata + self.dotcode = dotcode + +class TypeNode(Node): + def __init__(self, name): + self.name = name + self.attributes = [] + self.methods = [] + +class DataNode(Node): + def __init__(self, vname, value): + self.name = vname + self.value = value + +class FunctionNode(Node): + def __init__(self, fname, params, localvars, instructions): + self.name = fname + self.params = params + self.localvars = localvars + self.instructions = instructions + +class ParamNode(Node): + def __init__(self, name): + self.name = name + +class LocalNode(Node): + def __init__(self, name): + self.name = name + +class InstructionNode(Node): + pass + +class AssignNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + +class ArithmeticNode(InstructionNode): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + +class PlusNode(ArithmeticNode): + pass + +class MinusNode(ArithmeticNode): + pass + +class StarNode(ArithmeticNode): + pass + +class DivNode(ArithmeticNode): + pass + +class GetAttribNode(InstructionNode): + pass + +class SetAttribNode(InstructionNode): + pass + +class GetIndexNode(InstructionNode): + pass + +class SetIndexNode(InstructionNode): + pass + +class AllocateNode(InstructionNode): + def __init__(self, itype, dest): + self.type = itype + self.dest = dest + +class ArrayNode(InstructionNode): + pass + +class TypeOfNode(InstructionNode): + def __init__(self, obj, dest): + self.obj = obj + self.dest = dest + +class LabelNode(InstructionNode): + pass + +class GotoNode(InstructionNode): + pass + +class GotoIfNode(InstructionNode): + pass + +class StaticCallNode(InstructionNode): + def __init__(self, function, dest): + self.function = function + self.dest = dest + +class DynamicCallNode(InstructionNode): + def __init__(self, xtype, method, dest): + self.type = xtype + self.method = method + self.dest = dest + +class ArgNode(InstructionNode): + def __init__(self, name): + self.name = name + +class ReturnNode(InstructionNode): + def __init__(self, value=None): + self.value = value + +class LoadNode(InstructionNode): + def __init__(self, dest, msg): + self.dest = dest + self.msg = msg + +class LengthNode(InstructionNode): + pass + +class ConcatNode(InstructionNode): + pass + +class PrefixNode(InstructionNode): + pass + +class SubstringNode(InstructionNode): + pass + +class ToStrNode(InstructionNode): + def __init__(self, dest, ivalue): + self.dest = dest + self.ivalue = ivalue + +class ReadNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + +class PrintNode(InstructionNode): + def __init__(self, str_addr): + self.str_addr = str_addr + +def get_formatter(): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + dottypes = '\n'.join(self.visit(t) for t in node.dottypes) + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(TypeNode) + def visit(self, node): + attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) + methods = '\n\t'.join(f'method {x}: {y}' for x,y in node.methods) + + return f'type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}' + + @visitor.when(FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions) + + return f'function {node.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(ParamNode) + def visit(self, node): + return f'PARAM {node.name}' + + @visitor.when(LocalNode) + def visit(self, node): + return f'LOCAL {node.name}' + + @visitor.when(AssignNode) + def visit(self, node): + return f'{node.dest} = {node.source}' + + @visitor.when(PlusNode) + def visit(self, node): + return f'{node.dest} = {node.left} + {node.right}' + + @visitor.when(MinusNode) + def visit(self, node): + return f'{node.dest} = {node.left} - {node.right}' + + @visitor.when(StarNode) + def visit(self, node): + return f'{node.dest} = {node.left} * {node.right}' + + @visitor.when(DivNode) + def visit(self, node): + return f'{node.dest} = {node.left} / {node.right}' + + @visitor.when(AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.type}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method}' + + @visitor.when(ArgNode) + def visit(self, node): + return f'ARG {node.name}' + + @visitor.when(ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) + From 6205b0ed42d4e344e779e4bf88e4e48ad1536f24 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 20 Apr 2020 17:06:49 -0400 Subject: [PATCH 039/520] [main] - Update action comments and add some imports --- src/main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main.py b/src/main.py index 9b8a8ac0..c8a77b63 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,8 @@ from sys import exit from core.cmp.CoolUtils import tokenize_text, CoolParser from core.cmp.lex import CoolLexer +from core.cmp.evaluation import * +from core.cmp.cil import get_formatter from pprint import pprint @@ -10,7 +12,7 @@ def main(args): with open(args.file, 'r') as fd: code = fd.read() except: - print(f"(0,0) - CompilerError: file {args.file} not found") #TODO: Customize errors + print(f"(0,0) - CompilerError: file {args.file} not found") #//TODO: Customize errors exit(1) # Lexer @@ -36,12 +38,14 @@ def main(args): parse, (failure, token) = CoolParser(tokens) if failure: - print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token}") #TODO: Use correct line and column + print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token}") #//TODO: Use correct line and column exit(1) # Comming soon pipeline steps #print(parse) - + #//TODO: Semantic Check + + #//TODO: COOL to CIL exit(0) From 7c818e177ee82078c89383ea44784d8b0700d375 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 20 Apr 2020 17:08:19 -0400 Subject: [PATCH 040/520] [cil] - Add initial cil visitors --- src/core/cmp/visitors.py | 271 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index cf6e9dcf..1f038d22 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1,4 +1,5 @@ import core.cmp.visitor as visitor +import core.cmp.cil as cil from core.cmp.CoolUtils import * from core.cmp.semantic import SemanticError from core.cmp.semantic import Attribute, Method, Type @@ -1251,3 +1252,273 @@ def visit(self, node, tabs=0): @visitor.when(NewNode) def visit(self, node, tabs=0): return '\t' * tabs + f'\\__NewNode: new {node.type}()' + + +class BaseCOOLToCILVisitor: + def __init__(self, context): + self.dottypes = [] + self.dotdata = [] + self.dotcode = [] + self.current_type = None + self.current_method = None + self.current_function = None + self.context = context + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def instructions(self): + return self.current_function.instructions + + def register_param(self, vinfo): + #'param_{self.current_function.name[9:]}_{vinfo.name}_{len(self.params)}' + vinfo.name = vinfo.name + param_node = cil.ParamNode(vinfo.name) + self.params.append(param_node) + return vinfo.name + + def register_local(self, vinfo): + vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + local_node = cil.LocalNode(vinfo.name) + self.localvars.append(local_node) + return vinfo.name + + def define_internal_local(self): + vinfo = VariableInfo('internal', None) + return self.register_local(vinfo) + + def register_instruction(self, instruction): + self.instructions.append(instruction) + return instruction + ############################### + + def to_function_name(self, method_name, type_name): + return f'function_{method_name}_at_{type_name}' + + def register_function(self, function_name): + function_node = cil.FunctionNode(function_name, [], [], []) + self.dotcode.append(function_node) + return function_node + + def register_type(self, name): + type_node = cil.TypeNode(name) + self.dottypes.append(type_node) + return type_node + + def register_data(self, value): + vname = f'data_{len(self.dotdata)}' + data_node = cil.DataNode(vname, value) + self.dotdata.append(data_node) + return data_node + + +class COOLToCILVisitor(BaseCOOLToCILVisitor): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope): + ###################################################### + # node.declarations -> [ ClassDeclarationNode ... ] + ###################################################### + + self.current_function = self.register_function('entry') + instance = self.define_internal_local() + result = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Main', instance)) + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) + self.register_instruction(cil.ReturnNode(0)) + self.current_function = None + + for declaration, child_scope in zip(node.declarations, scope.children): + self.visit(declaration, child_scope) + + return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + #################################################################### + # node.id -> str + # node.parent -> str + # node.features -> [ FuncDeclarationNode/AttrDeclarationNode ... ] + #################################################################### + + self.current_type = self.context.get_type(node.id) + + # (Handle all the .TYPE section) + type_node = self.register_type(node.id) + type_node.attributes = [attr.name for attr, _ in self.current_type.all_attributes()] + type_node.methods = [(method.name, self.to_function_name(method.name, xtype.name)) for method, xtype in self.current_type.all_methods()] + + func_declarations = (f for f in node.features if isinstance(f, FuncDeclarationNode)) + for feature, child_scope in zip(func_declarations, scope.children): + self.visit(feature, child_scope) + + self.current_type = None + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.params -> [ (str, str) ... ] + # node.type -> str + # node.body -> [ ExpressionNode ... ] + ############################### + + self.current_method = self.current_type.get_method(node.id) + type_name = self.current_type.name + + # (Handle PARAMS) + self.current_function = self.register_function(self.to_function_name(self.current_method.name, type_name)) + for param_name, _ in node.params: + self.register_param(VariableInfo(param_name, None)) + + instructions = [] + for instruction in node.body: + instructions.append(self.visit(instruction, scope)) + # (Handle RETURN) + #//TODO: Handle RETURN 0 and RETURN + if type(instructions[-1]) is str: + self.register_instruction(cil.ReturnNode(instructions[-1])) + else: + self.register_instruction(cil.ReturnNode(instructions[-1].dest)) + + self.current_method = None + + @visitor.when(LetAttributeNode) + def visit(self, node, scope): + ############################### + # node.id -> strs + # node.type -> str + # node.expr -> ExpressionNode + ############################### + + vname = self.register_local(VariableInfo(node.id, node.type)) + expr = self.visit(node.expr, scope) + return self.register_instruction(cil.AssignNode(vname, expr)) + + @visitor.when(AssignNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + + vname = self.register_local(VariableInfo(node.id, None)) + expr = self.visit(node.expr, scope) + if issubclass(type(expr), cil.InstructionNode): + return self.register_instruction(cil.AssignNode(vname, expr.dest)) + return self.register_instruction(cil.AssignNode(vname, node.expr)) + + @visitor.when(FunctionCallNode) + def visit(self, node, scope): + ############################### + # node.obj -> AtomicNode + # node.id -> str + # node.args -> [ ExpressionNode ... ] + # node.type -> str + ############################### + + for arg in node.args: + vname = self.define_internal_local() + value = self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, value)) + self.register_instruction(cil.ArgNode(vname)) + result = self.define_internal_local() + + obj = self.visit(node.obj, scope) + if type(obj) is cil.AllocateNode: + return self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, obj.type), result)) + elif type(obj) is str: + for n in [lv.name for lv in self.current_function.localvars]: + if obj in n.split("_"): + return n + return None + + @visitor.when(IntegerNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex + + @visitor.when(IdNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + param_names = [pn.name for pn in self.current_function.params] + if node.lex in param_names: + for n in param_names: + if node.lex in n.split("_"): + return n + for n in [lv.name for lv in self.current_function.localvars]: + if node.lex in n.split("_"): + return n + + @visitor.when(NewNode) + def visit(self, node, scope): + ############################### + # node.type -> str + ############################### + + instance = self.define_internal_local() + return self.register_instruction(cil.AllocateNode(node.type, instance)) + + @visitor.when(PlusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + plus = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.PlusNode(plus, left, right)) + + @visitor.when(MinusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + minus = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.MinusNode(minus, left, right)) + + @visitor.when(StarNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + star = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.StarNode(star, left, right)) + + @visitor.when(DivNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + div = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.DivNode(star, left, right)) From b1d9776069dbd17d5c81512637b6eba5afa4ba34 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 20 Apr 2020 17:09:16 -0400 Subject: [PATCH 041/520] Update some action comments and add an example of Cool code --- src/core/cmp/CoolUtils.py | 21 ++++++++++++++++++++- src/core/cmp/utils.py | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 89448a26..6228b48b 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -342,7 +342,8 @@ def format_tokens(tokens): return txt # Example text -_text = ''' +#//TODO: [stdevMauricio1802] Add COOL codes here +_text1 = ''' class Main { main ( console : IO ) : AUTO_TYPE { let x : AUTO_TYPE <- 3 + 2 in { @@ -355,3 +356,21 @@ class Main { } ; ''' +text2 = ''' +class A { + a : int ; + def suma ( a : int , b : int ) : int { + a + b ; + } + b : int ; +} + +class B : A { + c : int ; + def f ( d : int , a : A ) : void { + let f : int = 8 ; + let c = new A ( ) . suma ( 5 , f ) ; + c ; + } +} +''' diff --git a/src/core/cmp/utils.py b/src/core/cmp/utils.py index 4e93321c..bb449000 100644 --- a/src/core/cmp/utils.py +++ b/src/core/cmp/utils.py @@ -247,7 +247,7 @@ def __call__(self, w, get_shift_reduce=False): # Your code here!!! (Detect error) if state not in self.action or lookahead not in self.action[state]: - return None, (True, w[cursor]) #TODO: Build the correct error using `w[cursor]` + return None, (True, w[cursor]) #//TODO: Build the correct error using `w[cursor]` action, tag = list(self.action[state][lookahead])[0] # Your code here!!! (Shift case) From fa5b4505c591a8cb9925f46df3c127d8a413f770 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 20 Apr 2020 17:09:57 -0400 Subject: [PATCH 042/520] Add CIL transformation to older_main pipeline --- src/older_main.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/older_main.py b/src/older_main.py index 44a03b58..22ba5541 100644 --- a/src/older_main.py +++ b/src/older_main.py @@ -4,6 +4,8 @@ from core.cmp.visitors import * from core.cmp.evaluation import * from core.cmp.lex import CoolLexer +from core.cmp.cil import get_formatter +from core.cmp.CoolUtils import CoolGrammar import sys from os.path import basename @@ -94,6 +96,13 @@ def run_pipeline(G, text): tree = formatter.visit(ast) txt += 'AST:\n' + str(tree) data.append(txt) + txt = '============= TRANSFORMING TO CIL =============\n' + cool_to_cil = COOLToCILVisitor(context) + cil_ast = cool_to_cil.visit(ast, scope) + formatter = get_formatter() + ast_cil = formatter(cil_ast) + txt += 'AST-CIL:\n' + str(ast_cil) + data.append(txt) return '\n\n'.join(data) @eel.expose @@ -117,11 +126,12 @@ def main(): text = "" with open(input_file, 'r') as f: text = f.read() - lex = CoolLexer() - lex.build() - toks, errors = lex.tokenize(text) + # lex = CoolLexer() + # lex.build() + # toks, errors = lex.tokenize(text) - for token in toks: - if token.token_type == "ERROR": - print(token.lex) + # for token in toks: + # if token.token_type == "ERROR": + # print(token.lex) + print(run_pipeline(CoolGrammar, text)) \ No newline at end of file From 0380d83e9b296c73d5272baa2cf5677a98f2283b Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 20 Apr 2020 17:11:14 -0400 Subject: [PATCH 043/520] Update gitignore Add a rule for src/test.cl script. This file is for quick testing purposes --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 4acafde1..d23b682c 100644 --- a/.gitignore +++ b/.gitignore @@ -408,3 +408,5 @@ dmypy.json # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + +src/test.cl From 1b94bd9a5f4e5bbe4817ec7ad2c1076a79353d0b Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 20 Apr 2020 17:14:50 -0400 Subject: [PATCH 044/520] [pipeline] Adding Cool AST Use all the visitors developed in TypeInferencer project --- src/main.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main.py b/src/main.py index 9b8a8ac0..742375d3 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,9 @@ from sys import exit -from core.cmp.CoolUtils import tokenize_text, CoolParser -from core.cmp.lex import CoolLexer from pprint import pprint +from core.cmp.visitors import * +from core.cmp.lex import CoolLexer +from core.cmp.evaluation import evaluate_reverse_parse +from core.cmp.CoolUtils import tokenize_text, CoolParser def main(args): @@ -33,7 +35,7 @@ def main(args): exit(1) # Parse - parse, (failure, token) = CoolParser(tokens) + (parse, operations), (failure, token) = CoolParser(tokens, get_shift_reduce=True) if failure: print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token}") #TODO: Use correct line and column @@ -42,6 +44,47 @@ def main(args): # Comming soon pipeline steps #print(parse) + # AST + ast = evaluate_reverse_parse(parse, operations, tokens) + + # Collect user types + collector = TypeCollector() + collector.visit(ast) + context = collector.context + + if collector.errors: + # Display errors + pass + + # Building types + builder = TypeBuilder(context) + builder.visit(ast) + + if builder.errors: + # Display errors + pass + + # Checking types + checker = TypeChecker(context) + scope = checker.visit(ast) + + if checker.errors: + # Display errors + pass + + # Infering types + inferer = InferenceVisitor(context) + while True: + old = scope.count_auto() + scope = inferer.visit(ast) + if old == scope.count_auto(): + break + inferer.errors.clear() + scope = inferer.visit(ast) + + if inferer.errors: + # Display errors + pass exit(0) From a85d89f09416460ef8cd3f2ff069134a49094c03 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 20 Apr 2020 18:14:15 -0400 Subject: [PATCH 045/520] Fix error with CoolParser output --- src/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index 742375d3..6d1e6a53 100644 --- a/src/main.py +++ b/src/main.py @@ -14,7 +14,7 @@ def main(args): except: print(f"(0,0) - CompilerError: file {args.file} not found") #TODO: Customize errors exit(1) - + # Lexer lexer = CoolLexer() @@ -35,7 +35,7 @@ def main(args): exit(1) # Parse - (parse, operations), (failure, token) = CoolParser(tokens, get_shift_reduce=True) + parsedData, (failure, token) = CoolParser(tokens, get_shift_reduce=True) if failure: print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token}") #TODO: Use correct line and column @@ -45,6 +45,7 @@ def main(args): #print(parse) # AST + parse, operations = parsedData ast = evaluate_reverse_parse(parse, operations, tokens) # Collect user types From 603cd4983c9e12a53972643486c773176006ddda Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 20 Apr 2020 18:23:13 -0400 Subject: [PATCH 046/520] [pipeline] - Add Cil transformation to pipeline --- src/main.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main.py b/src/main.py index 6d506204..5d3a0f6b 100644 --- a/src/main.py +++ b/src/main.py @@ -45,12 +45,6 @@ def main(args): print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token}") #//TODO: Use correct line and column exit(1) - # Comming soon pipeline steps - #print(parse) - #//TODO: Semantic Check - - #//TODO: COOL to CIL - # AST parse, operations = parsedData ast = evaluate_reverse_parse(parse, operations, tokens) @@ -62,7 +56,7 @@ def main(args): if collector.errors: # Display errors - pass + print('Collector have errors!!') # Building types builder = TypeBuilder(context) @@ -70,7 +64,7 @@ def main(args): if builder.errors: # Display errors - pass + print('Builder have errors!!') # Checking types checker = TypeChecker(context) @@ -78,7 +72,7 @@ def main(args): if checker.errors: # Display errors - pass + print('Checker have errors!!') # Infering types inferer = InferenceVisitor(context) @@ -92,7 +86,15 @@ def main(args): if inferer.errors: # Display errors - pass + print('Inferer have errors!!') + + #CIL Transformation + cool_to_cil = COOLToCILVisitor(context) + cil_ast = cool_to_cil.visit(ast, scope) + formatter = get_formatter() + ast_cil = formatter(cil_ast) + print(ast_cil) + #Write cil to a source file exit(0) From 36a9579ed425341d89eb7b70cf19771d4abfd8ae Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 21 Apr 2020 12:46:20 -0400 Subject: [PATCH 047/520] [cil] - Add nodes no implemented yet to visitor --- src/core/cmp/visitors.py | 225 +++++++++++++++++++++++++++++++-------- 1 file changed, 183 insertions(+), 42 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 1f038d22..c215ed27 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1363,6 +1363,16 @@ def visit(self, node, scope): self.visit(feature, child_scope) self.current_type = None + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement AttrDeclarationNode + pass @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -1393,10 +1403,65 @@ def visit(self, node, scope): self.current_method = None + @visitor.when(IfThenElseNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.if_body -> ExpressionNode + # node.else_body -> ExpressionNode + ################################## + #//TODO: Implement IfThenElseNode + pass + + @visitor.when(WhileLoopNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.body -> ExpressionNode + ################################### + #//TODO: Implement WhileLoopNode + pass + + @visitor.when(BlockNode) + def visit(self, node, scope): + ####################################### + # node.exprs -> [ ExpressionNode ... ] + ####################################### + #//TODO: Implement BlockNode + pass + + @visitor.when(LetInNode) + def visit(self, node, scope): + ############################################ + # node.let_body -> [ LetAttributeNode ... ] + # node.in_body -> ExpressionNode + ############################################ + #//TODO: Implement LetInNode + pass + + @visitor.when(CaseOfNode) + def visit(self, node, scope): + ############################################## + # node.expr -> ExpressionNode + # node.branches -> [ CaseExpressionNode ... } + ############################################## + #//TODO: Implement CaseOfNode + pass + + @visitor.when(CaseExpressionNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement CaseExpressionNode + pass + @visitor.when(LetAttributeNode) def visit(self, node, scope): ############################### - # node.id -> strs + # node.id -> str # node.type -> str # node.expr -> ExpressionNode ############################### @@ -1418,62 +1483,40 @@ def visit(self, node, scope): return self.register_instruction(cil.AssignNode(vname, expr.dest)) return self.register_instruction(cil.AssignNode(vname, node.expr)) - @visitor.when(FunctionCallNode) + @visitor.when(NotNode) def visit(self, node, scope): ############################### - # node.obj -> AtomicNode - # node.id -> str - # node.args -> [ ExpressionNode ... ] - # node.type -> str + # node.expr -> ExpressionNode ############################### - - for arg in node.args: - vname = self.define_internal_local() - value = self.visit(arg, scope) - self.register_instruction(cil.AssignNode(vname, value)) - self.register_instruction(cil.ArgNode(vname)) - result = self.define_internal_local() - - obj = self.visit(node.obj, scope) - if type(obj) is cil.AllocateNode: - return self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, obj.type), result)) - elif type(obj) is str: - for n in [lv.name for lv in self.current_function.localvars]: - if obj in n.split("_"): - return n - return None + #//TODO: Implement NotNode + pass - @visitor.when(IntegerNode) + @visitor.when(LessEqualNode) def visit(self, node, scope): ############################### - # node.lex -> str + # node.left -> ExpressionNode + # node.right -> ExpressionNode ############################### - - return node.lex + #//TODO: Implement LessEqualNode + pass - @visitor.when(IdNode) + @visitor.when(LessNode) def visit(self, node, scope): ############################### - # node.lex -> str + # node.left -> ExpressionNode + # node.right -> ExpressionNode ############################### - - param_names = [pn.name for pn in self.current_function.params] - if node.lex in param_names: - for n in param_names: - if node.lex in n.split("_"): - return n - for n in [lv.name for lv in self.current_function.localvars]: - if node.lex in n.split("_"): - return n + #//TODO: Implement LessNode + pass - @visitor.when(NewNode) + @visitor.when(EqualNode) def visit(self, node, scope): ############################### - # node.type -> str + # node.left -> ExpressionNode + # node.right -> ExpressionNode ############################### - - instance = self.define_internal_local() - return self.register_instruction(cil.AllocateNode(node.type, instance)) + #//TODO: Implement EqualNode + pass @visitor.when(PlusNode) def visit(self, node, scope): @@ -1522,3 +1565,101 @@ def visit(self, node, scope): left = self.visit(node.left, scope) right = self.visit(node.right, scope) return self.register_instruction(cil.DivNode(star, left, right)) + + @visitor.when(IsVoidNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement IsVoidNode + pass + + @visitor.when(ComplementNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement ComplementNode + pass + + @visitor.when(FunctionCallNode) + def visit(self, node, scope): + ###################################### + # node.obj -> AtomicNode + # node.id -> str + # node.args -> [ ExpressionNode ... ] + # node.type -> str + ##################################### + + for arg in node.args: + vname = self.define_internal_local() + value = self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, value)) + self.register_instruction(cil.ArgNode(vname)) + result = self.define_internal_local() + + obj = self.visit(node.obj, scope) + if type(obj) is cil.AllocateNode: + return self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, obj.type), result)) + elif type(obj) is str: + for n in [lv.name for lv in self.current_function.localvars]: + if obj in n.split("_"): + return n + return None + + @visitor.when(MemberCallNode) + def visit(self, node, scope): + ###################################### + # node.id -> str + # node.args -> [ ExpressionNode ... ] + ###################################### + #//TODO: Implement MemberCallNode + pass + + @visitor.when(NewNode) + def visit(self, node, scope): + ############################### + # node.type -> str + ############################### + + instance = self.define_internal_local() + return self.register_instruction(cil.AllocateNode(node.type, instance)) + + @visitor.when(IntegerNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex + + @visitor.when(IdNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + param_names = [pn.name for pn in self.current_function.params] + if node.lex in param_names: + for n in param_names: + if node.lex in n.split("_"): + return n + for n in [lv.name for lv in self.current_function.localvars]: + if node.lex in n.split("_"): + return n + + @visitor.when(StringNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex + + @visitor.when(StringNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex From d0aa3714dde03d6d06f0b46b17b52197975c7bc0 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 11:22:52 -0400 Subject: [PATCH 048/520] [cil] - Add labels_count property to FunctionNode --- src/core/cmp/cil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index c1f86113..f91b44bf 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -27,6 +27,7 @@ def __init__(self, fname, params, localvars, instructions): self.params = params self.localvars = localvars self.instructions = instructions + self.labels_count = 0 class ParamNode(Node): def __init__(self, name): From e81acab23e60594421760595cecf175ef95a9258 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 11:25:15 -0400 Subject: [PATCH 049/520] [cil] - Add `register_label` method to `BaseCOOLToCILVisitor` Factory method of `LabelNode` that creates label's name without conflicts with other labels of the `current_function` --- src/core/cmp/visitors.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index c215ed27..1a317491 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1317,6 +1317,11 @@ def register_data(self, value): self.dotdata.append(data_node) return data_node + def register_label(self, label): + lname = f'{label}_{self.current_function.labels_count}' + self.current_function.labels_count += 1 + return cil.LabelNode(lname) + class COOLToCILVisitor(BaseCOOLToCILVisitor): @visitor.on('node') From cb5f6c073939b03136397df13bdbe0875e75fa35 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 11:26:03 -0400 Subject: [PATCH 050/520] [cil] - Implements `Label`, `Goto`, and `GotoIf` nodes --- src/core/cmp/cil.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index f91b44bf..106b9696 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -89,13 +89,17 @@ def __init__(self, obj, dest): self.dest = dest class LabelNode(InstructionNode): - pass + def __init__(self, label): + self.label = label class GotoNode(InstructionNode): - pass + def __init__(self, label): + self.label = label class GotoIfNode(InstructionNode): - pass + def __init__(self, condition, label): + self.condition = condition + self.label = label class StaticCallNode(InstructionNode): def __init__(self, function, dest): From cd926c7948c26a3ffe2495f6108855f51b87d525 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 11:29:53 -0400 Subject: [PATCH 051/520] [cil] - Add `ret_expr` property to scope instance of a function This is to manage return value of function. Old way was to set a return value of some visit nodes and get the last one called --- src/core/cmp/visitors.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 1a317491..48ad815d 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1376,7 +1376,7 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - #//TODO: Implement AttrDeclarationNode + #//TODO: Implement AttrDeclarationNode, assess whether this needs to be done pass @visitor.when(FuncDeclarationNode) @@ -1396,15 +1396,15 @@ def visit(self, node, scope): for param_name, _ in node.params: self.register_param(VariableInfo(param_name, None)) - instructions = [] + scope.ret_expr = None for instruction in node.body: - instructions.append(self.visit(instruction, scope)) + self.visit(instruction, scope) # (Handle RETURN) #//TODO: Handle RETURN 0 and RETURN - if type(instructions[-1]) is str: - self.register_instruction(cil.ReturnNode(instructions[-1])) + if type(scope.ret_expr) is str: + self.register_instruction(cil.ReturnNode(scope.ret_expr)) else: - self.register_instruction(cil.ReturnNode(instructions[-1].dest)) + self.register_instruction(cil.ReturnNode(scope.ret_expr.dest)) self.current_method = None From 2785fed08488e04f062e536ae12ff6393f055a5e Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 11:30:28 -0400 Subject: [PATCH 052/520] [cil] - Implement visit of `IfThenElseNode` --- src/core/cmp/visitors.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 48ad815d..d2482f55 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1415,8 +1415,26 @@ def visit(self, node, scope): # node.if_body -> ExpressionNode # node.else_body -> ExpressionNode ################################## - #//TODO: Implement IfThenElseNode - pass + vret = self.define_internal_local() + + then_label_node = self.register_label('then_label') + else_label_node = self.register_label('else_label') + + #If x GOTO label + condition_value = self.visit(node.condition, scope).dest + self.register_instruction(cil.GotoIfNode(condition_value, then_label_node.label)) + #GOTO else_label + self.register_instruction(cil.GotoNode(else_label_node.label)) + + self.register_instruction(then_label_node) + then_expr = self.visit(node.if_body, scope) + self.register_instruction(cil.AssignNode(vret, then_expr.dest)) + + self.register_instruction(else_label_node) + else_expr = self.visit(node.else_body, scope) + self.register_instruction(cil.AssignNode(vret, then_expr.dest)) + + scope.ret_expr = vret @visitor.when(WhileLoopNode) def visit(self, node, scope): From 915305da4f0840a58368688c1ffc261f37f81853 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 12:00:18 -0400 Subject: [PATCH 053/520] [cil] - Implement visit of `WhileLoopNode` --- src/core/cmp/visitors.py | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index d2482f55..07d73916 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1415,24 +1415,24 @@ def visit(self, node, scope): # node.if_body -> ExpressionNode # node.else_body -> ExpressionNode ################################## - vret = self.define_internal_local() + vret = self.register_local(VariableInfo('if_then_else_value', None)) then_label_node = self.register_label('then_label') else_label_node = self.register_label('else_label') - #If x GOTO label - condition_value = self.visit(node.condition, scope).dest - self.register_instruction(cil.GotoIfNode(condition_value, then_label_node.label)) + #If condition GOTO then_label + self.visit(node.condition, scope) + self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, then_label_node.label)) #GOTO else_label self.register_instruction(cil.GotoNode(else_label_node.label)) - + #Label then_label self.register_instruction(then_label_node) - then_expr = self.visit(node.if_body, scope) - self.register_instruction(cil.AssignNode(vret, then_expr.dest)) - + self.visit(node.if_body, scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + #Label else_label self.register_instruction(else_label_node) - else_expr = self.visit(node.else_body, scope) - self.register_instruction(cil.AssignNode(vret, then_expr.dest)) + self.visit(node.else_body, scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) scope.ret_expr = vret @@ -1442,8 +1442,29 @@ def visit(self, node, scope): # node.condition -> ExpressionNode # node.body -> ExpressionNode ################################### - #//TODO: Implement WhileLoopNode - pass + vret = self.register_local(VariableInfo('while_value', None)) + + while_label_node = self.register_label('while_label') + loop_label_node = self.register_label('loop_label') + pool_label_node = self.register_label('pool_label') + #Label while + self.register_instruction(while_label_node) + #If condition GOTO loop + self.visit(node.condition, scope) + self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, loop_label_node.label)) + #GOTO pool + self.register_instruction(cil.GotoNode(pool_label_node.label)) + #Label loop + self.register_instruction(loop_label_node) + self.visit(node.body, scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + #GOTO while + self.register_instruction(cil.GotoNode(while_label_node.label)) + #Label pool + self.register_instruction(pool_label_node) + + scope.ret_expr = vret + @visitor.when(BlockNode) def visit(self, node, scope): From b2052edd9a235acce1dbb06657788bf1d1ad9bdb Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 12:38:25 -0400 Subject: [PATCH 054/520] [cil] - Add COOL to CIL visitor in a separate file. Also visit of `BlockNode` implemented --- src/core/cmp/cool_to_cil.py | 458 ++++++++++++++++++++++++++++++++++++ src/core/cmp/visitors.py | 456 ----------------------------------- 2 files changed, 458 insertions(+), 456 deletions(-) create mode 100644 src/core/cmp/cool_to_cil.py diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py new file mode 100644 index 00000000..71c7acf9 --- /dev/null +++ b/src/core/cmp/cool_to_cil.py @@ -0,0 +1,458 @@ +import core.cmp.visitor as visitor +import core.cmp.cil as cil +import core.cmp.CoolUtils as cool +from core.cmp.semantic import Attribute, Method, Type, VariableInfo + +class BaseCOOLToCILVisitor: + def __init__(self, context): + self.dottypes = [] + self.dotdata = [] + self.dotcode = [] + self.current_type = None + self.current_method = None + self.current_function = None + self.context = context + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def instructions(self): + return self.current_function.instructions + + def register_param(self, vinfo): + #'param_{self.current_function.name[9:]}_{vinfo.name}_{len(self.params)}' + vinfo.name = vinfo.name + param_node = cil.ParamNode(vinfo.name) + self.params.append(param_node) + return vinfo.name + + def register_local(self, vinfo): + vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + local_node = cil.LocalNode(vinfo.name) + self.localvars.append(local_node) + return vinfo.name + + def define_internal_local(self): + vinfo = VariableInfo('internal', None) + return self.register_local(vinfo) + + def register_instruction(self, instruction): + self.instructions.append(instruction) + return instruction + ############################### + + def to_function_name(self, method_name, type_name): + return f'function_{method_name}_at_{type_name}' + + def register_function(self, function_name): + function_node = cil.FunctionNode(function_name, [], [], []) + self.dotcode.append(function_node) + return function_node + + def register_type(self, name): + type_node = cil.TypeNode(name) + self.dottypes.append(type_node) + return type_node + + def register_data(self, value): + vname = f'data_{len(self.dotdata)}' + data_node = cil.DataNode(vname, value) + self.dotdata.append(data_node) + return data_node + + def register_label(self, label): + lname = f'{label}_{self.current_function.labels_count}' + self.current_function.labels_count += 1 + return cil.LabelNode(lname) + + +class COOLToCILVisitor(BaseCOOLToCILVisitor): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node, scope): + ###################################################### + # node.declarations -> [ ClassDeclarationNode ... ] + ###################################################### + + self.current_function = self.register_function('entry') + instance = self.define_internal_local() + result = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Main', instance)) + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) + self.register_instruction(cil.ReturnNode(0)) + self.current_function = None + + for declaration, child_scope in zip(node.declarations, scope.children): + self.visit(declaration, child_scope) + + return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) + + @visitor.when(cool.ClassDeclarationNode) + def visit(self, node, scope): + #################################################################### + # node.id -> str + # node.parent -> str + # node.features -> [ FuncDeclarationNode/AttrDeclarationNode ... ] + #################################################################### + + self.current_type = self.context.get_type(node.id) + + # (Handle all the .TYPE section) + type_node = self.register_type(node.id) + type_node.attributes = [attr.name for attr, _ in self.current_type.all_attributes()] + type_node.methods = [(method.name, self.to_function_name(method.name, xtype.name)) for method, xtype in self.current_type.all_methods()] + + func_declarations = (f for f in node.features if isinstance(f, cool.FuncDeclarationNode)) + for feature, child_scope in zip(func_declarations, scope.children): + self.visit(feature, child_scope) + + self.current_type = None + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement AttrDeclarationNode, assess whether this needs to be done + pass + + @visitor.when(cool.FuncDeclarationNode) + def visit(self, node, scope): + ##################################### + # node.id -> str + # node.params -> [ (str, str) ... ] + # node.type -> str + # node.body -> [ ExpressionNode ... ] + ##################################### + + self.current_method = self.current_type.get_method(node.id) + type_name = self.current_type.name + + # (Handle PARAMS) + self.current_function = self.register_function(self.to_function_name(self.current_method.name, type_name)) + for param_name, _ in node.params: + self.register_param(VariableInfo(param_name, None)) + + scope.ret_expr = None + for instruction in node.body: + self.visit(instruction, scope) + # (Handle RETURN) + #//TODO: Handle RETURN 0 and RETURN + if type(scope.ret_expr) is str: + self.register_instruction(cil.ReturnNode(scope.ret_expr)) + else: + self.register_instruction(cil.ReturnNode(scope.ret_expr.dest)) + + self.current_method = None + + @visitor.when(cool.IfThenElseNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.if_body -> ExpressionNode + # node.else_body -> ExpressionNode + ################################## + vret = self.register_local(VariableInfo('if_then_else_value', None)) + + then_label_node = self.register_label('then_label') + else_label_node = self.register_label('else_label') + + #If condition GOTO then_label + self.visit(node.condition, scope) + self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, then_label_node.label)) + #GOTO else_label + self.register_instruction(cil.GotoNode(else_label_node.label)) + #Label then_label + self.register_instruction(then_label_node) + self.visit(node.if_body, scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + #Label else_label + self.register_instruction(else_label_node) + self.visit(node.else_body, scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + + scope.ret_expr = vret + + @visitor.when(cool.WhileLoopNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.body -> ExpressionNode + ################################### + vret = self.register_local(VariableInfo('while_value', None)) + + while_label_node = self.register_label('while_label') + loop_label_node = self.register_label('loop_label') + pool_label_node = self.register_label('pool_label') + #Label while + self.register_instruction(while_label_node) + #If condition GOTO loop + self.visit(node.condition, scope) + self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, loop_label_node.label)) + #GOTO pool + self.register_instruction(cil.GotoNode(pool_label_node.label)) + #Label loop + self.register_instruction(loop_label_node) + self.visit(node.body, scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + #GOTO while + self.register_instruction(cil.GotoNode(while_label_node.label)) + #Label pool + self.register_instruction(pool_label_node) + + scope.ret_expr = vret + + + @visitor.when(cool.BlockNode) + def visit(self, node, scope): + ####################################### + # node.exprs -> [ ExpressionNode ... ] + ####################################### + for expr in node.exprs: + self.visit(expr, scope) + + @visitor.when(cool.LetInNode) + def visit(self, node, scope): + ############################################ + # node.let_body -> [ LetAttributeNode ... ] + # node.in_body -> ExpressionNode + ############################################ + #//TODO: Implement LetInNode + pass + + @visitor.when(cool.CaseOfNode) + def visit(self, node, scope): + ############################################## + # node.expr -> ExpressionNode + # node.branches -> [ CaseExpressionNode ... } + ############################################## + #//TODO: Implement CaseOfNode + pass + + @visitor.when(cool.CaseExpressionNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement CaseExpressionNode + pass + + @visitor.when(cool.LetAttributeNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + + vname = self.register_local(VariableInfo(node.id, node.type)) + expr = self.visit(node.expr, scope) + return self.register_instruction(cil.AssignNode(vname, expr)) + + @visitor.when(cool.AssignNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + + vname = self.register_local(VariableInfo(node.id, None)) + expr = self.visit(node.expr, scope) + if issubclass(type(expr), cil.InstructionNode): + return self.register_instruction(cil.AssignNode(vname, expr.dest)) + return self.register_instruction(cil.AssignNode(vname, node.expr)) + + @visitor.when(cool.NotNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement NotNode + pass + + @visitor.when(cool.LessEqualNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + #//TODO: Implement LessEqualNode + pass + + @visitor.when(cool.LessNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + #//TODO: Implement LessNode + pass + + @visitor.when(cool.EqualNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + #//TODO: Implement EqualNode + pass + + @visitor.when(cool.PlusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + plus = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.PlusNode(plus, left, right)) + + @visitor.when(cool.MinusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + minus = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.MinusNode(minus, left, right)) + + @visitor.when(cool.StarNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + star = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.StarNode(star, left, right)) + + @visitor.when(cool.DivNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + div = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + return self.register_instruction(cil.DivNode(div, left, right)) + + @visitor.when(cool.IsVoidNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement IsVoidNode + pass + + @visitor.when(cool.ComplementNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + #//TODO: Implement ComplementNode + pass + + @visitor.when(cool.FunctionCallNode) + def visit(self, node, scope): + ###################################### + # node.obj -> AtomicNode + # node.id -> str + # node.args -> [ ExpressionNode ... ] + # node.type -> str + ##################################### + + for arg in node.args: + vname = self.define_internal_local() + value = self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, value)) + self.register_instruction(cil.ArgNode(vname)) + result = self.define_internal_local() + + obj = self.visit(node.obj, scope) + if type(obj) is cil.AllocateNode: + return self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, obj.type), result)) + elif type(obj) is str: + for n in [lv.name for lv in self.current_function.localvars]: + if obj in n.split("_"): + return n + return None + + @visitor.when(cool.MemberCallNode) + def visit(self, node, scope): + ###################################### + # node.id -> str + # node.args -> [ ExpressionNode ... ] + ###################################### + #//TODO: Implement MemberCallNode + pass + + @visitor.when(cool.NewNode) + def visit(self, node, scope): + ############################### + # node.type -> str + ############################### + + instance = self.define_internal_local() + return self.register_instruction(cil.AllocateNode(node.type, instance)) + + @visitor.when(cool.IntegerNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex + + @visitor.when(cool.IdNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + param_names = [pn.name for pn in self.current_function.params] + if node.lex in param_names: + for n in param_names: + if node.lex in n.split("_"): + return n + for n in [lv.name for lv in self.current_function.localvars]: + if node.lex in n.split("_"): + return n + + @visitor.when(cool.StringNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex + + @visitor.when(cool.StringNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 07d73916..cf6e9dcf 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1,5 +1,4 @@ import core.cmp.visitor as visitor -import core.cmp.cil as cil from core.cmp.CoolUtils import * from core.cmp.semantic import SemanticError from core.cmp.semantic import Attribute, Method, Type @@ -1252,458 +1251,3 @@ def visit(self, node, tabs=0): @visitor.when(NewNode) def visit(self, node, tabs=0): return '\t' * tabs + f'\\__NewNode: new {node.type}()' - - -class BaseCOOLToCILVisitor: - def __init__(self, context): - self.dottypes = [] - self.dotdata = [] - self.dotcode = [] - self.current_type = None - self.current_method = None - self.current_function = None - self.context = context - - @property - def params(self): - return self.current_function.params - - @property - def localvars(self): - return self.current_function.localvars - - @property - def instructions(self): - return self.current_function.instructions - - def register_param(self, vinfo): - #'param_{self.current_function.name[9:]}_{vinfo.name}_{len(self.params)}' - vinfo.name = vinfo.name - param_node = cil.ParamNode(vinfo.name) - self.params.append(param_node) - return vinfo.name - - def register_local(self, vinfo): - vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' - local_node = cil.LocalNode(vinfo.name) - self.localvars.append(local_node) - return vinfo.name - - def define_internal_local(self): - vinfo = VariableInfo('internal', None) - return self.register_local(vinfo) - - def register_instruction(self, instruction): - self.instructions.append(instruction) - return instruction - ############################### - - def to_function_name(self, method_name, type_name): - return f'function_{method_name}_at_{type_name}' - - def register_function(self, function_name): - function_node = cil.FunctionNode(function_name, [], [], []) - self.dotcode.append(function_node) - return function_node - - def register_type(self, name): - type_node = cil.TypeNode(name) - self.dottypes.append(type_node) - return type_node - - def register_data(self, value): - vname = f'data_{len(self.dotdata)}' - data_node = cil.DataNode(vname, value) - self.dotdata.append(data_node) - return data_node - - def register_label(self, label): - lname = f'{label}_{self.current_function.labels_count}' - self.current_function.labels_count += 1 - return cil.LabelNode(lname) - - -class COOLToCILVisitor(BaseCOOLToCILVisitor): - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node, scope): - ###################################################### - # node.declarations -> [ ClassDeclarationNode ... ] - ###################################################### - - self.current_function = self.register_function('entry') - instance = self.define_internal_local() - result = self.define_internal_local() - self.register_instruction(cil.AllocateNode('Main', instance)) - self.register_instruction(cil.ArgNode(instance)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) - self.register_instruction(cil.ReturnNode(0)) - self.current_function = None - - for declaration, child_scope in zip(node.declarations, scope.children): - self.visit(declaration, child_scope) - - return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) - - @visitor.when(ClassDeclarationNode) - def visit(self, node, scope): - #################################################################### - # node.id -> str - # node.parent -> str - # node.features -> [ FuncDeclarationNode/AttrDeclarationNode ... ] - #################################################################### - - self.current_type = self.context.get_type(node.id) - - # (Handle all the .TYPE section) - type_node = self.register_type(node.id) - type_node.attributes = [attr.name for attr, _ in self.current_type.all_attributes()] - type_node.methods = [(method.name, self.to_function_name(method.name, xtype.name)) for method, xtype in self.current_type.all_methods()] - - func_declarations = (f for f in node.features if isinstance(f, FuncDeclarationNode)) - for feature, child_scope in zip(func_declarations, scope.children): - self.visit(feature, child_scope) - - self.current_type = None - - @visitor.when(AttrDeclarationNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### - #//TODO: Implement AttrDeclarationNode, assess whether this needs to be done - pass - - @visitor.when(FuncDeclarationNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.params -> [ (str, str) ... ] - # node.type -> str - # node.body -> [ ExpressionNode ... ] - ############################### - - self.current_method = self.current_type.get_method(node.id) - type_name = self.current_type.name - - # (Handle PARAMS) - self.current_function = self.register_function(self.to_function_name(self.current_method.name, type_name)) - for param_name, _ in node.params: - self.register_param(VariableInfo(param_name, None)) - - scope.ret_expr = None - for instruction in node.body: - self.visit(instruction, scope) - # (Handle RETURN) - #//TODO: Handle RETURN 0 and RETURN - if type(scope.ret_expr) is str: - self.register_instruction(cil.ReturnNode(scope.ret_expr)) - else: - self.register_instruction(cil.ReturnNode(scope.ret_expr.dest)) - - self.current_method = None - - @visitor.when(IfThenElseNode) - def visit(self, node, scope): - ################################### - # node.condition -> ExpressionNode - # node.if_body -> ExpressionNode - # node.else_body -> ExpressionNode - ################################## - vret = self.register_local(VariableInfo('if_then_else_value', None)) - - then_label_node = self.register_label('then_label') - else_label_node = self.register_label('else_label') - - #If condition GOTO then_label - self.visit(node.condition, scope) - self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, then_label_node.label)) - #GOTO else_label - self.register_instruction(cil.GotoNode(else_label_node.label)) - #Label then_label - self.register_instruction(then_label_node) - self.visit(node.if_body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) - #Label else_label - self.register_instruction(else_label_node) - self.visit(node.else_body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) - - scope.ret_expr = vret - - @visitor.when(WhileLoopNode) - def visit(self, node, scope): - ################################### - # node.condition -> ExpressionNode - # node.body -> ExpressionNode - ################################### - vret = self.register_local(VariableInfo('while_value', None)) - - while_label_node = self.register_label('while_label') - loop_label_node = self.register_label('loop_label') - pool_label_node = self.register_label('pool_label') - #Label while - self.register_instruction(while_label_node) - #If condition GOTO loop - self.visit(node.condition, scope) - self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, loop_label_node.label)) - #GOTO pool - self.register_instruction(cil.GotoNode(pool_label_node.label)) - #Label loop - self.register_instruction(loop_label_node) - self.visit(node.body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) - #GOTO while - self.register_instruction(cil.GotoNode(while_label_node.label)) - #Label pool - self.register_instruction(pool_label_node) - - scope.ret_expr = vret - - - @visitor.when(BlockNode) - def visit(self, node, scope): - ####################################### - # node.exprs -> [ ExpressionNode ... ] - ####################################### - #//TODO: Implement BlockNode - pass - - @visitor.when(LetInNode) - def visit(self, node, scope): - ############################################ - # node.let_body -> [ LetAttributeNode ... ] - # node.in_body -> ExpressionNode - ############################################ - #//TODO: Implement LetInNode - pass - - @visitor.when(CaseOfNode) - def visit(self, node, scope): - ############################################## - # node.expr -> ExpressionNode - # node.branches -> [ CaseExpressionNode ... } - ############################################## - #//TODO: Implement CaseOfNode - pass - - @visitor.when(CaseExpressionNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### - #//TODO: Implement CaseExpressionNode - pass - - @visitor.when(LetAttributeNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### - - vname = self.register_local(VariableInfo(node.id, node.type)) - expr = self.visit(node.expr, scope) - return self.register_instruction(cil.AssignNode(vname, expr)) - - @visitor.when(AssignNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.expr -> ExpressionNode - ############################### - - vname = self.register_local(VariableInfo(node.id, None)) - expr = self.visit(node.expr, scope) - if issubclass(type(expr), cil.InstructionNode): - return self.register_instruction(cil.AssignNode(vname, expr.dest)) - return self.register_instruction(cil.AssignNode(vname, node.expr)) - - @visitor.when(NotNode) - def visit(self, node, scope): - ############################### - # node.expr -> ExpressionNode - ############################### - #//TODO: Implement NotNode - pass - - @visitor.when(LessEqualNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - #//TODO: Implement LessEqualNode - pass - - @visitor.when(LessNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - #//TODO: Implement LessNode - pass - - @visitor.when(EqualNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - #//TODO: Implement EqualNode - pass - - @visitor.when(PlusNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - - plus = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.PlusNode(plus, left, right)) - - @visitor.when(MinusNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - - minus = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.MinusNode(minus, left, right)) - - @visitor.when(StarNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - - star = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.StarNode(star, left, right)) - - @visitor.when(DivNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - - div = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.DivNode(star, left, right)) - - @visitor.when(IsVoidNode) - def visit(self, node, scope): - ############################### - # node.expr -> ExpressionNode - ############################### - #//TODO: Implement IsVoidNode - pass - - @visitor.when(ComplementNode) - def visit(self, node, scope): - ############################### - # node.expr -> ExpressionNode - ############################### - #//TODO: Implement ComplementNode - pass - - @visitor.when(FunctionCallNode) - def visit(self, node, scope): - ###################################### - # node.obj -> AtomicNode - # node.id -> str - # node.args -> [ ExpressionNode ... ] - # node.type -> str - ##################################### - - for arg in node.args: - vname = self.define_internal_local() - value = self.visit(arg, scope) - self.register_instruction(cil.AssignNode(vname, value)) - self.register_instruction(cil.ArgNode(vname)) - result = self.define_internal_local() - - obj = self.visit(node.obj, scope) - if type(obj) is cil.AllocateNode: - return self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, obj.type), result)) - elif type(obj) is str: - for n in [lv.name for lv in self.current_function.localvars]: - if obj in n.split("_"): - return n - return None - - @visitor.when(MemberCallNode) - def visit(self, node, scope): - ###################################### - # node.id -> str - # node.args -> [ ExpressionNode ... ] - ###################################### - #//TODO: Implement MemberCallNode - pass - - @visitor.when(NewNode) - def visit(self, node, scope): - ############################### - # node.type -> str - ############################### - - instance = self.define_internal_local() - return self.register_instruction(cil.AllocateNode(node.type, instance)) - - @visitor.when(IntegerNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - - return node.lex - - @visitor.when(IdNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - - param_names = [pn.name for pn in self.current_function.params] - if node.lex in param_names: - for n in param_names: - if node.lex in n.split("_"): - return n - for n in [lv.name for lv in self.current_function.localvars]: - if node.lex in n.split("_"): - return n - - @visitor.when(StringNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - - return node.lex - - @visitor.when(StringNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - - return node.lex From fa663021428c5dcb110d10357f0f02c01fff29ba Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 12:45:27 -0400 Subject: [PATCH 055/520] [cil] - Implement visit of `LetInNode` --- src/core/cmp/cool_to_cil.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 71c7acf9..7453e381 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -214,7 +214,6 @@ def visit(self, node, scope): scope.ret_expr = vret - @visitor.when(cool.BlockNode) def visit(self, node, scope): ####################################### @@ -229,9 +228,14 @@ def visit(self, node, scope): # node.let_body -> [ LetAttributeNode ... ] # node.in_body -> ExpressionNode ############################################ - #//TODO: Implement LetInNode - pass + vret = self.register_local(VariableInfo('let_in_value', None)) + for let_att_node in node.let_body: + self.visit(let_att_node, scope) + self.visit(node.in_body, scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + scope.ret_expr = vret + @visitor.when(cool.CaseOfNode) def visit(self, node, scope): ############################################## From 55eec5001e273a1dfe515c9120d7a20c57a721be Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 12:59:31 -0400 Subject: [PATCH 056/520] [cil] - Handle RETURN in visit of `FuncDeclarationNode` --- src/core/cmp/cool_to_cil.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 7453e381..f298fef6 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -145,15 +145,17 @@ def visit(self, node, scope): for param_name, _ in node.params: self.register_param(VariableInfo(param_name, None)) + #//TODO: Check whether a value of a expression is a string and see if it must be added to .DATA or not scope.ret_expr = None for instruction in node.body: self.visit(instruction, scope) # (Handle RETURN) - #//TODO: Handle RETURN 0 and RETURN - if type(scope.ret_expr) is str: - self.register_instruction(cil.ReturnNode(scope.ret_expr)) + if scope.ret_expr is None: + self.register_instruction(cil.ReturnNode('')) + elif self.current_function.name is 'entry': + self.register_instruction(cil.ReturnNode(0)) else: - self.register_instruction(cil.ReturnNode(scope.ret_expr.dest)) + self.register_instruction(cil.ReturnNode(scope.ret_expr)) self.current_method = None From 196954145828a7d03d1a1efef621187fdf6262e3 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 13:00:30 -0400 Subject: [PATCH 057/520] [cil] - Update visit of `LetAttributeNode` and `AssignNode` --- src/core/cmp/cool_to_cil.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f298fef6..f31caf48 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -173,17 +173,17 @@ def visit(self, node, scope): #If condition GOTO then_label self.visit(node.condition, scope) - self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, then_label_node.label)) + self.register_instruction(cil.GotoIfNode(scope.ret_expr, then_label_node.label)) #GOTO else_label self.register_instruction(cil.GotoNode(else_label_node.label)) #Label then_label self.register_instruction(then_label_node) self.visit(node.if_body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) #Label else_label self.register_instruction(else_label_node) self.visit(node.else_body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) scope.ret_expr = vret @@ -202,13 +202,13 @@ def visit(self, node, scope): self.register_instruction(while_label_node) #If condition GOTO loop self.visit(node.condition, scope) - self.register_instruction(cil.GotoIfNode(scope.ret_expr.dest, loop_label_node.label)) + self.register_instruction(cil.GotoIfNode(scope.ret_expr, loop_label_node.label)) #GOTO pool self.register_instruction(cil.GotoNode(pool_label_node.label)) #Label loop self.register_instruction(loop_label_node) self.visit(node.body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) #GOTO while self.register_instruction(cil.GotoNode(while_label_node.label)) #Label pool @@ -235,7 +235,7 @@ def visit(self, node, scope): for let_att_node in node.let_body: self.visit(let_att_node, scope) self.visit(node.in_body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr.dest)) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) scope.ret_expr = vret @visitor.when(cool.CaseOfNode) @@ -264,10 +264,11 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - + #//TODO: See if node.type is string prior to add it to .DATA ??? vname = self.register_local(VariableInfo(node.id, node.type)) - expr = self.visit(node.expr, scope) - return self.register_instruction(cil.AssignNode(vname, expr)) + self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) + scope.ret_expr = None @visitor.when(cool.AssignNode) def visit(self, node, scope): @@ -277,10 +278,9 @@ def visit(self, node, scope): ############################### vname = self.register_local(VariableInfo(node.id, None)) - expr = self.visit(node.expr, scope) - if issubclass(type(expr), cil.InstructionNode): - return self.register_instruction(cil.AssignNode(vname, expr.dest)) - return self.register_instruction(cil.AssignNode(vname, node.expr)) + self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) + scope.ret_expr = vname @visitor.when(cool.NotNode) def visit(self, node, scope): From f444341610577375d1ca42b94f4c89338bf0a24d Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 16:21:50 -0400 Subject: [PATCH 058/520] [cil] - Add some new nodes to CIL AST Nodes: - LessEqualNode - LessNode - EqualNode - ComplementNode --- src/core/cmp/cil.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 106b9696..e3c65825 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -63,6 +63,15 @@ class StarNode(ArithmeticNode): class DivNode(ArithmeticNode): pass +class LessEqualNode(ArithmeticNode): + pass + +class LessNode(ArithmeticNode): + pass + +class EqualNode(ArithmeticNode): + pass + class GetAttribNode(InstructionNode): pass @@ -150,6 +159,11 @@ class PrintNode(InstructionNode): def __init__(self, str_addr): self.str_addr = str_addr +class ComplementNode(InstructionNode): + def __init__(self, dest, obj): + self.dest = dest + self.obj = obj + def get_formatter(): class PrintVisitor(object): From 783a4503433b8e87d5487a489bc5817c85894edd Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 16:24:13 -0400 Subject: [PATCH 059/520] [cil] - Implement and update visit of `BinaryNodes` Nodes: - NotNode - LessEqualNode - LessNode - EqualNode - PlusNode - MinusNode - StarNode - DivNode - ComplementNode --- src/core/cmp/cool_to_cil.py | 94 ++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f31caf48..4dadfa65 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -287,8 +287,10 @@ def visit(self, node, scope): ############################### # node.expr -> ExpressionNode ############################### - #//TODO: Implement NotNode - pass + vname = self.define_internal_local() + self.visit(node.expr, scope) + self.register_instruction(cil.MinusNode(vname, 1, scope.ret_expr)) + scope.ret_expr = vname @visitor.when(cool.LessEqualNode) def visit(self, node, scope): @@ -296,8 +298,13 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - #//TODO: Implement LessEqualNode - pass + vname = self.define_internal_local() + self.visit(node.left, scope) + left = scope.ret_expr + self.visit(node.right, scope) + right = scope.ret_expr + self.register_instruction(cil.LessEqualNode(vname, left, right)) + scope.ret_expr = vname @visitor.when(cool.LessNode) def visit(self, node, scope): @@ -305,8 +312,13 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - #//TODO: Implement LessNode - pass + vname = self.define_internal_local() + self.visit(node.left, scope) + left = scope.ret_expr + self.visit(node.right, scope) + right = scope.ret_expr + self.register_instruction(cil.LessNode(vname, left, right)) + scope.ret_expr = vname @visitor.when(cool.EqualNode) def visit(self, node, scope): @@ -314,8 +326,13 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - #//TODO: Implement EqualNode - pass + vname = self.define_internal_local() + self.visit(node.left, scope) + left = scope.ret_expr + self.visit(node.right, scope) + right = scope.ret_expr + self.register_instruction(cil.EqualNode(vname, left, right)) + scope.ret_expr = vname @visitor.when(cool.PlusNode) def visit(self, node, scope): @@ -323,11 +340,13 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - plus = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.PlusNode(plus, left, right)) + vname = self.define_internal_local() + self.visit(node.left, scope) + left = scope.ret_expr + self.visit(node.right, scope) + right = scope.ret_expr + self.register_instruction(cil.PlusNode(vname, left, right)) + scope.ret_expr = vname @visitor.when(cool.MinusNode) def visit(self, node, scope): @@ -335,11 +354,13 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - minus = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.MinusNode(minus, left, right)) + vname = self.define_internal_local() + self.visit(node.left, scope) + left = scope.ret_expr + self.visit(node.right, scope) + right = scope.ret_expr + self.register_instruction(cil.MinusNode(vname, left, right)) + scope.ret_expr = vname @visitor.when(cool.StarNode) def visit(self, node, scope): @@ -347,11 +368,13 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - star = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.StarNode(star, left, right)) + vname = self.define_internal_local() + self.visit(node.left, scope) + left = scope.ret_expr + self.visit(node.right, scope) + right = scope.ret_expr + self.register_instruction(cil.StarNode(vname, left, right)) + scope.ret_expr = vname @visitor.when(cool.DivNode) def visit(self, node, scope): @@ -359,11 +382,13 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - div = self.define_internal_local() - left = self.visit(node.left, scope) - right = self.visit(node.right, scope) - return self.register_instruction(cil.DivNode(div, left, right)) + vname = self.define_internal_local() + self.visit(node.left, scope) + left = scope.ret_expr + self.visit(node.right, scope) + right = scope.ret_expr + self.register_instruction(cil.DivNode(vname, left, right)) + scope.ret_expr = vname @visitor.when(cool.IsVoidNode) def visit(self, node, scope): @@ -378,8 +403,10 @@ def visit(self, node, scope): ############################### # node.expr -> ExpressionNode ############################### - #//TODO: Implement ComplementNode - pass + vname = self.define_internal_local() + self.visit(node.expr, scope) + self.register_instruction(cil.ComplementNode(vname, scope.ret_expr)) + scope.ret_expr = vname @visitor.when(cool.FunctionCallNode) def visit(self, node, scope): @@ -455,10 +482,3 @@ def visit(self, node, scope): return node.lex - @visitor.when(cool.StringNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - - return node.lex From 0841118647fa6d00b2f789c596ab989e021a6e29 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 16:35:28 -0400 Subject: [PATCH 060/520] [cil] - Update visit of `AtomicNodes` and implement visit of `BoolNode` AtomicNodes: - IntegerNode - IdNode - StringNode --- src/core/cmp/cool_to_cil.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 4dadfa65..e91c755d 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -456,29 +456,39 @@ def visit(self, node, scope): ############################### # node.lex -> str ############################### - - return node.lex + scope.ret_expr = node.lex @visitor.when(cool.IdNode) def visit(self, node, scope): ############################### # node.lex -> str ############################### - param_names = [pn.name for pn in self.current_function.params] if node.lex in param_names: for n in param_names: if node.lex in n.split("_"): - return n - for n in [lv.name for lv in self.current_function.localvars]: - if node.lex in n.split("_"): - return n + scope.ret_expr = n + break + else: + for n in [lv.name for lv in self.current_function.localvars]: + if node.lex in n.split("_"): + scope.ret_expr = n + break @visitor.when(cool.StringNode) def visit(self, node, scope): ############################### # node.lex -> str ############################### - - return node.lex + #//TODO: Check if this value must be added to .DATA or not + scope.ret_expr = node.lex + @visitor.when(cool.BoolNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + if node.lex is 'true': + scope.ret_expr = 1 + else: + scope.ret_expr = 0 From b8777477c3cfe37e4a502174b096fef777c1c860 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 16:36:08 -0400 Subject: [PATCH 061/520] [cil] - Implement visit of `NewNode` --- src/core/cmp/cool_to_cil.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index e91c755d..20c63567 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -447,9 +447,9 @@ def visit(self, node, scope): ############################### # node.type -> str ############################### - instance = self.define_internal_local() - return self.register_instruction(cil.AllocateNode(node.type, instance)) + self.register_instruction(cil.AllocateNode(node.type, instance)) + scope.ret_expr = instance @visitor.when(cool.IntegerNode) def visit(self, node, scope): From 75df533f83b96eb53864b4eaa4af8b7d7cc2ce15 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 22 Apr 2020 17:05:50 -0400 Subject: [PATCH 062/520] [visitor] - Fix an error when `FuncDeclarationNode` is visited Description: - Old grammar implementation of `` return a list of nodes. The current one one return a single node. --- src/core/cmp/visitors.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index cf6e9dcf..edfe2d8d 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -364,10 +364,10 @@ def visit(self, node, scope): for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): scope.define_variable(pname, ptype) - for expr in node.body: - self.visit(expr, scope) + # for expr in node.body: + self.visit(node.body, scope) - last_expr = node.body[-1] + last_expr = node.body last_expr_type = last_expr.computed_type method_rtn_type = self.current_method.return_type @@ -778,10 +778,10 @@ def visit(self, node, scope): for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): scope.define_variable(pname, ptype) - for expr in node.body: - self.visit(expr, scope) + # for expr in node.body: + self.visit(node.body, scope) - last_expr = node.body[-1] + last_expr = node.body last_expr_type = last_expr.computed_type method_rtn_type = self.current_method.return_type From 864f92a69b7a007abb19f0024d630a953c718aa4 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 18:19:19 -0400 Subject: [PATCH 063/520] [cil] - Implement visit of `MemberCallNode` --- src/core/cmp/cool_to_cil.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 20c63567..8d9abe83 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -439,8 +439,22 @@ def visit(self, node, scope): # node.id -> str # node.args -> [ ExpressionNode ... ] ###################################### - #//TODO: Implement MemberCallNode - pass + method = [self.to_function_name(method.name, xtype.name) for method, xtype in self.current_type.all_methods() if method.name == node.id][0] + + args = [] + for arg in node.args: + vname = self.register_local(VariableInfo(f'{node.id}_arg')) + self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) + args.append(cil.ArgNode(vname)) + result = self.register_local(VariableInfo(f'return_value_of_{node.id}')) + + for arg in args: + self.register_instruction(arg) + + self.register_instruction(cil.StaticCallNode(method, result)) + scope.ret_expr = result + @visitor.when(cool.NewNode) def visit(self, node, scope): From 96b3062711119bfb123b26c41f3efe9cdf7c45f0 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 22 Apr 2020 18:33:05 -0400 Subject: [PATCH 064/520] Remove WARNINGS from ply --- src/core/cmp/lex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/lex.py b/src/core/cmp/lex.py index acb025b7..a55a71f3 100644 --- a/src/core/cmp/lex.py +++ b/src/core/cmp/lex.py @@ -136,7 +136,7 @@ def __init__(self): self.build() def build(self, **kwargs): - self.lexer = lex.lex(module=self, **kwargs) + self.lexer = lex.lex(module=self, errorlog=lex.NullLogger(), **kwargs) self.lexer.eof= (1,1) self.comment_level = 0 self.string = "" From ed95addee714d15d9a9ef94a44c421cd2bb901c0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 22 Apr 2020 18:14:54 -0400 Subject: [PATCH 065/520] [visitors] - Fix class naming restrictions --- src/core/cmp/visitors.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index edfe2d8d..08fc4192 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -14,6 +14,7 @@ CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' build_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE' ] +build_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE', 'Object'] #AST Printer class FormatVisitor(object): @@ -189,14 +190,14 @@ def get_type_level(typex): @visitor.when(ClassDeclarationNode) def visit(self, node): - if node.id not in ['AUTO_TYPE', 'SELF_TYPE', 'self']: + if node.id not in build_in_types: try: self.context.create_type(node.id) self.type_level[node.id] = node.parent except SemanticError as ex: self.errors.append(ex.text) else: - self.errors.append(f'{node.id} is a keyword and cannot be a class name') + self.errors.append(f'{node.id} is an invalid class name') # Type Builder class TypeBuilder: From 6c9adc73165c6420593af65855ed4dc170fc981e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 22 Apr 2020 20:48:46 -0400 Subject: [PATCH 066/520] [grammar] - Make an L-Attributed Grammar Remove inherited attributes related to `` and `` --- src/core/cmp/CoolUtils.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 89448a26..9c450cfa 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -145,6 +145,11 @@ class StringNode(AtomicNode): class BoolNode(AtomicNode): pass +def FunctionCallNodeBuilder(obj, calls): + while len(calls): + obj = FunctionCallNode(obj, *calls[0]) + calls.pop(0) + return obj # Grammar @@ -240,12 +245,11 @@ class BoolNode(AtomicNode): # factor_3 %= atom, lambda h, s: s[1] -factor_3 %= atom + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) -factor_3 %= atom + func_call + invocation, lambda h, s: s[3], None, None, lambda h, s: FunctionCallNode(s[1], *s[2]) +factor_3 %= atom + invocation, lambda h, s: FunctionCallNodeBuilder(s[1], s[2]) # -invocation %= func_call, lambda h, s: s[1] -invocation %= func_call + invocation, lambda h, s: s[2], None, lambda h, s: FunctionCallNode(h[0], *s[1]) +invocation %= func_call, lambda h, s: [s[1]] +invocation %= func_call + invocation, lambda h, s: [s[1]] + s[2] # func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) From 556d303adcaab788ecfef4e0ec401d1f4002cb54 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 22 Apr 2020 23:23:28 -0400 Subject: [PATCH 067/520] [visitors] - Review inherit behaviors Description: - When try to inherit of an invalid type, it is replaced by Object. - New list of sealed typos --- src/core/cmp/visitors.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 08fc4192..5d69e8c6 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -13,7 +13,7 @@ INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' -build_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE' ] +sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] build_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE', 'Object'] #AST Printer @@ -218,7 +218,7 @@ def visit(self, node): try: main = self.context.get_type('Main') main.get_method('main') - if main.parent.name != 'Object': + if not main.parent or main.parent.name != 'Object': self.errors.append('The class "Main" cannot inherits from any type.') except SemanticError: self.errors.append('The class "Main" and his method "main" are needed.') @@ -229,14 +229,14 @@ def visit(self, node): self.current_type = self.context.get_type(node.id) if node.parent: - if node.parent in build_in_types: + if node.parent in sealed: self.errors.append(f'Is not possible to inherits from "{node.parent}"') - else: - try: - parent_type = self.context.get_type(node.parent) - self.current_type.set_parent(parent_type) - except SemanticError as ex: - self.errors.append(ex.text) + node.parent = 'Object' + try: + parent_type = self.context.get_type(node.parent) + self.current_type.set_parent(parent_type) + except SemanticError as ex: + self.errors.append(ex.text) for feature in node.features: self.visit(feature) From 3d63b998c68e529f53959f967838805e116051a8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 22 Apr 2020 23:26:56 -0400 Subject: [PATCH 068/520] [visitors] - Fix an error in Inferencer `IsAuto` need an string as param and `NotNode` was passing a type to it --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 5d69e8c6..60a73ba3 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -975,7 +975,7 @@ def visit(self, node, scope): @visitor.when(NotNode) def visit(self, node, scope): self.visit(node.expr, scope) - if IsAuto(node.expr.computed_type): + if IsAuto(node.expr.computed_type.name): self.update(node.expr, scope, self.context.get_type('Bool')) node.computed_type = node.expr.computed_type else: From 44552c434345c44aebbcc32c16950e1d19cbd0e1 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 23:42:51 -0400 Subject: [PATCH 069/520] [cil] - Handle `PARAM self` when return type of function is `SELF_TYPE` --- src/core/cmp/cool_to_cil.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 8d9abe83..a102a829 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -140,8 +140,12 @@ def visit(self, node, scope): self.current_method = self.current_type.get_method(node.id) type_name = self.current_type.name - # (Handle PARAMS) self.current_function = self.register_function(self.to_function_name(self.current_method.name, type_name)) + + # (Handle PARAMS) + #//TODO: Return type SELF_TYPE + if self.current_method.return_type.name is self.current_type.name: + self.register_param(VariableInfo('self', None)) for param_name, _ in node.params: self.register_param(VariableInfo(param_name, None)) From 7164aecb66d81f82eb5aac3f0c46a8f030b3f392 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 22 Apr 2020 23:43:24 -0400 Subject: [PATCH 070/520] [cil] - Update visit of `FunctionCallNode` --- src/core/cmp/cool_to_cil.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index a102a829..c4cbe151 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -421,21 +421,29 @@ def visit(self, node, scope): # node.type -> str ##################################### + args = [] for arg in node.args: - vname = self.define_internal_local() - value = self.visit(arg, scope) - self.register_instruction(cil.AssignNode(vname, value)) - self.register_instruction(cil.ArgNode(vname)) - result = self.define_internal_local() + vname = self.register_local(VariableInfo(f'{node.id}_arg')) + self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) + args.append(cil.ArgNode(vname)) + result = self.register_local(VariableInfo(f'return_value_of_{node.id}')) - obj = self.visit(node.obj, scope) - if type(obj) is cil.AllocateNode: - return self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, obj.type), result)) - elif type(obj) is str: - for n in [lv.name for lv in self.current_function.localvars]: - if obj in n.split("_"): - return n - return None + for arg in args: + self.register_instruction(arg) + + if node.type: + #Call of type @.id(,...,) + at_type = [typex for typex in self.dottypes if typex.name == node.type][0] + #method = [method for method in at_type.methods if method.name == node.id][0] + self.register_instruction(cil.DynamicCallNode(at_type.name, self.to_function_name(node.id, at_type.name), result)) + scope.ret_expr = result + else: + #Call of type .(,...,) + #//TODO: Check if node,obj's type is void, and in that case throw runtime error + _, vtype = [vinfo for vinfo in scope.locals if vinfo.name == node.obj.lex][0] + self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, vtype), result)) + scope.ret_expr = result @visitor.when(cool.MemberCallNode) def visit(self, node, scope): From fa0d4ba3c6ef9c1c329f08ae681512ab6bee538a Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 22 Apr 2020 23:46:46 -0400 Subject: [PATCH 071/520] [visitor] - Change an error variable `VARIABLE_NOT_DEFINED` assume that the variable is inside a method declaration only, and this is false --- src/core/cmp/visitors.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 60a73ba3..b5e9c685 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -9,7 +9,7 @@ SELF_IS_READONLY = 'Variable "self" is read-only.' LOCAL_ALREADY_DEFINED = 'Variable "%s" is already defined in method "%s".' INCOMPATIBLE_TYPES = 'Cannot convert "%s" into "%s".' -VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined in "%s".' +VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined.' INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' @@ -389,7 +389,7 @@ def visit(self, node, scope): elif not (IsAuto(expr_type.name) or expr_type.conforms_to(node_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1)) node_type = ErrorType() node.computed_type = node_type @@ -589,7 +589,7 @@ def visit(self, node, scope): var = scope.find_variable(node.lex) node_type = var.type else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1).replace('%s', self.current_method.name, 1)) + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1)) node_type = ErrorType() node.computed_type = node_type @@ -951,7 +951,7 @@ def visit(self, node, scope): self.update(node.expr, scope, node_type) node.computed_type = node.expr.computed_type else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1)) node.computed_type = ErrorType() @visitor.when(IsVoidNode) @@ -1104,7 +1104,7 @@ def visit(self, node, scope): var = scope.find_variable(node.lex) node_type = var.type else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1).replace('%s', self.current_method.name, 1)) + self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1)) node_type = ErrorType() node.computed_type = node_type From 0b76c019d7c01de9a364f29eb81cee2a811f475c Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 23 Apr 2020 15:36:51 -0400 Subject: [PATCH 072/520] [cil] - Update visit of `StringNode` --- src/core/cmp/cool_to_cil.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index c4cbe151..74ccdd12 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -149,7 +149,6 @@ def visit(self, node, scope): for param_name, _ in node.params: self.register_param(VariableInfo(param_name, None)) - #//TODO: Check whether a value of a expression is a string and see if it must be added to .DATA or not scope.ret_expr = None for instruction in node.body: self.visit(instruction, scope) @@ -506,8 +505,10 @@ def visit(self, node, scope): ############################### # node.lex -> str ############################### - #//TODO: Check if this value must be added to .DATA or not - scope.ret_expr = node.lex + data_node = self.register_data(node.lex) + vname = self.register_local(VariableInfo('msg', None)) + self.register_instruction(cil.LoadNode(vname, data_node.name)) + scope.ret_expr = vname @visitor.when(cool.BoolNode) def visit(self, node, scope): From e3b7f32a5d691556a87eccc673aab94baec01d64 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 15:45:48 -0400 Subject: [PATCH 073/520] [makefile] - Fill the body of `main` rule Default code is in `code.cl` --- src/Makefile | 1 + src/code.cl | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/coolc.sh | 1 + 3 files changed, 69 insertions(+) create mode 100644 src/code.cl diff --git a/src/Makefile b/src/Makefile index 3514ece9..b0d7fc9e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,6 +9,7 @@ DEVELOPERS := Lázaro Raúl Iglesias Vera, Miguel Tenorio Potrony, Mauricio Lá COPYRIGHT := Copyright © 2020: $(DEVELOPERS) main: ## Compiling the compiler :) + ./coolc.sh "code.cl" # Compiling the compiler :) clean: ## Remove temporary files diff --git a/src/code.cl b/src/code.cl new file mode 100644 index 00000000..ec38a776 --- /dev/null +++ b/src/code.cl @@ -0,0 +1,67 @@ +(* hairy . . .*) + +class Foo inherits Bazz { + a : Razz <- case self of + n : Razz => (new Bar); + n : Foo => (new Razz); + n : Bar => n; + esac; + + b : Int <- a.doh() + g.doh() + doh() + printh(); + + doh() : Int { (let i : Int <- h in { h <- h + 2; i; } ) }; + +}; + +class Bar inherits Razz { + + c : Int <- doh(); + + d : Object <- printh(); +}; + + +class Razz inherits Foo { + + e : Bar <- case self of + n : Razz => (new Bar); + n : Bar => n; + esac; + + f : Int <- a@Bazz.doh() + g.doh() + e.doh() + doh() + printh(); + +}; + +class Bazz inherits IO { + + h : Int <- 1; + + g : Foo <- case self of + n : Bazz => (new Foo); + n : Razz => (new Bar); + n : Foo => (new Razz); + n : Bar => n; + esac; + + i : Object <- printh(); + + printh() : Int { { out_int(h); 0; } }; + + doh() : Int { (let i: Int <- h in { h <- h + 1; i; } ) }; +}; + +(* scary . . . *) +class Main { + a : Bazz <- new Bazz; + b : Foo <- new Foo; + c : Razz <- new Razz; + d : Bar <- new Bar; + + main(): String { "do nothing" }; + +}; + + + + + diff --git a/src/coolc.sh b/src/coolc.sh index 2e3ec93d..e3dd2e9c 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,3 +1,4 @@ +#!/bin/bash # Execution details INPUT_FILE=$1 From 48e88d3100464266948a13257e859ecbc2d5d714 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 15:47:10 -0400 Subject: [PATCH 074/520] [visitors] - Class `Main` always has a parent --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index b5e9c685..81c1e079 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -218,7 +218,7 @@ def visit(self, node): try: main = self.context.get_type('Main') main.get_method('main') - if not main.parent or main.parent.name != 'Object': + if main.parent.name != 'Object': self.errors.append('The class "Main" cannot inherits from any type.') except SemanticError: self.errors.append('The class "Main" and his method "main" are needed.') From 337a77bcfaaf618ca0e184d48fb5c818a7141be6 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 16:02:35 -0400 Subject: [PATCH 075/520] [gitingore] - Ignore stuff files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 4acafde1..595fb35f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# 2kodevs ignores +*Stuff/ + # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig # Created by https://www.gitignore.io/api/visualstudiocode,linux,latex,python From 4c8a7c08458bea458b74d30cc95cb0f2b9e6a786 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 16:22:16 -0400 Subject: [PATCH 076/520] [semantic] - Implement a `CoolContenxt` to deal with `SELF_TYPE` --- src/core/cmp/semantic.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 6613abd0..93975a6a 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -243,4 +243,12 @@ def count_auto(self): for var in self.locals: if var.type.name == 'AUTO_TYPE': num += 1 - return num + sum([scp.count_auto() for scp in self.children]) \ No newline at end of file + return num + sum([scp.count_auto() for scp in self.children]) + +class CoolContext(Context): + def get_type(self, name:str, default=ErrorType()): + target = super().get_type(name) + print(target, target.name) + if target.name == "SELF_TYPE": + return default + return target \ No newline at end of file From df3eee666690ca7e812cabdac67d7d18171b5b92 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 16:22:39 -0400 Subject: [PATCH 077/520] [visitors] - Start to use `CoolContext` --- src/core/cmp/visitors.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 81c1e079..c2a055fc 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -3,7 +3,7 @@ from core.cmp.semantic import SemanticError from core.cmp.semantic import Attribute, Method, Type from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType -from core.cmp.semantic import Context, Scope +from core.cmp.semantic import CoolContext as Context, Scope WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' SELF_IS_READONLY = 'Variable "self" is read-only.' @@ -164,7 +164,7 @@ def visit(self, node): self.context.create_type('String').set_parent(obj) self.context.create_type('Bool').set_parent(obj) self.context.create_type('IO').set_parent(obj) - #self.context.create_type('SELF_TYPE') + self.context.create_type('SELF_TYPE') self.context.create_type('AUTO_TYPE') for def_class in node.declarations: @@ -221,8 +221,7 @@ def visit(self, node): if main.parent.name != 'Object': self.errors.append('The class "Main" cannot inherits from any type.') except SemanticError: - self.errors.append('The class "Main" and his method "main" are needed.') - + self.errors.append('The class "Main" and his method "main" are needed.') @visitor.when(ClassDeclarationNode) def visit(self, node): @@ -244,7 +243,7 @@ def visit(self, node): @visitor.when(AttrDeclarationNode) def visit(self, node): try: - attr_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + attr_type = self.context.get_type(node.type, self.current_type) except SemanticError as ex: self.errors.append(ex.text) attr_type = ErrorType() @@ -259,7 +258,7 @@ def visit(self, node): arg_names, arg_types = [], [] for idx, typex in node.params: try: - arg_type = self.context.get_type(typex) if node.type != 'SELF_TYPE' else self.current_type + arg_type = self.context.get_type(typex, self.current_type) except SemanticError as ex: self.errors.append(ex.text) arg_type = ErrorType() @@ -268,7 +267,7 @@ def visit(self, node): arg_types.append(arg_type) try: - ret_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + ret_type = self.context.get_type(node.type, self.current_type) except SemanticError as ex: self.errors.append(ex.text) ret_type = ErrorType() @@ -341,7 +340,7 @@ def visit(self, node, scope): @visitor.when(AttrDeclarationNode) def visit(self, node, scope): try: - node_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + node_type = self.context.get_type(node.type, self.current_type) except SemanticError as ex: self.errors.append(ex.text) node_type = ErrorType() @@ -432,7 +431,7 @@ def visit(self, node, scope): @visitor.when(LetAttributeNode) def visit(self, node, scope): try: - node_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + node_type = self.context.get_type(node.type, self.current_type) except SemanticError as ex: self.errors.append(ex.text) node_type = ErrorType() @@ -872,7 +871,7 @@ def visit(self, node, scope): def visit(self, node, scope): node.scope = scope try: - node_type = self.context.get_type(node.type) if node.type != 'SELF_TYPE' else self.current_type + node_type = self.context.get_type(node.type, self.current_type) except SemanticError as ex: self.errors.append(ex.text) node_type = ErrorType() From 085f8718986df37137630462a60738126c8e6e68 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 16:27:30 -0400 Subject: [PATCH 078/520] [semantic] - Change `SELF_TYPE` only if is required --- src/core/cmp/semantic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 93975a6a..e6ff207c 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -246,9 +246,9 @@ def count_auto(self): return num + sum([scp.count_auto() for scp in self.children]) class CoolContext(Context): - def get_type(self, name:str, default=ErrorType()): + def get_type(self, name:str, default=None, change=False): target = super().get_type(name) print(target, target.name) - if target.name == "SELF_TYPE": + if target.name == "SELF_TYPE" and change: return default return target \ No newline at end of file From 0a71eeb3dd9d229d8e7f7de9c596388e6e16ced8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 16:28:21 -0400 Subject: [PATCH 079/520] [visitors] - Define attributes without change `SELF_TYPE` --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index c2a055fc..67124ca3 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -243,7 +243,7 @@ def visit(self, node): @visitor.when(AttrDeclarationNode) def visit(self, node): try: - attr_type = self.context.get_type(node.type, self.current_type) + attr_type = self.context.get_type(node.type) except SemanticError as ex: self.errors.append(ex.text) attr_type = ErrorType() From a9a1df3647ed13ff81dd8c620967b2f6940accec Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 16:30:37 -0400 Subject: [PATCH 080/520] [visitors] - Define methods without change `SELF_TYPE` --- src/core/cmp/visitors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 67124ca3..ae46efb9 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -258,7 +258,7 @@ def visit(self, node): arg_names, arg_types = [], [] for idx, typex in node.params: try: - arg_type = self.context.get_type(typex, self.current_type) + arg_type = self.context.get_type(typex) except SemanticError as ex: self.errors.append(ex.text) arg_type = ErrorType() @@ -267,7 +267,7 @@ def visit(self, node): arg_types.append(arg_type) try: - ret_type = self.context.get_type(node.type, self.current_type) + ret_type = self.context.get_type(node.type) except SemanticError as ex: self.errors.append(ex.text) ret_type = ErrorType() From dc51023794967ee530980f71ee240e23062f3e4e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 16:44:15 -0400 Subject: [PATCH 081/520] [visitors] - Define methods of builtin classes --- src/core/cmp/visitors.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index ae46efb9..3e00e522 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -14,7 +14,29 @@ CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] -build_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE', 'Object'] +built_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE', 'Object'] + +def define_built_in_types(context): + obj = context.create_type('Object') + i = context.create_type('Int').set_parent(obj) + s = context.create_type('String').set_parent(obj) + context.create_type('Bool').set_parent(obj) + io = context.create_type('IO').set_parent(obj) + st = context.create_type('SELF_TYPE') + context.create_type('AUTO_TYPE') + + obj.define_method('abort', [], [], obj) + obj.define_method('type_name', [], [], s) + obj.define_method('copy', [], [], st) + + io.define_method('out_string', ['x'], [s], st) + io.define_method('out_int', ['x'], [i], st) + io.define_method('in_string', [], [], s) + io.define_method('in_int', [], [], i) + + s.define_method('length', [], [], i) + s.define_method('concat', ['s'], [s], s) + s.define_method('substr', ['i', 'l'], [i, i], s) #AST Printer class FormatVisitor(object): @@ -159,13 +181,7 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node): self.context = Context() - obj = self.context.create_type('Object') - self.context.create_type('Int').set_parent(obj) - self.context.create_type('String').set_parent(obj) - self.context.create_type('Bool').set_parent(obj) - self.context.create_type('IO').set_parent(obj) - self.context.create_type('SELF_TYPE') - self.context.create_type('AUTO_TYPE') + define_built_in_types(self.context) for def_class in node.declarations: self.visit(def_class) From 4e0d9e16ad18c5f3b573680322a33fa386df03c0 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 23 Apr 2020 17:02:49 -0400 Subject: [PATCH 082/520] [cil] - Fix return of visit of `WhileNode`. It must be void --- src/core/cmp/cool_to_cil.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 74ccdd12..1ede176b 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -196,7 +196,6 @@ def visit(self, node, scope): # node.condition -> ExpressionNode # node.body -> ExpressionNode ################################### - vret = self.register_local(VariableInfo('while_value', None)) while_label_node = self.register_label('while_label') loop_label_node = self.register_label('loop_label') @@ -211,13 +210,13 @@ def visit(self, node, scope): #Label loop self.register_instruction(loop_label_node) self.visit(node.body, scope) - self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) #GOTO while self.register_instruction(cil.GotoNode(while_label_node.label)) #Label pool self.register_instruction(pool_label_node) - scope.ret_expr = vret + #The result of a while loop is void + scope.ret_expr = None @visitor.when(cool.BlockNode) def visit(self, node, scope): @@ -466,7 +465,6 @@ def visit(self, node, scope): self.register_instruction(cil.StaticCallNode(method, result)) scope.ret_expr = result - @visitor.when(cool.NewNode) def visit(self, node, scope): ############################### From dd668aae7e2e0699c7e2cba0d66f05168ef28654 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 17:35:01 -0400 Subject: [PATCH 083/520] [visitors] - Working with `fixed_type` `fixed_type` usually is the same object type, but if object type is `SELF_TYPE` then the `fixed_type` is the current class --- src/core/cmp/semantic.py | 4 ++-- src/core/cmp/visitors.py | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index e6ff207c..66a63d51 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -246,9 +246,9 @@ def count_auto(self): return num + sum([scp.count_auto() for scp in self.children]) class CoolContext(Context): - def get_type(self, name:str, default=None, change=False): + def get_type(self, name:str, default=None): target = super().get_type(name) print(target, target.name) - if target.name == "SELF_TYPE" and change: + if target.name == "SELF_TYPE" and default: return default return target \ No newline at end of file diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 3e00e522..f952d2bc 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -13,6 +13,7 @@ INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' +ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] built_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE', 'Object'] @@ -38,6 +39,12 @@ def define_built_in_types(context): s.define_method('concat', ['s'], [s], s) s.define_method('substr', ['i', 'l'], [i, i], s) +def match(type1, type2): + return IsAuto(type1.name) or type1.conforms_to(type2) + +def fixed_type(type1, type2): + return type1 if type1.name != ST else type2 + #AST Printer class FormatVisitor(object): @visitor.on('node') @@ -368,7 +375,7 @@ def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type - if not (IsAuto(expr_type.name) or expr_type.conforms_to(node_type)): + if not match(expr_type, node_type): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) node.computed_type = node_type @@ -385,9 +392,10 @@ def visit(self, node, scope): last_expr = node.body last_expr_type = last_expr.computed_type - method_rtn_type = self.current_method.return_type + method_rtn_type = fixed_type(self.current_method.return_type, self.current_type) - if not last_expr_type.conforms_to(method_rtn_type): + # //TODO: be carefull whit void + if not match(last_expr_type, method_rtn_type): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', last_expr_type.name, 1).replace('%s', method_rtn_type.name, 1)) @visitor.when(AssignNode) @@ -397,11 +405,11 @@ def visit(self, node, scope): if scope.is_defined(node.id): var = scope.find_variable(node.id) - node_type = var.type + node_type = fixed_type(var.type, self.current_type) if var.name == 'self': self.errors.append(SELF_IS_READONLY) - elif not (IsAuto(expr_type.name) or expr_type.conforms_to(node_type)): + elif not match(expr_type, node_type): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) else: self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1)) From 14964be2689849d43b7095ad1bf15515059a6551 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 19:44:13 -0400 Subject: [PATCH 084/520] [visitors] - Type checker review Description: - Mainly check that `SELF_TYPE` is used correctly. - Remove CoolContext. --- src/core/cmp/semantic.py | 10 +--- src/core/cmp/visitors.py | 110 ++++++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 46 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 66a63d51..6613abd0 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -243,12 +243,4 @@ def count_auto(self): for var in self.locals: if var.type.name == 'AUTO_TYPE': num += 1 - return num + sum([scp.count_auto() for scp in self.children]) - -class CoolContext(Context): - def get_type(self, name:str, default=None): - target = super().get_type(name) - print(target, target.name) - if target.name == "SELF_TYPE" and default: - return default - return target \ No newline at end of file + return num + sum([scp.count_auto() for scp in self.children]) \ No newline at end of file diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index f952d2bc..b1d1ceb8 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -3,7 +3,7 @@ from core.cmp.semantic import SemanticError from core.cmp.semantic import Attribute, Method, Type from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType -from core.cmp.semantic import CoolContext as Context, Scope +from core.cmp.semantic import Context, Scope WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' SELF_IS_READONLY = 'Variable "self" is read-only.' @@ -15,14 +15,17 @@ ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] -built_in_types = [ 'Int', 'String', 'Bool', 'IO', 'SELF_TYPE', 'AUTO_TYPE', 'Object'] +built_in_types = [ 'Int', 'String', 'Bool', 'Object', 'IO', 'SELF_TYPE', 'AUTO_TYPE'] def define_built_in_types(context): obj = context.create_type('Object') - i = context.create_type('Int').set_parent(obj) - s = context.create_type('String').set_parent(obj) + i = context.create_type('Int') + i.set_parent(obj) + s = context.create_type('String') + s.set_parent(obj) context.create_type('Bool').set_parent(obj) - io = context.create_type('IO').set_parent(obj) + io = context.create_type('IO') + io.set_parent(obj) st = context.create_type('SELF_TYPE') context.create_type('AUTO_TYPE') @@ -213,7 +216,7 @@ def get_type_level(typex): @visitor.when(ClassDeclarationNode) def visit(self, node): - if node.id not in build_in_types: + if node.id not in built_in_types: try: self.context.create_type(node.id) self.type_level[node.id] = node.parent @@ -363,7 +366,7 @@ def visit(self, node, scope): @visitor.when(AttrDeclarationNode) def visit(self, node, scope): try: - node_type = self.context.get_type(node.type, self.current_type) + node_type = self.context.get_type(node.type) except SemanticError as ex: self.errors.append(ex.text) node_type = ErrorType() @@ -373,9 +376,9 @@ def visit(self, node, scope): return self.visit(node.expr, scope) - expr_type = node.expr.computed_type + expr_type = fixed_type(node.expr.computed_type, self.current_type) - if not match(expr_type, node_type): + if not match(expr_type, fixed_type(node_type, self.current_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) node.computed_type = node_type @@ -391,7 +394,7 @@ def visit(self, node, scope): self.visit(node.body, scope) last_expr = node.body - last_expr_type = last_expr.computed_type + last_expr_type = fixed_type(last_expr.computed_type, self.current_type) method_rtn_type = fixed_type(self.current_method.return_type, self.current_type) # //TODO: be carefull whit void @@ -405,11 +408,11 @@ def visit(self, node, scope): if scope.is_defined(node.id): var = scope.find_variable(node.id) - node_type = fixed_type(var.type, self.current_type) + node_type = var.type if var.name == 'self': self.errors.append(SELF_IS_READONLY) - elif not match(expr_type, node_type): + elif not match(fixed_type(expr_type, self.current_type), fixed_type(node_type, self.current_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) else: self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1)) @@ -424,7 +427,8 @@ def visit(self, node, scope): types_list = [] for case in node.branches: - self.visit(case.expr, scope) + self.visit(case.expr, scope.create_child()) + # //TODO: be carefull whit void has_auto |= IsAuto(case.expr.computed_type.name) has_error |= case.expr.computed_type.name == '' types_list.append(case.expr.computed_type) @@ -434,7 +438,10 @@ def visit(self, node, scope): elif has_auto: node.computed_type = self.context.get_type('AUTO_TYPE') else: - node.computed_type = LCA(types_list, self.context) + if all([t.name == ST for t in types_list]): + node.computed_type = types_list[0] + else: + node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) @visitor.when(CaseExpressionNode) def visit(self, node, scope): @@ -455,7 +462,7 @@ def visit(self, node, scope): @visitor.when(LetAttributeNode) def visit(self, node, scope): try: - node_type = self.context.get_type(node.type, self.current_type) + node_type = self.context.get_type(node.type) except SemanticError as ex: self.errors.append(ex.text) node_type = ErrorType() @@ -472,9 +479,8 @@ def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type - if not(IsAuto(expr_type.name) or expr_type.conforms_to(node_type)): + if not match(fixed_type(expr_type, self.current_type), fixed_type(node_type, self.current_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) - node.computed_type = node_type @@ -491,12 +497,17 @@ def visit(self, node, scope): if node.else_body: self.visit(node.else_body, scope) + # //TODO: Check priority preference between AUTO and Error if IsAuto(node.if_body.computed_type.name) or IsAuto(node.else_body.computed_type.name): node.computed_type = self.context.get_type('AUTO_TYPE') elif '' in [node.if_body.computed_type.name, node.else_body.computed_type.name]: node.computed_type = ErrorType() else: - node.computed_type = LCA([node.if_body.computed_type, node.else_body.computed_type], self.context) + types_list = [node.if_body.computed_type, node.else_body.computed_type] + if all([t.name == ST for t in types_list]): + node.computed_type = types_list[0] + else: + node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) @visitor.when(BlockNode) def visit(self, node, scope): @@ -522,17 +533,12 @@ def visit(self, node, scope): self.visit(node.obj, scope) obj_type = node.obj.computed_type - if node.type: - try: + try: + if node.type: if IsAuto(node.type): raise SemanticError('Is not possible to use AUTO_TYPE in a cast') if not obj_type.conforms_to(self.context.get_type(node.type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', obj_type.name, 1).replace('%s', node.type, 1)) - except SemanticError as ex: - self.errors.append(ex.text) - - try: - if node.type: obj_method = self.context.get_type(node.type).get_method(node.id) else: obj_method = obj_type.get_method(node.id) @@ -540,9 +546,9 @@ def visit(self, node, scope): if len(node.args) == len(obj_method.param_types): for arg, param_type in zip(node.args, obj_method.param_types): self.visit(arg, scope) - arg_type = arg.computed_type + arg_type = fixed_type(arg.computed_type, self.current_type) - if not (IsAuto(arg_type.name) or arg_type.conforms_to(param_type)): + if not match(arg_type, fixed_type(param_type, self.current_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) else: self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') @@ -564,9 +570,9 @@ def visit(self, node, scope): if len(node.args) == len(obj_method.param_types): for arg, param_type in zip(node.args, obj_method.param_types): self.visit(arg, scope) - arg_type = arg.computed_type + arg_type = fixed_type(arg.computed_type, self.current_type) - if not (IsAuto(arg_type.name) or arg_type.conforms_to(param_type)): + if not match(arg_type, fixed_type(param_type, self.current_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) else: self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') @@ -586,7 +592,7 @@ def visit(self, node, scope): self.visit(node.right, scope) right_type = node.right.computed_type - if not (IsAuto(left_type.name) or left_type.conforms_to(IntType())) or not (IsAuto(right_type.name) or right_type.conforms_to(IntType())): + if not (left_type.name == 'Int' and right_type.name == 'Int'): self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) node_type = ErrorType() else: @@ -619,7 +625,7 @@ def visit(self, node, scope): @visitor.when(NewNode) def visit(self, node, scope): - if node.type in build_in_types: + if node.type in built_in_types[:3]: self.errors.append(f'It cannot be initialized a {node.type} with the new keyword') node.computed_type = ErrorType() else: @@ -639,7 +645,7 @@ def visit(self, node, scope): @visitor.when(ComplementNode) def visit(self, node, scope): self.visit(node.expr, scope) - if not (IsAuto(node.expr.computed_type.name) or node.expr.computed_type.name != 'Int'): + if node.expr.computed_type.name != 'Int': self.errors.append("Complement works only for Int") node.computed_type = ErrorType() else: @@ -648,12 +654,28 @@ def visit(self, node, scope): @visitor.when(NotNode) def visit(self, node, scope): self.visit(node.expr, scope) - if not (IsAuto(node.expr.computed_type.name) or node.expr.computed_type.name != 'Bool'): + if node.expr.computed_type.name != 'Bool': self.errors.append("Not operator works only for Bool") node.computed_type = ErrorType() else: node.computed_type = self.context.get_type('Bool') + @visitor.when(EqualNode) + def visit(self, node, scope): + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + + if not (left_type.name == right_type.name and left_type.name in built_in_types[:3]): + self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) + node_type = ErrorType() + else: + node_type = right_type + + node.computed_type = node_type + # Type Inference Visitor class InferenceVisitor(object): def __init__(self, context, errors=[]): @@ -707,7 +729,11 @@ def update(self, node, scope, ntype): names = [node.if_body.computed_type.name, node.else_body.computed_type.name] if 'AUTO_TYPE' not in names and '' not in names: - node.computed_type = LCA([node.if_body.computed_type, node.else_body.computed_type], self.context) + types_list = [node.if_body.computed_type, node.else_body.computed_type] + if all([t.name == ST for t in types_list]): + node.computed_type = types_list[0] + else: + node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) else: if '' in names: node.computed_type = ErrorType() @@ -731,7 +757,10 @@ def update(self, node, scope, ntype): elif has_auto: node.computed_type = self.context.get_type('AUTO_TYPE') else: - node.computed_type = LCA(types_list) + if all([t.name == ST for t in types_list]): + node.computed_type = types_list[0] + else: + node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) @visitor.when(CaseExpressionNode) def update(self, node, scope, ntype): @@ -843,7 +872,11 @@ def visit(self, node, scope): self.visit(node.else_body, scope) names = [node.if_body.computed_type.name, node.else_body.computed_type.name] if 'AUTO_TYPE' not in names and '' not in names: - node.computed_type = LCA([node.if_body.computed_type, node.else_body.computed_type], self.context) + types_list = [node.if_body.computed_type, node.else_body.computed_type] + if all([t.name == ST for t in types_list]): + node.computed_type = types_list[0] + else: + node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) else: if '' in names: node.computed_type = ErrorType() @@ -942,7 +975,10 @@ def visit(self, node, scope): elif has_auto: node.computed_type = self.context.get_type('AUTO_TYPE') else: - node.computed_type = LCA(types_list, self.context) + if all([t.name == ST for t in types_list]): + node.computed_type = types_list[0] + else: + node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) @visitor.when(CaseExpressionNode) def visit(self, node, scope): From bc3976e7d887e2d2305504105ad9414a45ca44a0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 21:32:32 -0400 Subject: [PATCH 085/520] [cool] - Add `ComparisonNode` --- src/core/cmp/CoolUtils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 9c450cfa..32684522 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -83,13 +83,16 @@ def __init__(self, left, right): self.left = left self.right = right -class LessEqualNode(BinaryNode): +class ComparisonNode(BinaryNode): pass -class LessNode(BinaryNode): +class LessEqualNode(ComparisonNode): pass -class EqualNode(BinaryNode): +class LessNode(ComparisonNode): + pass + +class EqualNode(ComparisonNode): pass class ArithmeticNode(BinaryNode): From eb9b3c77771b5b0c812a9e5722735f62f94655b8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 23 Apr 2020 21:37:46 -0400 Subject: [PATCH 086/520] [visitors] - `TypeInferencer` initial review `TypeInferencer` is a kind of `TypeChecker`, cause that new implementation inherits form this class. - Deleted methods in common whit `TypeChecker` - Implement visitors on `ComparisonNode` --- src/core/cmp/visitors.py | 185 ++++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 99 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index b1d1ceb8..4edf5875 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -49,7 +49,7 @@ def fixed_type(type1, type2): return type1 if type1.name != ST else type2 #AST Printer -class FormatVisitor(object): +class FormatVisitor: @visitor.on('node') def visit(self, node, tabs): pass @@ -178,7 +178,7 @@ def visit(self, node, tabs=0): return '\t' * tabs + f'\\__NewNode: new {node.type}()' # Type Collector -class TypeCollector(object): +class TypeCollector: def __init__(self, errors=[]): self.context = None self.errors = errors @@ -380,6 +380,7 @@ def visit(self, node, scope): if not match(expr_type, fixed_type(node_type, self.current_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + node_type = ErrorType() node.computed_type = node_type @@ -471,7 +472,7 @@ def visit(self, node, scope): scope.define_variable(node.id, node_type) else: self.errors.append(LOCAL_ALREADY_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) - + # //TODO: use ErrorType here and in Inferencer if not node.expr: node.computed_type = node.type return @@ -481,6 +482,7 @@ def visit(self, node, scope): if not match(fixed_type(expr_type, self.current_type), fixed_type(node_type, self.current_type)): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + # //TODO: Same here node.computed_type = node_type @@ -584,7 +586,7 @@ def visit(self, node, scope): node.computed_type = node_type - @visitor.when(BinaryNode) + @visitor.when(ArithmeticNode) def visit(self, node, scope): self.visit(node.left, scope) left_type = node.left.computed_type @@ -600,6 +602,22 @@ def visit(self, node, scope): node.computed_type = node_type + @visitor.when(ComparisonNode) + def visit(self, node, scope): + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + + if not (left_type.name == 'Bool' and right_type.name == 'Bool'): + self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) + node_type = ErrorType() + else: + node_type = BoolType() + + node.computed_type = node_type + @visitor.when(IntegerNode) def visit(self, node, scope): node.computed_type = IntType() @@ -677,7 +695,7 @@ def visit(self, node, scope): node.computed_type = node_type # Type Inference Visitor -class InferenceVisitor(object): +class InferenceVisitor(TypeChecker): def __init__(self, context, errors=[]): self.context = context self.current_type = None @@ -688,10 +706,6 @@ def __init__(self, context, errors=[]): def update(self, node, scope, ntype): pass - @visitor.when(Node) - def update(self, node, scope, ntype): - pass - @visitor.when(FunctionCallNode) def update(self, node, scope, ntype): obj_type = node.obj.computed_type @@ -781,23 +795,13 @@ def update(self, node, scope, ntype): def visit(self, node, scope): pass - @visitor.when(ProgramNode) - def visit(self, node, scope=None): - scope = Scope() - for declaration in node.declarations: - self.visit(declaration, scope.create_child()) - return scope + @visitor.on(Node) + def visit(self, node, scope): + super().visit(node, scope) @visitor.when(ClassDeclarationNode) def visit(self, node, scope): - self.current_type = self.context.get_type(node.id) - - scope.define_variable('self', self.current_type) - for attr in self.current_type.attributes: - scope.define_variable(attr.name, attr.type) - - for feature in node.features: - self.visit(feature, scope.create_child()) + super().visit(node, scope) for idx, attr in enumerate(self.current_type.attributes): actual_type = scope.find_variable(attr.name).type @@ -812,16 +816,14 @@ def visit(self, node, scope): if IsAuto(node.type): if not IsAuto(node.expr.computed_type.name): scope.find_variable(node.id).type = node.expr.computed_type + node.computed_type = node.expr.computed_type else: if IsAuto(node.expr.computed_type.name): self.update(node.expr, scope, node.type) else: if not node.expr.computed_type.conforms_to(node.computed_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', node.expr.computed_type.name, 1).replace('%s', node.computed_type.name, 1)) - node.computed_type = ErrorType() - return - - node.computed_type = node.expr.computed_type + self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + node.computed_type = ErrorType() @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -897,30 +899,15 @@ def visit(self, node, scope): self.visit(node.body, scope) node.computed_type = VoidType() - @visitor.when(BlockNode) - def visit(self, node, scope): - for expr in node.exprs: - self.visit(expr, scope) - - last_expr = node.exprs[-1] - node.computed_type = last_expr.computed_type - @visitor.when(LetInNode) def visit(self, node, scope): - child = scope.create_child() - node.scope = child - - for attr in node.let_body: - self.visit(attr, child) - - self.visit(node.in_body, child) - node.computed_type = node.in_body.computed_type + super().visit(node, scope) for attr in node.let_body: type_name = attr.type if attr.computed_type.name == '': continue - actual_type = child.find_variable(attr.id).type + actual_type = node.scope.find_variable(attr.id).type if type_name != actual_type.name: attr.type = actual_type.name @@ -928,7 +915,7 @@ def visit(self, node, scope): def visit(self, node, scope): node.scope = scope try: - node_type = self.context.get_type(node.type, self.current_type) + node_type = self.context.get_type(node.type) except SemanticError as ex: self.errors.append(ex.text) node_type = ErrorType() @@ -958,33 +945,6 @@ def visit(self, node, scope): self.update(node.expr, scope, node_type) node.computed_type = node.expr.computed_type - @visitor.when(CaseOfNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - has_auto = has_error = False - - types_list = [] - for case in node.branches: - self.visit(case.expr, scope) - has_auto |= IsAuto(case.expr.computed_type.name) - has_error |= case.expr.computed_type.name == '' - types_list.append(case.expr.computed_type) - - if has_error: - node.computed_type = ErrorType() - elif has_auto: - node.computed_type = self.context.get_type('AUTO_TYPE') - else: - if all([t.name == ST for t in types_list]): - node.computed_type = types_list[0] - else: - node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) - - @visitor.when(CaseExpressionNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - node.computed_type = node.expr.computed_type - @visitor.when(AssignNode) def visit(self, node, scope): self.visit(node.expr, scope) @@ -1013,11 +973,6 @@ def visit(self, node, scope): self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1)) node.computed_type = ErrorType() - @visitor.when(IsVoidNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - node.computed_type = self.context.get_type('Bool') - @visitor.when(ComplementNode) def visit(self, node, scope): self.visit(node.expr, scope) @@ -1144,37 +1099,69 @@ def visit(self, node, scope): node_type = ErrorType() node.computed_type = node_type - - @visitor.when(IntegerNode) + + @visitor.when(ArithmeticNode) def visit(self, node, scope): - node.computed_type = IntType() + self.visit(node.left, scope) + left_type = node.left.computed_type - @visitor.when(StringNode) - def visit(self, node, scope): - node.computed_type = StringType() + self.visit(node.right, scope) + right_type = node.right.computed_type + + if IsAuto(left_type.name): + self.update(node.left, scope, self.context.get_type('Int')) + left_type = node.left.computed_type + + if IsAuto(right_type.name): + self.update(node.right, scope, self.context.get_type('Int')) + right_type = node.right.computed_type - @visitor.when(BoolNode) - def visit(self, node, scope): - node.computed_type = BoolType() + if not (IsAuto(left_type.name) or left_type.conforms_to(IntType())) or not (IsAuto(right_type.name) or right_type.conforms_to(IntType())): + self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) + node_type = ErrorType() + else: + node_type = IntType() + + node.computed_type = node_type - @visitor.when(IdNode) + @visitor.when(ComparisonNode) def visit(self, node, scope): - if scope.is_defined(node.lex): - var = scope.find_variable(node.lex) - node_type = var.type - else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1)) - node_type = ErrorType() + self.visit(node.left, scope) + left_type = node.left.computed_type + self.visit(node.right, scope) + right_type = node.right.computed_type + + if IsAuto(left_type.name): + self.update(node.left, scope, self.context.get_type('Bool')) + left_type = node.left.computed_type + + if IsAuto(right_type.name): + self.update(node.right, scope, self.context.get_type('Bool')) + right_type = node.right.computed_type + + if not (match(left_type, BoolType()) and match(right_type, BoolType())): + self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) + node_type = ErrorType() + else: + node_type = BoolType() + node.computed_type = node_type - @visitor.when(NewNode) + @visitor.when(EqualNode) def visit(self, node, scope): - try: - node_type = self.context.get_type(node.type) - except SemanticError as ex: - self.errors.append(ex.text) + # //TODO: What to do when left and rigth are AUTO + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + + if not (left_type.name == right_type.name and left_type.name in built_in_types[:3]): + self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) node_type = ErrorType() + else: + node_type = right_type node.computed_type = node_type From 78dcff6438567ea48376e7ceec75133eac85c8dd Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 24 Apr 2020 15:13:48 -0400 Subject: [PATCH 087/520] [visitors] - Fix compilation error in Inferencer --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 4edf5875..37518ebf 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -795,7 +795,7 @@ def update(self, node, scope, ntype): def visit(self, node, scope): pass - @visitor.on(Node) + @visitor.when(Node) def visit(self, node, scope): super().visit(node, scope) From a351d45d887decd2687af09180ab1b7ac0d87a78 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 24 Apr 2020 17:33:57 -0400 Subject: [PATCH 088/520] [ast] - Ast nodes use the tokens instead of their lex property --- src/core/cmp/CoolUtils.py | 79 +++++++++++++++++++++++--------------- src/core/cmp/evaluation.py | 4 +- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 32684522..6dde1dcc 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -2,6 +2,8 @@ from core.cmp.functions import LR1Parser from core.cmp.utils import Token, tokenizer +empty_token = Token("", "") + # AST Classes class Node: pass @@ -14,29 +16,36 @@ class DeclarationNode(Node): pass class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent='Object'): - self.id = idx - self.parent = parent + def __init__(self, idx, features, parent=Token('Object', "")): + self.id = idx.lex + self.tid = idx + self.parent = parent.lex + self.tparent = parent self.features = features class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expr=None): - self.id = idx - self.type = typex + def __init__(self, idx, typex, expr=None, arrow=empty_token): + self.id = idx.lex + self.tid = idx + self.type = typex.lex + self.ttype = typex + self.arrow = arrow self.expr = expr class FuncDeclarationNode(DeclarationNode): def __init__(self, idx, params, return_type, body): - self.id = idx + self.id = idx.lex + self.tid = idx self.params = params - self.type = return_type + self.type = return_type.lex + self.ttype = return_type self.body = body class ExpressionNode(Node): pass class IfThenElseNode(ExpressionNode): - def __init__(self, condition, if_body, else_body): + def __init__(self, condition, if_body, else_body=None): self.condition = condition self.if_body = if_body self.else_body = else_body @@ -68,18 +77,21 @@ class LetAttributeNode(AttrDeclarationNode): class AssignNode(ExpressionNode): def __init__(self, idx, expr): - self.id = idx + self.id = idx.lex + self.tid = idx self.expr= expr class UnaryNode(ExpressionNode): - def __init__(self, expr): + def __init__(self, expr, symbol): + self.symbol = symbol self.expr = expr class NotNode(UnaryNode): pass class BinaryNode(ExpressionNode): - def __init__(self, left, right): + def __init__(self, left, right, symbol): + self.symbol = symbol self.left = left self.right = right @@ -117,24 +129,29 @@ class ComplementNode(UnaryNode): pass class FunctionCallNode(ExpressionNode): - def __init__(self, obj, idx, args, typex=None): + def __init__(self, obj, idx, args, typex=empty_token): self.obj = obj - self.id = idx + self.id = idx.lex + self.tid = idx self.args = args - self.type = typex + self.type = typex.lex + self.ttype = typex class MemberCallNode(ExpressionNode): def __init__(self, idx, args): - self.id = idx + self.id = idx.lex + self.tid = idx self.args = args class NewNode(ExpressionNode): def __init__(self, typex): - self.type = typex + self.type = typex.lex + self.ttype = typex class AtomicNode(ExpressionNode): - def __init__(self, lex): - self.lex = lex + def __init__(self, token): + self.lex = token.lex + self.token = token class IntegerNode(AtomicNode): pass @@ -195,7 +212,7 @@ def FunctionCallNodeBuilder(obj, calls): # feature %= idx + colon + typex + semi, lambda h, s: AttrDeclarationNode(s[1], s[3]) -feature %= idx + colon + typex + larrow + expr + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]) +feature %= idx + colon + typex + larrow + expr + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5], s[4]) # feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) @@ -209,10 +226,10 @@ def FunctionCallNodeBuilder(obj, calls): param %= idx + colon + typex, lambda h, s: (s[1], s[3]) # -expr %= notx + expr, lambda h, s: NotNode(s[2]) -expr %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3]) -expr %= expr + less + expr, lambda h, s: LessNode(s[1], s[3]) -expr %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3]) +expr %= notx + expr, lambda h, s: NotNode(s[2], s[1]) +expr %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +expr %= expr + less + expr, lambda h, s: LessNode(s[1], s[3], s[2]) +expr %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3], s[2]) expr %= arith, lambda h, s: s[1] block %= expr + semi, lambda h, s: [s[1]] @@ -229,21 +246,21 @@ def FunctionCallNodeBuilder(obj, calls): case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] # -arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3]) -arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3]) +arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3], s[2]) +arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3], s[2]) arith %= term, lambda h, s: s[1] # -term %= term + star + factor, lambda h, s: StarNode(s[1], s[3]) -term %= term + div + factor, lambda h, s: DivNode(s[1], s[3]) +term %= term + star + factor, lambda h, s: StarNode(s[1], s[3], s[2]) +term %= term + div + factor, lambda h, s: DivNode(s[1], s[3], s[2]) term %= factor, lambda h, s: s[1] # -factor %= isvoid + factor_2, lambda h, s: IsVoidNode(s[2]) +factor %= isvoid + factor_2, lambda h, s: IsVoidNode(s[2], s[1]) factor %= factor_2, lambda h, s: s[1] # -factor_2 %= compl + factor_3, lambda h, s: ComplementNode(s[2]) +factor_2 %= compl + factor_3, lambda h, s: ComplementNode(s[2], s[1]) factor_2 %= factor_3, lambda h, s: s[1] # @@ -272,7 +289,7 @@ def FunctionCallNodeBuilder(obj, calls): atom %= integer, lambda h, s: IntegerNode(s[1]) atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) -atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) +atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4]) atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) diff --git a/src/core/cmp/evaluation.py b/src/core/cmp/evaluation.py index 1ac49378..cadd324d 100644 --- a/src/core/cmp/evaluation.py +++ b/src/core/cmp/evaluation.py @@ -11,10 +11,10 @@ def evaluate_reverse_parse(right_parse, operations, tokens): for operation in operations: if operation == ShiftReduceParser.SHIFT: token = next(tokens) - stack.append(token.lex) + stack.append(token) elif operation == ShiftReduceParser.REDUCE: production = next(right_parse) - head, body = production + _, body = production attributes = production.attributes assert all(rule is None for rule in attributes[1:]), 'There must be only synteticed attributes.' rule = attributes[0] From bbde437b0e77ec0619d42a63ad7e069ac43eb7e4 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 24 Apr 2020 18:42:20 -0400 Subject: [PATCH 089/520] [visitors] - Errors in `TypeCollector` return the problematic token --- src/core/cmp/visitors.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 37518ebf..6e81b25c 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -183,6 +183,7 @@ def __init__(self, errors=[]): self.context = None self.errors = errors self.type_level = {} + self.parent = {} @visitor.on('node') def visit(self, node): @@ -204,7 +205,7 @@ def get_type_level(typex): return 0 if parent == 0: - self.errors.append('Cyclic heritage.') + self.errors.append(('Cyclic heritage.', self.parent[typex])) elif type(parent) is not int: self.type_level[typex] = 0 if parent else 1 if type(parent) is str: @@ -220,10 +221,11 @@ def visit(self, node): try: self.context.create_type(node.id) self.type_level[node.id] = node.parent + self.parent[node.id] = node.tparent except SemanticError as ex: self.errors.append(ex.text) else: - self.errors.append(f'{node.id} is an invalid class name') + self.errors.append((f'{node.id} is an invalid class name', node.tid)) # Type Builder class TypeBuilder: From 3a6019d5c8c38b3b18a4758f23274c0dfeefc78e Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 24 Apr 2020 21:57:28 -0400 Subject: [PATCH 090/520] [cil] - Add nodes to CIL AST for predefined method. Update existing ones --- src/core/cmp/cil.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index e3c65825..c10d2de9 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -134,17 +134,33 @@ def __init__(self, dest, msg): self.dest = dest self.msg = msg +class TypeNameNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + +class CopyNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + class LengthNode(InstructionNode): - pass + def __init__(self, dest, source): + self.dest = dest + self.source = source class ConcatNode(InstructionNode): - pass - -class PrefixNode(InstructionNode): - pass + def __init__(self, dest, prefix, suffix): + self.dest = dest + self.prefix = prefix + self.suffix = suffix class SubstringNode(InstructionNode): - pass + def __init__(self, dest, str_value, index, length): + self.dest = dest + self.str_value = str_value + self.index = index + self.length = length class ToStrNode(InstructionNode): def __init__(self, dest, ivalue): From 738f075c6bb4cfa7fb9bb2e972e8998ba7928eae Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 24 Apr 2020 21:58:54 -0400 Subject: [PATCH 091/520] [cil] - Add `vself` property to base CIL AST, representing `self` value --- src/core/cmp/cool_to_cil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 1ede176b..46cb2fb0 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -12,6 +12,7 @@ def __init__(self, context): self.current_method = None self.current_function = None self.context = context + self.vself = VariableInfo('self', None) @property def params(self): From 840a92d4275f93d8cae39e218e4152ccbc86f139 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 24 Apr 2020 22:01:36 -0400 Subject: [PATCH 092/520] [cil] - Add `register_built_in` method to set built in types with its methods `abort` method of Object is left --- src/core/cmp/cool_to_cil.py | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 46cb2fb0..a9a68e77 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -72,6 +72,87 @@ def register_label(self, label): self.current_function.labels_count += 1 return cil.LabelNode(lname) + def register_built_in(self): + #Object + type_node = self.register_type('Object') + + self.current_function = self.register_function(self.to_function_name('abort', 'Object')) + #//TODO: Code of abort + + self.current_function = self.register_function(self.to_function_name('type_name', 'Object')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ArgNode(self.vself.name)) + self.register_instruction(cil.TypeNameNode(result, self.vself.name)) + self.register_instruction(cil.ReturnNode(result)) + + self.current_function = self.register_function(self.to_function_name('copy', 'Object')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ArgNode(self.vself.name)) + self.register_instruction(cil.CopyNode(result, self.vself.name)) + self.register_instruction(cil.ReturnNode(result)) + + type_node.methods = [(name, self.to_function_name(name, 'Object')) for name in ['abort', 'type_name', 'copy']] + + #IO + type_node = self.register_type('IO') + + self.current_function = self.register_function(self.to_function_name('out_string', 'IO')) + self.register_param(self.vself) + self.register_param(VariableInfo('x', None)) + vname = self.define_internal_local() + self.register_instruction(cil.LoadNode(vname, 'x')) + self.register_instruction(cil.PrintNode(vname)) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + self.current_function = self.register_function(self.to_function_name('out_int', 'IO')) + self.register_param(self.vself) + self.register_param(VariableInfo('x', None)) + self.register_instruction(cil.PrintNode('x')) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + self.current_function = self.register_function(self.to_function_name('in_string', 'IO')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ReadNode(result)) + self.register_instruction(cil.ReturnNode(result)) + + self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ReadNode(result)) + self.register_instruction(cil.ReturnNode(result)) + + #String + type_node = self.register_type('String') + + self.current_function = self.register_function(self.to_function_name('length', 'String')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.LengthNode(result, self.vself.name)) + self.register_instruction(cil.ReturnNode(result)) + + self.current_function = self.register_function(self.to_function_name('concat', 'String')) + self.register_param(self.vself) + self.register_param(VariableInfo('s', None)) + result = self.define_internal_local() + self.register_instruction(cil.ConcatNode(result, self.vself.name, 's')) + self.register_instruction(cil.ReturnNode(result)) + + self.current_function = self.register_function(self.to_function_name('substr', 'String')) + self.register_param(self.vself) + self.register_param(VariableInfo('i', None)) + self.register_param(VariableInfo('l', None)) + result = self.define_internal_local() + self.register_instruction(cil.SubstringNode(result, self.vself.name, 'i', 'l')) + self.register_instruction(cil.ReturnNode(result)) + + #Int + type_node = self.register_type('Int') + #Bool + type_node = self.register_type('Bool') + class COOLToCILVisitor(BaseCOOLToCILVisitor): @visitor.on('node') @@ -91,6 +172,7 @@ def visit(self, node, scope): self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) + self.register_built_in() self.current_function = None for declaration, child_scope in zip(node.declarations, scope.children): From 09b56f2d91902891b4b665530528f08ca1f4c91d Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 24 Apr 2020 22:05:44 -0400 Subject: [PATCH 093/520] [cil] - Add arg and param `self` to functions --- src/core/cmp/cool_to_cil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index a9a68e77..63f582e8 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -226,9 +226,7 @@ def visit(self, node, scope): self.current_function = self.register_function(self.to_function_name(self.current_method.name, type_name)) # (Handle PARAMS) - #//TODO: Return type SELF_TYPE - if self.current_method.return_type.name is self.current_type.name: - self.register_param(VariableInfo('self', None)) + self.register_param(self.vself) for param_name, _ in node.params: self.register_param(VariableInfo(param_name, None)) @@ -510,6 +508,7 @@ def visit(self, node, scope): args.append(cil.ArgNode(vname)) result = self.register_local(VariableInfo(f'return_value_of_{node.id}')) + self.register_instruction(cil.ArgNode(self.vself.name)) for arg in args: self.register_instruction(arg) @@ -542,6 +541,7 @@ def visit(self, node, scope): args.append(cil.ArgNode(vname)) result = self.register_local(VariableInfo(f'return_value_of_{node.id}')) + self.register_instruction(cil.ArgNode(self.vself.name)) for arg in args: self.register_instruction(arg) From 303d9112bc8510ff125737e85f83fb4c23800f74 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 24 Apr 2020 22:26:12 -0400 Subject: [PATCH 094/520] [cil] - Add support for `GETATTR` and `SETATTR` CIL commands --- src/core/cmp/cil.py | 10 +++++++-- src/core/cmp/cool_to_cil.py | 41 +++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index c10d2de9..62df4d3a 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -73,10 +73,16 @@ class EqualNode(ArithmeticNode): pass class GetAttribNode(InstructionNode): - pass + def __init__(self, dest, xtype, attr): + self.dest = dest + self.xtype = xtype + self.attr = attr class SetAttribNode(InstructionNode): - pass + def __init__(self, xtype, attr, value): + self.xtype = xtype + self.attr = attr + self.value = value class GetIndexNode(InstructionNode): pass diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 63f582e8..f26dfb20 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -1,7 +1,7 @@ import core.cmp.visitor as visitor import core.cmp.cil as cil import core.cmp.CoolUtils as cool -from core.cmp.semantic import Attribute, Method, Type, VariableInfo +from core.cmp.semantic import Attribute, Method, Type, VariableInfo, SemanticError class BaseCOOLToCILVisitor: def __init__(self, context): @@ -360,10 +360,15 @@ def visit(self, node, scope): # node.expr -> ExpressionNode ############################### - vname = self.register_local(VariableInfo(node.id, None)) self.visit(node.expr, scope) - self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) - scope.ret_expr = vname + + try: + self.current_type.attributes.get_attribute(node.id) + self.register_instruction(cil.SetAttribNode(self.current_type.name, node.id, scope.ret_expr)) + except SemanticError: + vname = self.register_local(VariableInfo(node.id, None)) + self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) + scope.ret_expr = vname @visitor.when(cool.NotNode) def visit(self, node, scope): @@ -569,17 +574,23 @@ def visit(self, node, scope): ############################### # node.lex -> str ############################### - param_names = [pn.name for pn in self.current_function.params] - if node.lex in param_names: - for n in param_names: - if node.lex in n.split("_"): - scope.ret_expr = n - break - else: - for n in [lv.name for lv in self.current_function.localvars]: - if node.lex in n.split("_"): - scope.ret_expr = n - break + try: + self.current_type.attributes.get_attribute(node.lex) + attr = self.register_local(VariableInfo('attr_value', None)) + self.register_instruction(cil.GetAttribNode(attr, self.current_type.name, node.lex)) + scope.ret_expr = attr + except SemanticError: + param_names = [pn.name for pn in self.current_function.params] + if node.lex in param_names: + for n in param_names: + if node.lex in n.split("_"): + scope.ret_expr = n + break + else: + for n in [lv.name for lv in self.current_function.localvars]: + if node.lex in n.split("_"): + scope.ret_expr = n + break @visitor.when(cool.StringNode) def visit(self, node, scope): From 0b9e40fda002c3c2b01c51f407cdcf9f7b27a909 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 25 Apr 2020 02:13:59 -0400 Subject: [PATCH 095/520] [visitors] - Errors in `TypeBuilder` return the problematic token Extra: - Create the `Param` class to store the methods parameters instead of use a raw tuple --- src/core/cmp/CoolUtils.py | 20 +++++++++++++++++--- src/core/cmp/visitors.py | 36 +++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 6dde1dcc..12153441 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -3,6 +3,7 @@ from core.cmp.utils import Token, tokenizer empty_token = Token("", "") +empty_token.row, empty_token.column = (0, 0) # AST Classes class Node: @@ -16,12 +17,16 @@ class DeclarationNode(Node): pass class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent=Token('Object', "")): + def __init__(self, idx, features, parent=None): self.id = idx.lex self.tid = idx + self.features = features + if not parent: + parent = Token("Object", "type") + parent.row = idx.row + parent.column = idx.column self.parent = parent.lex self.tparent = parent - self.features = features class AttrDeclarationNode(DeclarationNode): def __init__(self, idx, typex, expr=None, arrow=empty_token): @@ -171,6 +176,15 @@ def FunctionCallNodeBuilder(obj, calls): calls.pop(0) return obj +class Param: + def __init__(self, tid, ttype): + self.tid = tid + self.ttype = ttype + + def __iter__(self): + yield self.tid.lex + yield self.ttype.lex + # Grammar CoolGrammar = Grammar() @@ -223,7 +237,7 @@ def FunctionCallNodeBuilder(obj, calls): param_list %= param + comma + param_list, lambda h, s: [s[1]] + s[3] # -param %= idx + colon + typex, lambda h, s: (s[1], s[3]) +param %= idx + colon + typex, lambda h, s: Param(s[1], s[3]) # expr %= notx + expr, lambda h, s: NotNode(s[2], s[1]) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 6e81b25c..b90b0e12 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -223,7 +223,7 @@ def visit(self, node): self.type_level[node.id] = node.parent self.parent[node.id] = node.tparent except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.tid)) else: self.errors.append((f'{node.id} is an invalid class name', node.tid)) @@ -233,6 +233,7 @@ def __init__(self, context, errors=[]): self.context = context self.current_type = None self.errors = errors + self.methods = {} @visitor.on('node') def visit(self, node): @@ -240,16 +241,22 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node): + main_token = None for def_class in node.declarations: self.visit(def_class) + if def_class.id == 'Main': + main_token = def_class.tid try: main = self.context.get_type('Main') - main.get_method('main') - if main.parent.name != 'Object': - self.errors.append('The class "Main" cannot inherits from any type.') + method = main.methods['main'] + tmethod = self.methods['Main']['main'] + if method.param_names: + self.errors.append(('Method "main" must takes no formal parameters', tmethod)) except SemanticError: - self.errors.append('The class "Main" and his method "main" are needed.') + self.errors.append(('No definition for class "Main"', empty_token)) + except KeyError: + self.errors.append(('Class "Main" must have a method "main"', main_token)) @visitor.when(ClassDeclarationNode) def visit(self, node): @@ -257,13 +264,13 @@ def visit(self, node): if node.parent: if node.parent in sealed: - self.errors.append(f'Is not possible to inherits from "{node.parent}"') + self.errors.append((f'Is not possible to inherits from "{node.parent}"', node.tparent)) node.parent = 'Object' try: parent_type = self.context.get_type(node.parent) self.current_type.set_parent(parent_type) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.tparent)) for feature in node.features: self.visit(feature) @@ -273,22 +280,22 @@ def visit(self, node): try: attr_type = self.context.get_type(node.type) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.ttype)) attr_type = ErrorType() try: self.current_type.define_attribute(node.id, attr_type) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.tid)) @visitor.when(FuncDeclarationNode) def visit(self, node): arg_names, arg_types = [], [] - for idx, typex in node.params: + for i, (idx, typex) in enumerate(node.params): try: arg_type = self.context.get_type(typex) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.params[i].ttype)) arg_type = ErrorType() arg_names.append(idx) @@ -297,13 +304,16 @@ def visit(self, node): try: ret_type = self.context.get_type(node.type) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.ttype)) ret_type = ErrorType() try: self.current_type.define_method(node.id, arg_names, arg_types, ret_type) + if not self.current_type.name in self.methods: + self.methods[self.current_type.name] = {} + self.methods[self.current_type.name][node.id] = node.tid except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.tid)) # Compute the Lowest Common Ancestor in # the type hierarchy tree From 4ef8d2a474a34d92ad0e8abbcf0a78d805d31c9d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 25 Apr 2020 02:26:29 -0400 Subject: [PATCH 096/520] [main] - Update main --- src/main.py | 45 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/src/main.py b/src/main.py index 6d1e6a53..7dc927cb 100644 --- a/src/main.py +++ b/src/main.py @@ -12,7 +12,7 @@ def main(args): with open(args.file, 'r') as fd: code = fd.read() except: - print(f"(0,0) - CompilerError: file {args.file} not found") #TODO: Customize errors + print(f"(0,0) - CompilerError: file {args.file} not found") exit(1) # Lexer @@ -38,54 +38,29 @@ def main(args): parsedData, (failure, token) = CoolParser(tokens, get_shift_reduce=True) if failure: - print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token}") #TODO: Use correct line and column + print(f"({token.row},{token.column}) - SyntacticError: Unexpected token {token.lex}") exit(1) - # Comming soon pipeline steps - #print(parse) - # AST parse, operations = parsedData ast = evaluate_reverse_parse(parse, operations, tokens) + errors = [] # Collect user types collector = TypeCollector() collector.visit(ast) context = collector.context - - if collector.errors: - # Display errors - pass + errors.extend(collector.errors) # Building types builder = TypeBuilder(context) builder.visit(ast) - - if builder.errors: - # Display errors - pass - - # Checking types - checker = TypeChecker(context) - scope = checker.visit(ast) - - if checker.errors: - # Display errors - pass - - # Infering types - inferer = InferenceVisitor(context) - while True: - old = scope.count_auto() - scope = inferer.visit(ast) - if old == scope.count_auto(): - break - inferer.errors.clear() - scope = inferer.visit(ast) - - if inferer.errors: - # Display errors - pass + errors.extend(builder.errors) + + if errors: + for (msg, token) in errors: + print(f"({token.row},{token.column}) - SemanticError: {msg}") + exit(1) exit(0) From 0ac719135c1a98e630d9352743a709c487251359 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 25 Apr 2020 18:36:05 -0400 Subject: [PATCH 097/520] [visitors] - Making a duplicate of all malformed classes --- src/core/cmp/visitors.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index b90b0e12..4faff805 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -217,15 +217,26 @@ def get_type_level(typex): @visitor.when(ClassDeclarationNode) def visit(self, node): + def new_type(): + self.context.create_type(node.id) + self.type_level[node.id] = node.parent + self.parent[node.id] = node.tparent + + def make_a_duplicate(): + while True: + node.id = '1' + node.id + try: new_type() + except SemanticError: pass + else: break + if node.id not in built_in_types: - try: - self.context.create_type(node.id) - self.type_level[node.id] = node.parent - self.parent[node.id] = node.tparent + try: new_type() except SemanticError as ex: self.errors.append((ex.text, node.tid)) + make_a_duplicate() else: self.errors.append((f'{node.id} is an invalid class name', node.tid)) + make_a_duplicate() # Type Builder class TypeBuilder: From 7fe39c16ed5ce4e24e5e6be4c8b8f9ffaecf3ce7 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 25 Apr 2020 21:04:46 -0400 Subject: [PATCH 098/520] Add row and column to eof token --- src/core/cmp/lex.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/lex.py b/src/core/cmp/lex.py index a55a71f3..f8644886 100644 --- a/src/core/cmp/lex.py +++ b/src/core/cmp/lex.py @@ -404,6 +404,7 @@ def tokenize(self, text): tokens[-1].row = token.row tokens[-1].column = token.column EOF = Token('$', eof) + EOF.row, EOF.column = self.lexer.eof return tokens + [EOF] def add_line_column(self, t): From a64e626e6ef01bb83710517d391b40aec2f931f1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 25 Apr 2020 22:30:28 -0400 Subject: [PATCH 099/520] [visitors] - Change the parent of the class that generate cyclic heritage --- src/core/cmp/visitors.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 4faff805..2a78dfb5 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -205,7 +205,9 @@ def get_type_level(typex): return 0 if parent == 0: - self.errors.append(('Cyclic heritage.', self.parent[typex])) + node = self.parent[typex] + node.parent = "Object" + self.errors.append(('Cyclic heritage.', node.tparent)) elif type(parent) is not int: self.type_level[typex] = 0 if parent else 1 if type(parent) is str: @@ -220,7 +222,7 @@ def visit(self, node): def new_type(): self.context.create_type(node.id) self.type_level[node.id] = node.parent - self.parent[node.id] = node.tparent + self.parent[node.id] = node def make_a_duplicate(): while True: From a83a21ce97f2754c6649426995db02c5fb467a9a Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 25 Apr 2020 22:34:55 -0400 Subject: [PATCH 100/520] [semantic] - Avoids the definition of the same method multiple times in a class --- src/core/cmp/semantic.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 6613abd0..739a7a84 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -77,15 +77,8 @@ def get_method(self, name:str): raise SemanticError(f'Method "{name}" is not defined in {self.name}.') def define_method(self, name:str, param_names:list, param_types:list, return_type): - if name in self.methods: + if name in self.methods.keys(): raise SemanticError(f'Method "{name}" already defined in {self.name}') - # raise SemanticError(f'Method "{name}" already defined in {self.name} with a different signature.') - - method = self.methods[name] = Method(name, param_names, param_types, return_type) - return method - - # my method, change it in future - def define_method(self, name:str, param_names:list, param_types:list, return_type): try: method = self.get_method(name) except SemanticError: From ce29fdb511364e41789c9d8b61bf1f9bf5d13e48 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 18:39:03 -0400 Subject: [PATCH 101/520] [visitors] - Saving some data in the nodes to be used in future visitors --- src/core/cmp/visitors.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 2a78dfb5..cd51ccf9 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -295,7 +295,8 @@ def visit(self, node): except SemanticError as ex: self.errors.append((ex.text, node.ttype)) attr_type = ErrorType() - + node.attr_type = attr_type + try: self.current_type.define_attribute(node.id, attr_type) except SemanticError as ex: @@ -319,7 +320,9 @@ def visit(self, node): except SemanticError as ex: self.errors.append((ex.text, node.ttype)) ret_type = ErrorType() - + node.ret_type = ret_type + node.arg_types = arg_types + try: self.current_type.define_method(node.id, arg_names, arg_types, ret_type) if not self.current_type.name in self.methods: From cdf27b0897fc401c1cdea9df0b7544915ad724a0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 19:08:26 -0400 Subject: [PATCH 102/520] [semantic] - Fix error in semantic types Errors: - `VoidType` is not `bypass` - `Type` is `bypass` by default only when it's named `Object` --- src/core/cmp/semantic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 739a7a84..ccd3ee0b 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -94,7 +94,7 @@ def conforms_to(self, other): return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) def bypass(self): - if self.name == 'Object' or self.name == 'AUTO_TYPE': + if self.name == 'Object': return True return False @@ -137,10 +137,10 @@ def conforms_to(self, other): return True def bypass(self): - return True + return False def __eq__(self, other): - return isinstance(other, VoidType) + return other.name == self.name or isinstance(other, VoidType) class IntType(Type): def __init__(self): From 2f381ae5526aaec8c0d08cf3576f671c85870945 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 19:12:17 -0400 Subject: [PATCH 103/520] [visitors] - Save `arg_names` in the attribute node too --- src/core/cmp/visitors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index cd51ccf9..120c35b2 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -296,7 +296,7 @@ def visit(self, node): self.errors.append((ex.text, node.ttype)) attr_type = ErrorType() node.attr_type = attr_type - + try: self.current_type.define_attribute(node.id, attr_type) except SemanticError as ex: @@ -322,6 +322,7 @@ def visit(self, node): ret_type = ErrorType() node.ret_type = ret_type node.arg_types = arg_types + node.arg_names = arg_names try: self.current_type.define_method(node.id, arg_names, arg_types, ret_type) From 323dfe050e3054e20e0de327b47b3e909a6b2e52 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 19:19:22 -0400 Subject: [PATCH 104/520] [checker] - Attribute declaration review Attibute declaration dont need a `computed_type` --- src/core/cmp/visitors.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 120c35b2..21d06b8e 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -394,25 +394,17 @@ def visit(self, node, scope): @visitor.when(AttrDeclarationNode) def visit(self, node, scope): - try: - node_type = self.context.get_type(node.type) - except SemanticError as ex: - self.errors.append(ex.text) - node_type = ErrorType() - if not node.expr: - node.computed_type = node_type return self.visit(node.expr, scope) expr_type = fixed_type(node.expr.computed_type, self.current_type) + real_type = fixed_type(node.attr_type, self.current_type) - if not match(expr_type, fixed_type(node_type, self.current_type)): + # //TODO: SELF_TYPE match every type??? + if not expr_type.conforms_to(real_type): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) - node_type = ErrorType() - - node.computed_type = node_type - + @visitor.when(FuncDeclarationNode) def visit(self, node, scope): self.current_method = self.current_type.get_method(node.id) From a3070d19d277c7f5344f925ab4c5f2de3e78c220 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 19:23:34 -0400 Subject: [PATCH 105/520] [checker] - Function declaration node need to use their method definition `FunctionDeclarationNone` was using the method defined in the current class and this is not the expected behavior --- src/core/cmp/visitors.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 21d06b8e..501e9e70 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -407,7 +407,7 @@ def visit(self, node, scope): @visitor.when(FuncDeclarationNode) def visit(self, node, scope): - self.current_method = self.current_type.get_method(node.id) + self.current_method = Method(node.id, node.arg_names, node.arg_types, node.ret_type) for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): scope.define_variable(pname, ptype) @@ -415,12 +415,11 @@ def visit(self, node, scope): # for expr in node.body: self.visit(node.body, scope) - last_expr = node.body - last_expr_type = fixed_type(last_expr.computed_type, self.current_type) + body_type = fixed_type(node.body.computed_type, self.current_type) method_rtn_type = fixed_type(self.current_method.return_type, self.current_type) # //TODO: be carefull whit void - if not match(last_expr_type, method_rtn_type): + if not match(body_type, method_rtn_type): self.errors.append(INCOMPATIBLE_TYPES.replace('%s', last_expr_type.name, 1).replace('%s', method_rtn_type.name, 1)) @visitor.when(AssignNode) From abbe7cd3414aa74373d6290e7f461d693e1763c0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 19:31:09 -0400 Subject: [PATCH 106/520] [checker] - Update attribute an function declaration errors --- src/core/cmp/visitors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 501e9e70..c7530106 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -403,7 +403,7 @@ def visit(self, node, scope): # //TODO: SELF_TYPE match every type??? if not expr_type.conforms_to(real_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, real_type.name), node.arrow)) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -420,7 +420,7 @@ def visit(self, node, scope): # //TODO: be carefull whit void if not match(body_type, method_rtn_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', last_expr_type.name, 1).replace('%s', method_rtn_type.name, 1)) + self.errors.append((INCOMPATIBLE_TYPES % (body_type.name, method_rtn_type.name), node.ttype)) @visitor.when(AssignNode) def visit(self, node, scope): From a3ddf4aa8640d890d3eb29d6eea2811e85080e27 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 20:02:27 -0400 Subject: [PATCH 107/520] [checker] - Update assign node errors --- src/core/cmp/visitors.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index c7530106..8291875d 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -425,18 +425,19 @@ def visit(self, node, scope): @visitor.when(AssignNode) def visit(self, node, scope): self.visit(node.expr, scope) - expr_type = node.expr.computed_type + expr_type = fixed_type(node.expr.computed_type, self.current_type) if scope.is_defined(node.id): var = scope.find_variable(node.id) - node_type = var.type + node_type = var.type + var_type = fixed_type(node_type, self.current_type) if var.name == 'self': - self.errors.append(SELF_IS_READONLY) - elif not match(fixed_type(expr_type, self.current_type), fixed_type(node_type, self.current_type)): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) + self.errors.append((SELF_IS_READONLY, node.tid)) + elif not expr_type.conforms_to(var_type): + self.errors.append((INCOMPATIBLE_TYPES % (expr_type, var_type), node.tid)) else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1)) + self.errors.append((VARIABLE_NOT_DEFINED % (node.id), node.tid)) node_type = ErrorType() node.computed_type = node_type From 53cf16d98f452b67853a63f16ec4351b7ecbb08f Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 20:11:25 -0400 Subject: [PATCH 108/520] [checker] - CaseOf return type is unkown until runtime Details: - `ErrorType` is used for this node `computed_type` in order to avoid types mismatching --- src/core/cmp/visitors.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 8291875d..0e7f3bbe 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -445,25 +445,12 @@ def visit(self, node, scope): @visitor.when(CaseOfNode) def visit(self, node, scope): self.visit(node.expr, scope) - has_auto = has_error = False types_list = [] for case in node.branches: self.visit(case.expr, scope.create_child()) - # //TODO: be carefull whit void - has_auto |= IsAuto(case.expr.computed_type.name) - has_error |= case.expr.computed_type.name == '' - types_list.append(case.expr.computed_type) - - if has_error: - node.computed_type = ErrorType() - elif has_auto: - node.computed_type = self.context.get_type('AUTO_TYPE') - else: - if all([t.name == ST for t in types_list]): - node.computed_type = types_list[0] - else: - node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) + # The return type of a is known at runtime + node.computed_type = ErrorType() @visitor.when(CaseExpressionNode) def visit(self, node, scope): From a9c0058c3e74d6e9ce869a5252365b2ff5d039d6 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 20:26:59 -0400 Subject: [PATCH 109/520] [checker] - Define the branch variable in case of The case of matching branch variables needs to be defined and throw an error if the type not exists --- src/core/cmp/visitors.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 0e7f3bbe..2007eaa1 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -454,6 +454,12 @@ def visit(self, node, scope): @visitor.when(CaseExpressionNode) def visit(self, node, scope): + try: + branch_type = self.context.get_type(node.type) + except SemanticError as ex: + self.errors.append((ex.text, node.ttype)) + branch_type = ErrorType() + scope.define_variable(node.id, branch_type) self.visit(node.expr, scope) node.computed_type = node.expr.computed_type From c62480a06353d6df18684c70f9e662010ba3df4d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 20:44:21 -0400 Subject: [PATCH 110/520] [utils] - Give the arrow token to LetAttributeNode --- src/core/cmp/CoolUtils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 12153441..69a33408 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -251,9 +251,9 @@ def __iter__(self): # let_list %= idx + colon + typex, lambda h, s: [LetAttributeNode(s[1], s[3])] -let_list %= idx + colon + typex + larrow + expr, lambda h, s: [LetAttributeNode(s[1], s[3], s[5])] +let_list %= idx + colon + typex + larrow + expr, lambda h, s: [LetAttributeNode(s[1], s[3], s[5], s[4])] let_list %= idx + colon + typex + comma + let_list, lambda h, s: [LetAttributeNode(s[1], s[3])] + s[5] -let_list %= idx + colon + typex + larrow + expr + comma + let_list, lambda h, s: [LetAttributeNode(s[1], s[3], s[5])] + s[7] +let_list %= idx + colon + typex + larrow + expr + comma + let_list, lambda h, s: [LetAttributeNode(s[1], s[3], s[5], s[4])] + s[7] # case_list %= idx + colon + typex + rarrow + expr + semi, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] From 299282d07e8c30b72c888ac0cdd62d486de2e744 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 20:45:34 -0400 Subject: [PATCH 111/520] [checker] - LetAttributeNode dont need a computed type Also Update errors --- src/core/cmp/visitors.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 2007eaa1..dce2b3cb 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -479,26 +479,21 @@ def visit(self, node, scope): try: node_type = self.context.get_type(node.type) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.ttype)) node_type = ErrorType() if not scope.is_local(node.id): scope.define_variable(node.id, node_type) else: - self.errors.append(LOCAL_ALREADY_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) - # //TODO: use ErrorType here and in Inferencer - if not node.expr: - node.computed_type = node.type - return - - self.visit(node.expr, scope) - expr_type = node.expr.computed_type - - if not match(fixed_type(expr_type, self.current_type), fixed_type(node_type, self.current_type)): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) - # //TODO: Same here + self.errors.append((LOCAL_ALREADY_DEFINED % (node.id, self.current_method.name), node.tid)) - node.computed_type = node_type + if node.expr: + self.visit(node.expr, scope) + expr_type = fixed_type(node.expr.computed_type, self.current_type) + real_type = fixed_type(node_type, self.current_type) + + if not expr_type.conforms_to(real_type): + self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, real_type.name), node.arrow)) @visitor.when(IfThenElseNode) def visit(self, node, scope): From c8f712e45fad093a55db11a34cc6f2b29debf74e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 20:52:54 -0400 Subject: [PATCH 112/520] [ast] - Add the if token to the IfThenElseNode --- src/core/cmp/CoolUtils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 69a33408..3f72f92e 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -50,7 +50,8 @@ class ExpressionNode(Node): pass class IfThenElseNode(ExpressionNode): - def __init__(self, condition, if_body, else_body=None): + def __init__(self, condition, if_body, if_token, else_body=None): + self.token = if_token self.condition = condition self.if_body = if_body self.else_body = else_body @@ -303,8 +304,8 @@ def __iter__(self): atom %= integer, lambda h, s: IntegerNode(s[1]) atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) -atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4]) -atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) +atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) +atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) atom %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) From e976787b7ade4b4fc801e27d972d8845b11ef89e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 21:01:59 -0400 Subject: [PATCH 113/520] [checker] - Update IfThenElseNode Changes: - Update errors - Computed type set to `ErrorType` for the same reason as `CaseOfNode` --- src/core/cmp/visitors.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index dce2b3cb..34236fa9 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -449,7 +449,7 @@ def visit(self, node, scope): types_list = [] for case in node.branches: self.visit(case.expr, scope.create_child()) - # The return type of a is known at runtime + # The return type of a is unknown until runtime node.computed_type = ErrorType() @visitor.when(CaseExpressionNode) @@ -498,28 +498,17 @@ def visit(self, node, scope): @visitor.when(IfThenElseNode) def visit(self, node, scope): self.visit(node.condition, scope) - expr_type = node.condition.computed_type + cond_type = fixed_type(node.condition.computed_type, self.current_type) - if not expr_type.name in ['Bool', 'AUTO_TYPE']: - self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'If', 1).replace('%s', expr_type.name, 1)) + if BoolType() != cond_type: + self.errors.append((CONDITION_NOT_BOOL % ('If', cond_type.name), node.token)) self.visit(node.if_body, scope) - node.computed_type = node.if_body.computed_type - if node.else_body: self.visit(node.else_body, scope) - # //TODO: Check priority preference between AUTO and Error - if IsAuto(node.if_body.computed_type.name) or IsAuto(node.else_body.computed_type.name): - node.computed_type = self.context.get_type('AUTO_TYPE') - elif '' in [node.if_body.computed_type.name, node.else_body.computed_type.name]: - node.computed_type = ErrorType() - else: - types_list = [node.if_body.computed_type, node.else_body.computed_type] - if all([t.name == ST for t in types_list]): - node.computed_type = types_list[0] - else: - node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) - + # The return type of a is unknown until runtime + node.computed_type = ErrorType() + @visitor.when(BlockNode) def visit(self, node, scope): for expr in node.exprs: From 85695eac545ae28712522157878aa2682e14cce1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 21:05:43 -0400 Subject: [PATCH 114/520] [ast] - Add the while token to WhileLoopNode --- src/core/cmp/CoolUtils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 3f72f92e..61bbc1a9 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -57,7 +57,8 @@ def __init__(self, condition, if_body, if_token, else_body=None): self.else_body = else_body class WhileLoopNode(ExpressionNode): - def __init__(self, condition, body): + def __init__(self, condition, body, token): + self.token = token self.condition = condition self.body = body @@ -306,7 +307,7 @@ def __iter__(self): atom %= boolx, lambda h, s: BoolNode(s[1]) atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) -atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) +atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) atom %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) From 0368dd2b4d97fb97c0c007b4b5a02b8620f9cb47 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 21:07:22 -0400 Subject: [PATCH 115/520] [checker] - Update errors in WhileLoopNode --- src/core/cmp/visitors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 34236fa9..69aa0d50 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -520,10 +520,10 @@ def visit(self, node, scope): @visitor.when(WhileLoopNode) def visit(self, node, scope): self.visit(node.condition, scope) - expr_type = node.condition.computed_type + cond_type = fixed_type(node.condition.computed_type, self.current_type) - if not expr_type.name in ['Bool', 'AUTO_TYPE']: - self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'While', 1).replace('%s', expr_type.name, 1)) + if BoolType() != cond_type:: + self.errors.append((CONDITION_NOT_BOOL % ('While', cond_type.name), node.token)) self.visit(node.body, scope) node.computed_type = VoidType() From d4084eefe8a86c79671e083d3823a16315aa5b6e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 22:13:43 -0400 Subject: [PATCH 116/520] [checker] - FunctionCall review Changes: - Update errors - Add a custom error if the cast type is `SELF_TYPE` - Always visit the function call parameters --- src/core/cmp/visitors.py | 46 ++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 69aa0d50..5eb88c73 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -531,31 +531,45 @@ def visit(self, node, scope): @visitor.when(FunctionCallNode) def visit(self, node, scope): self.visit(node.obj, scope) - obj_type = node.obj.computed_type + obj_type = fixed_type(node.obj.computed_type, self.current_type) + + error = False + + arg_types = [] + for arg in node.args: + self.visit(arg, scope) + arg = fixed_type(arg.computed_type, self.current_type) + arg_types.append(arg) try: if node.type: - if IsAuto(node.type): - raise SemanticError('Is not possible to use AUTO_TYPE in a cast') - if not obj_type.conforms_to(self.context.get_type(node.type)): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', obj_type.name, 1).replace('%s', node.type, 1)) - obj_method = self.context.get_type(node.type).get_method(node.id) - else: - obj_method = obj_type.get_method(node.id) + token = node.ttype + cast_type = self.context.get_type(node.type) + if cast_type.name == ST: + raise SemanticError("Invalid use of SELF_TYPE") + # if IsAuto(node.type): + # raise SemanticError('Is not possible to use AUTO_TYPE in a cast') + if not obj_type.conforms_to(cast_type): + raise SemanticError(INCOMPATIBLE_TYPES % (obj_type.name, node.type)) + obj_type = cast_type + token = node.tid + obj_method = obj_type.get_method(node.id) if len(node.args) == len(obj_method.param_types): - for arg, param_type in zip(node.args, obj_method.param_types): - self.visit(arg, scope) - arg_type = fixed_type(arg.computed_type, self.current_type) + for idx, (arg, param_type) in enumerate(zip(arg_types, obj_method.param_types)): + real_type = fixed_type(param_type, self.current_type) - if not match(arg_type, fixed_type(param_type, self.current_type)): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) + if not arg.conforms_to(real_type): + self.errors.append((INCOMPATIBLE_TYPES % (arg.name, real_type.name + f" in the argument #{idx} of {node.id}"), token)) + error = True else: - self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - + raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + assert error node_type = obj_method.return_type except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, token)) + node_type = ErrorType() + except AssertionError: node_type = ErrorType() node.computed_type = node_type From ada0765ac056a737e7a2a9a58d158009a97206aa Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 23:22:11 -0400 Subject: [PATCH 117/520] [checker] - Use same behavior of Function CAll in member call --- src/core/cmp/visitors.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 5eb88c73..95907b86 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -578,22 +578,32 @@ def visit(self, node, scope): def visit(self, node, scope): obj_type = self.current_type + error = False + + arg_types = [] + for arg in node.args: + self.visit(arg, scope) + arg = fixed_type(arg.computed_type, self.current_type) + arg_types.append(arg) + try: + token = node.tid obj_method = obj_type.get_method(node.id) - if len(node.args) == len(obj_method.param_types): for arg, param_type in zip(node.args, obj_method.param_types): - self.visit(arg, scope) - arg_type = fixed_type(arg.computed_type, self.current_type) + real_type = fixed_type(param_type, self.current_type) - if not match(arg_type, fixed_type(param_type, self.current_type)): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) + if not arg.conforms_to(real_type): + self.errors.append((INCOMPATIBLE_TYPES % (arg.name, real_type.name + f" in the argument #{idx} of {node.id}"), token)) + error = True else: - self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - + raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + assert error node_type = obj_method.return_type except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, token)) + node_type = ErrorType() + except AssertionError: node_type = ErrorType() node.computed_type = node_type From 741756f3d17b94245b004dc9e7db543dc3c62c55 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 23:24:27 -0400 Subject: [PATCH 118/520] [checker] - Update errors in ArithmeticNode --- src/core/cmp/visitors.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 95907b86..7899c097 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -611,13 +611,13 @@ def visit(self, node, scope): @visitor.when(ArithmeticNode) def visit(self, node, scope): self.visit(node.left, scope) - left_type = node.left.computed_type + left_type = fixed_type(node.left.computed_type, self.current_type) self.visit(node.right, scope) - right_type = node.right.computed_type + right_type = fixed_type(node.right.computed_type, self.current_type) - if not (left_type.name == 'Int' and right_type.name == 'Int'): - self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) + if IntType() != right_type or IntType() != left_type: + self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) node_type = ErrorType() else: node_type = IntType() From 835ed97d5e6009990c91eaeff4c2e61266427939 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 26 Apr 2020 23:25:11 -0400 Subject: [PATCH 119/520] [checker] - Update errors in ComparisonNode --- src/core/cmp/visitors.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 7899c097..f8d9fef3 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -627,14 +627,14 @@ def visit(self, node, scope): @visitor.when(ComparisonNode) def visit(self, node, scope): self.visit(node.left, scope) - left_type = node.left.computed_type + left_type = fixed_type(node.left.computed_type, self.current_type) self.visit(node.right, scope) - right_type = node.right.computed_type + right_type = fixed_type(node.right.computed_type, self.current_type) - if not (left_type.name == 'Bool' and right_type.name == 'Bool'): - self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) - node_type = ErrorType() + if BoolType() != right_type or BoolType() != left_type: + self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) + node_type = BoolType() else: node_type = BoolType() From 907ed77720891953cdfbd6bdaa0a1749342aac07 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 00:04:07 -0400 Subject: [PATCH 120/520] [checker] - Update some simple nodes Nodes: - `NewNode` - `IdNode` - `IsVoidNode` - `ComplementNode` - ` NotNode` --- src/core/cmp/visitors.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index f8d9fef3..71339dfe 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -658,7 +658,7 @@ def visit(self, node, scope): var = scope.find_variable(node.lex) node_type = var.type else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.lex, 1)) + self.errors.append((VARIABLE_NOT_DEFINED % (node.lex), node.token)) node_type = ErrorType() node.computed_type = node_type @@ -666,13 +666,13 @@ def visit(self, node, scope): @visitor.when(NewNode) def visit(self, node, scope): if node.type in built_in_types[:3]: - self.errors.append(f'It cannot be initialized a {node.type} with the new keyword') + self.errors.append((f'It cannot be initialized a {node.type} with the new keyword', node.ttype)) node.computed_type = ErrorType() else: try: node_type = self.context.get_type(node.type) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex.text, node.ttype)) node_type = ErrorType() node.computed_type = node_type @@ -680,25 +680,27 @@ def visit(self, node, scope): @visitor.when(IsVoidNode) def visit(self, node, scope): self.visit(node.expr, scope) - node.computed_type = self.context.get_type('Bool') + node.computed_type = BoolType() @visitor.when(ComplementNode) def visit(self, node, scope): self.visit(node.expr, scope) - if node.expr.computed_type.name != 'Int': - self.errors.append("Complement works only for Int") + expr_type = fixed_type(node.expr.computed_type.name, self.current_type) + if IntType() != expr_type: + self.errors.append(("Complement works only for Int", node.symbol)) node.computed_type = ErrorType() else: - node.computed_type = self.context.get_type('Int') + node.computed_type = IntType() @visitor.when(NotNode) def visit(self, node, scope): self.visit(node.expr, scope) - if node.expr.computed_type.name != 'Bool': - self.errors.append("Not operator works only for Bool") + expr_type = fixed_type(node.expr.computed_type.name, self.current_type) + if BoolType() != expr_type: + self.errors.append(("Not operator works only for Bool", node.symbol)) node.computed_type = ErrorType() else: - node.computed_type = self.context.get_type('Bool') + node.computed_type = BoolType() @visitor.when(EqualNode) def visit(self, node, scope): From d1ad46c9d8eefafb5b187ab29f29816af61f027f Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 00:04:52 -0400 Subject: [PATCH 121/520] [checker] - Update EqualNode --- src/core/cmp/visitors.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 71339dfe..db24aa9f 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -705,19 +705,22 @@ def visit(self, node, scope): @visitor.when(EqualNode) def visit(self, node, scope): self.visit(node.left, scope) - left_type = node.left.computed_type + left_type = fixed_type(node.left.computed_type, self.current_type) self.visit(node.right, scope) - right_type = node.right.computed_type + right_type = fixed_type(node.right.computed_type, self.current_type) - if not (left_type.name == right_type.name and left_type.name in built_in_types[:3]): - self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) - node_type = ErrorType() + valid_types = [IntType(), BoolType(), StringType()] + for op_type in valid_types: + if op == right_type and op == left_type: + node_type = op_type + break else: - node_type = right_type + self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) + node_type = ErrorType() node.computed_type = node_type - + # Type Inference Visitor class InferenceVisitor(TypeChecker): def __init__(self, context, errors=[]): From a0ccebf36f4b654efe5f04cc86de4408967c2366 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 02:10:24 -0400 Subject: [PATCH 122/520] [checker] - Fix some errors - Compilacion errors - Wrong properties usage --- src/core/cmp/visitors.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index db24aa9f..c93791df 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -418,8 +418,7 @@ def visit(self, node, scope): body_type = fixed_type(node.body.computed_type, self.current_type) method_rtn_type = fixed_type(self.current_method.return_type, self.current_type) - # //TODO: be carefull whit void - if not match(body_type, method_rtn_type): + if not body_type.conforms_to(method_rtn_type): self.errors.append((INCOMPATIBLE_TYPES % (body_type.name, method_rtn_type.name), node.ttype)) @visitor.when(AssignNode) @@ -448,7 +447,7 @@ def visit(self, node, scope): types_list = [] for case in node.branches: - self.visit(case.expr, scope.create_child()) + self.visit(case, scope.create_child()) # The return type of a is unknown until runtime node.computed_type = ErrorType() @@ -470,7 +469,7 @@ def visit(self, node, scope): for expr in node.let_body: self.visit(expr, child) - + self.visit(node.in_body, child) node.computed_type = node.in_body.computed_type @@ -522,7 +521,7 @@ def visit(self, node, scope): self.visit(node.condition, scope) cond_type = fixed_type(node.condition.computed_type, self.current_type) - if BoolType() != cond_type:: + if BoolType() != cond_type: self.errors.append((CONDITION_NOT_BOOL % ('While', cond_type.name), node.token)) self.visit(node.body, scope) @@ -564,7 +563,7 @@ def visit(self, node, scope): error = True else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - assert error + assert not error node_type = obj_method.return_type except SemanticError as ex: self.errors.append((ex.text, token)) @@ -590,7 +589,7 @@ def visit(self, node, scope): token = node.tid obj_method = obj_type.get_method(node.id) if len(node.args) == len(obj_method.param_types): - for arg, param_type in zip(node.args, obj_method.param_types): + for arg, param_type in zip(arg_types, obj_method.param_types): real_type = fixed_type(param_type, self.current_type) if not arg.conforms_to(real_type): @@ -598,7 +597,7 @@ def visit(self, node, scope): error = True else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - assert error + assert not error node_type = obj_method.return_type except SemanticError as ex: self.errors.append((ex.text, token)) @@ -632,9 +631,9 @@ def visit(self, node, scope): self.visit(node.right, scope) right_type = fixed_type(node.right.computed_type, self.current_type) - if BoolType() != right_type or BoolType() != left_type: + if IntType() != right_type or IntType() != left_type: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) - node_type = BoolType() + node_type = ErrorType() else: node_type = BoolType() @@ -646,7 +645,7 @@ def visit(self, node, scope): @visitor.when(StringNode) def visit(self, node, scope): - node.computed_type = StringType() + node.computed_type = self.context.get_type("String") @visitor.when(BoolNode) def visit(self, node, scope): @@ -685,7 +684,7 @@ def visit(self, node, scope): @visitor.when(ComplementNode) def visit(self, node, scope): self.visit(node.expr, scope) - expr_type = fixed_type(node.expr.computed_type.name, self.current_type) + expr_type = fixed_type(node.expr.computed_type, self.current_type) if IntType() != expr_type: self.errors.append(("Complement works only for Int", node.symbol)) node.computed_type = ErrorType() @@ -695,7 +694,7 @@ def visit(self, node, scope): @visitor.when(NotNode) def visit(self, node, scope): self.visit(node.expr, scope) - expr_type = fixed_type(node.expr.computed_type.name, self.current_type) + expr_type = fixed_type(node.expr.computed_type, self.current_type) if BoolType() != expr_type: self.errors.append(("Not operator works only for Bool", node.symbol)) node.computed_type = ErrorType() @@ -712,15 +711,15 @@ def visit(self, node, scope): valid_types = [IntType(), BoolType(), StringType()] for op_type in valid_types: - if op == right_type and op == left_type: - node_type = op_type + if op_type == right_type and op_type == left_type: + node_type = BoolType() break else: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) node_type = ErrorType() node.computed_type = node_type - + # Type Inference Visitor class InferenceVisitor(TypeChecker): def __init__(self, context, errors=[]): From 049fc0aaeff3adad9bcd5cd6ebaa88a3fe1a8649 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 02:11:43 -0400 Subject: [PATCH 123/520] [grammar] - Grammar error detected --- src/code.cl | 80 +++++++-------------------------------- src/core/cmp/CoolUtils.py | 3 ++ src/main.py | 5 +++ 3 files changed, 21 insertions(+), 67 deletions(-) diff --git a/src/code.cl b/src/code.cl index ec38a776..493a00f9 100644 --- a/src/code.cl +++ b/src/code.cl @@ -1,67 +1,13 @@ -(* hairy . . .*) - -class Foo inherits Bazz { - a : Razz <- case self of - n : Razz => (new Bar); - n : Foo => (new Razz); - n : Bar => n; - esac; - - b : Int <- a.doh() + g.doh() + doh() + printh(); - - doh() : Int { (let i : Int <- h in { h <- h + 2; i; } ) }; - -}; - -class Bar inherits Razz { - - c : Int <- doh(); - - d : Object <- printh(); -}; - - -class Razz inherits Foo { - - e : Bar <- case self of - n : Razz => (new Bar); - n : Bar => n; - esac; - - f : Int <- a@Bazz.doh() + g.doh() + e.doh() + doh() + printh(); - -}; - -class Bazz inherits IO { - - h : Int <- 1; - - g : Foo <- case self of - n : Bazz => (new Foo); - n : Razz => (new Bar); - n : Foo => (new Razz); - n : Bar => n; - esac; - - i : Object <- printh(); - - printh() : Int { { out_int(h); 0; } }; - - doh() : Int { (let i: Int <- h in { h <- h + 1; i; } ) }; -}; - -(* scary . . . *) -class Main { - a : Bazz <- new Bazz; - b : Foo <- new Foo; - c : Razz <- new Razz; - d : Bar <- new Bar; - - main(): String { "do nothing" }; - -}; - - - - - +class Main inherits IO { + main() : String { + foo(42) + }; + + foo(i : Int) : String { + if i = 0 then "" else + (let next : Int <- i / 10 in + foo(next).concat(foo(i - next * 10)) + ) + fi + }; +}; \ No newline at end of file diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 61bbc1a9..e022300b 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -173,9 +173,12 @@ class BoolNode(AtomicNode): pass def FunctionCallNodeBuilder(obj, calls): + print("-------------------") while len(calls): + print(obj) obj = FunctionCallNode(obj, *calls[0]) calls.pop(0) + print("-------------------") return obj class Param: diff --git a/src/main.py b/src/main.py index 7dc927cb..cae92a27 100644 --- a/src/main.py +++ b/src/main.py @@ -56,6 +56,11 @@ def main(args): builder = TypeBuilder(context) builder.visit(ast) errors.extend(builder.errors) + + # Checking types + checker = TypeChecker(context) + checker.visit(ast) + errors.extend(checker.errors) if errors: for (msg, token) in errors: From 02c6ca80881f58a85729e084da800f4f26e10625 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 27 Apr 2020 14:42:03 -0400 Subject: [PATCH 124/520] Add mips.py and cil_to_mips.py --- .vscode/settings.json | 4 ++++ src/core/cmp/cil_to_mips.py | 0 src/core/cmp/mips.py | 0 3 files changed, 4 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 src/core/cmp/cil_to_mips.py create mode 100644 src/core/cmp/mips.py diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..cc67606f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.pylintEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py new file mode 100644 index 00000000..e69de29b diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py new file mode 100644 index 00000000..e69de29b From 84933ec79235462a617fcbc5c55380e746b1a09b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 27 Apr 2020 15:56:44 -0400 Subject: [PATCH 125/520] Create Classes for mips code representation --- src/core/cmp/mips.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index e69de29b..a6406774 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -0,0 +1,44 @@ +import core.cmp.visitor as visitor + +ATTR_SIZE = 32 + +class Node: + pass + +class ProgramNode(Node): + def __init__(self, dottext, dotdata, types): + self._dottext = dottext + self._dotdata = dotdata + +class TextSectionNode(Node): + def __init__(self, functions): + self.functions + +class FunctionNode(Node): + def __init__(self, instructions): + self.instructions = instructions + +class DataSectionNode(Node): + def __init__(self, data): + self.data = data + +class DataNode(Node): + def __init__(self, name, value): + self.name = name + self.value = value + +class InstructionNode(Node): + pass + +class LabelNode(InstructionNode): + def __init__(self, name): + self.name = name + + + +class MIPSType(): + def __init__(self, name, attributes): + self.attributes = attributes + + def get_offset(self, attr_name): + return ATTR_SIZE * self.attributes.index(attributes) \ No newline at end of file From 8ed5ae9584f13737f90ba7afa8c9328e390f3694 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 15:30:07 -0400 Subject: [PATCH 126/520] [checker] - Change the static type of an AssigmentNode accordingly to the manual --- src/core/cmp/visitors.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index c93791df..cd562687 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -424,19 +424,20 @@ def visit(self, node, scope): @visitor.when(AssignNode) def visit(self, node, scope): self.visit(node.expr, scope) - expr_type = fixed_type(node.expr.computed_type, self.current_type) + node_type = node.expr.computed_type - if scope.is_defined(node.id): + try: + if scope.is_defined(node.id): + raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) var = scope.find_variable(node.id) - node_type = var.type - var_type = fixed_type(node_type, self.current_type) + var_type = fixed_type(var.type, self.current_type) if var.name == 'self': - self.errors.append((SELF_IS_READONLY, node.tid)) - elif not expr_type.conforms_to(var_type): - self.errors.append((INCOMPATIBLE_TYPES % (expr_type, var_type), node.tid)) - else: - self.errors.append((VARIABLE_NOT_DEFINED % (node.id), node.tid)) + raise SemanticError(SELF_IS_READONLY) + if not node_type.conforms_to(var_type): + raise SemanticError(INCOMPATIBLE_TYPES % (node_type, var_type)) + except SemanticError as ex: + self.errors.append((ex.text, node.tid)) node_type = ErrorType() node.computed_type = node_type From b9a753777f018b825c9272ddda6583cc0595f887 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 16:02:43 -0400 Subject: [PATCH 127/520] [checker] - Change static type of CaseOfNode --- src/core/cmp/visitors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index cd562687..67dbeff1 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -449,8 +449,9 @@ def visit(self, node, scope): types_list = [] for case in node.branches: self.visit(case, scope.create_child()) + type_list.append(case.computed_type) # The return type of a is unknown until runtime - node.computed_type = ErrorType() + node.computed_type = LCA(types_list, self.context) @visitor.when(CaseExpressionNode) def visit(self, node, scope): From 6623d130767fd88982319b4826f22f449f57dea2 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 16:16:46 -0400 Subject: [PATCH 128/520] [checker] - Change the static type of IfTheElseNode --- src/core/cmp/visitors.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 67dbeff1..a4a251ec 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -499,16 +499,20 @@ def visit(self, node, scope): @visitor.when(IfThenElseNode) def visit(self, node, scope): self.visit(node.condition, scope) - cond_type = fixed_type(node.condition.computed_type, self.current_type) + cond_type = node.condition.computed_type if BoolType() != cond_type: self.errors.append((CONDITION_NOT_BOOL % ('If', cond_type.name), node.token)) self.visit(node.if_body, scope) + node_type = if_type = node.if_body.computed_type + if node.else_body: self.visit(node.else_body, scope) - # The return type of a is unknown until runtime - node.computed_type = ErrorType() + else_type = node.else_body.computed_type + node_type = LCA([if_body, else_body], self.context) + + node.computed_type = node_type @visitor.when(BlockNode) def visit(self, node, scope): From de3ef2998db1b046ea98fc8f125653f1d1790b8c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 16:29:01 -0400 Subject: [PATCH 129/520] [checker] - Change the usage of fixed_type to avoid node has SELF_TYPE as static type --- src/core/cmp/visitors.py | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index a4a251ec..9f91fdce 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -430,12 +430,11 @@ def visit(self, node, scope): if scope.is_defined(node.id): raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) var = scope.find_variable(node.id) - var_type = fixed_type(var.type, self.current_type) if var.name == 'self': raise SemanticError(SELF_IS_READONLY) - if not node_type.conforms_to(var_type): - raise SemanticError(INCOMPATIBLE_TYPES % (node_type, var_type)) + if not node_type.conforms_to(var.type): + raise SemanticError(INCOMPATIBLE_TYPES % (node_type, var.type)) except SemanticError as ex: self.errors.append((ex.text, node.tid)) node_type = ErrorType() @@ -450,7 +449,7 @@ def visit(self, node, scope): for case in node.branches: self.visit(case, scope.create_child()) type_list.append(case.computed_type) - # The return type of a is unknown until runtime + node.computed_type = LCA(types_list, self.context) @visitor.when(CaseExpressionNode) @@ -490,7 +489,7 @@ def visit(self, node, scope): if node.expr: self.visit(node.expr, scope) - expr_type = fixed_type(node.expr.computed_type, self.current_type) + expr_type = node.expr.computed_type real_type = fixed_type(node_type, self.current_type) if not expr_type.conforms_to(real_type): @@ -525,25 +524,25 @@ def visit(self, node, scope): @visitor.when(WhileLoopNode) def visit(self, node, scope): self.visit(node.condition, scope) - cond_type = fixed_type(node.condition.computed_type, self.current_type) + cond_type = node.condition.computed_type if BoolType() != cond_type: self.errors.append((CONDITION_NOT_BOOL % ('While', cond_type.name), node.token)) self.visit(node.body, scope) - node.computed_type = VoidType() + node.computed_type = self.context.get_type('Object') @visitor.when(FunctionCallNode) def visit(self, node, scope): self.visit(node.obj, scope) - obj_type = fixed_type(node.obj.computed_type, self.current_type) + obj_type = node.obj.computed_type error = False arg_types = [] for arg in node.args: self.visit(arg, scope) - arg = fixed_type(arg.computed_type, self.current_type) + arg = arg.computed_type arg_types.append(arg) try: @@ -570,7 +569,7 @@ def visit(self, node, scope): else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') assert not error - node_type = obj_method.return_type + node_type = fixed_type(obj_method.return_type, self.current_type) except SemanticError as ex: self.errors.append((ex.text, token)) node_type = ErrorType() @@ -588,7 +587,7 @@ def visit(self, node, scope): arg_types = [] for arg in node.args: self.visit(arg, scope) - arg = fixed_type(arg.computed_type, self.current_type) + arg = arg.computed_type arg_types.append(arg) try: @@ -604,7 +603,7 @@ def visit(self, node, scope): else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') assert not error - node_type = obj_method.return_type + node_type = fixed_type(obj_method.return_type, self.current_type) except SemanticError as ex: self.errors.append((ex.text, token)) node_type = ErrorType() @@ -616,10 +615,10 @@ def visit(self, node, scope): @visitor.when(ArithmeticNode) def visit(self, node, scope): self.visit(node.left, scope) - left_type = fixed_type(node.left.computed_type, self.current_type) + left_type = node.left.computed_type self.visit(node.right, scope) - right_type = fixed_type(node.right.computed_type, self.current_type) + right_type = node.right.computed_type if IntType() != right_type or IntType() != left_type: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) @@ -632,10 +631,10 @@ def visit(self, node, scope): @visitor.when(ComparisonNode) def visit(self, node, scope): self.visit(node.left, scope) - left_type = fixed_type(node.left.computed_type, self.current_type) + left_type = node.left.computed_type self.visit(node.right, scope) - right_type = fixed_type(node.right.computed_type, self.current_type) + right_type = node.right.computed_type if IntType() != right_type or IntType() != left_type: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) @@ -661,7 +660,7 @@ def visit(self, node, scope): def visit(self, node, scope): if scope.is_defined(node.lex): var = scope.find_variable(node.lex) - node_type = var.type + node_type = fixed_type(var.type, self.current_type) else: self.errors.append((VARIABLE_NOT_DEFINED % (node.lex), node.token)) node_type = ErrorType() @@ -675,7 +674,8 @@ def visit(self, node, scope): node.computed_type = ErrorType() else: try: - node_type = self.context.get_type(node.type) + raw_type = self.context.get_type(node.type) + node_type = fixed_type(raw_type, self.current_type) except SemanticError as ex: self.errors.append((ex.text, node.ttype)) node_type = ErrorType() @@ -690,7 +690,7 @@ def visit(self, node, scope): @visitor.when(ComplementNode) def visit(self, node, scope): self.visit(node.expr, scope) - expr_type = fixed_type(node.expr.computed_type, self.current_type) + expr_type = node.expr.computed_type if IntType() != expr_type: self.errors.append(("Complement works only for Int", node.symbol)) node.computed_type = ErrorType() @@ -700,7 +700,7 @@ def visit(self, node, scope): @visitor.when(NotNode) def visit(self, node, scope): self.visit(node.expr, scope) - expr_type = fixed_type(node.expr.computed_type, self.current_type) + expr_type = node.expr.computed_type if BoolType() != expr_type: self.errors.append(("Not operator works only for Bool", node.symbol)) node.computed_type = ErrorType() @@ -710,10 +710,10 @@ def visit(self, node, scope): @visitor.when(EqualNode) def visit(self, node, scope): self.visit(node.left, scope) - left_type = fixed_type(node.left.computed_type, self.current_type) + left_type = node.left.computed_type self.visit(node.right, scope) - right_type = fixed_type(node.right.computed_type, self.current_type) + right_type = node.right.computed_type valid_types = [IntType(), BoolType(), StringType()] for op_type in valid_types: From d78abacb68c5cad7e7c20d9a54fb4771ad2cd233 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 27 Apr 2020 17:00:04 -0400 Subject: [PATCH 130/520] [grammar] - Fix precedence of operators --- src/core/cmp/CoolUtils.py | 60 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 9c450cfa..e81f73db 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -161,7 +161,7 @@ def FunctionCallNodeBuilder(obj, calls): feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') -arith, term, factor, factor_2, factor_3, invocation = CoolGrammar.NonTerminals(' ') +arith, term, factor, factor_2, factor_3, factor_4, invocation = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') # terminals @@ -205,13 +205,7 @@ def FunctionCallNodeBuilder(obj, calls): # param %= idx + colon + typex, lambda h, s: (s[1], s[3]) -# -expr %= notx + expr, lambda h, s: NotNode(s[2]) -expr %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3]) -expr %= expr + less + expr, lambda h, s: LessNode(s[1], s[3]) -expr %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3]) -expr %= arith, lambda h, s: s[1] - +# block %= expr + semi, lambda h, s: [s[1]] block %= expr + semi + block, lambda h, s: [s[1]] + s[3] @@ -225,27 +219,43 @@ def FunctionCallNodeBuilder(obj, calls): case_list %= idx + colon + typex + rarrow + expr + semi, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] -# -arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3]) -arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3]) -arith %= term, lambda h, s: s[1] - -# -term %= term + star + factor, lambda h, s: StarNode(s[1], s[3]) -term %= term + div + factor, lambda h, s: DivNode(s[1], s[3]) -term %= factor, lambda h, s: s[1] +# +expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) +expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) +expr %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) +expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +expr %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) +expr %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) +expr %= factor + invocation, lambda h, s: FunctionCallNodeBuilder(s[1], s[2]) +expr %= factor, lambda h, s: s[1] # -factor %= isvoid + factor_2, lambda h, s: IsVoidNode(s[2]) +factor %= compl + factor_2, lambda h, s: ComplementNode(s[2]) factor %= factor_2, lambda h, s: s[1] # -factor_2 %= compl + factor_3, lambda h, s: ComplementNode(s[2]) -factor_2 %= factor_3, lambda h, s: s[1] +factor_2 %= isvoid + term, lambda h, s: IsVoidNode(s[2]) +factor_2 %= term, lambda h, s: s[1] + +# +term %= term + star + arith, lambda h, s: StarNode(s[1], s[3]) +term %= term + div + arith, lambda h, s: DivNode(s[1], s[3]) +term %= arith, lambda h, s: s[1] + +# +arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3]) +arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3]) +arith %= factor_3, lambda h, s: s[1] # -factor_3 %= atom, lambda h, s: s[1] -factor_3 %= atom + invocation, lambda h, s: FunctionCallNodeBuilder(s[1], s[2]) +factor_3 %= factor_3 + leq + factor_4, lambda h, s: LessEqualNode(s[1], s[3]) +factor_3 %= factor_3 + less + factor_4, lambda h, s: LessNode(s[1], s[3]) +factor_3 %= factor_3 + equal + factor_4, lambda h, s: EqualNode(s[1], s[3]) +factor_3 %= factor_4, lambda h, s: s[1] + +# +factor_4 %= notx + atom, lambda h, s: NotNode(s[2]) +factor_4 %= atom, lambda h, s: s[1] # invocation %= func_call, lambda h, s: [s[1]] @@ -269,13 +279,7 @@ def FunctionCallNodeBuilder(obj, calls): atom %= integer, lambda h, s: IntegerNode(s[1]) atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) -atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) -atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) -atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) -atom %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) -atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) -atom %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) atom %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) From cb7456448fbf4b56cef61289edc37b85be0d13c4 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 27 Apr 2020 17:06:13 -0400 Subject: [PATCH 131/520] Auto stash before merge of "grammar_fix" and "origin/TypeChecker" --- src/core/cmp/CoolUtils.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 1926825c..1359d27b 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -401,4 +401,18 @@ class Main { } ; } ; ''' - +_text2 = ''' +class Main inherits IO { + main() : String { + foo(42) + }; + + foo(i : Int) : String { + if i = 0 then "" else + (let next : Int <- i / 10 in + foo(next).concat(foo(i - next * 10)) + ) + fi + }; +}; +''' From c6a2e3289afb626e5824a74137f5a878ffb352e9 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 17:12:42 -0400 Subject: [PATCH 132/520] [checker] - define the builtin types globally --- src/core/cmp/visitors.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 9f91fdce..722f55f9 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -16,6 +16,7 @@ ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] built_in_types = [ 'Int', 'String', 'Bool', 'Object', 'IO', 'SELF_TYPE', 'AUTO_TYPE'] +INT, STRING, BOOL, OBJ = None, None, None, None def define_built_in_types(context): obj = context.create_type('Object') @@ -23,7 +24,8 @@ def define_built_in_types(context): i.set_parent(obj) s = context.create_type('String') s.set_parent(obj) - context.create_type('Bool').set_parent(obj) + b = context.create_type('Bool') + b.set_parent(obj) io = context.create_type('IO') io.set_parent(obj) st = context.create_type('SELF_TYPE') @@ -42,6 +44,9 @@ def define_built_in_types(context): s.define_method('concat', ['s'], [s], s) s.define_method('substr', ['i', 'l'], [i, i], s) + global INT, STRING, BOOL, OBJ + INT, STRING, BOOL, OBJ = i, s, b, obj + def match(type1, type2): return IsAuto(type1.name) or type1.conforms_to(type2) @@ -401,7 +406,6 @@ def visit(self, node, scope): expr_type = fixed_type(node.expr.computed_type, self.current_type) real_type = fixed_type(node.attr_type, self.current_type) - # //TODO: SELF_TYPE match every type??? if not expr_type.conforms_to(real_type): self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, real_type.name), node.arrow)) @@ -509,7 +513,7 @@ def visit(self, node, scope): if node.else_body: self.visit(node.else_body, scope) else_type = node.else_body.computed_type - node_type = LCA([if_body, else_body], self.context) + node_type = LCA([if_type, else_type], self.context) node.computed_type = node_type @@ -530,7 +534,7 @@ def visit(self, node, scope): self.errors.append((CONDITION_NOT_BOOL % ('While', cond_type.name), node.token)) self.visit(node.body, scope) - node.computed_type = self.context.get_type('Object') + node.computed_type = OBJ @visitor.when(FunctionCallNode) def visit(self, node, scope): @@ -587,8 +591,7 @@ def visit(self, node, scope): arg_types = [] for arg in node.args: self.visit(arg, scope) - arg = arg.computed_type - arg_types.append(arg) + arg_types.append(arg.computed_type) try: token = node.tid @@ -624,7 +627,7 @@ def visit(self, node, scope): self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) node_type = ErrorType() else: - node_type = IntType() + node_type = INT node.computed_type = node_type @@ -640,21 +643,21 @@ def visit(self, node, scope): self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) node_type = ErrorType() else: - node_type = BoolType() + node_type = BOOL node.computed_type = node_type @visitor.when(IntegerNode) def visit(self, node, scope): - node.computed_type = IntType() + node.computed_type = INT @visitor.when(StringNode) def visit(self, node, scope): - node.computed_type = self.context.get_type("String") + node.computed_type = STRING @visitor.when(BoolNode) def visit(self, node, scope): - node.computed_type = BoolType() + node.computed_type = BOOL @visitor.when(IdNode) def visit(self, node, scope): @@ -685,7 +688,7 @@ def visit(self, node, scope): @visitor.when(IsVoidNode) def visit(self, node, scope): self.visit(node.expr, scope) - node.computed_type = BoolType() + node.computed_type = BOOL @visitor.when(ComplementNode) def visit(self, node, scope): @@ -695,7 +698,7 @@ def visit(self, node, scope): self.errors.append(("Complement works only for Int", node.symbol)) node.computed_type = ErrorType() else: - node.computed_type = IntType() + node.computed_type = INT @visitor.when(NotNode) def visit(self, node, scope): @@ -705,7 +708,7 @@ def visit(self, node, scope): self.errors.append(("Not operator works only for Bool", node.symbol)) node.computed_type = ErrorType() else: - node.computed_type = BoolType() + node.computed_type = BOOL @visitor.when(EqualNode) def visit(self, node, scope): @@ -718,7 +721,7 @@ def visit(self, node, scope): valid_types = [IntType(), BoolType(), StringType()] for op_type in valid_types: if op_type == right_type and op_type == left_type: - node_type = BoolType() + node_type = BOOL break else: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) From 8f6cc780db9dacbb0215de01ab697f8ddbd58d6d Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 27 Apr 2020 17:14:24 -0400 Subject: [PATCH 133/520] [grammar] - Add symbol param in attributes --- src/core/cmp/CoolUtils.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 1359d27b..3727afb2 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -259,9 +259,9 @@ def __iter__(self): case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] # -expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], None) -expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) -expr %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4]) +expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) +expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) +expr %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) expr %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) expr %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) @@ -269,31 +269,31 @@ def __iter__(self): expr %= factor, lambda h, s: s[1] # -factor %= compl + factor_2, lambda h, s: ComplementNode(s[2]) +factor %= compl + factor_2, lambda h, s: ComplementNode(s[2], s[1]) factor %= factor_2, lambda h, s: s[1] # -factor_2 %= isvoid + term, lambda h, s: IsVoidNode(s[2]) +factor_2 %= isvoid + term, lambda h, s: IsVoidNode(s[2], s[1]) factor_2 %= term, lambda h, s: s[1] # -term %= term + star + arith, lambda h, s: StarNode(s[1], s[3]) -term %= term + div + arith, lambda h, s: DivNode(s[1], s[3]) +term %= term + star + arith, lambda h, s: StarNode(s[1], s[3], s[2]) +term %= term + div + arith, lambda h, s: DivNode(s[1], s[3], s[2]) term %= arith, lambda h, s: s[1] # -arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3]) -arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3]) +arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3], s[2]) +arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3], s[2]) arith %= factor_3, lambda h, s: s[1] # -factor_3 %= factor_3 + leq + factor_4, lambda h, s: LessEqualNode(s[1], s[3]) -factor_3 %= factor_3 + less + factor_4, lambda h, s: LessNode(s[1], s[3]) -factor_3 %= factor_3 + equal + factor_4, lambda h, s: EqualNode(s[1], s[3]) +factor_3 %= factor_3 + leq + factor_4, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +factor_3 %= factor_3 + less + factor_4, lambda h, s: LessNode(s[1], s[3], s[2]) +factor_3 %= factor_3 + equal + factor_4, lambda h, s: EqualNode(s[1], s[3], s[2]) factor_3 %= factor_4, lambda h, s: s[1] # -factor_4 %= notx + atom, lambda h, s: NotNode(s[2]) +factor_4 %= notx + atom, lambda h, s: NotNode(s[2], s[1]) factor_4 %= atom, lambda h, s: s[1] # From 276441ee99a58b6dc1b4ec63038fc2d54d93d318 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 27 Apr 2020 17:38:24 -0400 Subject: [PATCH 134/520] [grammar] - Change order of grammar code and fix boolean operators --- src/core/cmp/CoolUtils.py | 45 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 3727afb2..8abcc97d 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -258,6 +258,24 @@ def __iter__(self): case_list %= idx + colon + typex + rarrow + expr + semi, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] +# +invocation %= func_call, lambda h, s: [s[1]] +invocation %= func_call + invocation, lambda h, s: [s[1]] + s[2] + +# +func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) +func_call %= dot + idx + opar + cpar, lambda h, s: (s[2], []) +func_call %= at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: (s[4], s[6], s[2]) +func_call %= at + typex + dot + idx + opar + cpar, lambda h, s: (s[4], [], s[2]) + +# +arg_list %= expr, lambda h, s: [s[1]] +arg_list %= expr + comma + arg_list, lambda h, s: [s[1]] + s[3] + +# +member_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3]) +member_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) + # expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) @@ -287,29 +305,15 @@ def __iter__(self): arith %= factor_3, lambda h, s: s[1] # -factor_3 %= factor_3 + leq + factor_4, lambda h, s: LessEqualNode(s[1], s[3], s[2]) -factor_3 %= factor_3 + less + factor_4, lambda h, s: LessNode(s[1], s[3], s[2]) -factor_3 %= factor_3 + equal + factor_4, lambda h, s: EqualNode(s[1], s[3], s[2]) +factor_3 %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +factor_3 %= expr + less + expr, lambda h, s: LessNode(s[1], s[3], s[2]) +factor_3 %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3], s[2]) factor_3 %= factor_4, lambda h, s: s[1] # -factor_4 %= notx + atom, lambda h, s: NotNode(s[2], s[1]) +factor_4 %= notx + expr, lambda h, s: NotNode(s[2], s[1]) factor_4 %= atom, lambda h, s: s[1] -# -invocation %= func_call, lambda h, s: [s[1]] -invocation %= func_call + invocation, lambda h, s: [s[1]] + s[2] - -# -func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) -func_call %= dot + idx + opar + cpar, lambda h, s: (s[2], []) -func_call %= at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: (s[4], s[6], s[2]) -func_call %= at + typex + dot + idx + opar + cpar, lambda h, s: (s[4], [], s[2]) - -# -arg_list %= expr, lambda h, s: [s[1]] -arg_list %= expr + comma + arg_list, lambda h, s: [s[1]] + s[3] - # atom %= member_call, lambda h, s: s[1] atom %= new + typex, lambda h, s: NewNode(s[2]) @@ -321,11 +325,6 @@ def __iter__(self): atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) atom %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) - -# -member_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3]) -member_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) - # Parser CoolParser = LR1Parser(CoolGrammar) From 0d1b87767a1c6b38302cc136c51f70d2fff40bd6 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 27 Apr 2020 18:28:14 -0400 Subject: [PATCH 135/520] [grammar] - Add `` terminal and change arithmetic operations --- src/core/cmp/CoolUtils.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 8abcc97d..e6154dd6 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -173,12 +173,12 @@ class BoolNode(AtomicNode): pass def FunctionCallNodeBuilder(obj, calls): - print("-------------------") + #print("-------------------") while len(calls): - print(obj) + #print(obj) obj = FunctionCallNode(obj, *calls[0]) calls.pop(0) - print("-------------------") + #print("-------------------") return obj class Param: @@ -200,7 +200,7 @@ def __iter__(self): feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') -arith, term, factor, factor_2, factor_3, factor_4, invocation = CoolGrammar.NonTerminals(' ') +arith, term, factor, factor_2, factor_3, factor_4, invocation, func_expr = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') # terminals @@ -283,8 +283,11 @@ def __iter__(self): expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) expr %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) expr %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) -expr %= factor + invocation, lambda h, s: FunctionCallNodeBuilder(s[1], s[2]) -expr %= factor, lambda h, s: s[1] +expr %= func_expr, lambda h, s: s[1] + +# +func_expr %= factor + invocation, lambda h, s: FunctionCallNodeBuilder(s[1], s[2]) +func_expr %= factor, lambda h, s: s[1] # factor %= compl + factor_2, lambda h, s: ComplementNode(s[2], s[1]) @@ -295,13 +298,13 @@ def __iter__(self): factor_2 %= term, lambda h, s: s[1] # -term %= term + star + arith, lambda h, s: StarNode(s[1], s[3], s[2]) -term %= term + div + arith, lambda h, s: DivNode(s[1], s[3], s[2]) +term %= expr + star + expr, lambda h, s: StarNode(s[1], s[3], s[2]) +term %= expr + div + expr, lambda h, s: DivNode(s[1], s[3], s[2]) term %= arith, lambda h, s: s[1] # -arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3], s[2]) -arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3], s[2]) +arith %= expr + plus + expr, lambda h, s: PlusNode(s[1], s[3], s[2]) +arith %= expr + minus + expr, lambda h, s: MinusNode(s[1], s[3], s[2]) arith %= factor_3, lambda h, s: s[1] # @@ -311,7 +314,7 @@ def __iter__(self): factor_3 %= factor_4, lambda h, s: s[1] # -factor_4 %= notx + expr, lambda h, s: NotNode(s[2], s[1]) +factor_4 %= notx + func_expr, lambda h, s: NotNode(s[2], s[1]) factor_4 %= atom, lambda h, s: s[1] # From ed8abc6bae2ffd64aa91cde29b38f76ede275128 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 27 Apr 2020 18:44:02 -0400 Subject: [PATCH 136/520] Add more classes for Mips structure --- src/core/cmp/cil_to_mips.py | 17 +++++++++++++++ src/core/cmp/mips.py | 43 +++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index e69de29b..800a4b8a 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -0,0 +1,17 @@ +import core.cmp.visitor as visitor +import core.cmp.cil as cil +import core.cmp.mips as mips + +class BaseCILToMIPSVisitor: + pass + +class CILToMIPSVisitor(BaseCILToMIPSVisitor): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cil.ProgramNode) + def visit(self, node): + pass + + @visitor.when(cil.) \ No newline at end of file diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index a6406774..f1e8da98 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -1,6 +1,6 @@ import core.cmp.visitor as visitor -ATTR_SIZE = 32 +ATTR_SIZE = 4 class Node: pass @@ -34,6 +34,44 @@ class LabelNode(InstructionNode): def __init__(self, name): self.name = name +class MoveNode(InstructionNode): + def __init__(self, reg1, reg2): + self.reg1 = reg1 + self.reg2 = reg2 + +class LoadInmediateNode(InstructionNode): + def __init__(self, reg, value): + self.reg = reg + self.value = value + +class LoadWordNode(InstructionNode): + def __init__(self, reg, addr): + self.reg = reg + self.addr = addr + +class SyscallNode(InstructionNode): + pass + +class LoadAddressNode(InstructionNode): + def __init__(self, reg, label): + self.reg = reg + self.label = label + +class StoreWordNode(InstructionNode): + def __init__(self, reg, addr): + self.reg = reg + self.addr = addr + +class JumpAndLinkNode(InstructionNode): + def __init__(self, label): + self.label = label + +class JumpRegister(InstructionNode): + def __init__(self, reg): + self.reg = reg + + + class MIPSType(): @@ -41,4 +79,5 @@ def __init__(self, name, attributes): self.attributes = attributes def get_offset(self, attr_name): - return ATTR_SIZE * self.attributes.index(attributes) \ No newline at end of file + return ATTR_SIZE * self.attributes.index(attributes) + From 78634e342a53b0e530d6435a5288e6849df23bbc Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 27 Apr 2020 19:30:46 -0400 Subject: [PATCH 137/520] Add class for convert from CIL to MIPS --- src/core/cmp/cil_to_mips.py | 37 ++++++++++++++++++++++++++++++++++--- src/core/cmp/mips.py | 19 ++++++++++--------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 800a4b8a..5df77562 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -3,7 +3,12 @@ import core.cmp.mips as mips class BaseCILToMIPSVisitor: - pass + def __init__(self): + self.types = {} + self.dottext = [] + self.dotdata = [] + + class CILToMIPSVisitor(BaseCILToMIPSVisitor): @visitor.on('node') @@ -12,6 +17,32 @@ def visit(self, node): @visitor.when(cil.ProgramNode) def visit(self, node): - pass + for type_node in node.dottypes: + self.visit(type_node) + + for data_node in node.dotdata: + self.visit(data_node) + + for function_node in node.dotcode: + self.visit(function_node) + + return mips.ProgramNode(dottext, dotdata) + + @visitor.when(cil.TypeNode) + def visit(self, node): + self.types.append(cil_to_mips_type(node)) + + @visitor.when(cil.DataNode) + def visit(self, node): + self.dotdata.append(cil_to_mips_data(node)) + + @visitor.when(cil.FunctionNode) + def visit(self, node): + + + +def cil_to_mips_data(cil_data): + return mips.DataNode(cil_data.name, cil_data.value) - @visitor.when(cil.) \ No newline at end of file +def cil_to_mips_type(cil_type): + return mips.MIPSType(cil_type.name, cil_type.attributes, cil_type.methods) \ No newline at end of file diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index f1e8da98..74b7764c 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -10,18 +10,11 @@ def __init__(self, dottext, dotdata, types): self._dottext = dottext self._dotdata = dotdata -class TextSectionNode(Node): - def __init__(self, functions): - self.functions class FunctionNode(Node): def __init__(self, instructions): self.instructions = instructions -class DataSectionNode(Node): - def __init__(self, data): - self.data = data - class DataNode(Node): def __init__(self, name, value): self.name = name @@ -75,9 +68,17 @@ def __init__(self, reg): class MIPSType(): - def __init__(self, name, attributes): + def __init__(self, name, attributes, methods): self.attributes = attributes + self.methods = methods - def get_offset(self, attr_name): + def get_attr_offset(self, attr_name): return ATTR_SIZE * self.attributes.index(attributes) + + def get_func(self, method_name): + return self.methods[method_name] + + @property + def size(self): + return len(self.attributes) * ATTR_SIZE From a11be59b28889858c07475e25669453adc4875c0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 20:17:27 -0400 Subject: [PATCH 138/520] [checker] - Return ErrorType in LCA if any give is an instance of it --- src/core/cmp/visitors.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 722f55f9..92ae35f7 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -340,9 +340,10 @@ def visit(self, node): # Compute the Lowest Common Ancestor in # the type hierarchy tree def LCA(type_list, context): - known_types = set() counter = {} + if any([isinstance(t, ErrorType) for t in type_list]): + return ErrorType() type_list = [ context.get_type(tp.name) for tp in type_list ] for typex in type_list: node = typex @@ -350,14 +351,12 @@ def LCA(type_list, context): try: counter[node.name] += 1 if counter[node.name] == len(type_list): - return [t for t in known_types if t.name == node.name][0] + return node except KeyError: counter[node.name] = 1 - known_types.add(node) - if node.parent: - node = node.parent - else: + if not node.parent: break + node = node.parent raise Exception('El LCA se partio') @@ -431,7 +430,7 @@ def visit(self, node, scope): node_type = node.expr.computed_type try: - if scope.is_defined(node.id): + if not scope.is_defined(node.id): raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) var = scope.find_variable(node.id) @@ -448,11 +447,11 @@ def visit(self, node, scope): @visitor.when(CaseOfNode) def visit(self, node, scope): self.visit(node.expr, scope) - + types_list = [] for case in node.branches: self.visit(case, scope.create_child()) - type_list.append(case.computed_type) + types_list.append(case.computed_type) node.computed_type = LCA(types_list, self.context) From 8da51fbcba1ef4e02448698787644f611f59b1e2 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 27 Apr 2020 20:17:59 -0400 Subject: [PATCH 139/520] [semantic] - Solve an scope bug --- src/core/cmp/semantic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index ccd3ee0b..6d59f32f 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -223,7 +223,7 @@ def find_variable(self, vname, index=None): try: return next(x for x in locals if x.name == vname) except StopIteration: - return self.parent.find_variable(vname, self.index) if self.parent else None + return self.parent.find_variable(vname, self.index) if self.parent is not None else None def is_defined(self, vname): return self.find_variable(vname) is not None From c7b195e0c25da31d0e1bd54ae9eb996ee5d3003b Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 28 Apr 2020 13:56:30 -0400 Subject: [PATCH 140/520] [checker] - The static type of an operation is always Int or Bool --- src/core/cmp/visitors.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 92ae35f7..2fb24bdf 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -624,11 +624,8 @@ def visit(self, node, scope): if IntType() != right_type or IntType() != left_type: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) - node_type = ErrorType() - else: - node_type = INT - node.computed_type = node_type + node.computed_type = INT @visitor.when(ComparisonNode) def visit(self, node, scope): @@ -640,11 +637,8 @@ def visit(self, node, scope): if IntType() != right_type or IntType() != left_type: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) - node_type = ErrorType() - else: - node_type = BOOL - node.computed_type = node_type + node.computed_type = BOOL @visitor.when(IntegerNode) def visit(self, node, scope): @@ -695,9 +689,7 @@ def visit(self, node, scope): expr_type = node.expr.computed_type if IntType() != expr_type: self.errors.append(("Complement works only for Int", node.symbol)) - node.computed_type = ErrorType() - else: - node.computed_type = INT + node.computed_type = INT @visitor.when(NotNode) def visit(self, node, scope): @@ -705,9 +697,7 @@ def visit(self, node, scope): expr_type = node.expr.computed_type if BoolType() != expr_type: self.errors.append(("Not operator works only for Bool", node.symbol)) - node.computed_type = ErrorType() - else: - node.computed_type = BOOL + node.computed_type = BOOL @visitor.when(EqualNode) def visit(self, node, scope): @@ -718,15 +708,15 @@ def visit(self, node, scope): right_type = node.right.computed_type valid_types = [IntType(), BoolType(), StringType()] - for op_type in valid_types: - if op_type == right_type and op_type == left_type: - node_type = BOOL - break - else: + try: + for op_type in valid_types: + if op_type == right_type or op_type == left_type: + assert op_type == right_type and op_type == left_type + break + except AssertionError: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) - node_type = ErrorType() - node.computed_type = node_type + node.computed_type = BOOL # Type Inference Visitor class InferenceVisitor(TypeChecker): From 3cde8b5a2586ebd1545b7a1bd901f0d8fbe0f51f Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 28 Apr 2020 16:10:28 -0400 Subject: [PATCH 141/520] [grammar] - Update grammar Updates: - Fix precedence of operators - Update and productions --- src/core/cmp/CoolUtils.py | 67 ++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index e6154dd6..c0d81cf4 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -200,7 +200,7 @@ def __iter__(self): feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') -arith, term, factor, factor_2, factor_3, factor_4, invocation, func_expr = CoolGrammar.NonTerminals(' ') +arith, term, func_expr, void, compl_expr = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') # terminals @@ -258,10 +258,6 @@ def __iter__(self): case_list %= idx + colon + typex + rarrow + expr + semi, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] -# -invocation %= func_call, lambda h, s: [s[1]] -invocation %= func_call + invocation, lambda h, s: [s[1]] + s[2] - # func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) func_call %= dot + idx + opar + cpar, lambda h, s: (s[2], []) @@ -277,45 +273,34 @@ def __iter__(self): member_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) # -expr %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) -expr %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) -expr %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) -expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) -expr %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) -expr %= case + expr + of + ocur + case_list + ccur + esac, lambda h, s: CaseOfNode(s[2], s[5]) -expr %= func_expr, lambda h, s: s[1] - -# -func_expr %= factor + invocation, lambda h, s: FunctionCallNodeBuilder(s[1], s[2]) -func_expr %= factor, lambda h, s: s[1] - -# -factor %= compl + factor_2, lambda h, s: ComplementNode(s[2], s[1]) -factor %= factor_2, lambda h, s: s[1] +expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +expr %= notx + expr, lambda h, s: NotNode(s[2], s[1]) +expr %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +expr %= expr + less + expr, lambda h, s: LessNode(s[1], s[3], s[2]) +expr %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3], s[2]) +expr %= arith, lambda h, s: s[1] -# -factor_2 %= isvoid + term, lambda h, s: IsVoidNode(s[2], s[1]) -factor_2 %= term, lambda h, s: s[1] +# +arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3], s[2]) +arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3], s[2]) +arith %= term, lambda h, s: s[1] # -term %= expr + star + expr, lambda h, s: StarNode(s[1], s[3], s[2]) -term %= expr + div + expr, lambda h, s: DivNode(s[1], s[3], s[2]) -term %= arith, lambda h, s: s[1] +term %= term + star + void, lambda h, s: StarNode(s[1], s[3], s[2]) +term %= term + div + void, lambda h, s: DivNode(s[1], s[3], s[2]) +term %= void, lambda h, s: s[1] -# -arith %= expr + plus + expr, lambda h, s: PlusNode(s[1], s[3], s[2]) -arith %= expr + minus + expr, lambda h, s: MinusNode(s[1], s[3], s[2]) -arith %= factor_3, lambda h, s: s[1] +# +void %= isvoid + void, lambda h, s: IsVoidNode(s[2], s[1]) +void %= compl_expr, lambda h, s: s[1] -# -factor_3 %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) -factor_3 %= expr + less + expr, lambda h, s: LessNode(s[1], s[3], s[2]) -factor_3 %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3], s[2]) -factor_3 %= factor_4, lambda h, s: s[1] +# +compl_expr %= compl + compl_expr, lambda h, s: ComplementNode(s[2], s[1]) +compl_expr %= func_expr, lambda h, s: s[1] -# -factor_4 %= notx + func_expr, lambda h, s: NotNode(s[2], s[1]) -factor_4 %= atom, lambda h, s: s[1] +# +func_expr %= func_expr + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) +func_expr %= atom, lambda h, s: s[1] # atom %= member_call, lambda h, s: s[1] @@ -326,7 +311,11 @@ def __iter__(self): atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) -atom %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) +atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) +atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) +atom %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) # Parser CoolParser = LR1Parser(CoolGrammar) From 71a8455ffc33043d537a6f9f744bbed446f4ef2d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 28 Apr 2020 17:14:22 -0400 Subject: [PATCH 142/520] [grammar] - Not and comparison moved to different levels --- src/core/cmp/CoolUtils.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index c0d81cf4..7f42ef6a 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -200,7 +200,7 @@ def __iter__(self): feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') -arith, term, func_expr, void, compl_expr = CoolGrammar.NonTerminals(' ') +arith, term, func_expr, void, compl_expr, not_expr, cmp_expr = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') # terminals @@ -274,11 +274,15 @@ def __iter__(self): # expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) -expr %= notx + expr, lambda h, s: NotNode(s[2], s[1]) -expr %= expr + leq + expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) -expr %= expr + less + expr, lambda h, s: LessNode(s[1], s[3], s[2]) -expr %= expr + equal + expr, lambda h, s: EqualNode(s[1], s[3], s[2]) -expr %= arith, lambda h, s: s[1] +expr %= not_expr, lambda h, s: s[1] + +not_expr %= notx + not_expr, lambda h, s: NotNode(s[2], s[1]) +not_expr %= cmp_expr, lambda h, s: s[1] + +cmp_expr %= cmp_expr + leq + cmp_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +cmp_expr %= cmp_expr + less + cmp_expr, lambda h, s: LessNode(s[1], s[3], s[2]) +cmp_expr %= cmp_expr + equal + cmp_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) +cmp_expr %= arith, lambda h, s: s[1] # arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3], s[2]) From 8777f8757d99c9fcfee3b3d684619c558f62a043 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 28 Apr 2020 17:58:43 -0400 Subject: [PATCH 143/520] Visit method por FunctionNode --- src/core/cmp/cil_to_mips.py | 45 ++++++++++++++++++++++++++ src/core/cmp/mips.py | 64 ++++++++++++++++++++++++++++++++++--- 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 5df77562..7c63d7ec 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -7,8 +7,23 @@ def __init__(self): self.types = {} self.dottext = [] self.dotdata = [] + self.actual_function = None + self.actual_function_instructions = [] + @property + def localvars(self): + return self.actual_function.localvars + + @property + def params(self): + return self.actual_function.parmas + + @property + def cil_instructions(self) + return self.actual_function.instructions + def add_instructions(self, instructions): + self.actual_function_instructions.extend(instructions) class CILToMIPSVisitor(BaseCILToMIPSVisitor): @visitor.on('node') @@ -38,6 +53,36 @@ def visit(self, node): @visitor.when(cil.FunctionNode) def visit(self, node): + self.actual_function = node + + registers_to_save = ['ra', 't0', 't1'] + #Saving Register + # self.add_instructions(mips.save_register(mips.REGISTERS['ra'])) + # self.add_instructions(mips.save_register(mips.REGISTERS['t0'])) + # self.add_instructions(mips.save_register(mips.REGISTERS['t1'])) + self.add_instructions(mips.save_registers(registers_to_save)) + + #Argument received to params + self.add_instructions(mips.MoveNode(mips.REGISTERS['t1'], mips.REGISTERS['t2'])) + + #Allocating memory for local variables + addr = mips.Address(mips.REGISTERS['t0'], 0) + var_size = len(self.localvars) * mips.ATTR_SIZE + self.add_instructions(mips.allocate_memory(addr, var_size)) + + #function_body + for instruction in self.cil_isntructions: + self.visit(instruction) + + #Loading saved register + # self.add_instructions(mips.load_reg_from_stack(mips.REGISTERS['t1'])) + # self.add_instructions(mips.load_reg_from_stack(mips.REGISTERS['t0'])) + # self.add_instructions(mips.load_reg_from_stack(mips.REGISTERS['ra'])) + self.add_instructions(mips.load_registers_from_stack(registers_to_save[-1])) + + self.actual_function_instructions = [] + self.actual_function = None + diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 74b7764c..c093ed70 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -1,6 +1,12 @@ +from itertools import chain import core.cmp.visitor as visitor -ATTR_SIZE = 4 +ATTR_SIZE = 4 +RESGISTER_SIZE = 4 +REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9', 'v0', 'v1', 'a0', 'sp', 'ra'] +REGISTERS = { name: Register(name) for name in REGISTER_NAMES } + + class Node: pass @@ -12,7 +18,8 @@ def __init__(self, dottext, dotdata, types): class FunctionNode(Node): - def __init__(self, instructions): + def __init__(self, name, instructions): + self.name = name self.instructions = instructions class DataNode(Node): @@ -47,12 +54,12 @@ class SyscallNode(InstructionNode): class LoadAddressNode(InstructionNode): def __init__(self, reg, label): - self.reg = reg + self.reg = reg self.label = label class StoreWordNode(InstructionNode): def __init__(self, reg, addr): - self.reg = reg + self.reg = reg self.addr = addr class JumpAndLinkNode(InstructionNode): @@ -63,6 +70,12 @@ class JumpRegister(InstructionNode): def __init__(self, reg): self.reg = reg +class AddInmediateNode(InstructionNode): + def __init__(self, dest, src, value): + self.dest = dest + self.src = src + self.value = value + @@ -70,7 +83,7 @@ def __init__(self, reg): class MIPSType(): def __init__(self, name, attributes, methods): self.attributes = attributes - self.methods = methods + self.methods = methods def get_attr_offset(self, attr_name): return ATTR_SIZE * self.attributes.index(attributes) @@ -82,3 +95,44 @@ def get_func(self, method_name): def size(self): return len(self.attributes) * ATTR_SIZE +class Register(): + def __init__(self, name): + self.name = name + +class Address(): + def __init__(self, reg, offset): + self.offset = offset + self.reg = reg + +def save_register(reg): + move_stack = AddInmediateNode(REGISTERS['sp'], REGISTERS['sp'], -RESGISTER_SIZE) + addr = Address(REGISTERS['sp'], 0) + save_value = StoreWordNode(reg, addr) + return [move_stack, save_value] + +def save_registers(registers): + instructions = [] + instructions.extend(chain.from_iterable([save_register(reg) for reg in registers])) + return instructions + +def load_reg_from_stack(reg): + addr = Address(REGISTERS['sp'], 0) + load_value = LoadWordNode(reg, addr) + move_stack = AddInmediateNode(REGISTERS['sp'], REGISTERS['sp'], RESGISTER_SIZE) + return [load_value, move_stack] + +def load_registers_from_stack(registers): + instructions = [] + instructions.extend(chain.from_iterable([load_reg_from_stack(reg) for reg in registers])) + return instructions + +def allocate_memory(addr, size): + set_operation_number = LoadInmediateNode(REGISTERS['v0'], 9) + set_size = LoadInmediateNode(REGISTERS['a0'], size) + syscall = SyscallNode() + save_pointer = StoreWordNode(REGISTERS['v0'], addr) + return [set_operation_number, set_size, syscall, save_pointer] + + + + From e17e60e30af463522b27ec1a0aae58eff4dddba1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 28 Apr 2020 18:01:34 -0400 Subject: [PATCH 144/520] [grammar] - Creating an individual section for statements --- src/core/cmp/CoolUtils.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 7f42ef6a..905324d3 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -200,7 +200,7 @@ def __iter__(self): feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') -arith, term, func_expr, void, compl_expr, not_expr, cmp_expr = CoolGrammar.NonTerminals(' ') +arith, term, func_expr, void, compl_expr, not_expr, cmp_expr, statement = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') # terminals @@ -300,7 +300,15 @@ def __iter__(self): # compl_expr %= compl + compl_expr, lambda h, s: ComplementNode(s[2], s[1]) -compl_expr %= func_expr, lambda h, s: s[1] +compl_expr %= statement, lambda h, s: s[1] + +statement %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) +statement %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) +statement %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) +statement %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) +statement %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +statement %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) +statement %= func_expr, lambda h, s: s[1] # func_expr %= func_expr + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) From 8e33fd7aae9c54408eb3496b5d81a4437ab22771 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 28 Apr 2020 18:05:49 -0400 Subject: [PATCH 145/520] [grammar] - Update atom productions Remove statements as atom --- src/core/cmp/CoolUtils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 905324d3..39b7a9c8 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -322,12 +322,6 @@ def __iter__(self): atom %= integer, lambda h, s: IntegerNode(s[1]) atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) -atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) -atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) -atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) -atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) -atom %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) -atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) # Parser CoolParser = LR1Parser(CoolGrammar) From 17801496e4a6282287955c5eb3a8c6ef3644a283 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 28 Apr 2020 19:16:44 -0400 Subject: [PATCH 146/520] Add visit for AllocateNode --- src/core/cmp/cil_to_mips.py | 25 ++++++++++++++++++------- src/core/cmp/mips.py | 26 +++++++++++++++++++------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 7c63d7ec..03cdb9a0 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -22,6 +22,12 @@ def params(self): def cil_instructions(self) return self.actual_function.instructions + def variable_index(self, var_name): + for i, var in enumerate self.localvars: + if var.name == var_name: + return i + return -1 + def add_instructions(self, instructions): self.actual_function_instructions.extend(instructions) @@ -45,7 +51,11 @@ def visit(self, node): @visitor.when(cil.TypeNode) def visit(self, node): - self.types.append(cil_to_mips_type(node)) + data_label = mips.TYPE_NAME_LABEL.format(len(self.types)) + mips_type = cil_to_mips_type(node) + mips_type.set_data_label(data_label) + self.types.append(mips_type) + self.dotdata.append(mips.DataNode(data_label, node.name)) @visitor.when(cil.DataNode) def visit(self, node): @@ -57,9 +67,6 @@ def visit(self, node): registers_to_save = ['ra', 't0', 't1'] #Saving Register - # self.add_instructions(mips.save_register(mips.REGISTERS['ra'])) - # self.add_instructions(mips.save_register(mips.REGISTERS['t0'])) - # self.add_instructions(mips.save_register(mips.REGISTERS['t1'])) self.add_instructions(mips.save_registers(registers_to_save)) #Argument received to params @@ -75,14 +82,18 @@ def visit(self, node): self.visit(instruction) #Loading saved register - # self.add_instructions(mips.load_reg_from_stack(mips.REGISTERS['t1'])) - # self.add_instructions(mips.load_reg_from_stack(mips.REGISTERS['t0'])) - # self.add_instructions(mips.load_reg_from_stack(mips.REGISTERS['ra'])) self.add_instructions(mips.load_registers_from_stack(registers_to_save[-1])) self.actual_function_instructions = [] self.actual_function = None + @visitor.when(cil.AllocateNode) + def visit(self, node): + size = self.types[node.vtype].size + var_index = self.variable_index(node.dest.name) + offset = var_index * mips.ATTR_SIZE + addr = mips.Address(mips.REGISTERS['t0'], offset) + self.add_instructions(mips.allocate_memory(addr, size)) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index c093ed70..718ed11c 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -1,11 +1,12 @@ from itertools import chain import core.cmp.visitor as visitor -ATTR_SIZE = 4 -RESGISTER_SIZE = 4 -REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9', 'v0', 'v1', 'a0', 'sp', 'ra'] -REGISTERS = { name: Register(name) for name in REGISTER_NAMES } +ATTR_SIZE = 4 +RESGISTER_SIZE = 4 +REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9', 'v0', 'v1', 'a0', 'sp', 'ra'] +REGISTERS = { name: Register(name) for name in REGISTER_NAMES } +TYPE_NAME_LABEL = f"type_name_{}" class Node: @@ -84,6 +85,18 @@ class MIPSType(): def __init__(self, name, attributes, methods): self.attributes = attributes self.methods = methods + self.data_label = "" + + @property + def size(self): + return len(self.attributes) * ATTR_SIZE + + @property + def data_label(self): + return self.data_label + + def set_data_label(self, data_label): + self.data_label = string def get_attr_offset(self, attr_name): return ATTR_SIZE * self.attributes.index(attributes) @@ -91,9 +104,8 @@ def get_attr_offset(self, attr_name): def get_func(self, method_name): return self.methods[method_name] - @property - def size(self): - return len(self.attributes) * ATTR_SIZE + + class Register(): def __init__(self, name): From c45090402a02c59e1219ac9bb9c6887199c8ccd6 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 28 Apr 2020 20:38:08 -0400 Subject: [PATCH 147/520] [checker] - Define inherited attributes in Scope --- src/core/cmp/visitors.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 2fb24bdf..c8d7f770 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -390,8 +390,13 @@ def visit(self, node, scope): self.current_type = self.context.get_type(node.id) scope.define_variable('self', self.current_type) - for attr in self.current_type.attributes: - scope.define_variable(attr.name, attr.type) + cur_type = self.current_type + while True: + for attr in cur_type.attributes: + scope.define_variable(attr.name, attr.type) + if not cur_type.parent: + break + cur_type = cur_type.parent for feature in node.features: self.visit(feature, scope.create_child()) From fddfbf0e16dd61e14b3686c1cb029dbaac0fb6b5 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 29 Apr 2020 00:12:51 -0400 Subject: [PATCH 148/520] [checker] - FunctionCall return type updated --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index c8d7f770..3394d896 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -577,7 +577,7 @@ def visit(self, node, scope): else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') assert not error - node_type = fixed_type(obj_method.return_type, self.current_type) + node_type = fixed_type(obj_method.return_type, obj_type) except SemanticError as ex: self.errors.append((ex.text, token)) node_type = ErrorType() From 77be1cd09a67a33249a4fc5aa465c37fb8ffee7d Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 29 Apr 2020 11:14:09 -0400 Subject: [PATCH 149/520] [grammar] - Move to --- src/core/cmp/CoolUtils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 39b7a9c8..5cfb7668 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -200,7 +200,7 @@ def __iter__(self): feature_list, feature = CoolGrammar.NonTerminals(' ') param_list, param = CoolGrammar.NonTerminals(' ') expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') -arith, term, func_expr, void, compl_expr, not_expr, cmp_expr, statement = CoolGrammar.NonTerminals(' ') +arith, term, func_expr, void, compl_expr, cmp_expr, statement = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') # terminals @@ -274,14 +274,12 @@ def __iter__(self): # expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) -expr %= not_expr, lambda h, s: s[1] - -not_expr %= notx + not_expr, lambda h, s: NotNode(s[2], s[1]) -not_expr %= cmp_expr, lambda h, s: s[1] +expr %= cmp_expr, lambda h, s: s[1] cmp_expr %= cmp_expr + leq + cmp_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) cmp_expr %= cmp_expr + less + cmp_expr, lambda h, s: LessNode(s[1], s[3], s[2]) cmp_expr %= cmp_expr + equal + cmp_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) +cmp_expr %= notx + cmp_expr, lambda h, s: NotNode(s[2], s[1]) cmp_expr %= arith, lambda h, s: s[1] # From aae4486ec87a6c96979005b86da538ffd7c5cae2 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 29 Apr 2020 15:06:46 -0400 Subject: [PATCH 150/520] [semantic] - Add `all_attributes`and `all_methods` methods to `Type` --- src/core/cmp/semantic.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 6613abd0..10d08b07 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -1,4 +1,5 @@ import itertools as itt +from collections import OrderedDict class SemanticError(Exception): @property @@ -97,6 +98,18 @@ def define_method(self, name:str, param_names:list, param_types:list, return_typ method = self.methods[name] = Method(name, param_names, param_types, return_type) return method + def all_attributes(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_attributes(False) + for attr in self.attributes: + plain[attr.name] = (attr, self) + return plain.values() if clean else plain + + def all_methods(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) + for method in self.methods: + plain[method.name] = (method, self) + return plain.values() if clean else plain + def conforms_to(self, other): return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) From e6858e0f47f4fcb24a35eb031b0dee79f2711dc7 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 30 Apr 2020 12:56:06 -0400 Subject: [PATCH 151/520] [checker] - Use new to instantiate every type --- src/core/cmp/visitors.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 3394d896..b5dda970 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -670,18 +670,14 @@ def visit(self, node, scope): @visitor.when(NewNode) def visit(self, node, scope): - if node.type in built_in_types[:3]: - self.errors.append((f'It cannot be initialized a {node.type} with the new keyword', node.ttype)) - node.computed_type = ErrorType() - else: - try: - raw_type = self.context.get_type(node.type) - node_type = fixed_type(raw_type, self.current_type) - except SemanticError as ex: - self.errors.append((ex.text, node.ttype)) - node_type = ErrorType() - - node.computed_type = node_type + try: + raw_type = self.context.get_type(node.type) + node_type = fixed_type(raw_type, self.current_type) + except SemanticError as ex: + self.errors.append((ex.text, node.ttype)) + node_type = ErrorType() + + node.computed_type = node_type @visitor.when(IsVoidNode) def visit(self, node, scope): From 872a1f7cb206c0b3126b4c8c5b26e3510ac050c4 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 30 Apr 2020 17:06:56 -0400 Subject: [PATCH 152/520] [grammar] - 3rd Grammar attemp --- src/core/cmp/CoolUtils.py | 45 ++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 5cfb7668..532f54bb 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -202,6 +202,7 @@ def __iter__(self): expr, member_call, expr_list, block, let_list, case_list = CoolGrammar.NonTerminals(' ') arith, term, func_expr, void, compl_expr, cmp_expr, statement = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') +final_expr, unary_expr = CoolGrammar.NonTerminals(' ') # terminals classx, inherits = CoolGrammar.Terminals('class inherits') @@ -273,13 +274,30 @@ def __iter__(self): member_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) # -expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +expr %= arith + plus + final_expr, lambda h, s: PlusNode(s[1], s[3], s[2]) +expr %= arith + minus + final_expr, lambda h, s: MinusNode(s[1], s[3], s[2]) +expr %= arith + star + final_expr, lambda h, s: StarNode(s[1], s[3], s[2]) +expr %= arith + div + final_expr, lambda h, s: DivNode(s[1], s[3], s[2]) +expr %= arith + leq + final_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +expr %= arith + less + final_expr, lambda h, s: LessNode(s[1], s[3], s[2]) +expr %= arith + equal + final_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) +expr %= unary_expr, lambda h, s: s[1] expr %= cmp_expr, lambda h, s: s[1] -cmp_expr %= cmp_expr + leq + cmp_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) -cmp_expr %= cmp_expr + less + cmp_expr, lambda h, s: LessNode(s[1], s[3], s[2]) -cmp_expr %= cmp_expr + equal + cmp_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) -cmp_expr %= notx + cmp_expr, lambda h, s: NotNode(s[2], s[1]) +# +unary_expr %= isvoid + unary_expr, lambda h, s: IsVoidNode(s[1], s[3], s[2]) +unary_expr %= compl + unary_expr, lambda h, s: ComplementNode(s[1], s[3], s[2]) +unary_expr %= final_expr, lambda h, s: s[1] + +# +final_expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +final_expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +final_expr %= notx + expr, lambda h, s: AssignNode(s[1], s[3]) + +# +cmp_expr %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +cmp_expr %= arith + less + arith, lambda h, s: LessNode(s[1], s[3], s[2]) +cmp_expr %= arith + equal + arith, lambda h, s: EqualNode(s[1], s[3], s[2]) cmp_expr %= arith, lambda h, s: s[1] # @@ -297,16 +315,8 @@ def __iter__(self): void %= compl_expr, lambda h, s: s[1] # -compl_expr %= compl + compl_expr, lambda h, s: ComplementNode(s[2], s[1]) -compl_expr %= statement, lambda h, s: s[1] - -statement %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) -statement %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) -statement %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) -statement %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) -statement %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) -statement %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) -statement %= func_expr, lambda h, s: s[1] +compl_expr %= compl + void, lambda h, s: ComplementNode(s[2], s[1]) +compl_expr %= func_expr, lambda h, s: s[1] # func_expr %= func_expr + func_call, lambda h, s: FunctionCallNode(s[1], *s[2]) @@ -320,6 +330,11 @@ def __iter__(self): atom %= integer, lambda h, s: IntegerNode(s[1]) atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) +atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) +atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) +atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) +atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) +atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) # Parser CoolParser = LR1Parser(CoolGrammar) From 26d0707bd5c82b1553d4bf25604008a93a64f340 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 30 Apr 2020 18:55:32 -0400 Subject: [PATCH 153/520] [grammar] - Solve precedence errors in non terminal --- src/core/cmp/CoolUtils.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 532f54bb..d7604e45 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -215,6 +215,7 @@ def __iter__(self): notx, less, leq, equal = CoolGrammar.Terminals('not < <= =') new, idx, typex, integer, string, boolx = CoolGrammar.Terminals('new id type integer string bool') eof = CoolGrammar.EOF + # productions program %= class_list, lambda h, s: ProgramNode(s[1]) @@ -276,8 +277,12 @@ def __iter__(self): # expr %= arith + plus + final_expr, lambda h, s: PlusNode(s[1], s[3], s[2]) expr %= arith + minus + final_expr, lambda h, s: MinusNode(s[1], s[3], s[2]) -expr %= arith + star + final_expr, lambda h, s: StarNode(s[1], s[3], s[2]) -expr %= arith + div + final_expr, lambda h, s: DivNode(s[1], s[3], s[2]) +expr %= term + star + final_expr, lambda h, s: StarNode(s[1], s[3], s[2]) +expr %= term + div + final_expr, lambda h, s: DivNode(s[1], s[3], s[2]) +expr %= arith + plus + term + star + final_expr, lambda h, s: PlusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) +expr %= arith + minus + term + star + final_expr, lambda h, s: MinusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) +expr %= arith + plus + term + div + final_expr, lambda h, s: PlusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) +expr %= arith + minus + term + div + final_expr, lambda h, s: MinusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) expr %= arith + leq + final_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) expr %= arith + less + final_expr, lambda h, s: LessNode(s[1], s[3], s[2]) expr %= arith + equal + final_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) @@ -292,7 +297,7 @@ def __iter__(self): # final_expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) final_expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) -final_expr %= notx + expr, lambda h, s: AssignNode(s[1], s[3]) +final_expr %= notx + expr, lambda h, s: NotNode(s[2], s[1]) # cmp_expr %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[3], s[2]) From 36b9ba6bc2abe2166e3deb3975d052bace0673dd Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 30 Apr 2020 20:20:04 -0400 Subject: [PATCH 154/520] [grammar] - Always try to add unary operators before a final --- src/core/cmp/CoolUtils.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index d7604e45..593ebf62 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -275,17 +275,17 @@ def __iter__(self): member_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) # -expr %= arith + plus + final_expr, lambda h, s: PlusNode(s[1], s[3], s[2]) -expr %= arith + minus + final_expr, lambda h, s: MinusNode(s[1], s[3], s[2]) -expr %= term + star + final_expr, lambda h, s: StarNode(s[1], s[3], s[2]) -expr %= term + div + final_expr, lambda h, s: DivNode(s[1], s[3], s[2]) -expr %= arith + plus + term + star + final_expr, lambda h, s: PlusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) -expr %= arith + minus + term + star + final_expr, lambda h, s: MinusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) -expr %= arith + plus + term + div + final_expr, lambda h, s: PlusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) -expr %= arith + minus + term + div + final_expr, lambda h, s: MinusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) -expr %= arith + leq + final_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) -expr %= arith + less + final_expr, lambda h, s: LessNode(s[1], s[3], s[2]) -expr %= arith + equal + final_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) +expr %= arith + plus + unary_expr, lambda h, s: PlusNode(s[1], s[3], s[2]) +expr %= arith + minus + unary_expr, lambda h, s: MinusNode(s[1], s[3], s[2]) +expr %= term + star + unary_expr, lambda h, s: StarNode(s[1], s[3], s[2]) +expr %= term + div + unary_expr, lambda h, s: DivNode(s[1], s[3], s[2]) +expr %= arith + plus + term + star + unary_expr, lambda h, s: PlusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) +expr %= arith + minus + term + star + unary_expr, lambda h, s: MinusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) +expr %= arith + plus + term + div + unary_expr, lambda h, s: PlusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) +expr %= arith + minus + term + div + unary_expr, lambda h, s: MinusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) +expr %= arith + leq + unary_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +expr %= arith + less + unary_expr, lambda h, s: LessNode(s[1], s[3], s[2]) +expr %= arith + equal + unary_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) expr %= unary_expr, lambda h, s: s[1] expr %= cmp_expr, lambda h, s: s[1] From e9819fe1302e72bdf1b33d458b363e2918ec30f9 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 30 Apr 2020 21:11:15 -0400 Subject: [PATCH 155/520] [grammar][checker] - If command always has an else --- src/core/cmp/CoolUtils.py | 3 +-- src/core/cmp/visitors.py | 11 ++++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 593ebf62..75152fd7 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -50,7 +50,7 @@ class ExpressionNode(Node): pass class IfThenElseNode(ExpressionNode): - def __init__(self, condition, if_body, if_token, else_body=None): + def __init__(self, condition, if_body, if_token, else_body): self.token = if_token self.condition = condition self.if_body = if_body @@ -336,7 +336,6 @@ def __iter__(self): atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) -atom %= ifx + expr + then + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1]) atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index b5dda970..9b3a944a 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -512,14 +512,11 @@ def visit(self, node, scope): self.errors.append((CONDITION_NOT_BOOL % ('If', cond_type.name), node.token)) self.visit(node.if_body, scope) - node_type = if_type = node.if_body.computed_type + if_type = node.if_body.computed_type - if node.else_body: - self.visit(node.else_body, scope) - else_type = node.else_body.computed_type - node_type = LCA([if_type, else_type], self.context) - - node.computed_type = node_type + self.visit(node.else_body, scope) + else_type = node.else_body.computed_type + node.computed_type = LCA([if_type, else_type], self.context) @visitor.when(BlockNode) def visit(self, node, scope): From 43786318ea967a5d602e1eeae637554d33d2b11f Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 2 May 2020 11:02:37 -0400 Subject: [PATCH 156/520] [cil] - Update dispatch Changes: - Determine type of caller in a dispatch and use Static and Dynamic dispatch suitably. - Add self arg to function call correctly --- src/core/cmp/cool_to_cil.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f26dfb20..44366d67 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -166,10 +166,9 @@ def visit(self, node, scope): ###################################################### self.current_function = self.register_function('entry') - instance = self.define_internal_local() result = self.define_internal_local() - self.register_instruction(cil.AllocateNode('Main', instance)) - self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.AllocateNode('Main', self.vself.name)) + self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) self.register_built_in() @@ -504,7 +503,7 @@ def visit(self, node, scope): # node.args -> [ ExpressionNode ... ] # node.type -> str ##################################### - + args = [] for arg in node.args: vname = self.register_local(VariableInfo(f'{node.id}_arg')) @@ -513,21 +512,32 @@ def visit(self, node, scope): args.append(cil.ArgNode(vname)) result = self.register_local(VariableInfo(f'return_value_of_{node.id}')) - self.register_instruction(cil.ArgNode(self.vself.name)) - for arg in args: - self.register_instruction(arg) - if node.type: #Call of type @.id(,...,) at_type = [typex for typex in self.dottypes if typex.name == node.type][0] + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode(at_type.name, instance)) + #self for Static Dispatch + self.register_instruction(cil.ArgNode(instance)) + for arg in args: + self.register_instruction(arg) + #method = [method for method in at_type.methods if method.name == node.id][0] - self.register_instruction(cil.DynamicCallNode(at_type.name, self.to_function_name(node.id, at_type.name), result)) + #Shall we look method node.id in at_type parents??? + self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, at_type.name), result)) scope.ret_expr = result else: #Call of type .(,...,) + type_of_node = self.register_local(VariableInfo(f'{node.id}_type')) + self.register_instruction(cil.TypeOfNode(node.obj.lex, type_of_node)) + #self for Dynamic Dispatch + self.register_instruction(cil.ArgNode(type_of_node)) + for arg in args: + self.register_instruction(arg) + #//TODO: Check if node,obj's type is void, and in that case throw runtime error _, vtype = [vinfo for vinfo in scope.locals if vinfo.name == node.obj.lex][0] - self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, vtype), result)) + self.register_instruction(cil.DynamicCallNode(type_of_node, self.to_function_name(node.id, vtype), result)) scope.ret_expr = result @visitor.when(cool.MemberCallNode) From 2ec794edf90fd4f0eeea07d99d7f2fd504297bb8 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 18 May 2020 15:13:40 -0400 Subject: [PATCH 157/520] [cil] - Add ExitNode to cil AST --- src/core/cmp/cil.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 62df4d3a..0cf9e602 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -140,6 +140,9 @@ def __init__(self, dest, msg): self.dest = dest self.msg = msg +class ExitNode(InstructionNode): + pass + class TypeNameNode(InstructionNode): def __init__(self, dest, source): self.dest = dest From bf2b354514f3fde357543044e63845936822b4c1 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 18 May 2020 15:15:35 -0400 Subject: [PATCH 158/520] [cil] - Generate cil code for Object:abort data_0 is the address in .Data for the error message printed by abort. --- src/core/cmp/cool_to_cil.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 44366d67..443c2d3b 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -77,7 +77,11 @@ def register_built_in(self): type_node = self.register_type('Object') self.current_function = self.register_function(self.to_function_name('abort', 'Object')) - #//TODO: Code of abort + vname = self.define_internal_local() + self.register_instruction(cil.LoadNode(vname, 'data_0')) + self.register_instruction(cil.PrintNode(vname)) + self.register_instruction(cil.ExitNode()) + # No need for RETURN here right?? self.current_function = self.register_function(self.to_function_name('type_name', 'Object')) self.register_param(self.vself) @@ -172,6 +176,8 @@ def visit(self, node, scope): self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) self.register_built_in() + # Error message raised by Object:abort() + self.register_data('Program aborted') self.current_function = None for declaration, child_scope in zip(node.declarations, scope.children): From 9964efae08a3ca872a0978055b5759bb99a3353a Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 18 May 2020 15:37:38 -0400 Subject: [PATCH 159/520] [cil] - Remove unnecessary ArgNode in register_built_in --- src/core/cmp/cool_to_cil.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 443c2d3b..127263a7 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -86,14 +86,12 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('type_name', 'Object')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction(cil.TypeNameNode(result, self.vself.name)) self.register_instruction(cil.ReturnNode(result)) self.current_function = self.register_function(self.to_function_name('copy', 'Object')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction(cil.CopyNode(result, self.vself.name)) self.register_instruction(cil.ReturnNode(result)) From 9940354f6cd6ec49b93439b5e4fdfb05736fdf9a Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 18 May 2020 17:31:36 -0400 Subject: [PATCH 160/520] [cil] - Add dict with default values for built-in types Added to `BaseCOOLToCILVisitor` --- src/core/cmp/cool_to_cil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 127263a7..44099a4b 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -13,6 +13,7 @@ def __init__(self, context): self.current_function = None self.context = context self.vself = VariableInfo('self', None) + self.default_values = {'Int': 0, 'String': '', 'Bool': False} @property def params(self): From 058e676996a3767edf74285b53298b2655eafc25 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 18 May 2020 17:39:32 -0400 Subject: [PATCH 161/520] [cil] - Add an`init` function for every type to initialize its attrs Update old `register_instruction(cil.AllocateNode(...))` --- src/core/cmp/cool_to_cil.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 44099a4b..e0282d04 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -198,10 +198,23 @@ def visit(self, node, scope): type_node = self.register_type(node.id) type_node.attributes = [attr.name for attr, _ in self.current_type.all_attributes()] type_node.methods = [(method.name, self.to_function_name(method.name, xtype.name)) for method, xtype in self.current_type.all_methods()] - + func_declarations = (f for f in node.features if isinstance(f, cool.FuncDeclarationNode)) for feature, child_scope in zip(func_declarations, scope.children): self.visit(feature, child_scope) + + #init + self.current_function = self.register_function(self.to_function_name('init', node.id)) + #allocate + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.id, instance)) + scope.ret_expr = instance + + attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) + for feature in attr_declarations: + self.visit(feature, scope) + self.register_instruction(cil.ReturnNode(instance)) + self.current_function = None self.current_type = None @@ -519,9 +532,10 @@ def visit(self, node, scope): if node.type: #Call of type @.id(,...,) + #Is ok to search node.type in dottypes??? at_type = [typex for typex in self.dottypes if typex.name == node.type][0] instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode(at_type.name, instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', at_type.name), instance)) #self for Static Dispatch self.register_instruction(cil.ArgNode(instance)) for arg in args: @@ -535,14 +549,14 @@ def visit(self, node, scope): #Call of type .(,...,) type_of_node = self.register_local(VariableInfo(f'{node.id}_type')) self.register_instruction(cil.TypeOfNode(node.obj.lex, type_of_node)) + instance = self.define_internal_local() + self.register_instruction(cil.DynamicCallNode(type_of_node, 'init', instance)) #self for Dynamic Dispatch - self.register_instruction(cil.ArgNode(type_of_node)) + self.register_instruction(cil.ArgNode(instance)) for arg in args: self.register_instruction(arg) - #//TODO: Check if node,obj's type is void, and in that case throw runtime error - _, vtype = [vinfo for vinfo in scope.locals if vinfo.name == node.obj.lex][0] - self.register_instruction(cil.DynamicCallNode(type_of_node, self.to_function_name(node.id, vtype), result)) + self.register_instruction(cil.DynamicCallNode(type_of_node, node.id, result)) scope.ret_expr = result @visitor.when(cool.MemberCallNode) @@ -574,7 +588,7 @@ def visit(self, node, scope): # node.type -> str ############################### instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode(node.type, instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', node.type), instance)) scope.ret_expr = instance @visitor.when(cool.IntegerNode) From da860a2948251e96077552170bc2b752c1184799 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 18 May 2020 17:40:46 -0400 Subject: [PATCH 162/520] [cil] - Implement visit of `AttrDeclarationNode` --- src/core/cmp/cool_to_cil.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index e0282d04..3342b20c 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -225,8 +225,15 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - #//TODO: Implement AttrDeclarationNode, assess whether this needs to be done - pass + instance = scope.ret_expr + if node.expr: + self.visit(node.expr, scope) + else: + try: + scope.ret_expr = self.default_values[node.type] + except KeyError: + scope.ret_expr = None + self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr)) @visitor.when(cool.FuncDeclarationNode) def visit(self, node, scope): From 9467d1ffdbba40f16270a725079d7bc9c27d84ac Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 18 May 2020 17:42:41 -0400 Subject: [PATCH 163/520] [cil] - Fix creation of `cil.SetAttribNode` and `cil.GetAttribNode` --- src/core/cmp/cool_to_cil.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3342b20c..3caadf2c 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -388,7 +388,8 @@ def visit(self, node, scope): try: self.current_type.attributes.get_attribute(node.id) - self.register_instruction(cil.SetAttribNode(self.current_type.name, node.id, scope.ret_expr)) + self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr)) + scope.ret_expr = node.id except SemanticError: vname = self.register_local(VariableInfo(node.id, None)) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) @@ -613,7 +614,7 @@ def visit(self, node, scope): try: self.current_type.attributes.get_attribute(node.lex) attr = self.register_local(VariableInfo('attr_value', None)) - self.register_instruction(cil.GetAttribNode(attr, self.current_type.name, node.lex)) + self.register_instruction(cil.GetAttribNode(attr, self.vself, node.lex)) scope.ret_expr = attr except SemanticError: param_names = [pn.name for pn in self.current_function.params] From cd1c00f90fa48d83a818cbdb6916db4df8d00c6a Mon Sep 17 00:00:00 2001 From: Lazaro Raul <47638426+stdevRulo@users.noreply.github.com> Date: Tue, 19 May 2020 21:51:22 -0400 Subject: [PATCH 164/520] refs #2 - Suggestions - Unnecesary exception Co-authored-by: Miguel Tenorio <46824157+stdevAntiD2ta@users.noreply.github.com> --- src/core/cmp/visitors.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 9b3a944a..d48148c6 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -358,7 +358,6 @@ def LCA(type_list, context): break node = node.parent - raise Exception('El LCA se partio') def IsAuto(name): return name == 'AUTO_TYPE' or IsVoid(name) From 6cb0c97e03e59aca67f450e3d9b255f8e18ccc96 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 30 May 2020 11:31:16 -0400 Subject: [PATCH 165/520] Types and Data sections --- src/core/cmp/cil_to_mips.py | 101 ++++++++++++++++++++++++++++++------ src/core/cmp/mips.py | 71 +++++++++++++++++++------ src/core/cmp/utils.py | 20 ++++++- src/main.py | 18 ++++--- 4 files changed, 171 insertions(+), 39 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 03cdb9a0..4800033e 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -1,6 +1,7 @@ import core.cmp.visitor as visitor import core.cmp.cil as cil import core.cmp.mips as mips +from core.cmp.utils import CountDict class BaseCILToMIPSVisitor: def __init__(self): @@ -9,7 +10,12 @@ def __init__(self): self.dotdata = [] self.actual_function = None self.actual_function_instructions = [] - + self.types_dict = CountDict() + self.funcs_dict = CountDict() + self.str_consts = CountDict() + self.int_consts = CountDict() + + @property def localvars(self): return self.actual_function.localvars @@ -19,11 +25,11 @@ def params(self): return self.actual_function.parmas @property - def cil_instructions(self) + def cil_instructions(self): return self.actual_function.instructions def variable_index(self, var_name): - for i, var in enumerate self.localvars: + for i, var in enumerate(self.localvars): if var.name == var_name: return i return -1 @@ -31,6 +37,47 @@ def variable_index(self, var_name): def add_instructions(self, instructions): self.actual_function_instructions.extend(instructions) + def get_str_const(self, string): + try: + str_const = self.str_consts.get(string) + except: + name = f'str_const_{len(self.str_consts)}' + str_const = mips.StringConst(name, string) + self.dotdata.append(str_const) + self.str_consts.add(string, str_const) + + return str_const.label + + def new_type(self, name, attributes, methods): + label = f'type_{len(self.types_dict)}' + name = self.get_str_const(name) + size = (len(attributes) * mips.ATTR_SIZE) + mips.TYPE_METADATA_SIZE + + new_type = mips.MIPSType(label, name, size, methods) + self.types_dict.add(name, new_type) + func_names = [] + for _, func_name in methods: + new_func_name = f'func_{len(self.funcs_dict)}' + try: + new_func_name = self.funcs_dict.get(func_name) + except: + self.funcs_dict.add(func_name, new_func_name) + func_names.append(new_func_name) + + self.dotdata.append(mips.TypeDesc(label, name, size, func_names)) + + def register_basic_types_names(self): + self.types_dict.add("String", "string_type") + self.types_dict.add("Int", "int_type") + self.types_dict.add("Bool", "bool_type") + self.types_dict.add("Object", "object_type") + + + + + + + class CILToMIPSVisitor(BaseCILToMIPSVisitor): @visitor.on('node') def visit(self, node): @@ -43,23 +90,20 @@ def visit(self, node): for data_node in node.dotdata: self.visit(data_node) - - for function_node in node.dotcode: - self.visit(function_node) - - return mips.ProgramNode(dottext, dotdata) +# + # for function_node in node.dotcode: + # self.visit(function_node) + # + # return mips.ProgramNode(dottext, dotdata) @visitor.when(cil.TypeNode) def visit(self, node): - data_label = mips.TYPE_NAME_LABEL.format(len(self.types)) - mips_type = cil_to_mips_type(node) - mips_type.set_data_label(data_label) - self.types.append(mips_type) - self.dotdata.append(mips.DataNode(data_label, node.name)) + self.new_type(node.name, node.attributes, node.methods) + @visitor.when(cil.DataNode) def visit(self, node): - self.dotdata.append(cil_to_mips_data(node)) + self.get_str_const(node.string) @visitor.when(cil.FunctionNode) def visit(self, node): @@ -95,10 +139,37 @@ def visit(self, node): addr = mips.Address(mips.REGISTERS['t0'], offset) self.add_instructions(mips.allocate_memory(addr, size)) + + + + def cil_to_mips_data(cil_data): return mips.DataNode(cil_data.name, cil_data.value) def cil_to_mips_type(cil_type): - return mips.MIPSType(cil_type.name, cil_type.attributes, cil_type.methods) \ No newline at end of file + return mips.MIPSType(cil_type.name, cil_type.attributes, cil_type.methods) + + + + + +#TEST +CIL_TYPE_1 = cil.TypeNode("myType") +CIL_TYPE_1.attributes = ["attr1", "attr2", "attr3"] +CIL_TYPE_1.methods = [("method1", "func1"), ("method2", "func2"), ("method3", "func3"), ("method4", "func4")] +CIL_TYPE_2 = cil.TypeNode("myType2") +CIL_TYPE_2.attributes = ["attr1", "attr2"] +CIL_TYPE_2.methods = [("method1", "func5"), ("method2", "func2"), ("method3", "func6"), ("method4", "func7")] +CIL_AST_TEST = cil.ProgramNode([],[],[]) +CIL_AST_TEST.dottypes = [CIL_TYPE_1, CIL_TYPE_2] + + +# if __name__ == '__main__': +def test(): + conv = CILToMIPSVisitor() + conv.visit(CIL_AST_TEST) + for d in conv.dotdata: + print(d) + diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 718ed11c..dacf763a 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -1,14 +1,22 @@ from itertools import chain import core.cmp.visitor as visitor -ATTR_SIZE = 4 -RESGISTER_SIZE = 4 -REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9', 'v0', 'v1', 'a0', 'sp', 'ra'] -REGISTERS = { name: Register(name) for name in REGISTER_NAMES } +ATTR_SIZE = 4 +RESGISTER_SIZE = 4 +REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9', 'v0', 'v1', 'a0', 'sp', 'ra'] +STRING_TYPE = "string_type" -TYPE_NAME_LABEL = f"type_name_{}" +# TYPE_NAME_LABEL = f"type_name_{}" +TYPE_METADATA_SIZE = 4 + +class Register(): + def __init__(self, name): + self.name = name + +REGISTERS = { name: Register(name) for name in REGISTER_NAMES } + class Node: pass @@ -24,9 +32,35 @@ def __init__(self, name, instructions): self.instructions = instructions class DataNode(Node): - def __init__(self, name, value): - self.name = name - self.value = value + def __init__(self, label): + self._label = label + + @property + def label(self): + return self._label + +class StringConst(DataNode): + def __init__(self, label, string): + super().__init__(label) + self._string = string + + def __repr__(self): + return f'STRING_CONST {self._label} {STRING_TYPE} {self._string}' + +class TypeDesc(DataNode): + def __init__(self, label, name, size, methods): + super().__init__(label) + self._size = size + self._name = name + self._methods = methods + + def __repr__(self): + print(self._name) + print(self._size) + return f'TYPE_DESC {self._name} {self._size} {self._label} {self._methods}' + + + class InstructionNode(Node): pass @@ -82,10 +116,12 @@ def __init__(self, dest, src, value): class MIPSType(): - def __init__(self, name, attributes, methods): - self.attributes = attributes - self.methods = methods - self.data_label = "" + def __init__(self, label, name, size, methods): + self._label = label + self._name = name + self._size = size + self._methods = methods + @property def size(self): @@ -103,13 +139,18 @@ def get_attr_offset(self, attr_name): def get_func(self, method_name): return self.methods[method_name] + +class Label(): + def __init__(self, name): + self._name = name + + def __repr__(self): + return self._name + -class Register(): - def __init__(self, name): - self.name = name class Address(): def __init__(self, reg, offset): diff --git a/src/core/cmp/utils.py b/src/core/cmp/utils.py index bb449000..1d3ac8a0 100644 --- a/src/core/cmp/utils.py +++ b/src/core/cmp/utils.py @@ -267,4 +267,22 @@ def __call__(self, w, get_shift_reduce=False): return (output if not get_shift_reduce else(output,operations)), (False, None) # Your code here!!! (Invalid case) else: - raise ValueError \ No newline at end of file + raise ValueError + +class CountDict(): + def __init__(self): + self._dict = {} + self._count = 0 + + def add(self, key, value): + try: + oldv = self._dict[key] + except: + self._count += 1 + self._dict[key] = value + + def get(self, key): + return self._dict[key] + + def __len__(self): + return self._count \ No newline at end of file diff --git a/src/main.py b/src/main.py index 5d3a0f6b..5ae43179 100644 --- a/src/main.py +++ b/src/main.py @@ -8,6 +8,7 @@ from core.cmp.evaluation import * from core.cmp.cil import get_formatter from pprint import pprint +from core.cmp.cil_to_mips import test def main(args): @@ -100,11 +101,12 @@ def main(args): if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description='CoolCompiler pipeline') - parser.add_argument('-f', '--file', type=str, default='code.cl', help='node address') - - args = parser.parse_args() - main(args) - + # import argparse +# + # parser = argparse.ArgumentParser(description='CoolCompiler pipeline') + # parser.add_argument('-f', '--file', type=str, default='code.cl', help='node address') +# + # args = parser.parse_args() + # main(args) + + test() From 7bc76bafae2e8fd1f4e93ef7a5606c42cb54a345 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 10 Jun 2020 13:09:12 -0400 Subject: [PATCH 166/520] Remove ComputedVisitor --- src/core/cmp/visitors.py | 146 ++++----------------------------------- 1 file changed, 12 insertions(+), 134 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 9b3a944a..28c51a1a 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -390,7 +390,7 @@ def visit(self, node, scope): self.current_type = self.context.get_type(node.id) scope.define_variable('self', self.current_type) - cur_type = self.current_type + cur_type = self.current_type.parent while True: for attr in cur_type.attributes: scope.define_variable(attr.name, attr.type) @@ -398,7 +398,18 @@ def visit(self, node, scope): break cur_type = cur_type.parent + cur_type = self.current_type + pending, count = [], 0 for feature in node.features: + if isinstance(feature, AttrDeclarationNode): + self.visit(feature, scope) + if not scope.is_defined(feature.id): + scope.define_variable(feature.id, cur_type.attributes[count].type) + count += 1 + else: + pending.append(feature) + + for feature in pending: self.visit(feature, scope.create_child()) @visitor.when(AttrDeclarationNode) @@ -1187,136 +1198,3 @@ def visit(self, node, scope): node.computed_type = node_type -class ComputedVisitor(FormatVisitor): - def replace_auto(self, name): - return 'Object' if IsAuto(name) else name - - @visitor.on('node') - def visit(self, node, tabs): - pass - - @visitor.when(ProgramNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' - statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) - return f'{ans}\n{statements}' - - @visitor.when(ClassDeclarationNode) - def visit(self, node, tabs=0): - parent = '' if node.parent is None else f"inherits {node.parent}" - ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' - features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) - return f'{ans}\n{features}' - - @visitor.when(AttrDeclarationNode) - def visit(self, node, tabs=0): - sons = [node.expr] if node.expr else [] - text = '<- ' if node.expr else '' - real_type = self.replace_auto(node.scope.find_variable(node.id).type.name) - ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {real_type} {text}' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' if body else f'{ans}' - - @visitor.when(FuncDeclarationNode) - def visit(self, node, tabs=0): - params = ', '.join(':'.join(param) for param in node.params) - real_type = self.replace_auto(node.method.return_type.name) - ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {real_type} {{}}' - body = '\n'.join(self.visit(child, tabs + 1) for child in node.body) - return f'{ans}\n{body}' - - @visitor.when(IfThenElseNode) - def visit(self, node, tabs=0): - sons = [node.condition, node.if_body] - text = '' - if node.else_body: - sons.append(node.else_body) - text += 'else ' - ans = '\t' * tabs + f'\\__IfThenElseNode: if then {text} fi' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(WhileLoopNode) - def visit(self, node, tabs=0): - sons = [node.condition, node.body] - ans = '\t' * tabs + f'\\__WhileLoopNode: while loop pool' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(BlockNode) - def visit(self, node, tabs=0): - sons = node.exprs - ans = '\t' * tabs + f'\\__BlockNode: {{ ... }}' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(LetInNode) - def visit(self, node, tabs=0): - sons = node.let_body + [node.in_body] - ans = '\t' * tabs + f'\\__LetInNode: let {{ ... }} in ' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(CaseOfNode) - def visit(self, node, tabs=0): - sons = [node.expr] + node.branches - ans = '\t' * tabs + f'\\__CaseOfNode: case of {{ ... }} esac' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(CaseExpressionNode) - def visit(self, node, tabs=0): - sons = [node.expr] - ans = '\t' * tabs + f'\\__CaseExpressionNode: {node.id} : {node.type} => ' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(LetAttributeNode) - def visit(self, node, tabs=0): - sons = [node.expr] if node.expr else [] - text = '<- ' if node.expr else '' - real_type = self.replace_auto(node.scope.find_variable(node.id).type.name) - ans = '\t' * tabs + f'\\__LetAttributeNode: {node.id} : {real_type} {text}' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' if body else f'{ans}' - - @visitor.when(AssignNode) - def visit(self, node, tabs=0): - sons = [node.expr] - ans = '\t' * tabs + f'\\__AssignNode: {node.id} <- ' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(UnaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' - right = self.visit(node.expr, tabs + 1) - return f'{ans}\n{right}' - - @visitor.when(BinaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' - left = self.visit(node.left, tabs + 1) - right = self.visit(node.right, tabs + 1) - return f'{ans}\n{left}\n{right}' - - @visitor.when(AtomicNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' - - @visitor.when(FunctionCallNode) - def visit(self, node, tabs=0): - obj = self.visit(node.obj, tabs + 1) - ans = '\t' * tabs + f'\\__FunctionCallNode: .{node.id}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{obj}\n{args}' - - @visitor.when(MemberCallNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__MemberCallNode: {node.id}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{args}' - - @visitor.when(NewNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__NewNode: new {node.type}()' From 0b7ab102efac3074e715269fa13e74265ceba3e5 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 17:42:18 -0400 Subject: [PATCH 167/520] [semantic] - Add a Type for manage AutoType This type behave like `ErrorType` --- src/core/cmp/semantic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 6d59f32f..2e77c772 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -127,6 +127,10 @@ def bypass(self): def __eq__(self, other): return isinstance(other, Type) +class AutoType(ErrorType): + def __init__(self): + Type.__init__(self, 'AUTO_TYPE') + class VoidType(Type): def __init__(self): Type.__init__(self, 'void') From 86097a4d08f9dbd084a4fe6c16d8c1432a4528de Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 17:47:15 -0400 Subject: [PATCH 168/520] [semantic] - Improve the create type mechanisms --- src/core/cmp/semantic.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 2e77c772..b19761e4 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -179,12 +179,16 @@ class Context: def __init__(self): self.types = {} - def create_type(self, name:str): + def append_type(self, new_type): + name = new_type.name if name in self.types: raise SemanticError(f'Type with the same name ({name}) already in context.') - typex = self.types[name] = Type(name) + typex = self.types[name] = new_type return typex + def create_type(self, name:str): + return self.append_type(Type(name)) + def get_type(self, name:str): try: return self.types[name] From 25e40f7aa4e55be93060c95a3239033c46ca2ef5 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 18:11:02 -0400 Subject: [PATCH 169/520] [visitors][context] - Append the built-in types instead of create them --- src/core/cmp/visitors.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 28c51a1a..0edb785e 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -2,7 +2,7 @@ from core.cmp.CoolUtils import * from core.cmp.semantic import SemanticError from core.cmp.semantic import Attribute, Method, Type -from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType +from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType, AutoType from core.cmp.semantic import Context, Scope WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' @@ -20,16 +20,16 @@ def define_built_in_types(context): obj = context.create_type('Object') - i = context.create_type('Int') + i = context.append_type(IntType()) i.set_parent(obj) - s = context.create_type('String') + s = context.append_type(StringType()) s.set_parent(obj) - b = context.create_type('Bool') + b = context.append_type(BoolType()) b.set_parent(obj) - io = context.create_type('IO') + io = context.append_type(IOType()) io.set_parent(obj) st = context.create_type('SELF_TYPE') - context.create_type('AUTO_TYPE') + context.append_type(AutoType()) obj.define_method('abort', [], [], obj) obj.define_method('type_name', [], [], s) @@ -431,7 +431,6 @@ def visit(self, node, scope): for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): scope.define_variable(pname, ptype) - # for expr in node.body: self.visit(node.body, scope) body_type = fixed_type(node.body.computed_type, self.current_type) From 972cffc26bdced28d80d07bc4dd0148b920035a9 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 19:30:56 -0400 Subject: [PATCH 170/520] [checker] - Use conforms_to int all the Binary and Unnary nodes Change all the equality comparisons between types to conforms_to validations --- src/core/cmp/visitors.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 0edb785e..a98342a4 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -634,7 +634,7 @@ def visit(self, node, scope): self.visit(node.right, scope) right_type = node.right.computed_type - if IntType() != right_type or IntType() != left_type: + if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) node.computed_type = INT @@ -647,7 +647,7 @@ def visit(self, node, scope): self.visit(node.right, scope) right_type = node.right.computed_type - if IntType() != right_type or IntType() != left_type: + if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) node.computed_type = BOOL @@ -695,7 +695,7 @@ def visit(self, node, scope): def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type - if IntType() != expr_type: + if not expr_type.conforms_to(INT): self.errors.append(("Complement works only for Int", node.symbol)) node.computed_type = INT @@ -703,7 +703,7 @@ def visit(self, node, scope): def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type - if BoolType() != expr_type: + if not expr_type.conforms_to(BOOL): self.errors.append(("Not operator works only for Bool", node.symbol)) node.computed_type = BOOL From 3656d8e69fb957ee234352dcc1b7aba213fabeb7 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 19:34:38 -0400 Subject: [PATCH 171/520] [checker] - Use conforms_to in EqualNode --- src/core/cmp/visitors.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index a98342a4..1f274a36 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -717,10 +717,13 @@ def visit(self, node, scope): valid_types = [IntType(), BoolType(), StringType()] try: + cur_types = [right_type, left_type] for op_type in valid_types: - if op_type == right_type or op_type == left_type: - assert op_type == right_type and op_type == left_type + try: + cur_types.remove(op_type) + assert cur_types[0].conforms_to(op_type) break + except ValueError: pass except AssertionError: self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) From 18c80e14224c620e655cfab656bacc291cba9838 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 21:57:28 -0400 Subject: [PATCH 172/520] [checker] - Always increase the atrributes counter --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 1f274a36..ef5540bd 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -405,7 +405,7 @@ def visit(self, node, scope): self.visit(feature, scope) if not scope.is_defined(feature.id): scope.define_variable(feature.id, cur_type.attributes[count].type) - count += 1 + count += 1 else: pending.append(feature) From eb97fc447f7c92ce3a0eabd94e122cd210fbb7cb Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 21:59:39 -0400 Subject: [PATCH 173/520] [checker] - Adding missing fixed_type calls and removing the unnecessary ones --- src/core/cmp/visitors.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index ef5540bd..df6e46de 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -418,7 +418,7 @@ def visit(self, node, scope): return self.visit(node.expr, scope) - expr_type = fixed_type(node.expr.computed_type, self.current_type) + expr_type = node.expr.computed_type real_type = fixed_type(node.attr_type, self.current_type) if not expr_type.conforms_to(real_type): @@ -433,7 +433,7 @@ def visit(self, node, scope): self.visit(node.body, scope) - body_type = fixed_type(node.body.computed_type, self.current_type) + body_type = node.body.computed_type method_rtn_type = fixed_type(self.current_method.return_type, self.current_type) if not body_type.conforms_to(method_rtn_type): @@ -448,11 +448,11 @@ def visit(self, node, scope): if not scope.is_defined(node.id): raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) var = scope.find_variable(node.id) - if var.name == 'self': raise SemanticError(SELF_IS_READONLY) - if not node_type.conforms_to(var.type): - raise SemanticError(INCOMPATIBLE_TYPES % (node_type, var.type)) + var_type = fixed_type(var.type, self.current_type) + if not node_type.conforms_to(var_type): + raise SemanticError(INCOMPATIBLE_TYPES % (node_type.name, var.type.name)) except SemanticError as ex: self.errors.append((ex.text, node.tid)) node_type = ErrorType() @@ -557,8 +557,7 @@ def visit(self, node, scope): arg_types = [] for arg in node.args: self.visit(arg, scope) - arg = arg.computed_type - arg_types.append(arg) + arg_types.append(arg.computed_type) try: if node.type: From 0def1d8727bc1ac6f44a2ba0e6fbc85be92062ed Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 22:00:20 -0400 Subject: [PATCH 174/520] [checker] - Use conforms_to in conditional --- src/core/cmp/visitors.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index df6e46de..ce2a9e60 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -518,7 +518,7 @@ def visit(self, node, scope): self.visit(node.condition, scope) cond_type = node.condition.computed_type - if BoolType() != cond_type: + if not cond_type.conforms_to(BOOL): self.errors.append((CONDITION_NOT_BOOL % ('If', cond_type.name), node.token)) self.visit(node.if_body, scope) @@ -541,10 +541,11 @@ def visit(self, node, scope): self.visit(node.condition, scope) cond_type = node.condition.computed_type - if BoolType() != cond_type: + if not cond_type.conforms_to(BOOL): self.errors.append((CONDITION_NOT_BOOL % ('While', cond_type.name), node.token)) self.visit(node.body, scope) + # //TODO: Return VoidType() node.computed_type = OBJ @visitor.when(FunctionCallNode) From ddf66956cc833d71ebec124b7e226f4077c8e562 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 22:01:19 -0400 Subject: [PATCH 175/520] [checker] - Adding an error when a cast to AUTO_TYPE occur --- src/core/cmp/visitors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index ce2a9e60..2993790d 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -566,8 +566,8 @@ def visit(self, node, scope): cast_type = self.context.get_type(node.type) if cast_type.name == ST: raise SemanticError("Invalid use of SELF_TYPE") - # if IsAuto(node.type): - # raise SemanticError('Is not possible to use AUTO_TYPE in a cast') + if cast_type.name == AT: + raise SemanticError('Is not possible to use AUTO_TYPE in a cast') if not obj_type.conforms_to(cast_type): raise SemanticError(INCOMPATIBLE_TYPES % (obj_type.name, node.type)) obj_type = cast_type From f5de5ce52d8e32ab928f8f684d1a119eab8e08dd Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 31 Jul 2020 22:03:28 -0400 Subject: [PATCH 176/520] [checker] - Fix an error in FunctionCallNode type checking SELF_TYPE parameters of the function call needs to be fixed to the caller object type instead of the class type --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 2993790d..68ab584c 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -576,7 +576,7 @@ def visit(self, node, scope): obj_method = obj_type.get_method(node.id) if len(node.args) == len(obj_method.param_types): for idx, (arg, param_type) in enumerate(zip(arg_types, obj_method.param_types)): - real_type = fixed_type(param_type, self.current_type) + real_type = fixed_type(param_type, obj_type) if not arg.conforms_to(real_type): self.errors.append((INCOMPATIBLE_TYPES % (arg.name, real_type.name + f" in the argument #{idx} of {node.id}"), token)) From 79bf79c14f1f64dfc134cf36e4cb86b876645b91 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 1 Aug 2020 02:35:52 -0400 Subject: [PATCH 177/520] [checker] - Merge ArithmeticNode and ComparisonNode Verify only `BinaryNode` instead of `ArithmeticNode` and `ComparisonNode` individually --- src/core/cmp/visitors.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 68ab584c..49bf78e0 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -637,21 +637,8 @@ def visit(self, node, scope): if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) - node.computed_type = INT + node.computed_type = [BOOL, INT][isinstance(node, ArithmeticNode)] - @visitor.when(ComparisonNode) - def visit(self, node, scope): - self.visit(node.left, scope) - left_type = node.left.computed_type - - self.visit(node.right, scope) - right_type = node.right.computed_type - - if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): - self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) - - node.computed_type = BOOL - @visitor.when(IntegerNode) def visit(self, node, scope): node.computed_type = INT From 67c3664ac152e637a306e73fcca48569979a7d24 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 15:13:22 -0400 Subject: [PATCH 178/520] [semantic] - Create a MutableType Description: `MutableType` describe object objects that are `bypass` and `conforms_to` every type. This types are `ErrorType` and `AutoType`. Other changes: The bool value of `ErrorType` is `False` --- src/core/cmp/semantic.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index b19761e4..55c81627 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -114,23 +114,30 @@ def __str__(self): def __repr__(self): return str(self) -class ErrorType(Type): - def __init__(self): - Type.__init__(self, '') - +class MutableType(Type): def conforms_to(self, other): return True def bypass(self): return True +class ErrorType(MutableType): + def __init__(self): + Type.__init__(self, '') + def __eq__(self, other): return isinstance(other, Type) -class AutoType(ErrorType): + def __bool__(self): + return False + +class AutoType(MutableType): def __init__(self): Type.__init__(self, 'AUTO_TYPE') + def __eq__(self, other): + return isinstance(other, AutoType) + class VoidType(Type): def __init__(self): Type.__init__(self, 'void') From a8cc30781d2143a48d21641fa73853afbf1d222c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 15:19:53 -0400 Subject: [PATCH 179/520] [inferencer] - Add a method for validate the inference condition changes: - Removed the old method `match` - Added `update_condition` --- src/core/cmp/visitors.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 49bf78e0..d983d7a4 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -47,12 +47,14 @@ def define_built_in_types(context): global INT, STRING, BOOL, OBJ INT, STRING, BOOL, OBJ = i, s, b, obj -def match(type1, type2): - return IsAuto(type1.name) or type1.conforms_to(type2) - def fixed_type(type1, type2): return type1 if type1.name != ST else type2 +def update_condition(target, value): + c1 = isinstance(target, AutoType) + c2 = (not isinstance(value, AutoType)) and value + return c1 and c2 + #AST Printer class FormatVisitor: @visitor.on('node') From bb9395b28133a685dc2991c73f729b094552f1c4 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 15:20:55 -0400 Subject: [PATCH 180/520] [formatter] - Update the ast printer --- src/core/cmp/visitors.py | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index d983d7a4..90fef013 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -78,7 +78,7 @@ def visit(self, node, tabs=0): def visit(self, node, tabs=0): sons = [node.expr] if node.expr else [] text = '<- ' if node.expr else '' - ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {node.type} {text}' + ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.id} : {node.type} {text}' body = '\n'.join(self.visit(child, tabs + 1) for child in sons) return f'{ans}\n{body}' if body else f'{ans}' @@ -86,17 +86,13 @@ def visit(self, node, tabs=0): def visit(self, node, tabs=0): params = ', '.join(':'.join(param) for param in node.params) ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {node.type} {{}}' - body = '\n'.join(self.visit(child, tabs + 1) for child in node.body) + body = self.visit(node.body, tabs + 1) return f'{ans}\n{body}' @visitor.when(IfThenElseNode) def visit(self, node, tabs=0): - sons = [node.condition, node.if_body] - text = '' - if node.else_body: - sons.append(node.else_body) - text += 'else ' - ans = '\t' * tabs + f'\\__IfThenElseNode: if then {text} fi' + sons = [node.condition, node.if_body, node.else_body] + ans = '\t' * tabs + f'\\__IfThenElseNode: if then else fi' body = '\n'.join(self.visit(child, tabs + 1) for child in sons) return f'{ans}\n{body}' @@ -134,14 +130,6 @@ def visit(self, node, tabs=0): ans = '\t' * tabs + f'\\__CaseExpressionNode: {node.id} : {node.type} => ' body = '\n'.join(self.visit(child, tabs + 1) for child in sons) return f'{ans}\n{body}' - - @visitor.when(LetAttributeNode) - def visit(self, node, tabs=0): - sons = [node.expr] if node.expr else [] - text = '<- ' if node.expr else '' - ans = '\t' * tabs + f'\\__LetAttributeNode: {node.id} : {node.type} {text}' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' if body else f'{ans}' @visitor.when(AssignNode) def visit(self, node, tabs=0): @@ -152,13 +140,13 @@ def visit(self, node, tabs=0): @visitor.when(UnaryNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' + ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.symbol.lex} ' right = self.visit(node.expr, tabs + 1) return f'{ans}\n{right}' @visitor.when(BinaryNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.symbol.lex} ' left = self.visit(node.left, tabs + 1) right = self.visit(node.right, tabs + 1) return f'{ans}\n{left}\n{right}' @@ -172,13 +160,16 @@ def visit(self, node, tabs=0): obj = self.visit(node.obj, tabs + 1) ans = '\t' * tabs + f'\\__FunctionCallNode: .{node.id}(, ..., )' args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{obj}\n{args}' + ans = f'{ans}\n{obj}' + if args: ans += f'\n{args}' + return ans @visitor.when(MemberCallNode) def visit(self, node, tabs=0): ans = '\t' * tabs + f'\\__MemberCallNode: {node.id}(, ..., )' args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{args}' + if args: ans += f'\n{args}' + return ans @visitor.when(NewNode) def visit(self, node, tabs=0): From 66f41c78b43117ca7ecd1ad2222e350f4b33fee8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 15:22:24 -0400 Subject: [PATCH 181/520] [visitors][inferencer] - Update the LCA method Changes: If any type of the LCA targets is `AutoType` then the returned type is `AutoType` --- src/core/cmp/visitors.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 90fef013..5ea8bea8 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -337,7 +337,8 @@ def LCA(type_list, context): if any([isinstance(t, ErrorType) for t in type_list]): return ErrorType() - type_list = [ context.get_type(tp.name) for tp in type_list ] + if any([isinstance(t, AutoType) for t in type_list]): + return AutoType() for typex in type_list: node = typex while True: @@ -351,8 +352,6 @@ def LCA(type_list, context): break node = node.parent - raise Exception('El LCA se partio') - def IsAuto(name): return name == 'AUTO_TYPE' or IsVoid(name) From 84f7b2650ee252d245e4bc707123559ed48f51b0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 15:34:24 -0400 Subject: [PATCH 182/520] [visitors] - Fixing an LCA error --- src/core/cmp/visitors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 5ea8bea8..92f72d6d 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -344,10 +344,10 @@ def LCA(type_list, context): while True: try: counter[node.name] += 1 - if counter[node.name] == len(type_list): - return node except KeyError: counter[node.name] = 1 + if counter[node.name] == len(type_list): + return node if not node.parent: break node = node.parent From 60b2abbe952ef1f92dd121393c155a8cd9db6593 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 17:37:27 -0400 Subject: [PATCH 183/520] [inferencer] - Remove the old inferencer --- src/core/cmp/visitors.py | 469 --------------------------------------- 1 file changed, 469 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 92f72d6d..e252e6c1 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -708,474 +708,5 @@ def visit(self, node, scope): node.computed_type = BOOL -# Type Inference Visitor -class InferenceVisitor(TypeChecker): - def __init__(self, context, errors=[]): - self.context = context - self.current_type = None - self.current_method = None - self.errors = errors - - @visitor.on('node') - def update(self, node, scope, ntype): - pass - - @visitor.when(FunctionCallNode) - def update(self, node, scope, ntype): - obj_type = node.obj.computed_type - - obj_type.get_method(node.id).return_type = ntype - node.computed_type = ntype - - @visitor.when(MemberCallNode) - def update(self, node, scope, ntype): - obj_type = self.current_type - - obj_type.get_method(node.id).return_type = ntype - node.computed_type = ntype - - @visitor.when(AttrDeclarationNode) - def update(self, node, scope, ntype): - scope.find_variable(node.id).type = ntype - node.computed_type = ntype - - @visitor.when(IdNode) - def update(self, node, scope, ntype): - scope.find_variable(node.lex).type = ntype - node.computed_type = ntype - - @visitor.when(IfThenElseNode) - def update(self, node, scope, ntype): - if IsAuto(node.if_body.computed_type.name): - self.update(node.if_body, scope, ntype) - - node.computed_type = node.if_body.computed_type - - if node.else_body: - if IsAuto(node.else_body.computed_type.name): - self.update(node.else_body, scope, ntype) - - names = [node.if_body.computed_type.name, node.else_body.computed_type.name] - if 'AUTO_TYPE' not in names and '' not in names: - types_list = [node.if_body.computed_type, node.else_body.computed_type] - if all([t.name == ST for t in types_list]): - node.computed_type = types_list[0] - else: - node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) - else: - if '' in names: - node.computed_type = ErrorType() - else: - node.computed_type = self.context.get_type('AUTO_TYPE') - - @visitor.when(CaseOfNode) - def update(self, node, scope, ntype): - types_list = [] - has_auto = has_error = False - - for case in node.branches: - if IsAuto(case.computed_type.name): - self.update(branch, scope, ntype) - has_auto |= IsAuto(case.expr.computed_type.name) - has_error |= case.expr.computed_type.name == '' - types_list.append(case.expr.computed_type) - - if has_error: - node.computed_type = ErrorType() - elif has_auto: - node.computed_type = self.context.get_type('AUTO_TYPE') - else: - if all([t.name == ST for t in types_list]): - node.computed_type = types_list[0] - else: - node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) - - @visitor.when(CaseExpressionNode) - def update(self, node, scope, ntype): - self.update(node.expr, scope, ntype) - node.computed_type = node.expr.computed_type - - @visitor.when(LetInNode) - def update(self, node, scope, ntype): - self.update(node.in_body, node.scope, ntype) - node.computed_type = node.in_body.computed_type - - @visitor.when(BlockNode) - def update(self, node, scope, ntype): - self.update(node.exprs[-1], scope, ntype) - node.computed_type = node.exprs[-1].computed_type - - @visitor.on('node') - def visit(self, node, scope): - pass - - @visitor.when(Node) - def visit(self, node, scope): - super().visit(node, scope) - - @visitor.when(ClassDeclarationNode) - def visit(self, node, scope): - super().visit(node, scope) - - for idx, attr in enumerate(self.current_type.attributes): - actual_type = scope.find_variable(attr.name).type - if IsAuto(attr.type.name): - self.current_type.attributes[idx].type = actual_type - - @visitor.when(AttrDeclarationNode) - def visit(self, node, scope): - node.scope = scope - if node.expr: - self.visit(node.expr, scope) - if IsAuto(node.type): - if not IsAuto(node.expr.computed_type.name): - scope.find_variable(node.id).type = node.expr.computed_type - node.computed_type = node.expr.computed_type - else: - if IsAuto(node.expr.computed_type.name): - self.update(node.expr, scope, node.type) - else: - if not node.expr.computed_type.conforms_to(node.computed_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) - node.computed_type = ErrorType() - @visitor.when(FuncDeclarationNode) - def visit(self, node, scope): - self.current_method = self.current_type.get_method(node.id) - node.method = self.current_method - - for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): - scope.define_variable(pname, ptype) - - # for expr in node.body: - self.visit(node.body, scope) - - last_expr = node.body - last_expr_type = last_expr.computed_type - method_rtn_type = self.current_method.return_type - - if IsAuto(method_rtn_type.name): - if not IsAuto(last_expr_type.name): - self.current_method.return_type = last_expr_type - method_rtn_type = last_expr_type - else: - if IsAuto(last_expr_type.name): - self.update(last_expr, scope, method_rtn_type) - else: - if not last_expr_type.conforms_to(method_rtn_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', last_expr_type.name, 1).replace('%s', method_rtn_type.name, 1)) - - for idx, pname in enumerate(self.current_method.param_names): - actual_type = scope.find_variable(pname).type - if self.current_method.param_types[idx].name != actual_type.name: - self.current_method.param_types[idx] = actual_type - - @visitor.when(IfThenElseNode) - def visit(self, node, scope): - self.visit(node.condition, scope) - expr_type = node.condition.computed_type - - if IsAuto(expr_type.name): - self.update(node.condition, scope, self.context.get_type('Bool')) - expr_type = node.condition.computed_type - if expr_type.name not in ['Bool', 'AUTO_TYPE']: - self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'If', 1).replace('%s', expr_type.name, 1)) - - self.visit(node.if_body, scope) - node.computed_type = node.if_body.computed_type - - if node.else_body: - self.visit(node.else_body, scope) - names = [node.if_body.computed_type.name, node.else_body.computed_type.name] - if 'AUTO_TYPE' not in names and '' not in names: - types_list = [node.if_body.computed_type, node.else_body.computed_type] - if all([t.name == ST for t in types_list]): - node.computed_type = types_list[0] - else: - node.computed_type = LCA([fixed_type(t, self.current_type) for t in types_list], self.context) - else: - if '' in names: - node.computed_type = ErrorType() - else: - node.computed_type = self.context.get_type('AUTO_TYPE') - - @visitor.when(WhileLoopNode) - def visit(self, node, scope): - self.visit(node.condition, scope) - expr_type = node.condition.computed_type - if IsAuto(expr_type.name): - self.update(node.condition, scope, self.context.get_type('Bool')) - expr_type = node.condition.computed_type - - if expr_type.name not in ['Bool', 'AUTO_TYPE']: - self.errors.append(CONDITION_NOT_BOOL.replace('%s', 'If', 1).replace('%s', expr_type.name, 1)) - - self.visit(node.body, scope) - node.computed_type = VoidType() - - @visitor.when(LetInNode) - def visit(self, node, scope): - super().visit(node, scope) - - for attr in node.let_body: - type_name = attr.type - if attr.computed_type.name == '': - continue - actual_type = node.scope.find_variable(attr.id).type - if type_name != actual_type.name: - attr.type = actual_type.name - - @visitor.when(LetAttributeNode) - def visit(self, node, scope): - node.scope = scope - try: - node_type = self.context.get_type(node.type) - except SemanticError as ex: - self.errors.append(ex.text) - node_type = ErrorType() - - if not scope.is_local(node.id): - scope.define_variable(node.id, node_type) - else: - self.errors.append(LOCAL_ALREADY_DEFINED.replace('%s', node.id, 1).replace('%s', self.current_method.name, 1)) - - if not node.expr: - node.computed_type = node_type - return - - self.visit(node.expr, scope) - expr_type = node.expr.computed_type - - if IsAuto(node_type.name): - if not IsAuto(expr_type.name): - node.type = expr_type.name - scope.find_variable(node.id).type = expr_type - node.computed_type = expr_type - else: - if not IsAuto(expr_type.name): - if not expr_type.conforms_to(node_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) - else: - self.update(node.expr, scope, node_type) - node.computed_type = node.expr.computed_type - - @visitor.when(AssignNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - expr_type = node.expr.computed_type - - if scope.is_defined(node.id): - var = scope.find_variable(node.id) - node_type = var.type - - if var.name == 'self': - self.errors.append(SELF_IS_READONLY) - else: - if IsAuto(node_type.name): - if not IsAuto(expr_type.name): - node.type = expr_type.name - scope.find_variable(node.id).type = expr_type - node.computed_type = expr_type - else: - if not IsAuto(expr_type.name): - if not expr_type.conforms_to(node_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).replace('%s', node_type.name, 1)) - else: - self.update(node.expr, scope, node_type) - node.computed_type = node.expr.computed_type - else: - self.errors.append(VARIABLE_NOT_DEFINED.replace('%s', node.id, 1)) - node.computed_type = ErrorType() - - @visitor.when(ComplementNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - if IsAuto(node.expr.computed_type.name): - self.update(node.expr, scope, self.context.get_type('Int')) - node.computed_type = node.expr.computed_type - else: - if node.expr.computed_type.name != 'Int': - self.errors.append("Complement works only for Int") - node.computed_type = ErrorType() - else: - node.computed_type = self.context.get_type('Int') - - @visitor.when(NotNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - if IsAuto(node.expr.computed_type.name): - self.update(node.expr, scope, self.context.get_type('Bool')) - node.computed_type = node.expr.computed_type - else: - if node.expr.computed_type.name != 'Bool': - self.errors.append("Not operator works only for Bool") - node.computed_type = ErrorType() - else: - node.computed_type = self.context.get_type('Bool') - - @visitor.when(BinaryNode) - def visit(self, node, scope): - self.visit(node.left, scope) - left_type = node.left.computed_type - - self.visit(node.right, scope) - right_type = node.right.computed_type - - if IsAuto(left_type.name): - self.update(node.left, scope, self.context.get_type('Int')) - left_type = node.left.computed_type - - if IsAuto(right_type.name): - self.update(node.right, scope, self.context.get_type('Int')) - right_type = node.right.computed_type - - if not (IsAuto(left_type.name) or left_type.conforms_to(IntType())) or not (IsAuto(right_type.name) or right_type.conforms_to(IntType())): - self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) - node_type = ErrorType() - else: - node_type = IntType() - - node.computed_type = node_type - - @visitor.when(FunctionCallNode) - def visit(self, node, scope): - self.visit(node.obj, scope) - obj_type = node.obj.computed_type - - if node.type: - try: - if IsAuto(node.type): - raise SemanticError('Is not possible to use AUTO_TYPE in a cast') - if not obj_type.conforms_to(self.context.get_type(node.type)): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', obj_type.name, 1).replace('%s', node.type, 1)) - except SemanticError as ex: - self.errors.append(ex.text) - - try: - if node.type: - obj_method = self.context.get_type(node.type).get_method(node.id) - else: - obj_method = obj_type.get_method(node.id) - - if len(node.args) == len(obj_method.param_types): - for idx, arg in enumerate(node.args): - self.visit(arg, scope) - arg_type = arg.computed_type - param_type = obj_method.param_types[idx] - - if IsAuto(param_type.name): - if not IsAuto(arg_type.name): - obj_method.param_types[idx] = arg_type - else: - if IsAuto(arg_type.name): - self.update(arg, scope, param_type) - else: - if not arg_type.conforms_to(param_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) - else: - self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - - node_type = obj_method.return_type - except SemanticError as ex: - self.errors.append(ex.text) - node_type = ErrorType() - - node.computed_type = node_type - - @visitor.when(MemberCallNode) - def visit(self, node, scope): - obj_type = self.current_type - - try: - obj_method = obj_type.get_method(node.id) - - if len(node.args) == len(obj_method.param_types): - for idx, arg in enumerate(node.args): - self.visit(arg, scope) - arg_type = arg.computed_type - param_type = obj_method.param_types[idx] - - if IsAuto(param_type.name): - if not IsAuto(arg_type.name): - obj_method.param_types[idx] = arg_type - else: - if IsAuto(arg_type.name): - self.update(arg, scope, param_type) - else: - if not arg_type.conforms_to(param_type): - self.errors.append(INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', param_type.name, 1)) - else: - self.errors.append(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - - node_type = obj_method.return_type - except SemanticError as ex: - self.errors.append(ex.text) - node_type = ErrorType() - - node.computed_type = node_type - - @visitor.when(ArithmeticNode) - def visit(self, node, scope): - self.visit(node.left, scope) - left_type = node.left.computed_type - - self.visit(node.right, scope) - right_type = node.right.computed_type - - if IsAuto(left_type.name): - self.update(node.left, scope, self.context.get_type('Int')) - left_type = node.left.computed_type - - if IsAuto(right_type.name): - self.update(node.right, scope, self.context.get_type('Int')) - right_type = node.right.computed_type - - if not (IsAuto(left_type.name) or left_type.conforms_to(IntType())) or not (IsAuto(right_type.name) or right_type.conforms_to(IntType())): - self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) - node_type = ErrorType() - else: - node_type = IntType() - - node.computed_type = node_type - - @visitor.when(ComparisonNode) - def visit(self, node, scope): - self.visit(node.left, scope) - left_type = node.left.computed_type - - self.visit(node.right, scope) - right_type = node.right.computed_type - - if IsAuto(left_type.name): - self.update(node.left, scope, self.context.get_type('Bool')) - left_type = node.left.computed_type - - if IsAuto(right_type.name): - self.update(node.right, scope, self.context.get_type('Bool')) - right_type = node.right.computed_type - - if not (match(left_type, BoolType()) and match(right_type, BoolType())): - self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) - node_type = ErrorType() - else: - node_type = BoolType() - - node.computed_type = node_type - - @visitor.when(EqualNode) - def visit(self, node, scope): - # //TODO: What to do when left and rigth are AUTO - self.visit(node.left, scope) - left_type = node.left.computed_type - - self.visit(node.right, scope) - right_type = node.right.computed_type - - if not (left_type.name == right_type.name and left_type.name in built_in_types[:3]): - self.errors.append(INVALID_OPERATION.replace('%s', left_type.name, 1).replace('%s', right_type.name, 1)) - node_type = ErrorType() - else: - node_type = right_type - - node.computed_type = node_type From 5e9577c055a23666fb8abc792062e09be002d8d1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 17:42:39 -0400 Subject: [PATCH 184/520] [inferencer] - Initial Inferencer Just do the same as `TypeChecker` --- src/core/cmp/visitors.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index e252e6c1..bdf88306 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -709,4 +709,12 @@ def visit(self, node, scope): node.computed_type = BOOL +# Type Inference Visitor +class InferenceVisitor(TypeChecker): + @visitor.on('node') + def visit(self, node, scope): + pass + @visitor.when(Node) + def visit(self, node, scope): + super().visit(node, scope) From 9d52c10b9be822c7314fecc7811227ea588c2b05 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 17:48:51 -0400 Subject: [PATCH 185/520] [inferencer] - Add ProgramNode and ClassDecalration node After the checking process `ProgramNode` return the scope returned by the checker and `ClassDeclarationNode` updates the attributes which their type was changed in the inference process --- src/core/cmp/visitors.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index bdf88306..af6fb10a 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -390,13 +390,15 @@ def visit(self, node, scope): break cur_type = cur_type.parent + node.attr_idx = [] cur_type = self.current_type pending, count = [], 0 - for feature in node.features: + for idx, feature in enumerate(node.features): if isinstance(feature, AttrDeclarationNode): - self.visit(feature, scope) + node.attr_idx.append(idx) if not scope.is_defined(feature.id): scope.define_variable(feature.id, cur_type.attributes[count].type) + self.visit(feature, scope) count += 1 else: pending.append(feature) @@ -718,3 +720,17 @@ def visit(self, node, scope): @visitor.when(Node) def visit(self, node, scope): super().visit(node, scope) + + @visitor.when(ProgramNode) + def visit(self, node, scope=None): + return super().visit(node, scope) + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + super().visit(node, scope) + + for idx, attr in enumerate(self.current_type.attributes): + actual_type = scope.find_variable(attr.name).type + if update_condition(attr.type, actual_type): + self.current_type.attributes[idx].type = actual_type + node.features[node.attr_idx[idx]].type = actual_type.name \ No newline at end of file From 3027ae26e993d3c2e50803b4db10c4b32f7edbfc Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 17:58:56 -0400 Subject: [PATCH 186/520] [inferencer] - Add AttrDeclarationNode nodes Added all the nodes of type `AttrDeclarationNode` which are `LetAttributeNode`, `CaseExpressionNode` and `AttrDeclarationNode` itself. If the attribute is `AutoType` and has an initialization which has a `non-AutoType` computed value then the attribute obtain the type of initialization expression --- src/core/cmp/visitors.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index af6fb10a..63213afd 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -414,6 +414,7 @@ def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type real_type = fixed_type(node.attr_type, self.current_type) + node.info = [expr_type, real_type] if not expr_type.conforms_to(real_type): self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, real_type.name), node.arrow)) @@ -466,11 +467,14 @@ def visit(self, node, scope): @visitor.when(CaseExpressionNode) def visit(self, node, scope): + node.scope = scope try: branch_type = self.context.get_type(node.type) except SemanticError as ex: self.errors.append((ex.text, node.ttype)) branch_type = ErrorType() + node.branch_type = branch_type + scope.define_variable(node.id, branch_type) self.visit(node.expr, scope) node.computed_type = node.expr.computed_type @@ -493,7 +497,8 @@ def visit(self, node, scope): except SemanticError as ex: self.errors.append((ex.text, node.ttype)) node_type = ErrorType() - + node.attr_type = node_type + if not scope.is_local(node.id): scope.define_variable(node.id, node_type) else: @@ -503,7 +508,8 @@ def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type real_type = fixed_type(node_type, self.current_type) - + node.info = [expr_type, real_type] + if not expr_type.conforms_to(real_type): self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, real_type.name), node.arrow)) @@ -733,4 +739,20 @@ def visit(self, node, scope): actual_type = scope.find_variable(attr.name).type if update_condition(attr.type, actual_type): self.current_type.attributes[idx].type = actual_type - node.features[node.attr_idx[idx]].type = actual_type.name \ No newline at end of file + node.features[node.attr_idx[idx]].type = actual_type.name + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + super().visit(node, scope) + + if not node.expr: + return + + expr, rtype = node.info + if update_condition(rtype, expr): + scope.find_variable(node.id).type = expr + node.type = expr.name + + @visitor.when(CaseExpressionNode) + def visit(self, node, scope): + super().visit(node, scope) \ No newline at end of file From 06919f50783c405005bf442b2ac6a5b791b7a27c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 18:08:34 -0400 Subject: [PATCH 187/520] [inferencer] - Add FuncDeclarationNode if the return type is `AutoType` and the body return a `non-AutoType` value `t` then the return type of the function change to `t`. Also update the parameters wich their type was changed --- src/core/cmp/visitors.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 63213afd..3fc85bb0 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -430,6 +430,7 @@ def visit(self, node, scope): body_type = node.body.computed_type method_rtn_type = fixed_type(self.current_method.return_type, self.current_type) + node.info = [body_type, method_rtn_type] if not body_type.conforms_to(method_rtn_type): self.errors.append((INCOMPATIBLE_TYPES % (body_type.name, method_rtn_type.name), node.ttype)) @@ -753,6 +754,21 @@ def visit(self, node, scope): scope.find_variable(node.id).type = expr node.type = expr.name + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + super().visit(node, scope) + + body, rtn = node.info + if update_condition(rtn, body): + self.current_method.return_type = body + node.type = body.name + for idx, pname in enumerate(self.current_method.param_names): + actual_type = scope.find_variable(pname).type + if update_condition(self.current_method.param_types[idx], actual_type): + self.current_method.param_types[idx] = actual_type + node.params[idx].ttype = actual_type.name + + @visitor.when(CaseExpressionNode) def visit(self, node, scope): super().visit(node, scope) \ No newline at end of file From a27c420686a6c2372f0568e5e68795433262f647 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 18:12:35 -0400 Subject: [PATCH 188/520] [inferencer] - Add AssignNode If the identifier of the assignment is an `AutoType` variable and the assignment expression not, then the variable type change to the type of the expression --- src/core/cmp/visitors.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 3fc85bb0..8d795bc7 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -439,7 +439,8 @@ def visit(self, node, scope): def visit(self, node, scope): self.visit(node.expr, scope) node_type = node.expr.computed_type - + var_type = None + try: if not scope.is_defined(node.id): raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) @@ -453,6 +454,7 @@ def visit(self, node, scope): self.errors.append((ex.text, node.tid)) node_type = ErrorType() + node.info = [node_type, var_type] node.computed_type = node_type @visitor.when(CaseOfNode) @@ -767,8 +769,14 @@ def visit(self, node, scope): if update_condition(self.current_method.param_types[idx], actual_type): self.current_method.param_types[idx] = actual_type node.params[idx].ttype = actual_type.name - - + + @visitor.when(AssignNode) + def visit(self, node, scope): + super().visit(node, scope) + + node_type, var = node.info + if update_condition(var, node_type): + scope.find_variable(node.id).type = node_type @visitor.when(CaseExpressionNode) def visit(self, node, scope): super().visit(node, scope) \ No newline at end of file From 187d02f7913deae0116680647f8fa3d0f3fe4959 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 18:15:55 -0400 Subject: [PATCH 189/520] [inferrencer] - Add CaseOfNode Update the type of the variables declared on each branch if it was change during the inference process --- src/core/cmp/visitors.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 8d795bc7..80a921cc 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -777,6 +777,16 @@ def visit(self, node, scope): node_type, var = node.info if update_condition(var, node_type): scope.find_variable(node.id).type = node_type + + @visitor.when(CaseOfNode) + def visit(self, node, scope): + super().visit(node, scope) + + for idx, branch in enumerate(node.branches): + cur_type = branch.scope.find_variable(branch.id).type + if update_condition(branch.branch_type, cur_type): + branch.type = cur_type.name + @visitor.when(CaseExpressionNode) def visit(self, node, scope): super().visit(node, scope) \ No newline at end of file From c88f54be2003db8466f2c30b0aadbe2cc045d4bd Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 18:19:15 -0400 Subject: [PATCH 190/520] [inferencer] - Add LetInNode Update the attributes declared in the let clause which their type change in the inference process --- src/core/cmp/visitors.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 80a921cc..2658d5cd 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -789,4 +789,13 @@ def visit(self, node, scope): @visitor.when(CaseExpressionNode) def visit(self, node, scope): - super().visit(node, scope) \ No newline at end of file + super().visit(node, scope) + + @visitor.when(LetInNode) + def visit(self, node, scope): + super().visit(node, scope) + + for attr in node.let_body: + cur_type = node.scope.find_variable(attr.id).type + if update_condition(attr.attr_type, cur_type): + attr.type = cur_type.name \ No newline at end of file From 65fa500026863797ce201170a94d20af3eef00de Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 18:24:05 -0400 Subject: [PATCH 191/520] [inferencer] - Adding missing LetAttributeNode --- src/core/cmp/visitors.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 2658d5cd..18442d7f 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -798,4 +798,16 @@ def visit(self, node, scope): for attr in node.let_body: cur_type = node.scope.find_variable(attr.id).type if update_condition(attr.attr_type, cur_type): - attr.type = cur_type.name \ No newline at end of file + attr.type = cur_type.name + + @visitor.when(LetAttributeNode) + def visit(self, node, scope): + super().visit(node, scope) + + if not node.expr: + return + + expr, rtype = node.info + if update_condition(rtype, expr): + scope.find_variable(node.id).type = expr + node.type = expr.name \ No newline at end of file From f3b2e6561abdc398a1254a3256249ad38e6291d4 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 18:33:38 -0400 Subject: [PATCH 192/520] [inferencer] - Add FunctionCallNode and MemberCallNode Update the type of the `AutoType` parameters which are receiving a `non-AutoType` expression in the call --- src/core/cmp/visitors.py | 44 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 18442d7f..9509fa27 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -460,7 +460,7 @@ def visit(self, node, scope): @visitor.when(CaseOfNode) def visit(self, node, scope): self.visit(node.expr, scope) - + types_list = [] for case in node.branches: self.visit(case, scope.create_child()) @@ -558,7 +558,7 @@ def visit(self, node, scope): error = False - arg_types = [] + arg_types, real_types = [], [] for arg in node.args: self.visit(arg, scope) arg_types.append(arg.computed_type) @@ -577,10 +577,12 @@ def visit(self, node, scope): token = node.tid obj_method = obj_type.get_method(node.id) + node.obj_method = obj_method if len(node.args) == len(obj_method.param_types): for idx, (arg, param_type) in enumerate(zip(arg_types, obj_method.param_types)): real_type = fixed_type(param_type, obj_type) - + real_types.append(real_type) + if not arg.conforms_to(real_type): self.errors.append((INCOMPATIBLE_TYPES % (arg.name, real_type.name + f" in the argument #{idx} of {node.id}"), token)) error = True @@ -593,7 +595,8 @@ def visit(self, node, scope): node_type = ErrorType() except AssertionError: node_type = ErrorType() - + + node.info = [arg_types, real_types] node.computed_type = node_type @visitor.when(MemberCallNode) @@ -602,7 +605,7 @@ def visit(self, node, scope): error = False - arg_types = [] + arg_types, real_types = [], [] for arg in node.args: self.visit(arg, scope) arg_types.append(arg.computed_type) @@ -610,9 +613,11 @@ def visit(self, node, scope): try: token = node.tid obj_method = obj_type.get_method(node.id) + node.obj_method = obj_method if len(node.args) == len(obj_method.param_types): for arg, param_type in zip(arg_types, obj_method.param_types): real_type = fixed_type(param_type, self.current_type) + real_types.append(real_type) if not arg.conforms_to(real_type): self.errors.append((INCOMPATIBLE_TYPES % (arg.name, real_type.name + f" in the argument #{idx} of {node.id}"), token)) @@ -626,7 +631,8 @@ def visit(self, node, scope): node_type = ErrorType() except AssertionError: node_type = ErrorType() - + + node.info = [arg_types, real_types] node.computed_type = node_type @visitor.when(ArithmeticNode) @@ -810,4 +816,28 @@ def visit(self, node, scope): expr, rtype = node.info if update_condition(rtype, expr): scope.find_variable(node.id).type = expr - node.type = expr.name \ No newline at end of file + node.type = expr.name + + @visitor.when(FunctionCallNode) + def visit(self, node, scope): + super().visit(node, scope) + + args, real = node.info + if not real: + return + + for idx, (atype, rtype) in enumerate(zip(args, real)): + if update_condition(rtype, atype): + node.obj_method.param_types[idx] = atype + + @visitor.when(MemberCallNode) + def visit(self, node, scope): + super().visit(node, scope) + + args, real = node.info + if not real: + return + + for idx, (atype, rtype) in enumerate(zip(args, real)): + if update_condition(rtype, atype): + node.obj_method.param_types[idx] = atype \ No newline at end of file From 4a1362b34b9197e698114e2712da476b2b0d1b8d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 18:37:15 -0400 Subject: [PATCH 193/520] [inferencer] - Add the `update` visitor method for implement a more complex inference This method is for deal with expression that has `AutoType` as computed type. --- src/core/cmp/visitors.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 9509fa27..bbb9a1cc 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -728,6 +728,10 @@ def visit(self, node, scope): # Type Inference Visitor class InferenceVisitor(TypeChecker): + @visitor.on('node') + def update(self, node, scope, ntype): + pass + @visitor.on('node') def visit(self, node, scope): pass From a88ec849efdf9ddb10dedb832a611c32305ebec6 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 19:12:46 -0400 Subject: [PATCH 194/520] [inferencer] - Add the Update call for AssignNode, CaseExpressionNode and BlockNode If one of this nodes has computed type `AutoType` if because other expression passed to him Cases: - `AssigNode`: The assignment expression - `CaseExpressionNode`: The branch expression - `BlockNode`: The last expression --- src/core/cmp/visitors.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index bbb9a1cc..58ad5066 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -732,6 +732,18 @@ class InferenceVisitor(TypeChecker): def update(self, node, scope, ntype): pass + @visitor.when(AssignNode) + def update(self, node, scope, ntype): + self.update(node.expr, scope, ntype) + + @visitor.when(CaseExpressionNode) + def update(self, node, scope, ntype): + self.update(node.expr, node.scope, ntype) + + @visitor.when(BlockNode) + def update(self, node, scope, ntype): + self.update(node.exprs[-1], scope, ntype) + @visitor.on('node') def visit(self, node, scope): pass From a1908b9033dd10e264f4083ff027993a40d6562d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 19:20:29 -0400 Subject: [PATCH 195/520] [inferencer] - Add Update Call for CaseOfNode If this node has `AutoType` as computed type is because at least one branch is `AutoType` then is necessary to do a update call for each one of the `AutoType` branches and finally check the branches attributes just like in the visit call --- src/core/cmp/visitors.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 58ad5066..5d2ae0a3 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -736,6 +736,17 @@ def update(self, node, scope, ntype): def update(self, node, scope, ntype): self.update(node.expr, scope, ntype) + @visitor.when(CaseOfNode) + def update(self, node, scope, ntype): + for branch in node.branches: + if isinstance(branch.computed_type, AutoType): + self.update(branch, scope, ntype) + + for idx, branch in enumerate(node.branches): + cur_type = branch.scope.find_variable(branch.id).type + if update_condition(branch.branch_type, cur_type): + branch.type = cur_type.name + @visitor.when(CaseExpressionNode) def update(self, node, scope, ntype): self.update(node.expr, node.scope, ntype) From d721adf0476b4ed751ea419132e6912cc4772d64 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:03:39 -0400 Subject: [PATCH 196/520] [inferencer] - Add Upade call for IfThenElseNode As in CaseOfNode if this node is `AutoType` the at least the `then` clause or the `else` are `AutoType`, then is necessary to do the appropriate update calls --- src/core/cmp/visitors.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 5d2ae0a3..f5e77321 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -751,6 +751,13 @@ def update(self, node, scope, ntype): def update(self, node, scope, ntype): self.update(node.expr, node.scope, ntype) + @visitor.when(IfThenElseNode) + def update(self, node, scope, ntype): + if isinstance(node.if_body.computed_type, AutoType): + self.update(node.if_body, scope, ntype) + if isinstance(node.else_body.computed_type, AutoType): + self.update(node.else_body, scope, ntype) + @visitor.when(BlockNode) def update(self, node, scope, ntype): self.update(node.exprs[-1], scope, ntype) From 4be7beaff10d9cfc2616a507091318a8c37d2614 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:06:58 -0400 Subject: [PATCH 197/520] [inferencer] - Add Update call for LetInNode if this node is `AutoType` the his body is `AutoType`, then simply make an update call to the body and later check if there are some let attributes that changed their type an update them --- src/core/cmp/visitors.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index f5e77321..ad18384e 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -751,6 +751,15 @@ def update(self, node, scope, ntype): def update(self, node, scope, ntype): self.update(node.expr, node.scope, ntype) + @visitor.when(LetInNode) + def update(self, node, scope, ntype): + self.update(node.in_body, node.scope, ntype) + + for attr in node.let_body: + cur_type = node.scope.find_variable(attr.id).type + if update_condition(attr.attr_type, cur_type): + attr.type = cur_type.name + @visitor.when(IfThenElseNode) def update(self, node, scope, ntype): if isinstance(node.if_body.computed_type, AutoType): From c3b2dda3190ba068d84bd9023e07fedeb4311ed2 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:09:09 -0400 Subject: [PATCH 198/520] [inferencer] - Add Update call for FuncCallNode and MemberCallNode if this nodes are `AutoType` their return type are `AutoType` then simply change their return type to the given type --- src/core/cmp/visitors.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index ad18384e..55caa641 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -771,6 +771,14 @@ def update(self, node, scope, ntype): def update(self, node, scope, ntype): self.update(node.exprs[-1], scope, ntype) + @visitor.when(FunctionCallNode) + def update(self, node, scope, ntype): + node.obj_method.return_type = ntype + + @visitor.when(MemberCallNode) + def update(self, node, scope, ntype): + node.obj_method.return_type = ntype + @visitor.on('node') def visit(self, node, scope): pass From 54443e4ef9fb2524080c93f6ec840e2946d6f673 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:11:12 -0400 Subject: [PATCH 199/520] [inferencer] - Add Update Call for IdNode if this node is `AutoType` then simply change the type of the associated variable to the given type --- src/core/cmp/visitors.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 55caa641..86162cec 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -779,6 +779,11 @@ def update(self, node, scope, ntype): def update(self, node, scope, ntype): node.obj_method.return_type = ntype + @visitor.when(IdNode) + def update(self, node, scope, ntype): + print("id" , ntype, scope.find_variable(node.lex).type) + scope.find_variable(node.lex).type = ntype + @visitor.on('node') def visit(self, node, scope): pass From 38b44498e074b68e08ce2101717e3211bdf083a1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:16:17 -0400 Subject: [PATCH 200/520] [inferencer] - Add some update calls in the previously created methods New update calls in: - `AttrDeclarationNode` - `FunctionDeclarationNode` - `AssignNode` - `LetAttributeNode` - `FunctionCallNode` - `MemberCallNode` --- src/core/cmp/visitors.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 86162cec..3f65c481 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -817,6 +817,8 @@ def visit(self, node, scope): if update_condition(rtype, expr): scope.find_variable(node.id).type = expr node.type = expr.name + if update_condition(expr, rtype): + self.update(node.expr, scope, rtype) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -826,6 +828,9 @@ def visit(self, node, scope): if update_condition(rtn, body): self.current_method.return_type = body node.type = body.name + if update_condition(body, rtn): + self.update(node.body, scope, rtn) + for idx, pname in enumerate(self.current_method.param_names): actual_type = scope.find_variable(pname).type if update_condition(self.current_method.param_types[idx], actual_type): @@ -839,6 +844,9 @@ def visit(self, node, scope): node_type, var = node.info if update_condition(var, node_type): scope.find_variable(node.id).type = node_type + if update_condition(node_type, var): + self.update(node.expr, scope, var) + node.computed_type = var @visitor.when(CaseOfNode) def visit(self, node, scope): @@ -873,6 +881,8 @@ def visit(self, node, scope): if update_condition(rtype, expr): scope.find_variable(node.id).type = expr node.type = expr.name + if update_condition(expr, rtype): + self.update(node.expr, scope, rtype) @visitor.when(FunctionCallNode) def visit(self, node, scope): @@ -885,6 +895,8 @@ def visit(self, node, scope): for idx, (atype, rtype) in enumerate(zip(args, real)): if update_condition(rtype, atype): node.obj_method.param_types[idx] = atype + if update_condition(atype, rtype): + self.update(node.args[idx], scope, rtype) @visitor.when(MemberCallNode) def visit(self, node, scope): @@ -896,4 +908,6 @@ def visit(self, node, scope): for idx, (atype, rtype) in enumerate(zip(args, real)): if update_condition(rtype, atype): - node.obj_method.param_types[idx] = atype \ No newline at end of file + node.obj_method.param_types[idx] = atype + if update_condition(atype, rtype): + self.update(node.args[idx], scope, rtype) \ No newline at end of file From f1f4ee0d2ad53d2e77968ac513c0ff8c5bc8aeb8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:18:47 -0400 Subject: [PATCH 201/520] [inferencer] - Add IfThenElseNode and WhileLoopNode If the conditions of this nodes are `AutoType` is possible to make and update call to their conditional expression passing as suggested type `Bool` --- src/core/cmp/visitors.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 3f65c481..69b8d651 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -519,9 +519,9 @@ def visit(self, node, scope): @visitor.when(IfThenElseNode) def visit(self, node, scope): self.visit(node.condition, scope) - cond_type = node.condition.computed_type + node.cond_type = node.condition.computed_type - if not cond_type.conforms_to(BOOL): + if not node.cond_type.conforms_to(BOOL): self.errors.append((CONDITION_NOT_BOOL % ('If', cond_type.name), node.token)) self.visit(node.if_body, scope) @@ -542,13 +542,12 @@ def visit(self, node, scope): @visitor.when(WhileLoopNode) def visit(self, node, scope): self.visit(node.condition, scope) - cond_type = node.condition.computed_type + node.cond_type = node.condition.computed_type - if not cond_type.conforms_to(BOOL): + if not node.cond_type.conforms_to(BOOL): self.errors.append((CONDITION_NOT_BOOL % ('While', cond_type.name), node.token)) self.visit(node.body, scope) - # //TODO: Return VoidType() node.computed_type = OBJ @visitor.when(FunctionCallNode) @@ -884,6 +883,20 @@ def visit(self, node, scope): if update_condition(expr, rtype): self.update(node.expr, scope, rtype) + @visitor.when(IfThenElseNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.cond_type, AutoType): + self.update(node.condition, scope, BOOL) + + @visitor.when(WhileLoopNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.cond_type, AutoType): + self.update(node.condition, scope, BOOL) + @visitor.when(FunctionCallNode) def visit(self, node, scope): super().visit(node, scope) From b88e82230060f249e2a1549d262b11e0a98abef5 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:20:59 -0400 Subject: [PATCH 202/520] [inferencer] - Add BinaryNode Both member of the binary operation must be `Int` then in case of `AutoType` simply make an update call passing `Int` as suggested type --- src/core/cmp/visitors.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 69b8d651..53c0270c 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -634,13 +634,14 @@ def visit(self, node, scope): node.info = [arg_types, real_types] node.computed_type = node_type - @visitor.when(ArithmeticNode) + @visitor.when(BinaryNode) def visit(self, node, scope): self.visit(node.left, scope) left_type = node.left.computed_type self.visit(node.right, scope) right_type = node.right.computed_type + node.info = [left_type, right_type] if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) @@ -923,4 +924,13 @@ def visit(self, node, scope): if update_condition(rtype, atype): node.obj_method.param_types[idx] = atype if update_condition(atype, rtype): - self.update(node.args[idx], scope, rtype) \ No newline at end of file + self.update(node.args[idx], scope, rtype) + @visitor.when(BinaryNode) + def visit(self, node, scope): + super().visit(node, scope) + + left, right = node.info + if isinstance(left, AutoType): + self.update(node.left, scope, INT) + if isinstance(right, AutoType): + self.update(node.right, scope, INT) \ No newline at end of file From 7dd009ef41d02acc47172bd9e9ea2b3e1f9b647c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:26:10 -0400 Subject: [PATCH 203/520] [inferencer] - Add UnaryNode In case of `ComplementNode` the expression must be `Int`, and for `NotNode` the expression must be `Bool` then make an update call passing the appropriate suggested type in case of `AutoType` --- src/core/cmp/visitors.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 53c0270c..91cb34b9 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -691,6 +691,8 @@ def visit(self, node, scope): def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type + node.expr_type = expr_type + if not expr_type.conforms_to(INT): self.errors.append(("Complement works only for Int", node.symbol)) node.computed_type = INT @@ -699,6 +701,8 @@ def visit(self, node, scope): def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type + node.expr_type = expr_type + if not expr_type.conforms_to(BOOL): self.errors.append(("Not operator works only for Bool", node.symbol)) node.computed_type = BOOL @@ -933,4 +937,18 @@ def visit(self, node, scope): if isinstance(left, AutoType): self.update(node.left, scope, INT) if isinstance(right, AutoType): - self.update(node.right, scope, INT) \ No newline at end of file + self.update(node.right, scope, INT) + + @visitor.when(ComplementNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.expr_type, AutoType): + self.update(node.expr, scope, INT) + + @visitor.when(NotNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.expr_type, AutoType): + self.update(node.expr, scope, BOOL) \ No newline at end of file From f46d60de17d64c8e0abdf84781128d9cc5eb32a1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:28:05 -0400 Subject: [PATCH 204/520] [inferencer] - Add EqualNode In case of `AutoType` in only one of the members suggest the other member type to the `AutoType` one doing an update call --- src/core/cmp/visitors.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 91cb34b9..8ccfbd42 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -714,6 +714,7 @@ def visit(self, node, scope): self.visit(node.right, scope) right_type = node.right.computed_type + node.info = [left_type, right_type] valid_types = [IntType(), BoolType(), StringType()] try: @@ -730,6 +731,7 @@ def visit(self, node, scope): node.computed_type = BOOL +# //TODO: Try to infer SELF_TYPE # Type Inference Visitor class InferenceVisitor(TypeChecker): @visitor.on('node') @@ -788,6 +790,7 @@ def update(self, node, scope, ntype): print("id" , ntype, scope.find_variable(node.lex).type) scope.find_variable(node.lex).type = ntype + # Visit @visitor.on('node') def visit(self, node, scope): pass @@ -929,6 +932,7 @@ def visit(self, node, scope): node.obj_method.param_types[idx] = atype if update_condition(atype, rtype): self.update(node.args[idx], scope, rtype) + @visitor.when(BinaryNode) def visit(self, node, scope): super().visit(node, scope) @@ -951,4 +955,15 @@ def visit(self, node, scope): super().visit(node, scope) if isinstance(node.expr_type, AutoType): - self.update(node.expr, scope, BOOL) \ No newline at end of file + self.update(node.expr, scope, BOOL) + + @visitor.when(EqualNode) + def visit(self, node, scope): + super().visit(node, scope) + + left, right = node.info + if update_condition(left, right): + self.update(node.left, scope, right) + if update_condition(right, left): + self.update(node.right, scope, left) + From ff15ebcd92079df862956eb807ba0bf72fe457dd Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 3 Aug 2020 21:37:07 -0400 Subject: [PATCH 205/520] [main] - Add the inference process to the pipeline Replace the TypeChecker review by the TypeInferencer review. Its necessary to repeat the process until the amount of `AutoType` in the `scope` remains the same in two consecutive repetition of the inference process. Note: To see the resulting AST use the `else` clause commented in line 76 --- src/main.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index cae92a27..0aa86c35 100644 --- a/src/main.py +++ b/src/main.py @@ -58,14 +58,23 @@ def main(args): errors.extend(builder.errors) # Checking types - checker = TypeChecker(context) - checker.visit(ast) + checker = InferenceVisitor(context) + auto = -1 + while True: + checker.errors.clear() + scope = checker.visit(ast) + cant = scope.count_auto() + if auto == cant: + break + auto = cant errors.extend(checker.errors) if errors: for (msg, token) in errors: print(f"({token.row},{token.column}) - SemanticError: {msg}") exit(1) + # else: + # print(FormatVisitor().visit(ast)) exit(0) From 9c8f4eda37c18709f45396f89be1470b9efbe341 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 19:55:10 -0400 Subject: [PATCH 206/520] [CoolUtils] - Improve the Param class --- src/core/cmp/CoolUtils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 75152fd7..374c13bf 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -185,10 +185,12 @@ class Param: def __init__(self, tid, ttype): self.tid = tid self.ttype = ttype + self.type = ttype.lex def __iter__(self): yield self.tid.lex - yield self.ttype.lex + yield self.type + # Grammar From 22f9764414bfad54a926318382b47a80428f4e26 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 19:57:31 -0400 Subject: [PATCH 207/520] [utils][inferencer] - Add Class InferenceSets for manage the new Inference approach Given an `AutoType` variable there two sets of type, the types that the variable conforms to and the types that conforms to the variable --- src/core/cmp/utils.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/utils.py b/src/core/cmp/utils.py index 4e93321c..c7f39d18 100644 --- a/src/core/cmp/utils.py +++ b/src/core/cmp/utils.py @@ -267,4 +267,12 @@ def __call__(self, w, get_shift_reduce=False): return (output if not get_shift_reduce else(output,operations)), (False, None) # Your code here!!! (Invalid case) else: - raise ValueError \ No newline at end of file + raise ValueError + +class InferenceSets: + D, S = [], [] + + def add(self, new_type, conforms=True): + cur = [self.S, self.D][conforms] + cur.append(new_type) + return self \ No newline at end of file From ffb18165bb04184dac61f4897a32d10194f8b5aa Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 19:58:44 -0400 Subject: [PATCH 208/520] [semantic] - Refract some code and add a TODO --- src/core/cmp/semantic.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 55c81627..48334b7b 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -77,6 +77,7 @@ def get_method(self, name:str): raise SemanticError(f'Method "{name}" is not defined in {self.name}.') def define_method(self, name:str, param_names:list, param_types:list, return_type): + # //TODO: Remove the below if clause if name in self.methods.keys(): raise SemanticError(f'Method "{name}" already defined in {self.name}') try: @@ -247,8 +248,5 @@ def is_local(self, vname): return any(True for x in self.locals if x.name == vname) def count_auto(self): - num = 0 - for var in self.locals: - if var.type.name == 'AUTO_TYPE': - num += 1 + num = sum([x.type.name == 'AUTO_TYPE' for x in self.locals]) return num + sum([scp.count_auto() for scp in self.children]) \ No newline at end of file From 68a209ce1a86610b8888f4a19ce3ab8eedd29fe0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 20:04:06 -0400 Subject: [PATCH 209/520] [builder][checker] - Save the node associated to each variable, attribute or method Extra: - Import the `InferenceSets` class - Change the `Formatter` diplay a`Param` in `FunctionDeclarationNode` --- src/core/cmp/visitors.py | 44 ++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 8ccfbd42..a64c5d1c 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1,5 +1,6 @@ import core.cmp.visitor as visitor from core.cmp.CoolUtils import * +from core.cmp.utils import InferenceSets from core.cmp.semantic import SemanticError from core.cmp.semantic import Attribute, Method, Type from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType, AutoType @@ -84,7 +85,7 @@ def visit(self, node, tabs=0): @visitor.when(FuncDeclarationNode) def visit(self, node, tabs=0): - params = ', '.join(':'.join(param) for param in node.params) + params = ', '.join(' : '.join(param) for param in node.params) ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {node.type} {{}}' body = self.visit(node.body, tabs + 1) return f'{ans}\n{body}' @@ -296,14 +297,16 @@ def visit(self, node): node.attr_type = attr_type try: - self.current_type.define_attribute(node.id, attr_type) + attr = self.current_type.define_attribute(node.id, attr_type) + attr.node = node except SemanticError as ex: self.errors.append((ex.text, node.tid)) @visitor.when(FuncDeclarationNode) def visit(self, node): - arg_names, arg_types = [], [] - for i, (idx, typex) in enumerate(node.params): + arg_names, arg_types, arg_nodes = [], [], [] + for i, arg in enumerate(node.params): + idx, typex = arg try: arg_type = self.context.get_type(typex) except SemanticError as ex: @@ -312,6 +315,7 @@ def visit(self, node): arg_names.append(idx) arg_types.append(arg_type) + arg_nodes.append(arg) try: ret_type = self.context.get_type(node.type) @@ -321,9 +325,12 @@ def visit(self, node): node.ret_type = ret_type node.arg_types = arg_types node.arg_names = arg_names + node.arg_nodes = arg_nodes try: - self.current_type.define_method(node.id, arg_names, arg_types, ret_type) + method = self.current_type.define_method(node.id, arg_names, arg_types, ret_type) + method.nodes = arg_nodes + method.ret_node = node if not self.current_type.name in self.methods: self.methods[self.current_type.name] = {} self.methods[self.current_type.name][node.id] = node.tid @@ -385,20 +392,20 @@ def visit(self, node, scope): cur_type = self.current_type.parent while True: for attr in cur_type.attributes: - scope.define_variable(attr.name, attr.type) + var = scope.define_variable(attr.name, attr.type) + var.node = attr.node if not cur_type.parent: break cur_type = cur_type.parent - node.attr_idx = [] cur_type = self.current_type pending, count = [], 0 for idx, feature in enumerate(node.features): if isinstance(feature, AttrDeclarationNode): - node.attr_idx.append(idx) - if not scope.is_defined(feature.id): - scope.define_variable(feature.id, cur_type.attributes[count].type) self.visit(feature, scope) + if not scope.is_defined(feature.id): + var = scope.define_variable(feature.id, cur_type.attributes[count].type) + var.node = cur_type.attributes[count] count += 1 else: pending.append(feature) @@ -421,15 +428,16 @@ def visit(self, node, scope): @visitor.when(FuncDeclarationNode) def visit(self, node, scope): - self.current_method = Method(node.id, node.arg_names, node.arg_types, node.ret_type) + self.current_method = node.id - for pname, ptype in zip(self.current_method.param_names, self.current_method.param_types): - scope.define_variable(pname, ptype) + for pname, ptype, pnode in zip(node.arg_names, node.arg_types, node.arg_nodes): + var = scope.define_variable(pname, ptype) + var.node = pnode self.visit(node.body, scope) body_type = node.body.computed_type - method_rtn_type = fixed_type(self.current_method.return_type, self.current_type) + method_rtn_type = fixed_type(node.ret_type, self.current_type) node.info = [body_type, method_rtn_type] if not body_type.conforms_to(method_rtn_type): @@ -478,7 +486,8 @@ def visit(self, node, scope): branch_type = ErrorType() node.branch_type = branch_type - scope.define_variable(node.id, branch_type) + var = scope.define_variable(node.id, branch_type) + var.node = node self.visit(node.expr, scope) node.computed_type = node.expr.computed_type @@ -503,9 +512,10 @@ def visit(self, node, scope): node.attr_type = node_type if not scope.is_local(node.id): - scope.define_variable(node.id, node_type) + var = scope.define_variable(node.id, node_type) + var.node = node else: - self.errors.append((LOCAL_ALREADY_DEFINED % (node.id, self.current_method.name), node.tid)) + self.errors.append((LOCAL_ALREADY_DEFINED % (node.id, self.current_method), node.tid)) if node.expr: self.visit(node.expr, scope) From 083d68373908aed7d49aa7ae697c23a3b63c3b8c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 20:07:02 -0400 Subject: [PATCH 210/520] [visitors] - Update LCA Changes: - Unnecessary param removed - Check for `AutoType` first and later for `ErrorType` --- src/core/cmp/visitors.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index a64c5d1c..dc4e0532 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -339,13 +339,13 @@ def visit(self, node): # Compute the Lowest Common Ancestor in # the type hierarchy tree -def LCA(type_list, context): +def LCA(type_list): counter = {} - if any([isinstance(t, ErrorType) for t in type_list]): - return ErrorType() if any([isinstance(t, AutoType) for t in type_list]): return AutoType() + if any([isinstance(t, ErrorType) for t in type_list]): + return ErrorType() for typex in type_list: node = typex while True: @@ -474,7 +474,7 @@ def visit(self, node, scope): self.visit(case, scope.create_child()) types_list.append(case.computed_type) - node.computed_type = LCA(types_list, self.context) + node.computed_type = LCA(types_list) @visitor.when(CaseExpressionNode) def visit(self, node, scope): @@ -539,7 +539,7 @@ def visit(self, node, scope): self.visit(node.else_body, scope) else_type = node.else_body.computed_type - node.computed_type = LCA([if_type, else_type], self.context) + node.computed_type = LCA([if_type, else_type]) @visitor.when(BlockNode) def visit(self, node, scope): From ffe7de3251b2456a26db26c3ec0e217fc465202e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 20:15:55 -0400 Subject: [PATCH 211/520] [inferencer] - Add check_path method This method verify is a group of types conforms a path in the types tree. This condition needs to be hold in the set of types that an `AutoType` variable conforms to. Description: - Param `D`: List of types - Param `ans`: The initial First element in the path - Return: Tuple of `(Bool, Type)` representing if the set is a path and the first type in the path respectively --- src/core/cmp/visitors.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index dc4e0532..87983c6f 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -359,6 +359,16 @@ def LCA(type_list): break node = node.parent +def check_path(D, ans): + for t in D: + l = [ans, t] + lca = LCA(l) + try: l.remove(lca) + except ValueError: + return False, None + ans = l[0] + return True, ans + def IsAuto(name): return name == 'AUTO_TYPE' or IsVoid(name) From 68a77620442ed56d14575b1bbd5a982d18ad8ea9 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 20:18:02 -0400 Subject: [PATCH 212/520] [inferencer] - Save and update the collection of nodes that use AutoType Create a dictionary to store each node an its InferenceSets, and manage them --- src/core/cmp/visitors.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 87983c6f..4c24d7ec 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -754,6 +754,16 @@ def visit(self, node, scope): # //TODO: Try to infer SELF_TYPE # Type Inference Visitor class InferenceVisitor(TypeChecker): + def __init__(self, context, errors=[]): + super().__init__(context, errors) + self.variable = {} + + def inference(self, node, ntype, conforms=True): + try: + self.variable[node].add(ntype, conforms) + except KeyError: + self.variable[node] = InferenceSets().add(ntype, conforms) + @visitor.on('node') def update(self, node, scope, ntype): pass From b3e2fe99efe5c77f209103b817bc2a17ccdbd989 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 20:25:18 -0400 Subject: [PATCH 213/520] [inferencer] - Update the Inference process to the new approach Changes: - Deleted unnecessary code - Add `inference` method - Use `inference` in all everywhere the update_condition is verified `inference` Description: For each variable saved in `variables` find the best type to assign them or report a bad usage of `AUTO_TYPE` --- src/core/cmp/visitors.py | 80 +++++++++++----------------------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 4c24d7ec..58f749a2 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -778,11 +778,6 @@ def update(self, node, scope, ntype): if isinstance(branch.computed_type, AutoType): self.update(branch, scope, ntype) - for idx, branch in enumerate(node.branches): - cur_type = branch.scope.find_variable(branch.id).type - if update_condition(branch.branch_type, cur_type): - branch.type = cur_type.name - @visitor.when(CaseExpressionNode) def update(self, node, scope, ntype): self.update(node.expr, node.scope, ntype) @@ -791,11 +786,6 @@ def update(self, node, scope, ntype): def update(self, node, scope, ntype): self.update(node.in_body, node.scope, ntype) - for attr in node.let_body: - cur_type = node.scope.find_variable(attr.id).type - if update_condition(attr.attr_type, cur_type): - attr.type = cur_type.name - @visitor.when(IfThenElseNode) def update(self, node, scope, ntype): if isinstance(node.if_body.computed_type, AutoType): @@ -809,16 +799,15 @@ def update(self, node, scope, ntype): @visitor.when(FunctionCallNode) def update(self, node, scope, ntype): - node.obj_method.return_type = ntype + self.inference(node.obj_method.ret_node, ntype) @visitor.when(MemberCallNode) def update(self, node, scope, ntype): - node.obj_method.return_type = ntype + self.inference(node.obj_method.ret_node, ntype) @visitor.when(IdNode) def update(self, node, scope, ntype): - print("id" , ntype, scope.find_variable(node.lex).type) - scope.find_variable(node.lex).type = ntype + self.inference(scope.find_variable(node.lex).node, ntype) # Visit @visitor.on('node') @@ -831,17 +820,19 @@ def visit(self, node, scope): @visitor.when(ProgramNode) def visit(self, node, scope=None): - return super().visit(node, scope) + super().visit(node, scope) - @visitor.when(ClassDeclarationNode) - def visit(self, node, scope): - super().visit(node, scope) - - for idx, attr in enumerate(self.current_type.attributes): - actual_type = scope.find_variable(attr.name).type - if update_condition(attr.type, actual_type): - self.current_type.attributes[idx].type = actual_type - node.features[node.attr_idx[idx]].type = actual_type.name + for (auto, sets) in self.variable.items(): + try: + ok, D1 = check_path(sets.D, OBJ) + assert ok + if len(sets.S): + candidate = LCA(sets.S) + assert LCA([candidate, D1]) == D1 + D1 = candidate + auto.type = D1.name + except AssertionError: + self.errors.append((f'Bad use of AUTO_TYPE detected', auto.ttype)) @visitor.when(AttrDeclarationNode) def visit(self, node, scope): @@ -852,8 +843,7 @@ def visit(self, node, scope): expr, rtype = node.info if update_condition(rtype, expr): - scope.find_variable(node.id).type = expr - node.type = expr.name + self.inference(node, expr, False) if update_condition(expr, rtype): self.update(node.expr, scope, rtype) @@ -863,16 +853,9 @@ def visit(self, node, scope): body, rtn = node.info if update_condition(rtn, body): - self.current_method.return_type = body - node.type = body.name + self.inference(node, body, False) if update_condition(body, rtn): self.update(node.body, scope, rtn) - - for idx, pname in enumerate(self.current_method.param_names): - actual_type = scope.find_variable(pname).type - if update_condition(self.current_method.param_types[idx], actual_type): - self.current_method.param_types[idx] = actual_type - node.params[idx].ttype = actual_type.name @visitor.when(AssignNode) def visit(self, node, scope): @@ -880,33 +863,14 @@ def visit(self, node, scope): node_type, var = node.info if update_condition(var, node_type): - scope.find_variable(node.id).type = node_type + self.inference(scope.find_variable(node.id).node, node_type, False) if update_condition(node_type, var): self.update(node.expr, scope, var) - node.computed_type = var - - @visitor.when(CaseOfNode) - def visit(self, node, scope): - super().visit(node, scope) - - for idx, branch in enumerate(node.branches): - cur_type = branch.scope.find_variable(branch.id).type - if update_condition(branch.branch_type, cur_type): - branch.type = cur_type.name @visitor.when(CaseExpressionNode) def visit(self, node, scope): super().visit(node, scope) - @visitor.when(LetInNode) - def visit(self, node, scope): - super().visit(node, scope) - - for attr in node.let_body: - cur_type = node.scope.find_variable(attr.id).type - if update_condition(attr.attr_type, cur_type): - attr.type = cur_type.name - @visitor.when(LetAttributeNode) def visit(self, node, scope): super().visit(node, scope) @@ -916,8 +880,7 @@ def visit(self, node, scope): expr, rtype = node.info if update_condition(rtype, expr): - scope.find_variable(node.id).type = expr - node.type = expr.name + self.inference(scope.find_variable(node.id).node, expr, False) if update_condition(expr, rtype): self.update(node.expr, scope, rtype) @@ -945,7 +908,7 @@ def visit(self, node, scope): for idx, (atype, rtype) in enumerate(zip(args, real)): if update_condition(rtype, atype): - node.obj_method.param_types[idx] = atype + self.inference(node.obj_method.nodes[idx], atype, False) if update_condition(atype, rtype): self.update(node.args[idx], scope, rtype) @@ -959,7 +922,7 @@ def visit(self, node, scope): for idx, (atype, rtype) in enumerate(zip(args, real)): if update_condition(rtype, atype): - node.obj_method.param_types[idx] = atype + self.inference(node.obj_method.nodes[idx], atype, False) if update_condition(atype, rtype): self.update(node.args[idx], scope, rtype) @@ -991,6 +954,7 @@ def visit(self, node, scope): def visit(self, node, scope): super().visit(node, scope) + # //TODO: Only infer if atomic left, right = node.info if update_condition(left, right): self.update(node.left, scope, right) From 93e6d47a9e2d827117355ae37ed5a1593c3683de Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 20:28:33 -0400 Subject: [PATCH 214/520] [main] - Update the pipeline with the new inferencer --- src/main.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main.py b/src/main.py index 0aa86c35..f24a0c21 100644 --- a/src/main.py +++ b/src/main.py @@ -58,16 +58,9 @@ def main(args): errors.extend(builder.errors) # Checking types - checker = InferenceVisitor(context) - auto = -1 - while True: - checker.errors.clear() - scope = checker.visit(ast) - cant = scope.count_auto() - if auto == cant: - break - auto = cant - errors.extend(checker.errors) + inferencer = InferenceVisitor(context) + inferencer.visit(ast) + errors.extend(inferencer.errors) if errors: for (msg, token) in errors: From 416d004860be78efdbcdb265ea19b8db3e2930b2 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 20:59:53 -0400 Subject: [PATCH 215/520] [utils] - Fix a bug in InferenceSets --- src/core/cmp/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/utils.py b/src/core/cmp/utils.py index c7f39d18..55ba330f 100644 --- a/src/core/cmp/utils.py +++ b/src/core/cmp/utils.py @@ -270,7 +270,9 @@ def __call__(self, w, get_shift_reduce=False): raise ValueError class InferenceSets: - D, S = [], [] + def __init__(self): + self.D = [] + self.S = [] def add(self, new_type, conforms=True): cur = [self.S, self.D][conforms] From 32aaf6693988c6a8ab5bef3357d7ea1040217f51 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 4 Aug 2020 21:13:27 -0400 Subject: [PATCH 216/520] [inferencer] - Add always Object to the conforms to set of an AutoTypre variable --- src/core/cmp/visitors.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 58f749a2..d632b7f8 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -838,6 +838,9 @@ def visit(self, node, scope=None): def visit(self, node, scope): super().visit(node, scope) + if isinstance(node.attr_type, AutoType): + self.inference(node, OBJ) + if not node.expr: return @@ -852,6 +855,11 @@ def visit(self, node, scope): super().visit(node, scope) body, rtn = node.info + if isinstance(rtn, AutoType): + self.inference(node, OBJ) + for ptype, pnode in zip(node.arg_types, node.arg_nodes): + if isinstance(ptype, AutoType): + self.inference(pnode, OBJ) if update_condition(rtn, body): self.inference(node, body, False) if update_condition(body, rtn): @@ -871,10 +879,16 @@ def visit(self, node, scope): def visit(self, node, scope): super().visit(node, scope) + if isinstance(node.branch_type, AutoType): + self.inference(node, OBJ) + @visitor.when(LetAttributeNode) def visit(self, node, scope): super().visit(node, scope) + if isinstance(node.attr_type, AutoType): + self.inference(node, OBJ) + if not node.expr: return From 05ee65d8374a9bd8a67e3f15c67db1a0a3e90ff0 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 5 Aug 2020 00:42:15 -0400 Subject: [PATCH 217/520] [inferencer] - Add a visitor method context_update When a variable change its type it is necessary to propagate the change to the context Description: - Param `node`: The node that change its type - Param `ntype`: The new type given to the `node` Extra: - The class `Param` inherits from `Node` --- src/core/cmp/CoolUtils.py | 2 +- src/core/cmp/visitors.py | 51 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 374c13bf..77d448b0 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -181,7 +181,7 @@ def FunctionCallNodeBuilder(obj, calls): #print("-------------------") return obj -class Param: +class Param(Node): def __init__(self, tid, ttype): self.tid = tid self.ttype = ttype diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index d632b7f8..8dab7323 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -299,6 +299,7 @@ def visit(self, node): try: attr = self.current_type.define_attribute(node.id, attr_type) attr.node = node + node.attr = attr except SemanticError as ex: self.errors.append((ex.text, node.tid)) @@ -316,6 +317,8 @@ def visit(self, node): arg_names.append(idx) arg_types.append(arg_type) arg_nodes.append(arg) + arg.idx = i + arg.method_types = arg_types try: ret_type = self.context.get_type(node.type) @@ -331,6 +334,9 @@ def visit(self, node): method = self.current_type.define_method(node.id, arg_names, arg_types, ret_type) method.nodes = arg_nodes method.ret_node = node + node.method = method + for arg in node.params: + arg.method = method if not self.current_type.name in self.methods: self.methods[self.current_type.name] = {} self.methods[self.current_type.name][node.id] = node.tid @@ -415,7 +421,7 @@ def visit(self, node, scope): self.visit(feature, scope) if not scope.is_defined(feature.id): var = scope.define_variable(feature.id, cur_type.attributes[count].type) - var.node = cur_type.attributes[count] + var.node = cur_type.attributes[count].node count += 1 else: pending.append(feature) @@ -520,6 +526,7 @@ def visit(self, node, scope): self.errors.append((ex.text, node.ttype)) node_type = ErrorType() node.attr_type = node_type + node.scope = None if not scope.is_local(node.id): var = scope.define_variable(node.id, node_type) @@ -754,8 +761,9 @@ def visit(self, node, scope): # //TODO: Try to infer SELF_TYPE # Type Inference Visitor class InferenceVisitor(TypeChecker): - def __init__(self, context, errors=[]): + def __init__(self, context, skip_object=True, errors=[]): super().__init__(context, errors) + self.skip = skip_object self.variable = {} def inference(self, node, ntype, conforms=True): @@ -764,6 +772,35 @@ def inference(self, node, ntype, conforms=True): except KeyError: self.variable[node] = InferenceSets().add(ntype, conforms) + @visitor.on('node') + def context_update(self, node, ntype): + pass + + @visitor.when(Node) + def context_update(self, node, ntype): + pass + + @visitor.when(Param) + def context_update(self, node, ntype): + node.method_types[node.idx] = ntype + try: node.method.param_types[node.idx] = ntype + except AttributeError: pass + + @visitor.when(AttrDeclarationNode) + def context_update(self, node, ntype): + try: node.attr_type = ntype + except AttributeError: pass + try: node.branch_type = ntype + except AttributeError: pass + try: node.attr.type = ntype + except AttributeError: pass + + @visitor.when(FuncDeclarationNode) + def context_update(self, node, ntype): + node.ret_type = ntype + try: node.method.return_type = ntype + except AttributeError: pass + @visitor.on('node') def update(self, node, scope, ntype): pass @@ -822,8 +859,12 @@ def visit(self, node, scope): def visit(self, node, scope=None): super().visit(node, scope) + infered = 0 + if not self.skip: print(self.variable.keys()) for (auto, sets) in self.variable.items(): try: + if self.skip and (len(sets.D) + len(sets.S) == 1): + continue ok, D1 = check_path(sets.D, OBJ) assert ok if len(sets.S): @@ -831,8 +872,12 @@ def visit(self, node, scope=None): assert LCA([candidate, D1]) == D1 D1 = candidate auto.type = D1.name + self.context_update(auto, D1) + infered += 1 except AssertionError: self.errors.append((f'Bad use of AUTO_TYPE detected', auto.ttype)) + self.variable.clear() + return infered @visitor.when(AttrDeclarationNode) def visit(self, node, scope): @@ -888,7 +933,7 @@ def visit(self, node, scope): if isinstance(node.attr_type, AutoType): self.inference(node, OBJ) - + if not node.expr: return From 929163f5f09e477b6eb10cd582252372db4081d1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 5 Aug 2020 00:55:24 -0400 Subject: [PATCH 218/520] [main] - Repeat the inference process until there are not more variables changed Extra: - It is necessary a last repetition for change the not changed `AutoType` variables to `Object` --- src/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.py b/src/main.py index f24a0c21..dac50af3 100644 --- a/src/main.py +++ b/src/main.py @@ -59,6 +59,9 @@ def main(args): # Checking types inferencer = InferenceVisitor(context) + while inferencer.visit(ast): pass + inferencer.errors.clear() + inferencer.skip = False inferencer.visit(ast) errors.extend(inferencer.errors) From e3bbe98747977c4411f8ee2c200891282f6041f6 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 11 Aug 2020 12:31:47 -0400 Subject: [PATCH 219/520] Divide cil code in basic blocks and create flow graph between blocks --- src/core/cmp/cil.py | 4 +- src/core/cmp/cil_to_mips.py | 185 ++++++++++++++++++++++++++++++++---- 2 files changed, 167 insertions(+), 22 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 62df4d3a..03a2ea03 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -38,7 +38,9 @@ def __init__(self, name): self.name = name class InstructionNode(Node): - pass + def __init__(self): + self.leader = False + class AssignNode(InstructionNode): def __init__(self, dest, source): diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 4800033e..ee887c26 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -3,6 +3,44 @@ import core.cmp.mips as mips from core.cmp.utils import CountDict + + +class RegistersManager: + def __init__(self): + pass + + def getReg(self, instruction): + pass + +class Register: + def __init__(self, name): + self._name = name + self._registerDescriptor = RegisterDescriptor() + + @property + def name(self): + return self._name + + @property + def get_register_descriptor(self): + return self._registerDescriptor + +class RegisterDescriptor: + def __init__(self): + pass + + +class AddressDescriptor: + def __init__(self): + #List locations + pass + +class SymbolTable: + def __init__(self): + #Dict name: AddressDescriptor + pass + + class BaseCILToMIPSVisitor: def __init__(self): self.types = {} @@ -14,6 +52,7 @@ def __init__(self): self.funcs_dict = CountDict() self.str_consts = CountDict() self.int_consts = CountDict() + self.symbol_table = SymbolTable() @property @@ -109,27 +148,27 @@ def visit(self, node): def visit(self, node): self.actual_function = node - registers_to_save = ['ra', 't0', 't1'] - #Saving Register - self.add_instructions(mips.save_registers(registers_to_save)) - - #Argument received to params - self.add_instructions(mips.MoveNode(mips.REGISTERS['t1'], mips.REGISTERS['t2'])) - - #Allocating memory for local variables - addr = mips.Address(mips.REGISTERS['t0'], 0) - var_size = len(self.localvars) * mips.ATTR_SIZE - self.add_instructions(mips.allocate_memory(addr, var_size)) - - #function_body - for instruction in self.cil_isntructions: - self.visit(instruction) - - #Loading saved register - self.add_instructions(mips.load_registers_from_stack(registers_to_save[-1])) - - self.actual_function_instructions = [] - self.actual_function = None + #registers_to_save = ['ra', 't0', 't1'] + ## Saving Register + #self.add_instructions(mips.save_registers(registers_to_save)) + # + ## Argument received to params + #self.add_instructions(mips.MoveNode(mips.REGISTERS['t1'], mips.REGISTERS['t2'])) + # + ## Allocating memory for local variables + #addr = mips.Address(mips.REGISTERS['t0'], 0) + #var_size = len(self.localvars) * mips.ATTR_SIZE + #self.add_instructions(mips.allocate_memory(addr, var_size)) + + ## function_body + #for instruction in self.cil_isntructions: + # self.visit(instruction) + + ## Loading saved register + #self.add_instructions(mips.load_registers_from_stack(registers_to_save[-1])) + + #self.actual_function_instructions = [] + #self.actual_function = None @visitor.when(cil.AllocateNode) def visit(self, node): @@ -153,6 +192,110 @@ def cil_to_mips_type(cil_type): +# class A: + # pass +# +# class B(A): + # pass +# +# class C: + # pass +# +# @visitor.on('param') +# def try2(param): + # pass +# +# @visitor.when(A) +# def try2(param): + # print("is A") +# +# @visitor.when(B) +# def try2(param): + # print("is B") + + +#Change Name +class FunctionDivider: + def __init__(self): + self.mark + + + def divide_basics_blocks(self, intructions): + self.mark = True + for instruction in instructions: + self.mark_leaders(instruction) + + blocks = [] + + for instruction in instrucctions: + if instruction.leader: + block.append([instruction]) + block[-1].append(instruction) + + return blocks + + def create_flow_graph(blocks) #graph between blocks in a same function does not include relations between functions + graph = [[-1 for _ in range(len(blocks))] for _ in range(len(blocks)) ] + labels = {b.name : i for i, b in enumerate(blocks) if type(b[0]) == cil.LabelNode} + + for i, block in enumerate(blocks): + tp = type(block[-1]) + if tp == cil.GotoNode: + graph[i][labels[block[-1].label]] = 1 + + elif tp == cil.GotoIfNode: + graph[i][labels[block[-1].label]] = 1 + graph[i][min(len(blocks)-1, i+1)] = 1 + + elif tp == cil.DynamicCallNode: + pass #analize what to do with function calls + elif tp == cil.StaticCallNode: + pass #analize what to do with function calls + + return graph + + + + @visitor.on('instruction') + def mark_leaders(self, instruction): + pass + + @visitor.when(cil.LabelNode) + def mark_leaders(self, instruction): + instruction.leader = True + self.mark = False + + @visitor.when(cil.GotoNode) + def mark_leaders(self, instruction): + self.mark = True + + @visitor.when(cil.GotoIfNode) + def mark_leaders(self, instruction): + self.mark = True + + @visitor.when(cil.DynamicCallNode) + def mark_leaders(self, instruction): + self.mark = True + + @visitor.when(cil.StaticCallNode) + def mark_leaders(self, instruction): + self.mark = True + + @visitor.when(cil.InstructionNode) + def mark_leaders(self, instruction): + instruction.leader = self.mark + self.mark = False + + + + + + + + + + + #TEST From 9d33057e6c33a340a60fc80c69eac390a3bf42cc Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 14 Aug 2020 18:53:46 -0400 Subject: [PATCH 220/520] [inferencer] - Remove the skip flag When there are no new inferred variables, the variables without enough information to infer, change their type to Object Extra: Some unnecessary comments and prints removed --- src/core/cmp/CoolUtils.py | 3 --- src/core/cmp/visitors.py | 13 ++++++++----- src/main.py | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 77d448b0..0d2f0b0c 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -173,12 +173,9 @@ class BoolNode(AtomicNode): pass def FunctionCallNodeBuilder(obj, calls): - #print("-------------------") while len(calls): - #print(obj) obj = FunctionCallNode(obj, *calls[0]) calls.pop(0) - #print("-------------------") return obj class Param(Node): diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 8dab7323..089e4420 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -758,12 +758,10 @@ def visit(self, node, scope): node.computed_type = BOOL -# //TODO: Try to infer SELF_TYPE # Type Inference Visitor class InferenceVisitor(TypeChecker): - def __init__(self, context, skip_object=True, errors=[]): + def __init__(self, context, errors=[]): super().__init__(context, errors) - self.skip = skip_object self.variable = {} def inference(self, node, ntype, conforms=True): @@ -860,10 +858,11 @@ def visit(self, node, scope=None): super().visit(node, scope) infered = 0 - if not self.skip: print(self.variable.keys()) + pending = [] for (auto, sets) in self.variable.items(): try: - if self.skip and (len(sets.D) + len(sets.S) == 1): + if (len(sets.D) + len(sets.S) == 1): + pending.append(auto) continue ok, D1 = check_path(sets.D, OBJ) assert ok @@ -876,6 +875,10 @@ def visit(self, node, scope=None): infered += 1 except AssertionError: self.errors.append((f'Bad use of AUTO_TYPE detected', auto.ttype)) + if not infered: + for auto in pending: + auto.type = OBJ.name + self.context_update(auto, OBJ) self.variable.clear() return infered diff --git a/src/main.py b/src/main.py index dac50af3..3c6d9d48 100644 --- a/src/main.py +++ b/src/main.py @@ -61,7 +61,6 @@ def main(args): inferencer = InferenceVisitor(context) while inferencer.visit(ast): pass inferencer.errors.clear() - inferencer.skip = False inferencer.visit(ast) errors.extend(inferencer.errors) From 6c18b2604510ba1f8b914dc466f8bc08db9cf69d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 14 Aug 2020 19:55:03 -0400 Subject: [PATCH 221/520] [inferencer] - Use only builtin types to infer AUTO_TYPE variables in EqualNode --- src/core/cmp/visitors.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 089e4420..45b9d1a7 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1016,10 +1016,9 @@ def visit(self, node, scope): def visit(self, node, scope): super().visit(node, scope) - # //TODO: Only infer if atomic left, right = node.info - if update_condition(left, right): + if update_condition(left, right) and right in [INT, BOOL, STRING]: self.update(node.left, scope, right) - if update_condition(right, left): + if update_condition(right, left) and left in [INT, BOOL, STRING]: self.update(node.right, scope, left) From be88cab8eef222a73e4d603b1e7467bdcef34769 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 16 Aug 2020 19:03:34 -0400 Subject: [PATCH 222/520] Create StoreLocation, MemoryLocation, HeapLocation, StackLocation to represent memory --- src/core/cmp/cil_to_mips.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index ee887c26..51bda426 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -12,7 +12,10 @@ def __init__(self): def getReg(self, instruction): pass -class Register: +class StoreLocation: + pass + +class Register(StoreLocation): def __init__(self, name): self._name = name self._registerDescriptor = RegisterDescriptor() @@ -27,10 +30,20 @@ def get_register_descriptor(self): class RegisterDescriptor: def __init__(self): + #ListVariables pass +class MemoryLocation(StoreLocation): + pass + +class HeapLocation(MemoryLocation): + pass + +class StackLocation(MemoryLocation): + pass + -class AddressDescriptor: +class AddressDescriptor(): def __init__(self): #List locations pass From 1c86e7f0c8a0e7ad7dc55063b163f5cbee9c2531 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 17 Aug 2020 11:02:23 -0400 Subject: [PATCH 223/520] Fix func definition --- src/core/cmp/cil_to_mips.py | 2 +- src/main.py | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 51bda426..35aa1b0e 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -247,7 +247,7 @@ def divide_basics_blocks(self, intructions): return blocks - def create_flow_graph(blocks) #graph between blocks in a same function does not include relations between functions + def create_flow_graph(blocks): #graph between blocks in a same function does not include relations between functions graph = [[-1 for _ in range(len(blocks))] for _ in range(len(blocks)) ] labels = {b.name : i for i, b in enumerate(blocks) if type(b[0]) == cil.LabelNode} diff --git a/src/main.py b/src/main.py index 5ae43179..29292fc4 100644 --- a/src/main.py +++ b/src/main.py @@ -101,12 +101,12 @@ def main(args): if __name__ == "__main__": - # import argparse -# - # parser = argparse.ArgumentParser(description='CoolCompiler pipeline') - # parser.add_argument('-f', '--file', type=str, default='code.cl', help='node address') -# - # args = parser.parse_args() - # main(args) - - test() + import argparse + + parser = argparse.ArgumentParser(description='CoolCompiler pipeline') + parser.add_argument('-f', '--file', type=str, default='code.cl', help='node address') + + args = parser.parse_args() + main(args) + + # test() From 92bac9ca8d351c31aed3d5f4cc730d84c1458dde Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 17 Aug 2020 16:55:18 -0400 Subject: [PATCH 224/520] [cil] - Implement a function to help the translation of CaseOfNode Function: buildHierarchy Description: Build a list with all types that conforms to a given type --- src/core/cmp/cool_to_cil.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3caadf2c..41dbe381 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -158,6 +158,15 @@ def register_built_in(self): class COOLToCILVisitor(BaseCOOLToCILVisitor): + def __init__(self, context): + super().__init__(context) + + def buildHierarchy(self, t:str): + h = [] + if t == 'Object': return None + h.extend([x for x in self.context.types if x.conforms_to(t)]) + return h + @visitor.on('node') def visit(self, node): pass From 688cc7204f55658b17332e5434af725569e5c888 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 17 Aug 2020 16:57:55 -0400 Subject: [PATCH 225/520] [cil] - Implement visit of `CaseOfNode` and `CaseExpressionNode` --- src/core/cmp/cool_to_cil.py | 40 +++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 41dbe381..720a4187 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -360,8 +360,41 @@ def visit(self, node, scope): # node.expr -> ExpressionNode # node.branches -> [ CaseExpressionNode ... } ############################################## - #//TODO: Implement CaseOfNode - pass + vexpr = self.register_local(VariableInfo('case_expr_value', None)) + vtype = self.register_local(VariableInfo('typeName_value', None)) + vcond = self.register_local(VariableInfo('equal_value', None)) + vret = self.register_local(VariableInfo('case_value', None)) + self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(vexpr, scope.ret_expr)) + self.register_instruction(cil.TypeNameNode(vtype, scope.ret_expr)) + #//TODO: Ask if is void and raise proper error if vexpr value is void + + end_label = self.register_label('end_label') + labels = [] + for idx, b in enumerate(node.branches): + labels.append(self.register_label(f'{idx}_label')) + h = self.buildHierarchy(b.type) + if not h: + self.register_instruction(cil.GotoNode(labels[-1].label)) + break + for t in h: + data_node = self.register_data(t) + vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) + self.register_instruction(cil.LoadNode(vbranch_type_name, data_node.name)) + self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) + self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) + + #//TODO: Raise runtime error if no Goto was executed + + for idx, l in enumerate(labels): + self.register_instruction(l) + vid = self.register_local(VariableInfo(node.branches[idx].id, None)) + self.register_instruction(cil.AssignNode(vid, vexpr)) + self.visit(node.branches[idx], scope) + self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) + self.register_instruction(cil.GotoNode(end_label.label)) + + self.register_instruction(end_label) @visitor.when(cool.CaseExpressionNode) def visit(self, node, scope): @@ -370,8 +403,7 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - #//TODO: Implement CaseExpressionNode - pass + self.visit(node.expr, scope) @visitor.when(cool.LetAttributeNode) def visit(self, node, scope): From 9053564eb5e728acf1ef4e133b012aa47370e600 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 17 Aug 2020 20:18:11 -0400 Subject: [PATCH 226/520] [cil] - Refactor `buildHierarchy` --- src/core/cmp/cool_to_cil.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 720a4187..53947560 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -162,10 +162,8 @@ def __init__(self, context): super().__init__(context) def buildHierarchy(self, t:str): - h = [] if t == 'Object': return None - h.extend([x for x in self.context.types if x.conforms_to(t)]) - return h + return [x.name for x in self.context.types if x.conforms_to(t)]) @visitor.on('node') def visit(self, node): From ad6cb2680d3edbb72e62ae376862e1e48df2b5f2 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 17 Aug 2020 20:23:49 -0400 Subject: [PATCH 227/520] [cil] - Upgrade visit of `CaseOfNode` Skip branches which type already conforms to previous branches --- src/core/cmp/cool_to_cil.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 53947560..1573996e 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -369,18 +369,20 @@ def visit(self, node, scope): end_label = self.register_label('end_label') labels = [] + old = [] for idx, b in enumerate(node.branches): labels.append(self.register_label(f'{idx}_label')) h = self.buildHierarchy(b.type) if not h: self.register_instruction(cil.GotoNode(labels[-1].label)) break - for t in h: - data_node = self.register_data(t) - vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) - self.register_instruction(cil.LoadNode(vbranch_type_name, data_node.name)) - self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) - self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) + if len([True for x in old if b.type in x]) > 0: + for t in h: + data_node = self.register_data(t) + vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) + self.register_instruction(cil.LoadNode(vbranch_type_name, data_node.name)) + self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) + self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) #//TODO: Raise runtime error if no Goto was executed From 5e292393377eae2fa975f652ab7744d2fe76ffa4 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 17 Aug 2020 23:37:32 -0400 Subject: [PATCH 228/520] [inferencer] - Return the scope --- src/core/cmp/visitors.py | 2 +- src/main.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index f01234f8..cdfda5f6 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -874,7 +874,7 @@ def visit(self, node, scope=None): auto.type = OBJ.name self.context_update(auto, OBJ) self.variable.clear() - return infered + return infered, scope @visitor.when(AttrDeclarationNode) def visit(self, node, scope): diff --git a/src/main.py b/src/main.py index 6200daf3..1b43d0c0 100644 --- a/src/main.py +++ b/src/main.py @@ -63,9 +63,9 @@ def main(args): # Checking types inferencer = InferenceVisitor(context) - while inferencer.visit(ast): pass + while inferencer.visit(ast)[0]: pass inferencer.errors.clear() - inferencer.visit(ast) + _, scope = inferencer.visit(ast) errors.extend(inferencer.errors) if errors: From ef52793dc8915e6332becaffa8a890afd28aab6c Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 09:52:48 -0400 Subject: [PATCH 229/520] [cil] - Change return of method`buildHierarchy`. Return set instead of list --- src/core/cmp/CoolUtils.py | 1 + src/core/cmp/cool_to_cil.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 928281bc..07759b9e 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -431,6 +431,7 @@ class Main inherits IO { }; ''' +_text3 = ''' class B : A { c : int ; def f ( d : int , a : A ) : void { diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 1573996e..9e4851f6 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -163,7 +163,7 @@ def __init__(self, context): def buildHierarchy(self, t:str): if t == 'Object': return None - return [x.name for x in self.context.types if x.conforms_to(t)]) + return {x.name for x in self.context.types if x.conforms_to(t)} @visitor.on('node') def visit(self, node): From b5ab99b7726aca1b18a2a14b902cebdd1a8e2b45 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 09:55:34 -0400 Subject: [PATCH 230/520] [cil] - Upgrade visit of `CaseOfNode` Skip branches which type already conforms to previous branches. Also skip types that appear before. --- src/core/cmp/cool_to_cil.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 9e4851f6..860b14c0 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -369,20 +369,22 @@ def visit(self, node, scope): end_label = self.register_label('end_label') labels = [] - old = [] + old = {} for idx, b in enumerate(node.branches): labels.append(self.register_label(f'{idx}_label')) h = self.buildHierarchy(b.type) + h.add(b.type) if not h: self.register_instruction(cil.GotoNode(labels[-1].label)) break - if len([True for x in old if b.type in x]) > 0: - for t in h: - data_node = self.register_data(t) - vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) - self.register_instruction(cil.LoadNode(vbranch_type_name, data_node.name)) - self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) - self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) + for s in old: + h -= s + for t in h: + data_node = self.register_data(t) + vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) + self.register_instruction(cil.LoadNode(vbranch_type_name, data_node.name)) + self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) + self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) #//TODO: Raise runtime error if no Goto was executed From fd5c7dfec14c0237d53b401df100ce2f9a2fb79b Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 09:56:04 -0400 Subject: [PATCH 231/520] [main] - Import `COOLToCILVisitor` from `cool_to_cil` --- src/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.py b/src/main.py index 1b43d0c0..1693a9b6 100644 --- a/src/main.py +++ b/src/main.py @@ -8,6 +8,7 @@ from core.cmp.evaluation import * from core.cmp.cil import get_formatter from pprint import pprint +from core.cmp.cool_to_cil import COOLToCILVisitor def main(args): From 11638c6d90fad5bf6a7e949456a5677748cfcd3a Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 18 Aug 2020 11:41:07 -0400 Subject: [PATCH 232/520] [inferencer] - Fix an error retuning the scope --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index cdfda5f6..83874411 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -849,7 +849,7 @@ def visit(self, node, scope): @visitor.when(ProgramNode) def visit(self, node, scope=None): - super().visit(node, scope) + scope = super().visit(node, scope) infered = 0 pending = [] From 38e7f22eb7035a5b7d4fd4619b8c9c3d0f5415e3 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 21:36:52 -0400 Subject: [PATCH 233/520] [cil] [semantic] - Fix bug in `all_methods` and `buildHierarchy` methods Use values of the dictionary instead of keys --- src/core/cmp/cool_to_cil.py | 2 +- src/core/cmp/semantic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 860b14c0..edcdc3e6 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -163,7 +163,7 @@ def __init__(self, context): def buildHierarchy(self, t:str): if t == 'Object': return None - return {x.name for x in self.context.types if x.conforms_to(t)} + return {x.name for x in self.context.types.values() if x.conforms_to(self.context.get_type(t))} @visitor.on('node') def visit(self, node): diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index afa5944d..a8c80ae2 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -100,7 +100,7 @@ def all_attributes(self, clean=True): def all_methods(self, clean=True): plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) - for method in self.methods: + for method in self.methods.values(): plain[method.name] = (method, self) return plain.values() if clean else plain From 60be518163a7c820117b8544efb6d1912d0a27f6 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 21:40:00 -0400 Subject: [PATCH 234/520] [cil] - Fix initializations of some `VariableInfo` Used in `register_local` method. They lacked a parameter --- src/core/cmp/cool_to_cil.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index edcdc3e6..9d5faa3c 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -575,11 +575,11 @@ def visit(self, node, scope): args = [] for arg in node.args: - vname = self.register_local(VariableInfo(f'{node.id}_arg')) + vname = self.register_local(VariableInfo(f'{node.id}_arg', None)) self.visit(arg, scope) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) args.append(cil.ArgNode(vname)) - result = self.register_local(VariableInfo(f'return_value_of_{node.id}')) + result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None)) if node.type: #Call of type @.id(,...,) @@ -598,7 +598,7 @@ def visit(self, node, scope): scope.ret_expr = result else: #Call of type .(,...,) - type_of_node = self.register_local(VariableInfo(f'{node.id}_type')) + type_of_node = self.register_local(VariableInfo(f'{node.id}_type', None)) self.register_instruction(cil.TypeOfNode(node.obj.lex, type_of_node)) instance = self.define_internal_local() self.register_instruction(cil.DynamicCallNode(type_of_node, 'init', instance)) @@ -620,11 +620,11 @@ def visit(self, node, scope): args = [] for arg in node.args: - vname = self.register_local(VariableInfo(f'{node.id}_arg')) + vname = self.register_local(VariableInfo(f'{node.id}_arg', None)) self.visit(arg, scope) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) args.append(cil.ArgNode(vname)) - result = self.register_local(VariableInfo(f'return_value_of_{node.id}')) + result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None)) self.register_instruction(cil.ArgNode(self.vself.name)) for arg in args: From fb7d6c43f3202a8a3b349cced6d4f60e6396b1e9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 21:42:11 -0400 Subject: [PATCH 235/520] [cil] - Update visit of `FuncDeclarationNode` `node.body` attribute is a single `ExpressionNode`, no a list of it --- src/core/cmp/cool_to_cil.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 9d5faa3c..5b743961 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -248,7 +248,7 @@ def visit(self, node, scope): # node.id -> str # node.params -> [ (str, str) ... ] # node.type -> str - # node.body -> [ ExpressionNode ... ] + # node.body -> ExpressionNode ##################################### self.current_method = self.current_type.get_method(node.id) @@ -262,8 +262,7 @@ def visit(self, node, scope): self.register_param(VariableInfo(param_name, None)) scope.ret_expr = None - for instruction in node.body: - self.visit(instruction, scope) + self.visit(node.body, scope) # (Handle RETURN) if scope.ret_expr is None: self.register_instruction(cil.ReturnNode('')) From 7946e97b18aba76fa59cfb318d426dc2453431c6 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 21:44:51 -0400 Subject: [PATCH 236/520] [cil] - Fix bug related to 'list have no method method get_attribute' Fix: Call `get_attribute` from `current_type` instead from `current_type.attributes` --- src/core/cmp/cool_to_cil.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 5b743961..6f467e0b 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -429,7 +429,7 @@ def visit(self, node, scope): self.visit(node.expr, scope) try: - self.current_type.attributes.get_attribute(node.id) + self.current_type.get_attribute(node.id) self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr)) scope.ret_expr = node.id except SemanticError: @@ -654,7 +654,7 @@ def visit(self, node, scope): # node.lex -> str ############################### try: - self.current_type.attributes.get_attribute(node.lex) + self.current_type.get_attribute(node.lex) attr = self.register_local(VariableInfo('attr_value', None)) self.register_instruction(cil.GetAttribNode(attr, self.vself, node.lex)) scope.ret_expr = attr From 024c3cd482a1d569d7ebb8dd0b1b5cb8c508408b Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 18 Aug 2020 21:46:06 -0400 Subject: [PATCH 237/520] [cil] - Update visit of `FunctionCallNode` --- src/core/cmp/cool_to_cil.py | 23 ++++++++++++----------- src/main.py | 6 +++--- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 6f467e0b..113f19d4 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -372,10 +372,10 @@ def visit(self, node, scope): for idx, b in enumerate(node.branches): labels.append(self.register_label(f'{idx}_label')) h = self.buildHierarchy(b.type) - h.add(b.type) if not h: self.register_instruction(cil.GotoNode(labels[-1].label)) break + h.add(b.type) for s in old: h -= s for t in h: @@ -566,7 +566,7 @@ def visit(self, node, scope): @visitor.when(cool.FunctionCallNode) def visit(self, node, scope): ###################################### - # node.obj -> AtomicNode + # node.obj -> ExpressionNode # node.id -> str # node.args -> [ ExpressionNode ... ] # node.type -> str @@ -581,24 +581,25 @@ def visit(self, node, scope): result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None)) if node.type: - #Call of type @.id(,...,) + #Call of type @.id(,...,) #Is ok to search node.type in dottypes??? - at_type = [typex for typex in self.dottypes if typex.name == node.type][0] - instance = self.define_internal_local() - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', at_type.name), instance)) + vparent = self.define_internal_local() + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', node.type), vparent)) #self for Static Dispatch - self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.ArgNode(vparent)) for arg in args: self.register_instruction(arg) - #method = [method for method in at_type.methods if method.name == node.id][0] #Shall we look method node.id in at_type parents??? - self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, at_type.name), result)) + self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, node.type), result)) scope.ret_expr = result else: - #Call of type .(,...,) + #Call of type .(,...,) type_of_node = self.register_local(VariableInfo(f'{node.id}_type', None)) - self.register_instruction(cil.TypeOfNode(node.obj.lex, type_of_node)) + vobj = self.define_internal_local() + self.visit(node.obj, scope) + self.register_instruction(cil.AssignNode(vobj, scope.ret_expr)) + self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) instance = self.define_internal_local() self.register_instruction(cil.DynamicCallNode(type_of_node, 'init', instance)) #self for Dynamic Dispatch diff --git a/src/main.py b/src/main.py index 1693a9b6..0d50ede3 100644 --- a/src/main.py +++ b/src/main.py @@ -79,9 +79,9 @@ def main(args): #CIL Transformation cool_to_cil = COOLToCILVisitor(context) cil_ast = cool_to_cil.visit(ast, scope) - formatter = get_formatter() - ast_cil = formatter(cil_ast) - print(ast_cil) + #formatter = get_formatter() + #ast_cil = formatter(cil_ast) + #print(ast_cil) exit(0) From 4498f39691aaca7e43924deef24933224c520be8 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Wed, 19 Aug 2020 13:01:32 -0400 Subject: [PATCH 238/520] [cil] - Update visit of `FunctionCallNode` Pass obj caller as self, instead of a new instance of the dynamic type of obj --- src/core/cmp/cool_to_cil.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 113f19d4..486eb995 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -239,6 +239,7 @@ def visit(self, node, scope): try: scope.ret_expr = self.default_values[node.type] except KeyError: + #Void value scope.ret_expr = None self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr)) @@ -583,10 +584,11 @@ def visit(self, node, scope): if node.type: #Call of type @.id(,...,) #Is ok to search node.type in dottypes??? - vparent = self.define_internal_local() - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', node.type), vparent)) + vobj = self.define_internal_local() + self.visit(node.obj, scope) + self.register_instruction(cil.AssignNode(vobj, scope.ret_expr)) #self for Static Dispatch - self.register_instruction(cil.ArgNode(vparent)) + self.register_instruction(cil.ArgNode(vobj)) for arg in args: self.register_instruction(arg) #method = [method for method in at_type.methods if method.name == node.id][0] @@ -600,10 +602,8 @@ def visit(self, node, scope): self.visit(node.obj, scope) self.register_instruction(cil.AssignNode(vobj, scope.ret_expr)) self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) - instance = self.define_internal_local() - self.register_instruction(cil.DynamicCallNode(type_of_node, 'init', instance)) #self for Dynamic Dispatch - self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.ArgNode(vobj)) for arg in args: self.register_instruction(arg) From e1de646fc812e72a89046755de5e5ce096918585 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:27:19 -0400 Subject: [PATCH 239/520] [cil] - Update `register_built_in` method Updates: - Object::abort, use DataNode defined in visit of ProgramNode for abort message instead of creating a new DataNode with every call to abort - Object::type_name, use init of String to create a new String with the value of TYPENAME -IO::out_string, get attribute 'value' of the String passed as parameter instead of load that value from .DATA - IO::out_int, get attribute 'value' of the Int passed as parameter instead of print the param - IO::in_string, create a new String with init with the result of READ - IO::in_int, create a new Int with init with the result of READ - Create a `init` method for String and Int. - Remove `Bool` type from types registered in .TYPE --- src/core/cmp/cool_to_cil.py | 40 +++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 486eb995..f6acb155 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -79,7 +79,8 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('abort', 'Object')) vname = self.define_internal_local() - self.register_instruction(cil.LoadNode(vname, 'data_0')) + data_node = [d for d in self.dotdata if d.name == 'Program aborted'][0] + self.register_instruction(cil.LoadNode(vname, data_node)) self.register_instruction(cil.PrintNode(vname)) self.register_instruction(cil.ExitNode()) # No need for RETURN here right?? @@ -88,7 +89,10 @@ def register_built_in(self): self.register_param(self.vself) result = self.define_internal_local() self.register_instruction(cil.TypeNameNode(result, self.vself.name)) - self.register_instruction(cil.ReturnNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('copy', 'Object')) self.register_param(self.vself) @@ -105,30 +109,48 @@ def register_built_in(self): self.register_param(self.vself) self.register_param(VariableInfo('x', None)) vname = self.define_internal_local() - self.register_instruction(cil.LoadNode(vname, 'x')) + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value')) self.register_instruction(cil.PrintNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) self.current_function = self.register_function(self.to_function_name('out_int', 'IO')) self.register_param(self.vself) self.register_param(VariableInfo('x', None)) - self.register_instruction(cil.PrintNode('x')) + vname = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value')) + self.register_instruction(cil.PrintNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) self.current_function = self.register_function(self.to_function_name('in_string', 'IO')) self.register_param(self.vself) result = self.define_internal_local() self.register_instruction(cil.ReadNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) self.register_instruction(cil.ReturnNode(result)) self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) self.register_param(self.vself) result = self.define_internal_local() self.register_instruction(cil.ReadNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', result)) self.register_instruction(cil.ReturnNode(result)) #String type_node = self.register_type('String') + type_node.attributes = ['value'] + + self.current_function = self.register_function(self.to_function_name('init', 'String')) + self.register_param(VariableInfo('val', None)) + val = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(val, 'val', 'value')) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('String', instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', val)) + self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('length', 'String')) self.register_param(self.vself) @@ -153,8 +175,14 @@ def register_built_in(self): #Int type_node = self.register_type('Int') - #Bool - type_node = self.register_type('Bool') + type_node.attributes = ['value'] + + self.current_function = self.register_function(self.to_function_name('init', 'Int')) + self.register_param(VariableInfo('val', None)) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Int', instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val')) + self.register_instruction(cil.ReturnNode(instance)) class COOLToCILVisitor(BaseCOOLToCILVisitor): From 953b47ae33f4e8282ff1bde632b4347c9c6e0b1f Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:30:21 -0400 Subject: [PATCH 240/520] [cil] - Add VOID command and implement visit of `IsVoidNode` --- src/core/cmp/cil.py | 3 +++ src/core/cmp/cool_to_cil.py | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 0cf9e602..75c3d20f 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -189,6 +189,9 @@ def __init__(self, dest, obj): self.dest = dest self.obj = obj +class VoidNode(InstructionNode): + pass + def get_formatter(): class PrintVisitor(object): diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f6acb155..955116a7 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -268,7 +268,7 @@ def visit(self, node, scope): scope.ret_expr = self.default_values[node.type] except KeyError: #Void value - scope.ret_expr = None + scope.ret_expr = cil.VoidNode() self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr)) @visitor.when(cool.FuncDeclarationNode) @@ -291,6 +291,7 @@ def visit(self, node, scope): self.register_param(VariableInfo(param_name, None)) scope.ret_expr = None + #//TODO: scope children used here ??? self.visit(node.body, scope) # (Handle RETURN) if scope.ret_expr is None: @@ -356,7 +357,7 @@ def visit(self, node, scope): self.register_instruction(pool_label_node) #The result of a while loop is void - scope.ret_expr = None + scope.ret_expr = cil.VoidNode() @visitor.when(cool.BlockNode) def visit(self, node, scope): @@ -579,8 +580,13 @@ def visit(self, node, scope): ############################### # node.expr -> ExpressionNode ############################### - #//TODO: Implement IsVoidNode - pass + void = cil.VoidNode() + value = self.define_internal_local() + self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(value, scope.ret_expr)) + result = self.define_internal_local() + self.register_instruction(cil.EqualNode(result, value, void)) + scope.ret_expr = result @visitor.when(cool.ComplementNode) def visit(self, node, scope): @@ -589,7 +595,8 @@ def visit(self, node, scope): ############################### vname = self.define_internal_local() self.visit(node.expr, scope) - self.register_instruction(cil.ComplementNode(vname, scope.ret_expr)) + self.register_instruction(cil.GetAttribNode(value, scope.ret_expr, 'value')) + self.register_instruction(cil.ComplementNode(vname, value)) scope.ret_expr = vname @visitor.when(cool.FunctionCallNode) From a38cf2d21e4c183aeecc8db1aa888facbb9f95b2 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:33:00 -0400 Subject: [PATCH 241/520] [cil] - Update ArithmeticNodes with the new form of using and creating Int Also update ComplementNode --- src/core/cmp/cool_to_cil.py | 48 +++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 955116a7..a7b85059 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -526,12 +526,17 @@ def visit(self, node, scope): # node.right -> ExpressionNode ############################### vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() self.visit(node.left, scope) - left = scope.ret_expr + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) self.visit(node.right, scope) - right = scope.ret_expr - self.register_instruction(cil.PlusNode(vname, left, right)) - scope.ret_expr = vname + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + self.register_instruction(cil.PlusNode(vname, vleft, vright)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + scope.ret_expr = instance @visitor.when(cool.MinusNode) def visit(self, node, scope): @@ -540,11 +545,16 @@ def visit(self, node, scope): # node.right -> ExpressionNode ############################### vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() self.visit(node.left, scope) - left = scope.ret_expr + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) self.visit(node.right, scope) - right = scope.ret_expr - self.register_instruction(cil.MinusNode(vname, left, right)) + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + self.register_instruction(cil.MinusNode(vname, vleft, vright)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) scope.ret_expr = vname @visitor.when(cool.StarNode) @@ -554,11 +564,16 @@ def visit(self, node, scope): # node.right -> ExpressionNode ############################### vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() self.visit(node.left, scope) - left = scope.ret_expr + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) self.visit(node.right, scope) - right = scope.ret_expr - self.register_instruction(cil.StarNode(vname, left, right)) + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + self.register_instruction(cil.StarNode(vname, vleft, vright)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) scope.ret_expr = vname @visitor.when(cool.DivNode) @@ -568,11 +583,17 @@ def visit(self, node, scope): # node.right -> ExpressionNode ############################### vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() self.visit(node.left, scope) - left = scope.ret_expr + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) self.visit(node.right, scope) - right = scope.ret_expr - self.register_instruction(cil.DivNode(vname, left, right)) + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + #//TODO: Check division by 0 runtime error??? + self.register_instruction(cil.DivNode(vname, vleft, vright)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) scope.ret_expr = vname @visitor.when(cool.IsVoidNode) @@ -594,6 +615,7 @@ def visit(self, node, scope): # node.expr -> ExpressionNode ############################### vname = self.define_internal_local() + value = self.define_internal_local() self.visit(node.expr, scope) self.register_instruction(cil.GetAttribNode(value, scope.ret_expr, 'value')) self.register_instruction(cil.ComplementNode(vname, value)) From 5aa1d120e266a57a12ba4889282711e02a87a3ce Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:35:05 -0400 Subject: [PATCH 242/520] [cil] - Add NAME command for easier code translation of `CaseOfNode` --- src/core/cmp/cil.py | 5 +++++ src/core/cmp/cool_to_cil.py | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 75c3d20f..6fffc143 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -148,6 +148,11 @@ def __init__(self, dest, source): self.dest = dest self.source = source +class NameNode(InstructionNode): + def __init__(self, dest, name): + self.dest = dest + self.name = name + class CopyNode(InstructionNode): def __init__(self, dest, source): self.dest = dest diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index a7b85059..5ec105ae 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -409,9 +409,8 @@ def visit(self, node, scope): for s in old: h -= s for t in h: - data_node = self.register_data(t) vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) - self.register_instruction(cil.LoadNode(vbranch_type_name, data_node.name)) + self.register_instruction(cil.NameNode(vbranch_type_name, t)) self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) From 15a1bad0b24f6987dae4ab2bfc87bd420b095ccf Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:35:42 -0400 Subject: [PATCH 243/520] [cil] - Update `Bool` type default value to 0 instead of 'False' --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 5ec105ae..67c7d614 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -13,7 +13,7 @@ def __init__(self, context): self.current_function = None self.context = context self.vself = VariableInfo('self', None) - self.default_values = {'Int': 0, 'String': '', 'Bool': False} + self.default_values = {'Int': 0, 'String': '', 'Bool': 0} @property def params(self): From 639db6891769907d344a507afb0fc3dc491e2fee Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:39:01 -0400 Subject: [PATCH 244/520] [cil] - Update visit of `LetAttributeNode` Check if the variable is initialized, and if not, give it a default value given the declared type --- src/core/cmp/cool_to_cil.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 67c7d614..ba3ff339 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -375,8 +375,8 @@ def visit(self, node, scope): ############################################ vret = self.register_local(VariableInfo('let_in_value', None)) - for let_att_node in node.let_body: - self.visit(let_att_node, scope) + for let_attr_node in node.let_body: + self.visit(let_attr_node, scope) self.visit(node.in_body, scope) self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) scope.ret_expr = vret @@ -442,11 +442,16 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - #//TODO: See if node.type is string prior to add it to .DATA ??? vname = self.register_local(VariableInfo(node.id, node.type)) - self.visit(node.expr, scope) + if node.expr: + self.visit(node.expr, scope) + else: + try: + scope.ret_expr = self.default_values[node.type] + except KeyError: + #Void value + scope.ret_expr = cil.VoidNode() self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) - scope.ret_expr = None @visitor.when(cool.AssignNode) def visit(self, node, scope): From 5cba3921a59914cb91a3ebe4437ef6f2a1879949 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:40:15 -0400 Subject: [PATCH 245/520] [cil] - Update visit of `IntegerNode` and `BoolNode` Create a proper Int object with init --- src/core/cmp/cool_to_cil.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index ba3ff339..20f69047 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -708,7 +708,10 @@ def visit(self, node, scope): ############################### # node.lex -> str ############################### - scope.ret_expr = node.lex + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(int(node.lex))) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + scope.ret_expr = instance @visitor.when(cool.IdNode) def visit(self, node, scope): @@ -752,3 +755,7 @@ def visit(self, node, scope): scope.ret_expr = 1 else: scope.ret_expr = 0 + instance = self.define_internal_local() + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', scope.ret_expr)) + scope.ret_expr = instance From be539dc36a587416fecfb26a8ffc380106b242da Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 20 Aug 2020 10:48:03 -0400 Subject: [PATCH 246/520] [cil] - Fix `Object::abort` implementation Give to LOAD the label 'data_0' instead the `DataNode` --- src/core/cmp/cool_to_cil.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 20f69047..d931fbfc 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -79,8 +79,7 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('abort', 'Object')) vname = self.define_internal_local() - data_node = [d for d in self.dotdata if d.name == 'Program aborted'][0] - self.register_instruction(cil.LoadNode(vname, data_node)) + self.register_instruction(cil.LoadNode(vname, 'data_0')) self.register_instruction(cil.PrintNode(vname)) self.register_instruction(cil.ExitNode()) # No need for RETURN here right?? @@ -209,9 +208,9 @@ def visit(self, node, scope): self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) - self.register_built_in() # Error message raised by Object:abort() self.register_data('Program aborted') + self.register_built_in() self.current_function = None for declaration, child_scope in zip(node.declarations, scope.children): From 304a2b2938272b356910c94b23eadf122c262b97 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 23 Aug 2020 14:10:49 -0400 Subject: [PATCH 247/520] [cil] - Update `register_built_in` method Updates: - Add init method to `Object` and `IO` types - Set TypeNode `methods` attribute for `IO`, `String`, and `Int` types --- src/core/cmp/cool_to_cil.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index d931fbfc..02d9d5a1 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -77,6 +77,11 @@ def register_built_in(self): #Object type_node = self.register_type('Object') + self.current_function = self.register_function(self.to_function_name('init', 'Object')) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Object', instance)) + self.register_instruction(cil.ReturnNode(instance)) + self.current_function = self.register_function(self.to_function_name('abort', 'Object')) vname = self.define_internal_local() self.register_instruction(cil.LoadNode(vname, 'data_0')) @@ -99,11 +104,16 @@ def register_built_in(self): self.register_instruction(cil.CopyNode(result, self.vself.name)) self.register_instruction(cil.ReturnNode(result)) - type_node.methods = [(name, self.to_function_name(name, 'Object')) for name in ['abort', 'type_name', 'copy']] + type_node.methods = [(name, self.to_function_name(name, 'Object')) for name in ['init', 'abort', 'type_name', 'copy']] #IO type_node = self.register_type('IO') + self.current_function = self.register_function(self.to_function_name('init', 'Object')) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Object', instance)) + self.register_instruction(cil.ReturnNode(instance)) + self.current_function = self.register_function(self.to_function_name('out_string', 'IO')) self.register_param(self.vself) self.register_param(VariableInfo('x', None)) @@ -138,6 +148,8 @@ def register_built_in(self): self.register_instruction(cil.SetAttribNode(instance, 'value', result)) self.register_instruction(cil.ReturnNode(result)) + type_node.methods = [(name, self.to_function_name(name, 'IO')) for name in ['init', 'out_string', 'out_int', 'in_string', 'in_int']] + #String type_node = self.register_type('String') type_node.attributes = ['value'] @@ -172,6 +184,8 @@ def register_built_in(self): self.register_instruction(cil.SubstringNode(result, self.vself.name, 'i', 'l')) self.register_instruction(cil.ReturnNode(result)) + type_node.methods = [(name, self.to_function_name(name, 'String')) for name in ['init', 'length', 'concat', 'substr']] + #Int type_node = self.register_type('Int') type_node.attributes = ['value'] @@ -183,6 +197,8 @@ def register_built_in(self): self.register_instruction(cil.SetAttribNode(instance, 'value', 'val')) self.register_instruction(cil.ReturnNode(instance)) + type_node.methods = [('init', self.to_function_name('init', 'Int'))] + class COOLToCILVisitor(BaseCOOLToCILVisitor): def __init__(self, context): From b6a2d48e64659b94db8883f5ab920e7aa50b6b22 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 24 Aug 2020 14:41:37 -0400 Subject: [PATCH 248/520] [cil] - Remove `PrintNode` and add `PrintStrNode` and `PrintIntNode` Update `out_string` and `out_int` methods of `IO` --- src/core/cmp/cil.py | 10 +++++++--- src/core/cmp/cool_to_cil.py | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 6fffc143..57b9298d 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -185,9 +185,13 @@ class ReadNode(InstructionNode): def __init__(self, dest): self.dest = dest -class PrintNode(InstructionNode): - def __init__(self, str_addr): - self.str_addr = str_addr +class PrintStrNode(InstructionNode): + def __init__(self, value): + self.value = value + +class PrintIntNode(InstructionNode): + def __init__(self, value): + self.value = value class ComplementNode(InstructionNode): def __init__(self, dest, obj): diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 02d9d5a1..5da614d8 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -119,7 +119,7 @@ def register_built_in(self): self.register_param(VariableInfo('x', None)) vname = self.define_internal_local() self.register_instruction(cil.GetAttribNode(vname, 'x', 'value')) - self.register_instruction(cil.PrintNode(vname)) + self.register_instruction(cil.PrintStrNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) self.current_function = self.register_function(self.to_function_name('out_int', 'IO')) @@ -127,7 +127,7 @@ def register_built_in(self): self.register_param(VariableInfo('x', None)) vname = self.define_internal_local() self.register_instruction(cil.GetAttribNode(vname, 'x', 'value')) - self.register_instruction(cil.PrintNode(vname)) + self.register_instruction(cil.PrintIntNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) self.current_function = self.register_function(self.to_function_name('in_string', 'IO')) From 1683eb13128c1b648c3f393292460ef681dbf767 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 24 Aug 2020 16:29:25 -0400 Subject: [PATCH 249/520] [cil] - Add attribute `type` to `GetAttribNode` and `SetAttribNode` This is to facilitate the generation code process --- src/core/cmp/cil.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 57b9298d..da77d706 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -73,16 +73,18 @@ class EqualNode(ArithmeticNode): pass class GetAttribNode(InstructionNode): - def __init__(self, dest, xtype, attr): + def __init__(self, dest, obj, attr, xtype): self.dest = dest - self.xtype = xtype + self.obj = obj self.attr = attr + self.xtype = xtype class SetAttribNode(InstructionNode): - def __init__(self, xtype, attr, value): - self.xtype = xtype + def __init__(self, obj, attr, value, xtype): + self.obj = obj self.attr = attr self.value = value + self.xtype = xtype class GetIndexNode(InstructionNode): pass From 4b1bfe2a443397af176ea9fae405d65edbe31a5e Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 24 Aug 2020 16:31:09 -0400 Subject: [PATCH 250/520] [cil] - Update calls to `GetAttribNode` and `SetAttribNode` Add new argument --- src/core/cmp/cool_to_cil.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 5da614d8..ff7313ad 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -118,7 +118,7 @@ def register_built_in(self): self.register_param(self.vself) self.register_param(VariableInfo('x', None)) vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, 'x', 'value')) + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'String')) self.register_instruction(cil.PrintStrNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) @@ -126,7 +126,7 @@ def register_built_in(self): self.register_param(self.vself) self.register_param(VariableInfo('x', None)) vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, 'x', 'value')) + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'Int')) self.register_instruction(cil.PrintIntNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) @@ -157,10 +157,10 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('init', 'String')) self.register_param(VariableInfo('val', None)) val = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(val, 'val', 'value')) + self.register_instruction(cil.GetAttribNode(val, 'val', 'value', 'String')) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('String', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', val)) + self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'String')) self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('length', 'String')) @@ -194,7 +194,7 @@ def register_built_in(self): self.register_param(VariableInfo('val', None)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('Int', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', 'val')) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Int')) self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [('init', self.to_function_name('init', 'Int'))] @@ -284,7 +284,7 @@ def visit(self, node, scope): except KeyError: #Void value scope.ret_expr = cil.VoidNode() - self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr)) + self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, node.type)) @visitor.when(cool.FuncDeclarationNode) def visit(self, node, scope): @@ -479,7 +479,7 @@ def visit(self, node, scope): try: self.current_type.get_attribute(node.id) - self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr)) + self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr, self.current_type.name)) scope.ret_expr = node.id except SemanticError: vname = self.register_local(VariableInfo(node.id, None)) @@ -548,9 +548,9 @@ def visit(self, node, scope): vleft = self.define_internal_local() vright = self.define_internal_local() self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value', 'Int')) self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value', 'Int')) self.register_instruction(cil.PlusNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) @@ -567,9 +567,9 @@ def visit(self, node, scope): vleft = self.define_internal_local() vright = self.define_internal_local() self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value', 'Int')) self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value', 'Int')) self.register_instruction(cil.MinusNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) @@ -586,9 +586,9 @@ def visit(self, node, scope): vleft = self.define_internal_local() vright = self.define_internal_local() self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value', 'Int')) self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value', 'Int')) self.register_instruction(cil.StarNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) @@ -605,9 +605,9 @@ def visit(self, node, scope): vleft = self.define_internal_local() vright = self.define_internal_local() self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value', 'Int')) self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value', 'Int')) #//TODO: Check division by 0 runtime error??? self.register_instruction(cil.DivNode(vname, vleft, vright)) instance = self.define_internal_local() @@ -636,7 +636,7 @@ def visit(self, node, scope): vname = self.define_internal_local() value = self.define_internal_local() self.visit(node.expr, scope) - self.register_instruction(cil.GetAttribNode(value, scope.ret_expr, 'value')) + self.register_instruction(cil.GetAttribNode(value, scope.ret_expr, 'value', 'Int')) self.register_instruction(cil.ComplementNode(vname, value)) scope.ret_expr = vname @@ -736,7 +736,7 @@ def visit(self, node, scope): try: self.current_type.get_attribute(node.lex) attr = self.register_local(VariableInfo('attr_value', None)) - self.register_instruction(cil.GetAttribNode(attr, self.vself, node.lex)) + self.register_instruction(cil.GetAttribNode(attr, self.vself, node.lex, scope.find_variable(node.lex).type)) scope.ret_expr = attr except SemanticError: param_names = [pn.name for pn in self.current_function.params] @@ -772,5 +772,5 @@ def visit(self, node, scope): scope.ret_expr = 0 instance = self.define_internal_local() self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', scope.ret_expr)) + self.register_instruction(cil.SetAttribNode(instance, 'value', scope.ret_expr, 'Int')) scope.ret_expr = instance From f504e0444f7a50eb3b76c655a9d4e68edbdb728f Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 24 Aug 2020 16:32:34 -0400 Subject: [PATCH 251/520] [cil] - Fix `in_string` and `in_int` implementations --- src/core/cmp/cool_to_cil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index ff7313ad..2b36fb66 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -137,16 +137,16 @@ def register_built_in(self): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) - self.register_instruction(cil.ReturnNode(result)) + self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) self.register_param(self.vself) result = self.define_internal_local() self.register_instruction(cil.ReadNode(result)) instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', result)) - self.register_instruction(cil.ReturnNode(result)) + self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(name, self.to_function_name(name, 'IO')) for name in ['init', 'out_string', 'out_int', 'in_string', 'in_int']] From 4da2f58f930155715649ba4d9b8138a7f15036bc Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 24 Aug 2020 16:43:59 -0400 Subject: [PATCH 252/520] [cil] - Fix some mistypes --- src/core/cmp/cool_to_cil.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 2b36fb66..0bcc2eb5 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -85,7 +85,7 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('abort', 'Object')) vname = self.define_internal_local() self.register_instruction(cil.LoadNode(vname, 'data_0')) - self.register_instruction(cil.PrintNode(vname)) + self.register_instruction(cil.PrintStrNode(vname)) self.register_instruction(cil.ExitNode()) # No need for RETURN here right?? @@ -736,7 +736,7 @@ def visit(self, node, scope): try: self.current_type.get_attribute(node.lex) attr = self.register_local(VariableInfo('attr_value', None)) - self.register_instruction(cil.GetAttribNode(attr, self.vself, node.lex, scope.find_variable(node.lex).type)) + self.register_instruction(cil.GetAttribNode(attr, self.vself, node.lex, self.current_type.name)) scope.ret_expr = attr except SemanticError: param_names = [pn.name for pn in self.current_function.params] From 08c83e68b40892b1025ec4730ecf1eb98dc6ddc9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 27 Aug 2020 19:32:50 -0400 Subject: [PATCH 253/520] [cil] - Update `init` method of `String` type --- src/core/cmp/cool_to_cil.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 0bcc2eb5..136d00cf 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -156,11 +156,9 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('init', 'String')) self.register_param(VariableInfo('val', None)) - val = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(val, 'val', 'value', 'String')) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('String', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'String')) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'String')) self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('length', 'String')) From 1bdd9be643aa6310be2aab8ddb4ce3c3b44b7d74 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 27 Aug 2020 19:35:35 -0400 Subject: [PATCH 254/520] [cil] - Update visit of `StringNode` Updates: - Check if a `DataNode` with value `node.lex` is already in .DATA to avoid duplicated `DataNode` - Create a `cil.String` with the value of the `LOAD` instruction and return it --- src/core/cmp/cool_to_cil.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 136d00cf..772646c3 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -754,10 +754,16 @@ def visit(self, node, scope): ############################### # node.lex -> str ############################### - data_node = self.register_data(node.lex) + try: + data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] + except IndexError: + data_node = self.register_data(node.lex) vname = self.register_local(VariableInfo('msg', None)) - self.register_instruction(cil.LoadNode(vname, data_node.name)) - scope.ret_expr = vname + self.register_instruction(cil.LoadNode(vname, data_node)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + scope.ret_expr = instance @visitor.when(cool.BoolNode) def visit(self, node, scope): From 137ff7e1e0bd79913f206afef49b8635e65ec3f5 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 15:26:59 -0400 Subject: [PATCH 255/520] [mips] Rewrite ProgramNode class --- src/core/cmp/mips.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index dacf763a..378267b8 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -21,10 +21,22 @@ class Node: pass class ProgramNode(Node): - def __init__(self, dottext, dotdata, types): - self._dottext = dottext - self._dotdata = dotdata + def __init__(self, data, types, functions): + self._data = data + self._types = types + self._functions = functions + @property + def data(self): + return self._data + + @property + def types(self): + return self._types + + @property + def functions(self): + return self._functions class FunctionNode(Node): def __init__(self, name, instructions): From bd2791bab3c7f049ebe10043b1af1f8b26eb2897 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 15:39:32 -0400 Subject: [PATCH 256/520] [mips] Add Classes MemoryLocation, RegisterRelativeLocation and LabelRelativeLocation --- src/core/cmp/mips.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 378267b8..832c4107 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -201,3 +201,32 @@ def allocate_memory(addr, size): + +class MemoryLocation: + pass + +class RegisterRelativeLocation(MemoryLocation): + def __init__(self, register, offset): + self._register = register + self._offset = offset + + @property + def register(self): + return self._register + + @property + def offset(self): + return self._offset + +class LabelRelativeLocation(MemoryLocation): + def __init__(self, label, offset): + self._label = label + self._offset = offset + + @property + def label(self): + return self._label + + @property + def offset(self): + return self._offset From 372acf111c71ead95b342ca613ee916f1d4435cf Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 15:42:43 -0400 Subject: [PATCH 257/520] [mips] Add constanst for special registers and edit old ones --- src/core/cmp/mips.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 832c4107..7ca32c15 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -3,10 +3,10 @@ ATTR_SIZE = 4 RESGISTER_SIZE = 4 -REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9', 'v0', 'v1', 'a0', 'sp', 'ra'] -STRING_TYPE = "string_type" +REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9'] +ARG_REGISTERS_NAMES = ['a0', 'a1', 'a2', 'a3'] + -# TYPE_NAME_LABEL = f"type_name_{}" TYPE_METADATA_SIZE = 4 @@ -16,6 +16,12 @@ def __init__(self, name): self.name = name REGISTERS = { name: Register(name) for name in REGISTER_NAMES } +ARG_REGISTERS = [ Register(name) for name in ARG_REGISTERS_NAMES ] +FP_REG = Register('fp') +SP_REG = Register('sp') +RA_REG = Register('ra') +V0_REG = Register('v0') +V1_REG = Register('v1') class Node: pass From c85e9cefeb92078eaf5ff04153fc8bb7b18714e4 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 15:46:22 -0400 Subject: [PATCH 258/520] [mips] Rewrite FunctionNode class --- src/core/cmp/mips.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 7ca32c15..e0449e33 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -45,9 +45,32 @@ def functions(self): return self._functions class FunctionNode(Node): - def __init__(self, name, instructions): - self.name = name - self.instructions = instructions + def __init__(self, label, params, localvars, instructions = []): + self._label = label + self._instructions = instructions + self._params = params + self._localvars = localvars + + def add_instructions(self, instructions): + self._instructions.extend(instructions) + + def get_param_stack_location(self, name): + #TODO Tener en cuenta que los primeros argumentos se guardan en los registros para argumentos + index = self._params.index(name) + offset = ((len(self._params) -1 ) - index) * ATTR_SIZE + return RegisterRelativeLocation(FP_REG, offset) + + def get_local_stack_location(self, name): + index = self._localvars.index(name) + offset = (index + 2) * -ATTR_SIZE + return RegisterRelativeLocation(FP_REG, offset) + + def get_var_location(self, name): + try: + return self.get_param_stack_location(name) + except ValueError: + return self.get_local_stack_location(name) + class DataNode(Node): def __init__(self, label): From 7fe129cf34eecdf1669682cfb4eb4e7d346fcdb2 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:12:04 -0400 Subject: [PATCH 259/520] [mips] Remove TypeDesc class --- src/core/cmp/mips.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index e0449e33..013ffe3e 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -85,23 +85,6 @@ def __init__(self, label, string): super().__init__(label) self._string = string - def __repr__(self): - return f'STRING_CONST {self._label} {STRING_TYPE} {self._string}' - -class TypeDesc(DataNode): - def __init__(self, label, name, size, methods): - super().__init__(label) - self._size = size - self._name = name - self._methods = methods - - def __repr__(self): - print(self._name) - print(self._size) - return f'TYPE_DESC {self._name} {self._size} {self._label} {self._methods}' - - - class InstructionNode(Node): pass From 84cee8f7163227f12d3db3f41fc45c219fc88c58 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:13:44 -0400 Subject: [PATCH 260/520] [mips] Add JumpRegisterAndLinkNode class --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 013ffe3e..65daf807 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -125,6 +125,10 @@ class JumpAndLinkNode(InstructionNode): def __init__(self, label): self.label = label +class JumpRegisterAndLinkNode(InstructionNode): + def __init__(self, reg): + self.reg = reg + class JumpRegister(InstructionNode): def __init__(self, reg): self.reg = reg From faf26a8009ebaf672208d212d8cd1b6036181ab1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:19:53 -0400 Subject: [PATCH 261/520] [mips] Rewrite MIPSType class --- src/core/cmp/mips.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 65daf807..654233e2 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -7,7 +7,7 @@ ARG_REGISTERS_NAMES = ['a0', 'a1', 'a2', 'a3'] -TYPE_METADATA_SIZE = 4 +INSTANCE_METADATA_SIZE = 4 @@ -140,33 +140,26 @@ def __init__(self, dest, src, value): self.value = value - - - -class MIPSType(): - def __init__(self, label, name, size, methods): +class MIPSType: + def __init__(self, label, name_addr, attributes, methods): self._label = label - self._name = name - self._size = size + self._name = name_addr + self._attributes = attributes self._methods = methods @property def size(self): - return len(self.attributes) * ATTR_SIZE + return (len(self.attributes) * ATTR_SIZE) + INSTANCE_METADATA_SIZE @property - def data_label(self): - return self.data_label - - def set_data_label(self, data_label): - self.data_label = string - - def get_attr_offset(self, attr_name): - return ATTR_SIZE * self.attributes.index(attributes) + def label(self): + return self._label - def get_func(self, method_name): - return self.methods[method_name] + @property + def string_name_label(self): + return self._name + class Label(): def __init__(self, name): From 4b9ba301a37c721b4f1cf5a3dadb3eb8f2de23df Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:22:01 -0400 Subject: [PATCH 262/520] [mips] Remove Label and Address class --- src/core/cmp/mips.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 654233e2..b21ddac8 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -161,22 +161,7 @@ def string_name_label(self): return self._name -class Label(): - def __init__(self, name): - self._name = name - - def __repr__(self): - return self._name - - - - - -class Address(): - def __init__(self, reg, offset): - self.offset = offset - self.reg = reg def save_register(reg): move_stack = AddInmediateNode(REGISTERS['sp'], REGISTERS['sp'], -RESGISTER_SIZE) From 2c457dbdefb1a59e981b74f304d61bbb4034e420 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:25:47 -0400 Subject: [PATCH 263/520] [mips] Edit mips snippets --- src/core/cmp/mips.py | 59 +++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index b21ddac8..3fad4ca9 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -163,39 +163,6 @@ def string_name_label(self): -def save_register(reg): - move_stack = AddInmediateNode(REGISTERS['sp'], REGISTERS['sp'], -RESGISTER_SIZE) - addr = Address(REGISTERS['sp'], 0) - save_value = StoreWordNode(reg, addr) - return [move_stack, save_value] - -def save_registers(registers): - instructions = [] - instructions.extend(chain.from_iterable([save_register(reg) for reg in registers])) - return instructions - -def load_reg_from_stack(reg): - addr = Address(REGISTERS['sp'], 0) - load_value = LoadWordNode(reg, addr) - move_stack = AddInmediateNode(REGISTERS['sp'], REGISTERS['sp'], RESGISTER_SIZE) - return [load_value, move_stack] - -def load_registers_from_stack(registers): - instructions = [] - instructions.extend(chain.from_iterable([load_reg_from_stack(reg) for reg in registers])) - return instructions - -def allocate_memory(addr, size): - set_operation_number = LoadInmediateNode(REGISTERS['v0'], 9) - set_size = LoadInmediateNode(REGISTERS['a0'], size) - syscall = SyscallNode() - save_pointer = StoreWordNode(REGISTERS['v0'], addr) - return [set_operation_number, set_size, syscall, save_pointer] - - - - - class MemoryLocation: pass @@ -224,3 +191,29 @@ def label(self): @property def offset(self): return self._offset + +##Snippets + +def push_register(reg): + move_stack = AddInmediateNode(SP_REG, SP_REG, -RESGISTER_SIZE) + save_location = RegisterRelativeLocation(SP_REG, 0) + save_register = StoreWordNode(reg, save_location) + return [move_stack, save_register] + +def pop_register(reg): + load_value = LoadWordNode(reg, RegisterRelativeLocation(SP_REG, 0)) + move_stack = AddInmediateNode(SP_REG, SP_REG, RESGISTER_SIZE) + return [load_value, move_stack] + +def alloc_memory(size): + instructions = [] + instructions.append(LoadInmediateNode(V0_REG, 9)) + instructions.append(LoadInmediateNode(ARG_REGISTERS[0], size)) + instructions.append(SyscallNode()) + return instructions + +def exit_program(): + instructions = [] + instructions.append(LoadInmediateNode(V0_REG, 10)) + instructions.append(SyscallNode()) + return instructions From f27c7ced21266b6e291e4007aa0f786b36ad6737 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:28:17 -0400 Subject: [PATCH 264/520] [mips] Create PrintVisitor to generate mips code from mips AST --- src/core/cmp/mips.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 3fad4ca9..37f0aa7e 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -217,3 +217,12 @@ def exit_program(): instructions.append(LoadInmediateNode(V0_REG, 10)) instructions.append(SyscallNode()) return instructions + + +class PrintVisitor: + + @visitor.on('node') + def print(self, node): + pass + + From 8dc3a6a33b165c424c5134fc5bb05f5fa27bccc7 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:30:00 -0400 Subject: [PATCH 265/520] [mips] Add visitor cases for Register, int, str and ProgramNode classes --- src/core/cmp/mips.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 37f0aa7e..55fc5ad1 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -225,4 +225,24 @@ class PrintVisitor: def print(self, node): pass - + @visitor.when(Register) + def visit(self, node): + return f'${node.name}' + + @visitor.when(int) + def visit(self, node): + return str(node) + + @visitor.when(str) + def visit(self, node): + return node + + @visitor.when(ProgramNode) + def visit(self, node): + data_section_header = "\t.data" + static_strings = '\n'.join([self.visit(string_const) for string_const in node.static_data]) + + types = "\n".join([self.visit(tp) for tp in node.types]) + + code = "\n".join([self.visit(func) for func in node.functions]) + return f'{data_section_header}\n{static_strings}\n{types}\n\t.text\n\t.globl main\n{code}' From d35b192d0a009b77d55d5bfed18c14324f3b4c94 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:31:09 -0400 Subject: [PATCH 266/520] [mips] Add visitor cases for StringCons, MIPSType, SyscallNode and LabelRelativeLocation classes --- src/core/cmp/mips.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 55fc5ad1..63f1f82b 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -246,3 +246,20 @@ def visit(self, node): code = "\n".join([self.visit(func) for func in node.functions]) return f'{data_section_header}\n{static_strings}\n{types}\n\t.text\n\t.globl main\n{code}' + + @visitor.when(StringConst) + def visit(self, node): + return f'{node.label}: .asciiz "{node.string}"' + + @visitor.when(MIPSType) + def visit(self, node): + methods = " ".join(node.methods) + return f'{node.label}:\n\t.word {node.string_name_label} {node.size} {methods}' + + @visitor.when(SyscallNode) + def visit(self, node): + return 'syscall' + + @visitor.when(LabelRelativeLocation) + def visit(self, node): + return f'{node.label} + {node.offset}' From 594578f43777d5e8ba475872555d62d0cbb8ae65 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:33:40 -0400 Subject: [PATCH 267/520] [mips] Add visitor cases for RegisterRelativeLocation and FunctionNode classes --- src/core/cmp/mips.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 63f1f82b..9649e41c 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -263,3 +263,15 @@ def visit(self, node): @visitor.when(LabelRelativeLocation) def visit(self, node): return f'{node.label} + {node.offset}' + + @visitor.when(RegisterRelativeLocation) + def visit(self, node): + return f'{node.offset}({self.visit(node.register)})' + + @visitor.when(FunctionNode) + def visit(self, node): + instr = [self.visit(instruction) for instruction in node.instructions] + #TODO la linea de abajo sobra, es necesaria mientras la traduccion del AST de CIL este incompleta + instr2 = [inst for inst in instr if type(inst) == str] + instructions = "\n\t".join(instr2) + return f'{node.label}:\n\t{instructions}' From 930556511a8b84d9031f8bd96b864082b967e2f6 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:34:57 -0400 Subject: [PATCH 268/520] [mips] Add visitor cases for AddInmediateNode, StoreWordNode, LoadInmediateNode and JumpAndLinkNode classes --- src/core/cmp/mips.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 9649e41c..6d02ab02 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -275,3 +275,19 @@ def visit(self, node): instr2 = [inst for inst in instr if type(inst) == str] instructions = "\n\t".join(instr2) return f'{node.label}:\n\t{instructions}' + + @visitor.when(AddInmediateNode) + def visit(self, node): + return f'addi {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.value)}' + + @visitor.when(StoreWordNode) + def visit(self, node): + return f'sw {self.visit(node.reg)}, {self.visit(node.addr)}' + + @visitor.when(LoadInmediateNode) + def visit(self, node): + return f'li {self.visit(node.reg)}, {self.visit(node.value)}' + + @visitor.when(JumpAndLinkNode) + def visit(self, node): + return f'jal {node.label}' \ No newline at end of file From 26d898b8197eb9b4f14c720b5f81f356437ff8bb Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 28 Aug 2020 18:36:06 -0400 Subject: [PATCH 269/520] [mips] Add visitor cases for JumpRegister, LoadWordNode, LoadAddressNode and MoveNode classes --- src/core/cmp/mips.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 6d02ab02..c18487e3 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -290,4 +290,20 @@ def visit(self, node): @visitor.when(JumpAndLinkNode) def visit(self, node): - return f'jal {node.label}' \ No newline at end of file + return f'jal {node.label}' + + @visitor.when(JumpRegister) + def visit(self, node): + return f'jr {self.visit(node.reg)}' + + @visitor.when(LoadWordNode) + def visit(self, node): + return f'lw {self.visit(node.reg)}, {self.visit(node.addr)}' + + @visitor.when(LoadAddressNode) + def visit(self, node): + return f'la {self.visit(node.reg)}, {self.visit(node.label)}' + + @visitor.when(MoveNode) + def visit(self, node): + return f'move {self.visit(node.reg1)} {self.visit(node.reg2 )}' \ No newline at end of file From 55dd8c3f9a071f8a718c0810fed01592c308add8 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 09:42:31 -0400 Subject: [PATCH 270/520] [mips] Add SimpleRegistersManager class --- src/core/cmp/cil_to_mips.py | 64 ++++++++++++------------------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 35aa1b0e..0b6df986 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -4,54 +4,32 @@ from core.cmp.utils import CountDict +USED = 1 +UNUSED = 0 -class RegistersManager: - def __init__(self): - pass - - def getReg(self, instruction): - pass - -class StoreLocation: - pass -class Register(StoreLocation): - def __init__(self, name): - self._name = name - self._registerDescriptor = RegisterDescriptor() +class SimpleRegistersManager: + def __init__(self, registers): + self.registers = {reg: UNUSED for reg in registers} + + def get_free_reg(self): + for reg, state in self.registers.items(): + if state == UNUSED: + self.registers[reg] = USED + return reg + raise Exception("not free register") + - @property - def name(self): - return self._name + def free_reg(self, reg): + self.registers[reg] = UNUSED - @property - def get_register_descriptor(self): - return self._registerDescriptor + def is_used(self, reg): + return self.registers[reg] == USED -class RegisterDescriptor: - def __init__(self): - #ListVariables - pass - -class MemoryLocation(StoreLocation): - pass - -class HeapLocation(MemoryLocation): - pass - -class StackLocation(MemoryLocation): - pass - - -class AddressDescriptor(): - def __init__(self): - #List locations - pass - -class SymbolTable: - def __init__(self): - #Dict name: AddressDescriptor - pass + def get_registers_for_save(self): + regs = [reg for reg, state in self.registers.items() if state == USED] + regs.extend([mips.RA_REG]) + return regs class BaseCILToMIPSVisitor: From 130c08e7e4cb84979df20090a1d8c26e76b9e15f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 09:43:21 -0400 Subject: [PATCH 271/520] [mips] Add LabelGenerator class --- src/core/cmp/cil_to_mips.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 0b6df986..2fc1c6f7 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -32,6 +32,25 @@ def get_registers_for_save(self): return regs +class LabelGenerator: + def __init__(self): + self.data_count = 0 + self.type_count = 0 + self.code_count = 0 + + def generate_type_label(self): + self.type_count += 1 + return f'type_{self.type_count}' + + def generate_data_label(self): + self.data_count += 1 + return f'data_{self.data_count}' + + def generate_code_label(self): + self.code_count += 1 + return f'L_{self.code_count}' + + class BaseCILToMIPSVisitor: def __init__(self): self.types = {} From 51a95cd54af5340ee34751e0547cbbd90e59e517 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 09:47:34 -0400 Subject: [PATCH 272/520] [mips] Remove CILTOMipsVisitor class --- src/core/cmp/cil_to_mips.py | 135 ------------------------------------ 1 file changed, 135 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 2fc1c6f7..c8d1ab85 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -51,143 +51,8 @@ def generate_code_label(self): return f'L_{self.code_count}' -class BaseCILToMIPSVisitor: - def __init__(self): - self.types = {} - self.dottext = [] - self.dotdata = [] - self.actual_function = None - self.actual_function_instructions = [] - self.types_dict = CountDict() - self.funcs_dict = CountDict() - self.str_consts = CountDict() - self.int_consts = CountDict() - self.symbol_table = SymbolTable() - - - @property - def localvars(self): - return self.actual_function.localvars - - @property - def params(self): - return self.actual_function.parmas - - @property - def cil_instructions(self): - return self.actual_function.instructions - - def variable_index(self, var_name): - for i, var in enumerate(self.localvars): - if var.name == var_name: - return i - return -1 - - def add_instructions(self, instructions): - self.actual_function_instructions.extend(instructions) - - def get_str_const(self, string): - try: - str_const = self.str_consts.get(string) - except: - name = f'str_const_{len(self.str_consts)}' - str_const = mips.StringConst(name, string) - self.dotdata.append(str_const) - self.str_consts.add(string, str_const) - - return str_const.label - - def new_type(self, name, attributes, methods): - label = f'type_{len(self.types_dict)}' - name = self.get_str_const(name) - size = (len(attributes) * mips.ATTR_SIZE) + mips.TYPE_METADATA_SIZE - - new_type = mips.MIPSType(label, name, size, methods) - self.types_dict.add(name, new_type) - func_names = [] - for _, func_name in methods: - new_func_name = f'func_{len(self.funcs_dict)}' - try: - new_func_name = self.funcs_dict.get(func_name) - except: - self.funcs_dict.add(func_name, new_func_name) - func_names.append(new_func_name) - - self.dotdata.append(mips.TypeDesc(label, name, size, func_names)) - - def register_basic_types_names(self): - self.types_dict.add("String", "string_type") - self.types_dict.add("Int", "int_type") - self.types_dict.add("Bool", "bool_type") - self.types_dict.add("Object", "object_type") - - - - - -class CILToMIPSVisitor(BaseCILToMIPSVisitor): - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(cil.ProgramNode) - def visit(self, node): - for type_node in node.dottypes: - self.visit(type_node) - - for data_node in node.dotdata: - self.visit(data_node) -# - # for function_node in node.dotcode: - # self.visit(function_node) - # - # return mips.ProgramNode(dottext, dotdata) - - @visitor.when(cil.TypeNode) - def visit(self, node): - self.new_type(node.name, node.attributes, node.methods) - - - @visitor.when(cil.DataNode) - def visit(self, node): - self.get_str_const(node.string) - - @visitor.when(cil.FunctionNode) - def visit(self, node): - self.actual_function = node - - #registers_to_save = ['ra', 't0', 't1'] - ## Saving Register - #self.add_instructions(mips.save_registers(registers_to_save)) - # - ## Argument received to params - #self.add_instructions(mips.MoveNode(mips.REGISTERS['t1'], mips.REGISTERS['t2'])) - # - ## Allocating memory for local variables - #addr = mips.Address(mips.REGISTERS['t0'], 0) - #var_size = len(self.localvars) * mips.ATTR_SIZE - #self.add_instructions(mips.allocate_memory(addr, var_size)) - - ## function_body - #for instruction in self.cil_isntructions: - # self.visit(instruction) - - ## Loading saved register - #self.add_instructions(mips.load_registers_from_stack(registers_to_save[-1])) - - #self.actual_function_instructions = [] - #self.actual_function = None - - @visitor.when(cil.AllocateNode) - def visit(self, node): - size = self.types[node.vtype].size - var_index = self.variable_index(node.dest.name) - offset = var_index * mips.ATTR_SIZE - addr = mips.Address(mips.REGISTERS['t0'], offset) - self.add_instructions(mips.allocate_memory(addr, size)) - From d643c2d13f25110829259532bb04cad0b416d59b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 09:50:29 -0400 Subject: [PATCH 273/520] [mips] Create new CILToMIPSVisitor --- src/core/cmp/cil_to_mips.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index c8d1ab85..462e0834 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -51,7 +51,13 @@ def generate_code_label(self): return f'L_{self.code_count}' - +class CILToMIPSVisitor: + def __init__(self, label_generator = LabelGenerator(), regiters_manager = SimpleRegistersManager()): + self._label_generator = label_generator + self._registers_manager = regiters_manager + self._types = {} + self._data_sections = {} + self._functions = {} From 85a2f219b45bdb8a657d4de387dfab927632f847 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 09:52:33 -0400 Subject: [PATCH 274/520] [mips] Add methods to CILToMIPSVisitor for labels generation --- src/core/cmp/cil_to_mips.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 462e0834..68d11e86 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -58,6 +58,15 @@ def __init__(self, label_generator = LabelGenerator(), regiters_manager = Simple self._types = {} self._data_sections = {} self._functions = {} + + def generate_type_label(self): + return self._label_generator.generate_type_label() + + def generate_data_label(self): + return self._label_generator.generate_data_label() + + def generate_code_label(self): + return self._label_generator.generate_code_label() From 9dbe20f398bca0519a3cc25298391b2f726def92 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 10:10:35 -0400 Subject: [PATCH 275/520] [mips] Add get_var_location method to CILToMIPSVisitor --- src/core/cmp/cil_to_mips.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 68d11e86..3d56f01f 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -58,6 +58,7 @@ def __init__(self, label_generator = LabelGenerator(), regiters_manager = Simple self._types = {} self._data_sections = {} self._functions = {} + self._actual_function = None def generate_type_label(self): return self._label_generator.generate_type_label() @@ -67,6 +68,11 @@ def generate_data_label(self): def generate_code_label(self): return self._label_generator.generate_code_label() + + def get_var_location(self, name): + return self._actual_function.get_var_location(name) + + From ffbdc993ec236b6b19855e319fd916cf2726ee7f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 10:13:52 -0400 Subject: [PATCH 276/520] [mips] Add register_function, init_function, finish_function methods to CILToMIPSVisitor --- src/core/cmp/cil_to_mips.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 3d56f01f..7f2a175b 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -72,7 +72,14 @@ def generate_code_label(self): def get_var_location(self, name): return self._actual_function.get_var_location(name) + def register_function(self, name, function): + self._functions[name] = function + def init_function(self, function): + self._actual_function = function + + def finish_functions(self): + self._actual_function = None From 218f4cbf926d2c2d2626d50a404aaf847ef5a372 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 10:27:30 -0400 Subject: [PATCH 277/520] [mips] Add visitor method collect_func_names to CILToMIPSVisitor --- src/core/cmp/cil_to_mips.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 7f2a175b..4df4f2e5 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -59,6 +59,7 @@ def __init__(self, label_generator = LabelGenerator(), regiters_manager = Simple self._data_sections = {} self._functions = {} self._actual_function = None + self._name_func_map = {} def generate_type_label(self): return self._label_generator.generate_type_label() @@ -80,6 +81,23 @@ def init_function(self, function): def finish_functions(self): self._actual_function = None + + @vistor.on('node') + def collect_func_names(self, node): + pass + + @visitor.when(cil.ProgramNode) + def collect_func_names(self, node): + for func in node.dotcode: + self.collect_func_names(func) + + @visitor.when(cil.FunctionNode) + def collect_func_names(self, node): + if node.name == "entry": + self._name_func_map[node.name] = 'main' + else: + self._name_func_map[node.name] = self.generate_code_label() + From 3dad9704dbed38dfff18ea112fac34f000b4cd8e Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 10:33:49 -0400 Subject: [PATCH 278/520] [mips] Add visitor mehotd visit to CILToMIPSVisitor to convert from CIL AST to MIPS AST --- src/core/cmp/cil_to_mips.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 4df4f2e5..f9e2b2d0 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -97,6 +97,12 @@ def collect_func_names(self, node): self._name_func_map[node.name] = 'main' else: self._name_func_map[node.name] = self.generate_code_label() + + + @visitor.on('node') + def visit(self, node): + pass + From f0e36788fcbdc5b05025150460694f892fc41c7b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 10:35:04 -0400 Subject: [PATCH 279/520] [mips] Add cil.InstructionNode case for debugin purposes --- src/core/cmp/cil_to_mips.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index f9e2b2d0..585a9bff 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -103,6 +103,9 @@ def collect_func_names(self, node): def visit(self, node): pass + @visitor.when(cil.InstructionNode) + def visit(self, node): + print(type(node)) From 77e9554e598f5b501aee581c3b13849bd3e8c740 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 10:55:56 -0400 Subject: [PATCH 280/520] [mips] Add cil.ProgramNode, cil.TypeNode and cil.DataNode cases to visit function --- src/core/cmp/cil_to_mips.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 585a9bff..b34c8607 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -56,7 +56,7 @@ def __init__(self, label_generator = LabelGenerator(), regiters_manager = Simple self._label_generator = label_generator self._registers_manager = regiters_manager self._types = {} - self._data_sections = {} + self._data_section = {} self._functions = {} self._actual_function = None self._name_func_map = {} @@ -106,6 +106,40 @@ def visit(self, node): @visitor.when(cil.InstructionNode) def visit(self, node): print(type(node)) + + @visitor.when(cil.ProgramNode) + def visit(self, node): + #Get functions names + self.collect_func_names(node) + + #Convert CIL ProgramNode to MIPS ProgramNode + for tp in node.dottypes: + self.visit(tp) + + for data in node.dotdata: + self.visit(data) + + for func in node.dotcode: + self.visit(func) + + return mips.ProgramNode([func for func in self._functions.values()], [data for data in self._data_section.values()], [tp for tp in self._types.values()]) + + @visitor.when(cil.TypeNode) + def visit(self, node): + name_label = self.generate_data_label() + self._data_section[node.name] = mips.StringConst(name_label, node.name) + + type_label = self.generate_type_label() + methods = {key: self._name_func_map[value] for key, value in node.methods} + new_type = mips.MIPSType(type_label, name_label, node.attributes, methods) + + self._types[node.name] = new_type + + @visitor.when(cil.DataNode) + def visit(self, node): + label = self.generate_data_label() + self._data_section[node.name] = mips.StringConst(label, node.value) + From c6809c0a2212daa82a07585363efd9a046e47a07 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 11:29:37 -0400 Subject: [PATCH 281/520] [mips] Add cil.ArgNode and cil.StaticCallNode cases to visit function --- src/core/cmp/cil_to_mips.py | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index b34c8607..c91e847b 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -60,6 +60,7 @@ def __init__(self, label_generator = LabelGenerator(), regiters_manager = Simple self._functions = {} self._actual_function = None self._name_func_map = {} + self._pushed_args = 0 def generate_type_label(self): return self._label_generator.generate_type_label() @@ -82,6 +83,12 @@ def init_function(self, function): def finish_functions(self): self._actual_function = None + def push_args(self): + self._pushed_args += 1 + + def clean_pushed_args(self): + self._pushed_args = 0 + @vistor.on('node') def collect_func_names(self, node): pass @@ -139,6 +146,45 @@ def visit(self, node): def visit(self, node): label = self.generate_data_label() self._data_section[node.name] = mips.StringConst(label, node.value) + + @visitor.when(cil.ArgNode) + def visit(self, node): + self.push_arg() + + instructions = [] + + if type(node.name) == int: + reg = self.register_manager.get_free_reg() + load_value = mips.LoadInmediateNode(reg, node.name) + instructions.append(load_value) + instructions.extend(mips.push_register(reg)) + else: + reg = self.register_manager.get_free_reg() + # if not loaded: + value_address = self.get_var_location(node.name) + load_value = mips.LoadWordNode(reg, value_address) + instructions.append(load_value) + instructions.extend(mips.push_register(reg)) + + self.register_manager.free_reg(reg) + return instructions + + @visitor.when(cil.StaticCallNode) + def visit(self, node): + instructions = [] + + label = self._name_func_map[node.function] + + instructions.append(mips.JumpAndLinkNode(label)) + + dst_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + + if self._pushed_args > 0: + instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) + self.clean_pushed_args() + + return instructions From 0f5bb6cfe0621438be49c0930baac6efbe756c46 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 12:11:21 -0400 Subject: [PATCH 282/520] [mips] Add cil.AssignNode and cil.AllocateNode cases to visit function --- src/core/cmp/cil_to_mips.py | 52 ++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index c91e847b..7dff60a5 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -89,6 +89,12 @@ def push_args(self): def clean_pushed_args(self): self._pushed_args = 0 + def get_free_reg(self): + return self._registers_manager.get_free_reg() + + def free_reg(self, reg): + self._registers_manager.free_reg(reg) + @vistor.on('node') def collect_func_names(self, node): pass @@ -154,19 +160,19 @@ def visit(self, node): instructions = [] if type(node.name) == int: - reg = self.register_manager.get_free_reg() + reg = self.get_free_reg() load_value = mips.LoadInmediateNode(reg, node.name) instructions.append(load_value) instructions.extend(mips.push_register(reg)) else: - reg = self.register_manager.get_free_reg() + reg = self.get_free_reg() # if not loaded: value_address = self.get_var_location(node.name) load_value = mips.LoadWordNode(reg, value_address) instructions.append(load_value) instructions.extend(mips.push_register(reg)) - self.register_manager.free_reg(reg) + self.free_reg(reg) return instructions @visitor.when(cil.StaticCallNode) @@ -184,7 +190,45 @@ def visit(self, node): instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) self.clean_pushed_args() - return instructions + return instructions + + @visitor.when(cil.AssignNode) + def visit(self, node): + instructions = [] + reg = self.get_free_reg() + + if node.source.isnumeric(): + load_value = mips.LoadInmediateNode(reg, int(node.source)) + instrucctions.append(load_value) + else: + value_location = self.get_var_location(node.source) + load_value = mips.LoadWordNode(reg, value_location) + instrucctions.append(load_value) + + location = self.get_var_location(node.dest) + instrucctions.append(mips.StoreWordNode(reg, location)) + self.free_reg(reg) + + return instructions + + @visitor.when(cil.AllocateNode) + def visit(self, node): + #TODO Save $a0 register if is beign used + object_size = self._types[node.type].size + object_label = self._types[node.type].label + + instructions = [] + instructions.extend(mips.alloc_memory(object_size)) + + reg = self.get_free_reg() + instructions.append(mips.LoadAddressNode(reg, object_label)) + instructions.append(mips.StoreWordNode(reg, mips.RegisterRelativeLocation(mips.V0_REG, 0))) + self.free_reg(reg) + + location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(mips.V0_REG, location)) + + return instructions From 212d130100000f0d4a6f3205551b06dcff7d007f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 12:25:26 -0400 Subject: [PATCH 283/520] [mips] Add cil.ReturnNode and cil.LoadNode cases to visit function --- src/core/cmp/cil_to_mips.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 7dff60a5..134614bf 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -229,6 +229,35 @@ def visit(self, node): instructions.append(mips.StoreWordNode(mips.V0_REG, location)) return instructions + + @visitor.when(cil.ReturnNode) + def visit(self, node): + instructions = [] + + if node.value is None: + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0)) + elif type(node.value) == int: + instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value)) + else: + location = self.get_var_location(node.value) + instructions.append(mips.LoadWordNode(mips.V0_REG, location)) + + return instructions + + @visitor.when(cil.LoadNode) + def visit(self, node): + instructions = [] + + reg = self.get_free_reg() + string_location = mips.LabelRelativeLocation(self._data_section[node.msg].label, 0) + instructions.append(mips.LoadAddressNode(reg, string_location)) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(reg, dest_location)) + + self.free_reg(reg) + + return instructions From 2f13b4634996697d8e485204e1e653930df41d57 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 12:35:19 -0400 Subject: [PATCH 284/520] [mips] Add cil.PrintIntNode and cil.PrintStrNode cases to visit function --- src/core/cmp/cil_to_mips.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 134614bf..9c964dd6 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -258,6 +258,31 @@ def visit(self, node): self.free_reg(reg) return instructions + + @visitor.when(cil.PrintIntNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1)) + + #TODO save $a0 if is beign used + location = self.get_var_location(node.value) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(cil.PrintStrNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) + + #TODO save $a0 if is beign used + + location = self.get_var_location(node.value) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + instructions.append(mips.SyscallNode()) + + return instructions From dddbbaa6e9f065d81be21b16611556e612d7bcd1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 12:43:15 -0400 Subject: [PATCH 285/520] [mips] Add cil.TypeNameNode and cil.ExitNode cases to visit function --- src/core/cmp/cil_to_mips.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 9c964dd6..6a5ac6a8 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -283,6 +283,31 @@ def visit(self, node): instructions.append(mips.SyscallNode()) return instructions + + @visitor.when(cil.TypeNameNode) + def visit(self, node): + instructions = [] + reg = self.get_free_reg() + + src_location = self.get_var_location(node.source) + dst_location = self.get_var_location(node.dest) + + instructions.append(mips.LoadWordNode(reg, src_location)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + instructions.append(mips.StoreWordNode(reg, dst_location)) + + self.free_reg(reg) + + return instructions + + @visitor.when(cil.ExitNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.SyscallNode()) + + return instructions From 9bea2504f3aac4cbd7f9bb914af4b6b12501b3e3 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 12:51:41 -0400 Subject: [PATCH 286/520] [mips] Add cil.GetAttributeNode and cil.SetAttributeNode to visit function --- src/core/cmp/cil_to_mips.py | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 6a5ac6a8..f63beba0 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -308,6 +308,48 @@ def visit(self, node): instructions.append(mips.SyscallNode()) return instructions + + @visitor.when(cil.GetAttribNode) + def visit(self, node): + instructions = [] + reg = self.get_free_reg() + + obj_location = self.get_var_location(node.obj) + dst_location = self.get_var_location(node.dest) + tp = self._types[node.xtype] + offset = (tp.attributes.index(node.attr) + 1) * mips.ATTR_SIZE + + instructions.append(mips.LoadWordNode(reg, obj_location)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, offset))) + instructions.append(mips.StoreWordNode(reg, dst_location)) + + self.free_reg(reg) + return instructions + + @visitor.when(cil.SetAttribNode) + def visit(self, node): + instructions = [] + reg = self.get_free_reg() + reg2 = self.get_free_reg() + + obj_location = self.get_var_location(node.obj) + tp = self._types[node.xtype] + offset = (tp.attributes.index(node.attr) + 1) * mips.ATTR_SIZE + + instructions.append(mips.LoadWordNode(reg2, obj_location)) + + if node.value.isnumeric(): + instructions.append(mips.LoadInmediateNode(reg, int(node.value))) + else: + src_location = self.get_var_location(node.value) + instructions.append(mips.LoadWordNode(reg, src_location)) + + instructions.append(mips.StoreWordNode(reg, mips.RegisterRelativeLocation(reg2, offset))) + + self.free_reg(reg) + self.free_reg(reg2) + return instructions + From 19177f223161b85eab4a3647b319984a01ff0099 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 12:53:55 -0400 Subject: [PATCH 287/520] [mips] Add UsedRegisterFinder class to get used registers from a list of Instructions --- src/core/cmp/cil_to_mips.py | 65 +++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index f63beba0..fd8e42dd 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -351,42 +351,57 @@ def visit(self, node): return instructions - +class UsedRegisterFinder: + def __init__(self): + self.used_registers = set() + + def get_used_registers(self, instructions): + self.used_registers = set() + + for inst in instructions: + self.visit(inst) + self.used_registers = set.difference(self.used_registers, set([mips.SP_REG, mips.FP_REG, mips.V0_REG])) + return [reg for reg in self.used_registers] + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(mips.LoadInmediateNode) + def visit(self, node): + self.used_registers.add(node.reg) + @visitor.when(mips.LoadAddressNode) + def visit(self, node): + self.used_registers.add(node.reg) + @visitor.when(mips.AddInmediateNode) + def visit(self, node): + self.used_registers.add(node.dest) + + @visitor.when(mips.MoveNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.LoadWordNode) + def visit(self, node): + self.used_registers.add(node.reg) + @visitor.when(mips.JumpAndLinkNode) + def visit(self, node): + self.used_registers.add(mips.RA_REG) + + + + -def cil_to_mips_data(cil_data): - return mips.DataNode(cil_data.name, cil_data.value) -def cil_to_mips_type(cil_type): - return mips.MIPSType(cil_type.name, cil_type.attributes, cil_type.methods) -# class A: - # pass -# -# class B(A): - # pass -# -# class C: - # pass -# -# @visitor.on('param') -# def try2(param): - # pass -# -# @visitor.when(A) -# def try2(param): - # print("is A") -# -# @visitor.when(B) -# def try2(param): - # print("is B") #Change Name From ab3bb914974eb875e233e470f8bab166ee353cbf Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 13:12:51 -0400 Subject: [PATCH 288/520] [mips] Add cil.FunctionNode case to visit function --- src/core/cmp/cil_to_mips.py | 60 ++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index fd8e42dd..7c812b7a 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -1,3 +1,4 @@ +import itertools as itt import core.cmp.visitor as visitor import core.cmp.cil as cil import core.cmp.mips as mips @@ -95,6 +96,9 @@ def get_free_reg(self): def free_reg(self, reg): self._registers_manager.free_reg(reg) + def in_entry_function(self): + return self._actual_function.label == 'main' + @vistor.on('node') def collect_func_names(self, node): pass @@ -152,7 +156,60 @@ def visit(self, node): def visit(self, node): label = self.generate_data_label() self._data_section[node.name] = mips.StringConst(label, node.value) - + + @visitor.when(cil.FunctionNode) + def visit(self, node): + used_regs_finder = UsedRegisterFinder() + + label = self._name_func_map[node.name] + params = [param.name for param in node.params] + localvars = [local.name for local in node.localvars] + size_for_locals = len(localvars) * mips.ATTR_SIZE + + new_func = mips.FunctionNode(label, params, localvars) + self.register_function(node.name, new_func) + self.init_function(new_func) + + initial_instructions = [] + + + initial_instructions.extend(mips.push_register(mips.FP_REG)) + initial_instructions.append(mips.AddInmediateNode(mips.FP_REG, mips.SP_REG, 4)) + initial_instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals)) + + code_instrutions = [] + + #This try-except block is for debuggin purposes + try: + code_instrutions = list(itt.chain.from_iterable([self.visit(instrucion) for instruction in node.instructions])) + except: + print(node.name) + + final_instructions = [] + + if not self.in_entry_function(): + used_regs = used_regs_finder.get_used_registers(code_instrutions) + #TODO change this to grow the stack just once + for reg in used_regs: + initial_instructions.extend(mips.push_register(reg)) + + #TODO change this to shrink the stack just once + for reg in used_regs[::-1]: + final_instructions.extend(mips.pop_register(reg)) + + final_instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, size_for_locals)) + final_instructions.extend(mips.pop_register(mips.FP_REG)) + + if not self.in_entry_function(): + final_instructions.append(mips.JumpRegister(mips.RA_REG)) + else: + final_instructions.extend(mips.exit_program()) + + func_instructions = list(itt.chain(initial_instructions, code_instrutions, final_instructions)) + new_func.add_instructions(func_instructions) + self.finish_functions() + + @visitor.when(cil.ArgNode) def visit(self, node): self.push_arg() @@ -351,6 +408,7 @@ def visit(self, node): return instructions + class UsedRegisterFinder: def __init__(self): self.used_registers = set() From 64e42ce5ecc99a9f14a3f0fc31580732e56b04b5 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 13:19:09 -0400 Subject: [PATCH 289/520] [mips] Add cil to mips phase to main pipeline and fix some typos --- src/core/cmp/cil_to_mips.py | 4 ++-- src/core/cmp/mips.py | 2 +- src/main.py | 12 ++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 7c812b7a..1e1490fc 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -53,7 +53,7 @@ def generate_code_label(self): class CILToMIPSVisitor: - def __init__(self, label_generator = LabelGenerator(), regiters_manager = SimpleRegistersManager()): + def __init__(self, label_generator = LabelGenerator(), regiters_manager = SimpleRegistersManager(mips.REGISTERS)): self._label_generator = label_generator self._registers_manager = regiters_manager self._types = {} @@ -99,7 +99,7 @@ def free_reg(self, reg): def in_entry_function(self): return self._actual_function.label == 'main' - @vistor.on('node') + @visitor.on('node') def collect_func_names(self, node): pass diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index c18487e3..a13caa7e 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -222,7 +222,7 @@ def exit_program(): class PrintVisitor: @visitor.on('node') - def print(self, node): + def visit(self, node): pass @visitor.when(Register) diff --git a/src/main.py b/src/main.py index 71cf6f2c..c1ccd239 100644 --- a/src/main.py +++ b/src/main.py @@ -9,6 +9,8 @@ from core.cmp.cil import get_formatter from pprint import pprint from core.cmp.cool_to_cil import COOLToCILVisitor +from core.cmp.cil_to_mips import CILToMIPSVisitor +from core.cmp.mips import PrintVisitor def main(args): @@ -82,6 +84,16 @@ def main(args): #formatter = get_formatter() #ast_cil = formatter(cil_ast) #print(ast_cil) + + cil_to_mips = CILToMIPSVisitor() + mips_ast = cil_to_mips.visit(cil_ast) + printer = PrintVisitor() + mips_code = printer.visit(mips_ast) + + with open("compiled.asm") as f: + f.write(mips_code) + + exit(0) From 426f77787f2cea9a78c844a655bf8573440aac64 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 13:21:48 -0400 Subject: [PATCH 290/520] [cil] init method for IO type was beign registered to Object type instead IO type --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 772646c3..60e45a45 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -109,7 +109,7 @@ def register_built_in(self): #IO type_node = self.register_type('IO') - self.current_function = self.register_function(self.to_function_name('init', 'Object')) + self.current_function = self.register_function(self.to_function_name('init', 'IO')) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('Object', instance)) self.register_instruction(cil.ReturnNode(instance)) From 1e75203a2da8445dc0c3fc439c916b49371437ab Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 13:26:34 -0400 Subject: [PATCH 291/520] [mips] Add label property to FunctionNode class --- src/core/cmp/mips.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index a13caa7e..ed0a5b5c 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -51,6 +51,10 @@ def __init__(self, label, params, localvars, instructions = []): self._params = params self._localvars = localvars + @property + def label(self): + return self._label + def add_instructions(self, instructions): self._instructions.extend(instructions) @@ -240,7 +244,7 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node): data_section_header = "\t.data" - static_strings = '\n'.join([self.visit(string_const) for string_const in node.static_data]) + static_strings = '\n'.join([self.visit(string_const) for string_const in node.data]) types = "\n".join([self.visit(tp) for tp in node.types]) From 16d9092b9db3bbf6a5a5a599e5da3c6fa3dafa78 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 14:00:22 -0400 Subject: [PATCH 292/520] [mips] Add instructions property to FunctionNode class --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index ed0a5b5c..1c36bf14 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -55,6 +55,10 @@ def __init__(self, label, params, localvars, instructions = []): def label(self): return self._label + @property + def instructions(self): + return self._instructions + def add_instructions(self, instructions): self._instructions.extend(instructions) From c42a382e8fdd0eb8de9530517e0f4e681215f004 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 14:01:40 -0400 Subject: [PATCH 293/520] [mips] Add string property to StringConst class --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 1c36bf14..bc87b330 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -93,6 +93,10 @@ def __init__(self, label, string): super().__init__(label) self._string = string + @property + def string(self): + return self._string + class InstructionNode(Node): pass From b54365a7d38e24fd7c71208bba8eca8f20161632 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 14:23:29 -0400 Subject: [PATCH 294/520] [mips] Add methods property to MIPSType class --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index bc87b330..138976f9 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -171,6 +171,10 @@ def label(self): @property def string_name_label(self): return self._name + + @property + def methods(self): + return self._methods From 61b5e48e31a31c8ba217d81f79bc0109d9bb24ea Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 14:24:13 -0400 Subject: [PATCH 295/520] [mips] Add attributes property to MIPSType class --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 138976f9..fe19eff0 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -175,6 +175,10 @@ def string_name_label(self): @property def methods(self): return self._methods + + @property + def attributes(self): + return self._attributes From 71297f5a8bce90fff82175474d780f1898bcb4ee Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 14:46:39 -0400 Subject: [PATCH 296/520] [mips] Fix typos errors and convert REGISTER global from dict to list --- src/core/cmp/cil_to_mips.py | 22 +++++++++++----------- src/core/cmp/mips.py | 2 +- src/main.py | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 1e1490fc..76bd40f5 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -84,7 +84,7 @@ def init_function(self, function): def finish_functions(self): self._actual_function = None - def push_args(self): + def push_arg(self): self._pushed_args += 1 def clean_pushed_args(self): @@ -139,7 +139,7 @@ def visit(self, node): for func in node.dotcode: self.visit(func) - return mips.ProgramNode([func for func in self._functions.values()], [data for data in self._data_section.values()], [tp for tp in self._types.values()]) + return mips.ProgramNode( [data for data in self._data_section.values()], [tp for tp in self._types.values()], [func for func in self._functions.values()]) @visitor.when(cil.TypeNode) def visit(self, node): @@ -177,18 +177,18 @@ def visit(self, node): initial_instructions.append(mips.AddInmediateNode(mips.FP_REG, mips.SP_REG, 4)) initial_instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals)) - code_instrutions = [] + code_instructions = [] #This try-except block is for debuggin purposes try: - code_instrutions = list(itt.chain.from_iterable([self.visit(instrucion) for instruction in node.instructions])) - except: + code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) + except Exception as e: print(node.name) - + final_instructions = [] if not self.in_entry_function(): - used_regs = used_regs_finder.get_used_registers(code_instrutions) + used_regs = used_regs_finder.get_used_registers(code_instructions) #TODO change this to grow the stack just once for reg in used_regs: initial_instructions.extend(mips.push_register(reg)) @@ -205,7 +205,7 @@ def visit(self, node): else: final_instructions.extend(mips.exit_program()) - func_instructions = list(itt.chain(initial_instructions, code_instrutions, final_instructions)) + func_instructions = list(itt.chain(initial_instructions, code_instructions, final_instructions)) new_func.add_instructions(func_instructions) self.finish_functions() @@ -256,14 +256,14 @@ def visit(self, node): if node.source.isnumeric(): load_value = mips.LoadInmediateNode(reg, int(node.source)) - instrucctions.append(load_value) + instructions.append(load_value) else: value_location = self.get_var_location(node.source) load_value = mips.LoadWordNode(reg, value_location) - instrucctions.append(load_value) + instructions.append(load_value) location = self.get_var_location(node.dest) - instrucctions.append(mips.StoreWordNode(reg, location)) + instructions.append(mips.StoreWordNode(reg, location)) self.free_reg(reg) return instructions diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index fe19eff0..3eb4073a 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -15,7 +15,7 @@ class Register(): def __init__(self, name): self.name = name -REGISTERS = { name: Register(name) for name in REGISTER_NAMES } +REGISTERS = [ Register(name) for name in REGISTER_NAMES ] ARG_REGISTERS = [ Register(name) for name in ARG_REGISTERS_NAMES ] FP_REG = Register('fp') SP_REG = Register('sp') diff --git a/src/main.py b/src/main.py index c1ccd239..8f5e60c4 100644 --- a/src/main.py +++ b/src/main.py @@ -90,7 +90,7 @@ def main(args): printer = PrintVisitor() mips_code = printer.visit(mips_ast) - with open("compiled.asm") as f: + with open("compiled.asm", 'w') as f: f.write(mips_code) From 4a5f1e92a9adc2efad98e21042a7a04297aa236e Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 16:49:51 -0400 Subject: [PATCH 297/520] [mips] Change declaration of __init__ method from FunctionNode class --- src/core/cmp/mips.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 3eb4073a..bd1b0c5c 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -45,9 +45,9 @@ def functions(self): return self._functions class FunctionNode(Node): - def __init__(self, label, params, localvars, instructions = []): + def __init__(self, label, params, localvars): self._label = label - self._instructions = instructions + self._instructions = [] self._params = params self._localvars = localvars From 5bf1ae593c43eb08a252b77fa0dd62013e2922f5 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 16:52:28 -0400 Subject: [PATCH 298/520] [cil] Register variable used in entry function for Main object --- src/core/cmp/cool_to_cil.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 60e45a45..20c390ab 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -218,8 +218,9 @@ def visit(self, node, scope): self.current_function = self.register_function('entry') result = self.define_internal_local() - self.register_instruction(cil.AllocateNode('Main', self.vself.name)) - self.register_instruction(cil.ArgNode(self.vself.name)) + instance = self.register_local(VariableInfo('instance', None)) + self.register_instruction(cil.AllocateNode('Main', instance)) + self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) # Error message raised by Object:abort() From 8a2f027b309700b6b4ded29f6cee6cead53b09ca Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 31 Aug 2020 18:13:41 -0400 Subject: [PATCH 299/520] [mips] Change cil.LoadNode case of visit function to use new LoadNode attribute type --- src/core/cmp/cil_to_mips.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 76bd40f5..34a1762a 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -184,7 +184,7 @@ def visit(self, node): code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) except Exception as e: print(node.name) - + final_instructions = [] if not self.in_entry_function(): @@ -304,16 +304,14 @@ def visit(self, node): @visitor.when(cil.LoadNode) def visit(self, node): instructions = [] - reg = self.get_free_reg() - string_location = mips.LabelRelativeLocation(self._data_section[node.msg].label, 0) + string_location = mips.LabelRelativeLocation(self._data_section[node.msg.name].label, 0) instructions.append(mips.LoadAddressNode(reg, string_location)) dest_location = self.get_var_location(node.dest) instructions.append(mips.StoreWordNode(reg, dest_location)) self.free_reg(reg) - return instructions @visitor.when(cil.PrintIntNode) From 0743ce3560f92866f31f9de59426c72bbd8650d1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 2 Sep 2020 11:55:03 -0400 Subject: [PATCH 300/520] [gitignore][make] - Update gitignore and Makefile - Ignore the code.cl and code.s files - Add a `coolc` rule to Makefile for compile and run code.cl --- .gitignore | 2 ++ src/Makefile | 6 +++++- src/code.cl | 36 ++++++++++++++++++++++++------------ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 595fb35f..71f21347 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ # 2kodevs ignores +*src/code.cl +*src/code.s *Stuff/ # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig diff --git a/src/Makefile b/src/Makefile index b0d7fc9e..f355f6d0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ main: ## Compiling the compiler :) # Compiling the compiler :) clean: ## Remove temporary files - rm -rf build/* + @rm -rf build/* test: ## Run testsuit with name TAG pytest ../tests -v --tb=short -m=${TAG} @@ -22,6 +22,10 @@ info: ## Display project description @echo "$(APP_DESCRIPTION)" @echo "$(COPYRIGHT)" +coolc: ## Run the code.cl file using coolc + @coolc code.cl + @spim code.s + help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/src/code.cl b/src/code.cl index 493a00f9..4d3b1df6 100644 --- a/src/code.cl +++ b/src/code.cl @@ -1,13 +1,25 @@ +class A{ + x:Int <- 3; +}; + +class B inherits A { + +}; + class Main inherits IO { - main() : String { - foo(42) - }; - - foo(i : Int) : String { - if i = 0 then "" else - (let next : Int <- i / 10 in - foo(next).concat(foo(i - next * 10)) - ) - fi - }; -}; \ No newline at end of file + y:B <- new B; + x:A <- y; + + main() : Object {{ + + out_string("\n"); + if (x = y) + then out_string("EQUAL\n") + else out_string("NOT EQUAL\n") + fi; + out_string(x.type_name()); + out_string("\n"); + + 1 < 3 + let a:Int<-3 in a; + }}; +}; From 01ab31e5a1e7988a5beac2f36233d2f0537a8eee Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 2 Sep 2020 12:08:50 -0400 Subject: [PATCH 301/520] [coolUtils] - Remove old unnecessary code --- src/core/cmp/CoolUtils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index 0d2f0b0c..e3a2b319 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -172,12 +172,6 @@ class StringNode(AtomicNode): class BoolNode(AtomicNode): pass -def FunctionCallNodeBuilder(obj, calls): - while len(calls): - obj = FunctionCallNode(obj, *calls[0]) - calls.pop(0) - return obj - class Param(Node): def __init__(self, tid, ttype): self.tid = tid From 9b27f07774c6625b448284b2a060ac6e5ec3378e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 2 Sep 2020 12:17:13 -0400 Subject: [PATCH 302/520] [grammar] - Refactoring the grammar --- src/core/cmp/CoolUtils.py | 68 +++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index e3a2b319..327d2720 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -196,6 +196,7 @@ def __iter__(self): arith, term, func_expr, void, compl_expr, cmp_expr, statement = CoolGrammar.NonTerminals(' ') atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') final_expr, unary_expr = CoolGrammar.NonTerminals(' ') +special, special_arith, special_term = CoolGrammar.NonTerminals(' ') # terminals classx, inherits = CoolGrammar.Terminals('class inherits') @@ -217,8 +218,8 @@ def __iter__(self): class_list %= def_class, lambda h, s: [s[1]] # -def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[4]) -def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[6], s[4]) +def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[4]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[6], s[4]) # feature_list %= feature + feature_list, lambda h, s: [s[1]] + s[2] @@ -229,8 +230,8 @@ def __iter__(self): feature %= idx + colon + typex + larrow + expr + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5], s[4]) # -feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) -feature %= idx + opar + cpar + colon + typex + ocur + expr + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], s[5], s[7]) +feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) +feature %= idx + opar + cpar + colon + typex + ocur + expr + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], s[5], s[7]) # param_list %= param, lambda h, s: [s[1]] @@ -254,10 +255,10 @@ def __iter__(self): case_list %= idx + colon + typex + rarrow + expr + semi + case_list, lambda h, s: [CaseExpressionNode(s[1], s[3], s[5])] + s[7] # -func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) -func_call %= dot + idx + opar + cpar, lambda h, s: (s[2], []) -func_call %= at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: (s[4], s[6], s[2]) -func_call %= at + typex + dot + idx + opar + cpar, lambda h, s: (s[4], [], s[2]) +func_call %= at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: (s[4], s[6], s[2]) +func_call %= at + typex + dot + idx + opar + cpar, lambda h, s: (s[4], [], s[2]) +func_call %= dot + idx + opar + arg_list + cpar, lambda h, s: (s[2], s[4]) +func_call %= dot + idx + opar + cpar, lambda h, s: (s[2], []) # arg_list %= expr, lambda h, s: [s[1]] @@ -268,44 +269,49 @@ def __iter__(self): member_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) # -expr %= arith + plus + unary_expr, lambda h, s: PlusNode(s[1], s[3], s[2]) -expr %= arith + minus + unary_expr, lambda h, s: MinusNode(s[1], s[3], s[2]) -expr %= term + star + unary_expr, lambda h, s: StarNode(s[1], s[3], s[2]) -expr %= term + div + unary_expr, lambda h, s: DivNode(s[1], s[3], s[2]) -expr %= arith + plus + term + star + unary_expr, lambda h, s: PlusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) -expr %= arith + minus + term + star + unary_expr, lambda h, s: MinusNode(s[1], StarNode(s[3], s[5], s[4]), s[2]) -expr %= arith + plus + term + div + unary_expr, lambda h, s: PlusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) -expr %= arith + minus + term + div + unary_expr, lambda h, s: MinusNode(s[1], DivNode(s[3], s[5], s[4]), s[2]) -expr %= arith + leq + unary_expr, lambda h, s: LessEqualNode(s[1], s[3], s[2]) -expr %= arith + less + unary_expr, lambda h, s: LessNode(s[1], s[3], s[2]) -expr %= arith + equal + unary_expr, lambda h, s: EqualNode(s[1], s[3], s[2]) -expr %= unary_expr, lambda h, s: s[1] +expr %= special, lambda h, s: s[1] expr %= cmp_expr, lambda h, s: s[1] +# +special %= arith + leq + special_arith, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +special %= arith + less + special_arith, lambda h, s: LessNode(s[1], s[3], s[2]) +special %= arith + equal + special_arith, lambda h, s: EqualNode(s[1], s[3], s[2]) +special %= special_arith, lambda h, s: s[1] + +# +special_arith %= arith + plus + special_term, lambda h, s: PlusNode(s[1], s[3], s[2]) +special_arith %= arith + minus + special_term, lambda h, s: MinusNode(s[1], s[3], s[2]) +special_arith %= special_term, lambda h, s: s[1] + +# +special_term %= term + star + unary_expr, lambda h, s: StarNode(s[1], s[3], s[2]) +special_term %= term + div + unary_expr, lambda h, s: DivNode(s[1], s[3], s[2]) +special_term %= unary_expr, lambda h, s: s[1] + # unary_expr %= isvoid + unary_expr, lambda h, s: IsVoidNode(s[1], s[3], s[2]) unary_expr %= compl + unary_expr, lambda h, s: ComplementNode(s[1], s[3], s[2]) unary_expr %= final_expr, lambda h, s: s[1] # -final_expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) -final_expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +final_expr %= let + let_list + inx + expr, lambda h, s: LetInNode(s[2], s[4]) +final_expr %= idx + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) final_expr %= notx + expr, lambda h, s: NotNode(s[2], s[1]) # -cmp_expr %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[3], s[2]) -cmp_expr %= arith + less + arith, lambda h, s: LessNode(s[1], s[3], s[2]) +cmp_expr %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[3], s[2]) +cmp_expr %= arith + less + arith, lambda h, s: LessNode(s[1], s[3], s[2]) cmp_expr %= arith + equal + arith, lambda h, s: EqualNode(s[1], s[3], s[2]) cmp_expr %= arith, lambda h, s: s[1] # -arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3], s[2]) +arith %= arith + plus + term, lambda h, s: PlusNode(s[1], s[3], s[2]) arith %= arith + minus + term, lambda h, s: MinusNode(s[1], s[3], s[2]) arith %= term, lambda h, s: s[1] # term %= term + star + void, lambda h, s: StarNode(s[1], s[3], s[2]) -term %= term + div + void, lambda h, s: DivNode(s[1], s[3], s[2]) +term %= term + div + void, lambda h, s: DivNode(s[1], s[3], s[2]) term %= void, lambda h, s: s[1] # @@ -322,16 +328,16 @@ def __iter__(self): # atom %= member_call, lambda h, s: s[1] -atom %= new + typex, lambda h, s: NewNode(s[2]) +atom %= new + typex, lambda h, s: NewNode(s[2]) atom %= opar + expr + cpar, lambda h, s: s[2] atom %= idx, lambda h, s: IdNode(s[1]) atom %= integer, lambda h, s: IntegerNode(s[1]) atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) -atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) -atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) -atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) -atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) +atom %= ocur + block + ccur, lambda h, s: BlockNode(s[2]) +atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) +atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) +atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) # Parser CoolParser = LR1Parser(CoolGrammar) From cc34a7562d1a8e2a3a52535cbe6f43ecfededeef Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 3 Sep 2020 15:06:42 -0400 Subject: [PATCH 303/520] [cil] - Update method `length` of `String` A String object have now a `length` attribute where is the length of the string lying in `value` attr, calculated in the `init` method --- src/core/cmp/cool_to_cil.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 772646c3..5e771552 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -159,12 +159,18 @@ def register_built_in(self): instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('String', instance)) self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'String')) + result = self.define_internal_local() + self.register_instruction(cil.LengthNode(result, 'val')) + attr = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), attr)) + self.register_instruction(cil.SetAttribNode(instance, 'length', attr, 'String')) self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('length', 'String')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.LengthNode(result, self.vself.name)) + self.register_instruction(cil.GetAttribNode(result, self.vself, 'length', 'String')) self.register_instruction(cil.ReturnNode(result)) self.current_function = self.register_function(self.to_function_name('concat', 'String')) From e232554bbfe95be87d6bfe883bbf1b50ff70ee1b Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 3 Sep 2020 15:09:38 -0400 Subject: [PATCH 304/520] [cil] - Update visit of `EqualNode` Disambiguate between if the types are by value or reference. If the types are by value, compare their value attr --- src/core/cmp/cool_to_cil.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 5e771552..3f781029 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -535,11 +535,48 @@ def visit(self, node, scope): # node.right -> ExpressionNode ############################### vname = self.define_internal_local() + type_left = self.define_internal_local() + type_int = self.define_internal_local() + type_string = self.define_internal_local() + equal_result = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + self.visit(node.left, scope) left = scope.ret_expr self.visit(node.right, scope) right = scope.ret_expr + + self.register_instruction(cil.TypeNameNode(type_left, left)) + self.register_instruction(cil.NameNode(type_int, 'Int')) + self.register_instruction(cil.NameNode(type_string, 'String')) + + int_node = self.register_label('int_label') + string_node = self.register_label('string_label') + reference_node = self.register_label('reference_label') + continue_node = self.register_label('continue_label') + self.register_instruction(cil.EqualNode(equal_result, type_left, type_int)) + self.register_instruction(cil.GotoIfNode(equal_result, int_node.label)) + self.register_instruction(cil.EqualNode(equal_result, type_left, type_string)) + self.register_instruction(cil.GotoIfNode(equal_result, string_node.label)) + self.register_instruction(cil.GotoNode(reference_node.label)) + + self.register_instruction(int_node) + self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'Int')) + self.register_instruction(cil.EqualNode(vname, left_value, right_value)) + self.register_instruction(cil.GotoNode(continue_node.label)) + + self.register_instruction(string_node) + self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'String')) + self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'String')) + self.register_instruction(cil.EqualNode(vname, left_value, right_value)) + self.register_instruction(cil.GotoNode(continue_node.label)) + + self.register_instruction(reference_node) self.register_instruction(cil.EqualNode(vname, left, right)) + + self.register_instruction(continue_node) scope.ret_expr = vname @visitor.when(cool.PlusNode) From cf2f8697f0dd7421c421c9ea819e757ae1971b26 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 3 Sep 2020 15:10:46 -0400 Subject: [PATCH 305/520] [cil] - Add ErrorNode to CIL AST Represents the ERROR cmd. Receives a messages to be printed and do a exit after that. --- src/core/cmp/cil.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index da77d706..2f963013 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -203,6 +203,10 @@ def __init__(self, dest, obj): class VoidNode(InstructionNode): pass +class ErrorNode(InstructionNode): + def __init__(self, data_node): + self.data_node = data_node + def get_formatter(): class PrintVisitor(object): From ef1a6bc2e66b99edc9439b66975d61759c342b5b Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 3 Sep 2020 15:12:47 -0400 Subject: [PATCH 306/520] [cil] - Add support and management for 5 of 6 runtime errors The sixth runtime error must be managed in mips (Heap overflow) --- src/core/cmp/cool_to_cil.py | 102 ++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 27 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3f781029..6ed255df 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -178,15 +178,33 @@ def register_built_in(self): self.register_param(VariableInfo('s', None)) result = self.define_internal_local() self.register_instruction(cil.ConcatNode(result, self.vself.name, 's')) - self.register_instruction(cil.ReturnNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('substr', 'String')) self.register_param(self.vself) self.register_param(VariableInfo('i', None)) self.register_param(VariableInfo('l', None)) result = self.define_internal_local() - self.register_instruction(cil.SubstringNode(result, self.vself.name, 'i', 'l')) - self.register_instruction(cil.ReturnNode(result)) + index_value = self.define_internal_local() + length_value = self.define_internal_local() + length_attr = self.define_internal_local() + length_substr = self.define_internal_local() + less_value = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(index_value, 'i', 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(length_value, 'l', 'value', 'Int')) + #Check Out of range error + self.register_instruction(cil.GetAttribNode(length_attr, self.vself, 'length', 'String')) + self.register_instruction(cil.PlusNode(length_substr, length_value, index_value)) + self.register_instruction(cil.LessNode(less_value, length_attr, length_substr)) + self.register_runtime_error(less_value, 'Substring out of range') + self.register_instruction(cil.SubstringNode(result, self.vself.name, index_value, length_value)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(name, self.to_function_name(name, 'String')) for name in ['init', 'length', 'concat', 'substr']] @@ -203,6 +221,24 @@ def register_built_in(self): type_node.methods = [('init', self.to_function_name('init', 'Int'))] + def register_errors_messages(self): + self.register_data('Dispatch on void') + self.register_data('Case on void') + self.register_data('Execution of a case statement without a matching branch') + self.register_data('Division by zero') + self.register_data('Substring out of range') + + def register_runtime_error(self, condition, msg): + error_node = self.register_label('error_label') + continue_node = self.register_label('continue_label') + self.register_instruction(cil.GotoIfNode(condition, error_node.label)) + self.register_instruction(cil.GotoNode(continue_node.label)) + self.register_instruction(error_node) + data_node = [dn for dn in self.dotdata if dn.value == msg][0] + self.register_instruction(cil.ErrorNode(data_node)) + + self.register_instruction(continue_node) + class COOLToCILVisitor(BaseCOOLToCILVisitor): def __init__(self, context): @@ -230,6 +266,7 @@ def visit(self, node, scope): self.register_instruction(cil.ReturnNode(0)) # Error message raised by Object:abort() self.register_data('Program aborted') + self.register_errors_messages() self.register_built_in() self.current_function = None @@ -413,7 +450,13 @@ def visit(self, node, scope): self.visit(node.expr, scope) self.register_instruction(cil.AssignNode(vexpr, scope.ret_expr)) self.register_instruction(cil.TypeNameNode(vtype, scope.ret_expr)) - #//TODO: Ask if is void and raise proper error if vexpr value is void + + #Check if node.expr is void and raise proper error if vexpr value is void + void = cil.VoidNode() + equal_result = self.define_internal_local() + self.register_instruction(cil.EqualNode(equal_result, vexpr, void)) + + self.register_runtime_error(equal_result, 'Case on void') end_label = self.register_label('end_label') labels = [] @@ -433,8 +476,6 @@ def visit(self, node, scope): self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) - #//TODO: Raise runtime error if no Goto was executed - for idx, l in enumerate(labels): self.register_instruction(l) vid = self.register_local(VariableInfo(node.branches[idx].id, None)) @@ -443,6 +484,10 @@ def visit(self, node, scope): self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) self.register_instruction(cil.GotoNode(end_label.label)) + #Raise runtime error if no Goto was executed + data_node = [dn for dn in self.dotdata if dn.value == 'Execution of a case statement without a matching branch'][0] + self.register_instruction(cil.ErrorNode(data_node)) + self.register_instruction(end_label) @visitor.when(cool.CaseExpressionNode) @@ -649,7 +694,12 @@ def visit(self, node, scope): self.register_instruction(cil.GetAttribNode(vleft, scope.ret_expr, 'value', 'Int')) self.visit(node.right, scope) self.register_instruction(cil.GetAttribNode(vright, scope.ret_expr, 'value', 'Int')) - #//TODO: Check division by 0 runtime error??? + + #Check division by 0 + equal_result = self.define_internal_local() + self.register_instruction(cil.EqualNode(equal_result, vright, 0)) + self.register_runtime_error(equal_result, 'Division by zero') + self.register_instruction(cil.DivNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) @@ -698,34 +748,32 @@ def visit(self, node, scope): args.append(cil.ArgNode(vname)) result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None)) + vobj = self.define_internal_local() + self.visit(node.obj, scope) + self.register_instruction(cil.AssignNode(vobj, scope.ret_expr)) + + #Check if node.obj is void + void = cil.VoidNode() + equal_result = self.define_internal_local() + self.register_instruction(cil.EqualNode(equal_result, vobj, void)) + + self.register_runtime_error(equal_result, 'Dispatch on void') + + #self + self.register_instruction(cil.ArgNode(vobj)) + for arg in args: + self.register_instruction(arg) + if node.type: #Call of type @.id(,...,) - #Is ok to search node.type in dottypes??? - vobj = self.define_internal_local() - self.visit(node.obj, scope) - self.register_instruction(cil.AssignNode(vobj, scope.ret_expr)) - #self for Static Dispatch - self.register_instruction(cil.ArgNode(vobj)) - for arg in args: - self.register_instruction(arg) - #method = [method for method in at_type.methods if method.name == node.id][0] - #Shall we look method node.id in at_type parents??? self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, node.type), result)) - scope.ret_expr = result else: #Call of type .(,...,) type_of_node = self.register_local(VariableInfo(f'{node.id}_type', None)) - vobj = self.define_internal_local() - self.visit(node.obj, scope) - self.register_instruction(cil.AssignNode(vobj, scope.ret_expr)) self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) - #self for Dynamic Dispatch - self.register_instruction(cil.ArgNode(vobj)) - for arg in args: - self.register_instruction(arg) - self.register_instruction(cil.DynamicCallNode(type_of_node, node.id, result)) - scope.ret_expr = result + + scope.ret_expr = result @visitor.when(cool.MemberCallNode) def visit(self, node, scope): From a998dbdd3ea622e9e31aa6ba024aa778eb4c9221 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 8 Sep 2020 18:33:45 -0400 Subject: [PATCH 307/520] Mips library added with memory manager methods. The memory for the program will be managed using two linked list: Free-List: A linked list conformed by the blocks that are not beign used. Used-List: A linked list conformed by the blocks that are beign used. A block is conformed by a Block_Header whit the form: |Size | Next | Reachable | space | space | ----- | space | space | where each of the slot defined by | | is a word sized segment of memory. Size if the amount of memory defined by spaces segments, Next if the address of the next Block in the linked-list, and Reachable is used for GC tasks. There are implemented several routines to manage this lists: - mem_manager_init - free_block - expand_block - extend_heap - split_block There is implemented a malloc routine too that will be used when the program needs memory size to use --- src/core/cmp/trap.handler | 435 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 src/core/cmp/trap.handler diff --git a/src/core/cmp/trap.handler b/src/core/cmp/trap.handler new file mode 100644 index 00000000..e320e9d5 --- /dev/null +++ b/src/core/cmp/trap.handler @@ -0,0 +1,435 @@ + + + + .text + +header_size = 12 #in bytes +header_size_slot = 0 +header_next_slot = 4 +header_reachable_slot = 8 +alloc_size = 2048 +total_alloc_size = alloc_size + header_size +neg_header_size = 0-header_size +free_list = 0 +used_list = header_size +state_size = 4 +init_alloc_size = (header_size*2) + state_size + + + + +##################################################################################################### +# Initialize memory manager # +# Args: # +# # +# Return: # +# # +# Summary: # +# The initial blocks for Free-List and Used-List are created. # +# The $gp is set to use as reference when initial blocks or values related to memory manager # +# state are needed. # +# A block of size alloc_size is created an added to Free-List # +##################################################################################################### +mem_manager_init: + addiu $sp $sp -16 + sw $v0 0($sp) + sw $a0 4($sp) + sw $a1 8($sp) + sw $ra 12($sp) + + + li $v0 9 + li $a0 init_alloc_size + syscall #Creating free-list start point + move $gp $v0 + addiu $gp $gp state_size + + sw $zero header_size_slot($gp) #The free-list start with a block without space, just header, that will always be there. + sw $zero header_next_slot($gp) + sw $zero header_reachable_slot($gp) + + move $a0 $gp + li $a1 alloc_size + jal extend_heap + + addi $a0 $a0 header_size + sw $zero header_size_slot($a0) #The used-list start with a block without space, just header, that will always be there. + sw $zero header_next_slot($a0) + sw $zero header_reachable_slot($a0) + + lw $v0 0($sp) + lw $a0 4($sp) + lw $a1 8($sp) + lw $ra 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Free a block previously allocated # +# Args: # +# $a0 Block to free address # +# Return: # +# # +# Summary: # +# Remove the block from the used-list and add it to the free-list # +##################################################################################################### +free_block: + addiu $sp $sp -28 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $a0 12($sp) + sw $ra 16($sp) + sw $t3 20($sp) + sw $t4 24($sp) + + move $t0 $a0 + + addiu $t1 $gp free_list # Store in $t1 the initial block of the free-list + + addiu $t3 $gp used_list # Store in $t3 the initial block of the used-list + +free_block_loop_used_list: # Iterate througth the used-list until find the block + lw $t4 header_next_slot($t3) + beq $t4 $t0 free_block_loop_free_list + move $t3 $t4 + j free_block_loop_used_list + + +free_block_loop_free_list: # Iterate througth the free-list to find the antecesor of the block in the free-list + lw $t2 header_next_slot($t1) + beq $t2 $zero free_block_founded_prev + bge $t2 $t0 free_block_founded_prev + move $t1 $t2 + j free_block_loop_free_list + +free_block_founded_prev: + # Remove the block from the used-list + lw $t4 header_next_slot($t0) + sw $t4 header_next_slot($t3) + + # Add the block to the free-list + sw $t2 header_next_slot($t0) + sw $t0 header_next_slot($t1) + +free_block_end: + + # Try to merge the list where the new block was added + move $a0 $t0 + jal expand_block + move $a0 $t1 + jal expand_block + + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $a0 12($sp) + lw $ra 16($sp) + lw $t3 20($sp) + lw $t4 24($sp) + addiu $sp $sp 28 + + jr $ra + + +##################################################################################################### +# Merge two continuos blocks of the free-list # +# Args: # +# $a0 First of the two blocks to merge # +# Return: # +# # +# Summary: # +# Check if a block can be merged with its sucesor in the free list # +##################################################################################################### +expand_block: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + + + addiu $t0 $gp free_list # $t0 = the initial block of the free-list + + beq $t0 $a0 expand_block_end # The initial block can't be expanded, the initial block always will have size 0 + + move $t0 $a0 + + # Check if the block and its sucesor in the free list are contiguous in memory + lw $t1 header_next_slot($t0) + lw $t2 header_size_slot($t0) + move $t3 $t2 + addiu $t2 $t2 header_size + addu $t2 $t2 $t0 + beq $t2 $t1 expand_block_expand + j expand_block_end + +expand_block_expand: #Increment the size of the first block and update next field + lw $t2 header_size_slot($t1) + addi $t2 $t2 header_size + add $t2 $t2 $t3 + sw $t2 header_size_slot($t0) + lw $t1 header_next_slot($t1) + sw $t1 header_next_slot($t0) + +expand_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Allocate more memory for the process and add it to the free-list # +# Args: # +# $a0 Last block of the free-list # +# $a1 Memory amount to alloc # +# Return: # +# # +# Summary: # +# More memory is allocated and add it to the free-list as a block. # +##################################################################################################### +extend_heap: + addiu $sp $sp -12 + sw $a0 0($sp) + sw $a1 4($sp) + sw $t0 8($sp) + + # Increase the amount of memory by header_size to create a block with that size + li $v0 9 + addiu $a0 $a1 header_size + syscall + + # Set values of the block_header + move $t0 $a1 + sw $t0 header_size_slot($v0) + sw $zero header_next_slot($v0) + sw $zero header_reachable_slot($v0) + + # Add block to the end of the free-list + lw $t0, 0($sp) + sw $v0 header_next_slot($t0) + + move $a0 $t0 + lw $a1 4($sp) + lw $t0 8($sp) + addiu $sp $sp 12 + + jr $ra + + + +##################################################################################################### +# Split a block into two blocks, one of the requested size and the other with the rest. # +# Args: # +# $a0 Address of the block to split # +# $a1 Size requested for one block # +# Return: # +# # +# Summary: # +# The block is splitted into two blocks if the size allow it. # +##################################################################################################### +split_block: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + + # Check if the block can be splitted in two blocks, one of the requested size + lw $t0 header_size_slot($a0) + bgt $a1 $t0 split_block_error_small + + # Check if after a split the block there is enough space to create another block, if there is not do not split + sub $t0 $t0 $a1 + li $t1 header_size + ble $t0 $t1 split_block_same_size + + # Compute the address of the second block + addu $t0 $a0 $a1 + addiu $t0 $t0 header_size + + #Update headers of the two blocks + lw $t1 header_next_slot($a0) + sw $t1 header_next_slot($t0) + sw $t0 header_next_slot($a0) + + lw $t1 header_size_slot($a0) #update sizes + sub $t1 $t1 $a1 + + addi $t1 $t1 neg_header_size + sw $t1 header_size_slot($t0) + sw $a1 header_size_slot($a0) + move $v0 $a0 + j split_block_end + +split_block_same_size: + move $v0 $a0 + j split_block_end + +split_block_error_small: + j split_block_end + +split_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $a0 8($sp) + lw $a1 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Best Fit strategy is used to select the block # +# Args: # +# $a0 size to alloc # +# Return: # +# $v0 address of allocated block # +# Summary: # +# Actual block is store in $t0, the size block is checked to know if it is a # +# valid block (a block is valid if its size is larger or equal than the required size), # +# if the block is valid we compare it with the actual best block and keep the shorter block. # +# If there is not a block with the required size, a new block of size # +# max(total_alloc_size, size requested) is requested with sbrk and splitted if necessary # +##################################################################################################### +malloc: + move $v0 $zero + addiu $sp $sp -28 + sw $t1 0($sp) + sw $t0 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + sw $ra 16($sp) + sw $t2 20($sp) + sw $t3 24($sp) + + addiu $t0 $gp free_list + j malloc_loop + +malloc_end: + + move $a0 $v0 + lw $a1 8($sp) # a1 = requested block size + jal split_block + + lw $t1 header_next_slot($v0) + sw $t1 header_next_slot($t3) + + addiu $t1 $gp used_list + lw $a0 header_next_slot($t1) + + sw $a0 header_next_slot($v0) + sw $v0 header_next_slot($t1) + + addiu $v0 $v0 header_size + + lw $t3 24($sp) + lw $t2 20($sp) + lw $ra 16($sp) + lw $a1 12($sp) + lw $a0 8($sp) + lw $t0 4($sp) + lw $t1 0($sp) + addiu $sp $sp 28 + + jr $ra +####################################################################### +# t0 = actual block address # +####################################################################### +malloc_loop: + move $t2 $t0 # save previous block in $t2 (this is usefull when we lw $t3 24($sp)need to alloc the new block) + lw $t0 header_next_slot($t0) # t0 = next block address + beq $t0 $zero malloc_search_end # if t0 == 0 we reach to the free-list end + j malloc_check_valid_block + +####################################################################### +# $v0 = actual selected block address # +####################################################################### +malloc_search_end: + beq $v0 $zero malloc_alloc_new_block # if v0 == 0 a valid block was not found + j malloc_end + +####################################################################### +# t2 = last block of free list # +# a0 = requested block size # +####################################################################### +malloc_alloc_new_block: + li $t1 alloc_size # t1 = standard alloc size + move $t3 $t2 + move $a1 $a0 # a1 = requested block size + move $a0 $t2 # a0 = last block of free list + bge $a1 $t1 malloc_big_block # if the requested size is bigger than the standar alloc size go to malloc_big_block + li $a1 alloc_size # a1 = standard alloc size + jal extend_heap + + j malloc_end + +###################################################################### +# a1 = requested block size # +###################################################################### +malloc_big_block: + #addiu $a1 $a1 header_size # Add header size to alloc size + jal extend_heap + j malloc_end + + + +######################################################################## +# t0 = actual block address # +######################################################################## +malloc_check_valid_block: + lw $t1 header_size_slot($t0) # t1 = size new block + bge $t1 $a0 malloc_valid_block # the actual block have the required size + j malloc_loop + +######################################################################## +# t0 = actual block address # +# t1 = size actual block # +# v0 = actual selected block address(0 if no one have been selected) # +# v1 = actual selected block size # +######################################################################## +malloc_valid_block: + beq $v0 $zero malloc_first_valid_block # this is the first valid block + bge $t1 $v1 malloc_loop # the selected block is smaller than actual block + move $v0 $t0 # selected block address = actual block address + move $v1 $t1 # selected block size = actual block size + move $t3 $t2 + j malloc_loop + + +######################################################################## +# t0 = actual block address # +# t1 = size actual block # +# v0 = actual selected block address(0 if no one have been selected) # +# v1 = actual selected block size # +######################################################################## +malloc_first_valid_block: + move $v0 $t0 # selected block address = actual block address + move $v1 $t1 # selected block size = actual block size + move $t3 $t2 + j malloc_loop + + + + + + + + + + + + + + + + + + + From 0cf73e781a22bc07b73ac7b02c61655db56d48d0 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 10 Sep 2020 21:38:13 -0400 Subject: [PATCH 308/520] [mips] Change how types are represented in mips --- compiled.asm | 447 ++++++++++++++++++++++++++++++++ src/code.cl | 6 + src/core/cmp/mips.py | 23 +- src/core/cmp/trap_handler.asm | 468 ++++++++++++++++++++++++++++++++++ 4 files changed, 940 insertions(+), 4 deletions(-) create mode 100644 compiled.asm create mode 100644 src/core/cmp/trap_handler.asm diff --git a/compiled.asm b/compiled.asm new file mode 100644 index 00000000..2b38e7e6 --- /dev/null +++ b/compiled.asm @@ -0,0 +1,447 @@ + .data +data_1: .asciiz "Object" +data_2: .asciiz "IO" +data_3: .asciiz "String" +data_4: .asciiz "Int" +data_5: .asciiz "Main" +data_6: .asciiz "A" +data_7: .asciiz "Program aborted" +data_8: .asciiz "Hello World!" + +types_names_table: + .word data_1 + .word data_2 + .word data_3 + .word data_4 + .word data_5 + .word data_6 + +proto_table: + .word type_1_proto + .word type_2_proto + .word type_3_proto + .word type_4_proto + .word type_5_proto + .word type_6_proto + +type_1_dispatch: + .word L_1 + .word L_2 + .word L_3 + .word L_4 + +type_1_proto: + .word 0 + .word 4 + .word type_1_dispatch + .word -1 + +type_2_dispatch: + .word L_5 + .word L_6 + .word L_7 + .word L_8 + .word L_9 + +type_2_proto: + .word 1 + .word 4 + .word type_2_dispatch + .word -1 + +type_3_dispatch: + .word L_10 + .word L_11 + .word L_12 + .word L_13 + +type_3_proto: + .word 2 + .word 8 + .word type_3_dispatch + .word 0 + .word -1 + +type_4_dispatch: + .word L_14 + +type_4_proto: + .word 3 + .word 8 + .word type_4_dispatch + .word 0 + .word -1 + +type_5_dispatch: + .word L_2 + .word L_3 + .word L_4 + .word L_6 + .word L_7 + .word L_8 + .word L_9 + .word L_15 + +type_5_proto: + .word 4 + .word 4 + .word type_5_dispatch + .word -1 + +type_6_dispatch: + .word L_2 + .word L_3 + .word L_4 + +type_6_proto: + .word 5 + .word 16 + .word type_6_dispatch + .word 0 + .word 0 + .word 0 + .word -1 + .text + .globl main +main: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -8 + li $v0, 9 + li $a0, 4 + syscall + la $t0, type_5 + sw $t0, 0($v0) + sw $v0, -12($fp) + lw $t0, -12($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_15 + sw $v0, -8($fp) + addi $sp, $sp, 4 + li $v0, 0 + addi $sp, $sp, 8 + lw $fp, 0($sp) + addi $sp, $sp, 4 + li $v0, 10 + syscall +L_1: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + li $v0, 9 + li $a0, 4 + syscall + la $t0, type_1 + sw $t0, 0($v0) + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_2: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_3: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -8 + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, 0($fp) + lw $t1, 0($t1) + lw $t1, 0($t1) + sw $t1, -8($fp) + lw $t1, -8($fp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + jal L_10 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $v0, -12($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 8 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_4: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_5: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + li $v0, 9 + li $a0, 4 + syscall + la $t1, type_1 + sw $t1, 0($v0) + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_6: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, 0($fp) + lw $t1, 4($t1) + sw $t1, -8($fp) + li $v0, 4 + lw $a0, -8($fp) + syscall + lw $v0, 4($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_7: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, 0($fp) + lw $t1, 4($t1) + sw $t1, -8($fp) + li $v0, 1 + lw $a0, -8($fp) + syscall + lw $v0, 4($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_8: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -8 + addi $sp, $sp, 8 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_9: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -8 + addi $sp, $sp, 8 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_10: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t2, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + li $v0, 9 + li $a0, 8 + syscall + la $t1, type_3 + sw $t1, 0($v0) + sw $v0, -8($fp) + lw $t2, -8($fp) + lw $t1, 0($fp) + sw $t1, 4($t2) + lw $v0, -8($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_11: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_12: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_13: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_14: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t2, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + li $v0, 9 + li $a0, 8 + syscall + la $t1, type_4 + sw $t1, 0($v0) + sw $v0, -8($fp) + lw $t2, -8($fp) + lw $t1, 0($fp) + sw $t1, 4($t2) + lw $v0, -8($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_15: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -16 + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + la $t1, data_8 + 0 + sw $t1, -12($fp) + lw $t1, -12($fp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + jal L_10 + sw $v0, -16($fp) + addi $sp, $sp, 4 + lw $t1, -16($fp) + sw $t1, -8($fp) + lw $t1, 0($fp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, -8($fp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + jal L_6 + sw $v0, -20($fp) + addi $sp, $sp, 8 + lw $v0, -20($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 16 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_16: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + li $v0, 9 + li $a0, 4 + syscall + la $t1, type_5 + sw $t1, 0($v0) + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_17: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra \ No newline at end of file diff --git a/src/code.cl b/src/code.cl index 493a00f9..d82714fc 100644 --- a/src/code.cl +++ b/src/code.cl @@ -10,4 +10,10 @@ class Main inherits IO { ) fi }; +}; + +class A{ + X: Int; + Y: Int; + Z: Int; }; \ No newline at end of file diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index bd1b0c5c..e1e0cd78 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -5,6 +5,7 @@ RESGISTER_SIZE = 4 REGISTER_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7','t8', 't9'] ARG_REGISTERS_NAMES = ['a0', 'a1', 'a2', 'a3'] +OBJECT_MARK = -1 INSTANCE_METADATA_SIZE = 4 @@ -30,6 +31,8 @@ class ProgramNode(Node): def __init__(self, data, types, functions): self._data = data self._types = types + for i, t in enumerate(self._types): + t.index = i self._functions = functions @property @@ -262,10 +265,16 @@ def visit(self, node): data_section_header = "\t.data" static_strings = '\n'.join([self.visit(string_const) for string_const in node.data]) - types = "\n".join([self.visit(tp) for tp in node.types]) + + names_table = "types_names_table:\n" + "\n".join([f"\t.word\t{tp.string_name_label}" for tp in node.types]) + proto_table = "proto_table:\n" + "\n".join([f"\t.word\t{tp.label}_proto" for tp in node.types]) + + + + types = "\n\n".join([self.visit(tp) for tp in node.types]) code = "\n".join([self.visit(func) for func in node.functions]) - return f'{data_section_header}\n{static_strings}\n{types}\n\t.text\n\t.globl main\n{code}' + return f'{data_section_header}\n{static_strings}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\t.text\n\t.globl main\n{code}' @visitor.when(StringConst) def visit(self, node): @@ -273,8 +282,14 @@ def visit(self, node): @visitor.when(MIPSType) def visit(self, node): - methods = " ".join(node.methods) - return f'{node.label}:\n\t.word {node.string_name_label} {node.size} {methods}' + methods = "\n".join([f"\t.word\t {v}" for v in node.methods.values()]) + dispatch_table = f"{node.label}_dispatch:\n{methods}" + proto_begin = f"{node.label}_proto:\n\t.word\t{node.index}\n\t.word\t{node.size}\n\t.word\t{node.label}_dispatch" + proto_attr = "\n".join(['\t.word\t0' for _ in node.attributes]) + proto_end = f"\t.word\t{OBJECT_MARK}" + proto = f"{proto_begin}\n{proto_attr}\n{proto_end}" if proto_attr != "" else f"{proto_begin}\n{proto_end}" + + return f'{dispatch_table}\n\n{proto}' @visitor.when(SyscallNode) def visit(self, node): diff --git a/src/core/cmp/trap_handler.asm b/src/core/cmp/trap_handler.asm new file mode 100644 index 00000000..bc6126f1 --- /dev/null +++ b/src/core/cmp/trap_handler.asm @@ -0,0 +1,468 @@ + + + + .text + +header_size = 12 #in bytes +header_size_slot = 0 +header_next_slot = 4 +header_reachable_slot = 8 +alloc_size = 2048 +total_alloc_size = alloc_size + header_size +neg_header_size = 0-header_size +free_list = 0 +used_list = header_size +state_size = 4 +stack_base = -4 +init_alloc_size = (header_size*2) + state_size + + + .globl main +main: + jal mem_manager_init + + li $v0 10 + syscall + + +##################################################################################################### +# Initialize memory manager # +# Args: # +# # +# Return: # +# # +# Summary: # +# The initial blocks for Free-List and Used-List are created. # +# The $gp is set to use as reference when initial blocks or values related to memory manager # +# state are needed. # +# A block of size alloc_size is created an added to Free-List # +##################################################################################################### +mem_manager_init: + addiu $sp $sp -16 + sw $v0 0($sp) + sw $a0 4($sp) + sw $a1 8($sp) + sw $ra 12($sp) + + + li $v0 9 + li $a0 init_alloc_size + syscall #Creating free-list start point + move $gp $v0 + addiu $gp $gp state_size + + sw $zero header_size_slot($gp) #The free-list start with a block without space, just header, that will always be there. + sw $zero header_next_slot($gp) + sw $zero header_reachable_slot($gp) + + move $a0 $gp + li $a1 alloc_size + jal extend_heap + + addiu $a0 $a0 header_size + sw $zero header_size_slot($a0) #The used-list start with a block without space, just header, that will always be there. + sw $zero header_next_slot($a0) + sw $zero header_reachable_slot($a0) + + sw $sp stack_base($gp) + + lw $v0 0($sp) + lw $a0 4($sp) + lw $a1 8($sp) + lw $ra 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Free a block previously allocated # +# Args: # +# $a0 Block to free address # +# Return: # +# # +# Summary: # +# Remove the block from the used-list and add it to the free-list # +##################################################################################################### +free_block: + addiu $sp $sp -28 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $a0 12($sp) + sw $ra 16($sp) + sw $t3 20($sp) + sw $t4 24($sp) + + move $t0 $a0 + + addiu $t1 $gp free_list # Store in $t1 the initial block of the free-list + + addiu $t3 $gp used_list # Store in $t3 the initial block of the used-list + +free_block_loop_used_list: # Iterate througth the used-list until find the block + lw $t4 header_next_slot($t3) + beq $t4 $t0 free_block_loop_free_list + move $t3 $t4 + j free_block_loop_used_list + + +free_block_loop_free_list: # Iterate througth the free-list to find the antecesor of the block in the free-list + lw $t2 header_next_slot($t1) + beq $t2 $zero free_block_founded_prev + bge $t2 $t0 free_block_founded_prev + move $t1 $t2 + j free_block_loop_free_list + +free_block_founded_prev: + # Remove the block from the used-list + lw $t4 header_next_slot($t0) + sw $t4 header_next_slot($t3) + + # Add the block to the free-list + sw $t2 header_next_slot($t0) + sw $t0 header_next_slot($t1) + +free_block_end: + + # Try to merge the list where the new block was added + move $a0 $t0 + jal expand_block + move $a0 $t1 + jal expand_block + + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $a0 12($sp) + lw $ra 16($sp) + lw $t3 20($sp) + lw $t4 24($sp) + addiu $sp $sp 28 + + jr $ra + + +##################################################################################################### +# Merge two continuos blocks of the free-list # +# Args: # +# $a0 First of the two blocks to merge # +# Return: # +# # +# Summary: # +# Check if a block can be merged with its sucesor in the free list # +##################################################################################################### +expand_block: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + + + addiu $t0 $gp free_list # $t0 = the initial block of the free-list + + beq $t0 $a0 expand_block_end # The initial block can't be expanded, the initial block always will have size 0 + + move $t0 $a0 + + # Check if the block and its sucesor in the free list are contiguous in memory + lw $t1 header_next_slot($t0) + lw $t2 header_size_slot($t0) + move $t3 $t2 + addiu $t2 $t2 header_size + addu $t2 $t2 $t0 + beq $t2 $t1 expand_block_expand + j expand_block_end + +expand_block_expand: #Increment the size of the first block and update next field + lw $t2 header_size_slot($t1) + addi $t2 $t2 header_size + add $t2 $t2 $t3 + sw $t2 header_size_slot($t0) + lw $t1 header_next_slot($t1) + sw $t1 header_next_slot($t0) + +expand_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Allocate more memory for the process and add it to the free-list # +# Args: # +# $a0 Last block of the free-list # +# $a1 Memory amount to alloc # +# Return: # +# # +# Summary: # +# More memory is allocated and add it to the free-list as a block. # +##################################################################################################### +extend_heap: + addiu $sp $sp -12 + sw $a0 0($sp) + sw $a1 4($sp) + sw $t0 8($sp) + + # Increase the amount of memory by header_size to create a block with that size + li $v0 9 + addiu $a0 $a1 header_size + syscall + + # Set values of the block_header + move $t0 $a1 + sw $t0 header_size_slot($v0) + sw $zero header_next_slot($v0) + sw $zero header_reachable_slot($v0) + + # Add block to the end of the free-list + lw $t0, 0($sp) + sw $v0 header_next_slot($t0) + + move $a0 $t0 + lw $a1 4($sp) + lw $t0 8($sp) + addiu $sp $sp 12 + + jr $ra + + + +##################################################################################################### +# Split a block into two blocks, one of the requested size and the other with the rest. # +# Args: # +# $a0 Address of the block to split # +# $a1 Size requested for one block # +# Return: # +# # +# Summary: # +# The block is splitted into two blocks if the size allow it. # +##################################################################################################### +split_block: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + + # Check if the block can be splitted in two blocks, one of the requested size + lw $t0 header_size_slot($a0) + bgt $a1 $t0 split_block_error_small + + # Check if after a split the block there is enough space to create another block, if there is not do not split + sub $t0 $t0 $a1 + li $t1 header_size + ble $t0 $t1 split_block_same_size + + # Compute the address of the second block + addu $t0 $a0 $a1 + addiu $t0 $t0 header_size + + #Update headers of the two blocks + lw $t1 header_next_slot($a0) + sw $t1 header_next_slot($t0) + sw $t0 header_next_slot($a0) + + lw $t1 header_size_slot($a0) #update sizes + sub $t1 $t1 $a1 + + addi $t1 $t1 neg_header_size + sw $t1 header_size_slot($t0) + sw $a1 header_size_slot($a0) + move $v0 $a0 + j split_block_end + +split_block_same_size: + move $v0 $a0 + j split_block_end + +split_block_error_small: + j split_block_end + +split_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $a0 8($sp) + lw $a1 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Best Fit strategy is used to select the block # +# Args: # +# $a0 size to alloc # +# Return: # +# $v0 address of allocated block # +# Summary: # +# Actual block is store in $t0, the size block is checked to know if it is a # +# valid block (a block is valid if its size is larger or equal than the required size), # +# if the block is valid we compare it with the actual best block and keep the shorter block. # +# If there is not a block with the required size, a new block of size # +# max(total_alloc_size, size requested) is requested with sbrk and splitted if necessary # +##################################################################################################### +malloc: + move $v0 $zero + addiu $sp $sp -28 + sw $t1 0($sp) + sw $t0 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + sw $ra 16($sp) + sw $t2 20($sp) + sw $t3 24($sp) + + addiu $t0 $gp free_list + j malloc_loop + +malloc_end: + + move $a0 $v0 + lw $a1 8($sp) # a1 = requested block size + jal split_block + + lw $t1 header_next_slot($v0) + sw $t1 header_next_slot($t3) + + addiu $t1 $gp used_list + lw $a0 header_next_slot($t1) + + sw $a0 header_next_slot($v0) + sw $v0 header_next_slot($t1) + + addiu $v0 $v0 header_size + + lw $t3 24($sp) + lw $t2 20($sp) + lw $ra 16($sp) + lw $a1 12($sp) + lw $a0 8($sp) + lw $t0 4($sp) + lw $t1 0($sp) + addiu $sp $sp 28 + + jr $ra +####################################################################### +# t0 = actual block address # +####################################################################### +malloc_loop: + move $t2 $t0 # save previous block in $t2 (this is usefull when we lw $t3 24($sp)need to alloc the new block) + lw $t0 header_next_slot($t0) # t0 = next block address + beq $t0 $zero malloc_search_end # if t0 == 0 we reach to the free-list end + j malloc_check_valid_block + +####################################################################### +# $v0 = actual selected block address # +####################################################################### +malloc_search_end: + beq $v0 $zero malloc_alloc_new_block # if v0 == 0 a valid block was not found + j malloc_end + +####################################################################### +# t2 = last block of free list # +# a0 = requested block size # +####################################################################### +malloc_alloc_new_block: + li $t1 alloc_size # t1 = standard alloc size + move $t3 $t2 + move $a1 $a0 # a1 = requested block size + move $a0 $t2 # a0 = last block of free list + bge $a1 $t1 malloc_big_block # if the requested size is bigger than the standar alloc size go to malloc_big_block + li $a1 alloc_size # a1 = standard alloc size + jal extend_heap + + j malloc_end + +###################################################################### +# a1 = requested block size # +###################################################################### +malloc_big_block: + #addiu $a1 $a1 header_size # Add header size to alloc size + jal extend_heap + j malloc_end + + + +######################################################################## +# t0 = actual block address # +######################################################################## +malloc_check_valid_block: + lw $t1 header_size_slot($t0) # t1 = size new block + bge $t1 $a0 malloc_valid_block # the actual block have the required size + j malloc_loop + +######################################################################## +# t0 = actual block address # +# t1 = size actual block # +# v0 = actual selected block address(0 if no one have been selected) # +# v1 = actual selected block size # +######################################################################## +malloc_valid_block: + beq $v0 $zero malloc_first_valid_block # this is the first valid block + bge $t1 $v1 malloc_loop # the selected block is smaller than actual block + move $v0 $t0 # selected block address = actual block address + move $v1 $t1 # selected block size = actual block size + move $t3 $t2 + j malloc_loop + + +######################################################################## +# t0 = actual block address # +# t1 = size actual block # +# v0 = actual selected block address(0 if no one have been selected) # +# v1 = actual selected block size # +######################################################################## +malloc_first_valid_block: + move $v0 $t0 # selected block address = actual block address + move $v1 $t1 # selected block size = actual block size + move $t3 $t2 + j malloc_loop + + +gc_collect: + addiu $sp $sp + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + + li $t3 1 + addiu $t0 $sp + addiu $t1 $gp stack_base + +gc_collect_loop: + addiu $t0 $t0 4 + beq $t0 $t1 gc_collect_loop_end + j gc_collect_check_if_is_object + +gc_collect_check_if_is_object: + lw $t2 0($t0) + #Check if is object, if not object return to loop + + sw $t3 header_reachable_slot($t2) + + + + + + + + + + + + + + + + + + + + From 2c932063503b6f7e883b25a140d46952751e7dd4 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 10 Sep 2020 21:45:22 -0400 Subject: [PATCH 309/520] [mips] Add ShiftLeftLogicalNode --- src/core/cmp/mips.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index e1e0cd78..de1b2a37 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -154,6 +154,13 @@ def __init__(self, dest, src, value): self.src = src self.value = value +class ShiftLeftLogical(InstructionNode): + def __init__(self, dest, src, bits): + self.dest = dest + self.src = src + self.bits = bits + + class MIPSType: def __init__(self, label, name_addr, attributes, methods): From 0e01f9119bd81cf2d5ceaf7553b4793f2c7acda7 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 10 Sep 2020 21:47:21 -0400 Subject: [PATCH 310/520] [mips] Add case ShiftLeftLogical to visit method of PrintVisitor --- src/core/cmp/mips.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index de1b2a37..28191025 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -154,7 +154,7 @@ def __init__(self, dest, src, value): self.src = src self.value = value -class ShiftLeftLogical(InstructionNode): +class ShiftLeftLogicalNode(InstructionNode): def __init__(self, dest, src, bits): self.dest = dest self.src = src @@ -348,4 +348,8 @@ def visit(self, node): @visitor.when(MoveNode) def visit(self, node): - return f'move {self.visit(node.reg1)} {self.visit(node.reg2 )}' \ No newline at end of file + return f'move {self.visit(node.reg1)} {self.visit(node.reg2 )}' + + @visitor.when(ShiftLeftLogicalNode) + def visit(self, node): + return f"sll {self.visit(node.dest)} {self.visit(node.src)} {node.bits}" \ No newline at end of file From 95e6613230afedaad687577e13514c936261008f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 10 Sep 2020 22:09:52 -0400 Subject: [PATCH 311/520] [mips] Add AddInmediateUnsignedNode clase and its case to visit method of PrintVisitor --- src/core/cmp/mips.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 28191025..46707453 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -10,7 +10,8 @@ INSTANCE_METADATA_SIZE = 4 - +TYPENAMES_TABLE_LABEL = "type_name_table" +PROTO_TABLE_LABEL = "proto_table" class Register(): def __init__(self, name): @@ -153,6 +154,12 @@ def __init__(self, dest, src, value): self.dest = dest self.src = src self.value = value + +class AddInmediateUnsignedNode(InstructionNode): + def __init__(self, dest, src, value): + self.dest = dest + self.src = src + self.value = value class ShiftLeftLogicalNode(InstructionNode): def __init__(self, dest, src, bits): @@ -273,8 +280,8 @@ def visit(self, node): static_strings = '\n'.join([self.visit(string_const) for string_const in node.data]) - names_table = "types_names_table:\n" + "\n".join([f"\t.word\t{tp.string_name_label}" for tp in node.types]) - proto_table = "proto_table:\n" + "\n".join([f"\t.word\t{tp.label}_proto" for tp in node.types]) + names_table = f"{TYPENAMES_TABLE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.string_name_label}" for tp in node.types]) + proto_table = f"{PROTO_TABLE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.label}_proto" for tp in node.types]) @@ -352,4 +359,8 @@ def visit(self, node): @visitor.when(ShiftLeftLogicalNode) def visit(self, node): - return f"sll {self.visit(node.dest)} {self.visit(node.src)} {node.bits}" \ No newline at end of file + return f"sll {self.visit(node.dest)} {self.visit(node.src)} {node.bits}" + + @visitor.when(AddInmediateUnsignedNode) + def visit(self, node): + return f"addiu {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.value)}"" \ No newline at end of file From 5fc4e0f605a30a680d8b3530cc05061b841bbb31 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 10 Sep 2020 22:12:15 -0400 Subject: [PATCH 312/520] [mips] Add AddUnsignedNode clase and its case to visit method of PrintVisitor --- src/core/cmp/mips.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 46707453..f4bc9c2e 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -161,6 +161,12 @@ def __init__(self, dest, src, value): self.src = src self.value = value +class AddUnsignedNode(InstructionNode): + def __init__(self, dest, sum1, sum2): + self.dest = dest + self.sum1 = sum1 + self.sum2 = sum2 + class ShiftLeftLogicalNode(InstructionNode): def __init__(self, dest, src, bits): self.dest = dest @@ -363,4 +369,8 @@ def visit(self, node): @visitor.when(AddInmediateUnsignedNode) def visit(self, node): - return f"addiu {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.value)}"" \ No newline at end of file + return f"addiu {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.value)}"" + + @visitor.when(AddUnsignedNode) + def visit(self, node): + return f"addu {self.visit(node.dest)} {self.visit(node.sum1)} {self.visit(node.sum2)}" \ No newline at end of file From 3b638ea8a8a5551d85486c7aa94448cba7cfb7ba Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 10 Sep 2020 22:15:00 -0400 Subject: [PATCH 313/520] [mips] Change how cil.TypeNameNode is converted to mips --- src/core/cmp/cil_to_mips.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 34a1762a..1a24a701 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -270,6 +270,7 @@ def visit(self, node): @visitor.when(cil.AllocateNode) def visit(self, node): + #This have to change after GC be implemented #TODO Save $a0 register if is beign used object_size = self._types[node.type].size object_label = self._types[node.type].label @@ -343,16 +344,20 @@ def visit(self, node): def visit(self, node): instructions = [] reg = self.get_free_reg() + reg2 = self.get_free_reg() src_location = self.get_var_location(node.source) dst_location = self.get_var_location(node.dest) instructions.append(mips.LoadWordNode(reg, src_location)) instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) - instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + instructions.append(mips.ShiftLeftLogicalNode(reg, reg, 2)) + instructions.append(mips.LoadAddressNode(reg2, mips.TYPENAMES_TABLE_LABEL)) + instructions.append(mips.AddUnsignedNode(reg, reg, reg2)) instructions.append(mips.StoreWordNode(reg, dst_location)) self.free_reg(reg) + self.free_reg(reg2) return instructions From 4ddd55b6d6d40868a3862f0ad7355b9daf53b90c Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 11 Sep 2020 20:14:41 -0400 Subject: [PATCH 314/520] [cil] - Add static type of caller to `DynamicCallNode` --- src/core/cmp/cil.py | 11 ++++++----- src/core/cmp/cool_to_cil.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 2f963013..4d6b44bd 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -73,18 +73,18 @@ class EqualNode(ArithmeticNode): pass class GetAttribNode(InstructionNode): - def __init__(self, dest, obj, attr, xtype): + def __init__(self, dest, obj, attr, computed_type): self.dest = dest self.obj = obj self.attr = attr - self.xtype = xtype + self.computed_type = computed_type class SetAttribNode(InstructionNode): - def __init__(self, obj, attr, value, xtype): + def __init__(self, obj, attr, value, computed_type): self.obj = obj self.attr = attr self.value = value - self.xtype = xtype + self.computed_type = computed_type class GetIndexNode(InstructionNode): pass @@ -124,10 +124,11 @@ def __init__(self, function, dest): self.dest = dest class DynamicCallNode(InstructionNode): - def __init__(self, xtype, method, dest): + def __init__(self, xtype, method, dest, computed_type): self.type = xtype self.method = method self.dest = dest + self.computed_type = computed_type class ArgNode(InstructionNode): def __init__(self, name): diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 6ed255df..59205a7b 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -771,7 +771,7 @@ def visit(self, node, scope): #Call of type .(,...,) type_of_node = self.register_local(VariableInfo(f'{node.id}_type', None)) self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) - self.register_instruction(cil.DynamicCallNode(type_of_node, node.id, result)) + self.register_instruction(cil.DynamicCallNode(type_of_node, node.id, result, node.obj.computed_type)) scope.ret_expr = result From 42129e2bcfb2998296878fe8da0859e833a6e27d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 14 Sep 2020 23:42:15 -0400 Subject: [PATCH 315/520] [builder] - Funcion parameters must not be `SELF_TYPE` --- src/core/cmp/visitors.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index f01234f8..394cb997 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -13,6 +13,7 @@ VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined.' INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' +INVALID_PARAMETER = 'Formal parameter "%s" cannot have type SELF_TYPE.' ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] @@ -309,10 +310,14 @@ def visit(self, node): for i, arg in enumerate(node.params): idx, typex = arg try: + assert typex != ST arg_type = self.context.get_type(typex) except SemanticError as ex: self.errors.append((ex.text, node.params[i].ttype)) arg_type = ErrorType() + except AssertionError: + self.errors.append((INVALID_PARAMETER % (idx), node.params[i].ttype)) + arg_type = ErrorType() arg_names.append(idx) arg_types.append(arg_type) From 2bb681e9079302f372739c7152fb278347c82d1d Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 14 Sep 2020 23:58:22 -0400 Subject: [PATCH 316/520] [checker] - Branch type must not be `SELF_TYPE` --- src/core/cmp/visitors.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 394cb997..1da58156 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -14,6 +14,7 @@ INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' INVALID_PARAMETER = 'Formal parameter "%s" cannot have type SELF_TYPE.' +INVALID_BRANCH = 'Identifier "%s" declared with type SELF_TYPE in case branch.' ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] @@ -495,10 +496,14 @@ def visit(self, node, scope): def visit(self, node, scope): node.scope = scope try: + assert node.type != ST branch_type = self.context.get_type(node.type) except SemanticError as ex: self.errors.append((ex.text, node.ttype)) branch_type = ErrorType() + except AssertionError: + self.errors.append((INVALID_BRANCH % node.id, node.ttype)) + branch_type = ErrorType() node.branch_type = branch_type var = scope.define_variable(node.id, branch_type) From 1293c773046b2a94e31f4d78d009062d1b56601c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 12:12:50 -0400 Subject: [PATCH 317/520] [semantic] - Add a class for manage `SELF_TYPE` --- src/core/cmp/semantic.py | 19 +++++++++++++++++++ src/core/cmp/visitors.py | 15 ++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 48334b7b..56eebf19 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -182,6 +182,25 @@ def __init__(self): def __eq__(self, other): return other.name == self.name or isinstance(other, IOType) +class SelfType(Type): + def __init__(self, fixed=None): + Type.__init__(self, 'SELF_TYPE') + self.fixed = fixed + + def fixed_to(self, fixed): + return SelfType(fixed) + + def get_method(self, name): + return self.fixed.get_method(name) + + def get_attribute(self, name): + return self.fixed.get_attribute(name) + + def conforms_to(self, other): + return Type.conforms_to(self, other) or self.fixed is not None and self.fixed.conforms_to(other) + + def __eq__(self, other): + return other.name == self.name or isinstance(other, SelfType) class Context: def __init__(self): diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 1da58156..aa7e3a27 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -3,7 +3,7 @@ from core.cmp.utils import InferenceSets from core.cmp.semantic import SemanticError from core.cmp.semantic import Attribute, Method, Type -from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType, AutoType +from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType, AutoType, SelfType from core.cmp.semantic import Context, Scope WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' @@ -19,7 +19,7 @@ ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] built_in_types = [ 'Int', 'String', 'Bool', 'Object', 'IO', 'SELF_TYPE', 'AUTO_TYPE'] -INT, STRING, BOOL, OBJ = None, None, None, None +INT, STRING, BOOL, OBJ, SELF_TYPE = None, None, None, None, None def define_built_in_types(context): obj = context.create_type('Object') @@ -31,7 +31,7 @@ def define_built_in_types(context): b.set_parent(obj) io = context.append_type(IOType()) io.set_parent(obj) - st = context.create_type('SELF_TYPE') + st = context.append_type(SelfType()) context.append_type(AutoType()) obj.define_method('abort', [], [], obj) @@ -47,11 +47,12 @@ def define_built_in_types(context): s.define_method('concat', ['s'], [s], s) s.define_method('substr', ['i', 'l'], [i, i], s) - global INT, STRING, BOOL, OBJ - INT, STRING, BOOL, OBJ = i, s, b, obj + global INT, STRING, BOOL, OBJ, SELF_TYPE + INT, STRING, BOOL, OBJ, SELF_TYPE = i, s, b, obj, st -def fixed_type(type1, type2): - return type1 if type1.name != ST else type2 +def fixed_type(cur_type): + try: return cur_type.fixed + except AttributeError: return cur_type def update_condition(target, value): c1 = isinstance(target, AutoType) From 1b5c3f887a128bf94e756e6ea15afbb50b16c4c9 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 12:17:04 -0400 Subject: [PATCH 318/520] [checker] - Define types as `SELF_TYPE` instead of use the `current_class` --- src/core/cmp/visitors.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index aa7e3a27..4ebba5c9 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -405,11 +405,14 @@ def visit(self, node, scope=None): def visit(self, node, scope): self.current_type = self.context.get_type(node.id) - scope.define_variable('self', self.current_type) + scope.define_variable('self', SELF_TYPE.fixed_to(self.current_type)) cur_type = self.current_type.parent while True: for attr in cur_type.attributes: - var = scope.define_variable(attr.name, attr.type) + vtype = attr.type + if vtype.conforms_to(SELF_TYPE): + vtype = vtype.fixed_to(self.current_type) + var = scope.define_variable(attr.name, vtype) var.node = attr.node if not cur_type.parent: break @@ -421,7 +424,10 @@ def visit(self, node, scope): if isinstance(feature, AttrDeclarationNode): self.visit(feature, scope) if not scope.is_defined(feature.id): - var = scope.define_variable(feature.id, cur_type.attributes[count].type) + vtype = cur_type.attributes[count].type + if vtype.conforms_to(SELF_TYPE): + vtype = vtype.fixed_to(self.current_type) + var = scope.define_variable(feature.id, vtype) var.node = cur_type.attributes[count].node count += 1 else: @@ -527,6 +533,8 @@ def visit(self, node, scope): def visit(self, node, scope): try: node_type = self.context.get_type(node.type) + if node_type.name == ST: + node_type = node_type.fixed_to(self.current_type) except SemanticError as ex: self.errors.append((ex.text, node.ttype)) node_type = ErrorType() @@ -632,7 +640,7 @@ def visit(self, node, scope): @visitor.when(MemberCallNode) def visit(self, node, scope): - obj_type = self.current_type + obj_type = SELF_TYPE.fixed_to(self.current_type) error = False @@ -706,8 +714,9 @@ def visit(self, node, scope): @visitor.when(NewNode) def visit(self, node, scope): try: - raw_type = self.context.get_type(node.type) - node_type = fixed_type(raw_type, self.current_type) + node_type = self.context.get_type(node.type) + if node.type == ST: + node_type = node_type.fixed_to(self.current_type) except SemanticError as ex: self.errors.append((ex.text, node.ttype)) node_type = ErrorType() From f97650562a316e3bf59828ba78b84c36f1d5cb64 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 12:20:43 -0400 Subject: [PATCH 319/520] [checker] - Update and remove unnecessary `fixed_type` calls --- src/core/cmp/visitors.py | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 4ebba5c9..2288791a 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -443,7 +443,7 @@ def visit(self, node, scope): self.visit(node.expr, scope) expr_type = node.expr.computed_type - real_type = fixed_type(node.attr_type, self.current_type) + real_type = node.attr_type node.info = [expr_type, real_type] if not expr_type.conforms_to(real_type): @@ -460,7 +460,7 @@ def visit(self, node, scope): self.visit(node.body, scope) body_type = node.body.computed_type - method_rtn_type = fixed_type(node.ret_type, self.current_type) + method_rtn_type = node.ret_type node.info = [body_type, method_rtn_type] if not body_type.conforms_to(method_rtn_type): @@ -478,8 +478,7 @@ def visit(self, node, scope): var = scope.find_variable(node.id) if var.name == 'self': raise SemanticError(SELF_IS_READONLY) - var_type = fixed_type(var.type, self.current_type) - if not node_type.conforms_to(var_type): + if not node_type.conforms_to(var.type): raise SemanticError(INCOMPATIBLE_TYPES % (node_type.name, var.type.name)) except SemanticError as ex: self.errors.append((ex.text, node.tid)) @@ -550,11 +549,10 @@ def visit(self, node, scope): if node.expr: self.visit(node.expr, scope) expr_type = node.expr.computed_type - real_type = fixed_type(node_type, self.current_type) - node.info = [expr_type, real_type] + node.info = [expr_type, node_type] - if not expr_type.conforms_to(real_type): - self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, real_type.name), node.arrow)) + if not expr_type.conforms_to(node_type): + self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, node_type.name), node.arrow)) @visitor.when(IfThenElseNode) def visit(self, node, scope): @@ -619,16 +617,15 @@ def visit(self, node, scope): node.obj_method = obj_method if len(node.args) == len(obj_method.param_types): for idx, (arg, param_type) in enumerate(zip(arg_types, obj_method.param_types)): - real_type = fixed_type(param_type, obj_type) - real_types.append(real_type) + real_types.append(param_type) - if not arg.conforms_to(real_type): - self.errors.append((INCOMPATIBLE_TYPES % (arg.name, real_type.name + f" in the argument #{idx} of {node.id}"), token)) + if not arg.conforms_to(param_type): + self.errors.append((INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}"), token)) error = True else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') assert not error - node_type = fixed_type(obj_method.return_type, obj_type) + node_type = obj_method.return_type except SemanticError as ex: self.errors.append((ex.text, token)) node_type = ErrorType() @@ -655,16 +652,15 @@ def visit(self, node, scope): node.obj_method = obj_method if len(node.args) == len(obj_method.param_types): for arg, param_type in zip(arg_types, obj_method.param_types): - real_type = fixed_type(param_type, self.current_type) - real_types.append(real_type) + real_types.append(param_type) - if not arg.conforms_to(real_type): - self.errors.append((INCOMPATIBLE_TYPES % (arg.name, real_type.name + f" in the argument #{idx} of {node.id}"), token)) + if not arg.conforms_to(param_type): + self.errors.append((INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}"), token)) error = True else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') assert not error - node_type = fixed_type(obj_method.return_type, self.current_type) + node_type = obj_method.return_type except SemanticError as ex: self.errors.append((ex.text, token)) node_type = ErrorType() @@ -703,8 +699,7 @@ def visit(self, node, scope): @visitor.when(IdNode) def visit(self, node, scope): if scope.is_defined(node.lex): - var = scope.find_variable(node.lex) - node_type = fixed_type(var.type, self.current_type) + node_type = scope.find_variable(node.lex).type else: self.errors.append((VARIABLE_NOT_DEFINED % (node.lex), node.token)) node_type = ErrorType() From fc8aa9824e74e3d46f3ae57dc763a900c556ab05 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 12:22:45 -0400 Subject: [PATCH 320/520] [checker] - Return the caller type in function/member calls when the return type is `SELF_TYPE` --- src/core/cmp/visitors.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 2288791a..3913cf72 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -626,6 +626,8 @@ def visit(self, node, scope): raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') assert not error node_type = obj_method.return_type + if node_type.name == ST: + node_type = obj_type except SemanticError as ex: self.errors.append((ex.text, token)) node_type = ErrorType() @@ -661,6 +663,8 @@ def visit(self, node, scope): raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') assert not error node_type = obj_method.return_type + if node_type.name == ST: + node_type = obj_type except SemanticError as ex: self.errors.append((ex.text, token)) node_type = ErrorType() From ec4aec2a6492ad293d39668d66163f0577024608 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 14:38:56 -0400 Subject: [PATCH 321/520] [checker] - Use always the `fixed_type` in the `LCA` computation --- src/core/cmp/visitors.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 3913cf72..b011985a 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -355,6 +355,7 @@ def visit(self, node): def LCA(type_list): counter = {} + type_list = [fixed_type(t) for t in type_list] if any([isinstance(t, AutoType) for t in type_list]): return AutoType() if any([isinstance(t, ErrorType) for t in type_list]): From 97ecbe558f6d5eb4f9ec34ea5097b2c31da39c50 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 14:39:27 -0400 Subject: [PATCH 322/520] [makefile] - Add a rule for save code.cl as a test --- src/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Makefile b/src/Makefile index f355f6d0..583092d9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,6 +7,8 @@ APP_VERSION := v0.1 APP_DESCRIPTION := $(ORG_NAME) - $(PROJECT_NAME)$(APP_VERSION) DEVELOPERS := Lázaro Raúl Iglesias Vera, Miguel Tenorio Potrony, Mauricio Lázaro Perdomo Cortéz COPYRIGHT := Copyright © 2020: $(DEVELOPERS) +TEST_DIR := core/cmp/Stuff/tests/ +TEST := main: ## Compiling the compiler :) ./coolc.sh "code.cl" @@ -26,6 +28,9 @@ coolc: ## Run the code.cl file using coolc @coolc code.cl @spim code.s +save: ## Save the code.cl as a test + @cat code.cl > $(TEST_DIR)$(TEST).cl + help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' From e14ee4a8a8643abc42fd261d0a464a1649859ac7 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 18:46:45 -0400 Subject: [PATCH 323/520] [inferencer] - If an `AUTO_TYPE` conforms to `SELF_TYPE`, `SELF_TYPE` is inferreded --- src/core/cmp/visitors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index b011985a..009ffac4 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -374,6 +374,8 @@ def LCA(type_list): node = node.parent def check_path(D, ans): + if any([(t.name == ST) for t in D]): + return True, SelfType() for t in D: l = [ans, t] lca = LCA(l) @@ -880,7 +882,7 @@ def visit(self, node, scope=None): continue ok, D1 = check_path(sets.D, OBJ) assert ok - if len(sets.S): + if len(sets.S) and not isinstance(D1, SelfType): candidate = LCA(sets.S) assert LCA([candidate, D1]) == D1 D1 = candidate From e8e95cd698af2a114267dc84a9f5bde47784562c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Tue, 15 Sep 2020 18:49:56 -0400 Subject: [PATCH 324/520] [sematic][checker] - Remove the `fixed_to` method of `SelfType` --- src/core/cmp/semantic.py | 3 --- src/core/cmp/visitors.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 56eebf19..583a624f 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -187,9 +187,6 @@ def __init__(self, fixed=None): Type.__init__(self, 'SELF_TYPE') self.fixed = fixed - def fixed_to(self, fixed): - return SelfType(fixed) - def get_method(self, name): return self.fixed.get_method(name) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 009ffac4..5b0c963f 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -408,13 +408,13 @@ def visit(self, node, scope=None): def visit(self, node, scope): self.current_type = self.context.get_type(node.id) - scope.define_variable('self', SELF_TYPE.fixed_to(self.current_type)) + scope.define_variable('self', SelfType(self.current_type)) cur_type = self.current_type.parent while True: for attr in cur_type.attributes: vtype = attr.type - if vtype.conforms_to(SELF_TYPE): - vtype = vtype.fixed_to(self.current_type) + if vtype.name == ST: + vtype = SelfType(self.current_type) var = scope.define_variable(attr.name, vtype) var.node = attr.node if not cur_type.parent: @@ -428,8 +428,8 @@ def visit(self, node, scope): self.visit(feature, scope) if not scope.is_defined(feature.id): vtype = cur_type.attributes[count].type - if vtype.conforms_to(SELF_TYPE): - vtype = vtype.fixed_to(self.current_type) + if vtype.name == ST: + vtype = SelfType(self.current_type) var = scope.define_variable(feature.id, vtype) var.node = cur_type.attributes[count].node count += 1 @@ -536,7 +536,7 @@ def visit(self, node, scope): try: node_type = self.context.get_type(node.type) if node_type.name == ST: - node_type = node_type.fixed_to(self.current_type) + node_type = SelfType(self.current_type) except SemanticError as ex: self.errors.append((ex.text, node.ttype)) node_type = ErrorType() @@ -642,7 +642,7 @@ def visit(self, node, scope): @visitor.when(MemberCallNode) def visit(self, node, scope): - obj_type = SELF_TYPE.fixed_to(self.current_type) + obj_type = SelfType(self.current_type) error = False @@ -718,7 +718,7 @@ def visit(self, node, scope): try: node_type = self.context.get_type(node.type) if node.type == ST: - node_type = node_type.fixed_to(self.current_type) + node_type = SelfType(self.current_type) except SemanticError as ex: self.errors.append((ex.text, node.ttype)) node_type = ErrorType() From a91c4021fc77650381f827d42d7798b64913f014 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 15 Sep 2020 21:29:32 -0400 Subject: [PATCH 325/520] [cil] - Add support for SELF_TYPE --- src/core/cmp/cool_to_cil.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 59205a7b..db7d05e5 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -771,7 +771,10 @@ def visit(self, node, scope): #Call of type .(,...,) type_of_node = self.register_local(VariableInfo(f'{node.id}_type', None)) self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) - self.register_instruction(cil.DynamicCallNode(type_of_node, node.id, result, node.obj.computed_type)) + computed_type = node.obj.computed_type + if computed_type.name == 'SELF_TYPE': + computed_type = computed_type.fixed + self.register_instruction(cil.DynamicCallNode(type_of_node, node.id, result, computed_type.name)) scope.ret_expr = result @@ -804,6 +807,19 @@ def visit(self, node, scope): # node.type -> str ############################### instance = self.define_internal_local() + + if node.type == 'SELF_TYPE': + vtype = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(self.vself, vtype)) + self.register_instruction(cil.AllocateNode(vtype, instance)) + elif node.type == 'Int' or node.type == 'Bool': + self.register_instruction(cil.ArgNode(0)) + elif node.type == 'String': + data_node = [dn for dn in self.dotdata if dn.value == ''][0] + vmsg = self.register_local(VariableInfo('msg', None)) + self.register_instruction(cil.LoadNode(vmsg, data_node)) + self.register_instruction(cil.ArgNode(vmsg)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', node.type), instance)) scope.ret_expr = instance @@ -849,10 +865,10 @@ def visit(self, node, scope): data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] except IndexError: data_node = self.register_data(node.lex) - vname = self.register_local(VariableInfo('msg', None)) - self.register_instruction(cil.LoadNode(vname, data_node)) + vmsg = self.register_local(VariableInfo('msg', None)) instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.LoadNode(vmsg, data_node)) + self.register_instruction(cil.ArgNode(vmsg)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) scope.ret_expr = instance From 4e4f892bb0cec2732926188f220a00c426cba82e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 16 Sep 2020 12:44:31 -0400 Subject: [PATCH 326/520] [verifier] - Add a new visitor This visitor is for checking after the inference process whether all the types are still well formed --- src/core/cmp/visitors.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 5b0c963f..d4907ede 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -1038,3 +1038,43 @@ def visit(self, node, scope): if update_condition(right, left) and left in [INT, BOOL, STRING]: self.update(node.right, scope, left) +# Type Verifier +class TypeVerifier: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + main_token = None + for def_class in node.declarations: + self.visit(def_class) + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + + for feature in node.features: + self.visit(feature) + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + pass + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + try: + m1 = node.method + m2 = self.current_type.parent.get_method(m1.name) + assert m1.return_type == m2.return_type and m1.param_types == m2.param_types + except AttributeError: + pass + except SemanticError: + pass + except AssertionError: + self.errors.append((f'Method "{m1.name}" already defined in {self.current_type.name} with a different signature.', node.tid)) \ No newline at end of file From b2118ca32be9e935904cacb27c5bed65296d69bf Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 16 Sep 2020 12:45:59 -0400 Subject: [PATCH 327/520] [main] - Add the TypeVerifier to the main pipeline --- src/main.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index 3c6d9d48..0a3d50ca 100644 --- a/src/main.py +++ b/src/main.py @@ -63,6 +63,12 @@ def main(args): inferencer.errors.clear() inferencer.visit(ast) errors.extend(inferencer.errors) + + verifier = TypeVerifier(context) + verifier.visit(ast) + for e in verifier.errors: + if not e in errors: + errors.append(e) if errors: for (msg, token) in errors: @@ -78,7 +84,7 @@ def main(args): import argparse parser = argparse.ArgumentParser(description='CoolCompiler pipeline') - parser.add_argument('-f', '--file', type=str, default='code.cl', help='node address') + parser.add_argument('-f', '--file', type=str, default='code.cl', help='file to read') args = parser.parse_args() main(args) From cb27f302be9c9103633a2a53ce5381a15b472d57 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 16 Sep 2020 12:47:11 -0400 Subject: [PATCH 328/520] [checker] - Add a missing line --- src/core/cmp/visitors.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index d4907ede..092228a3 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -479,6 +479,7 @@ def visit(self, node, scope): if not scope.is_defined(node.id): raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) var = scope.find_variable(node.id) + var_type = var.type if var.name == 'self': raise SemanticError(SELF_IS_READONLY) if not node_type.conforms_to(var.type): From e3fbe24316cd346cbdb12f4c60ea6d1aaabc34e6 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 16 Sep 2020 23:18:54 -0400 Subject: [PATCH 329/520] [inferencer] - Avoid executing multiple visits when node is a BinaryNode descendant --- src/core/cmp/visitors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 092228a3..76ef6421 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -868,7 +868,8 @@ def visit(self, node, scope): @visitor.when(Node) def visit(self, node, scope): - super().visit(node, scope) + if not issubclass(node.__class__, BinaryNode): + super().visit(node, scope) @visitor.when(ProgramNode) def visit(self, node, scope=None): From 1cb2e57ff37d3ca3b396e30f121791aebc22db28 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 16 Sep 2020 23:52:56 -0400 Subject: [PATCH 330/520] [checker] - Make class attributes visible within their initialization expression --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 76ef6421..8bc691a0 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -409,7 +409,7 @@ def visit(self, node, scope): self.current_type = self.context.get_type(node.id) scope.define_variable('self', SelfType(self.current_type)) - cur_type = self.current_type.parent + cur_type = self.current_type while True: for attr in cur_type.attributes: vtype = attr.type From bb9d4c9dc4eba340f8bb06e1d788981079363f00 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Wed, 16 Sep 2020 23:54:22 -0400 Subject: [PATCH 331/520] [checker] - Define let attributes after their initialization --- src/core/cmp/visitors.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 8bc691a0..c8c7aa7e 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -544,10 +544,8 @@ def visit(self, node, scope): node.attr_type = node_type node.scope = None - if not scope.is_local(node.id): - var = scope.define_variable(node.id, node_type) - var.node = node - else: + correct_declaration = not scope.is_local(node.id) + if not correct_declaration: self.errors.append((LOCAL_ALREADY_DEFINED % (node.id, self.current_method), node.tid)) if node.expr: @@ -557,6 +555,10 @@ def visit(self, node, scope): if not expr_type.conforms_to(node_type): self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, node_type.name), node.arrow)) + + if correct_declaration: + var = scope.define_variable(node.id, node_type) + var.node = node @visitor.when(IfThenElseNode) def visit(self, node, scope): From 0c11b3b6391459baa83e11e39939c987fb160b50 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 17 Sep 2020 00:06:07 -0400 Subject: [PATCH 332/520] [checker] - Avoid the error report in FunctionCallNode when the caller is ErrorType --- src/core/cmp/visitors.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index c8c7aa7e..743b8d76 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -618,6 +618,7 @@ def visit(self, node, scope): raise SemanticError(INCOMPATIBLE_TYPES % (obj_type.name, node.type)) obj_type = cast_type + assert obj_type token = node.tid obj_method = obj_type.get_method(node.id) node.obj_method = obj_method From 0cf0030e068ae5e0d614ea79b80d12ccb691addd Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 17 Sep 2020 00:19:14 -0400 Subject: [PATCH 333/520] [checker] - Add an error when a case branch is duplicated --- src/core/cmp/visitors.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 743b8d76..790986c5 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -12,9 +12,10 @@ INCOMPATIBLE_TYPES = 'Cannot convert "%s" into "%s".' VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined.' INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' -CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s"' +CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s".' INVALID_PARAMETER = 'Formal parameter "%s" cannot have type SELF_TYPE.' INVALID_BRANCH = 'Identifier "%s" declared with type SELF_TYPE in case branch.' +DUPLICATED_BRANCH = 'Duplicate branch "%s" in case statement.' ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] @@ -496,7 +497,11 @@ def visit(self, node, scope): self.visit(node.expr, scope) types_list = [] + branches = set() for case in node.branches: + if case.type in branches: + self.errors.append((DUPLICATED_BRANCH % (case.type), case.ttype)) + branches.add(case.type) self.visit(case, scope.create_child()) types_list.append(case.computed_type) From 9813f7f91e32f96e24ad9a82ffe5d562735f5f7a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 17 Sep 2020 15:59:22 -0400 Subject: [PATCH 334/520] [mips] Add copy function to mips memory manager lib and start GC_collect function --- .../cmp/{trap.handler => trap_handler.asm} | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) rename src/core/cmp/{trap.handler => trap_handler.asm} (92%) diff --git a/src/core/cmp/trap.handler b/src/core/cmp/trap_handler.asm similarity index 92% rename from src/core/cmp/trap.handler rename to src/core/cmp/trap_handler.asm index e320e9d5..a57bcc7f 100644 --- a/src/core/cmp/trap.handler +++ b/src/core/cmp/trap_handler.asm @@ -13,9 +13,17 @@ neg_header_size = 0-header_size free_list = 0 used_list = header_size state_size = 4 +stack_base = -4 init_alloc_size = (header_size*2) + state_size +object_mark = -1 + .globl main +main: + jal mem_manager_init + + li $v0 10 + syscall ##################################################################################################### @@ -52,10 +60,12 @@ mem_manager_init: li $a1 alloc_size jal extend_heap - addi $a0 $a0 header_size + addiu $a0 $a0 header_size sw $zero header_size_slot($a0) #The used-list start with a block without space, just header, that will always be there. sw $zero header_next_slot($a0) sw $zero header_reachable_slot($a0) + + sw $sp stack_base($gp) lw $v0 0($sp) lw $a0 4($sp) @@ -417,6 +427,79 @@ malloc_first_valid_block: +gc_collect: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + + li $t3 -1 + addiu $t0 $sp 16 + lw $t1 stack_base($gp) + +gc_collect_loop: + addiu $t0 $t0 4 + beq $t0 $t1 gc_collect_loop_end + j gc_collect_check_if_is_object + +gc_collect_check_if_is_object: + lw $t2 0($t0) + blt $t2 $zero j gc_collect_loop + bge $t2 type_number($zero) j gc_collect_loop + lw $t2 4($t0) + addiu $t2 $t2 -1 + ssl $t2 $t2 2 + addu $t2 $t0 $t2 + lw $t2 0($t2) + bne $t2 object_mark($zero) gc_collect_loop + + sw $t3 header_reachable_slot($t2) + + j gc_collect_loop + +gc_collect_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + + addiu $sp $sp 16 + + jr $ra + + + +$a0 address from +$a1 address to +$a2 size +copy: + addiu $sp $sp -16 + sw $a0 0($sp) + sw $a1 4($sp) + sw $a2 8($sp) + sw $t0 12($sp) + +copy_loop: + beq $a2 $zero copy_end + lw $t0 0($a0) + sw $t0 0($a1) + addiu $a0 $a0 4 + addiu $a1 $a1 4 + addi $a2 $a2 -4 + j copy_loop + +copy_end: + lw $a0 0($sp) + lw $a1 4($sp) + lw $a2 8($sp) + lw $t0 12($sp) + addiu $sp $sp 16 + + jr $ra + + + From 158efc45a596119ce7d61b19b2de8623b0aba80d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 17 Sep 2020 16:37:02 -0400 Subject: [PATCH 335/520] [mips] Add check_if_is_object routine --- src/core/cmp/trap_handler.asm | 90 ++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/trap_handler.asm b/src/core/cmp/trap_handler.asm index a57bcc7f..3de688ad 100644 --- a/src/core/cmp/trap_handler.asm +++ b/src/core/cmp/trap_handler.asm @@ -16,6 +16,7 @@ state_size = 4 stack_base = -4 init_alloc_size = (header_size*2) + state_size object_mark = -1 +meta_data_object_size = 4 #in words .globl main @@ -440,7 +441,7 @@ gc_collect: gc_collect_loop: addiu $t0 $t0 4 - beq $t0 $t1 gc_collect_loop_end + beq $t0 $t1 gc_collect_dfs j gc_collect_check_if_is_object gc_collect_check_if_is_object: @@ -458,6 +459,19 @@ gc_collect_check_if_is_object: j gc_collect_loop +gc_collect_dfs: + addiu $t1 $gp used_list + +gc_collect_outer_loop: + lw $t1 header_next_slot($t1) + lw $t2 header_reachable_slot($t1) + beq $t2 $t3 gc_collect_expand + +gc_collect_expand: + lw $t4 4($t1) + addi $t4 $t4 -3 + addiu $t5 $t1 8 + gc_collect_end: lw $t0 0($sp) lw $t1 4($sp) @@ -469,6 +483,25 @@ gc_collect_end: jr $ra +gc_collect_recursive_expand: + addiu $sp $sp -12 + sw $a0 0($sp) + sw $t0 4($sp) + sw $t1 8($sp) + + move $t0 $a0 + + + lw $t1 4($a0) + addi $t1 $t1 -3 + +gc_collect + + + + + + $a0 address from $a1 address to @@ -500,7 +533,62 @@ copy_end: +# $a0 address to check +check_if_is_object: + addiu $sp $sp -20 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $a0 16($sp) + + move $t0 $a0 + + li $v0 9 + move $a0 $zero + syscall + + addiu $t1 $v0 -4 #Last word of heap + + blt $t0 $gp check_if_is_object_not_object + bgt $t0 $t1 check_if_is_object_not_object + lw $t2 0($t0) + blt $t2 $zero check_if_is_object_not_object + bgt $t2 type_number check_if_is_object_not_object + + addiu $t0 $t0 4 + blt $t0 $gp check_if_is_object_not_object + bgt $t0 $t1 check_if_is_object_not_object + lw $t2 0($t0) #Store size in $t2 + + addiu $t0 $t0 8 + + li $t3 meta_data_object_size + sub $t2 $t2 $t3 + sll $t2 $t2 2 + addu $t0 $t0 $t2 + blt $t0 $gp check_if_is_object_not_object + bgt $t0 $t1 check_if_is_object_not_object + lw $t2 0($t0) + bnq $t2 object_mark check_if_is_object_not_object + li $v0 1 + j check_if_is_object_end + + +check_if_is_object_not_object: + li $v0 0 + + +check_if_is_object_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $a0 16($sp) + addiu $sp $sp 20 + + jr $ra From 9b01b7774934166b350e96840b9f1e6136c178be Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 20 Sep 2020 18:06:04 -0400 Subject: [PATCH 336/520] [mips] Add gc_collect routine --- src/core/cmp/trap_handler.asm | 227 ++++++++++++++++++++++++++-------- 1 file changed, 177 insertions(+), 50 deletions(-) diff --git a/src/core/cmp/trap_handler.asm b/src/core/cmp/trap_handler.asm index 3de688ad..89d95c09 100644 --- a/src/core/cmp/trap_handler.asm +++ b/src/core/cmp/trap_handler.asm @@ -1,8 +1,6 @@ - - - .text +type_number= 2 header_size = 12 #in bytes header_size_slot = 0 header_next_slot = 4 @@ -17,14 +15,10 @@ stack_base = -4 init_alloc_size = (header_size*2) + state_size object_mark = -1 meta_data_object_size = 4 #in words +object_expanded = -2 +reachable = 1 - .globl main -main: - jal mem_manager_init - - li $v0 10 - syscall ##################################################################################################### @@ -40,6 +34,7 @@ main: # A block of size alloc_size is created an added to Free-List # ##################################################################################################### mem_manager_init: + addiu $sp $sp -16 sw $v0 0($sp) sw $a0 4($sp) @@ -66,13 +61,15 @@ mem_manager_init: sw $zero header_next_slot($a0) sw $zero header_reachable_slot($a0) - sw $sp stack_base($gp) + lw $v0 0($sp) lw $a0 4($sp) lw $a1 8($sp) lw $ra 12($sp) addiu $sp $sp 16 + + sw $sp stack_base($gp) jr $ra @@ -427,75 +424,191 @@ malloc_first_valid_block: j malloc_loop +#TODO Look for objects in registers +##################################################################################################### +# Remove from used-list the blocks that are not reachables, the root objects are in the stack and # +# registers # +# Args: # +# # +# Return: # +# # +# Summary: # +# First the objects in stack and registers are marked as reachables, after that the objects # +# that are reachables from them are marked as reachable too using a dfs algorithm. When all # +# reachables objects are marked the used-list is scanned and all the objects that are not # +# marked as reachables are released. # +##################################################################################################### gc_collect: - addiu $sp $sp -16 + addiu $sp $sp -24 sw $t0 0($sp) sw $t1 4($sp) sw $t2 8($sp) sw $t3 12($sp) + sw $a0 16($sp) + sw $ra 20($sp) - li $t3 -1 - addiu $t0 $sp 16 - lw $t1 stack_base($gp) + li $t3 reachable # $t3 = reachable value + addiu $t0 $sp 20 # $t0 = the start of the stack without count this function + lw $t1 stack_base($gp) # $t1 = the end of the stack + li $t2 1 +# Go through the stack searching for objects gc_collect_loop: addiu $t0 $t0 4 - beq $t0 $t1 gc_collect_dfs - j gc_collect_check_if_is_object + beq $t0 $t1 gc_collect_dfs # If the end of the stack was reached finish this loop + + lw $a0 0($t0) + jal check_if_is_object + + bne $v0 $t2 gc_collect_loop -gc_collect_check_if_is_object: - lw $t2 0($t0) - blt $t2 $zero j gc_collect_loop - bge $t2 type_number($zero) j gc_collect_loop - lw $t2 4($t0) - addiu $t2 $t2 -1 - ssl $t2 $t2 2 - addu $t2 $t0 $t2 - lw $t2 0($t2) - bne $t2 object_mark($zero) gc_collect_loop - - sw $t3 header_reachable_slot($t2) + addiu $a0 $a0 neg_header_size + sw $t3 header_reachable_slot($a0) j gc_collect_loop gc_collect_dfs: addiu $t1 $gp used_list +# Go through the used-list and try to expand any reachable block gc_collect_outer_loop: lw $t1 header_next_slot($t1) + beq $t1 $zero gc_collect_free lw $t2 header_reachable_slot($t1) - beq $t2 $t3 gc_collect_expand + beq $t2 reachable gc_collect_expand + j gc_collect_outer_loop gc_collect_expand: - lw $t4 4($t1) - addi $t4 $t4 -3 - addiu $t5 $t1 8 + addiu $a0 $t1 header_size # expand an object not a block + jal gc_collect_recursive_expand + j gc_collect_outer_loop + +gc_collect_free: + addiu $t0 $gp used_list + lw $t0 header_next_slot($t0) + +# Go through the used-list and free any unreachable object and set the reachable and expanded field to their default values +gc_collect_free_loop: + beq $t0 $zero gc_collect_end + lw $t1 header_reachable_slot($t0) + bne $t1 reachable gc_collect_free_loop_free + sw $zero header_reachable_slot($t0) + move $a0 $t0 + jal check_if_is_object + beq $v0 $zero j gc_collect_free_loop + li $t1 object_mark + addiu $t2 $t0 header_size + lw $t3 4($t2) + sll $t3 $t3 2 + addu $t2 $t2 $t3 + sw $t1 -4($t2) + lw $t0 header_next_slot($t0) + j gc_collect_free_loop + +gc_collect_free_loop_free: + move $a0 $t0 + lw $t0 header_next_slot($t0) + jal free_block + j gc_collect_free_loop + gc_collect_end: lw $t0 0($sp) lw $t1 4($sp) lw $t2 8($sp) lw $t3 12($sp) - - addiu $sp $sp 16 + lw $a0 16($sp) + lw $ra 20($sp) + addiu $sp $sp 24 jr $ra + + +##################################################################################################### +# Mark the objects that are reachable from the attrs of one object in a recursive way. # +# Args: # +# $a0: Object to expand # +# Return: # +# # +# Summary: # +# The actual object is marked as reachable and expanded to avoid infinite cycles, and this # +# routine is called recursively to expand the objects in the attrs of the actual object. # +##################################################################################################### gc_collect_recursive_expand: - addiu $sp $sp -12 + addiu $sp $sp -16 sw $a0 0($sp) sw $t0 4($sp) sw $t1 8($sp) + sw $ra 12($sp) + + jal check_if_is_object # If is not an object can not be expanded + beq $v0 $zero gc_collect_recursive_expand_end + + lw $t0 4($a0) + sll $t0 $t0 2 + addiu $t0 $t0 -4 + addu $t0 $a0 $t0 + lw $t1 0($t0) # Check if the object was ready expanded to avoid infinite cycles + beq $t1 object_expanded gc_collect_recursive_expand_end + + # Mark the block that contains the object as reachable + li $t1 reachable + addiu $a0 $a0 neg_header_size + sw $t1 header_reachable_slot($a0) + addiu $a0 $a0 header_size - move $t0 $a0 + # Mark the object as expanded + li $t1 object_expanded + sw $t1 0($t0) + lw $t0 0($a0) # $t0 = type of the object + + # int and string types are special cases + la $t1 int_type + lw $t1 0($t1) + beq $t0 $t1 gc_collect_recursive_expand_end + + la $t1 string_type + lw $t1 0($t1) + beq $t0 $t1 gc_collect_recursive_expand_string_object + + lw $t0 4($a0) + li $t1 meta_data_object_size + sub $t0 $t0 $t1 + + addiu $t1 $a0 12 + +# call this routine in every attr of the object +gc_collect_recursive_expand_attr_loop: + beq $t0 $zero gc_collect_recursive_expand_end + lw $a0 0($t1) + jal gc_collect_recursive_expand + addiu $t1 $t1 4 + sub $t0 $t0 1 + j gc_collect_recursive_expand_attr_loop + +# the value field of string object is not an object but it is a +# reference to the block where the string is saved, so that block +# needs to be marked as reachable +gc_collect_recursive_expand_string_object: + lw $t0 8($a0) + addiu $t0 $t0 neg_header_size + li $t1 reachable + sw $t1 header_reachable_slot($t0) + + +gc_collect_recursive_expand_end: + lw $a0 0($sp) + lw $t0 4($sp) + lw $t1 8($sp) + lw $ra 12($sp) + addiu $sp $sp 16 - lw $t1 4($a0) - addi $t1 $t1 -3 + jr $ra -gc_collect @@ -503,9 +616,9 @@ gc_collect -$a0 address from -$a1 address to -$a2 size +# $a0 address from +# $a1 address to +# $a2 size copy: addiu $sp $sp -16 sw $a0 0($sp) @@ -532,8 +645,16 @@ copy_end: jr $ra - -# $a0 address to check +##################################################################################################### +# Check if a value is a reference to an object # +# Args: # +# $a0: Value to check # +# Return: # +# $v0: 1 if is a reference to an object else 0 # +# Summary: # +# Check if a value is a valid heap address and if it is check if in that address there are # +# values that match with the object schema # +##################################################################################################### check_if_is_object: addiu $sp $sp -20 sw $t0 0($sp) @@ -548,8 +669,9 @@ check_if_is_object: move $a0 $zero syscall - addiu $t1 $v0 -4 #Last word of heap + addiu $t1 $v0 -4 # Last word of heap + # Check that the first word is a type object blt $t0 $gp check_if_is_object_not_object bgt $t0 $t1 check_if_is_object_not_object lw $t2 0($t0) @@ -563,21 +685,26 @@ check_if_is_object: addiu $t0 $t0 8 + li $t3 meta_data_object_size sub $t2 $t2 $t3 sll $t2 $t2 2 - addu $t0 $t0 $t2 + + # Check if the last word of the object is an object mark blt $t0 $gp check_if_is_object_not_object bgt $t0 $t1 check_if_is_object_not_object lw $t2 0($t0) - bnq $t2 object_mark check_if_is_object_not_object - li $v0 1 + beq $t2 object_mark check_if_is_object_is_object + beq $t2 object_expanded check_if_is_object_is_object + +check_if_is_object_not_object: + li $v0 0 j check_if_is_object_end -check_if_is_object_not_object: - li $v0 0 +check_if_is_object_is_object: + li $v0 1 check_if_is_object_end: From 011ea3e445e0d835c1ac12bcf219e7e9c8aea65a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 20 Sep 2020 18:37:47 -0400 Subject: [PATCH 337/520] [mips] Fix error in check_if_is_object routine --- src/core/cmp/mips_lib.asm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 89d95c09..96734ced 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -1,6 +1,5 @@ .text -type_number= 2 header_size = 12 #in bytes header_size_slot = 0 header_next_slot = 4 @@ -676,7 +675,9 @@ check_if_is_object: bgt $t0 $t1 check_if_is_object_not_object lw $t2 0($t0) blt $t2 $zero check_if_is_object_not_object - bgt $t2 type_number check_if_is_object_not_object + la $t3 type_number + lw $t3 0($t3) + bge $t2 $t3 check_if_is_object_not_object addiu $t0 $t0 4 blt $t0 $gp check_if_is_object_not_object From d05ef1ab77d627ac5a7c2fb64a92446c4fe91cba Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 20 Sep 2020 18:53:21 -0400 Subject: [PATCH 338/520] [mips] Create snippet for object creation --- src/core/cmp/mips.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index f4bc9c2e..74280a53 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -261,6 +261,20 @@ def exit_program(): instructions.append(SyscallNode()) return instructions +def create_object(reg1, reg2, reg3): + instructions = [] + instructions.append(ShiftLeftLogicalNode(reg1, reg1, 2)) + instructions.append(LoadAddressNode(reg2, PROTO_TABLE_LABEL)) + instructions.append(AddUnsignedNode(reg2, reg2, reg1)) + instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4))) + instructions.append(JumpAndLinkNode("malloc")) + instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) + instructions.append(MoveNode(ARG_REGISTERS[0], reg2)) + instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG)) + instructions.append(JumpAndLinkNode("copy")) + instructions.append(MoveNode(V0_REG, reg3)) + return instructions + class PrintVisitor: From 6a8c53bba481c12ab90339d63913e64921cdac32 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 20 Sep 2020 19:39:13 -0400 Subject: [PATCH 339/520] [mips] Change how cil.AllocateNode was converted to mips instructions --- compiled.asm | 230 +++++++----------------------------- src/core/cmp/cil_to_mips.py | 27 +++-- src/core/cmp/mips.py | 16 ++- 3 files changed, 65 insertions(+), 208 deletions(-) diff --git a/compiled.asm b/compiled.asm index 2b38e7e6..00d94bc2 100644 --- a/compiled.asm +++ b/compiled.asm @@ -3,12 +3,16 @@ data_1: .asciiz "Object" data_2: .asciiz "IO" data_3: .asciiz "String" data_4: .asciiz "Int" -data_5: .asciiz "Main" -data_6: .asciiz "A" +data_5: .asciiz "A" +data_6: .asciiz "Main" data_7: .asciiz "Program aborted" -data_8: .asciiz "Hello World!" +data_8: .asciiz "Dispatch on void" +data_9: .asciiz "Case on void" +data_10: .asciiz "Execution of a case statement without a matching branch" +data_11: .asciiz "Division by zero" +data_12: .asciiz "Substring out of range" -types_names_table: +type_name_table: .word data_1 .word data_2 .word data_3 @@ -76,30 +80,30 @@ type_5_dispatch: .word L_2 .word L_3 .word L_4 - .word L_6 - .word L_7 - .word L_8 - .word L_9 - .word L_15 type_5_proto: .word 4 - .word 4 + .word 16 .word type_5_dispatch + .word 0 + .word 0 + .word 0 .word -1 type_6_dispatch: .word L_2 .word L_3 .word L_4 + .word L_6 + .word L_7 + .word L_8 + .word L_9 + .word L_16 type_6_proto: .word 5 - .word 16 + .word 4 .word type_6_dispatch - .word 0 - .word 0 - .word 0 .word -1 .text .globl main @@ -108,19 +112,6 @@ main: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 - li $v0, 9 - li $a0, 4 - syscall - la $t0, type_5 - sw $t0, 0($v0) - sw $v0, -12($fp) - lw $t0, -12($fp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - jal L_15 - sw $v0, -8($fp) - addi $sp, $sp, 4 - li $v0, 0 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -131,21 +122,6 @@ L_1: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - li $v0, 9 - li $a0, 4 - syscall - la $t0, type_1 - sw $t0, 0($v0) - sw $v0, -8($fp) - lw $v0, -8($fp) - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -167,19 +143,25 @@ L_3: addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) - lw $t1, 0($fp) - lw $t1, 0($t1) - lw $t1, 0($t1) - sw $t1, -8($fp) - lw $t1, -8($fp) + sw $t5, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) + sw $t6, 0($sp) + lw $t5, 0($fp) + lw $t5, 0($t5) + sll $t5 $t5 2 + la $t6, type_name_table + addu $t5 $t5 $t6 + sw $t5, -8($fp) + lw $t5, -8($fp) + addi $sp, $sp, -4 + sw $t5, 0($sp) jal L_10 sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) - lw $t1, 0($sp) + lw $t6, 0($sp) + addi $sp, $sp, 4 + lw $t5, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 @@ -201,21 +183,6 @@ L_5: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - li $v0, 9 - li $a0, 4 - syscall - la $t1, type_1 - sw $t1, 0($v0) - sw $v0, -8($fp) - lw $v0, -8($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -225,21 +192,6 @@ L_6: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - lw $t1, 0($fp) - lw $t1, 4($t1) - sw $t1, -8($fp) - li $v0, 4 - lw $a0, -8($fp) - syscall - lw $v0, 4($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -249,21 +201,6 @@ L_7: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - lw $t1, 0($fp) - lw $t1, 4($t1) - sw $t1, -8($fp) - li $v0, 1 - lw $a0, -8($fp) - syscall - lw $v0, 4($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -290,30 +227,8 @@ L_10: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t2, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - li $v0, 9 - li $a0, 8 - syscall - la $t1, type_3 - sw $t1, 0($v0) - sw $v0, -8($fp) - lw $t2, -8($fp) - lw $t1, 0($fp) - sw $t1, 4($t2) - lw $v0, -8($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $t2, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 - addi $sp, $sp, 4 + addi $sp, $sp, -12 + addi $sp, $sp, 12 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -330,8 +245,8 @@ L_12: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -4 - addi $sp, $sp, 4 + addi $sp, $sp, -8 + addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -339,8 +254,8 @@ L_13: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -4 - addi $sp, $sp, 4 + addi $sp, $sp, -28 + addi $sp, $sp, 28 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -349,28 +264,6 @@ L_14: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t2, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - li $v0, 9 - li $a0, 8 - syscall - la $t1, type_4 - sw $t1, 0($v0) - sw $v0, -8($fp) - lw $t2, -8($fp) - lw $t1, 0($fp) - sw $t1, 4($t2) - lw $v0, -8($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $t2, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -379,36 +272,8 @@ L_15: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -16 - addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - la $t1, data_8 + 0 - sw $t1, -12($fp) - lw $t1, -12($fp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - jal L_10 - sw $v0, -16($fp) - addi $sp, $sp, 4 - lw $t1, -16($fp) - sw $t1, -8($fp) - lw $t1, 0($fp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - lw $t1, -8($fp) addi $sp, $sp, -4 - sw $t1, 0($sp) - jal L_6 - sw $v0, -20($fp) - addi $sp, $sp, 8 - lw $v0, -20($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) addi $sp, $sp, 4 - addi $sp, $sp, 16 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -416,23 +281,8 @@ L_16: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - li $v0, 9 - li $a0, 4 - syscall - la $t1, type_5 - sw $t1, 0($v0) - sw $v0, -8($fp) - lw $v0, -8($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 - addi $sp, $sp, 4 + addi $sp, $sp, -36 + addi $sp, $sp, 36 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 1a24a701..c0e36787 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -148,7 +148,7 @@ def visit(self, node): type_label = self.generate_type_label() methods = {key: self._name_func_map[value] for key, value in node.methods} - new_type = mips.MIPSType(type_label, name_label, node.attributes, methods) + new_type = mips.MIPSType(type_label, name_label, node.attributes, methods, len(self._types)) self._types[node.name] = new_type @@ -185,6 +185,7 @@ def visit(self, node): except Exception as e: print(node.name) + final_instructions = [] if not self.in_entry_function(): @@ -270,19 +271,21 @@ def visit(self, node): @visitor.when(cil.AllocateNode) def visit(self, node): - #This have to change after GC be implemented - #TODO Save $a0 register if is beign used - object_size = self._types[node.type].size - object_label = self._types[node.type].label - + #TODO Save $a0, $a1 and $a2 registers if are beign used instructions = [] - instructions.extend(mips.alloc_memory(object_size)) - - reg = self.get_free_reg() - instructions.append(mips.LoadAddressNode(reg, object_label)) - instructions.append(mips.StoreWordNode(reg, mips.RegisterRelativeLocation(mips.V0_REG, 0))) - self.free_reg(reg) + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + tp = 0 + if node.type.isnumeric(): + tp = node.type + else: + tp = self._types[node.type].index + + instructions.append(mips.LoadInmediateNode(reg1, tp)) + instrucctions.extend(mips.create_object(reg1, reg2)) + location = self.get_var_location(node.dest) instructions.append(mips.StoreWordNode(mips.V0_REG, location)) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 74280a53..b25d55ff 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -32,8 +32,6 @@ class ProgramNode(Node): def __init__(self, data, types, functions): self._data = data self._types = types - for i, t in enumerate(self._types): - t.index = i self._functions = functions @property @@ -176,11 +174,12 @@ def __init__(self, dest, src, bits): class MIPSType: - def __init__(self, label, name_addr, attributes, methods): + def __init__(self, label, name_addr, attributes, methods, index): self._label = label self._name = name_addr self._attributes = attributes self._methods = methods + self._index = index @property @@ -202,6 +201,10 @@ def methods(self): @property def attributes(self): return self._attributes + + @property + def index(self): + return self._index @@ -261,8 +264,9 @@ def exit_program(): instructions.append(SyscallNode()) return instructions -def create_object(reg1, reg2, reg3): +def create_object(reg1, reg2): instructions = [] + instructions.append(ShiftLeftLogicalNode(reg1, reg1, 2)) instructions.append(LoadAddressNode(reg2, PROTO_TABLE_LABEL)) instructions.append(AddUnsignedNode(reg2, reg2, reg1)) @@ -272,7 +276,7 @@ def create_object(reg1, reg2, reg3): instructions.append(MoveNode(ARG_REGISTERS[0], reg2)) instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG)) instructions.append(JumpAndLinkNode("copy")) - instructions.append(MoveNode(V0_REG, reg3)) + return instructions @@ -383,7 +387,7 @@ def visit(self, node): @visitor.when(AddInmediateUnsignedNode) def visit(self, node): - return f"addiu {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.value)}"" + return f"addiu {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.value)}" @visitor.when(AddUnsignedNode) def visit(self, node): From 67a037bb1dab305b1eddcd63583a6ba7f68b0de3 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 20 Sep 2020 19:45:15 -0400 Subject: [PATCH 340/520] [mips] Fix size in proto objects --- compiled.asm | 104 +++++++++++++++++++++++++++++++++--- src/core/cmp/cil_to_mips.py | 4 +- src/core/cmp/mips.py | 2 +- 3 files changed, 101 insertions(+), 9 deletions(-) diff --git a/compiled.asm b/compiled.asm index 00d94bc2..b7eeed53 100644 --- a/compiled.asm +++ b/compiled.asm @@ -61,7 +61,7 @@ type_3_dispatch: type_3_proto: .word 2 - .word 8 + .word 5 .word type_3_dispatch .word 0 .word -1 @@ -71,7 +71,7 @@ type_4_dispatch: type_4_proto: .word 3 - .word 8 + .word 5 .word type_4_dispatch .word 0 .word -1 @@ -83,7 +83,7 @@ type_5_dispatch: type_5_proto: .word 4 - .word 16 + .word 7 .word type_5_dispatch .word 0 .word 0 @@ -112,6 +112,24 @@ main: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 + li $t0, 5 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $a0, 4($t1) + jal malloc + move $a2 $a0 + move $a0 $t1 + move $a1 $v0 + jal copy + sw $v0, -12($fp) + lw $t2, -12($fp) + addi $sp, $sp, -4 + sw $t2, 0($sp) + jal L_16 + sw $v0, -8($fp) + addi $sp, $sp, 4 + li $v0, 0 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -122,6 +140,42 @@ L_1: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t2, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t3, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t2, 0 + sll $t2 $t2 2 + la $t3, proto_table + addu $t3 $t3 $t2 + lw $a0, 4($t3) + jal malloc + move $a2 $a0 + move $a0 $t3 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t3, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $t2, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -141,11 +195,11 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 + sw $t6, 0($sp) + addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 sw $t5, 0($sp) - addi $sp, $sp, -4 - sw $t6, 0($sp) lw $t5, 0($fp) lw $t5, 0($t5) sll $t5 $t5 2 @@ -159,12 +213,12 @@ L_3: sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) - lw $t6, 0($sp) - addi $sp, $sp, 4 lw $t5, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 + lw $t6, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -183,6 +237,42 @@ L_5: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $t6, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t5, 0($sp) + li $t5, 0 + sll $t5 $t5 2 + la $t6, proto_table + addu $t6 $t6 $t5 + lw $a0, 4($t6) + jal malloc + move $a2 $a0 + move $a0 $t6 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $t5, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $t6, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index c0e36787..87fd6f1f 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -183,6 +183,8 @@ def visit(self, node): try: code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) except Exception as e: + if node.name == "entry": + print(e) print(node.name) @@ -284,7 +286,7 @@ def visit(self, node): tp = self._types[node.type].index instructions.append(mips.LoadInmediateNode(reg1, tp)) - instrucctions.extend(mips.create_object(reg1, reg2)) + instructions.extend(mips.create_object(reg1, reg2)) location = self.get_var_location(node.dest) instructions.append(mips.StoreWordNode(mips.V0_REG, location)) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index b25d55ff..0e4accc1 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -184,7 +184,7 @@ def __init__(self, label, name_addr, attributes, methods, index): @property def size(self): - return (len(self.attributes) * ATTR_SIZE) + INSTANCE_METADATA_SIZE + return len(self.attributes) + INSTANCE_METADATA_SIZE @property def label(self): From f17374e9de2b1ca79089c58a1673ca6dc18cf069 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 12:22:43 -0400 Subject: [PATCH 341/520] [mips] Add snippet for objects copy --- compiled.asm | 48 ++++++++++++++++++++++---------------------- src/core/cmp/mips.py | 13 ++++++++++++ 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/compiled.asm b/compiled.asm index b7eeed53..ef2aaeb7 100644 --- a/compiled.asm +++ b/compiled.asm @@ -143,15 +143,15 @@ L_1: addi $sp, $sp, -4 sw $t2, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t3, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t3, 0($sp) + sw $a2, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $a1, 0($sp) li $t2, 0 sll $t2 $t2 2 la $t3, proto_table @@ -164,15 +164,15 @@ L_1: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $ra, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 - lw $t3, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $t3, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 lw $t2, 0($sp) addi $sp, $sp, 4 @@ -195,11 +195,11 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 - sw $t6, 0($sp) + sw $t5, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 - sw $t5, 0($sp) + sw $t6, 0($sp) lw $t5, 0($fp) lw $t5, 0($t5) sll $t5 $t5 2 @@ -213,11 +213,11 @@ L_3: sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) - lw $t5, 0($sp) + lw $t6, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t6, 0($sp) + lw $t5, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) @@ -238,17 +238,17 @@ L_5: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $t6, 0($sp) + sw $t5, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $a2, 0($sp) addi $sp, $sp, -4 - sw $t5, 0($sp) + sw $t6, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) li $t5, 0 sll $t5 $t5 2 la $t6, proto_table @@ -261,18 +261,18 @@ L_5: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $t5, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 lw $t6, 0($sp) addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t5, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 0e4accc1..98f65664 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -279,6 +279,19 @@ def create_object(reg1, reg2): return instructions +def copy_object(reg1, reg2): + instructions = [] + + instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg1, 4))) + instructions.append(JumpAndLinkNode("malloc")) + instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) + instructions.append(MoveNode(ARG_REGISTERS[0], reg1)) + instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG)) + instructions.append(JumpAndLinkNode("copy")) + + return instructions + + class PrintVisitor: From ec76b58e94bb8ab9d0ab6cd7e4867488296987f4 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 12:27:12 -0400 Subject: [PATCH 342/520] [mips] Add cil.CopyNode case to visit function --- src/core/cmp/cil_to_mips.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 87fd6f1f..4af56ff0 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -414,6 +414,24 @@ def visit(self, node): self.free_reg(reg) self.free_reg(reg2) return instructions + + @visitor.when(cil.CopyNode) + def visit(self, node): + instructions = [] + + #Save $a0, $a1, $a2 + + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + src_location = self.get_var_location(node.source) + instructions.append(mips.LoadWordNode(reg1, src_location)) + instructions.extend(mips.copy_object(reg1, reg2)) + + dst_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + + return instructions From 47067ef8ad87eeccd43db9759ee9ab243dc74e61 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 15:34:13 -0400 Subject: [PATCH 343/520] [mips] Fix some errors --- compiled.asm | 1002 +++++++++++++++++++++++++++++++++-- src/core/cmp/cil_to_mips.py | 16 +- src/core/cmp/mips.py | 2 + src/core/cmp/mips_lib.asm | 10 +- src/main.py | 3 + 5 files changed, 977 insertions(+), 56 deletions(-) diff --git a/compiled.asm b/compiled.asm index ef2aaeb7..b080b00b 100644 --- a/compiled.asm +++ b/compiled.asm @@ -108,6 +108,7 @@ type_6_proto: .text .globl main main: + jal mem_manager_init addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 @@ -116,16 +117,18 @@ main: sll $t0 $t0 2 la $t1, proto_table addu $t1 $t1 $t0 + lw $t1, 0($t1) lw $a0, 4($t1) + sll $a0 $a0 2 jal malloc move $a2 $a0 move $a0 $t1 move $a1 $v0 jal copy sw $v0, -12($fp) - lw $t2, -12($fp) + lw $t0, -12($fp) addi $sp, $sp, -4 - sw $t2, 0($sp) + sw $t0, 0($sp) jal L_16 sw $v0, -8($fp) addi $sp, $sp, 4 @@ -141,40 +144,42 @@ L_1: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t2, 0($sp) + sw $t0, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $t1, 0($sp) addi $sp, $sp, -4 - sw $t3, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) - li $t2, 0 - sll $t2 $t2 2 - la $t3, proto_table - addu $t3 $t3 $t2 - lw $a0, 4($t3) + addi $sp, $sp, -4 + sw $a2, 0($sp) + li $t0, 0 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) + sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t3 + move $a0 $t1 move $a1 $v0 jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 - lw $t3, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t2, 0($sp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -195,29 +200,29 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 - sw $t5, 0($sp) + sw $t1, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 - sw $t6, 0($sp) - lw $t5, 0($fp) - lw $t5, 0($t5) - sll $t5 $t5 2 - la $t6, type_name_table - addu $t5 $t5 $t6 - sw $t5, -8($fp) - lw $t5, -8($fp) + sw $t2, 0($sp) + lw $t1, 0($fp) + lw $t1, 0($t1) + sll $t1 $t1 2 + la $t2, type_name_table + addu $t1 $t1 $t2 + sw $t1, -8($fp) + lw $t1, -8($fp) addi $sp, $sp, -4 - sw $t5, 0($sp) + sw $t1, 0($sp) jal L_10 sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) - lw $t6, 0($sp) + lw $t2, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t5, 0($sp) + lw $t1, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) @@ -228,6 +233,35 @@ L_4: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + lw $t1, 0($fp) + lw $a0, 4($t1) + jal malloc + move $a2 $a0 + move $a0 $t1 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -238,40 +272,42 @@ L_5: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t5, 0($sp) + sw $t2, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $t6, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) - li $t5, 0 - sll $t5 $t5 2 - la $t6, proto_table - addu $t6 $t6 $t5 - lw $a0, 4($t6) + addi $sp, $sp, -4 + sw $a2, 0($sp) + li $t1, 0 + sll $t1 $t1 2 + la $t2, proto_table + addu $t2 $t2 $t1 + lw $t2, 0($t2) + lw $a0, 4($t2) + sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t6 + move $a0 $t2 move $a1 $v0 jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $t6, 0($sp) - addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t5, 0($sp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t2, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -282,6 +318,21 @@ L_6: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, 0($fp) + lw $t1, 4($t1) + sw $t1, -8($fp) + li $v0, 4 + lw $a0, -8($fp) + syscall + lw $v0, 4($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -291,6 +342,21 @@ L_7: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, 0($fp) + lw $t1, 4($t1) + sw $t1, -8($fp) + li $v0, 1 + lw $a0, -8($fp) + syscall + lw $v0, 4($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -354,6 +420,47 @@ L_14: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t5, 0($sp) + addi $sp, $sp, -4 + sw $t6, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + li $t5, 3 + sll $t5 $t5 2 + la $t6, proto_table + addu $t6 $t6 $t5 + lw $t6, 0($t6) + lw $a0, 4($t6) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t6 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $t6, -8($fp) + lw $t5, 0($fp) + sw $t5, 4($t6) + lw $v0, -8($fp) + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t6, 0($sp) + addi $sp, $sp, 4 + lw $t5, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -371,8 +478,34 @@ L_16: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -36 - addi $sp, $sp, 36 + addi $sp, $sp, -12 + addi $sp, $sp, -4 + sw $t7, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t7, 18 + addi $sp, $sp, -4 + sw $t7, 0($sp) + jal L_14 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $t7, -12($fp) + sw $t7, -8($fp) + lw $t7, 0($fp) + addi $sp, $sp, -4 + sw $t7, 0($sp) + lw $t7, -8($fp) + addi $sp, $sp, -4 + sw $t7, 0($sp) + jal L_7 + sw $v0, -16($fp) + addi $sp, $sp, 8 + lw $v0, -16($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t7, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 12 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -381,7 +514,778 @@ L_17: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t8, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t7, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + li $t7, 5 + sll $t7 $t7 2 + la $t8, proto_table + addu $t8 $t8 $t7 + lw $t8, 0($t8) + lw $a0, 4($t8) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t8 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $t7, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t8, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 - jr $ra \ No newline at end of file + jr $ra + +header_size = 12 #in bytes +header_size_slot = 0 +header_next_slot = 4 +header_reachable_slot = 8 +alloc_size = 2048 +total_alloc_size = 2060 #alloc_size + header_size +neg_header_size = -12 #-header_size +free_list = 0 +used_list = header_size +state_size = 4 +stack_base = -4 +init_alloc_size = 28 #(header_size*2) + state_size +object_mark = -1 +meta_data_object_size = 4 #in words +object_expanded = -2 +reachable = 1 + + + + +##################################################################################################### +# Initialize memory manager # +# Args: # +# # +# Return: # +# # +# Summary: # +# The initial blocks for Free-List and Used-List are created. # +# The $gp is set to use as reference when initial blocks or values related to memory manager # +# state are needed. # +# A block of size alloc_size is created an added to Free-List # +##################################################################################################### +mem_manager_init: + + addiu $sp $sp -16 + sw $v0 0($sp) + sw $a0 4($sp) + sw $a1 8($sp) + sw $ra 12($sp) + + + li $v0 9 + li $a0 init_alloc_size + syscall #Creating free-list start point + move $gp $v0 + addiu $gp $gp state_size + + sw $zero header_size_slot($gp) #The free-list start with a block without space, just header, that will always be there. + sw $zero header_next_slot($gp) + sw $zero header_reachable_slot($gp) + + move $a0 $gp + li $a1 alloc_size + jal extend_heap + + addiu $a0 $a0 header_size + sw $zero header_size_slot($a0) #The used-list start with a block without space, just header, that will always be there. + sw $zero header_next_slot($a0) + sw $zero header_reachable_slot($a0) + + + + lw $v0 0($sp) + lw $a0 4($sp) + lw $a1 8($sp) + lw $ra 12($sp) + addiu $sp $sp 16 + + sw $sp stack_base($gp) + + jr $ra + + +##################################################################################################### +# Free a block previously allocated # +# Args: # +# $a0 Block to free address # +# Return: # +# # +# Summary: # +# Remove the block from the used-list and add it to the free-list # +##################################################################################################### +free_block: + addiu $sp $sp -28 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $a0 12($sp) + sw $ra 16($sp) + sw $t3 20($sp) + sw $t4 24($sp) + + move $t0 $a0 + + addiu $t1 $gp free_list # Store in $t1 the initial block of the free-list + + addiu $t3 $gp used_list # Store in $t3 the initial block of the used-list + +free_block_loop_used_list: # Iterate througth the used-list until find the block + lw $t4 header_next_slot($t3) + beq $t4 $t0 free_block_loop_free_list + move $t3 $t4 + j free_block_loop_used_list + + +free_block_loop_free_list: # Iterate througth the free-list to find the antecesor of the block in the free-list + lw $t2 header_next_slot($t1) + beq $t2 $zero free_block_founded_prev + bge $t2 $t0 free_block_founded_prev + move $t1 $t2 + j free_block_loop_free_list + +free_block_founded_prev: + # Remove the block from the used-list + lw $t4 header_next_slot($t0) + sw $t4 header_next_slot($t3) + + # Add the block to the free-list + sw $t2 header_next_slot($t0) + sw $t0 header_next_slot($t1) + +free_block_end: + + # Try to merge the list where the new block was added + move $a0 $t0 + jal expand_block + move $a0 $t1 + jal expand_block + + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $a0 12($sp) + lw $ra 16($sp) + lw $t3 20($sp) + lw $t4 24($sp) + addiu $sp $sp 28 + + jr $ra + + +##################################################################################################### +# Merge two continuos blocks of the free-list # +# Args: # +# $a0 First of the two blocks to merge # +# Return: # +# # +# Summary: # +# Check if a block can be merged with its sucesor in the free list # +##################################################################################################### +expand_block: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + + + addiu $t0 $gp free_list # $t0 = the initial block of the free-list + + beq $t0 $a0 expand_block_end # The initial block can't be expanded, the initial block always will have size 0 + + move $t0 $a0 + + # Check if the block and its sucesor in the free list are contiguous in memory + lw $t1 header_next_slot($t0) + lw $t2 header_size_slot($t0) + move $t3 $t2 + addiu $t2 $t2 header_size + addu $t2 $t2 $t0 + beq $t2 $t1 expand_block_expand + j expand_block_end + +expand_block_expand: #Increment the size of the first block and update next field + lw $t2 header_size_slot($t1) + addi $t2 $t2 header_size + add $t2 $t2 $t3 + sw $t2 header_size_slot($t0) + lw $t1 header_next_slot($t1) + sw $t1 header_next_slot($t0) + +expand_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Allocate more memory for the process and add it to the free-list # +# Args: # +# $a0 Last block of the free-list # +# $a1 Memory amount to alloc # +# Return: # +# # +# Summary: # +# More memory is allocated and add it to the free-list as a block. # +##################################################################################################### +extend_heap: + addiu $sp $sp -12 + sw $a0 0($sp) + sw $a1 4($sp) + sw $t0 8($sp) + + # Increase the amount of memory by header_size to create a block with that size + li $v0 9 + addiu $a0 $a1 header_size + syscall + + # Set values of the block_header + move $t0 $a1 + sw $t0 header_size_slot($v0) + sw $zero header_next_slot($v0) + sw $zero header_reachable_slot($v0) + + # Add block to the end of the free-list + lw $t0, 0($sp) + sw $v0 header_next_slot($t0) + + move $a0 $t0 + lw $a1 4($sp) + lw $t0 8($sp) + addiu $sp $sp 12 + + jr $ra + + + +##################################################################################################### +# Split a block into two blocks, one of the requested size and the other with the rest. # +# Args: # +# $a0 Address of the block to split # +# $a1 Size requested for one block # +# Return: # +# # +# Summary: # +# The block is splitted into two blocks if the size allow it. # +##################################################################################################### +split_block: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + + # Check if the block can be splitted in two blocks, one of the requested size + lw $t0 header_size_slot($a0) + bgt $a1 $t0 split_block_error_small + + # Check if after a split the block there is enough space to create another block, if there is not do not split + sub $t0 $t0 $a1 + li $t1 header_size + ble $t0 $t1 split_block_same_size + + # Compute the address of the second block + addu $t0 $a0 $a1 + addiu $t0 $t0 header_size + + #Update headers of the two blocks + lw $t1 header_next_slot($a0) + sw $t1 header_next_slot($t0) + sw $t0 header_next_slot($a0) + + lw $t1 header_size_slot($a0) #update sizes + sub $t1 $t1 $a1 + + addi $t1 $t1 neg_header_size + sw $t1 header_size_slot($t0) + sw $a1 header_size_slot($a0) + move $v0 $a0 + j split_block_end + +split_block_same_size: + move $v0 $a0 + j split_block_end + +split_block_error_small: + j split_block_end + +split_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $a0 8($sp) + lw $a1 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Best Fit strategy is used to select the block # +# Args: # +# $a0 size to alloc # +# Return: # +# $v0 address of allocated block # +# Summary: # +# Actual block is store in $t0, the size block is checked to know if it is a # +# valid block (a block is valid if its size is larger or equal than the required size), # +# if the block is valid we compare it with the actual best block and keep the shorter block. # +# If there is not a block with the required size, a new block of size # +# max(total_alloc_size, size requested) is requested with sbrk and splitted if necessary # +##################################################################################################### +malloc: + move $v0 $zero + addiu $sp $sp -28 + sw $t1 0($sp) + sw $t0 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + sw $ra 16($sp) + sw $t2 20($sp) + sw $t3 24($sp) + + addiu $t0 $gp free_list + j malloc_loop + +malloc_end: + + move $a0 $v0 + lw $a1 8($sp) # a1 = requested block size + jal split_block + + lw $t1 header_next_slot($v0) + sw $t1 header_next_slot($t3) + + addiu $t1 $gp used_list + lw $a0 header_next_slot($t1) + + sw $a0 header_next_slot($v0) + sw $v0 header_next_slot($t1) + + addiu $v0 $v0 header_size + + lw $t3 24($sp) + lw $t2 20($sp) + lw $ra 16($sp) + lw $a1 12($sp) + lw $a0 8($sp) + lw $t0 4($sp) + lw $t1 0($sp) + addiu $sp $sp 28 + + jr $ra +####################################################################### +# t0 = actual block address # +####################################################################### +malloc_loop: + move $t2 $t0 # save previous block in $t2 (this is usefull when we lw $t3 24($sp)need to alloc the new block) + lw $t0 header_next_slot($t0) # t0 = next block address + beq $t0 $zero malloc_search_end # if t0 == 0 we reach to the free-list end + j malloc_check_valid_block + +####################################################################### +# $v0 = actual selected block address # +####################################################################### +malloc_search_end: + beq $v0 $zero malloc_alloc_new_block # if v0 == 0 a valid block was not found + j malloc_end + +####################################################################### +# t2 = last block of free list # +# a0 = requested block size # +####################################################################### +malloc_alloc_new_block: + li $t1 alloc_size # t1 = standard alloc size + move $t3 $t2 + move $a1 $a0 # a1 = requested block size + move $a0 $t2 # a0 = last block of free list + bge $a1 $t1 malloc_big_block # if the requested size is bigger than the standar alloc size go to malloc_big_block + li $a1 alloc_size # a1 = standard alloc size + jal extend_heap + + j malloc_end + +###################################################################### +# a1 = requested block size # +###################################################################### +malloc_big_block: + #addiu $a1 $a1 header_size # Add header size to alloc size + jal extend_heap + j malloc_end + + + +######################################################################## +# t0 = actual block address # +######################################################################## +malloc_check_valid_block: + lw $t1 header_size_slot($t0) # t1 = size new block + bge $t1 $a0 malloc_valid_block # the actual block have the required size + j malloc_loop + +######################################################################## +# t0 = actual block address # +# t1 = size actual block # +# v0 = actual selected block address(0 if no one have been selected) # +# v1 = actual selected block size # +######################################################################## +malloc_valid_block: + beq $v0 $zero malloc_first_valid_block # this is the first valid block + bge $t1 $v1 malloc_loop # the selected block is smaller than actual block + move $v0 $t0 # selected block address = actual block address + move $v1 $t1 # selected block size = actual block size + move $t3 $t2 + j malloc_loop + + +######################################################################## +# t0 = actual block address # +# t1 = size actual block # +# v0 = actual selected block address(0 if no one have been selected) # +# v1 = actual selected block size # +######################################################################## +malloc_first_valid_block: + move $v0 $t0 # selected block address = actual block address + move $v1 $t1 # selected block size = actual block size + move $t3 $t2 + j malloc_loop + + +#TODO Look for objects in registers +##################################################################################################### +# Remove from used-list the blocks that are not reachables, the root objects are in the stack and # +# registers # +# Args: # +# # +# Return: # +# # +# Summary: # +# First the objects in stack and registers are marked as reachables, after that the objects # +# that are reachables from them are marked as reachable too using a dfs algorithm. When all # +# reachables objects are marked the used-list is scanned and all the objects that are not # +# marked as reachables are released. # +##################################################################################################### + +gc_collect: + addiu $sp $sp -24 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $a0 16($sp) + sw $ra 20($sp) + + li $t3 reachable # $t3 = reachable value + addiu $t0 $sp 20 # $t0 = the start of the stack without count this function + lw $t1 stack_base($gp) # $t1 = the end of the stack + + li $t2 1 +# Go through the stack searching for objects +gc_collect_loop: + addiu $t0 $t0 4 + beq $t0 $t1 gc_collect_dfs # If the end of the stack was reached finish this loop + + lw $a0 0($t0) + jal check_if_is_object + + bne $v0 $t2 gc_collect_loop + + addiu $a0 $a0 neg_header_size + sw $t3 header_reachable_slot($a0) + + j gc_collect_loop + +gc_collect_dfs: + addiu $t1 $gp used_list + +# Go through the used-list and try to expand any reachable block +gc_collect_outer_loop: + lw $t1 header_next_slot($t1) + beq $t1 $zero gc_collect_free + lw $t2 header_reachable_slot($t1) + beq $t2 reachable gc_collect_expand + j gc_collect_outer_loop + +gc_collect_expand: + addiu $a0 $t1 header_size # expand an object not a block + jal gc_collect_recursive_expand + j gc_collect_outer_loop + +gc_collect_free: + addiu $t0 $gp used_list + lw $t0 header_next_slot($t0) + +# Go through the used-list and free any unreachable object and set the reachable and expanded field to their default values +gc_collect_free_loop: + beq $t0 $zero gc_collect_end + lw $t1 header_reachable_slot($t0) + bne $t1 reachable gc_collect_free_loop_free + sw $zero header_reachable_slot($t0) + move $a0 $t0 + jal check_if_is_object + beq $v0 $zero gc_collect_free_loop + li $t1 object_mark + addiu $t2 $t0 header_size + lw $t3 4($t2) + sll $t3 $t3 2 + addu $t2 $t2 $t3 + sw $t1 -4($t2) + lw $t0 header_next_slot($t0) + j gc_collect_free_loop + +gc_collect_free_loop_free: + move $a0 $t0 + lw $t0 header_next_slot($t0) + jal free_block + j gc_collect_free_loop + + +gc_collect_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $a0 16($sp) + lw $ra 20($sp) + addiu $sp $sp 24 + + jr $ra + + + + +##################################################################################################### +# Mark the objects that are reachable from the attrs of one object in a recursive way. # +# Args: # +# $a0: Object to expand # +# Return: # +# # +# Summary: # +# The actual object is marked as reachable and expanded to avoid infinite cycles, and this # +# routine is called recursively to expand the objects in the attrs of the actual object. # +##################################################################################################### +gc_collect_recursive_expand: + addiu $sp $sp -16 + sw $a0 0($sp) + sw $t0 4($sp) + sw $t1 8($sp) + sw $ra 12($sp) + + jal check_if_is_object # If is not an object can not be expanded + beq $v0 $zero gc_collect_recursive_expand_end + + lw $t0 4($a0) + sll $t0 $t0 2 + addiu $t0 $t0 -4 + addu $t0 $a0 $t0 + lw $t1 0($t0) # Check if the object was ready expanded to avoid infinite cycles + beq $t1 object_expanded gc_collect_recursive_expand_end + + # Mark the block that contains the object as reachable + li $t1 reachable + addiu $a0 $a0 neg_header_size + sw $t1 header_reachable_slot($a0) + addiu $a0 $a0 header_size + + # Mark the object as expanded + li $t1 object_expanded + sw $t1 0($t0) + + lw $t0 0($a0) # $t0 = type of the object + + # int and string types are special cases + la $t1 int_type + lw $t1 0($t1) + beq $t0 $t1 gc_collect_recursive_expand_end + + la $t1 string_type + lw $t1 0($t1) + beq $t0 $t1 gc_collect_recursive_expand_string_object + + lw $t0 4($a0) + li $t1 meta_data_object_size + sub $t0 $t0 $t1 + + addiu $t1 $a0 12 + +# call this routine in every attr of the object +gc_collect_recursive_expand_attr_loop: + beq $t0 $zero gc_collect_recursive_expand_end + lw $a0 0($t1) + jal gc_collect_recursive_expand + addiu $t1 $t1 4 + sub $t0 $t0 1 + j gc_collect_recursive_expand_attr_loop + +# the value field of string object is not an object but it is a +# reference to the block where the string is saved, so that block +# needs to be marked as reachable +gc_collect_recursive_expand_string_object: + lw $t0 8($a0) + addiu $t0 $t0 neg_header_size + li $t1 reachable + sw $t1 header_reachable_slot($t0) + + +gc_collect_recursive_expand_end: + lw $a0 0($sp) + lw $t0 4($sp) + lw $t1 8($sp) + lw $ra 12($sp) + addiu $sp $sp 16 + + jr $ra + + + + + + + + +# $a0 address from +# $a1 address to +# $a2 size +copy: + addiu $sp $sp -16 + sw $a0 0($sp) + sw $a1 4($sp) + sw $a2 8($sp) + sw $t0 12($sp) + +copy_loop: + beq $a2 $zero copy_end + lw $t0 0($a0) + sw $t0 0($a1) + addiu $a0 $a0 4 + addiu $a1 $a1 4 + addi $a2 $a2 -4 + j copy_loop + +copy_end: + lw $a0 0($sp) + lw $a1 4($sp) + lw $a2 8($sp) + lw $t0 12($sp) + addiu $sp $sp 16 + + jr $ra + + +##################################################################################################### +# Check if a value is a reference to an object # +# Args: # +# $a0: Value to check # +# Return: # +# $v0: 1 if is a reference to an object else 0 # +# Summary: # +# Check if a value is a valid heap address and if it is check if in that address there are # +# values that match with the object schema # +##################################################################################################### +check_if_is_object: + addiu $sp $sp -20 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $a0 16($sp) + + move $t0 $a0 + + li $v0 9 + move $a0 $zero + syscall + + addiu $t1 $v0 -4 # Last word of heap + + # Check that the first word is a type object + blt $t0 $gp check_if_is_object_not_object + bgt $t0 $t1 check_if_is_object_not_object + lw $t2 0($t0) + blt $t2 $zero check_if_is_object_not_object + la $t3 type_number + lw $t3 0($t3) + bge $t2 $t3 check_if_is_object_not_object + + addiu $t0 $t0 4 + blt $t0 $gp check_if_is_object_not_object + bgt $t0 $t1 check_if_is_object_not_object + lw $t2 0($t0) #Store size in $t2 + + addiu $t0 $t0 8 + + + li $t3 meta_data_object_size + sub $t2 $t2 $t3 + sll $t2 $t2 2 + addu $t0 $t0 $t2 + + # Check if the last word of the object is an object mark + blt $t0 $gp check_if_is_object_not_object + bgt $t0 $t1 check_if_is_object_not_object + lw $t2 0($t0) + beq $t2 object_mark check_if_is_object_is_object + beq $t2 object_expanded check_if_is_object_is_object + +check_if_is_object_not_object: + li $v0 0 + j check_if_is_object_end + + +check_if_is_object_is_object: + li $v0 1 + + +check_if_is_object_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $a0 16($sp) + addiu $sp $sp 20 + + jr $ra + + + + + + + + + + + + + + + diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 4af56ff0..ac77fa59 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -171,6 +171,8 @@ def visit(self, node): self.init_function(new_func) initial_instructions = [] + if self.in_entry_function(): + initial_instructions.append(mips.JumpAndLinkNode("mem_manager_init")) initial_instructions.extend(mips.push_register(mips.FP_REG)) @@ -181,9 +183,13 @@ def visit(self, node): #This try-except block is for debuggin purposes try: + if node.name == "function_main_at_Main": + print(node.instructions) + print(node.instructions[1].function) code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) except Exception as e: if node.name == "entry": + print(node.instructions) print(e) print(node.name) @@ -291,6 +297,9 @@ def visit(self, node): location = self.get_var_location(node.dest) instructions.append(mips.StoreWordNode(mips.V0_REG, location)) + self.free_reg(reg1) + self.free_reg(reg2) + return instructions @visitor.when(cil.ReturnNode) @@ -381,7 +390,7 @@ def visit(self, node): obj_location = self.get_var_location(node.obj) dst_location = self.get_var_location(node.dest) - tp = self._types[node.xtype] + tp = self._types[node.computed_type] offset = (tp.attributes.index(node.attr) + 1) * mips.ATTR_SIZE instructions.append(mips.LoadWordNode(reg, obj_location)) @@ -398,7 +407,7 @@ def visit(self, node): reg2 = self.get_free_reg() obj_location = self.get_var_location(node.obj) - tp = self._types[node.xtype] + tp = self._types[node.computed_type] offset = (tp.attributes.index(node.attr) + 1) * mips.ATTR_SIZE instructions.append(mips.LoadWordNode(reg2, obj_location)) @@ -431,6 +440,9 @@ def visit(self, node): dst_location = self.get_var_location(node.dest) instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + self.free_reg(reg1) + self.free_reg(reg2) + return instructions diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 98f65664..531d04fb 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -270,7 +270,9 @@ def create_object(reg1, reg2): instructions.append(ShiftLeftLogicalNode(reg1, reg1, 2)) instructions.append(LoadAddressNode(reg2, PROTO_TABLE_LABEL)) instructions.append(AddUnsignedNode(reg2, reg2, reg1)) + instructions.append(LoadWordNode(reg2, RegisterRelativeLocation(reg2, 0))) instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4))) + instructions.append(ShiftLeftLogicalNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) instructions.append(JumpAndLinkNode("malloc")) instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) instructions.append(MoveNode(ARG_REGISTERS[0], reg2)) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 96734ced..a440c3af 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -1,17 +1,17 @@ - .text + header_size = 12 #in bytes header_size_slot = 0 header_next_slot = 4 header_reachable_slot = 8 alloc_size = 2048 -total_alloc_size = alloc_size + header_size -neg_header_size = 0-header_size +total_alloc_size = 2060 #alloc_size + header_size +neg_header_size = -12 #-header_size free_list = 0 used_list = header_size state_size = 4 stack_base = -4 -init_alloc_size = (header_size*2) + state_size +init_alloc_size = 28 #(header_size*2) + state_size object_mark = -1 meta_data_object_size = 4 #in words object_expanded = -2 @@ -495,7 +495,7 @@ gc_collect_free_loop: sw $zero header_reachable_slot($t0) move $a0 $t0 jal check_if_is_object - beq $v0 $zero j gc_collect_free_loop + beq $v0 $zero gc_collect_free_loop li $t1 object_mark addiu $t2 $t0 header_size lw $t3 4($t2) diff --git a/src/main.py b/src/main.py index 8f5e60c4..b25e1ef3 100644 --- a/src/main.py +++ b/src/main.py @@ -92,6 +92,9 @@ def main(args): with open("compiled.asm", 'w') as f: f.write(mips_code) + with open("./src/core/cmp/mips_lib.asm") as f2: + f.write("".join(f2.readlines())) + From 2554fb5397fd4bd5b329a437a17ee5660ae94e25 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 17:02:09 -0400 Subject: [PATCH 344/520] [mips] Fix errors in cool_to_cil visitor cool.AttributeDeclarationNode case --- compiled.asm | 187 +++++++++++++++++++++--------------- src/core/cmp/cil_to_mips.py | 16 +-- src/core/cmp/cool_to_cil.py | 5 +- 3 files changed, 125 insertions(+), 83 deletions(-) diff --git a/compiled.asm b/compiled.asm index b080b00b..7100e75e 100644 --- a/compiled.asm +++ b/compiled.asm @@ -80,6 +80,7 @@ type_5_dispatch: .word L_2 .word L_3 .word L_4 + .word L_15 type_5_proto: .word 4 @@ -98,7 +99,7 @@ type_6_dispatch: .word L_7 .word L_8 .word L_9 - .word L_16 + .word L_17 type_6_proto: .word 5 @@ -129,7 +130,7 @@ main: lw $t0, -12($fp) addi $sp, $sp, -4 sw $t0, 0($sp) - jal L_16 + jal L_17 sw $v0, -8($fp) addi $sp, $sp, 4 li $v0, 0 @@ -144,17 +145,17 @@ L_1: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t0, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t0, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) li $t0, 0 sll $t0 $t0 2 la $t1, proto_table @@ -169,17 +170,17 @@ L_1: jal copy sw $v0, -8($fp) lw $v0, -8($fp) + lw $a1, 0($sp) + addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 - lw $t0, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -200,11 +201,11 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 + sw $t2, 0($sp) + addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t2, 0($sp) lw $t1, 0($fp) lw $t1, 0($t1) sll $t1 $t1 2 @@ -218,12 +219,12 @@ L_3: sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) - lw $t2, 0($sp) - addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 + lw $t2, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -234,15 +235,15 @@ L_4: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 - sw $a0, 0($sp) + sw $t1, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $a0, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) lw $t1, 0($fp) lw $a0, 4($t1) jal malloc @@ -252,16 +253,16 @@ L_4: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a2, 0($sp) - addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -272,17 +273,17 @@ L_5: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t2, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t2, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) li $t1, 0 sll $t1 $t1 2 la $t2, proto_table @@ -297,17 +298,17 @@ L_5: jal copy sw $v0, -8($fp) lw $v0, -8($fp) + lw $a1, 0($sp) + addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $t2, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 - lw $t2, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -319,9 +320,9 @@ L_6: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) lw $t1, 0($fp) lw $t1, 4($t1) sw $t1, -8($fp) @@ -329,10 +330,10 @@ L_6: lw $a0, -8($fp) syscall lw $v0, 4($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -343,9 +344,9 @@ L_7: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) lw $t1, 0($fp) lw $t1, 4($t1) sw $t1, -8($fp) @@ -353,10 +354,10 @@ L_7: lw $a0, -8($fp) syscall lw $v0, 4($fp) - lw $t1, 0($sp) - addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -423,15 +424,15 @@ L_14: addi $sp, $sp, -4 sw $t5, 0($sp) addi $sp, $sp, -4 - sw $t6, 0($sp) - addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 sw $a1, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) + sw $t6, 0($sp) li $t5, 3 sll $t5 $t5 2 la $t6, proto_table @@ -449,16 +450,16 @@ L_14: lw $t5, 0($fp) sw $t5, 4($t6) lw $v0, -8($fp) - lw $a2, 0($sp) + lw $t6, 0($sp) addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t6, 0($sp) - addi $sp, $sp, 4 lw $t5, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 @@ -478,34 +479,61 @@ L_16: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -12 - addi $sp, $sp, -4 - sw $t7, 0($sp) + addi $sp, $sp, -8 addi $sp, $sp, -4 sw $ra, 0($sp) - li $t7, 18 addi $sp, $sp, -4 sw $t7, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t6, 0($sp) + li $t6, 4 + sll $t6 $t6 2 + la $t7, proto_table + addu $t7 $t7 $t6 + lw $t7, 0($t7) + lw $a0, 4($t7) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t7 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + li $t6, 3 + addi $sp, $sp, -4 + sw $t6, 0($sp) jal L_14 sw $v0, -12($fp) addi $sp, $sp, 4 - lw $t7, -12($fp) - sw $t7, -8($fp) - lw $t7, 0($fp) - addi $sp, $sp, -4 - sw $t7, 0($sp) lw $t7, -8($fp) - addi $sp, $sp, -4 - sw $t7, 0($sp) - jal L_7 - sw $v0, -16($fp) - addi $sp, $sp, 8 - lw $v0, -16($fp) - lw $ra, 0($sp) + lw $t6, -12($fp) + sw $t6, 4($t7) + lw $t7, -8($fp) + li $t6, 0 + sw $t6, 8($t7) + lw $t7, -8($fp) + li $t6, 0 + sw $t6, 12($t7) + lw $v0, -8($fp) + lw $t6, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) addi $sp, $sp, 4 lw $t7, 0($sp) addi $sp, $sp, 4 - addi $sp, $sp, 12 + lw $ra, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -513,45 +541,54 @@ L_17: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 + addi $sp, $sp, -36 + addi $sp, $sp, 36 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_18: addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 addi $sp, $sp, -4 - sw $t8, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 sw $t7, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $a0, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) addi $sp, $sp, -4 - sw $a0, 0($sp) - li $t7, 5 - sll $t7 $t7 2 - la $t8, proto_table - addu $t8 $t8 $t7 - lw $t8, 0($t8) - lw $a0, 4($t8) + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t6, 0($sp) + li $t6, 5 + sll $t6 $t6 2 + la $t7, proto_table + addu $t7 $t7 $t6 + lw $t7, 0($t7) + lw $a0, 4($t7) sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t8 + move $a0 $t7 move $a1 $v0 jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a0, 0($sp) + lw $t6, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 lw $t7, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t8, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index ac77fa59..6d25e6c6 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -183,14 +183,13 @@ def visit(self, node): #This try-except block is for debuggin purposes try: - if node.name == "function_main_at_Main": - print(node.instructions) - print(node.instructions[1].function) code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) except Exception as e: - if node.name == "entry": + if node.name == "function_main_at_Main": print(node.instructions) + print(e) + print("HEREEEEEEE") print(node.name) @@ -407,13 +406,18 @@ def visit(self, node): reg2 = self.get_free_reg() obj_location = self.get_var_location(node.obj) + tp = self._types[node.computed_type] + offset = (tp.attributes.index(node.attr) + 1) * mips.ATTR_SIZE + instructions.append(mips.LoadWordNode(reg2, obj_location)) - if node.value.isnumeric(): - instructions.append(mips.LoadInmediateNode(reg, int(node.value))) + if type(node.value) == int: + instructions.append(mips.LoadInmediateNode(reg, node.value)) + #if node.value.isnumeric(): + # instructions.append(mips.LoadInmediateNode(reg, int(node.value))) else: src_location = self.get_var_location(node.value) instructions.append(mips.LoadWordNode(reg, src_location)) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 57afe010..f7525f5d 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -300,10 +300,11 @@ def visit(self, node, scope): #allocate instance = self.define_internal_local() self.register_instruction(cil.AllocateNode(node.id, instance)) - scope.ret_expr = instance + attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) for feature in attr_declarations: + scope.ret_expr = instance self.visit(feature, scope) self.register_instruction(cil.ReturnNode(instance)) self.current_function = None @@ -326,7 +327,7 @@ def visit(self, node, scope): except KeyError: #Void value scope.ret_expr = cil.VoidNode() - self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, node.type)) + self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, self.current_type.name)) @visitor.when(cool.FuncDeclarationNode) def visit(self, node, scope): From 4a87df0efcc757c966ed8d3222217b542500ecbb Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 17:21:55 -0400 Subject: [PATCH 345/520] [mips] Add equals routine to mips_lib.asm --- src/core/cmp/cil_to_mips.py | 28 ++++++++++++++++++++++++++++ src/core/cmp/mips_lib.asm | 10 ++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 6d25e6c6..f6e5708e 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -444,6 +444,34 @@ def visit(self, node): dst_location = self.get_var_location(node.dest) instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + self.free_reg(reg1) + self.free_reg(reg2) + + return instructions + + @visitor.when(cil.EqualNode) + def visit(self, node): + instructions = [] + + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + location = sefl.get_var_location(node.left) + instructions.append(mips.LoadWordNode(reg1, location)) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + location = sefl.get_var_location(node.right) + instructions.append(mips.LoadWordNode(reg2, location)) + + + + + self.free_reg(reg1) self.free_reg(reg2) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index a440c3af..b33f80da 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -719,6 +719,16 @@ check_if_is_object_end: jr $ra +equals: + beq $a0 $a1 equals_equal + li $v0 0 + j equals_end + +equals_equal: + li $v0 1 + +equals_end: + jr $ra From 842c55221b96fffd25d856ab9a5438f9cc7ed98d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 17:24:49 -0400 Subject: [PATCH 346/520] [mips] Add cil.EqualNode case to cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index f6e5708e..c5421920 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -453,28 +453,25 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() + #TODO save $a0 $a1 $v0 if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.left)) + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) else: location = sefl.get_var_location(node.left) - instructions.append(mips.LoadWordNode(reg1, location)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.right)) + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) else: location = sefl.get_var_location(node.right) - instructions.append(mips.LoadWordNode(reg2, location)) - - - + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) + instructions.append(mips.JumpAndLinkNode("equals")) - self.free_reg(reg1) - self.free_reg(reg2) - + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + return instructions From b5486a5ef3d274eaa500ff30d629cf7030bd75c3 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 17:37:14 -0400 Subject: [PATCH 347/520] [mips] Add cil.LabelNode case to cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index c5421920..47515db5 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -62,6 +62,7 @@ def __init__(self, label_generator = LabelGenerator(), regiters_manager = Simple self._actual_function = None self._name_func_map = {} self._pushed_args = 0 + self._labels_map = {} def generate_type_label(self): return self._label_generator.generate_type_label() @@ -77,6 +78,7 @@ def get_var_location(self, name): def register_function(self, name, function): self._functions[name] = function + self._labels_map = {} def init_function(self, function): self._actual_function = function @@ -98,6 +100,12 @@ def free_reg(self, reg): def in_entry_function(self): return self._actual_function.label == 'main' + + def register_label(self, cil_label, mips_label): + self._labels_map[cil_label] = mips_label + + def get_mips_label(self, label): + return self._labels_map[label] @visitor.on('node') def collect_func_names(self, node): @@ -474,6 +482,17 @@ def visit(self, node): return instructions + @visitor.when(cil.LabelNode) + def visit(self, node): + mips_label = self.generate_code_label() + self.register_label(node.label, mips_label) + + return [mips.LabelNode(mips_label)] + + + + + class UsedRegisterFinder: From c0afa17e561752eec6cb2efa94213c5c38128e2a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 17:38:40 -0400 Subject: [PATCH 348/520] [mips] Add LabelNode case to PrintVisitor --- src/core/cmp/mips.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 531d04fb..da525a90 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -406,4 +406,8 @@ def visit(self, node): @visitor.when(AddUnsignedNode) def visit(self, node): - return f"addu {self.visit(node.dest)} {self.visit(node.sum1)} {self.visit(node.sum2)}" \ No newline at end of file + return f"addu {self.visit(node.dest)} {self.visit(node.sum1)} {self.visit(node.sum2)}" + + @visitor.when(LabelNode) + def visit(self, node): + return f"{node.name}:" \ No newline at end of file From bd71a26adb27374695545126c17eab53b7768b84 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 21 Sep 2020 17:50:18 -0400 Subject: [PATCH 349/520] [mips] Add a visitor function collect_labels_if_func to get all labels used in actual func before pass visit function --- compiled.asm | 106 ++++++++++++++++++++---------------- src/core/cmp/cil_to_mips.py | 23 ++++++-- 2 files changed, 75 insertions(+), 54 deletions(-) diff --git a/compiled.asm b/compiled.asm index 7100e75e..241c9399 100644 --- a/compiled.asm +++ b/compiled.asm @@ -147,15 +147,15 @@ L_1: addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) + sw $t0, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t0, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) + sw $t1, 0($sp) addi $sp, $sp, -4 sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) li $t0, 0 sll $t0 $t0 2 la $t1, proto_table @@ -170,15 +170,15 @@ L_1: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $t0, 0($sp) + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $t1, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 @@ -201,11 +201,11 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 sw $t2, 0($sp) addi $sp, $sp, -4 sw $t1, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) lw $t1, 0($fp) lw $t1, 0($t1) sll $t1 $t1 2 @@ -219,12 +219,12 @@ L_3: sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) - lw $ra, 0($sp) - addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 lw $t2, 0($sp) addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -237,13 +237,13 @@ L_4: addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) + sw $t1, 0($sp) addi $sp, $sp, -4 sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) lw $t1, 0($fp) lw $a0, 4($t1) jal malloc @@ -253,14 +253,14 @@ L_4: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 @@ -273,17 +273,17 @@ L_5: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $t2, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t2, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) + sw $t1, 0($sp) addi $sp, $sp, -4 sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) li $t1, 0 sll $t1 $t1 2 la $t2, proto_table @@ -298,18 +298,18 @@ L_5: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $t2, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 + lw $t2, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -320,9 +320,9 @@ L_6: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) lw $t1, 0($fp) lw $t1, 4($t1) sw $t1, -8($fp) @@ -330,10 +330,10 @@ L_6: lw $a0, -8($fp) syscall lw $v0, 4($fp) - lw $a0, 0($sp) - addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -344,9 +344,9 @@ L_7: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) lw $t1, 0($fp) lw $t1, 4($t1) sw $t1, -8($fp) @@ -354,10 +354,10 @@ L_7: lw $a0, -8($fp) syscall lw $v0, 4($fp) - lw $a0, 0($sp) - addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -428,11 +428,11 @@ L_14: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) addi $sp, $sp, -4 sw $t6, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) li $t5, 3 sll $t5 $t5 2 la $t6, proto_table @@ -450,12 +450,12 @@ L_14: lw $t5, 0($fp) sw $t5, 4($t6) lw $v0, -8($fp) + lw $a2, 0($sp) + addi $sp, $sp, 4 lw $t6, 0($sp) addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) @@ -487,11 +487,11 @@ L_16: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) addi $sp, $sp, -4 sw $t6, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) li $t6, 4 sll $t6 $t6 2 la $t7, proto_table @@ -521,12 +521,12 @@ L_16: li $t6, 0 sw $t6, 12($t7) lw $v0, -8($fp) + lw $a2, 0($sp) + addi $sp, $sp, 4 lw $t6, 0($sp) addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 lw $t7, 0($sp) @@ -558,11 +558,11 @@ L_18: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) addi $sp, $sp, -4 sw $t6, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) li $t6, 5 sll $t6 $t6 2 la $t7, proto_table @@ -577,12 +577,12 @@ L_18: jal copy sw $v0, -8($fp) lw $v0, -8($fp) + lw $a2, 0($sp) + addi $sp, $sp, 4 lw $t6, 0($sp) addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 lw $t7, 0($sp) @@ -1313,6 +1313,16 @@ check_if_is_object_end: jr $ra +equals: + beq $a0 $a1 equals_equal + li $v0 0 + j equals_end + +equals_equal: + li $v0 1 + +equals_end: + jr $ra diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 47515db5..71cd5570 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -123,6 +123,15 @@ def collect_func_names(self, node): else: self._name_func_map[node.name] = self.generate_code_label() + @visitor.on('node') + def collect_labels_in_func(self, node): + pass + + @visitor.when(cil.LabelNode) + def collect_labels_in_func(self, node): + mips_label = self.generate_code_label() + self.register_label(node.label, mips_label) + @visitor.on('node') def visit(self, node): @@ -178,6 +187,9 @@ def visit(self, node): self.register_function(node.name, new_func) self.init_function(new_func) + for intructions in node.instructions: + self.collect_labels_in_func(instruction) + initial_instructions = [] if self.in_entry_function(): initial_instructions.append(mips.JumpAndLinkNode("mem_manager_init")) @@ -466,13 +478,13 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) else: - location = sefl.get_var_location(node.left) + location = self.get_var_location(node.left) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) else: - location = sefl.get_var_location(node.right) + location = self.get_var_location(node.right) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) instructions.append(mips.JumpAndLinkNode("equals")) @@ -484,10 +496,9 @@ def visit(self, node): @visitor.when(cil.LabelNode) def visit(self, node): - mips_label = self.generate_code_label() - self.register_label(node.label, mips_label) - - return [mips.LabelNode(mips_label)] + return [mips.LabelNode(self.get_mips_label(node.label))] + + From af2a646082d628091a863d64b2f7b37f2bfb1ba8 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 21 Sep 2020 21:38:02 -0400 Subject: [PATCH 350/520] [cil] - Remove definition of default values and one fix The first is done now in mips. Fix: - Keep instance value in `scope.ret_expr` for next iterations of visit of `AttrDeclarationNode` --- src/core/cmp/cool_to_cil.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index db7d05e5..44544c30 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -13,7 +13,6 @@ def __init__(self, context): self.current_function = None self.context = context self.vself = VariableInfo('self', None) - self.default_values = {'Int': 0, 'String': '', 'Bool': 0} @property def params(self): @@ -319,13 +318,8 @@ def visit(self, node, scope): instance = scope.ret_expr if node.expr: self.visit(node.expr, scope) - else: - try: - scope.ret_expr = self.default_values[node.type] - except KeyError: - #Void value - scope.ret_expr = cil.VoidNode() - self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, node.type)) + self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, self.current_type)) + scope.ret_expr = instance @visitor.when(cool.FuncDeclarationNode) def visit(self, node, scope): @@ -509,14 +503,8 @@ def visit(self, node, scope): vname = self.register_local(VariableInfo(node.id, node.type)) if node.expr: self.visit(node.expr, scope) - else: - try: - scope.ret_expr = self.default_values[node.type] - except KeyError: - #Void value - scope.ret_expr = cil.VoidNode() - self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) - + self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) + @visitor.when(cool.AssignNode) def visit(self, node, scope): ############################### From 1d72e158a9c08da5a1b56c9502abbce0002c5eef Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 21 Sep 2020 21:38:56 -0400 Subject: [PATCH 351/520] [cil] - Fix misuse of `LoadNode` in definition of `Object::abort()` --- src/core/cmp/cool_to_cil.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 44544c30..57902856 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -83,7 +83,8 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('abort', 'Object')) vname = self.define_internal_local() - self.register_instruction(cil.LoadNode(vname, 'data_0')) + data_node = [dn for dn in self.dotdata if dn.value == 'Program aborted'][0] + self.register_instruction(cil.LoadNode(vname, data_node)) self.register_instruction(cil.PrintStrNode(vname)) self.register_instruction(cil.ExitNode()) # No need for RETURN here right?? From 3df0d6bf912387ceece5452589ea298f504270e5 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 15:44:41 -0400 Subject: [PATCH 352/520] [mips] Add case for Void value in cil.AssignNode and cil.EqualNode --- compiled.asm | 120 ++++++++++++++++++------------------ src/core/cmp/cil_to_mips.py | 8 ++- src/core/cmp/cool_to_cil.py | 1 + 3 files changed, 68 insertions(+), 61 deletions(-) diff --git a/compiled.asm b/compiled.asm index 241c9399..cd29271f 100644 --- a/compiled.asm +++ b/compiled.asm @@ -145,17 +145,17 @@ L_1: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) li $t0, 0 sll $t0 $t0 2 la $t1, proto_table @@ -170,17 +170,17 @@ L_1: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a2, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 - lw $t1, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t0, 0($sp) + lw $t1, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -201,11 +201,11 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 sw $t2, 0($sp) addi $sp, $sp, -4 sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) lw $t1, 0($fp) lw $t1, 0($t1) sll $t1 $t1 2 @@ -219,12 +219,12 @@ L_3: sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 lw $t2, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -235,15 +235,15 @@ L_4: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) lw $t1, 0($fp) lw $a0, 4($t1) jal malloc @@ -253,16 +253,16 @@ L_4: jal copy sw $v0, -8($fp) lw $v0, -8($fp) + lw $a1, 0($sp) + addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -273,17 +273,17 @@ L_5: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t2, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 + sw $t2, 0($sp) + addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) li $t1, 0 sll $t1 $t1 2 la $t2, proto_table @@ -298,18 +298,18 @@ L_5: jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a2, 0($sp) - addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 lw $t2, 0($sp) addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -422,17 +422,17 @@ L_14: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t5, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $t6, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t5, 0($sp) addi $sp, $sp, -4 - sw $t6, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) li $t5, 3 sll $t5 $t5 2 la $t6, proto_table @@ -450,18 +450,18 @@ L_14: lw $t5, 0($fp) sw $t5, 4($t6) lw $v0, -8($fp) - lw $a2, 0($sp) - addi $sp, $sp, 4 - lw $t6, 0($sp) - addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 lw $t5, 0($sp) addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $t6, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -481,17 +481,17 @@ L_16: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t7, 0($sp) + sw $t6, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t7, 0($sp) addi $sp, $sp, -4 - sw $t6, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) li $t6, 4 sll $t6 $t6 2 la $t7, proto_table @@ -521,17 +521,17 @@ L_16: li $t6, 0 sw $t6, 12($t7) lw $v0, -8($fp) + lw $a1, 0($sp) + addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $t6, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $t7, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $t7, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) + lw $t6, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) @@ -552,17 +552,17 @@ L_18: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t7, 0($sp) + sw $t6, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t7, 0($sp) addi $sp, $sp, -4 - sw $t6, 0($sp) + sw $ra, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) li $t6, 5 sll $t6 $t6 2 la $t7, proto_table @@ -577,17 +577,17 @@ L_18: jal copy sw $v0, -8($fp) lw $v0, -8($fp) + lw $a1, 0($sp) + addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $t6, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $t7, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $t7, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) + lw $t6, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 71cd5570..adf5f754 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -187,7 +187,7 @@ def visit(self, node): self.register_function(node.name, new_func) self.init_function(new_func) - for intructions in node.instructions: + for instruction in node.instructions: self.collect_labels_in_func(instruction) initial_instructions = [] @@ -285,6 +285,8 @@ def visit(self, node): if node.source.isnumeric(): load_value = mips.LoadInmediateNode(reg, int(node.source)) instructions.append(load_value) + elif type(node.source) == cil.VoidNode: + instructions.append(mips.LoadInmediateNode(reg, 0)) else: value_location = self.get_var_location(node.source) load_value = mips.LoadWordNode(reg, value_location) @@ -477,12 +479,16 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) + elif type(node.left) == cil.VoidNode: + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], 0)) else: location = self.get_var_location(node.left) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) + elif type(node.right) == cil.VoidNode: + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], 0)) else: location = self.get_var_location(node.right) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f7525f5d..0803191a 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -326,6 +326,7 @@ def visit(self, node, scope): scope.ret_expr = self.default_values[node.type] except KeyError: #Void value + return scope.ret_expr = cil.VoidNode() self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, self.current_type.name)) From 316d0ce7168fa8609327db1b674d0dbe59b69c74 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 16:00:58 -0400 Subject: [PATCH 353/520] [mips] Add BranchOnnotEqualNode class --- compiled.asm | 254 ++++++++++++++++++------------------------- src/core/cmp/mips.py | 6 + 2 files changed, 114 insertions(+), 146 deletions(-) diff --git a/compiled.asm b/compiled.asm index cd29271f..29c55072 100644 --- a/compiled.asm +++ b/compiled.asm @@ -147,13 +147,13 @@ L_1: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $a1, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) addi $sp, $sp, -4 sw $t0, 0($sp) li $t0, 0 @@ -172,14 +172,14 @@ L_1: lw $v0, -8($fp) lw $t0, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 @@ -191,6 +191,21 @@ L_2: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + la $t0, data_7 + 0 + sw $t0, -8($fp) + li $v0, 4 + lw $a0, -8($fp) + syscall + li $v0, 10 + syscall + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -201,30 +216,30 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 - sw $t2, 0($sp) - addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) - lw $t1, 0($fp) - lw $t1, 0($t1) - sll $t1 $t1 2 - la $t2, type_name_table - addu $t1 $t1 $t2 - sw $t1, -8($fp) - lw $t1, -8($fp) addi $sp, $sp, -4 - sw $t1, 0($sp) + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $t0, 0($t0) + sll $t0 $t0 2 + la $t1, type_name_table + addu $t0 $t0 $t1 + sw $t0, -8($fp) + lw $t0, -8($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) jal L_10 sw $v0, -12($fp) addi $sp, $sp, 4 lw $v0, -12($fp) + lw $t0, 0($sp) + addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 - lw $t2, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -237,29 +252,29 @@ L_4: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $a1, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) - lw $t1, 0($fp) - lw $a0, 4($t1) + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $a0, 4($t0) jal malloc move $a2 $a0 - move $a0 $t1 + move $a0 $t0 move $a1 $v0 jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t1, 0($sp) + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 @@ -275,38 +290,38 @@ L_5: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t2, 0($sp) + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) addi $sp, $sp, -4 sw $t1, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $a1, 0($sp) - li $t1, 0 - sll $t1 $t1 2 - la $t2, proto_table - addu $t2 $t2 $t1 - lw $t2, 0($t2) - lw $a0, 4($t2) + sw $t0, 0($sp) + li $t0, 0 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t2 + move $a0 $t1 move $a1 $v0 jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 lw $t1, 0($sp) addi $sp, $sp, 4 - lw $t2, 0($sp) + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 @@ -322,15 +337,15 @@ L_6: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) - lw $t1, 0($fp) - lw $t1, 4($t1) - sw $t1, -8($fp) + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $t0, 4($t0) + sw $t0, -8($fp) li $v0, 4 lw $a0, -8($fp) syscall lw $v0, 4($fp) - lw $t1, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 @@ -346,15 +361,15 @@ L_7: addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) - lw $t1, 0($fp) - lw $t1, 4($t1) - sw $t1, -8($fp) + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $t0, 4($t0) + sw $t0, -8($fp) li $v0, 1 lw $a0, -8($fp) syscall lw $v0, 4($fp) - lw $t1, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 @@ -422,45 +437,45 @@ L_14: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t6, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t5, 0($sp) + sw $a1, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $t5, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) - li $t5, 3 - sll $t5 $t5 2 - la $t6, proto_table - addu $t6 $t6 $t5 - lw $t6, 0($t6) - lw $a0, 4($t6) + sw $t4, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t4, 3 + sll $t4 $t4 2 + la $t5, proto_table + addu $t5 $t5 $t4 + lw $t5, 0($t5) + lw $a0, 4($t5) sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t6 + move $a0 $t5 move $a1 $v0 jal copy sw $v0, -8($fp) - lw $t6, -8($fp) - lw $t5, 0($fp) - sw $t5, 4($t6) + lw $t5, -8($fp) + lw $t4, 0($fp) + sw $t4, 4($t5) lw $v0, -8($fp) - lw $a1, 0($sp) + lw $ra, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) + lw $t4, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 lw $t5, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 - lw $t6, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -480,59 +495,6 @@ L_16: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 - addi $sp, $sp, -4 - sw $t6, 0($sp) - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t7, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $a1, 0($sp) - li $t6, 4 - sll $t6 $t6 2 - la $t7, proto_table - addu $t7 $t7 $t6 - lw $t7, 0($t7) - lw $a0, 4($t7) - sll $a0 $a0 2 - jal malloc - move $a2 $a0 - move $a0 $t7 - move $a1 $v0 - jal copy - sw $v0, -8($fp) - li $t6, 3 - addi $sp, $sp, -4 - sw $t6, 0($sp) - jal L_14 - sw $v0, -12($fp) - addi $sp, $sp, 4 - lw $t7, -8($fp) - lw $t6, -12($fp) - sw $t6, 4($t7) - lw $t7, -8($fp) - li $t6, 0 - sw $t6, 8($t7) - lw $t7, -8($fp) - li $t6, 0 - sw $t6, 12($t7) - lw $v0, -8($fp) - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $t7, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 - lw $t6, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -552,43 +514,43 @@ L_18: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $t6, 0($sp) - addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 - sw $t7, 0($sp) + sw $a1, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $t8, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) - li $t6, 5 - sll $t6 $t6 2 - la $t7, proto_table - addu $t7 $t7 $t6 - lw $t7, 0($t7) - lw $a0, 4($t7) + sw $t7, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t7, 5 + sll $t7 $t7 2 + la $t8, proto_table + addu $t8 $t8 $t7 + lw $t8, 0($t8) + lw $a0, 4($t8) sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t7 + move $a0 $t8 move $a1 $v0 jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $a1, 0($sp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t7, 0($sp) addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 - lw $ra, 0($sp) + lw $t8, 0($sp) addi $sp, $sp, 4 - lw $t7, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 - lw $t6, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index da525a90..80d2dfdc 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -171,6 +171,12 @@ def __init__(self, dest, src, bits): self.src = src self.bits = bits +class BranchOnNotEqualNode(InstructionNode): + def __init__(self, reg1, reg2, label): + self.reg1 = reg1 + self.reg2 = reg2 + self.label = label + class MIPSType: From 683e817132a59a5415ff3358de1b3e7736f22526 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 16:03:48 -0400 Subject: [PATCH 354/520] [mips] Add case BranchOnNotEqualNode to visit function in PrintVisitor --- src/core/cmp/mips.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 80d2dfdc..fc17fb62 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -416,4 +416,8 @@ def visit(self, node): @visitor.when(LabelNode) def visit(self, node): - return f"{node.name}:" \ No newline at end of file + return f"{node.name}:" + + @visitor.when(BranchOnNotEqualNode) + def visit(self, node): + return f"bne {self.visit(node.reg1)} {self.visit(node.reg2)} {node.label}" \ No newline at end of file From ab2bfb43c5729e3faa8212fbc5678d062e184a70 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 16:18:47 -0400 Subject: [PATCH 355/520] [mips] Add case cil.GotoIfNode to visit function in cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 18 ++++++++++++++++++ src/core/cmp/mips.py | 1 + 2 files changed, 19 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index adf5f754..1650fdc5 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -503,6 +503,24 @@ def visit(self, node): @visitor.when(cil.LabelNode) def visit(self, node): return [mips.LabelNode(self.get_mips_label(node.label))] + + @visitor.when(cil.GotoIfNode) + def visit(self, node): + + instructions = [] + + reg = self.get_free_reg() + + mips_label = self.get_mips_label(node.label) + + location = self.get_var_location(node.condition) + instrucctions.append(mips.LoadWordNode(reg, location)) + instrucctions.append(mips.BranchOnNotEqualNode(reg, mips.ZERO_REG, mips_label)) + + self.free_reg(reg) + + return instructions + diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index fc17fb62..842c2c73 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -24,6 +24,7 @@ def __init__(self, name): RA_REG = Register('ra') V0_REG = Register('v0') V1_REG = Register('v1') +ZERO_REG = Register('zero') class Node: pass From 52e6cdff6dc0ce01389bfbb20afb21920a0e9e97 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 16:21:33 -0400 Subject: [PATCH 356/520] [mips] Add JumpNode and its case to visit function in PrintVisitor --- src/core/cmp/mips.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 842c2c73..1effacfc 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -178,6 +178,10 @@ def __init__(self, reg1, reg2, label): self.reg2 = reg2 self.label = label +class JumpNode(InstructionNode): + def __init__(self, label): + self.label = label + class MIPSType: @@ -421,4 +425,8 @@ def visit(self, node): @visitor.when(BranchOnNotEqualNode) def visit(self, node): - return f"bne {self.visit(node.reg1)} {self.visit(node.reg2)} {node.label}" \ No newline at end of file + return f"bne {self.visit(node.reg1)} {self.visit(node.reg2)} {node.label}" + + @visitor.when(JumpNode) + def visit(self, node): + return f"j {node.label}" \ No newline at end of file From aa433c5d2114647c5dee52a61babf442f5e4a011 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 16:22:31 -0400 Subject: [PATCH 357/520] [mips] Add cil.GotoNode case to visit function in cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 1650fdc5..1f208dc1 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -520,7 +520,12 @@ def visit(self, node): self.free_reg(reg) return instructions - + + @visitor.when(cil.GotoNode) + def visit(self, node): + mips_label = self.get_mips_label(node.label) + return [mips.JumpNode(mips_label)] + From d1ebc345cc4c32b9b44b4edbd257bee11ac06585 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 16:27:33 -0400 Subject: [PATCH 358/520] [mips] Add cil.TypeOfNode case to visit function in cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 1f208dc1..75c8aaf1 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -526,6 +526,24 @@ def visit(self, node): mips_label = self.get_mips_label(node.label) return [mips.JumpNode(mips_label)] + @visitor.when(cil.TypeOfNode) + def visit(self, node): + instructions = [] + + reg = self.get_free_reg() + + obj_location = self.get_var_location(node.obj) + instructions.append(mips.LoadWordNode(reg, obj_location)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(reg, dest_location)) + + self.free_reg(reg) + + return instructions + + From 836b362fe9ac4b9ea6b55e22ee29e612180fdb88 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 17:38:15 -0400 Subject: [PATCH 359/520] [mips] Add cil.DynamicCallNoce to visit function in cil_to_mips_visitor --- compiled.asm | 21 ++++++++++++++++++++- src/core/cmp/cil_to_mips.py | 33 +++++++++++++++++++++++++++++++++ src/core/cmp/mips.py | 2 +- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/compiled.asm b/compiled.asm index 29c55072..aab065f5 100644 --- a/compiled.asm +++ b/compiled.asm @@ -68,7 +68,8 @@ type_3_proto: type_4_dispatch: .word L_14 - + .word test1 + type_4_proto: .word 3 .word 5 @@ -109,6 +110,17 @@ type_6_proto: .text .globl main main: + + li $t0 3 + la $s1 proto_table + sll $t0 $t0 2 + addu $s1 $s1 $t0 + lw $s1 0($s1) + lw $s1 8($s1) + addiu $s1 $s1 4 + lw $s1 0($s1) + jal $s1 + jal mem_manager_init addi $sp, $sp, -4 sw $fp, 0($sp) @@ -139,6 +151,13 @@ main: addi $sp, $sp, 4 li $v0, 10 syscall + +test1: + li $v0 1 + li $a0 18 + syscall + li $v0 10 + syscall L_1: addi $sp, $sp, -4 sw $fp, 0($sp) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 75c8aaf1..e9e9be38 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -543,6 +543,39 @@ def visit(self, node): return instructions + @visitor.when(cil.DynamicCallNode) + def visit(self, node): + instructions = [] + + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + comp_tp = self._types[node.computed_type] + method_index = list(comp_tp.methods).index(node.method) + + tp_location = self.get_var_location(node.type) + instructions.append(mips.LoadAddressNode(reg1, mips.PROTO_TABLE_LABEL)) + instructions.append(mips.LoadWordNode(reg2, tp_location)) + instructions.append(mips.ShiftLeftLogicalNode(reg2, reg2, 2)) + instructions.append(mips.AddUnsignedNode(reg1, reg1, reg2 )) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8))) + instructions.append(mips.AddInmediateUnsignedNode(reg1, reg1, method_index*4)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.JumpRegisterAndLinkNode(reg1)) + + self.free_reg(reg1) + self.free_reg(reg2) + + return instructions + + + + + + + + diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 1effacfc..119cadf1 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -346,7 +346,7 @@ def visit(self, node): @visitor.when(MIPSType) def visit(self, node): - methods = "\n".join([f"\t.word\t {v}" for v in node.methods.values()]) + methods = "\n".join([f"\t.word\t {node.methods[k]}" for k in node.methods]) dispatch_table = f"{node.label}_dispatch:\n{methods}" proto_begin = f"{node.label}_proto:\n\t.word\t{node.index}\n\t.word\t{node.size}\n\t.word\t{node.label}_dispatch" proto_attr = "\n".join(['\t.word\t0' for _ in node.attributes]) From 7b51a515a596c08c7648218aea9acd1d0498a008 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 17:39:55 -0400 Subject: [PATCH 360/520] [mips] Add JumpRegisterAndLink node case to visit function in PrintVisitor --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 119cadf1..85b4a2d7 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -395,6 +395,10 @@ def visit(self, node): def visit(self, node): return f'jr {self.visit(node.reg)}' + @visitor.when(JumpRegisterAndLinkNode) + def visit(self, node): + return f'jal {self.visit(node.reg)}' + @visitor.when(LoadWordNode) def visit(self, node): return f'lw {self.visit(node.reg)}, {self.visit(node.addr)}' From 05cb34fd6d71b7e5a659def6f1f09026b6af92d8 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 17:55:27 -0400 Subject: [PATCH 361/520] [mips] Add cil.ErrorNode case to visit function in cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index e9e9be38..2ae4513a 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -568,6 +568,21 @@ def visit(self, node): self.free_reg(reg2) return instructions + + + @visitor.when(cil.ErrorNode) + def visit(self, node): + instructions = [] + + mips_label = self._data_section[node.data_node.name].label + + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) + instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], mips_label)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.LoadInmediateNode(mips.V0_REG)) + instructions.append(mips.SyscallNode()) + + return instructions From 63537c4507845f775607770e8b89bd0ce6740555 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 22 Sep 2020 18:12:00 -0400 Subject: [PATCH 362/520] [mips] Fix some errors --- compiled.asm | 295 +----------------------------------- src/core/cmp/cil_to_mips.py | 36 +++-- 2 files changed, 21 insertions(+), 310 deletions(-) diff --git a/compiled.asm b/compiled.asm index aab065f5..04a82896 100644 --- a/compiled.asm +++ b/compiled.asm @@ -68,8 +68,7 @@ type_3_proto: type_4_dispatch: .word L_14 - .word test1 - + type_4_proto: .word 3 .word 5 @@ -110,97 +109,21 @@ type_6_proto: .text .globl main main: - - li $t0 3 - la $s1 proto_table - sll $t0 $t0 2 - addu $s1 $s1 $t0 - lw $s1 0($s1) - lw $s1 8($s1) - addiu $s1 $s1 4 - lw $s1 0($s1) - jal $s1 - jal mem_manager_init addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 - li $t0, 5 - sll $t0 $t0 2 - la $t1, proto_table - addu $t1 $t1 $t0 - lw $t1, 0($t1) - lw $a0, 4($t1) - sll $a0 $a0 2 - jal malloc - move $a2 $a0 - move $a0 $t1 - move $a1 $v0 - jal copy - sw $v0, -12($fp) - lw $t0, -12($fp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - jal L_17 - sw $v0, -8($fp) - addi $sp, $sp, 4 - li $v0, 0 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 li $v0, 10 syscall - -test1: - li $v0 1 - li $a0 18 - syscall - li $v0 10 - syscall L_1: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a1, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - li $t0, 0 - sll $t0 $t0 2 - la $t1, proto_table - addu $t1 $t1 $t0 - lw $t1, 0($t1) - lw $a0, 4($t1) - sll $a0 $a0 2 - jal malloc - move $a2 $a0 - move $a0 $t1 - move $a1 $v0 - jal copy - sw $v0, -8($fp) - lw $v0, -8($fp) - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -210,21 +133,6 @@ L_2: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - la $t0, data_7 + 0 - sw $t0, -8($fp) - li $v0, 4 - lw $a0, -8($fp) - syscall - li $v0, 10 - syscall - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -234,31 +142,6 @@ L_3: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 - addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - lw $t0, 0($fp) - lw $t0, 0($t0) - sll $t0 $t0 2 - la $t1, type_name_table - addu $t0 $t0 $t1 - sw $t0, -8($fp) - lw $t0, -8($fp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - jal L_10 - sw $v0, -12($fp) - addi $sp, $sp, 4 - lw $v0, -12($fp) - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $t1, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -268,35 +151,6 @@ L_4: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a1, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - lw $t0, 0($fp) - lw $a0, 4($t0) - jal malloc - move $a2 $a0 - move $a0 $t0 - move $a1 $v0 - jal copy - sw $v0, -8($fp) - lw $v0, -8($fp) - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -306,44 +160,6 @@ L_5: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a1, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $t1, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - li $t0, 0 - sll $t0 $t0 2 - la $t1, proto_table - addu $t1 $t1 $t0 - lw $t1, 0($t1) - lw $a0, 4($t1) - sll $a0 $a0 2 - jal malloc - move $a2 $a0 - move $a0 $t1 - move $a1 $v0 - jal copy - sw $v0, -8($fp) - lw $v0, -8($fp) - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $t1, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -353,21 +169,6 @@ L_6: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - lw $t0, 0($fp) - lw $t0, 4($t0) - sw $t0, -8($fp) - li $v0, 4 - lw $a0, -8($fp) - syscall - lw $v0, 4($fp) - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -377,21 +178,6 @@ L_7: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $t0, 0($sp) - lw $t0, 0($fp) - lw $t0, 4($t0) - sw $t0, -8($fp) - li $v0, 1 - lw $a0, -8($fp) - syscall - lw $v0, 4($fp) - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -455,47 +241,6 @@ L_14: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a1, 0($sp) - addi $sp, $sp, -4 - sw $t5, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $t4, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - li $t4, 3 - sll $t4 $t4 2 - la $t5, proto_table - addu $t5 $t5 $t4 - lw $t5, 0($t5) - lw $a0, 4($t5) - sll $a0 $a0 2 - jal malloc - move $a2 $a0 - move $a0 $t5 - move $a1 $v0 - jal copy - sw $v0, -8($fp) - lw $t5, -8($fp) - lw $t4, 0($fp) - sw $t4, 4($t5) - lw $v0, -8($fp) - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $t4, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 - lw $t5, 0($sp) - addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -532,44 +277,6 @@ L_18: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 - addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a1, 0($sp) - addi $sp, $sp, -4 - sw $t8, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 - sw $t7, 0($sp) - addi $sp, $sp, -4 - sw $ra, 0($sp) - li $t7, 5 - sll $t7 $t7 2 - la $t8, proto_table - addu $t8 $t8 $t7 - lw $t8, 0($t8) - lw $a0, 4($t8) - sll $a0 $a0 2 - jal malloc - move $a2 $a0 - move $a0 $t8 - move $a1 $v0 - jal copy - sw $v0, -8($fp) - lw $v0, -8($fp) - lw $ra, 0($sp) - addi $sp, $sp, 4 - lw $t7, 0($sp) - addi $sp, $sp, 4 - lw $a2, 0($sp) - addi $sp, $sp, 4 - lw $t8, 0($sp) - addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 - lw $a0, 0($sp) - addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 2ae4513a..c6e79f46 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -203,16 +203,22 @@ def visit(self, node): #This try-except block is for debuggin purposes try: - code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) + # code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) + try: + for i, instruction in enumerate(node.instructions): + self.visit(instruction) + except Exception as e: + if node.name == "function_init_at_A": + print(i) + raise e except Exception as e: - if node.name == "function_main_at_Main": - print(node.instructions) - + if node.name == "function_init_at_A": print(e) - print("HEREEEEEEE") + print(node.instructions) print(node.name) + final_instructions = [] if not self.in_entry_function(): @@ -424,29 +430,27 @@ def visit(self, node): @visitor.when(cil.SetAttribNode) def visit(self, node): instructions = [] - reg = self.get_free_reg() + reg1 = self.get_free_reg() reg2 = self.get_free_reg() obj_location = self.get_var_location(node.obj) tp = self._types[node.computed_type] - offset = (tp.attributes.index(node.attr) + 1) * mips.ATTR_SIZE + offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE instructions.append(mips.LoadWordNode(reg2, obj_location)) if type(node.value) == int: - instructions.append(mips.LoadInmediateNode(reg, node.value)) - #if node.value.isnumeric(): - # instructions.append(mips.LoadInmediateNode(reg, int(node.value))) + instructions.append(mips.LoadInmediateNode(reg1, node.value)) else: src_location = self.get_var_location(node.value) - instructions.append(mips.LoadWordNode(reg, src_location)) + instructions.append(mips.LoadWordNode(reg1, src_location)) - instructions.append(mips.StoreWordNode(reg, mips.RegisterRelativeLocation(reg2, offset))) + instructions.append(mips.StoreWordNode(reg1, mips.RegisterRelativeLocation(reg2, offset))) - self.free_reg(reg) + self.free_reg(reg1) self.free_reg(reg2) return instructions @@ -514,8 +518,8 @@ def visit(self, node): mips_label = self.get_mips_label(node.label) location = self.get_var_location(node.condition) - instrucctions.append(mips.LoadWordNode(reg, location)) - instrucctions.append(mips.BranchOnNotEqualNode(reg, mips.ZERO_REG, mips_label)) + instructions.append(mips.LoadWordNode(reg, location)) + instructions.append(mips.BranchOnNotEqualNode(reg, mips.ZERO_REG, mips_label)) self.free_reg(reg) @@ -579,7 +583,7 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], mips_label)) instructions.append(mips.SyscallNode()) - instructions.append(mips.LoadInmediateNode(mips.V0_REG)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) instructions.append(mips.SyscallNode()) return instructions From 4b05c0c1fd864fa12308520dc0df7774c4274ee4 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 22 Sep 2020 19:59:58 -0400 Subject: [PATCH 363/520] [cil] - Fix some args given to `GetAttribNode` creation --- src/core/cmp/cool_to_cil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 57902856..1e9305e2 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -170,7 +170,7 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('length', 'String')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(result, self.vself, 'length', 'String')) + self.register_instruction(cil.GetAttribNode(result, self.vself.name, 'length', 'String')) self.register_instruction(cil.ReturnNode(result)) self.current_function = self.register_function(self.to_function_name('concat', 'String')) @@ -196,7 +196,7 @@ def register_built_in(self): self.register_instruction(cil.GetAttribNode(index_value, 'i', 'value', 'Int')) self.register_instruction(cil.GetAttribNode(length_value, 'l', 'value', 'Int')) #Check Out of range error - self.register_instruction(cil.GetAttribNode(length_attr, self.vself, 'length', 'String')) + self.register_instruction(cil.GetAttribNode(length_attr, self.vself.name, 'length', 'String')) self.register_instruction(cil.PlusNode(length_substr, length_value, index_value)) self.register_instruction(cil.LessNode(less_value, length_attr, length_substr)) self.register_runtime_error(less_value, 'Substring out of range') @@ -830,7 +830,7 @@ def visit(self, node, scope): try: self.current_type.get_attribute(node.lex) attr = self.register_local(VariableInfo('attr_value', None)) - self.register_instruction(cil.GetAttribNode(attr, self.vself, node.lex, self.current_type.name)) + self.register_instruction(cil.GetAttribNode(attr, self.vself.name, node.lex, self.current_type.name)) scope.ret_expr = attr except SemanticError: param_names = [pn.name for pn in self.current_function.params] From 7155cde02a75aac8888338031200b84ddd3d2c2b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 12:36:58 -0400 Subject: [PATCH 364/520] [mips] Fix some errors --- compiled.asm | 367 ++++++++++++++++++++++++++++++++++-- src/code.cl | 10 +- src/core/cmp/cil_to_mips.py | 67 ++++--- 3 files changed, 399 insertions(+), 45 deletions(-) diff --git a/compiled.asm b/compiled.asm index 04a82896..1309123d 100644 --- a/compiled.asm +++ b/compiled.asm @@ -5,12 +5,13 @@ data_3: .asciiz "String" data_4: .asciiz "Int" data_5: .asciiz "A" data_6: .asciiz "Main" -data_7: .asciiz "Program aborted" -data_8: .asciiz "Dispatch on void" -data_9: .asciiz "Case on void" -data_10: .asciiz "Execution of a case statement without a matching branch" -data_11: .asciiz "Division by zero" -data_12: .asciiz "Substring out of range" +data_7: .asciiz "B" +data_8: .asciiz "Program aborted" +data_9: .asciiz "Dispatch on void" +data_10: .asciiz "Case on void" +data_11: .asciiz "Execution of a case statement without a matching branch" +data_12: .asciiz "Division by zero" +data_13: .asciiz "Substring out of range" type_name_table: .word data_1 @@ -19,6 +20,7 @@ type_name_table: .word data_4 .word data_5 .word data_6 + .word data_7 proto_table: .word type_1_proto @@ -27,6 +29,7 @@ proto_table: .word type_4_proto .word type_5_proto .word type_6_proto + .word type_7_proto type_1_dispatch: .word L_1 @@ -80,15 +83,12 @@ type_5_dispatch: .word L_2 .word L_3 .word L_4 - .word L_15 type_5_proto: .word 4 - .word 7 + .word 5 .word type_5_dispatch .word 0 - .word 0 - .word 0 .word -1 type_6_dispatch: @@ -99,12 +99,26 @@ type_6_dispatch: .word L_7 .word L_8 .word L_9 - .word L_17 + .word L_16 type_6_proto: .word 5 - .word 4 + .word 6 .word type_6_dispatch + .word 0 + .word 0 + .word -1 + +type_7_dispatch: + .word L_2 + .word L_3 + .word L_4 + +type_7_proto: + .word 6 + .word 5 + .word type_7_dispatch + .word 0 .word -1 .text .globl main @@ -114,6 +128,26 @@ main: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 + li $t0, 5 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t1 + move $a1 $v0 + jal copy + sw $v0, -12($fp) + lw $t0, -12($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_16 + sw $v0, -8($fp) + addi $sp, $sp, 4 + li $v0, 0 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -124,6 +158,44 @@ L_1: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t0, 0 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t1 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -133,6 +205,21 @@ L_2: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + la $t0, data_8 + 0 + sw $t0, -8($fp) + li $v0, 4 + lw $a0, -8($fp) + syscall + li $v0, 10 + syscall + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -142,6 +229,31 @@ L_3: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t0, 0($fp) + lw $t0, 0($t0) + sll $t0 $t0 2 + la $t1, type_name_table + addu $t0 $t0 $t1 + sw $t0, -8($fp) + lw $t0, -8($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_10 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $v0, -12($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -151,6 +263,35 @@ L_4: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + lw $t0, 0($fp) + lw $a0, 4($t0) + jal malloc + move $a2 $a0 + move $a0 $t0 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -160,6 +301,44 @@ L_5: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t0, 0 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t1 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -169,6 +348,21 @@ L_6: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + lw $t0, 0($fp) + lw $t0, 12($t0) + sw $t0, -8($fp) + li $v0, 4 + lw $a0, -8($fp) + syscall + lw $v0, 4($fp) + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -178,6 +372,21 @@ L_7: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + lw $t0, 0($fp) + lw $t0, 12($t0) + sw $t0, -8($fp) + li $v0, 1 + lw $a0, -8($fp) + syscall + lw $v0, 4($fp) + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -241,6 +450,47 @@ L_14: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t4, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t5, 0($sp) + li $t4, 3 + sll $t4 $t4 2 + la $t5, proto_table + addu $t5 $t5 $t4 + lw $t5, 0($t5) + lw $a0, 4($t5) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t5 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $t5, -8($fp) + lw $t4, 0($fp) + sw $t4, 12($t5) + lw $v0, -8($fp) + lw $t5, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t4, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -249,8 +499,55 @@ L_15: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 + addi $sp, $sp, -8 addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t4, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t5, 0($sp) + li $t4, 4 + sll $t4 $t4 2 + la $t5, proto_table + addu $t5 $t5 $t4 + lw $t5, 0($t5) + lw $a0, 4($t5) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t5 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + li $t4, 3 + addi $sp, $sp, -4 + sw $t4, 0($sp) + jal L_14 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $t5, -8($fp) + lw $t4, -12($fp) + sw $t4, 12($t5) + lw $v0, -8($fp) + lw $t5, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) addi $sp, $sp, 4 + lw $t4, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -258,8 +555,8 @@ L_16: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -8 - addi $sp, $sp, 8 + addi $sp, $sp, -128 + addi $sp, $sp, 128 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -267,8 +564,8 @@ L_17: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -36 - addi $sp, $sp, 36 + addi $sp, $sp, -12 + addi $sp, $sp, 12 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra @@ -277,6 +574,44 @@ L_18: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $t6, 0($sp) + addi $sp, $sp, -4 + sw $t5, 0($sp) + li $t5, 6 + sll $t5 $t5 2 + la $t6, proto_table + addu $t6 $t6 $t5 + lw $t6, 0($t6) + lw $a0, 4($t6) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t6 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $v0, -8($fp) + lw $t5, 0($sp) + addi $sp, $sp, 4 + lw $t6, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 diff --git a/src/code.cl b/src/code.cl index 4d3b1df6..b3eaa155 100644 --- a/src/code.cl +++ b/src/code.cl @@ -12,13 +12,13 @@ class Main inherits IO { main() : Object {{ - out_string("\n"); + out_int(0); if (x = y) - then out_string("EQUAL\n") - else out_string("NOT EQUAL\n") + then out_int(1) + else out_int(2) fi; - out_string(x.type_name()); - out_string("\n"); + + out_int(0); 1 < 3 + let a:Int<-3 in a; }}; diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index c6e79f46..3591a255 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -31,6 +31,9 @@ def get_registers_for_save(self): regs = [reg for reg, state in self.registers.items() if state == USED] regs.extend([mips.RA_REG]) return regs + + def __repr__(self): + return str(len([0 for r in self.registers if self.registers[r] == USED ])) class LabelGenerator: @@ -78,10 +81,10 @@ def get_var_location(self, name): def register_function(self, name, function): self._functions[name] = function - self._labels_map = {} - + def init_function(self, function): self._actual_function = function + self._labels_map = {} def finish_functions(self): self._actual_function = None @@ -202,15 +205,14 @@ def visit(self, node): code_instructions = [] #This try-except block is for debuggin purposes + if node.name == "function_get_x_at_A": + print(node.instructions) + try: - # code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) - try: - for i, instruction in enumerate(node.instructions): - self.visit(instruction) - except Exception as e: - if node.name == "function_init_at_A": - print(i) - raise e + code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) + if node.name == "function_main_at_Main": + print(node.instructions[-7].dest) + except Exception as e: if node.name == "function_init_at_A": print(e) @@ -242,14 +244,13 @@ def visit(self, node): func_instructions = list(itt.chain(initial_instructions, code_instructions, final_instructions)) new_func.add_instructions(func_instructions) self.finish_functions() - + @visitor.when(cil.ArgNode) def visit(self, node): self.push_arg() - instructions = [] - + if type(node.name) == int: reg = self.get_free_reg() load_value = mips.LoadInmediateNode(reg, node.name) @@ -268,6 +269,7 @@ def visit(self, node): @visitor.when(cil.StaticCallNode) def visit(self, node): + instructions = [] label = self._name_func_map[node.function] @@ -286,6 +288,7 @@ def visit(self, node): @visitor.when(cil.AssignNode) def visit(self, node): instructions = [] + reg = self.get_free_reg() if node.source.isnumeric(): @@ -307,6 +310,7 @@ def visit(self, node): @visitor.when(cil.AllocateNode) def visit(self, node): #TODO Save $a0, $a1 and $a2 registers if are beign used + instructions = [] reg1 = self.get_free_reg() @@ -326,7 +330,7 @@ def visit(self, node): self.free_reg(reg1) self.free_reg(reg2) - + return instructions @visitor.when(cil.ReturnNode) @@ -384,6 +388,7 @@ def visit(self, node): @visitor.when(cil.TypeNameNode) def visit(self, node): instructions = [] + reg = self.get_free_reg() reg2 = self.get_free_reg() @@ -413,12 +418,18 @@ def visit(self, node): @visitor.when(cil.GetAttribNode) def visit(self, node): instructions = [] - reg = self.get_free_reg() - obj_location = self.get_var_location(node.obj) - dst_location = self.get_var_location(node.dest) - tp = self._types[node.computed_type] - offset = (tp.attributes.index(node.attr) + 1) * mips.ATTR_SIZE + reg = self.get_free_reg() + + dest = node.dest if type(node.dest) == str else node.dest.name + obj = node.obj if type(node.obj) == str else node.obj.name + comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name + + obj_location = self.get_var_location(obj) + dst_location = self.get_var_location(dest) + + tp = self._types[comp_type] + offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE instructions.append(mips.LoadWordNode(reg, obj_location)) instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, offset))) @@ -432,14 +443,18 @@ def visit(self, node): instructions = [] reg1 = self.get_free_reg() reg2 = self.get_free_reg() + + obj = node.obj if type(node.obj) == str else node.obj.name + comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name - obj_location = self.get_var_location(node.obj) - - tp = self._types[node.computed_type] + obj_location = self.get_var_location(obj) + + tp = self._types[comp_type] + offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE - - + + instructions.append(mips.LoadWordNode(reg2, obj_location)) if type(node.value) == int: @@ -448,10 +463,12 @@ def visit(self, node): src_location = self.get_var_location(node.value) instructions.append(mips.LoadWordNode(reg1, src_location)) + instructions.append(mips.StoreWordNode(reg1, mips.RegisterRelativeLocation(reg2, offset))) self.free_reg(reg1) self.free_reg(reg2) + return instructions @visitor.when(cil.CopyNode) @@ -556,6 +573,7 @@ def visit(self, node): comp_tp = self._types[node.computed_type] method_index = list(comp_tp.methods).index(node.method) + dest_location = self.get_var_location(node.dest) tp_location = self.get_var_location(node.type) instructions.append(mips.LoadAddressNode(reg1, mips.PROTO_TABLE_LABEL)) @@ -567,6 +585,7 @@ def visit(self, node): instructions.append(mips.AddInmediateUnsignedNode(reg1, reg1, method_index*4)) instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) instructions.append(mips.JumpRegisterAndLinkNode(reg1)) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) self.free_reg(reg1) self.free_reg(reg2) From 43f064f24db111621eed3a1946ae276a0d8e0d43 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 18:26:44 -0400 Subject: [PATCH 365/520] [mips] Add cil.NameNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 3591a255..ced9a6ec 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -608,6 +608,25 @@ def visit(self, node): return instructions + @visitor.when(cil.NameNode) + def visit(self, node): + instructions = [] + + reg = self.get_free_reg() + + instructions.append(mips.LoadAddressNode(reg, mips.TYPENAMES_TABLE_LABEL)) + tp_number = self._types[node.name].index() + instructions.append(mips.AddInmediateUnsignedNode(reg, reg, tp_number*4)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(reg, dest_location)) + + self.free_reg(reg) + + return instructions + + From f175be54c290adfaaa7e4425bfc8a4f786816bf9 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 18:32:48 -0400 Subject: [PATCH 366/520] [mips] Add AddNode to Mips classes --- src/core/cmp/mips.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 85b4a2d7..36e2d7dc 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -181,6 +181,12 @@ def __init__(self, reg1, reg2, label): class JumpNode(InstructionNode): def __init__(self, label): self.label = label + +class AddNode(InstructionNode): + def __init__(self, reg1, reg2, reg3) + self.reg1 = reg1 + self.reg2 = reg2 + self.reg3 = reg3 From 010106cac67e420070d1304d1a5c02cabde05e4a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 18:33:54 -0400 Subject: [PATCH 367/520] [mips] Add AddNode case to visit function of PrintVisitor --- src/core/cmp/mips.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 36e2d7dc..a753ce77 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -439,4 +439,8 @@ def visit(self, node): @visitor.when(JumpNode) def visit(self, node): - return f"j {node.label}" \ No newline at end of file + return f"j {node.label}" + + @visitor.when(AddNode) + def visit(self, node): + return f"add {self.visit(node.reg1} {self.visit(node.reg2} {self.visit(node.reg3}" \ No newline at end of file From 712994dacc878e14365505a02b2fe2d9f4aa9265 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 18:36:45 -0400 Subject: [PATCH 368/520] [mips] Add cil.PlusNode case to visit function in cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index ced9a6ec..05466042 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -625,6 +625,36 @@ def visit(self, node): self.free_reg(reg) return instructions + + @visitor.when(cil.PlusNode) + def visit(self, node): + instructions = [] + + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + left_location = self.get_var_location(node.left) + instructions.append(mips.LoadWordNode(reg1, left_location)) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + right_location = self.get_var_location(node.right) + instructions.append(mips.LoadWordNode(reg2, right_location)) + + instructions.append(mips.AddNode(reg1, reg1, reg2)) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(reg1, dest_location)) + + self.free_reg(reg1) + self.free_reg(reg2) + + return instructions + From 7bc2f40f91db9716bca633166b42ecdd0213a3ae Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 19:06:11 -0400 Subject: [PATCH 369/520] [mips] Add SubNode to Mips classes --- src/core/cmp/mips.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index a753ce77..c3436660 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -187,6 +187,12 @@ def __init__(self, reg1, reg2, reg3) self.reg1 = reg1 self.reg2 = reg2 self.reg3 = reg3 + +class SubNode(InstructionNode): + def __init__(self, reg1, reg2, reg3) + self.reg1 = reg1 + self.reg2 = reg2 + self.reg3 = reg3 From 44f5ce61dda7dad2225c614793995c0462bab115 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 19:07:29 -0400 Subject: [PATCH 370/520] [mips] Add SubNode case to visit function of PrintVisitor --- src/core/cmp/mips.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index c3436660..bbe4ff37 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -449,4 +449,8 @@ def visit(self, node): @visitor.when(AddNode) def visit(self, node): - return f"add {self.visit(node.reg1} {self.visit(node.reg2} {self.visit(node.reg3}" \ No newline at end of file + return f"add {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(SubNode) + def visit(self, node): + return f"sub {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" \ No newline at end of file From 9937f2de27ed25440be1be15533f3a34d1709f7c Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 19:08:46 -0400 Subject: [PATCH 371/520] [mips] Add cil.MinusNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 05466042..a872aeab 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -654,6 +654,37 @@ def visit(self, node): self.free_reg(reg2) return instructions + + @visitor.when(cil.MinusNode) + def visit(self, node): + instructions = [] + + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + left_location = self.get_var_location(node.left) + instructions.append(mips.LoadWordNode(reg1, left_location)) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + right_location = self.get_var_location(node.right) + instructions.append(mips.LoadWordNode(reg2, right_location)) + + instructions.append(mips.SubNode(reg1, reg1, reg2)) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(reg1, dest_location)) + + self.free_reg(reg1) + self.free_reg(reg2) + + return instructions + + From 0ee68ccdfa5af9087cea0870ba440bd14e60aa7d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 22:02:44 -0400 Subject: [PATCH 372/520] [mips] Add MultiplyNode to Mips classes --- src/core/cmp/mips.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index bbe4ff37..b909bcc3 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -193,6 +193,12 @@ def __init__(self, reg1, reg2, reg3) self.reg1 = reg1 self.reg2 = reg2 self.reg3 = reg3 + +class MultiplyNode(InstructionNode): + def __init__(self, reg1, reg2, reg3) + self.reg1 = reg1 + self.reg2 = reg2 + self.reg3 = reg3 @@ -453,4 +459,6 @@ def visit(self, node): @visitor.when(SubNode) def visit(self, node): - return f"sub {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" \ No newline at end of file + return f"sub {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + \ No newline at end of file From c4b19403432b104584113c04d648ddefcb9f83e9 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 22:04:10 -0400 Subject: [PATCH 373/520] [mips] Add MultiplyNode case to visit function of PrintVisitor --- src/core/cmp/mips.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index b909bcc3..fd460be5 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -461,4 +461,6 @@ def visit(self, node): def visit(self, node): return f"sub {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" - \ No newline at end of file + @visitor.when(MultiplyNode) + def visit(self, node): + return f"mul {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" \ No newline at end of file From 24bdbc38d5a735c252ed35b89add877f2fe47b32 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 22:05:02 -0400 Subject: [PATCH 374/520] [mips] Add cil.StarNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index a872aeab..06ebfa25 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -684,6 +684,35 @@ def visit(self, node): return instructions + @visitor.when(cil.StarNode) + def visit(self, node): + instructions = [] + + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + left_location = self.get_var_location(node.left) + instructions.append(mips.LoadWordNode(reg1, left_location)) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + right_location = self.get_var_location(node.right) + instructions.append(mips.LoadWordNode(reg2, right_location)) + + instructions.append(mips.MultiplyNode(reg1, reg1, reg2)) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(reg1, dest_location)) + + self.free_reg(reg1) + self.free_reg(reg2) + + return instructions + From 22a110ac01a9139999098d96a9f33c2958a8cea8 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 22:56:37 -0400 Subject: [PATCH 375/520] [mips] Add DivideNode to Mips classes --- src/core/cmp/mips.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index fd460be5..cca2e027 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -199,6 +199,11 @@ def __init__(self, reg1, reg2, reg3) self.reg1 = reg1 self.reg2 = reg2 self.reg3 = reg3 + +class DivideNode(InstructionNode): + def __init__(self, reg1, reg2): + self.reg1 = reg1 + self.reg2 = reg2 From 55615ff6b9dc88778ec5129327b231d6b6651e69 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 22:58:14 -0400 Subject: [PATCH 376/520] [mips] Add DivideNode case to visit function of PrintVisitor --- src/core/cmp/mips.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index cca2e027..c649a716 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -468,4 +468,8 @@ def visit(self, node): @visitor.when(MultiplyNode) def visit(self, node): - return f"mul {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" \ No newline at end of file + return f"mul {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(DivideNode) + def visit(self, node): + return f"div {self.visit(node.reg1)} {self.visit(node.reg2)}" \ No newline at end of file From 44744e5d7e1eb075aef904518340bee81dae4b82 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 23:00:17 -0400 Subject: [PATCH 377/520] [mips] Add cil.DivNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 29 +++++++++++++++++++++++++++++ src/core/cmp/mips.py | 1 + 2 files changed, 30 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 06ebfa25..a4a99886 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -713,6 +713,35 @@ def visit(self, node): return instructions + @visitor.when(cil.DivNode) + def visit(self, node): + instructions = [] + + reg1 = self.get_free_reg() + reg2 = self.get_free_reg() + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + left_location = self.get_var_location(node.left) + instructions.append(mips.LoadWordNode(reg1, left_location)) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + right_location = self.get_var_location(node.right) + instructions.append(mips.LoadWordNode(reg2, right_location)) + + instructions.append(mips.DivideNode(reg1, reg2)) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(mips.LOW_REG, dest_location)) + + self.free_reg(reg1) + self.free_reg(reg2) + + return instructions + diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index c649a716..92d91706 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -25,6 +25,7 @@ def __init__(self, name): V0_REG = Register('v0') V1_REG = Register('v1') ZERO_REG = Register('zero') +LOW_REG = Register('low') class Node: pass From b76eb990f035fbdf9052986a896ef9d5b1bd2f1d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 23:09:42 -0400 Subject: [PATCH 378/520] [mips] Add less_equal routine to mips library --- src/core/cmp/mips_lib.asm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index b33f80da..9138d091 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -732,7 +732,16 @@ equals_end: +less_equal: + ble $a0 $a1 less_equal_true + li $v0 0 + j less_equal_end + +less_equal: + li $v0 1 +less_equal_end: + jr $ra From 732914e48d715be9c9d1d73171203fb9125e9903 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 23:14:34 -0400 Subject: [PATCH 379/520] [mips] Add cil.LessEqualNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index a4a99886..f92e24eb 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -741,6 +741,31 @@ def visit(self, node): self.free_reg(reg2) return instructions + + @visitor.when(cil.LessEqualNode) + def visit(self, node): + instructions = [] + #Save $a0, $a1, $v0 + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + left_location = self.get_var_location(node.left) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + right_location = self.get_var_location(node.right) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) + + instruction.append(mips.JumpAndLinkNode('less_equal')) + dest_location = self.get_var_location(node.dest) + instruction.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + return instructions + + From 8ad3d864005419e9271c4802b3a42eac51bb161d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 23:15:43 -0400 Subject: [PATCH 380/520] [mips] Add less routine to mips library --- src/core/cmp/mips_lib.asm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 9138d091..4c2a48dd 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -744,6 +744,18 @@ less_equal_end: jr $ra +less: + blt $a0 $a1 less_true + li $v0 0 + j less_end + +less_true: + li $v0 1 + +less_end: + jr $ra + + From f93f59f67fb062f7aab9814ca356b387566bbecc Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 23 Sep 2020 23:16:28 -0400 Subject: [PATCH 381/520] [mips] Add cil.LessNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index f92e24eb..e26f0dfb 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -765,6 +765,31 @@ def visit(self, node): return instructions + @visitor.when(cil.LessNode) + def visit(self, node): + instructions = [] + #Save $a0, $a1, $v0 + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + left_location = self.get_var_location(node.left) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + right_location = self.get_var_location(node.right) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) + + instruction.append(mips.JumpAndLinkNode('less')) + dest_location = self.get_var_location(node.dest) + instruction.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + return instructions + + + From ff198f6f8dcb3af424a755337a173ccc03bd9274 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 24 Sep 2020 20:11:09 -0400 Subject: [PATCH 382/520] [cil] - Change `ReadNode` for `ReadStrNode` and `ReadIntNode` Also remove `ToStrNode` --- src/core/cmp/cil.py | 7 +++---- src/core/cmp/cool_to_cil.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 4d6b44bd..561cb9d6 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -179,12 +179,11 @@ def __init__(self, dest, str_value, index, length): self.index = index self.length = length -class ToStrNode(InstructionNode): - def __init__(self, dest, ivalue): +class ReadStrNode(InstructionNode): + def __init__(self, dest): self.dest = dest - self.ivalue = ivalue -class ReadNode(InstructionNode): +class ReadIntNode(InstructionNode): def __init__(self, dest): self.dest = dest diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 1e9305e2..8536da83 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -133,7 +133,7 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('in_string', 'IO')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.ReadNode(result)) + self.register_instruction(cil.ReadStrNode(result)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) @@ -142,7 +142,7 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.ReadNode(result)) + self.register_instruction(cil.ReadIntNode(result)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) From cec13e259e91d7639ec90155bf339e952c9f735a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 15:28:53 -0400 Subject: [PATCH 383/520] [mips] Add read_str routine to mips_library --- src/core/cmp/mips_lib.asm | 207 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 4c2a48dd..4e25c333 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -16,6 +16,7 @@ object_mark = -1 meta_data_object_size = 4 #in words object_expanded = -2 reachable = 1 +new_line = 10 @@ -756,6 +757,212 @@ less_end: jr $ra +len: + addiu $sp $sp -8 + sw $t0 0($sp) + sw $t1 4($sp) + + move $t0 $a0 + move $v0 $zero + +len_loop: + lb $t1 0($t0) + beq $t1 $zero len_end + addi $v0 $v0 1 + addiu $t0 $t0 4 + j len_loop + +len_end: + lw $t0 0($sp) + lw $t1 4($sp) + addiu $sp $sp 8 + + jr $ra + + +use_block: + addiu $sp $sp -12 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + + addiu $t0 $gp free_list + +use_block_loop: + move $t1 $t0 + lw $t0 header_next_slot($t0) + beq $t0 $zero use_block_end + beq $t0 $a0 use_block_founded + j use_block_loop + +use_block_founded: + lw $t2 header_next_slot($t0) + sw $t2 header_next_slot($t1) + + addiu $t1 $gp used_list + lw $t2 header_next_slot($t1) + sw $t0 header_next_slot($t1) + sw $t2 header_next_slot($t0) + +use_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + addiu $sp $sp 12 + + jr $ra + + + + +read_str: + addiu $sp $sp -28 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $t4 16($sp) + sw $t5 20($sp) + sw $ra 24($sp) + + addiu $t0 $gp free_list + move $t1 $zero + move $t2 $t0 + +read_str_larger_block_loop: + lw $t0 header_next_slot($t0) + beq $t0 $zero read_str_reading + lw $t3 header_size_slot($t0) + bge $t1 $t3 read_str_larger_block_loop + move $t1 $t3 + move $t2 $t0 + j read_str_larger_block_loop + +read_str_reading: + beq $t1 $zero read_str_new_block + move $a1 $t1 + li $v0 8 + addiu $a0 $t2 header_size + syscall + move $t0 $a0 + move $t1 $zero + +read_str_look_nl: + lb $t2 0($t0) + beq $t2 $zero read_str_no_nl + beq $t2 new_line read_str_nl_founded + addi $t1 $t1 1 + addi $t0 $t0 1 + j read_str_look_nl + +read_str_nl_founded: + sb $zero 0($t0) + addi $t1 $t1 1 + li $t2 4 + div $t1 $t2 + mfhi $t3 + beq $t3 $zero read_str_nl_founded_alligned + sub $t2 $t2 $t3 + add $t1 $t1 $t2 +read_str_nl_founded_alligned: + move $a1 $t1 + addiu $a0 $a0 neg_header_size + jal split_block + jal use_block + + addiu $v0 $a0 header_size + j read_str_end + + +read_str_no_nl: + addi $t1 $t1 1 + blt $t1 str_size_treshold read_str_dup + addi $t1 $t1 alloc_size + j read_str_extend_heap +read_str_dup: + sll $t1 $t1 1 +read_str_extend_heap: + move $a1 $t1 + move $t0 $a0 + addiu $a0 $gp free_list + +read_str_last_block_loop: + lw $t1 header_next_slot($a0) + beq $t1 $zero read_str_last_block_founded + lw $a0 header_next_slot($a0) + j read_str_last_block_loop + +read_str_last_block_founded: + jal extend_heap + jal expand_block + lw $t1 header_next_slot($a0) + bne $t1 $zero read_str_copy_prev + move $t1 $a0 + +read_str_copy_prev: + lw $t3 header_size_slot($t1) + move $t2 $zero + move $t5 $t1 + addiu $t1 $t1 header_size + +read_str_copy_loop: + lb $t4 0($t0) + beq $t4 $zero read_str_copy_end + sb $t4 0($t1) + addi $t2 $t2 1 + addi $t0 $t0 1 + addi $t1 $t1 1 + j read_str_copy_loop + +read_str_copy_end: + sub $t3 $t3 $t2 + move $a0 $t1 + move $a1 $t3 + li $v0 8 + syscall + move $t0 $a0 + move $t1 $t2 + addiu $a0 $t5 header_size + j read_str_look_nl + + +read_str_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $t4 16($sp) + lw $t5 20($sp) + lw $ra 24($sp) + addiu $sp $sp 28 + + jr $ra + + +read_str_new_block: + addiu $t0 $gp free_list + +read_str_new_block_search_last: + lw $t1 header_next_slot($t0) + beq $t1 $zero read_str_new_block_create + move $t0 $t1 + j read_str_new_block_search_last + +read_str_new_block_create: + move $a0 $t0 + li $a1 alloc_size + jal extend_heap + jal expand_block + lw $t2 header_next_slot($a0) + beq $t2 $zero read_str_new_block_expanded + lw $t1 header_size_slot($t2) + j read_str_reading + +read_str_new_block_expanded: + move $t2 $a0 + lw $t1 header_size_slot($a0) + j read_str_reading + From d35f58f48fd3f04cb354bdb6b240f3bfb4f1678b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 16:04:13 -0400 Subject: [PATCH 384/520] [mips] Add cil.ReadStrNode case to visit function in cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index e26f0dfb..ed3d3a8d 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -788,6 +788,17 @@ def visit(self, node): return instructions + @visitor.when(cil.ReadStrNode) + def visit(self, node): + instructions = [] + #Save $v0 + instructions.append(mips.JumpAndLinkNode("read_str")) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(mips.V0_REG), dest_location) + + return instructions + From a3c15f7062b0e2e2fa9d55c3bc35528599a84e55 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 16:08:54 -0400 Subject: [PATCH 385/520] [mips] Add cil.LengthNode case to visit function in cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index ed3d3a8d..6fa19578 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -798,6 +798,22 @@ def visit(self, node): instructions.append(mips.StoreWordNode(mips.V0_REG), dest_location) return instructions + + @visitor.when(cil.LengthNode) + def visit(self, node): + instructions = [] + #save $a0 $v0 + + src_location = self.get_var_location(node.source) + dest_location = self.get_var_location(node.dest) + + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], src_location)) + instructions.append(mips.JumpAndLinkNode("len")) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + return instructions + + From 718ffff850d171bba095a993ff909334a60a80dd Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 16:09:42 -0400 Subject: [PATCH 386/520] Fix some syntax errors --- src/core/cmp/mips.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 92d91706..23ac00e0 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -184,19 +184,19 @@ def __init__(self, label): self.label = label class AddNode(InstructionNode): - def __init__(self, reg1, reg2, reg3) + def __init__(self, reg1, reg2, reg3): self.reg1 = reg1 self.reg2 = reg2 self.reg3 = reg3 class SubNode(InstructionNode): - def __init__(self, reg1, reg2, reg3) + def __init__(self, reg1, reg2, reg3): self.reg1 = reg1 self.reg2 = reg2 self.reg3 = reg3 class MultiplyNode(InstructionNode): - def __init__(self, reg1, reg2, reg3) + def __init__(self, reg1, reg2, reg3): self.reg1 = reg1 self.reg2 = reg2 self.reg3 = reg3 From 4c20f20cbcd6f7065f55607478f01119b2810871 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 26 Sep 2020 19:57:34 -0400 Subject: [PATCH 387/520] [cil] - Update visit of `AssignNode` and add `length` attr to String attr's array --- src/core/cmp/cool_to_cil.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 8536da83..d092ee1e 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -152,7 +152,7 @@ def register_built_in(self): #String type_node = self.register_type('String') - type_node.attributes = ['value'] + type_node.attributes = ['value', 'length'] self.current_function = self.register_function(self.to_function_name('init', 'String')) self.register_param(VariableInfo('val', None)) @@ -518,11 +518,9 @@ def visit(self, node, scope): try: self.current_type.get_attribute(node.id) self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr, self.current_type.name)) - scope.ret_expr = node.id except SemanticError: vname = self.register_local(VariableInfo(node.id, None)) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) - scope.ret_expr = vname @visitor.when(cool.NotNode) def visit(self, node, scope): From ef231b75573e6cccbb9df3e711b6cfa59f0dd912 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 26 Sep 2020 22:12:42 -0400 Subject: [PATCH 388/520] [cil] - Search for node.id in params and localvars in visit of AssignNode --- src/core/cmp/cool_to_cil.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index d092ee1e..0d9394ed 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -519,7 +519,18 @@ def visit(self, node, scope): self.current_type.get_attribute(node.id) self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr, self.current_type.name)) except SemanticError: - vname = self.register_local(VariableInfo(node.id, None)) + vname = None + param_names = [pn.name for pn in self.current_function.params] + if node.id in param_names: + for n in param_names: + if node.id in n.split("_"): + vname = n + break + else: + for n in [lv.name for lv in self.current_function.localvars]: + if node.id in n.split("_"): + vname = n + break self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) @visitor.when(cool.NotNode) @@ -827,7 +838,7 @@ def visit(self, node, scope): ############################### try: self.current_type.get_attribute(node.lex) - attr = self.register_local(VariableInfo('attr_value', None)) + attr = self.register_local(VariableInfo(node.lex, None)) self.register_instruction(cil.GetAttribNode(attr, self.vself.name, node.lex, self.current_type.name)) scope.ret_expr = attr except SemanticError: From 397bbe714de017d7089f98e4ff533f9b21056f5e Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 22:26:17 -0400 Subject: [PATCH 389/520] [mips] Fix some errors in mips_library --- src/core/cmp/mips_lib.asm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 4e25c333..91d89190 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -17,7 +17,7 @@ meta_data_object_size = 4 #in words object_expanded = -2 reachable = 1 new_line = 10 - +str_size_treshold = 1024 @@ -738,7 +738,7 @@ less_equal: li $v0 0 j less_equal_end -less_equal: +less_equal_true: li $v0 1 less_equal_end: @@ -769,7 +769,7 @@ len_loop: lb $t1 0($t0) beq $t1 $zero len_end addi $v0 $v0 1 - addiu $t0 $t0 4 + addiu $t0 $t0 1 j len_loop len_end: @@ -876,7 +876,7 @@ read_str_nl_founded_alligned: read_str_no_nl: addi $t1 $t1 1 - blt $t1 str_size_treshold read_str_dup + blt $t1 str_size_treshold read_str_dup addi $t1 $t1 alloc_size j read_str_extend_heap read_str_dup: From 851ca5e058d5341e17d4775251feb2c3f0eb571c Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 22:28:37 -0400 Subject: [PATCH 390/520] [mips] Fix typo error --- src/core/cmp/cil_to_mips.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 6fa19578..7c3ef89b 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -148,6 +148,7 @@ def visit(self, node): def visit(self, node): #Get functions names self.collect_func_names(node) + print(self._name_func_map) #Convert CIL ProgramNode to MIPS ProgramNode for tp in node.dottypes: @@ -205,16 +206,32 @@ def visit(self, node): code_instructions = [] #This try-except block is for debuggin purposes - if node.name == "function_get_x_at_A": + if node.name == "function_main_at_Main": print(node.instructions) - + print(f"{node.instructions[0].dest} = CALL {node.instructions[0].function}") + print(f"{node.instructions[1].dest} = {node.instructions[1].source}") + print(f"ARG {node.instructions[2].name}") + print(f"{node.instructions[3].dest} = CALL {node.instructions[3].function}") + print(f"{node.instructions[4].dest} = {node.instructions[4].source}") + print(f"{node.instructions[5].dest} = {node.instructions[5].source}") + print(f"{node.instructions[6].dest} = {node.instructions[6].source}") + print(f"ARG {node.instructions[-6].name}") + print(f"ARG {node.instructions[-5].name}") + try: code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) - if node.name == "function_main_at_Main": - print(node.instructions[-7].dest) - + # for i, ins in enumerate(node.instructions): + # try: + # self.visit(ins) + # except Exception as e: + # if node.name == "function_in_string_at_IO": + # print(i) + # print(ins) + # print(ins.dest) + # print(e) + # raise e except Exception as e: - if node.name == "function_init_at_A": + if node.name == "function_in_string_at_IO": print(e) print(node.instructions) print(node.name) @@ -400,6 +417,7 @@ def visit(self, node): instructions.append(mips.ShiftLeftLogicalNode(reg, reg, 2)) instructions.append(mips.LoadAddressNode(reg2, mips.TYPENAMES_TABLE_LABEL)) instructions.append(mips.AddUnsignedNode(reg, reg, reg2)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) instructions.append(mips.StoreWordNode(reg, dst_location)) self.free_reg(reg) @@ -451,7 +469,9 @@ def visit(self, node): obj_location = self.get_var_location(obj) tp = self._types[comp_type] - + # print(self._actual_function.label) + # print(node.value) + # print(tp.attributes) offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE @@ -795,7 +815,7 @@ def visit(self, node): instructions.append(mips.JumpAndLinkNode("read_str")) dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG), dest_location) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) return instructions From 0955e9ad7c00ec9c8d7e8e8a957bc867462be34a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 23:13:17 -0400 Subject: [PATCH 391/520] [mips] Remove args pushed to the stack after at the end of a DynamicCallNode --- src/core/cmp/cil_to_mips.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 7c3ef89b..50bc92e3 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -609,6 +609,9 @@ def visit(self, node): self.free_reg(reg1) self.free_reg(reg2) + if self._pushed_args > 0: + instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) + self.clean_pushed_args() return instructions From 3da15b3567c1d840d8ee2847d1279ff705ddd78f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 26 Sep 2020 23:23:00 -0400 Subject: [PATCH 392/520] [mips] Add cil.ReadInt case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 50bc92e3..64d392f7 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -206,18 +206,6 @@ def visit(self, node): code_instructions = [] #This try-except block is for debuggin purposes - if node.name == "function_main_at_Main": - print(node.instructions) - print(f"{node.instructions[0].dest} = CALL {node.instructions[0].function}") - print(f"{node.instructions[1].dest} = {node.instructions[1].source}") - print(f"ARG {node.instructions[2].name}") - print(f"{node.instructions[3].dest} = CALL {node.instructions[3].function}") - print(f"{node.instructions[4].dest} = {node.instructions[4].source}") - print(f"{node.instructions[5].dest} = {node.instructions[5].source}") - print(f"{node.instructions[6].dest} = {node.instructions[6].source}") - print(f"ARG {node.instructions[-6].name}") - print(f"ARG {node.instructions[-5].name}") - try: code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) # for i, ins in enumerate(node.instructions): @@ -836,6 +824,19 @@ def visit(self, node): return instructions + @visitor.when(cil.ReadIntNode) + def visit(self, node): + instructions = [] + #save $v0 + dest_location = self.get_var_location(node.dest) + + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + return instructions + + From cb734e34c94a8e89f36ebc9960297610a12333ac Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 27 Sep 2020 16:04:21 -0400 Subject: [PATCH 393/520] [mips] Add concat routine to mips_library --- src/core/cmp/mips_lib.asm | 70 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 91d89190..75fd5b50 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -816,14 +816,16 @@ use_block_end: read_str: - addiu $sp $sp -28 + addiu $sp $sp -36 sw $t0 0($sp) sw $t1 4($sp) sw $t2 8($sp) sw $t3 12($sp) sw $t4 16($sp) sw $t5 20($sp) - sw $ra 24($sp) + sw $a0 24($sp) + sw $a1 28($sp) + sw $ra 32($sp) addiu $t0 $gp free_list move $t1 $zero @@ -933,8 +935,10 @@ read_str_end: lw $t3 12($sp) lw $t4 16($sp) lw $t5 20($sp) - lw $ra 24($sp) - addiu $sp $sp 28 + sw $a0 24($sp) + sw $a1 28($sp) + sw $ra 32($sp) + addiu $sp $sp 36 jr $ra @@ -965,6 +969,64 @@ read_str_new_block_expanded: +concat: + addiu $sp $sp -24 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $a0 12($sp) + sw $a1 16($sp) + sw $ra 20($sp) + + move $t0 $a0 + move $t1 $a1 + addiu $a0 $a0 neg_header_size + addiu $a1 $a1 neg_header_size + + lw $a0 header_size_slot($a0) + lw $a1 header_size_slot($a1) + + add $a0 $a0 $a1 + jal malloc + move $t2 $v0 + +concat_copy_first_loop: + lb $a0 0($t0) + beq $a0 $zero concat_copy_second_loop + sb $a0 0($t2) + addiu $t0 $t0 1 + addiu $t2 $t2 1 + j concat_copy_first_loop + +concat_copy_second_loop: + lb $a0 0($t1) + beq $a0 $zero concat_end + sb $a0 0($t2) + addiu $t1 $t1 1 + addiu $t2 $t2 1 + j concat_copy_second_loop + +concat_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $a0 12($sp) + lw $a1 16($sp) + lw $ra 20($sp) + addiu $sp $sp 24 + + jr $ra + + + + + + + + + + + From 125aeb436b5a5f2b93f58401416a531402f1efbd Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 27 Sep 2020 16:08:48 -0400 Subject: [PATCH 394/520] [mips] Add cil.ConcatNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 64d392f7..b764a516 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -836,8 +836,22 @@ def visit(self, node): return instructions + @visitor.when(cil.ConcatNode) + def visit(self, node): + instructions = [] + #save $a0, $a1, $v0 + + prefix_location = self.get_var_location(node.prefix) + suffix_location = self.get_var_location(node.suffix) + dest_location = self.get_var_location(node.dest) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], prefix_location)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], suffix_location)) + instructions.append(mips.JumpAndLinkNode("concat")) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + return instructions From 8f1756ad5b375d0d319cefc349aaaea9e5a6ba22 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 27 Sep 2020 17:33:29 -0400 Subject: [PATCH 395/520] [mips] Add substr routine to mips_library --- src/core/cmp/mips_lib.asm | 72 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 75fd5b50..7eb09187 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -935,9 +935,9 @@ read_str_end: lw $t3 12($sp) lw $t4 16($sp) lw $t5 20($sp) - sw $a0 24($sp) - sw $a1 28($sp) - sw $ra 32($sp) + lw $a0 24($sp) + lw $a1 28($sp) + lw $ra 32($sp) addiu $sp $sp 36 jr $ra @@ -1007,6 +1007,7 @@ concat_copy_second_loop: j concat_copy_second_loop concat_end: + sb $zero 0($t2) lw $t0 0($sp) lw $t1 4($sp) lw $t2 8($sp) @@ -1018,6 +1019,71 @@ concat_end: jr $ra +substr: + addiu $sp $sp -24 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $a0 16($sp) + sw $ra 20($sp) + + move $t0 $a0 + li $t1 4 + + div $a2 $t1 + + mfhi $t2 + bne $t2 $zero substr_allign_size + move $t1 $a2 + j substr_new_block + +substr_allign_size: + sub $t1 $t1 $t2 + add $t1 $t1 $a2 + +substr_new_block: + move $a0 $t1 + jal malloc + move $t3 $v0 + move $t1 $zero + addu $t0 $t0 $a1 + +substr_copy_loop: + beq $t1 $a2 substr_end + lb $t2 0($t0) + sb $t2 0($t3) + addiu $t0 $t0 1 + addiu $t3 $t3 1 + addiu $t1 $t1 1 + j substr_copy_loop + +substr_end: + sb $zero 0($t3) + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $a0 16($sp) + lw $ra 20($sp) + addiu $sp $sp 24 + + jr $ra + + + + + + + + + + + + + + + From e4eae77a3f4318fce5616f8fe9eca2d3adb9953e Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 27 Sep 2020 17:33:58 -0400 Subject: [PATCH 396/520] [mips] Add cil.SubstringNode case to visit function of cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index b764a516..15887d19 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -852,7 +852,34 @@ def visit(self, node): instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) return instructions - + + @visitor.when(cil.SubstringNode) + def visit(self, node): + instructions = [] + + #save $a0, $a1, $a2, $v0 + + str_location = self.get_var_location(node.str_value) + dest_location = self.get_var_location(node.dest) + + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], str_location)) + + if type(node.index) == int: + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.index)) + else: + index_location = self.get_var_location(node.index) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], index_location)) + + if type(node.length) == int: + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[2], node.length)) + else: + lenght_location = self.get_var_location(node.length) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) + + instructions.append(mips.JumpAndLinkNode("substr")) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + return instructions From 536471c71647f28b4b9f48d14bf6ea88b0440e47 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 27 Sep 2020 17:39:04 -0400 Subject: [PATCH 397/520] [mips] Fix typo in cil.LessNode case of visit function in cil_to_mips visitor --- compiled.asm | 1155 ++++++++++++++++++++++++++++++----- src/core/cmp/cil_to_mips.py | 17 +- 2 files changed, 1003 insertions(+), 169 deletions(-) diff --git a/compiled.asm b/compiled.asm index 1309123d..66ddac15 100644 --- a/compiled.asm +++ b/compiled.asm @@ -5,13 +5,13 @@ data_3: .asciiz "String" data_4: .asciiz "Int" data_5: .asciiz "A" data_6: .asciiz "Main" -data_7: .asciiz "B" -data_8: .asciiz "Program aborted" -data_9: .asciiz "Dispatch on void" -data_10: .asciiz "Case on void" -data_11: .asciiz "Execution of a case statement without a matching branch" -data_12: .asciiz "Division by zero" -data_13: .asciiz "Substring out of range" +data_7: .asciiz "Program aborted" +data_8: .asciiz "Dispatch on void" +data_9: .asciiz "Case on void" +data_10: .asciiz "Execution of a case statement without a matching branch" +data_11: .asciiz "Division by zero" +data_12: .asciiz "Substring out of range" +data_13: .asciiz "n" type_name_table: .word data_1 @@ -20,7 +20,6 @@ type_name_table: .word data_4 .word data_5 .word data_6 - .word data_7 proto_table: .word type_1_proto @@ -29,7 +28,6 @@ proto_table: .word type_4_proto .word type_5_proto .word type_6_proto - .word type_7_proto type_1_dispatch: .word L_1 @@ -64,9 +62,10 @@ type_3_dispatch: type_3_proto: .word 2 - .word 5 + .word 6 .word type_3_dispatch .word 0 + .word 0 .word -1 type_4_dispatch: @@ -83,12 +82,19 @@ type_5_dispatch: .word L_2 .word L_3 .word L_4 + .word L_15 + .word L_16 + .word L_17 + .word L_18 + .word L_19 type_5_proto: .word 4 - .word 5 + .word 7 .word type_5_dispatch .word 0 + .word 0 + .word 0 .word -1 type_6_dispatch: @@ -99,26 +105,12 @@ type_6_dispatch: .word L_7 .word L_8 .word L_9 - .word L_16 + .word L_21 type_6_proto: .word 5 - .word 6 + .word 4 .word type_6_dispatch - .word 0 - .word 0 - .word -1 - -type_7_dispatch: - .word L_2 - .word L_3 - .word L_4 - -type_7_proto: - .word 6 - .word 5 - .word type_7_dispatch - .word 0 .word -1 .text .globl main @@ -144,7 +136,7 @@ main: lw $t0, -12($fp) addi $sp, $sp, -4 sw $t0, 0($sp) - jal L_16 + jal L_21 sw $v0, -8($fp) addi $sp, $sp, 4 li $v0, 0 @@ -159,15 +151,15 @@ L_1: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 sw $t0, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) li $t0, 0 @@ -186,15 +178,15 @@ L_1: lw $v0, -8($fp) lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t1, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 - lw $t0, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) + lw $t1, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -209,7 +201,7 @@ L_2: sw $t0, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) - la $t0, data_8 + 0 + la $t0, data_7 + 0 sw $t0, -8($fp) li $v0, 4 lw $a0, -8($fp) @@ -230,16 +222,17 @@ L_3: addi $fp, $sp, 4 addi $sp, $sp, -8 addi $sp, $sp, -4 - sw $ra, 0($sp) - addi $sp, $sp, -4 sw $t0, 0($sp) addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 sw $t1, 0($sp) lw $t0, 0($fp) lw $t0, 0($t0) sll $t0 $t0 2 la $t1, type_name_table addu $t0 $t0 $t1 + lw $t0, 0($t0) sw $t0, -8($fp) lw $t0, -8($fp) addi $sp, $sp, -4 @@ -250,10 +243,10 @@ L_3: lw $v0, -12($fp) lw $t1, 0($sp) addi $sp, $sp, 4 - lw $t0, 0($sp) - addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -264,14 +257,14 @@ L_4: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) addi $sp, $sp, -4 sw $t0, 0($sp) addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 sw $ra, 0($sp) lw $t0, 0($fp) lw $a0, 4($t0) @@ -284,14 +277,14 @@ L_4: lw $v0, -8($fp) lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t0, 0($sp) - addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -302,15 +295,15 @@ L_5: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $a0, 0($sp) - addi $sp, $sp, -4 - sw $a2, 0($sp) - addi $sp, $sp, -4 sw $a1, 0($sp) addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 sw $t0, 0($sp) addi $sp, $sp, -4 - sw $t1, 0($sp) + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) addi $sp, $sp, -4 sw $ra, 0($sp) li $t0, 0 @@ -329,15 +322,15 @@ L_5: lw $v0, -8($fp) lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t1, 0($sp) + lw $a2, 0($sp) addi $sp, $sp, 4 - lw $t0, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) + lw $t1, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) @@ -396,6 +389,23 @@ L_8: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + jal read_str + sw $v0, -8($fp) + lw $t0, -8($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_10 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $v0, -12($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -405,6 +415,24 @@ L_9: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $v0, 5 + syscall + sw $v0, -8($fp) + lw $t0, -8($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_14 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $v0, -12($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -414,6 +442,59 @@ L_10: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -12 + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t0, 2 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t1 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + lw $t1, -8($fp) + lw $t0, 0($fp) + sw $t0, 12($t1) + lw $a0, 0($fp) + jal len + sw $v0, -12($fp) + lw $t0, -12($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_14 + sw $v0, -16($fp) + addi $sp, $sp, 4 + lw $t1, -8($fp) + lw $t0, -16($fp) + sw $t0, 16($t1) + lw $v0, -8($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 12 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -423,6 +504,14 @@ L_11: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $t0, 16($t0) + sw $t0, -8($fp) + lw $v0, -8($fp) + lw $t0, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -432,6 +521,33 @@ L_12: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -8 + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $a0, 4($fp) + lw $a1, 0($fp) + jal concat + sw $v0, -8($fp) + lw $t0, -8($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_10 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $v0, -12($fp) + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -441,6 +557,69 @@ L_13: sw $fp, 0($sp) addi $fp, $sp, 4 addi $sp, $sp, -28 + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + lw $t0, 4($fp) + lw $t0, 12($t0) + sw $t0, -12($fp) + lw $t0, 0($fp) + lw $t0, 12($t0) + sw $t0, -16($fp) + lw $t0, 8($fp) + lw $t0, 16($t0) + sw $t0, -20($fp) + lw $t0, -16($fp) + lw $t1, -12($fp) + add $t0 $t0 $t1 + sw $t0, -24($fp) + lw $a0, -20($fp) + lw $a1, -24($fp) + jal less + sw $v0, -28($fp) + lw $t0, -28($fp) + bne $t0 $zero L_23 + j L_24 + L_23: + li $v0, 4 + la $a0, data_12 + syscall + li $v0, 10 + syscall + L_24: + lw $a0, 8($fp) + lw $a1, -12($fp) + lw $a2, -16($fp) + jal substr + sw $v0, -8($fp) + lw $t0, -8($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_10 + sw $v0, -32($fp) + addi $sp, $sp, 4 + lw $v0, -32($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 28 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -451,167 +630,475 @@ L_14: addi $fp, $sp, 4 addi $sp, $sp, -4 addi $sp, $sp, -4 - sw $a0, 0($sp) + sw $a1, 0($sp) addi $sp, $sp, -4 - sw $a2, 0($sp) + sw $t1, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t0, 0($sp) addi $sp, $sp, -4 - sw $t4, 0($sp) + sw $a0, 0($sp) addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $a2, 0($sp) addi $sp, $sp, -4 - sw $t5, 0($sp) - li $t4, 3 - sll $t4 $t4 2 - la $t5, proto_table - addu $t5 $t5 $t4 - lw $t5, 0($t5) - lw $a0, 4($t5) + sw $ra, 0($sp) + li $t0, 3 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t5 + move $a0 $t1 move $a1 $v0 jal copy sw $v0, -8($fp) - lw $t5, -8($fp) - lw $t4, 0($fp) - sw $t4, 12($t5) + lw $t1, -8($fp) + lw $t0, 0($fp) + sw $t0, 12($t1) lw $v0, -8($fp) - lw $t5, 0($sp) - addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t4, 0($sp) + lw $a2, 0($sp) + addi $sp, $sp, 4 + lw $a0, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) addi $sp, $sp, 4 lw $a1, 0($sp) addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_15: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $t0, 12($t0) + sw $t0, -8($fp) + lw $v0, -8($fp) + lw $t0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_16: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, 0 + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, 4($fp) + lw $t0, 0($fp) + sw $t0, 16($t1) + lw $v0, 0($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 0 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_17: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $t0, 16($t0) + sw $t0, -8($fp) + lw $v0, -8($fp) + lw $t0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_18: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, 0 + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + lw $t1, 4($fp) + lw $t0, 0($fp) + sw $t0, 20($t1) + lw $v0, 0($fp) + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 0 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_19: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -4 + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, 0($fp) + lw $t0, 20($t0) + sw $t0, -8($fp) + lw $v0, -8($fp) + lw $t0, 0($sp) + addi $sp, $sp, 4 + addi $sp, $sp, 4 + lw $fp, 0($sp) + addi $sp, $sp, 4 + jr $ra +L_20: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -8 + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $a2, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + li $t0, 4 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) + sll $a0 $a0 2 + jal malloc + move $a2 $a0 + move $a0 $t1 + move $a1 $v0 + jal copy + sw $v0, -8($fp) + li $t0, 18 + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_14 + sw $v0, -12($fp) + addi $sp, $sp, 4 + lw $t1, -8($fp) + lw $t0, -12($fp) + sw $t0, 12($t1) + lw $v0, -8($fp) + lw $ra, 0($sp) + addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) addi $sp, $sp, 4 + addi $sp, $sp, 8 lw $fp, 0($sp) addi $sp, $sp, 4 - jr $ra -L_15: + jr $ra +L_21: + addi $sp, $sp, -4 + sw $fp, 0($sp) + addi $fp, $sp, 4 + addi $sp, $sp, -128 + addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + addi $sp, $sp, -4 + sw $a0, 0($sp) + addi $sp, $sp, -4 + sw $ra, 0($sp) + jal L_20 + sw $v0, -16($fp) + lw $t0, -16($fp) + sw $t0, -12($fp) + lw $t0, 0($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_8 + sw $v0, -24($fp) + addi $sp, $sp, 4 + lw $t0, -24($fp) + sw $t0, -20($fp) + lw $t0, -20($fp) + sw $t0, -28($fp) + lw $t0, -12($fp) + sw $t0, -36($fp) + lw $a0, -36($fp) + li $a1, 0 + jal equals + sw $v0, -40($fp) + lw $t0, -40($fp) + bne $t0 $zero L_25 + j L_26 + L_25: + li $v0, 4 + la $a0, data_8 + syscall + li $v0, 10 + syscall + L_26: + lw $t0, -36($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, -28($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, -36($fp) + lw $t0, 0($t0) + sw $t0, -44($fp) + la $t0, proto_table + lw $t1, -44($fp) + sll $t1 $t1 2 + addu $t0 $t0 $t1 + lw $t0, 0($t0) + lw $t0, 8($t0) + addiu $t0 $t0 16 + lw $t0, 0($t0) + jal $t0 + sw $v0, -32($fp) + addi $sp, $sp, 8 + lw $t0, -12($fp) + sw $t0, -56($fp) + lw $a0, -56($fp) + li $a1, 0 + jal equals + sw $v0, -60($fp) + lw $t0, -60($fp) + bne $t0 $zero L_27 + j L_28 + L_27: + li $v0, 4 + la $a0, data_8 + syscall + li $v0, 10 + syscall + L_28: + lw $t0, -56($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, -56($fp) + lw $t0, 0($t0) + sw $t0, -64($fp) + la $t0, proto_table + lw $t1, -64($fp) + sll $t1 $t1 2 + addu $t0 $t0 $t1 + lw $t0, 0($t0) + lw $t0, 8($t0) + addiu $t0 $t0 20 + lw $t0, 0($t0) + jal $t0 + sw $v0, -52($fp) + addi $sp, $sp, 4 + lw $t0, -52($fp) + sw $t0, -48($fp) + lw $t0, 0($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + lw $t0, -48($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_6 + sw $v0, -68($fp) + addi $sp, $sp, 8 + la $t0, data_13 + 0 + sw $t0, -76($fp) + lw $t0, -76($fp) + addi $sp, $sp, -4 + sw $t0, 0($sp) + jal L_10 + sw $v0, -80($fp) + addi $sp, $sp, 4 + lw $t0, -80($fp) + sw $t0, -72($fp) + lw $t0, 0($fp) addi $sp, $sp, -4 - sw $fp, 0($sp) - addi $fp, $sp, 4 - addi $sp, $sp, -8 + sw $t0, 0($sp) + lw $t0, -72($fp) addi $sp, $sp, -4 - sw $a0, 0($sp) + sw $t0, 0($sp) + jal L_6 + sw $v0, -84($fp) + addi $sp, $sp, 8 + lw $t0, 0($fp) addi $sp, $sp, -4 - sw $a2, 0($sp) + sw $t0, 0($sp) + jal L_9 + sw $v0, -92($fp) + addi $sp, $sp, 4 + lw $t0, -92($fp) + sw $t0, -88($fp) + lw $t0, -12($fp) + sw $t0, -100($fp) + lw $a0, -100($fp) + li $a1, 0 + jal equals + sw $v0, -104($fp) + lw $t0, -104($fp) + bne $t0 $zero L_29 + j L_30 + L_29: + li $v0, 4 + la $a0, data_8 + syscall + li $v0, 10 + syscall + L_30: + lw $t0, -100($fp) addi $sp, $sp, -4 - sw $a1, 0($sp) + sw $t0, 0($sp) + lw $t0, -88($fp) addi $sp, $sp, -4 - sw $t4, 0($sp) + sw $t0, 0($sp) + lw $t0, -100($fp) + lw $t0, 0($t0) + sw $t0, -108($fp) + la $t0, proto_table + lw $t1, -108($fp) + sll $t1 $t1 2 + addu $t0 $t0 $t1 + lw $t0, 0($t0) + lw $t0, 8($t0) + addiu $t0 $t0 24 + lw $t0, 0($t0) + jal $t0 + sw $v0, -96($fp) + addi $sp, $sp, 8 + lw $t0, -12($fp) + sw $t0, -120($fp) + lw $a0, -120($fp) + li $a1, 0 + jal equals + sw $v0, -124($fp) + lw $t0, -124($fp) + bne $t0 $zero L_31 + j L_32 + L_31: + li $v0, 4 + la $a0, data_8 + syscall + li $v0, 10 + syscall + L_32: + lw $t0, -120($fp) addi $sp, $sp, -4 - sw $ra, 0($sp) + sw $t0, 0($sp) + lw $t0, -120($fp) + lw $t0, 0($t0) + sw $t0, -128($fp) + la $t0, proto_table + lw $t1, -128($fp) + sll $t1 $t1 2 + addu $t0 $t0 $t1 + lw $t0, 0($t0) + lw $t0, 8($t0) + addiu $t0 $t0 28 + lw $t0, 0($t0) + jal $t0 + sw $v0, -116($fp) + addi $sp, $sp, 4 + lw $t0, -116($fp) + sw $t0, -112($fp) + lw $t0, 0($fp) addi $sp, $sp, -4 - sw $t5, 0($sp) - li $t4, 4 - sll $t4 $t4 2 - la $t5, proto_table - addu $t5 $t5 $t4 - lw $t5, 0($t5) - lw $a0, 4($t5) - sll $a0 $a0 2 - jal malloc - move $a2 $a0 - move $a0 $t5 - move $a1 $v0 - jal copy - sw $v0, -8($fp) - li $t4, 3 + sw $t0, 0($sp) + lw $t0, -112($fp) addi $sp, $sp, -4 - sw $t4, 0($sp) - jal L_14 - sw $v0, -12($fp) - addi $sp, $sp, 4 - lw $t5, -8($fp) - lw $t4, -12($fp) - sw $t4, 12($t5) + sw $t0, 0($sp) + jal L_7 + sw $v0, -132($fp) + addi $sp, $sp, 8 + lw $t0, -132($fp) + sw $t0, -8($fp) lw $v0, -8($fp) - lw $t5, 0($sp) - addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $t4, 0($sp) - addi $sp, $sp, 4 - lw $a1, 0($sp) + lw $a0, 0($sp) addi $sp, $sp, 4 - lw $a2, 0($sp) + lw $t0, 0($sp) addi $sp, $sp, 4 - lw $a0, 0($sp) + lw $t1, 0($sp) addi $sp, $sp, 4 - addi $sp, $sp, 8 - lw $fp, 0($sp) + lw $a1, 0($sp) addi $sp, $sp, 4 - jr $ra -L_16: - addi $sp, $sp, -4 - sw $fp, 0($sp) - addi $fp, $sp, 4 - addi $sp, $sp, -128 addi $sp, $sp, 128 lw $fp, 0($sp) addi $sp, $sp, 4 jr $ra -L_17: +L_22: addi $sp, $sp, -4 sw $fp, 0($sp) addi $fp, $sp, 4 - addi $sp, $sp, -12 - addi $sp, $sp, 12 - lw $fp, 0($sp) - addi $sp, $sp, 4 - jr $ra -L_18: addi $sp, $sp, -4 - sw $fp, 0($sp) - addi $fp, $sp, 4 addi $sp, $sp, -4 + sw $a1, 0($sp) + addi $sp, $sp, -4 + sw $t1, 0($sp) + addi $sp, $sp, -4 + sw $t0, 0($sp) addi $sp, $sp, -4 sw $a0, 0($sp) addi $sp, $sp, -4 sw $a2, 0($sp) addi $sp, $sp, -4 - sw $a1, 0($sp) - addi $sp, $sp, -4 sw $ra, 0($sp) - addi $sp, $sp, -4 - sw $t6, 0($sp) - addi $sp, $sp, -4 - sw $t5, 0($sp) - li $t5, 6 - sll $t5 $t5 2 - la $t6, proto_table - addu $t6 $t6 $t5 - lw $t6, 0($t6) - lw $a0, 4($t6) + li $t0, 5 + sll $t0 $t0 2 + la $t1, proto_table + addu $t1 $t1 $t0 + lw $t1, 0($t1) + lw $a0, 4($t1) sll $a0 $a0 2 jal malloc move $a2 $a0 - move $a0 $t6 + move $a0 $t1 move $a1 $v0 jal copy sw $v0, -8($fp) lw $v0, -8($fp) - lw $t5, 0($sp) - addi $sp, $sp, 4 - lw $t6, 0($sp) - addi $sp, $sp, 4 lw $ra, 0($sp) addi $sp, $sp, 4 - lw $a1, 0($sp) - addi $sp, $sp, 4 lw $a2, 0($sp) addi $sp, $sp, 4 lw $a0, 0($sp) addi $sp, $sp, 4 + lw $t0, 0($sp) + addi $sp, $sp, 4 + lw $t1, 0($sp) + addi $sp, $sp, 4 + lw $a1, 0($sp) + addi $sp, $sp, 4 addi $sp, $sp, 4 lw $fp, 0($sp) addi $sp, $sp, 4 @@ -633,7 +1120,8 @@ object_mark = -1 meta_data_object_size = 4 #in words object_expanded = -2 reachable = 1 - +new_line = 10 +str_size_treshold = 1024 @@ -1349,12 +1837,367 @@ equals_end: +less_equal: + ble $a0 $a1 less_equal_true + li $v0 0 + j less_equal_end + +less_equal_true: + li $v0 1 + +less_equal_end: + jr $ra + + +less: + blt $a0 $a1 less_true + li $v0 0 + j less_end + +less_true: + li $v0 1 + +less_end: + jr $ra + + +len: + addiu $sp $sp -8 + sw $t0 0($sp) + sw $t1 4($sp) + + move $t0 $a0 + move $v0 $zero + +len_loop: + lb $t1 0($t0) + beq $t1 $zero len_end + addi $v0 $v0 1 + addiu $t0 $t0 1 + j len_loop + +len_end: + lw $t0 0($sp) + lw $t1 4($sp) + addiu $sp $sp 8 + + jr $ra + + +use_block: + addiu $sp $sp -12 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + + addiu $t0 $gp free_list + +use_block_loop: + move $t1 $t0 + lw $t0 header_next_slot($t0) + beq $t0 $zero use_block_end + beq $t0 $a0 use_block_founded + j use_block_loop + +use_block_founded: + lw $t2 header_next_slot($t0) + sw $t2 header_next_slot($t1) + + addiu $t1 $gp used_list + lw $t2 header_next_slot($t1) + sw $t0 header_next_slot($t1) + sw $t2 header_next_slot($t0) + +use_block_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + addiu $sp $sp 12 + + jr $ra + + + + +read_str: + addiu $sp $sp -36 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $t4 16($sp) + sw $t5 20($sp) + sw $a0 24($sp) + sw $a1 28($sp) + sw $ra 32($sp) + + addiu $t0 $gp free_list + move $t1 $zero + move $t2 $t0 + +read_str_larger_block_loop: + lw $t0 header_next_slot($t0) + beq $t0 $zero read_str_reading + lw $t3 header_size_slot($t0) + bge $t1 $t3 read_str_larger_block_loop + move $t1 $t3 + move $t2 $t0 + j read_str_larger_block_loop + +read_str_reading: + beq $t1 $zero read_str_new_block + move $a1 $t1 + li $v0 8 + addiu $a0 $t2 header_size + syscall + move $t0 $a0 + move $t1 $zero + +read_str_look_nl: + lb $t2 0($t0) + beq $t2 $zero read_str_no_nl + beq $t2 new_line read_str_nl_founded + addi $t1 $t1 1 + addi $t0 $t0 1 + j read_str_look_nl + +read_str_nl_founded: + sb $zero 0($t0) + addi $t1 $t1 1 + li $t2 4 + div $t1 $t2 + mfhi $t3 + beq $t3 $zero read_str_nl_founded_alligned + sub $t2 $t2 $t3 + add $t1 $t1 $t2 +read_str_nl_founded_alligned: + move $a1 $t1 + addiu $a0 $a0 neg_header_size + jal split_block + jal use_block + + addiu $v0 $a0 header_size + j read_str_end + + +read_str_no_nl: + addi $t1 $t1 1 + blt $t1 str_size_treshold read_str_dup + addi $t1 $t1 alloc_size + j read_str_extend_heap +read_str_dup: + sll $t1 $t1 1 +read_str_extend_heap: + move $a1 $t1 + move $t0 $a0 + addiu $a0 $gp free_list + +read_str_last_block_loop: + lw $t1 header_next_slot($a0) + beq $t1 $zero read_str_last_block_founded + lw $a0 header_next_slot($a0) + j read_str_last_block_loop + +read_str_last_block_founded: + jal extend_heap + jal expand_block + lw $t1 header_next_slot($a0) + bne $t1 $zero read_str_copy_prev + move $t1 $a0 + +read_str_copy_prev: + lw $t3 header_size_slot($t1) + move $t2 $zero + move $t5 $t1 + addiu $t1 $t1 header_size + +read_str_copy_loop: + lb $t4 0($t0) + beq $t4 $zero read_str_copy_end + sb $t4 0($t1) + addi $t2 $t2 1 + addi $t0 $t0 1 + addi $t1 $t1 1 + j read_str_copy_loop + +read_str_copy_end: + sub $t3 $t3 $t2 + move $a0 $t1 + move $a1 $t3 + li $v0 8 + syscall + move $t0 $a0 + move $t1 $t2 + addiu $a0 $t5 header_size + j read_str_look_nl + + +read_str_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $t4 16($sp) + lw $t5 20($sp) + lw $a0 24($sp) + lw $a1 28($sp) + lw $ra 32($sp) + addiu $sp $sp 36 + + jr $ra + + +read_str_new_block: + addiu $t0 $gp free_list + +read_str_new_block_search_last: + lw $t1 header_next_slot($t0) + beq $t1 $zero read_str_new_block_create + move $t0 $t1 + j read_str_new_block_search_last + +read_str_new_block_create: + move $a0 $t0 + li $a1 alloc_size + jal extend_heap + jal expand_block + lw $t2 header_next_slot($a0) + beq $t2 $zero read_str_new_block_expanded + lw $t1 header_size_slot($t2) + j read_str_reading + +read_str_new_block_expanded: + move $t2 $a0 + lw $t1 header_size_slot($a0) + j read_str_reading + + + +concat: + addiu $sp $sp -24 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $a0 12($sp) + sw $a1 16($sp) + sw $ra 20($sp) + + move $t0 $a0 + move $t1 $a1 + addiu $a0 $a0 neg_header_size + addiu $a1 $a1 neg_header_size + + lw $a0 header_size_slot($a0) + lw $a1 header_size_slot($a1) + + add $a0 $a0 $a1 + jal malloc + move $t2 $v0 + +concat_copy_first_loop: + lb $a0 0($t0) + beq $a0 $zero concat_copy_second_loop + sb $a0 0($t2) + addiu $t0 $t0 1 + addiu $t2 $t2 1 + j concat_copy_first_loop + +concat_copy_second_loop: + lb $a0 0($t1) + beq $a0 $zero concat_end + sb $a0 0($t2) + addiu $t1 $t1 1 + addiu $t2 $t2 1 + j concat_copy_second_loop + +concat_end: + sb $zero 0($t2) + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $a0 12($sp) + lw $a1 16($sp) + lw $ra 20($sp) + addiu $sp $sp 24 + + jr $ra + + +substr: + addiu $sp $sp -24 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $a0 16($sp) + sw $ra 20($sp) + + move $t0 $a0 + li $t1 4 + + div $a2 $t1 + + mfhi $t2 + bne $t2 $zero substr_allign_size + move $t1 $a2 + j substr_new_block + +substr_allign_size: + sub $t1 $t1 $t2 + add $t1 $t1 $a2 + +substr_new_block: + move $a0 $t1 + jal malloc + move $t3 $v0 + move $t1 $zero + addu $t0 $t0 $a1 + +substr_copy_loop: + beq $t1 $a2 substr_end + lb $t2 0($t0) + sb $t2 0($t3) + addiu $t0 $t0 1 + addiu $t3 $t3 1 + addiu $t1 $t1 1 + j substr_copy_loop + +substr_end: + sb $zero 0($t3) + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $a0 16($sp) + lw $ra 20($sp) + addiu $sp $sp 24 + + jr $ra + + + + + + + + + + + + + + + + + + + diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 15887d19..a861282b 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -208,18 +208,9 @@ def visit(self, node): #This try-except block is for debuggin purposes try: code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) - # for i, ins in enumerate(node.instructions): - # try: - # self.visit(ins) - # except Exception as e: - # if node.name == "function_in_string_at_IO": - # print(i) - # print(ins) - # print(ins.dest) - # print(e) - # raise e + except Exception as e: - if node.name == "function_in_string_at_IO": + if node.name == "function_substr_at_String": print(e) print(node.instructions) print(node.name) @@ -793,9 +784,9 @@ def visit(self, node): right_location = self.get_var_location(node.right) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) - instruction.append(mips.JumpAndLinkNode('less')) + instructions.append(mips.JumpAndLinkNode('less')) dest_location = self.get_var_location(node.dest) - instruction.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) return instructions From 481db872a60db2a38d5a62dac61ff9c80ebd41d1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 28 Sep 2020 15:32:59 -0400 Subject: [PATCH 398/520] [mips] Fix error on size to malloc in substr routine --- src/core/cmp/mips_lib.asm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 7eb09187..4bfd99c8 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -1030,17 +1030,17 @@ substr: move $t0 $a0 li $t1 4 - - div $a2 $t1 + addiu $t3 $a2 1 + div $t3 $t1 mfhi $t2 bne $t2 $zero substr_allign_size - move $t1 $a2 + move $t1 $t3 j substr_new_block substr_allign_size: sub $t1 $t1 $t2 - add $t1 $t1 $a2 + add $t1 $t1 $t3 substr_new_block: move $a0 $t1 From 07d54a1a0a806d25b6b0aee83682910f22565fba Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 28 Sep 2020 15:34:08 -0400 Subject: [PATCH 399/520] Remove code with debuggin purpose --- src/core/cmp/cil_to_mips.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index a861282b..44ddc246 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -148,8 +148,7 @@ def visit(self, node): def visit(self, node): #Get functions names self.collect_func_names(node) - print(self._name_func_map) - + #Convert CIL ProgramNode to MIPS ProgramNode for tp in node.dottypes: self.visit(tp) From dbd3971f46f929b2095b982bcac1456443e591a1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 28 Sep 2020 15:41:07 -0400 Subject: [PATCH 400/520] [cil] Fix String.Concat function to use value attributes in CONCAT command --- src/core/cmp/cool_to_cil.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 0d9394ed..d0d40f47 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -176,8 +176,12 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('concat', 'String')) self.register_param(self.vself) self.register_param(VariableInfo('s', None)) + str_1 = self.define_internal_local() + str_2 = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(str_1, self.vself.name, 'value', 'String')) + self.register_instruction(cil.GetAttribNode(str_2, 's', 'value', 'String')) result = self.define_internal_local() - self.register_instruction(cil.ConcatNode(result, self.vself.name, 's')) + self.register_instruction(cil.ConcatNode(result, str_1, str_2)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) From 646c06d588d7d15f4da947689880763ca7f4e96b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 28 Sep 2020 15:43:05 -0400 Subject: [PATCH 401/520] [cil] Fix String.Substring function to use value attribute in SUBSTR command --- src/core/cmp/cool_to_cil.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index d0d40f47..cfe6a984 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -197,6 +197,8 @@ def register_built_in(self): length_attr = self.define_internal_local() length_substr = self.define_internal_local() less_value = self.define_internal_local() + str_value = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(str_value, self.vself.name, 'value', 'String')) self.register_instruction(cil.GetAttribNode(index_value, 'i', 'value', 'Int')) self.register_instruction(cil.GetAttribNode(length_value, 'l', 'value', 'Int')) #Check Out of range error @@ -204,7 +206,7 @@ def register_built_in(self): self.register_instruction(cil.PlusNode(length_substr, length_value, index_value)) self.register_instruction(cil.LessNode(less_value, length_attr, length_substr)) self.register_runtime_error(less_value, 'Substring out of range') - self.register_instruction(cil.SubstringNode(result, self.vself.name, index_value, length_value)) + self.register_instruction(cil.SubstringNode(result, str_value, index_value, length_value)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) From 807d17bf9ff77d3990864c313090c36f4ff9e039 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 1 Oct 2020 17:21:25 -0400 Subject: [PATCH 402/520] Remove the ignored code.cl --- src/code.cl | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/code.cl diff --git a/src/code.cl b/src/code.cl deleted file mode 100644 index 4d3b1df6..00000000 --- a/src/code.cl +++ /dev/null @@ -1,25 +0,0 @@ -class A{ - x:Int <- 3; -}; - -class B inherits A { - -}; - -class Main inherits IO { - y:B <- new B; - x:A <- y; - - main() : Object {{ - - out_string("\n"); - if (x = y) - then out_string("EQUAL\n") - else out_string("NOT EQUAL\n") - fi; - out_string(x.type_name()); - out_string("\n"); - - 1 < 3 + let a:Int<-3 in a; - }}; -}; From 5251d2c18f77f50bda61065356968937ee66edc7 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Thu, 1 Oct 2020 17:41:00 -0400 Subject: [PATCH 403/520] [lca] - LCA is SELF_TYPE if all the types passed are SELF_TYPE --- src/core/cmp/visitors.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 790986c5..16edf28d 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -356,11 +356,16 @@ def visit(self, node): def LCA(type_list): counter = {} - type_list = [fixed_type(t) for t in type_list] - if any([isinstance(t, AutoType) for t in type_list]): + def check(target): + return [isinstance(t, target) for t in type_list] + + if all(check(SelfType)): + return SelfType(type_list[0].fixed) + if any(check(AutoType)): return AutoType() - if any([isinstance(t, ErrorType) for t in type_list]): + if any(check(ErrorType)): return ErrorType() + type_list = [fixed_type(t) for t in type_list] for typex in type_list: node = typex while True: From 1d0bdb5ec2c6109698e35c85fef40b7ff9a4ce13 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 2 Oct 2020 18:28:15 -0400 Subject: [PATCH 404/520] [main] - The execution root change to /src --- src/main.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main.py b/src/main.py index b25e1ef3..065a0178 100644 --- a/src/main.py +++ b/src/main.py @@ -92,11 +92,8 @@ def main(args): with open("compiled.asm", 'w') as f: f.write(mips_code) - with open("./src/core/cmp/mips_lib.asm") as f2: + with open("./core/cmp/mips_lib.asm") as f2: f.write("".join(f2.readlines())) - - - exit(0) From b3a995bf9e04119bc3cdfc5f994ae52404e67422 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 2 Oct 2020 18:29:11 -0400 Subject: [PATCH 405/520] [gitignore] - Add compiled.asm to the ignored files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 85039b06..fd5ceaf6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # 2kodevs ignores +*src/compiled.asm *src/code.cl *src/code.s *Stuff/ From c46daa4d990382fc947068ceafb4fdf6d9c51e45 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Fri, 2 Oct 2020 19:36:40 -0400 Subject: [PATCH 406/520] [Makefile] - Update the main and coolc rules --- src/Makefile | 11 +++++++---- src/code.cl | 25 ------------------------- 2 files changed, 7 insertions(+), 29 deletions(-) delete mode 100644 src/code.cl diff --git a/src/Makefile b/src/Makefile index 583092d9..498fdd60 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,9 @@ .DEFAULT_GOAL := main .PHONY: clean, info +CODE := code.cl +COOLC_ASM := $(shell echo $(CODE) | cut -d '.' -f 1).s +ASM := compiled.asm ORG_NAME := 2kodevs PROJECT_NAME := CoolCompiler APP_VERSION := v0.1 @@ -11,8 +14,8 @@ TEST_DIR := core/cmp/Stuff/tests/ TEST := main: ## Compiling the compiler :) - ./coolc.sh "code.cl" - # Compiling the compiler :) + @./coolc.sh $(CODE) + @spim -file $(ASM) clean: ## Remove temporary files @rm -rf build/* @@ -25,8 +28,8 @@ info: ## Display project description @echo "$(COPYRIGHT)" coolc: ## Run the code.cl file using coolc - @coolc code.cl - @spim code.s + @coolc $(CODE) + @coolc_spim $(COOLC_ASM) save: ## Save the code.cl as a test @cat code.cl > $(TEST_DIR)$(TEST).cl diff --git a/src/code.cl b/src/code.cl deleted file mode 100644 index b3eaa155..00000000 --- a/src/code.cl +++ /dev/null @@ -1,25 +0,0 @@ -class A{ - x:Int <- 3; -}; - -class B inherits A { - -}; - -class Main inherits IO { - y:B <- new B; - x:A <- y; - - main() : Object {{ - - out_int(0); - if (x = y) - then out_int(1) - else out_int(2) - fi; - - out_int(0); - - 1 < 3 + let a:Int<-3 in a; - }}; -}; From a052e2d8d90e4d82380211da828464a5effa7014 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 2 Oct 2020 21:20:36 -0400 Subject: [PATCH 407/520] [lexer] Fix lexer to recognize \n --- src/core/cmp/lex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/lex.py b/src/core/cmp/lex.py index f8644886..03c366af 100644 --- a/src/core/cmp/lex.py +++ b/src/core/cmp/lex.py @@ -165,7 +165,7 @@ def t_strings_NULL(self, t): def t_strings_newline(self, t): - r'\\\n' + r'\\n' t.lexer.lineno+=1 self.string += '\n' From f458b9b9ba5858498e8e11b41694c35874feea87 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 2 Oct 2020 22:04:47 -0400 Subject: [PATCH 408/520] [cil] Fix value assigned to scope.ret_expr in MinusNode, StarNode and DivNode --- src/core/cmp/cool_to_cil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index cfe6a984..f9a1b13d 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -664,7 +664,7 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - scope.ret_expr = vname + scope.ret_expr = instance @visitor.when(cool.StarNode) def visit(self, node, scope): @@ -683,7 +683,7 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - scope.ret_expr = vname + scope.ret_expr = instance @visitor.when(cool.DivNode) def visit(self, node, scope): @@ -708,7 +708,7 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - scope.ret_expr = vname + scope.ret_expr = instance @visitor.when(cool.IsVoidNode) def visit(self, node, scope): From 3619c84128b5bce7cd55015d4f940d47db53f1d9 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 2 Oct 2020 22:11:15 -0400 Subject: [PATCH 409/520] [mips] Add MoveFromLowNode to mips classes --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 23ac00e0..53b4b321 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -205,6 +205,10 @@ class DivideNode(InstructionNode): def __init__(self, reg1, reg2): self.reg1 = reg1 self.reg2 = reg2 + +class MoveFromLowNode(InstructionNode): + def __init__(self, reg): + self.reg = reg From 2144115b9b6a19130df79e471c7a557e7406b3e1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 2 Oct 2020 22:12:22 -0400 Subject: [PATCH 410/520] [mips] Add MoveFromLowNode case to visit function of PrintVisitor --- src/core/cmp/mips.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 53b4b321..c08970ac 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -477,4 +477,8 @@ def visit(self, node): @visitor.when(DivideNode) def visit(self, node): - return f"div {self.visit(node.reg1)} {self.visit(node.reg2)}" \ No newline at end of file + return f"div {self.visit(node.reg1)} {self.visit(node.reg2)}" + + @visitor.when(MoveFromLowNode) + def visit(self, node): + return f"mflo {self.visit(node.reg)}" \ No newline at end of file From b5bb5024a92944f28a61f38cd14840d71613343a Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 2 Oct 2020 22:19:04 -0400 Subject: [PATCH 411/520] [mips] Fix cil.DivNode case to use MoveFromLowNode instead StoreWord from low registry --- src/core/cmp/cil_to_mips.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 44ddc246..4c283f34 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -209,7 +209,7 @@ def visit(self, node): code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) except Exception as e: - if node.name == "function_substr_at_String": + if node.name == "function_main_at_Main": print(e) print(node.instructions) print(node.name) @@ -736,7 +736,9 @@ def visit(self, node): instructions.append(mips.DivideNode(reg1, reg2)) dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.LOW_REG, dest_location)) + + instructions.append(mips.MoveFromLowNode(reg1)) + instructions.append(mips.StoreWordNode(reg1, dest_location)) self.free_reg(reg1) self.free_reg(reg2) From 1b33890a63348fc30fe845413e2e241ee7b7f3ed Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 2 Oct 2020 22:20:44 -0400 Subject: [PATCH 412/520] [launcher] Change coolc.sh to use python3 explicitly --- src/coolc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coolc.sh b/src/coolc.sh index e3dd2e9c..9ef2fe5e 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -11,4 +11,4 @@ echo "Copyright © 2020: Lázaro Raúl Iglesias Vera, Miguel Tenorio Potrony, Ma # Compile and Run #echo "Compiling $INPUT_FILE into $OUTPUT_FILE" -python main.py -f $INPUT_FILE +python3 main.py -f $INPUT_FILE From c0907b9eba9d98b44073c54778db84b931bc6205 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 3 Oct 2020 12:55:02 -0400 Subject: [PATCH 413/520] [cil] - Fix value left in `scope.ret_expr` in Minus, Star, and Divide Nodes Replace vname for instance variable --- src/core/cmp/cool_to_cil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 0d9394ed..f15d39af 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -658,7 +658,7 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - scope.ret_expr = vname + scope.ret_expr = instance @visitor.when(cool.StarNode) def visit(self, node, scope): @@ -677,7 +677,7 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - scope.ret_expr = vname + scope.ret_expr = instance @visitor.when(cool.DivNode) def visit(self, node, scope): @@ -702,7 +702,7 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - scope.ret_expr = vname + scope.ret_expr = instance @visitor.when(cool.IsVoidNode) def visit(self, node, scope): From f43bdd3f2036e6dace7062b7e839ee34e1d94633 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 3 Oct 2020 15:45:11 -0400 Subject: [PATCH 414/520] [checker] - Report variable is not define only in the first occurrence --- src/core/cmp/visitors.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 16edf28d..3955d43f 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -483,6 +483,7 @@ def visit(self, node, scope): try: if not scope.is_defined(node.id): + scope.define_variable(node.id, ErrorType()) raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) var = scope.find_variable(node.id) var_type = var.type @@ -722,6 +723,7 @@ def visit(self, node, scope): if scope.is_defined(node.lex): node_type = scope.find_variable(node.lex).type else: + scope.define_variable(node.lex, ErrorType()) self.errors.append((VARIABLE_NOT_DEFINED % (node.lex), node.token)) node_type = ErrorType() From 7c7bc13f85d160dab76f08febb448f5e96edc5f3 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 3 Oct 2020 21:45:17 -0400 Subject: [PATCH 415/520] [functions] - Add helper function `get_token` Return the token associated to a given node --- src/core/cmp/functions.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/functions.py b/src/core/cmp/functions.py index 03b4d66d..185553e1 100644 --- a/src/core/cmp/functions.py +++ b/src/core/cmp/functions.py @@ -258,4 +258,11 @@ def _build_parsing_table(self): else: self.ok &= upd_table(self.goto, idx, next_symbol, node[next_symbol.Name][0].idx) - +def get_token(node): + try: + return node.tid + except AttributeError: + try: + return node.token + except AttributeError: + return node.ttype From add9ab703ded82c44977386c9d2407b380c63137 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 3 Oct 2020 21:46:48 -0400 Subject: [PATCH 416/520] [cil] - Update runtime errors messages to show row and column of the error --- src/core/cmp/cool_to_cil.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 8420eecf..b717bdf9 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -2,6 +2,7 @@ import core.cmp.cil as cil import core.cmp.CoolUtils as cool from core.cmp.semantic import Attribute, Method, Type, VariableInfo, SemanticError +from core.cmp.functions import get_token class BaseCOOLToCILVisitor: def __init__(self, context): @@ -227,20 +228,13 @@ def register_built_in(self): type_node.methods = [('init', self.to_function_name('init', 'Int'))] - def register_errors_messages(self): - self.register_data('Dispatch on void') - self.register_data('Case on void') - self.register_data('Execution of a case statement without a matching branch') - self.register_data('Division by zero') - self.register_data('Substring out of range') - def register_runtime_error(self, condition, msg): error_node = self.register_label('error_label') continue_node = self.register_label('continue_label') self.register_instruction(cil.GotoIfNode(condition, error_node.label)) self.register_instruction(cil.GotoNode(continue_node.label)) self.register_instruction(error_node) - data_node = [dn for dn in self.dotdata if dn.value == msg][0] + data_node = self.register_data(msg) self.register_instruction(cil.ErrorNode(data_node)) self.register_instruction(continue_node) @@ -273,7 +267,6 @@ def visit(self, node, scope): self.register_instruction(cil.ReturnNode(0)) # Error message raised by Object:abort() self.register_data('Program aborted') - self.register_errors_messages() self.register_built_in() self.current_function = None @@ -459,7 +452,8 @@ def visit(self, node, scope): equal_result = self.define_internal_local() self.register_instruction(cil.EqualNode(equal_result, vexpr, void)) - self.register_runtime_error(equal_result, 'Case on void') + token = get_token(node.expr) + self.register_runtime_error(equal_result, f'({token.row},{token.column}) - RuntimeError: Case on void\n') end_label = self.register_label('end_label') labels = [] @@ -488,7 +482,7 @@ def visit(self, node, scope): self.register_instruction(cil.GotoNode(end_label.label)) #Raise runtime error if no Goto was executed - data_node = [dn for dn in self.dotdata if dn.value == 'Execution of a case statement without a matching branch'][0] + data_node = self.register_data(f'({token.row + 5},{token.column + 1 + len(node.branches)}) - RuntimeError: Execution of a case statement without a matching branch\n') self.register_instruction(cil.ErrorNode(data_node)) self.register_instruction(end_label) @@ -704,7 +698,8 @@ def visit(self, node, scope): #Check division by 0 equal_result = self.define_internal_local() self.register_instruction(cil.EqualNode(equal_result, vright, 0)) - self.register_runtime_error(equal_result, 'Division by zero') + token = get_token(node.right) + self.register_runtime_error(equal_result, f'({token.row},{token.column}) - RuntimeError: Division by zero\n') self.register_instruction(cil.DivNode(vname, vleft, vright)) instance = self.define_internal_local() @@ -763,7 +758,8 @@ def visit(self, node, scope): equal_result = self.define_internal_local() self.register_instruction(cil.EqualNode(equal_result, vobj, void)) - self.register_runtime_error(equal_result, 'Dispatch on void') + token = get_token(node.obj) + self.register_runtime_error(equal_result, f'({token.row},{token.column}) - RuntimeError: Dispatch on void\n') #self self.register_instruction(cil.ArgNode(vobj)) From 762a59de45e8e0e117cf96fe4e2a89da28a41e61 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 3 Oct 2020 21:48:04 -0400 Subject: [PATCH 417/520] [cil] - Add Object's methods to built in types methods --- src/core/cmp/cool_to_cil.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index b717bdf9..1bbd5394 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -106,6 +106,7 @@ def register_built_in(self): self.register_instruction(cil.ReturnNode(result)) type_node.methods = [(name, self.to_function_name(name, 'Object')) for name in ['init', 'abort', 'type_name', 'copy']] + obj_methods = ['abort', 'type_name', 'copy'] #IO type_node = self.register_type('IO') @@ -150,6 +151,7 @@ def register_built_in(self): self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(name, self.to_function_name(name, 'IO')) for name in ['init', 'out_string', 'out_int', 'in_string', 'in_int']] + type_node.methods += [(method, self.to_function_name(method, 'Object')) for method in obj_methods] #String type_node = self.register_type('String') @@ -214,6 +216,7 @@ def register_built_in(self): self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(name, self.to_function_name(name, 'String')) for name in ['init', 'length', 'concat', 'substr']] + type_node.methods += [(method, self.to_function_name(method, 'Object')) for method in obj_methods] #Int type_node = self.register_type('Int') @@ -227,6 +230,7 @@ def register_built_in(self): self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [('init', self.to_function_name('init', 'Int'))] + type_node.methods += [(method, self.to_function_name(method, 'Object')) for method in obj_methods] def register_runtime_error(self, condition, msg): error_node = self.register_label('error_label') From bfcfa5b41dbeb789aa250012b189d0d6697662b9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 3 Oct 2020 21:48:54 -0400 Subject: [PATCH 418/520] [cil] - Call init of Main in entry function instead of an Allocate --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 1bbd5394..f6b4ec4c 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -265,7 +265,7 @@ def visit(self, node, scope): self.current_function = self.register_function('entry') result = self.define_internal_local() instance = self.register_local(VariableInfo('instance', None)) - self.register_instruction(cil.AllocateNode('Main', instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Main'), instance)) self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) From 0ce7f8655e1dc52bcc83daa8f9ed9b09fd27d509 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 4 Oct 2020 15:09:05 -0400 Subject: [PATCH 419/520] [cil] Add __repr__ method to classes of CIL AST for debuggin purposes --- src/core/cmp/cil.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 5be3d420..3fc52a54 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -46,6 +46,9 @@ class AssignNode(InstructionNode): def __init__(self, dest, source): self.dest = dest self.source = source + + def __repr__(self): + return f"{self.dest} = {self.source}" class ArithmeticNode(InstructionNode): def __init__(self, dest, left, right): @@ -72,7 +75,8 @@ class LessNode(ArithmeticNode): pass class EqualNode(ArithmeticNode): - pass + def __repr__(self): + return f"{self.dest} = {self.left} == {self.right}" class GetAttribNode(InstructionNode): def __init__(self, dest, obj, attr, computed_type): @@ -80,6 +84,9 @@ def __init__(self, dest, obj, attr, computed_type): self.obj = obj self.attr = attr self.computed_type = computed_type + + def __repr__(self): + return f"{self.dest} = GETATTR {self.obj} {self.attr}" class SetAttribNode(InstructionNode): def __init__(self, obj, attr, value, computed_type): @@ -107,18 +114,30 @@ def __init__(self, obj, dest): self.obj = obj self.dest = dest + def __repr__(self): + return f"{self.dest} = TYPEOF {self.obj}" + class LabelNode(InstructionNode): def __init__(self, label): self.label = label + + def __repr__(self): + return f"LABEL {self.label}:" class GotoNode(InstructionNode): def __init__(self, label): self.label = label + + def __repr__(self): + return f"GOTO {self.label}" class GotoIfNode(InstructionNode): def __init__(self, condition, label): self.condition = condition self.label = label + + def __repr__(self): + return f"GOTO {self.label} if {self.condition}" class StaticCallNode(InstructionNode): def __init__(self, function, dest): @@ -131,14 +150,23 @@ def __init__(self, xtype, method, dest, computed_type): self.method = method self.dest = dest self.computed_type = computed_type + + def __repr__(self): + return f"{self.dest} = VCALL {self.type} {self.method}" class ArgNode(InstructionNode): def __init__(self, name): self.name = name + def __repr__(self): + return f"ARG {self.name}" + class ReturnNode(InstructionNode): def __init__(self, value=None): self.value = value + + def __repr__(self): + return f"RETURN {self.value}" class LoadNode(InstructionNode): def __init__(self, dest, msg): @@ -208,6 +236,9 @@ class VoidNode(InstructionNode): class ErrorNode(InstructionNode): def __init__(self, data_node): self.data_node = data_node + + def __repr__(self): + return f"ERROR {self.data_node}" def get_formatter(): From c76783802286d09e46ac0e0e36b868ce6e6df07f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 4 Oct 2020 15:09:59 -0400 Subject: [PATCH 420/520] [mips] Fix copy_object snippet to convert the object size from words to bytes before call malloc and copy --- src/core/cmp/mips.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index c08970ac..65088db4 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -324,6 +324,7 @@ def copy_object(reg1, reg2): instructions = [] instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg1, 4))) + instructions.append(ShiftLeftLogicalNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) instructions.append(JumpAndLinkNode("malloc")) instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) instructions.append(MoveNode(ARG_REGISTERS[0], reg1)) From fe8d3713ce9daf3df47fc6f943fcef2b569ddd82 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 5 Oct 2020 23:00:17 -0400 Subject: [PATCH 421/520] [cil] - Change order of the methods of the built in types Put Object methods first --- src/core/cmp/cool_to_cil.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f6b4ec4c..2d74945b 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -150,8 +150,8 @@ def register_built_in(self): self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) self.register_instruction(cil.ReturnNode(instance)) - type_node.methods = [(name, self.to_function_name(name, 'IO')) for name in ['init', 'out_string', 'out_int', 'in_string', 'in_int']] - type_node.methods += [(method, self.to_function_name(method, 'Object')) for method in obj_methods] + type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] + type_node.methods += [(name, self.to_function_name(name, 'IO')) for name in ['init', 'out_string', 'out_int', 'in_string', 'in_int']] #String type_node = self.register_type('String') @@ -215,8 +215,8 @@ def register_built_in(self): self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) self.register_instruction(cil.ReturnNode(instance)) - type_node.methods = [(name, self.to_function_name(name, 'String')) for name in ['init', 'length', 'concat', 'substr']] - type_node.methods += [(method, self.to_function_name(method, 'Object')) for method in obj_methods] + type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] + type_node.methods += [(name, self.to_function_name(name, 'String')) for name in ['init', 'length', 'concat', 'substr']] #Int type_node = self.register_type('Int') @@ -229,8 +229,8 @@ def register_built_in(self): self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Int')) self.register_instruction(cil.ReturnNode(instance)) - type_node.methods = [('init', self.to_function_name('init', 'Int'))] - type_node.methods += [(method, self.to_function_name(method, 'Object')) for method in obj_methods] + type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] + type_node.methods += [('init', self.to_function_name('init', 'Int'))] def register_runtime_error(self, condition, msg): error_node = self.register_label('error_label') From d24562ac1593432020a94a451c5dc4e78484b3f9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 5 Oct 2020 23:20:06 -0400 Subject: [PATCH 422/520] [cil] - Remove AUTO_TYPE from type list created by `buildHierachy` method --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 2d74945b..70f4c6bb 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -250,7 +250,7 @@ def __init__(self, context): def buildHierarchy(self, t:str): if t == 'Object': return None - return {x.name for x in self.context.types.values() if x.conforms_to(self.context.get_type(t))} + return {x.name for x in self.context.types.values() if x.name != 'AUTO_TYPE' and x.conforms_to(self.context.get_type(t))} @visitor.on('node') def visit(self, node): From 2e86aa72e4ddbc1d0d8cd50133c1f612fc504de1 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 5 Oct 2020 23:30:47 -0400 Subject: [PATCH 423/520] [cil] - Value types initialization without expressions Allow that String, Int, or Bool attrs declared without a initialization expression, can be initialized with default value --- src/core/cmp/cool_to_cil.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 70f4c6bb..f3932b20 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -14,6 +14,7 @@ def __init__(self, context): self.current_function = None self.context = context self.vself = VariableInfo('self', None) + self.value_types = ['String', 'Int', 'Bool'] @property def params(self): @@ -325,6 +326,10 @@ def visit(self, node, scope): if node.expr: self.visit(node.expr, scope) self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, self.current_type)) + elif node.type in self.value_types: + vtemp = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.type, vtemp)) + self.register_instruction(cil.SetAttribNode(instance, node.id, vtemp, self.current_type)) scope.ret_expr = instance @visitor.when(cool.FuncDeclarationNode) @@ -511,6 +516,8 @@ def visit(self, node, scope): if node.expr: self.visit(node.expr, scope) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) + elif node.type in self.value_types: + self.register_instruction(cil.AllocateNode(node.type, vname)) @visitor.when(cool.AssignNode) def visit(self, node, scope): From fc11c3123ea405227d61641ca967f0ccea164d27 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 5 Oct 2020 23:35:33 -0400 Subject: [PATCH 424/520] [cil] - Add `Bool` as a CIL type --- src/core/cmp/cool_to_cil.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f3932b20..e99f4b8e 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -233,6 +233,20 @@ def register_built_in(self): type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] type_node.methods += [('init', self.to_function_name('init', 'Int'))] + #Bool + type_node = self.register_type('Bool') + type_node.attributes = ['value'] + + self.current_function = self.register_function(self.to_function_name('init', 'Bool')) + self.register_param(VariableInfo('val', None)) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Bool', instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Bool')) + self.register_instruction(cil.ReturnNode(instance)) + + type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] + type_node.methods += [('init', self.to_function_name('init', 'Bool'))] + def register_runtime_error(self, condition, msg): error_node = self.register_label('error_label') continue_node = self.register_label('continue_label') @@ -895,6 +909,6 @@ def visit(self, node, scope): else: scope.ret_expr = 0 instance = self.define_internal_local() - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', scope.ret_expr, 'Int')) + self.register_instruction(cil.ArgNode(scope.ret_expr)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) scope.ret_expr = instance From 7f28c4328cadab6ff961a42522c739b4909b5216 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 8 Oct 2020 22:01:16 -0400 Subject: [PATCH 425/520] [mips] Fix cil.NameNode index property of MIPSType was being called as a function --- src/core/cmp/cil_to_mips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 4c283f34..2c537a5e 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -616,7 +616,7 @@ def visit(self, node): reg = self.get_free_reg() instructions.append(mips.LoadAddressNode(reg, mips.TYPENAMES_TABLE_LABEL)) - tp_number = self._types[node.name].index() + tp_number = self._types[node.name].index instructions.append(mips.AddInmediateUnsignedNode(reg, reg, tp_number*4)) instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) From 4b1cff238adf88531048f148d6fe2f408f020fd9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 9 Oct 2020 10:13:57 -0400 Subject: [PATCH 426/520] [cil] - Finish CIL formatter --- src/core/cmp/cil.py | 96 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 3fc52a54..11e1781a 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -262,6 +262,10 @@ def visit(self, node): return f'type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}' + @visitor.when(DataNode) + def visit(self, node): + return f'{node.name} = {node.value}' + @visitor.when(FunctionNode) def visit(self, node): params = '\n\t'.join(self.visit(x) for x in node.params) @@ -298,6 +302,26 @@ def visit(self, node): def visit(self, node): return f'{node.dest} = {node.left} / {node.right}' + @visitor.when(LessEqualNode) + def visit(self, node): + return f'{node.dest} = {node.left} <= {node.right}' + + @visitor.when(LessNode) + def visit(self, node): + return f'{node.dest} = {node.left} < {node.right}' + + @visitor.when(EqualNode) + def visit(self, node): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(GetAttribNode) + def visit(self, node): + return f'{node.dest} = GETATTR {node.obj} {node.attr}' + + @visitor.when(SetAttribNode) + def visit(self, node): + return f'SETATTR {node.obj} {node.attr} {node.value}' + @visitor.when(AllocateNode) def visit(self, node): return f'{node.dest} = ALLOCATE {node.type}' @@ -306,6 +330,18 @@ def visit(self, node): def visit(self, node): return f'{node.dest} = TYPEOF {node.type}' + @visitor.when(LabelNode) + def visit(self, node): + return f'LABEL {node.label}' + + @visitor.when(GotoNode) + def visit(self, node): + return f'GOTO {node.label}' + + @visitor.when(GotoIfNode) + def visit(self, node): + return f'IF {node.condition} GOTO {node.label}' + @visitor.when(StaticCallNode) def visit(self, node): return f'{node.dest} = CALL {node.function}' @@ -322,6 +358,66 @@ def visit(self, node): def visit(self, node): return f'RETURN {node.value if node.value is not None else ""}' + @visitor.when(LoadNode) + def visit(self, node): + return f'{node.dest} = Load {node.msg}' + + @visitor.when(ExitNode) + def visit(self, node): + return f'EXIT' + + @visitor.when(TypeNameNode) + def visit(self, node): + return f'{node.dest} = TYPENAME {node.source}' + + @visitor.when(NameNode) + def visit(self, node): + return f'{node.dest} = NAME {node.name}' + + @visitor.when(CopyNode) + def visit(self, node): + return f'{node.dest} = COPY {node.source}' + + @visitor.when(LengthNode) + def visit(self, node): + return f'{node.dest} = LENGTH {node.source}' + + @visitor.when(ConcatNode) + def visit(self, node): + return f'{node.dest} = CONCAT {node.prefix} {node.suffix}' + + @visitor.when(SubstringNode) + def visit(self, node): + return f'{node.dest} = SUBSTRING {node.index} {node.length}' + + @visitor.when(ReadStrNode) + def visit(self, node): + return f'{node.dest} = READSTR' + + @visitor.when(ReadIntNode) + def visit(self, node): + return f'{node.dest} = READINT' + + @visitor.when(PrintStrNode) + def visit(self, node): + return f'PRINT {node.value}' + + @visitor.when(PrintIntNode) + def visit(self, node): + return f'PRINT {node.value}' + + @visitor.when(ComplementNode) + def visit(self, node): + return f'{node.dest} = COMPL {node.obj}' + + @visitor.when(VoidNode) + def visit(self, node): + return 'VOID' + + @visitor.when(ErrorNode) + def visit(self, node): + return f'ERROR {node.data_node}' + printer = PrintVisitor() return (lambda ast: printer.visit(ast)) From 98fa7d7bc2329fc1b000e8362b85931e62667eba Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 9 Oct 2020 21:19:13 -0400 Subject: [PATCH 427/520] [cil] - Fix formatter of TypeOfNode --- src/core/cmp/cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 11e1781a..d992a1ec 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -328,7 +328,7 @@ def visit(self, node): @visitor.when(TypeOfNode) def visit(self, node): - return f'{node.dest} = TYPEOF {node.type}' + return f'{node.dest} = TYPEOF {node.obj}' @visitor.when(LabelNode) def visit(self, node): From 2d0a956aad6d52e8de63fc6cf2496431b796b33b Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 9 Oct 2020 21:20:59 -0400 Subject: [PATCH 428/520] [cil] - Fix visit of CaseOfNode. Fix: Check error of an execution of a case statement without a matching branch after the branches conditions --- src/core/cmp/cool_to_cil.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index e99f4b8e..c760c957 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -496,6 +496,10 @@ def visit(self, node, scope): self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) + #Raise runtime error if no Goto was executed + data_node = self.register_data(f'({token.row + 1 + len(node.branches)},{token.column - 5}) - RuntimeError: Execution of a case statement without a matching branch\n') + self.register_instruction(cil.ErrorNode(data_node)) + for idx, l in enumerate(labels): self.register_instruction(l) vid = self.register_local(VariableInfo(node.branches[idx].id, None)) @@ -504,10 +508,6 @@ def visit(self, node, scope): self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) self.register_instruction(cil.GotoNode(end_label.label)) - #Raise runtime error if no Goto was executed - data_node = self.register_data(f'({token.row + 5},{token.column + 1 + len(node.branches)}) - RuntimeError: Execution of a case statement without a matching branch\n') - self.register_instruction(cil.ErrorNode(data_node)) - self.register_instruction(end_label) @visitor.when(cool.CaseExpressionNode) From 82bee464a9cf57a8fa93cbb09b3b7cac9c595be9 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 9 Oct 2020 21:34:56 -0400 Subject: [PATCH 429/520] [cil] Add __repr__ method to some types of CIL AST for debuggin purpose --- src/core/cmp/cil.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 3fc52a54..7045a72a 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -143,6 +143,9 @@ class StaticCallNode(InstructionNode): def __init__(self, function, dest): self.function = function self.dest = dest + + def __repr__(self): + return f"{self.dest} = CALL {self.function}" class DynamicCallNode(InstructionNode): def __init__(self, xtype, method, dest, computed_type): @@ -173,6 +176,9 @@ def __init__(self, dest, msg): self.dest = dest self.msg = msg + def __repr__(self): + return f"{self.dest} LOAD {self.msg}" + class ExitNode(InstructionNode): pass @@ -180,11 +186,17 @@ class TypeNameNode(InstructionNode): def __init__(self, dest, source): self.dest = dest self.source = source + + def __repr__(self): + return f"{self.dest} = TYPENAME {self.source}" class NameNode(InstructionNode): def __init__(self, dest, name): self.dest = dest self.name = name + + def __repr__(self): + return f"{self.dest} = NAME {self.name}" class CopyNode(InstructionNode): def __init__(self, dest, source): @@ -220,6 +232,9 @@ def __init__(self, dest): class PrintStrNode(InstructionNode): def __init__(self, value): self.value = value + + def __repr__(self): + return f"PRINTSTR {self.value}" class PrintIntNode(InstructionNode): def __init__(self, value): From 271a7a3bae068f54c107884e786de2d2a8aca981 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 9 Oct 2020 21:43:22 -0400 Subject: [PATCH 430/520] [mips] Add ComplementNode to mips classes --- src/core/cmp/mips.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 65088db4..127126de 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -206,6 +206,11 @@ def __init__(self, reg1, reg2): self.reg1 = reg1 self.reg2 = reg2 +class ComplementNode(InstructionNode): + def __init__(self, reg1, reg2): + self.reg1 = reg1 + self.reg2 = reg2 + class MoveFromLowNode(InstructionNode): def __init__(self, reg): self.reg = reg From c3e35173031656755f59b9b84cd5da893e93788e Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 9 Oct 2020 21:47:56 -0400 Subject: [PATCH 431/520] [mips] Add ComplementNode case to visit function of PrintVisitor --- src/core/cmp/mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 127126de..3bb94ec2 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -484,6 +484,10 @@ def visit(self, node): @visitor.when(DivideNode) def visit(self, node): return f"div {self.visit(node.reg1)} {self.visit(node.reg2)}" + + @visitor.when(ComplementNode) + def visit(self, node): + return f"not {self.visit(node.reg1)} {self.visit(node.reg2)}" @visitor.when(MoveFromLowNode) def visit(self, node): From d0df3333e8c1713b373c9561847d83759b8c00e5 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 9 Oct 2020 21:56:54 -0400 Subject: [PATCH 432/520] [mips] Check if the source in cil.AssignNode is Void --- src/core/cmp/cil_to_mips.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 2c537a5e..2c487dab 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -209,9 +209,9 @@ def visit(self, node): code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) except Exception as e: - if node.name == "function_main_at_Main": + if node.name == "function_a2i_aux_at_A2I": print(e) - print(node.instructions) + # print(node.instructions) print(node.name) @@ -286,7 +286,9 @@ def visit(self, node): reg = self.get_free_reg() - if node.source.isnumeric(): + if type(node.source) == cil.VoidNode: + instructions.append(mips.LoadInmediateNode(reg, 0)) + elif node.source.isnumeric(): load_value = mips.LoadInmediateNode(reg, int(node.source)) instructions.append(load_value) elif type(node.source) == cil.VoidNode: @@ -745,6 +747,29 @@ def visit(self, node): return instructions + @visitor.when(cil.ComplementNode) + def visit(self, node): + instructions = [] + + reg1 = self.get_free_reg() + + if type(node.obj) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.obj)) + else: + left_location = self.get_var_location(node.obj) + instructions.append(mips.LoadWordNode(reg1, left_location)) + + dest_location = self.get_var_location(node.dest) + + instructions.append(mips.ComplementNode(reg1, reg1)) + instructions.append(mips.StoreWordNode(reg1, dest_location)) + + self.free_reg(reg1) + + return instructions + + + @visitor.when(cil.LessEqualNode) def visit(self, node): instructions = [] From ae0aea50ffdbbd3c61f71095742118be0f82cb33 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 10 Oct 2020 11:18:03 -0400 Subject: [PATCH 433/520] [cil] - Add `EqualStrNode` Is an EqualNode especially for String comparison --- src/core/cmp/cil.py | 3 +++ src/core/cmp/cool_to_cil.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index d992a1ec..b0d877a7 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -78,6 +78,9 @@ class EqualNode(ArithmeticNode): def __repr__(self): return f"{self.dest} = {self.left} == {self.right}" +class EqualStrNode(ArithmeticNode): + pass + class GetAttribNode(InstructionNode): def __init__(self, dest, obj, attr, computed_type): self.dest = dest diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index c760c957..55a82a38 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -640,7 +640,7 @@ def visit(self, node, scope): self.register_instruction(string_node) self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'String')) self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'String')) - self.register_instruction(cil.EqualNode(vname, left_value, right_value)) + self.register_instruction(cil.EqualStrNode(vname, left_value, right_value)) self.register_instruction(cil.GotoNode(continue_node.label)) self.register_instruction(reference_node) From 644424f6fb782d7833bf4164d8938d25db6e63db Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 10 Oct 2020 14:02:27 -0400 Subject: [PATCH 434/520] [mips] Add equal_str routine to mips library --- src/core/cmp/mips_lib.asm | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index 4bfd99c8..e8cb7ff2 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -1071,6 +1071,39 @@ substr_end: jr $ra +equal_str: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + + move $t0 $a0 + move $t1 $a1 + +equal_str_loop: + lb $t2 0($t0) + lb $t3 0($t1) + bne $t2 $t3 equal_str_not_equal + beq $t2 $zero equal_str_equal + + addiu $t0 $t0 1 + addiu $t1 $t1 1 + j equal_str_loop + +equal_str_not_equal: + move $v0 $zero + j equal_str_end + +equal_str_equal: + li $v0 1 + +equal_str_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + addiu $sp $sp 16 From b6bbe4670435caaf8f4b874da129cceed5957b8e Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 18:49:03 -0400 Subject: [PATCH 435/520] [mips] Modify concat routine to receive the length of the new string in --- src/core/cmp/mips_lib.asm | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index e8cb7ff2..bc4cb14f 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -980,15 +980,25 @@ concat: move $t0 $a0 move $t1 $a1 - addiu $a0 $a0 neg_header_size - addiu $a1 $a1 neg_header_size - lw $a0 header_size_slot($a0) - lw $a1 header_size_slot($a1) - - add $a0 $a0 $a1 + + addiu $a0 $a2 1 + li $t2 4 + div $a0 $t2 + mfhi $a0 + bne $a0 $zero concat_allign_size + addiu $a0 $a2 1 + +concat_size_alligned: jal malloc move $t2 $v0 + j concat_copy_first_loop + +concat_allign_size: + sub $t2 $t2 $a0 + add $a0 $a2 $t2 + addiu $a0 $a0 1 + j concat_size_alligned concat_copy_first_loop: lb $a0 0($t0) From 9e4b6817724ca0fba13ca000a6ce06a0ec2d0445 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 18:50:03 -0400 Subject: [PATCH 436/520] [mips] Fix equal_str routine to jump to when finish --- src/core/cmp/mips_lib.asm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index bc4cb14f..bb1df72b 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -1115,6 +1115,8 @@ equal_str_end: lw $t3 12($sp) addiu $sp $sp 16 + jr $ra + From 6a3b9b776ea876b237a5105701d17095db4ba0d6 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 18:54:47 -0400 Subject: [PATCH 437/520] [mips] Add cil.EqualStrNode case to visit function from cil_to_mips visitor --- src/core/cmp/cil_to_mips.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 2c487dab..0625821c 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -520,6 +520,26 @@ def visit(self, node): instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) return instructions + + @visitor.when(cil.EqualStrNode) + def visit(self, node): + instructions = [] + + location = self.get_var_location(node.left) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + + location = self.get_var_location(node.right) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) + + instructions.append(mips.JumpAndLinkNode("equal_str")) + + dest_location = self.get_var_location(node.dest) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + return instructions + + + @visitor.when(cil.LabelNode) def visit(self, node): From f795ffaa2b2b5a4d7a41c8b9ae8fd4fd8a8056cd Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 18:57:46 -0400 Subject: [PATCH 438/520] [cil] Fix conversion of LessNode and LessEqualNode to compare int value attr instead int address --- src/core/cmp/cool_to_cil.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 55a82a38..5e596879 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -577,11 +577,15 @@ def visit(self, node, scope): # node.right -> ExpressionNode ############################### vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() self.visit(node.left, scope) left = scope.ret_expr self.visit(node.right, scope) right = scope.ret_expr - self.register_instruction(cil.LessEqualNode(vname, left, right)) + self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'Int')) + self.register_instruction(cil.LessEqualNode(vname, left_value, right_value)) scope.ret_expr = vname @visitor.when(cool.LessNode) @@ -591,11 +595,15 @@ def visit(self, node, scope): # node.right -> ExpressionNode ############################### vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() self.visit(node.left, scope) left = scope.ret_expr self.visit(node.right, scope) right = scope.ret_expr - self.register_instruction(cil.LessNode(vname, left, right)) + self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'Int')) + self.register_instruction(cil.LessNode(vname, left_value, right_value)) scope.ret_expr = vname @visitor.when(cool.EqualNode) From 29e0023a1a694191e209fe6423da48f5a5d2d819 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 18:59:29 -0400 Subject: [PATCH 439/520] [cil] Fix IfThenElseNode conversion to jump from then block to end block --- src/core/cmp/cool_to_cil.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 5e596879..f3d73856 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -389,6 +389,7 @@ def visit(self, node, scope): then_label_node = self.register_label('then_label') else_label_node = self.register_label('else_label') + end_label_node = self.register_label('end_label') #If condition GOTO then_label self.visit(node.condition, scope) @@ -399,11 +400,13 @@ def visit(self, node, scope): self.register_instruction(then_label_node) self.visit(node.if_body, scope) self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) + self.register_instruction(cil.GotoNode(end_label_node.label)) #Label else_label self.register_instruction(else_label_node) self.visit(node.else_body, scope) self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) + self.register_instruction(end_label_node) scope.ret_expr = vret @visitor.when(cool.WhileLoopNode) From 611503b067982259eb3f021284b5806376d4e15b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 19:00:45 -0400 Subject: [PATCH 440/520] [cil][mips] Change concat conversion to use the lenght of the new string as argument. Changes: - Change add length attribute to cil.Concat class - Change cil concat function to calculate the new string length and use it when the cil.ConcatNode is created - Change conversion of cil.ConcatNode to use the new length attr as an argument to concat routine --- src/core/cmp/cil.py | 3 ++- src/core/cmp/cil_to_mips.py | 4 +++- src/core/cmp/cool_to_cil.py | 10 +++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index bc37109d..f70d73d2 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -212,10 +212,11 @@ def __init__(self, dest, source): self.source = source class ConcatNode(InstructionNode): - def __init__(self, dest, prefix, suffix): + def __init__(self, dest, prefix, suffix, length): self.dest = dest self.prefix = prefix self.suffix = suffix + self.length = length class SubstringNode(InstructionNode): def __init__(self, dest, str_value, index, length): diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 0625821c..6fa878e7 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -877,14 +877,16 @@ def visit(self, node): def visit(self, node): instructions = [] - #save $a0, $a1, $v0 + #save $a0, $a1, $a2, $v0 prefix_location = self.get_var_location(node.prefix) suffix_location = self.get_var_location(node.suffix) + lenght_location = self.get_var_location(node.length) dest_location = self.get_var_location(node.dest) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], prefix_location)) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], suffix_location)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) instructions.append(mips.JumpAndLinkNode("concat")) instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f3d73856..0eacefd6 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -182,10 +182,18 @@ def register_built_in(self): self.register_param(VariableInfo('s', None)) str_1 = self.define_internal_local() str_2 = self.define_internal_local() + length_1 = self.define_internal_local() + length_2 = self.define_internal_local() self.register_instruction(cil.GetAttribNode(str_1, self.vself.name, 'value', 'String')) self.register_instruction(cil.GetAttribNode(str_2, 's', 'value', 'String')) + self.register_instruction(cil.GetAttribNode(length_1, self.vself.name, 'length', 'String')) + self.register_instruction(cil.GetAttribNode(length_2, 's', 'length', 'String')) + self.register_instruction(cil.GetAttribNode(length_1, length_1, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(length_2, length_2, 'value', 'Int')) + self.register_instruction(cil.PlusNode(length_1, length_1, length_2)) + result = self.define_internal_local() - self.register_instruction(cil.ConcatNode(result, str_1, str_2)) + self.register_instruction(cil.ConcatNode(result, str_1, str_2, length_1)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) From 2a45ef6e5a8dc3d73caf4a66257b1149e0f9f819 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 12 Oct 2020 21:10:15 -0400 Subject: [PATCH 441/520] Auto stash before merge of "CIL_AST" and "origin/MIPS_AST" --- src/core/cmp/cool_to_cil.py | 37 +++++++++++++++++++++++++++---------- src/main.py | 6 +++--- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 0eacefd6..74415c2d 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -397,7 +397,7 @@ def visit(self, node, scope): then_label_node = self.register_label('then_label') else_label_node = self.register_label('else_label') - end_label_node = self.register_label('end_label') + continue_label_node = self.register_label('continue_label') #If condition GOTO then_label self.visit(node.condition, scope) @@ -408,13 +408,13 @@ def visit(self, node, scope): self.register_instruction(then_label_node) self.visit(node.if_body, scope) self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) - self.register_instruction(cil.GotoNode(end_label_node.label)) + self.register_instruction(cil.GotoNode(continue_label_node.label)) #Label else_label self.register_instruction(else_label_node) self.visit(node.else_body, scope) self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) - self.register_instruction(end_label_node) + self.register_instruction(continue_label_node) scope.ret_expr = vret @visitor.when(cool.WhileLoopNode) @@ -590,14 +590,19 @@ def visit(self, node, scope): vname = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() + instance = self.define_internal_local() + self.visit(node.left, scope) left = scope.ret_expr self.visit(node.right, scope) right = scope.ret_expr - self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'Int')) - self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'Bool')) + self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'Bool')) self.register_instruction(cil.LessEqualNode(vname, left_value, right_value)) - scope.ret_expr = vname + + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + scope.ret_expr = instance @visitor.when(cool.LessNode) def visit(self, node, scope): @@ -608,14 +613,19 @@ def visit(self, node, scope): vname = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() + instance = self.define_internal_local() + self.visit(node.left, scope) left = scope.ret_expr self.visit(node.right, scope) right = scope.ret_expr - self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'Int')) - self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left_value, left, 'value', 'Bool')) + self.register_instruction(cil.GetAttribNode(right_value, right, 'value', 'Bool')) self.register_instruction(cil.LessNode(vname, left_value, right_value)) - scope.ret_expr = vname + + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + scope.ret_expr = instance @visitor.when(cool.EqualNode) def visit(self, node, scope): @@ -626,10 +636,12 @@ def visit(self, node, scope): vname = self.define_internal_local() type_left = self.define_internal_local() type_int = self.define_internal_local() + type_bool = self.define_internal_local() type_string = self.define_internal_local() equal_result = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() + instance = self.define_internal_local() self.visit(node.left, scope) left = scope.ret_expr @@ -638,6 +650,7 @@ def visit(self, node, scope): self.register_instruction(cil.TypeNameNode(type_left, left)) self.register_instruction(cil.NameNode(type_int, 'Int')) + self.register_instruction(cil.NameNode(type_bool, 'Bool')) self.register_instruction(cil.NameNode(type_string, 'String')) int_node = self.register_label('int_label') @@ -646,6 +659,8 @@ def visit(self, node, scope): continue_node = self.register_label('continue_label') self.register_instruction(cil.EqualNode(equal_result, type_left, type_int)) self.register_instruction(cil.GotoIfNode(equal_result, int_node.label)) + self.register_instruction(cil.EqualNode(equal_result, type_left, type_bool)) + self.register_instruction(cil.GotoIfNode(equal_result, int_node.label)) self.register_instruction(cil.EqualNode(equal_result, type_left, type_string)) self.register_instruction(cil.GotoIfNode(equal_result, string_node.label)) self.register_instruction(cil.GotoNode(reference_node.label)) @@ -666,7 +681,9 @@ def visit(self, node, scope): self.register_instruction(cil.EqualNode(vname, left, right)) self.register_instruction(continue_node) - scope.ret_expr = vname + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + scope.ret_expr = instance @visitor.when(cool.PlusNode) def visit(self, node, scope): diff --git a/src/main.py b/src/main.py index 594a3cba..0a8ef061 100644 --- a/src/main.py +++ b/src/main.py @@ -87,9 +87,9 @@ def main(args): #CIL Transformation cool_to_cil = COOLToCILVisitor(context) cil_ast = cool_to_cil.visit(ast, scope) - #formatter = get_formatter() - #ast_cil = formatter(cil_ast) - #print(ast_cil) + # formatter = get_formatter() + # ast_cil = formatter(cil_ast) + # print(ast_cil) cil_to_mips = CILToMIPSVisitor() mips_ast = cil_to_mips.visit(cil_ast) From c734d9b51a616917daa2637bc9c82d59457e525d Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 21:54:09 -0400 Subject: [PATCH 442/520] [cil] Change cool.ComplementNode case to return an Int --- src/core/cmp/cool_to_cil.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 0eacefd6..4e0d0782 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -771,10 +771,13 @@ def visit(self, node, scope): ############################### vname = self.define_internal_local() value = self.define_internal_local() + instance = self.define_internal_local() self.visit(node.expr, scope) self.register_instruction(cil.GetAttribNode(value, scope.ret_expr, 'value', 'Int')) self.register_instruction(cil.ComplementNode(vname, value)) - scope.ret_expr = vname + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + scope.ret_expr = instance @visitor.when(cool.FunctionCallNode) def visit(self, node, scope): From 6672adf651d287fe889260087c12ef23003a438f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 21:55:09 -0400 Subject: [PATCH 443/520] [mips] Change cil.ComplementNode case to add 1 after negate bits --- src/core/cmp/cil_to_mips.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 6fa878e7..ed8301fa 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -782,6 +782,7 @@ def visit(self, node): dest_location = self.get_var_location(node.dest) instructions.append(mips.ComplementNode(reg1, reg1)) + instructions.append(mips.AddInmediateNode(reg1, reg1, 1)) instructions.append(mips.StoreWordNode(reg1, dest_location)) self.free_reg(reg1) From 3819eb57fb1149bf6a26821bf42a251d92d0ae00 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 12 Oct 2020 22:26:44 -0400 Subject: [PATCH 444/520] [cil] - Update visit of `NotNode` --- src/core/cmp/cool_to_cil.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 74415c2d..b76345a8 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -577,9 +577,16 @@ def visit(self, node, scope): # node.expr -> ExpressionNode ############################### vname = self.define_internal_local() + value = self.define_internal_local() + instance = self.define_internal_local() + self.visit(node.expr, scope) - self.register_instruction(cil.MinusNode(vname, 1, scope.ret_expr)) - scope.ret_expr = vname + self.register_instruction(cil.GetAttribNode(value, scope.ret_expr, 'value', 'Bool')) + self.register_instruction(cil.MinusNode(vname, 1, value)) + + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + scope.ret_expr = instance @visitor.when(cool.LessEqualNode) def visit(self, node, scope): From 6c83afb9363d3add6bdcdb18ddc47e21acc55fc4 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 12 Oct 2020 22:27:47 -0400 Subject: [PATCH 445/520] [cil] - Use `value` of Bool object returned by visit of node.condition This is in visit of IfThenElseNode --- src/core/cmp/cool_to_cil.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index b76345a8..e3de203e 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -394,6 +394,7 @@ def visit(self, node, scope): # node.else_body -> ExpressionNode ################################## vret = self.register_local(VariableInfo('if_then_else_value', None)) + vcondition = self.define_internal_local() then_label_node = self.register_label('then_label') else_label_node = self.register_label('else_label') @@ -401,7 +402,8 @@ def visit(self, node, scope): #If condition GOTO then_label self.visit(node.condition, scope) - self.register_instruction(cil.GotoIfNode(scope.ret_expr, then_label_node.label)) + self.register_instruction(cil.GetAttribNode(vcondition, scope.ret_expr, 'value', 'Bool')) + self.register_instruction(cil.GotoIfNode(vcondition, then_label_node.label)) #GOTO else_label self.register_instruction(cil.GotoNode(else_label_node.label)) #Label then_label From 727af29619ef4a735125376062a267c4976d01f4 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 12 Oct 2020 22:45:43 -0400 Subject: [PATCH 446/520] [cil] Use of Bool object returned by visit of node.condition in Loops --- src/core/cmp/cool_to_cil.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index e3de203e..1d861a94 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -426,6 +426,7 @@ def visit(self, node, scope): # node.body -> ExpressionNode ################################### + vcondition = self.define_internal_local() while_label_node = self.register_label('while_label') loop_label_node = self.register_label('loop_label') pool_label_node = self.register_label('pool_label') @@ -433,7 +434,8 @@ def visit(self, node, scope): self.register_instruction(while_label_node) #If condition GOTO loop self.visit(node.condition, scope) - self.register_instruction(cil.GotoIfNode(scope.ret_expr, loop_label_node.label)) + self.register_instruction(cil.GetAttribNode(vcondition, scope.ret_exp, 'value', 'Bool')) + self.register_instruction(cil.GotoIfNode(vcondition, loop_label_node.label)) #GOTO pool self.register_instruction(cil.GotoNode(pool_label_node.label)) #Label loop From e4e8002f273ff05a03ec64c4fc2bfd1eca051921 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 13 Oct 2020 15:00:52 -0400 Subject: [PATCH 447/520] [mips] Fix typo in case cil.LessEqualNode of visit function --- src/core/cmp/cil_to_mips.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index ed8301fa..488ae1d7 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -808,9 +808,9 @@ def visit(self, node): right_location = self.get_var_location(node.right) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) - instruction.append(mips.JumpAndLinkNode('less_equal')) + instructions.append(mips.JumpAndLinkNode('less_equal')) dest_location = self.get_var_location(node.dest) - instruction.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) return instructions From 032dc074e25ce842f8d8a73b539197714b2d7333 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 14 Nov 2020 14:03:44 -0500 Subject: [PATCH 448/520] [cil] Fix typo in case cool.WhileLoopNode of cool_to_cil visitor --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 1d861a94..2a395347 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -434,7 +434,7 @@ def visit(self, node, scope): self.register_instruction(while_label_node) #If condition GOTO loop self.visit(node.condition, scope) - self.register_instruction(cil.GetAttribNode(vcondition, scope.ret_exp, 'value', 'Bool')) + self.register_instruction(cil.GetAttribNode(vcondition, scope.ret_expr, 'value', 'Bool')) self.register_instruction(cil.GotoIfNode(vcondition, loop_label_node.label)) #GOTO pool self.register_instruction(cil.GotoNode(pool_label_node.label)) From bcd346c70011d7ef42c0d04c4b6cb7996b7ae875 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 17 Nov 2020 21:01:17 -0500 Subject: [PATCH 449/520] [cil] - Use value of Bool object instead of the object itself in conditions This conditions are of GotoIfNode --- src/core/cmp/cool_to_cil.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 1d861a94..12f3a4d7 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -477,10 +477,11 @@ def visit(self, node, scope): # node.expr -> ExpressionNode # node.branches -> [ CaseExpressionNode ... } ############################################## - vexpr = self.register_local(VariableInfo('case_expr_value', None)) - vtype = self.register_local(VariableInfo('typeName_value', None)) - vcond = self.register_local(VariableInfo('equal_value', None)) - vret = self.register_local(VariableInfo('case_value', None)) + vexpr = self.register_local(VariableInfo('case_expr_value', None)) + vtype = self.register_local(VariableInfo('typeName_value', None)) + vequal = self.register_local(VariableInfo('equal_node_value', None)) + vcond = self.register_local(VariableInfo('equal_value', None)) + vret = self.register_local(VariableInfo('case_value', None)) self.visit(node.expr, scope) self.register_instruction(cil.AssignNode(vexpr, scope.ret_expr)) self.register_instruction(cil.TypeNameNode(vtype, scope.ret_expr)) @@ -508,7 +509,8 @@ def visit(self, node, scope): for t in h: vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) self.register_instruction(cil.NameNode(vbranch_type_name, t)) - self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) + self.register_instruction(cil.EqualNode(vequal, vtype, vbranch_type_name)) + self.register_instruction(cil.GetAttribNode(vcond, vequal, 'value', 'Bool')) self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) #Raise runtime error if no Goto was executed @@ -650,6 +652,7 @@ def visit(self, node, scope): type_bool = self.define_internal_local() type_string = self.define_internal_local() equal_result = self.define_internal_local() + equal_value = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() instance = self.define_internal_local() @@ -669,11 +672,14 @@ def visit(self, node, scope): reference_node = self.register_label('reference_label') continue_node = self.register_label('continue_label') self.register_instruction(cil.EqualNode(equal_result, type_left, type_int)) - self.register_instruction(cil.GotoIfNode(equal_result, int_node.label)) + self.register_instruction(cil.GetAttribNode(equal_value, equal_result, 'value', 'Bool')) + self.register_instruction(cil.GotoIfNode(equal_value, int_node.label)) self.register_instruction(cil.EqualNode(equal_result, type_left, type_bool)) - self.register_instruction(cil.GotoIfNode(equal_result, int_node.label)) + self.register_instruction(cil.GetAttribNode(equal_value, equal_result, 'value', 'Bool')) + self.register_instruction(cil.GotoIfNode(equal_value, int_node.label)) self.register_instruction(cil.EqualNode(equal_result, type_left, type_string)) - self.register_instruction(cil.GotoIfNode(equal_result, string_node.label)) + self.register_instruction(cil.GetAttribNode(equal_value, equal_result, 'value', 'Bool')) + self.register_instruction(cil.GotoIfNode(equal_value, string_node.label)) self.register_instruction(cil.GotoNode(reference_node.label)) self.register_instruction(int_node) From e814c1f30f8a7a00b144a919ecbe6f3d10408b5f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Wed, 18 Nov 2020 15:00:07 -0500 Subject: [PATCH 450/520] Create RegistersAllocator to assign registers to variables' --- src/core/cmp/cil_to_mips.py | 319 ++++++++++++++++++++++++++++++++---- 1 file changed, 286 insertions(+), 33 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 488ae1d7..39d7b14a 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -2,7 +2,10 @@ import core.cmp.visitor as visitor import core.cmp.cil as cil import core.cmp.mips as mips +from random import choice +from collections import defaultdict from core.cmp.utils import CountDict +from pprint import pprint USED = 1 @@ -180,6 +183,15 @@ def visit(self, node): @visitor.when(cil.FunctionNode) def visit(self, node): used_regs_finder = UsedRegisterFinder() + div = RegistersAllocator() + for ins in enumerate(node.instructions): + print(ins) + pprint(div.get_registers_for_variables(node.instructions, node.params, 4)) + #div.numered_instructions(node.instructions) + #div.mark_leaders(node.instructions) + #basic_blocks = div.divide_basics_blocks(node.instructions) + #ady_list = div.create_flow_graph(basic_blocks) + #div.liveness_analysis((basic_blocks, ady_list), node.params) label = self._name_func_map[node.name] params = [param.name for param in node.params] @@ -291,8 +303,6 @@ def visit(self, node): elif node.source.isnumeric(): load_value = mips.LoadInmediateNode(reg, int(node.source)) instructions.append(load_value) - elif type(node.source) == cil.VoidNode: - instructions.append(mips.LoadInmediateNode(reg, 0)) else: value_location = self.get_var_location(node.source) load_value = mips.LoadWordNode(reg, value_location) @@ -998,46 +1008,299 @@ def visit(self, node): #Change Name -class FunctionDivider: +class RegistersAllocator: def __init__(self): - self.mark - + self.mark = False - def divide_basics_blocks(self, intructions): + def get_registers_for_variables(self, instructions, params, n): + self.numered_instructions(instructions) + self.mark_leaders(instructions) + basic_blocks = self.divide_basics_blocks(instructions) + flow_graph = RegistersAllocator.create_flow_graph(basic_blocks) + gk, io = self.liveness_analysis((basic_blocks, flow_graph), params) + interference = RegistersAllocator.interference_compute(gk, io) + return RegistersAllocator.assign_registers(interference, n) + + def divide_basics_blocks(self, instructions): self.mark = True for instruction in instructions: self.mark_leaders(instruction) blocks = [] - for instruction in instrucctions: + for instruction in instructions: if instruction.leader: - block.append([instruction]) - block[-1].append(instruction) + blocks.append([instruction]) + else: + blocks[-1].append(instruction) return blocks - def create_flow_graph(blocks): #graph between blocks in a same function does not include relations between functions + def liveness_analysis(self, graph, params): + blocks, ady_list = graph + + instructions = [] + for block in blocks: + instructions.extend(block) + instructions_total = len(instructions) + + suc = [ 0 for _ in range(instructions_total) ] + for block_index, block in enumerate(blocks): + for ins_index, instruction in enumerate(block): + if ins_index == len(block) - 1: + ady = [ i for i in range(len(blocks)) if ady_list[block_index][i] == 1 ] + suc[instruction.number] = [ blocks[b][0].number for b in ady ] + else: + suc[instruction.number] = [ block[ins_index + 1].number ] + + gk = [self.gen_kill(inst) for inst in instructions] + io = RegistersAllocator.out_in_compute(suc, gk) + gk = [([], [param.name for param in params ] )] + gk + io = [([], io[0][0])] + io + + return gk, io + interference = RegistersAllocator.interference_compute(gk, oi) + + RegistersAllocator.assign_registers(interference, 4) + + @staticmethod + def interference_compute(gk, in_out): + neigs = defaultdict(set) + for i,(_, k) in enumerate(gk): + for v in k: + neigs[v].update(in_out[i][1]) + + for k, v in neigs.items(): + for n in v: + neigs[n].add(k) + + for k, v in neigs.items(): + neigs[k] = list(v.difference([k])) + + return neigs + + @staticmethod + def assign_registers(interference_graph, n): + stack = [] + print(type(n)) + var_registers = defaultdict(lambda : -1) + nodes = set(interference_graph.keys()) + + def myLen(l): + count = 0 + for v in l: + if v in nodes: + count += 1 + return count + + #remove nodes with less than n edges + while nodes: + to_remove = None + for node in nodes: + if myLen(interference_graph[node]) < n: + stack.append((node, interference_graph[node])) + to_remove = node + break + + if to_remove: + nodes.remove(to_remove) + else: + selection = choice(list(nodes)) + stack.append((selection, interference_graph[selection])) + nodes.remove(selection) + + while stack: + node, ady = stack.pop() + regs = set(range(n)) + for neig in ady: + reg = var_registers[neig] + if reg != -1: + try: + regs.remove(reg) + except: + pass + if regs: + var_registers[node] = min(regs) + else: + var_registers[node] = -1 + + return var_registers + + @staticmethod + def out_in_compute(suc, gk): + n_instructions = len(gk) + in_out = [(set(), set()) for _ in range(n_instructions)] + next_in_out = [(set(), set()) for _ in range(n_instructions)] + + def add(set1, set2): + new = not set1 == set2 + set1.update(set2) + return new + + changed = True + while changed: + changed = False + for i in range(n_instructions)[::-1]: + for i_suc in suc[i]: + if i_suc < i: + changed = add(next_in_out[i][1], in_out[i_suc][0]) + else: + changed = add(next_in_out[i][1], next_in_out[i_suc][0]) + g_i = set(gk[i][0]) + k_i = set(gk[i][1]) + new = g_i.union(next_in_out[i][1].difference(k_i)) + changed = add(next_in_out[i][0], new) + in_out = next_in_out + + return in_out + + @staticmethod + def create_flow_graph(blocks, debug = False): #graph between blocks in a same function does not include relations between functions graph = [[-1 for _ in range(len(blocks))] for _ in range(len(blocks)) ] - labels = {b.name : i for i, b in enumerate(blocks) if type(b[0]) == cil.LabelNode} + labels = {b[0].label : i for i, b in enumerate(blocks) if isinstance(b[0], cil.LabelNode)} for i, block in enumerate(blocks): - tp = type(block[-1]) - if tp == cil.GotoNode: + if isinstance(block[-1], cil.GotoNode): graph[i][labels[block[-1].label]] = 1 - - elif tp == cil.GotoIfNode: + elif isinstance(block[-1], cil.GotoIfNode): graph[i][labels[block[-1].label]] = 1 - graph[i][min(len(blocks)-1, i+1)] = 1 + graph[i][i + 1] = 1 if i + 1 < len(blocks) else -1 + return graph + + @staticmethod + def numered_instructions(instructions): + for i, instr in enumerate(instructions): + instr.number = i + + @visitor.on('instruction') + def gen_kill(self, instruction): + pass + + @visitor.when(cil.ArgNode) + def gen_kill(self, instruction): + if isinstance(instruction.name, int): + return ([], []) + return ([instruction.name], []) + + @visitor.when(cil.StaticCallNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.AssignNode) + def gen_kill(self, instruction): + gen = [] + if isinstance(instruction.source, str): + if not instruction.source.isnumeric(): + gen = [ instruction.source ] + return (gen, [ instruction.dest ]) + + @visitor.when(cil.AllocateNode) + def gen_kill(self, instruction): + return ([], [ instruction.dest ]) + + @visitor.when(cil.ReturnNode) + def gen_kill(self, instruction): + gen = [ instruction.value ] if isinstance(instruction.value, str) else [] + return (gen, []) + + @visitor.when(cil.LoadNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.PrintIntNode) + def gen_kill(self, instruction): + return ([ instruction.value ], []) + + @visitor.when(cil.PrintStrNode) + def gen_kill(self, instruction): + return ([ instruction.value ], []) + + @visitor.when(cil.TypeNameNode) + def gen_kill(self, instruction): + return ([ instruction.source ], [ instruction.dest ]) + + @visitor.when(cil.ExitNode) + def gen_kill(self, instruction): + return ( [], []) + + @visitor.when(cil.GetAttribNode) + def gen_kill(self, instruction): + return ([ instruction.obj ], [ instruction.dest ]) + + @visitor.when(cil.SetAttribNode) + def gen_kill(self, instruction): + gen = [ instruction.obj ] + if not isinstance(instruction.value, int): + gen.append(instruction.value) + return (gen, []) + + @visitor.when(cil.CopyNode) + def gen_kill(self, instruction): + return ([ instruction.source ], [ instruction.dest ]) + + @visitor.when(cil.ArithmeticNode) + def gen_kill(self, instruction): + gen = [x for x in [instruction.left, instruction.right] if isinstance(x, str)] + return (gen, [instruction.dest]) + + @visitor.when(cil.GotoIfNode) + def gen_kill(self, instruction): + return ([ instruction.condition ], []) + + @visitor.when(cil.GotoNode) + def gen_kill(self, instruction): + return ([], []) - elif tp == cil.DynamicCallNode: - pass #analize what to do with function calls - elif tp == cil.StaticCallNode: - pass #analize what to do with function calls + @visitor.when(cil.TypeOfNode) + def gen_kill(self, instruction): + return ([instruction.obj], [instruction.dest]) - return graph + @visitor.when(cil.DynamicCallNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + @visitor.when(cil.NameNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.ComplementNode) + def gen_kill(self, instruction): + gen = [ instruction.obj ] if isinstance(instruction.obj, str) else [] + return (gen, [ instruction.dest ]) + + @visitor.when(cil.ReadStrNode) + def gen_kill(self, instruction): + return ([], [ instruction.dest ]) + @visitor.when(cil.LengthNode) + def gen_kill(self, instruction): + return ([ instruction.source ], [ instruction.dest ]) + + @visitor.when(cil.ReadIntNode) + def gen_kill(self, instruction): + return ([], [ instruction.dest ]) + + @visitor.when(cil.ConcatNode) + def gen_kill(self, instruction): + return ( [ instruction.prefix, instruction.suffix ], [ instruction.dest ]) + + @visitor.when(cil.SubstringNode) + def gen_kill(self, instruction): + gen = [ instruction.str_value ] + if isinstance(instruction.index, str): + gen.append(instruction.index) + if isinstance(instruction.length, str): + gen.append(instruction.length) + + return (gen, [ instruction.dest ]) + + @visitor.when(cil.LabelNode) + def gen_kill(self, instruction): + return ([], []) + + @visitor.when(cil.ErrorNode) + def gen_kill(self, instruction): + return ([], []) @visitor.on('instruction') def mark_leaders(self, instruction): @@ -1050,18 +1313,12 @@ def mark_leaders(self, instruction): @visitor.when(cil.GotoNode) def mark_leaders(self, instruction): + instruction.leader = self.mark self.mark = True @visitor.when(cil.GotoIfNode) def mark_leaders(self, instruction): - self.mark = True - - @visitor.when(cil.DynamicCallNode) - def mark_leaders(self, instruction): - self.mark = True - - @visitor.when(cil.StaticCallNode) - def mark_leaders(self, instruction): + instruction.leader = self.mark self.mark = True @visitor.when(cil.InstructionNode) @@ -1077,10 +1334,6 @@ def mark_leaders(self, instruction): - - - - #TEST CIL_TYPE_1 = cil.TypeNode("myType") CIL_TYPE_1.attributes = ["attr1", "attr2", "attr3"] From 2074c5e9b8d1d169a8bb53b41cfe46630418786e Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 19 Nov 2020 17:26:42 -0500 Subject: [PATCH 451/520] Use RegisterManager in code generation --- src/core/cmp/cil_to_mips.py | 1186 ++++++++++++++++++++++++++--------- 1 file changed, 897 insertions(+), 289 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 39d7b14a..f388a88c 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -38,6 +38,103 @@ def get_registers_for_save(self): def __repr__(self): return str(len([0 for r in self.registers if self.registers[r] == USED ])) +class MemoryManager: + def __init__(self, registers, vars_with_addresses, function_for_assign): + self.registers = registers + self.func = function_for_assign + self.place_in_memory = dict(vars_with_addresses) + self.updated_in_mem = {var : True for var, _ in vars_with_addresses} + self.vars_in_reg = { reg : [] for reg in registers } + self.locked = [] + + def lock(self, reg): + self.locked.append(reg) + + def value_updated(self, var): + self.updated_in_mem[var] = False + + def use_register(self, reg, var): + instructions = [] + for v in self.vars_in_reg[reg]: + if v == var: + continue + instructions.extend(self.remove_from_reg(reg, v)) + + if not var in self.vars_in_reg[reg]: + self.vars_in_reg[reg].append(var) + + return instructions + + def use_register_for_value(self, reg): + instructions = [] + for v in self.vars_in_reg[reg]: + instructions.extend(self.remove_from_reg(reg, v)) + return instructions + + def load_in_register(self, reg, var): + if var in self.vars_in_reg[reg]: + return [] + + instructions = [] + for v in self.vars_in_reg[reg]: + instructions.extend(self.remove_from_reg(reg, v)) + location = self.place_in_memory[var] + instructions.append( mips.LoadWordNode(reg, location)) + self.vars_in_reg[reg].append(var) + return instructions + + def load_value_in_register(self, reg, val): + instructions = [] + for v in self.vars_in_reg[reg]: + instructions.extend(self.remove_from_reg(reg, v)) + instructions.append(mips.LoadInmediateNode(reg, val)) + return instructions + + def remove_from_reg(self, reg, var): + instructions = [] + if var in self.vars_in_reg[reg]: + if not self.is_in_mem(var): + location = self.place_in_memory[var] + instructions.append(mips.StoreWordNode(reg, location)) + self.vars_in_reg[reg].remove(var) + return instructions + + def is_in_mem(self, var): + return self.updated_in_mem[var] + + def get_register(self, var): + index = self.func(var) + if index == -1: + return choice(self.registers) + return self.registers[index] + + def get_register_for_value(self): + best = None + occu = 0 + for reg, var in self.vars_in_reg.items(): + if reg in self.locked: + continue + if not best: + best, occu = reg, len(var) + if len(var) < occu: + best, occu = reg, len(var) + + self.locked.append(best) + return best + + def free(self, reg): + try: + self.locked.remove(reg) + except: + pass + + def save_values(self): + instructions = [] + for reg in self.vars_in_reg: + while self.vars_in_reg[reg]: + instructions.extend(self.remove_from_reg(reg, self.vars_in_reg[reg][0])) + return instructions + class LabelGenerator: def __init__(self): @@ -62,6 +159,7 @@ class CILToMIPSVisitor: def __init__(self, label_generator = LabelGenerator(), regiters_manager = SimpleRegistersManager(mips.REGISTERS)): self._label_generator = label_generator self._registers_manager = regiters_manager + self.memory_manager = None self._types = {} self._data_section = {} self._functions = {} @@ -183,25 +281,32 @@ def visit(self, node): @visitor.when(cil.FunctionNode) def visit(self, node): used_regs_finder = UsedRegisterFinder() - div = RegistersAllocator() - for ins in enumerate(node.instructions): - print(ins) - pprint(div.get_registers_for_variables(node.instructions, node.params, 4)) - #div.numered_instructions(node.instructions) - #div.mark_leaders(node.instructions) - #basic_blocks = div.divide_basics_blocks(node.instructions) - #ady_list = div.create_flow_graph(basic_blocks) - #div.liveness_analysis((basic_blocks, ady_list), node.params) + label = self._name_func_map[node.name] params = [param.name for param in node.params] localvars = [local.name for local in node.localvars] size_for_locals = len(localvars) * mips.ATTR_SIZE + + for ins in enumerate(node.instructions): + print(ins) new_func = mips.FunctionNode(label, params, localvars) self.register_function(node.name, new_func) self.init_function(new_func) + ra = RegistersAllocator() + reg_for_var = ra.get_registers_for_variables(node.instructions, node.params, len(mips.REGISTERS)) + + vars_with_addresses = [] + for var in params + localvars: + vars_with_addresses.append((var, self.get_var_location(var))) + + self.memory_manager = MemoryManager(mips.REGISTERS, vars_with_addresses, lambda x : reg_for_var[x]) + + + + for instruction in node.instructions: self.collect_labels_in_func(instruction) @@ -221,13 +326,16 @@ def visit(self, node): code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) except Exception as e: + raise e if node.name == "function_a2i_aux_at_A2I": print(e) # print(node.instructions) + print(e) print(node.name) - + code_instructions.extend(self.memory_manager.save_values()) + final_instructions = [] if not self.in_entry_function(): @@ -250,6 +358,10 @@ def visit(self, node): func_instructions = list(itt.chain(initial_instructions, code_instructions, final_instructions)) new_func.add_instructions(func_instructions) + #print(mips.PrintVisitor().visit(new_func)) + if "a2i" in node.name: + input() + self.finish_functions() @@ -257,21 +369,29 @@ def visit(self, node): def visit(self, node): self.push_arg() instructions = [] - + locked = None if type(node.name) == int: - reg = self.get_free_reg() - load_value = mips.LoadInmediateNode(reg, node.name) - instructions.append(load_value) + #reg = self.get_free_reg() + reg = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg, node.name) + instructions.extend(load) + locked = reg + #load_value = mips.LoadInmediateNode(reg, node.name) + #instructions.append(load_value) instructions.extend(mips.push_register(reg)) else: - reg = self.get_free_reg() + #reg = self.get_free_reg() # if not loaded: - value_address = self.get_var_location(node.name) - load_value = mips.LoadWordNode(reg, value_address) - instructions.append(load_value) + reg = self.memory_manager.get_register(node.name) + load = self.memory_manager.load_in_register(reg, node.name) + instructions.extend(load) + #value_address = self.get_var_location(node.name) + #load_value = mips.LoadWordNode(reg, value_address) + #instructions.append(load_value) instructions.extend(mips.push_register(reg)) - - self.free_reg(reg) + if locked: + self.memory_manager.free(locked) + #self.free_reg(reg) return instructions @visitor.when(cil.StaticCallNode) @@ -281,10 +401,15 @@ def visit(self, node): label = self._name_func_map[node.function] - instructions.append(mips.JumpAndLinkNode(label)) + instructions.append(mips.JumpAndLinkNode(label)) - dst_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + #dst_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) if self._pushed_args > 0: instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) @@ -296,21 +421,37 @@ def visit(self, node): def visit(self, node): instructions = [] - reg = self.get_free_reg() + #reg = self.get_free_reg() + reg1 = None if type(node.source) == cil.VoidNode: - instructions.append(mips.LoadInmediateNode(reg, 0)) + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, 0) + instructions.extend(load) + #instructions.append(mips.LoadInmediateNode(reg, 0)) elif node.source.isnumeric(): - load_value = mips.LoadInmediateNode(reg, int(node.source)) - instructions.append(load_value) + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, int(node.source)) + instructions.extend(load) + #load_value = mips.LoadInmediateNode(reg, int(node.source)) + #instructions.append(load_value) else: - value_location = self.get_var_location(node.source) - load_value = mips.LoadWordNode(reg, value_location) - instructions.append(load_value) - - location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(reg, location)) - self.free_reg(reg) + reg1 = self.memory_manager.get_register(node.source) + load = self.memory_manager.load_in_register(reg1, node.source) + instructions.extend(load) + #value_location = self.get_var_location(node.source) + #load_value = mips.LoadWordNode(reg, value_location) + #instructions.append(load_value) + + reg2 = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg2, node.dest) + instructions.extend(load) + instructions.append(mips.MoveNode(reg2, reg1)) + self.memory_manager.value_updated(node.dest) + self.memory_manager.free(reg1) + #location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(reg2, location)) + #self.free_reg(reg) return instructions @@ -320,23 +461,41 @@ def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() - + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() + tp = 0 if node.type.isnumeric(): tp = node.type else: tp = self._types[node.type].index - - instructions.append(mips.LoadInmediateNode(reg1, tp)) + + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, tp) + instructions.extend(load) + + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.use_register_for_value(reg2) + instructions.extend(load) + + #instructions.append(mips.LoadInmediateNode(reg1, tp)) instructions.extend(mips.create_object(reg1, reg2)) - location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, location)) + #location = self.get_var_location(node.dest) + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) - self.free_reg(reg1) - self.free_reg(reg2) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) + #instructions.append(mips.StoreWordNode(mips.V0_REG, location)) + + + #self.free_reg(reg1) + #self.free_reg(reg2) return instructions @@ -349,22 +508,33 @@ def visit(self, node): elif type(node.value) == int: instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value)) else: - location = self.get_var_location(node.value) - instructions.append(mips.LoadWordNode(mips.V0_REG, location)) + reg = self.memory_manager.get_register(node.value) + load = self.memory_manager.load_in_register(reg, node.value) + instructions.extend(load) + #location = self.get_var_location(node.value) + #instructions.append(mips.LoadWordNode(mips.V0_REG, location)) + instructions.append(mips.MoveNode(mips.V0_REG, reg)) return instructions @visitor.when(cil.LoadNode) def visit(self, node): instructions = [] - reg = self.get_free_reg() + + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + + + #reg = self.get_free_reg() string_location = mips.LabelRelativeLocation(self._data_section[node.msg.name].label, 0) instructions.append(mips.LoadAddressNode(reg, string_location)) + self.memory_manager.value_updated(node.dest) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(reg, dest_location)) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(reg, dest_location)) - self.free_reg(reg) + #self.free_reg(reg) return instructions @visitor.when(cil.PrintIntNode) @@ -373,8 +543,12 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1)) #TODO save $a0 if is beign used - location = self.get_var_location(node.value) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + #location = self.get_var_location(node.value) + reg = self.memory_manager.get_register(node.value) + load = self.memory_manager.load_in_register(reg, node.value) + instructions.extend(load) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) instructions.append(mips.SyscallNode()) return instructions @@ -385,9 +559,12 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) #TODO save $a0 if is beign used - - location = self.get_var_location(node.value) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + reg = self.memory_manager.get_register(node.value) + load = self.memory_manager.load_in_register(reg, node.value) + instructions.extend(load) + #location = self.get_var_location(node.value) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) instructions.append(mips.SyscallNode()) return instructions @@ -396,22 +573,46 @@ def visit(self, node): def visit(self, node): instructions = [] - reg = self.get_free_reg() - reg2 = self.get_free_reg() - - src_location = self.get_var_location(node.source) - dst_location = self.get_var_location(node.dest) - - instructions.append(mips.LoadWordNode(reg, src_location)) - instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) - instructions.append(mips.ShiftLeftLogicalNode(reg, reg, 2)) - instructions.append(mips.LoadAddressNode(reg2, mips.TYPENAMES_TABLE_LABEL)) - instructions.append(mips.AddUnsignedNode(reg, reg, reg2)) - instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) - instructions.append(mips.StoreWordNode(reg, dst_location)) - - self.free_reg(reg) - self.free_reg(reg2) + #reg = self.get_free_reg() + #reg2 = self.get_free_reg() + reg1 = self.memory_manager.get_register(node.source) + load = self.memory_manager.load_in_register(reg1, node.source) + instructions.extend(load) + self.memory_manager.lock(reg1) + + reg2 = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg2, node.dest) + instructions.extend(load) + self.memory_manager.lock(reg2) + + reg3 = self.memory_manager.get_register_for_value() + load = self.memory_manager.use_register_for_value(reg3) + instructions.extend(load) + + reg4 = self.memory_manager.get_register_for_value() + load = self.memory_manager.use_register_for_value(reg4) + instructions.extend(load) + + #src_location = self.get_var_location(node.source) + #dst_location = self.get_var_location(node.dest) + + #instructions.append(mips.LoadWordNode(reg, src_location)) + instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.ShiftLeftLogicalNode(reg3, reg3, 2)) + instructions.append(mips.LoadAddressNode(reg4, mips.TYPENAMES_TABLE_LABEL)) + instructions.append(mips.AddUnsignedNode(reg3, reg3, reg4)) + instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) + #instructions.append(mips.StoreWordNode(reg1, dst_location)) + instructions.append(mips.MoveNode(reg2, reg3)) + self.memory_manager.value_updated(node.dest) + + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + self.memory_manager.free(reg3) + self.memory_manager.free(reg4) + +# self.free_reg(reg) +# self.free_reg(reg2) return instructions @@ -427,57 +628,95 @@ def visit(self, node): def visit(self, node): instructions = [] - reg = self.get_free_reg() + #reg = self.get_free_reg() dest = node.dest if type(node.dest) == str else node.dest.name obj = node.obj if type(node.obj) == str else node.obj.name comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name - - obj_location = self.get_var_location(obj) - dst_location = self.get_var_location(dest) + print(obj) + #input() + #obj_location = self.get_var_location(obj) + #dst_location = self.get_var_location(dest) + reg1 = self.memory_manager.get_register(obj) + print(reg1) + print(self.memory_manager.vars_in_reg.items()) + load = self.memory_manager.load_in_register(reg1, obj) + instructions.extend(load) + if obj == "local_a2i_at_A2I_return_value_of_substr_32": + for i in load: + print(mips.PrintVisitor().visit(i)) + input("here") + #self.memory_manager.lock(reg1) + + reg2 = self.memory_manager.get_register(dest) + load = self.memory_manager.use_register(reg2, dest) + instructions.extend(load) + + #reg3 = self.memory_manager.get_register_for_value() + #load = self.memory_manager.use_register_for_value(reg3) + #instructions.extend(load) tp = self._types[comp_type] offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE - instructions.append(mips.LoadWordNode(reg, obj_location)) - instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, offset))) - instructions.append(mips.StoreWordNode(reg, dst_location)) + #instructions.append(mips.LoadWordNode(reg, obj_location)) + + #instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, offset))) + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) + + # instructions.append(mips.StoreWordNode(reg, dst_location)) + #instructions.append(mips.MoveNode(reg2, reg3)) + self.memory_manager.value_updated(dest) - self.free_reg(reg) + # self.memory_manager.free(reg1) + # self.memory_manager.free(reg3) + + #self.free_reg(reg) return instructions @visitor.when(cil.SetAttribNode) def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() obj = node.obj if type(node.obj) == str else node.obj.name comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name + + reg1 = self.memory_manager.get_register(obj) + load = self.memory_manager.load_in_register(reg1, obj) + instructions.extend(load) + self.memory_manager.lock(reg1) - - obj_location = self.get_var_location(obj) + #obj_location = self.get_var_location(obj) tp = self._types[comp_type] - # print(self._actual_function.label) - # print(node.value) - # print(tp.attributes) offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE - instructions.append(mips.LoadWordNode(reg2, obj_location)) + #instructions.append(mips.LoadWordNode(reg2, obj_location)) + reg2 = None if type(node.value) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.value)) + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg2, node.value) + instructions.extend(load) + #instructions.append(mips.LoadInmediateNode(reg1, node.value)) else: - src_location = self.get_var_location(node.value) - instructions.append(mips.LoadWordNode(reg1, src_location)) + reg2 = self.memory_manager.get_register(node.value) + load = self.memory_manager.load_in_register(reg2, node.value) + instructions.extend(load) + #src_location = self.get_var_location(node.value) + #instructions.append(mips.LoadWordNode(reg1, src_location)) + instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) - instructions.append(mips.StoreWordNode(reg1, mips.RegisterRelativeLocation(reg2, offset))) + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + - self.free_reg(reg1) - self.free_reg(reg2) + # self.free_reg(reg1) + # self.free_reg(reg2) return instructions @@ -487,18 +726,37 @@ def visit(self, node): #Save $a0, $a1, $a2 - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() + reg1 = self.memory_manager.get_register(node.source) + load = self.memory_manager.load_in_register(reg1, node.source) + instructions.extend(load) + self.memory_manager.lock(reg1) - src_location = self.get_var_location(node.source) - instructions.append(mips.LoadWordNode(reg1, src_location)) - instructions.extend(mips.copy_object(reg1, reg2)) + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.use_register_for_value(reg2) + instructions.extend(load) - dst_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + + #src_location = self.get_var_location(node.source) + #instructions.append(mips.LoadWordNode(reg1, src_location)) + instructions.extend(mips.copy_object(reg1, reg2)) - self.free_reg(reg1) - self.free_reg(reg2) + reg3 = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg3, node.dest) + instructions.extend(load) + + #dst_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) + + instructions.append(mips.MoveNode(reg3, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) + + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + + #self.free_reg(reg1) + #self.free_reg(reg2) return instructions @@ -513,21 +771,35 @@ def visit(self, node): elif type(node.left) == cil.VoidNode: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], 0)) else: - location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + #location = self.get_var_location(node.left) + reg = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg, node.left) + instructions.extend(load) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) elif type(node.right) == cil.VoidNode: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], 0)) else: - location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) - - instructions.append(mips.JumpAndLinkNode("equals")) - - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #location = self.get_var_location(node.right) + reg = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg, node.right) + instructions.extend(load) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + + instructions.append(mips.JumpAndLinkNode("equals")) + + #dest_location = self.get_var_location(node.dest) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) return instructions @@ -535,22 +807,33 @@ def visit(self, node): def visit(self, node): instructions = [] - location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + #location = self.get_var_location(node.left) + reg = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg, node.left) + instructions.extend(load) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) - location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) + #location = self.get_var_location(node.right) + reg = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg, node.right) + instructions.extend(load) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) instructions.append(mips.JumpAndLinkNode("equal_str")) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #dest_location = self.get_var_location(node.dest) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) return instructions - - @visitor.when(cil.LabelNode) def visit(self, node): return [mips.LabelNode(self.get_mips_label(node.label))] @@ -560,15 +843,18 @@ def visit(self, node): instructions = [] - reg = self.get_free_reg() + #reg = self.get_free_reg() + reg = self.memory_manager.get_register(node.condition) + load = self.memory_manager.load_in_register(reg, node.condition) + instructions.extend(load) mips_label = self.get_mips_label(node.label) - location = self.get_var_location(node.condition) - instructions.append(mips.LoadWordNode(reg, location)) + #location = self.get_var_location(node.condition) + #instructions.append(mips.LoadWordNode(reg, location)) instructions.append(mips.BranchOnNotEqualNode(reg, mips.ZERO_REG, mips_label)) - self.free_reg(reg) + #self.free_reg(reg) return instructions @@ -581,16 +867,26 @@ def visit(self, node): def visit(self, node): instructions = [] - reg = self.get_free_reg() + #reg = self.get_free_reg() + reg1 = self.memory_manager.get_register(node.obj) + load = self.memory_manager.load_in_register(reg1, node.obj) + instructions.extend(load) + + reg2 = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg2, node.dest) + instructions.extend(load) + - obj_location = self.get_var_location(node.obj) - instructions.append(mips.LoadWordNode(reg, obj_location)) - instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + + #obj_location = self.get_var_location(node.obj) + #instructions.append(mips.LoadWordNode(reg, obj_location)) + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0))) + self.memory_manager.value_updated(node.dest) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(reg, dest_location)) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(reg, dest_location)) - self.free_reg(reg) + #self.free_reg(reg) return instructions @@ -598,27 +894,65 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() + reg1 = self.memory_manager.get_register(node.type) + load = self.memory_manager.load_in_register(reg1, node.type) + instructions.extend(load) + self.memory_manager.lock(reg1) + + reg4 = self.memory_manager.get_register(node.dest) + self.memory_manager.lock(reg4) + + + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.use_register_for_value(reg2) + instructions.extend(load) + + reg3 = self.memory_manager.get_register_for_value() + load = self.memory_manager.use_register_for_value(reg3) + instructions.extend(load) + + comp_tp = self._types[node.computed_type] method_index = list(comp_tp.methods).index(node.method) - dest_location = self.get_var_location(node.dest) - - tp_location = self.get_var_location(node.type) - instructions.append(mips.LoadAddressNode(reg1, mips.PROTO_TABLE_LABEL)) - instructions.append(mips.LoadWordNode(reg2, tp_location)) - instructions.append(mips.ShiftLeftLogicalNode(reg2, reg2, 2)) - instructions.append(mips.AddUnsignedNode(reg1, reg1, reg2 )) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8))) - instructions.append(mips.AddInmediateUnsignedNode(reg1, reg1, method_index*4)) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) - instructions.append(mips.JumpRegisterAndLinkNode(reg1)) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #dest_location = self.get_var_location(node.dest) + + #tp_location = self.get_var_location(node.type) + #instructions.append(mips.LoadAddressNode(reg1, mips.PROTO_TABLE_LABEL)) + #instructions.append(mips.LoadWordNode(reg2, tp_location)) + #instructions.append(mips.ShiftLeftLogicalNode(reg2, reg2, 2)) + #instructions.append(mips.AddUnsignedNode(reg1, reg1, reg2 )) + #instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) + #instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8))) + #instructions.append(mips.AddInmediateUnsignedNode(reg1, reg1, method_index*4)) + #instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) + #instructions.append(mips.JumpRegisterAndLinkNode(reg1)) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + instructions.append(mips.LoadAddressNode(reg2, mips.PROTO_TABLE_LABEL)) + instructions.append(mips.ShiftLeftLogicalNode(reg3, reg1, 2)) + instructions.append(mips.AddUnsignedNode(reg3, reg3, reg2)) + instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) + instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 8))) + instructions.append(mips.AddInmediateUnsignedNode(reg3, reg3, method_index*4)) + instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) + instructions.append(mips.JumpRegisterAndLinkNode(reg3)) + + load = self.memory_manager.use_register(reg4, node.dest) + instructions.extend(load) + + instructions.append(mips.MoveNode(reg4, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) + + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + self.memory_manager.free(reg3) + self.memory_manager.free(reg4) - self.free_reg(reg1) - self.free_reg(reg2) + #self.free_reg(reg1) + #self.free_reg(reg2) if self._pushed_args > 0: instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) self.clean_pushed_args() @@ -645,17 +979,28 @@ def visit(self, node): def visit(self, node): instructions = [] - reg = self.get_free_reg() + #reg = self.get_free_reg() + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.use_register_for_value(reg1) + instructions.extend(load) + + instructions.append(mips.LoadAddressNode(reg1, mips.TYPENAMES_TABLE_LABEL)) - instructions.append(mips.LoadAddressNode(reg, mips.TYPENAMES_TABLE_LABEL)) tp_number = self._types[node.name].index - instructions.append(mips.AddInmediateUnsignedNode(reg, reg, tp_number*4)) - instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + instructions.append(mips.AddInmediateUnsignedNode(reg1, reg1, tp_number*4)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(reg, dest_location)) + reg2 = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg2, node.dest) + instructions.extend(load) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(reg, dest_location)) + instructions.append(mips.MoveNode(reg2, reg1)) - self.free_reg(reg) + self.memory_manager.value_updated(node.dest) + self.memory_manager.free(reg1) + + #self.free_reg(reg) return instructions @@ -663,28 +1008,62 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.left)) - else: - left_location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(reg1, left_location)) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.right)) - else: - right_location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(reg2, right_location)) - - instructions.append(mips.AddNode(reg1, reg1, reg2)) - - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(reg1, dest_location)) - - self.free_reg(reg1) - self.free_reg(reg2) + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() + reg1 = None + reg2 = None + reg3 = self.memory_manager.get_register(node.dest) + + if isinstance(node.left, str): + reg1 = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg1, node.left) + instructions.extend(load) + self.memory_manager.lock(reg1) + + if isinstance(node.right, str): + reg2 = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg2, node.right) + instructions.extend(load) + self.memory_manager.lock(reg2) + + if isinstance(node.left, int): + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, node.left) + instructions.extend(load) + + if isinstance(node.right, int): + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg2, node.right) + instructions.extend(load) + + load = self.memory_manager.use_register(reg3, node.dest) + instructions.extend(load) + + instructions.append(mips.AddNode(reg3, reg2, reg1)) + self.memory_manager.value_updated(node.dest) + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + + #if type(node.left) == int: + # #instructions.append(mips.LoadInmediateNode(reg1, node.left)) + # reg1 = self.memory_manager.get + #else: + # left_location = self.get_var_location(node.left) + # instructions.append(mips.LoadWordNode(reg1, left_location)) + + #if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(reg2, node.right)) + #else: + # right_location = self.get_var_location(node.right) + # instructions.append(mips.LoadWordNode(reg2, right_location)) + + #instructions.append(mips.AddNode(reg1, reg1, reg2)) + + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(reg1, dest_location)) + + #self.free_reg(reg1) + #self.free_reg(reg2) return instructions @@ -692,28 +1071,64 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() + reg1 = None + reg2 = None + reg3 = self.memory_manager.get_register(node.dest) + self.memory_manager.lock(reg3) - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.left)) - else: - left_location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(reg1, left_location)) + if isinstance(node.left, str): + reg1 = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg1, node.left) + instructions.extend(load) + self.memory_manager.lock(reg1) + + if isinstance(node.right, str): + reg2 = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg2, node.right) + instructions.extend(load) + self.memory_manager.lock(reg2) + + if isinstance(node.left, int): + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, node.left) + instructions.extend(load) + + if isinstance(node.right, int): + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg2, node.right) + instructions.extend(load) + + load = self.memory_manager.use_register(reg3, node.dest) + instructions.extend(load) - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.right)) - else: - right_location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(reg2, right_location)) + instructions.append(mips.SubNode(reg3, reg1, reg2)) + self.memory_manager.value_updated(node.dest) + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + self.memory_manager.free(reg3) + + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() + + #if type(node.left) == int: + # instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #else: + # left_location = self.get_var_location(node.left) + # instructions.append(mips.LoadWordNode(reg1, left_location)) + + #if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(reg2, node.right)) + #else: + # right_location = self.get_var_location(node.right) + # instructions.append(mips.LoadWordNode(reg2, right_location)) - instructions.append(mips.SubNode(reg1, reg1, reg2)) + #instructions.append(mips.SubNode(reg1, reg1, reg2)) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(reg1, dest_location)) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(reg1, dest_location)) - self.free_reg(reg1) - self.free_reg(reg2) + #self.free_reg(reg1) + #self.free_reg(reg2) return instructions @@ -721,28 +1136,62 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() + reg1 = None + reg2 = None + reg3 = self.memory_manager.get_register(node.dest) - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.left)) - else: - left_location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(reg1, left_location)) + if isinstance(node.left, str): + reg1 = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg1, node.left) + instructions.extend(load) + self.memory_manager.lock(reg1) + + if isinstance(node.right, str): + reg2 = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg2, node.right) + instructions.extend(load) + self.memory_manager.lock(reg2) + + if isinstance(node.left, int): + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, node.left) + instructions.extend(load) + + if isinstance(node.right, int): + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg2, node.right) + instructions.extend(load) + + load = self.memory_manager.use_register(reg3, node.dest) + instructions.extend(load) - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.right)) - else: - right_location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(reg2, right_location)) + instructions.append(mips.MultiplyNode(reg3, reg2, reg1)) + self.memory_manager.value_updated(node.dest) + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() + + #if type(node.left) == int: + # instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #else: + # left_location = self.get_var_location(node.left) + # instructions.append(mips.LoadWordNode(reg1, left_location)) - instructions.append(mips.MultiplyNode(reg1, reg1, reg2)) + #if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(reg2, node.right)) + #else: + # right_location = self.get_var_location(node.right) + # instructions.append(mips.LoadWordNode(reg2, right_location)) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(reg1, dest_location)) + #instructions.append(mips.MultiplyNode(reg1, reg1, reg2)) - self.free_reg(reg1) - self.free_reg(reg2) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(reg1, dest_location)) + + #self.free_reg(reg1) + #self.free_reg(reg2) return instructions @@ -750,30 +1199,65 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = self.get_free_reg() - reg2 = self.get_free_reg() + reg1 = None + reg2 = None + reg3 = self.memory_manager.get_register(node.dest) - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.left)) - else: - left_location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(reg1, left_location)) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.right)) - else: - right_location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(reg2, right_location)) + if isinstance(node.left, str): + reg1 = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg1, node.left) + instructions.extend(load) + self.memory_manager.lock(reg1) + + if isinstance(node.right, str): + reg2 = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg2, node.right) + instructions.extend(load) + self.memory_manager.lock(reg2) + + if isinstance(node.left, int): + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, node.left) + instructions.extend(load) + + if isinstance(node.right, int): + reg2 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg2, node.right) + instructions.extend(load) + + load = self.memory_manager.use_register(reg3, node.dest) + instructions.extend(load) instructions.append(mips.DivideNode(reg1, reg2)) + instructions.append(mips.MoveFromLowNode(reg3)) + self.memory_manager.value_updated(node.dest) + self.memory_manager.free(reg1) + self.memory_manager.free(reg2) + + #reg1 = self.get_free_reg() + #reg2 = self.get_free_reg() - dest_location = self.get_var_location(node.dest) + #if type(node.left) == int: + # instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #else: + # left_location = self.get_var_location(node.left) + # instructions.append(mips.LoadWordNode(reg1, left_location)) + + #if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(reg2, node.right)) + #else: + # right_location = self.get_var_location(node.right) + # instructions.append(mips.LoadWordNode(reg2, right_location)) + + #instructions.append(mips.DivideNode(reg1, reg2)) + + #dest_location = self.get_var_location(node.dest) - instructions.append(mips.MoveFromLowNode(reg1)) - instructions.append(mips.StoreWordNode(reg1, dest_location)) + #instructions.append(mips.MoveFromLowNode(reg1)) + #instructions.append(mips.StoreWordNode(reg1, dest_location)) - self.free_reg(reg1) - self.free_reg(reg2) + #self.free_reg(reg1) + #self.free_reg(reg2) return instructions @@ -781,21 +1265,37 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = self.get_free_reg() + reg1 = None + if type(node.obj) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.obj)) + #instructions.append(mips.LoadInmediateNode(reg1, node.obj)) + reg1 = self.memory_manager.get_register_for_value() + load = self.memory_manager.load_value_in_register(reg1, node.obj) + instructions.extend(load) else: - left_location = self.get_var_location(node.obj) - instructions.append(mips.LoadWordNode(reg1, left_location)) + #left_location = self.get_var_location(node.obj) + #instructions.append(mips.LoadWordNode(reg1, left_location)) + reg1 = self.memory_manager.get_register(node.obj) + load = self.memory_manager.load_in_register(reg1, node.obj) + instructions.extend(load) - dest_location = self.get_var_location(node.dest) - - instructions.append(mips.ComplementNode(reg1, reg1)) - instructions.append(mips.AddInmediateNode(reg1, reg1, 1)) - instructions.append(mips.StoreWordNode(reg1, dest_location)) + #dest_location = self.get_var_location(node.dest) + reg2 = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg2, node.dest) + instructions.extend(load) - self.free_reg(reg1) + #instructions.append(mips.ComplementNode(reg1, reg1)) + #instructions.append(mips.AddInmediateNode(reg1, reg1, 1)) + #instructions.append(mips.StoreWordNode(reg1, dest_location)) + + instructions.append(mips.ComplementNode(reg2, reg1)) + instructions.append(mips.AddInmediateNode(reg2, reg2, 1)) + self.memory_manager.value_updated(node.dest) + + self.memory_manager.free(reg1) + + #self.free_reg(reg1) return instructions @@ -806,21 +1306,47 @@ def visit(self, node): instructions = [] #Save $a0, $a1, $v0 - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.left)) - else: - left_location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) + if isinstance(node.left, str): + reg = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg, node.left) + instructions.extend(load) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + + if isinstance(node.right, str): + reg = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg, node.right) + instructions.extend(load) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + + if isinstance(node.left, int): + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) + + if isinstance(node.right, int): + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) + + instructions.append(mips.JumpAndLinkNode('less_equal')) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) + + + #if type(node.left) == int: + # instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #else: + # left_location = self.get_var_location(node.left) + # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.right)) - else: - right_location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) + #if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(reg2, node.right)) + #else: + # right_location = self.get_var_location(node.right) + # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) - instructions.append(mips.JumpAndLinkNode('less_equal')) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #instructions.append(mips.JumpAndLinkNode('less_equal')) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) return instructions @@ -829,21 +1355,46 @@ def visit(self, node): instructions = [] #Save $a0, $a1, $v0 - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.left)) - else: - left_location = self.get_var_location(node.left) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) + if isinstance(node.left, str): + reg = self.memory_manager.get_register(node.left) + load = self.memory_manager.load_in_register(reg, node.left) + instructions.extend(load) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + + if isinstance(node.right, str): + reg = self.memory_manager.get_register(node.right) + load = self.memory_manager.load_in_register(reg, node.right) + instructions.extend(load) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + + if isinstance(node.left, int): + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) + + if isinstance(node.right, int): + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) + + instructions.append(mips.JumpAndLinkNode('less')) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) + + #if type(node.left) == int: + # instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #else: + # left_location = self.get_var_location(node.left) + # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.right)) - else: - right_location = self.get_var_location(node.right) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) + #if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(reg2, node.right)) + #else: + # right_location = self.get_var_location(node.right) + # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) - instructions.append(mips.JumpAndLinkNode('less')) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #instructions.append(mips.JumpAndLinkNode('less')) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) return instructions @@ -853,8 +1404,12 @@ def visit(self, node): #Save $v0 instructions.append(mips.JumpAndLinkNode("read_str")) - dest_location = self.get_var_location(node.dest) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #dest_location = self.get_var_location(node.dest) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -862,13 +1417,23 @@ def visit(self, node): def visit(self, node): instructions = [] #save $a0 $v0 + #src_location = self.get_var_location(node.source) + reg = self.memory_manager.get_register(node.source) + load = self.memory_manager.load_in_register(reg, node.source) + instructions.extend(load) - src_location = self.get_var_location(node.source) - dest_location = self.get_var_location(node.dest) + #dest_location = self.get_var_location(node.dest) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], src_location)) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], src_location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) instructions.append(mips.JumpAndLinkNode("len")) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) return instructions @@ -876,11 +1441,16 @@ def visit(self, node): def visit(self, node): instructions = [] #save $v0 - dest_location = self.get_var_location(node.dest) + #dest_location = self.get_var_location(node.dest) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.load_in_register(reg, node.dest) + instructions.extend(load) instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) instructions.append(mips.SyscallNode()) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) return instructions @@ -890,16 +1460,37 @@ def visit(self, node): #save $a0, $a1, $a2, $v0 - prefix_location = self.get_var_location(node.prefix) - suffix_location = self.get_var_location(node.suffix) - lenght_location = self.get_var_location(node.length) - dest_location = self.get_var_location(node.dest) - - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], prefix_location)) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], suffix_location)) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) + #prefix_location = self.get_var_location(node.prefix) + #suffix_location = self.get_var_location(node.suffix) + #lenght_location = self.get_var_location(node.length) + reg1 = self.memory_manager.get_register(node.prefix) + load = self.memory_manager.load_in_register(reg1, node.prefix) + instructions.extend(load) + + reg2 = self.memory_manager.get_register(node.suffix) + load = self.memory_manager.load_in_register(reg2, node.suffix) + instructions.extend(load) + + reg3 = self.memory_manager.get_register(node.length) + load = self.memory_manager.load_in_register(reg3, node.length) + instructions.extend(load) + + #dest_location = self.get_var_location(node.dest) + + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], prefix_location)) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], suffix_location)) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg1)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg2)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg3)) instructions.append(mips.JumpAndLinkNode("concat")) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) return instructions @@ -909,25 +1500,43 @@ def visit(self, node): #save $a0, $a1, $a2, $v0 - str_location = self.get_var_location(node.str_value) - dest_location = self.get_var_location(node.dest) + #str_location = self.get_var_location(node.str_value) + reg = self.memory_manager.get_register(node.str_value) + load = self.memory_manager.load_in_register(reg, node.str_value) + instructions.extend(load) + #dest_location = self.get_var_location(node.dest) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], str_location)) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], str_location)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) if type(node.index) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.index)) else: - index_location = self.get_var_location(node.index) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], index_location)) + #index_location = self.get_var_location(node.index) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], index_location)) + reg = self.memory_manager.get_register(node.index) + load = self.memory_manager.load_in_register(reg, node.index) + instructions.extend(load) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + if type(node.length) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[2], node.length)) else: - lenght_location = self.get_var_location(node.length) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) + #lenght_location = self.get_var_location(node.length) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) + reg = self.memory_manager.get_register(node.length) + load = self.memory_manager.load_in_register(reg, node.length) + instructions.extend(load) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg)) instructions.append(mips.JumpAndLinkNode("substr")) - instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + reg = self.memory_manager.get_register(node.dest) + load = self.memory_manager.use_register(reg, node.dest) + instructions.extend(load) + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + self.memory_manager.value_updated(node.dest) return instructions @@ -1082,7 +1691,6 @@ def interference_compute(gk, in_out): @staticmethod def assign_registers(interference_graph, n): stack = [] - print(type(n)) var_registers = defaultdict(lambda : -1) nodes = set(interference_graph.keys()) From cd6553932b9f3f7783f2889f3e6a69a5b9c33980 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Thu, 19 Nov 2020 22:37:34 -0500 Subject: [PATCH 452/520] Change code to use new approach --- src/core/cmp/cil_to_mips.py | 1152 +++++++++++++++-------------------- 1 file changed, 486 insertions(+), 666 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index f388a88c..2b3c294f 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -47,93 +47,18 @@ def __init__(self, registers, vars_with_addresses, function_for_assign): self.vars_in_reg = { reg : [] for reg in registers } self.locked = [] - def lock(self, reg): - self.locked.append(reg) - def value_updated(self, var): - self.updated_in_mem[var] = False - - def use_register(self, reg, var): - instructions = [] - for v in self.vars_in_reg[reg]: - if v == var: - continue - instructions.extend(self.remove_from_reg(reg, v)) - - if not var in self.vars_in_reg[reg]: - self.vars_in_reg[reg].append(var) - - return instructions - - def use_register_for_value(self, reg): - instructions = [] - for v in self.vars_in_reg[reg]: - instructions.extend(self.remove_from_reg(reg, v)) - return instructions - - def load_in_register(self, reg, var): - if var in self.vars_in_reg[reg]: - return [] - - instructions = [] - for v in self.vars_in_reg[reg]: - instructions.extend(self.remove_from_reg(reg, v)) - location = self.place_in_memory[var] - instructions.append( mips.LoadWordNode(reg, location)) - self.vars_in_reg[reg].append(var) - return instructions - - def load_value_in_register(self, reg, val): - instructions = [] - for v in self.vars_in_reg[reg]: - instructions.extend(self.remove_from_reg(reg, v)) - instructions.append(mips.LoadInmediateNode(reg, val)) - return instructions - - def remove_from_reg(self, reg, var): - instructions = [] - if var in self.vars_in_reg[reg]: - if not self.is_in_mem(var): - location = self.place_in_memory[var] - instructions.append(mips.StoreWordNode(reg, location)) - self.vars_in_reg[reg].remove(var) - return instructions - - def is_in_mem(self, var): - return self.updated_in_mem[var] - - def get_register(self, var): + def get_reg_for_var(self, var): index = self.func(var) if index == -1: - return choice(self.registers) + return None return self.registers[index] - - def get_register_for_value(self): - best = None - occu = 0 - for reg, var in self.vars_in_reg.items(): - if reg in self.locked: - continue - if not best: - best, occu = reg, len(var) - if len(var) < occu: - best, occu = reg, len(var) - - self.locked.append(best) - return best - - def free(self, reg): - try: - self.locked.remove(reg) - except: - pass - def save_values(self): - instructions = [] - for reg in self.vars_in_reg: - while self.vars_in_reg[reg]: - instructions.extend(self.remove_from_reg(reg, self.vars_in_reg[reg][0])) - return instructions + def get_reg_unusued(self, used = []): + possibles = list(set(self.registers).difference(set(used))) + return choice(possibles) + + class LabelGenerator: @@ -303,7 +228,7 @@ def visit(self, node): vars_with_addresses.append((var, self.get_var_location(var))) self.memory_manager = MemoryManager(mips.REGISTERS, vars_with_addresses, lambda x : reg_for_var[x]) - + @@ -322,6 +247,8 @@ def visit(self, node): code_instructions = [] #This try-except block is for debuggin purposes + + try: code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) @@ -334,9 +261,18 @@ def visit(self, node): print(node.name) - code_instructions.extend(self.memory_manager.save_values()) + #code_instructions.extend(self.memory_manager.save_values()) final_instructions = [] + + for param in params: + if "main" in node.name: + print(param) + reg = self.memory_manager.get_reg_for_var(param) + if reg is not None: + if "main" in node.name: + print(f"{param} in {reg}") + code_instructions.insert(0,mips.LoadWordNode(reg, self.get_var_location(param))) if not self.in_entry_function(): used_regs = used_regs_finder.get_used_registers(code_instructions) @@ -355,11 +291,13 @@ def visit(self, node): final_instructions.append(mips.JumpRegister(mips.RA_REG)) else: final_instructions.extend(mips.exit_program()) + + func_instructions = list(itt.chain(initial_instructions, code_instructions, final_instructions)) new_func.add_instructions(func_instructions) #print(mips.PrintVisitor().visit(new_func)) - if "a2i" in node.name: + if "main" in node.name: input() self.finish_functions() @@ -369,28 +307,23 @@ def visit(self, node): def visit(self, node): self.push_arg() instructions = [] - locked = None if type(node.name) == int: #reg = self.get_free_reg() - reg = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg, node.name) - instructions.extend(load) - locked = reg #load_value = mips.LoadInmediateNode(reg, node.name) #instructions.append(load_value) - instructions.extend(mips.push_register(reg)) + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.name)) + instructions.extend(mips.push_register(mips.ARG_REGISTERS[0])) else: #reg = self.get_free_reg() # if not loaded: - reg = self.memory_manager.get_register(node.name) - load = self.memory_manager.load_in_register(reg, node.name) - instructions.extend(load) #value_address = self.get_var_location(node.name) #load_value = mips.LoadWordNode(reg, value_address) #instructions.append(load_value) + reg = self.memory_manager.get_reg_for_var(node.name) + if reg is None: + reg = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name))) instructions.extend(mips.push_register(reg)) - if locked: - self.memory_manager.free(locked) #self.free_reg(reg) return instructions @@ -403,13 +336,14 @@ def visit(self, node): instructions.append(mips.JumpAndLinkNode(label)) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) #dst_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + #instructions.append(mips.MoveNode(reg, mips.V0_REG)) if self._pushed_args > 0: instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) @@ -422,36 +356,33 @@ def visit(self, node): instructions = [] #reg = self.get_free_reg() - + reg1 = None if type(node.source) == cil.VoidNode: - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, 0) - instructions.extend(load) #instructions.append(mips.LoadInmediateNode(reg, 0)) + reg1 = mips.ZERO_REG elif node.source.isnumeric(): - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, int(node.source)) - instructions.extend(load) #load_value = mips.LoadInmediateNode(reg, int(node.source)) #instructions.append(load_value) + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, int(node.source))) else: - reg1 = self.memory_manager.get_register(node.source) - load = self.memory_manager.load_in_register(reg1, node.source) - instructions.extend(load) #value_location = self.get_var_location(node.source) #load_value = mips.LoadWordNode(reg, value_location) #instructions.append(load_value) + reg1 = self.memory_manager.get_reg_for_var(node.source) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(LoadWordNode(reg1, self.get_var_location(node.source))) - reg2 = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg2, node.dest) - instructions.extend(load) - instructions.append(mips.MoveNode(reg2, reg1)) - self.memory_manager.value_updated(node.dest) - self.memory_manager.free(reg1) #location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(reg2, location)) #self.free_reg(reg) + reg2 = self.memory_manager.get_reg_for_var(node.dest) + if reg2 is None: + instructions.append(mips.StoreWordNode(reg1, self.get_var_locations(node.dest))) + else: + instructions.append(mips.MoveNode(reg2, reg1)) return instructions @@ -470,29 +401,27 @@ def visit(self, node): else: tp = self._types[node.type].index - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, tp) - instructions.extend(load) - - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.use_register_for_value(reg2) - instructions.extend(load) - + reg1 = self.memory_manager.get_reg_unusued() + reg2 = self.memory_manager.get_reg_unusued([reg1]) + instructions.extend(mips.push_register(reg1)) + instructions.extend(mips.push_register(reg2)) + #instructions.append(mips.LoadInmediateNode(reg1, tp)) + instructions.append(mips.LoadInmediateNode(reg1, tp)) + instructions.extend(mips.create_object(reg1, reg2)) + instructions.extend(mips.pop_register(reg2)) + instructions.extend(mips.pop_register(reg1)) #location = self.get_var_location(node.dest) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, location)) + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg3, mips.V0_REG)) + #instructions.append(mips.StoreWordNode(mips.V0_REG, location)) #self.free_reg(reg1) #self.free_reg(reg2) @@ -508,31 +437,31 @@ def visit(self, node): elif type(node.value) == int: instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value)) else: - reg = self.memory_manager.get_register(node.value) - load = self.memory_manager.load_in_register(reg, node.value) - instructions.extend(load) #location = self.get_var_location(node.value) #instructions.append(mips.LoadWordNode(mips.V0_REG, location)) - instructions.append(mips.MoveNode(mips.V0_REG, reg)) - + reg = self.memory_manager.get_reg_for_var(node.value) + if reg is None: + instructions.append(mips.LoadWordNode(mips.V0_REG, self.get_var_location(node.value))) + else: + instructions.append(mips.MoveNode(mips.V0_REG, reg)) return instructions @visitor.when(cil.LoadNode) def visit(self, node): instructions = [] - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - - #reg = self.get_free_reg() string_location = mips.LabelRelativeLocation(self._data_section[node.msg.name].label, 0) - instructions.append(mips.LoadAddressNode(reg, string_location)) - self.memory_manager.value_updated(node.dest) + #instructions.append(mips.LoadAddressNode(reg, string_location)) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(reg, dest_location)) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], string_location)) + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.LoadAddressNode(reg, string_location)) #self.free_reg(reg) return instructions @@ -544,11 +473,13 @@ def visit(self, node): #TODO save $a0 if is beign used #location = self.get_var_location(node.value) - reg = self.memory_manager.get_register(node.value) - load = self.memory_manager.load_in_register(reg, node.value) - instructions.extend(load) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + reg = self.memory_manager.get_reg_for_var(node.value) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + instructions.append(mips.SyscallNode()) return instructions @@ -559,12 +490,13 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) #TODO save $a0 if is beign used - reg = self.memory_manager.get_register(node.value) - load = self.memory_manager.load_in_register(reg, node.value) - instructions.extend(load) #location = self.get_var_location(node.value) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + reg = self.memory_manager.get_reg_for_var(node.value) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0]. self.get_var_location(node.value))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) instructions.append(mips.SyscallNode()) return instructions @@ -575,41 +507,44 @@ def visit(self, node): #reg = self.get_free_reg() #reg2 = self.get_free_reg() - reg1 = self.memory_manager.get_register(node.source) - load = self.memory_manager.load_in_register(reg1, node.source) - instructions.extend(load) - self.memory_manager.lock(reg1) - - reg2 = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg2, node.dest) - instructions.extend(load) - self.memory_manager.lock(reg2) - - reg3 = self.memory_manager.get_register_for_value() - load = self.memory_manager.use_register_for_value(reg3) - instructions.extend(load) - - reg4 = self.memory_manager.get_register_for_value() - load = self.memory_manager.use_register_for_value(reg4) - instructions.extend(load) #src_location = self.get_var_location(node.source) #dst_location = self.get_var_location(node.dest) + reg1 = self.memory_manager.get_reg_for_var(node.source) + pushed = False + if reg1 is None: + reg1 = self.memory_manager.get_reg_unusued() + instructions.extend(mips.push_register(reg1)) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source))) + pushed = True + + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(reg1, 0))) + + if pushed: + instructions.extend(mips.pop_register(reg1)) + + instructions.append(mips.ShiftLeftLogicalNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2)) + instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[1], mips.TYPENAMES_TABLE_LABEL)) + instructions.append(mips.AddUnsignedNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[1])) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[0], 0))) + + reg2 = self.memory_manager.get_reg_for_var(node.dest) + if reg2 is None: + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg2, mips.ARG_REGISTERS[0])) + + #instructions.append(mips.LoadWordNode(reg, src_location)) - instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg1, 0))) - instructions.append(mips.ShiftLeftLogicalNode(reg3, reg3, 2)) - instructions.append(mips.LoadAddressNode(reg4, mips.TYPENAMES_TABLE_LABEL)) - instructions.append(mips.AddUnsignedNode(reg3, reg3, reg4)) - instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) + #instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg1, 0))) + #instructions.append(mips.ShiftLeftLogicalNode(reg3, reg3, 2)) + #instructions.append(mips.LoadAddressNode(reg4, mips.TYPENAMES_TABLE_LABEL)) + #instructions.append(mips.AddUnsignedNode(reg3, reg3, reg4)) + #instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) #instructions.append(mips.StoreWordNode(reg1, dst_location)) - instructions.append(mips.MoveNode(reg2, reg3)) - self.memory_manager.value_updated(node.dest) + #instructions.append(mips.MoveNode(reg2, reg3)) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) - self.memory_manager.free(reg3) - self.memory_manager.free(reg4) # self.free_reg(reg) # self.free_reg(reg2) @@ -633,28 +568,13 @@ def visit(self, node): dest = node.dest if type(node.dest) == str else node.dest.name obj = node.obj if type(node.obj) == str else node.obj.name comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name - print(obj) - #input() #obj_location = self.get_var_location(obj) #dst_location = self.get_var_location(dest) - reg1 = self.memory_manager.get_register(obj) - print(reg1) - print(self.memory_manager.vars_in_reg.items()) - load = self.memory_manager.load_in_register(reg1, obj) - instructions.extend(load) - if obj == "local_a2i_at_A2I_return_value_of_substr_32": - for i in load: - print(mips.PrintVisitor().visit(i)) - input("here") - #self.memory_manager.lock(reg1) - - reg2 = self.memory_manager.get_register(dest) - load = self.memory_manager.use_register(reg2, dest) - instructions.extend(load) - - #reg3 = self.memory_manager.get_register_for_value() - #load = self.memory_manager.use_register_for_value(reg3) - #instructions.extend(load) + + reg = self.memory_manager.get_reg_for_var(obj) + if reg is None: + reg = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(obj))) tp = self._types[comp_type] offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE @@ -662,14 +582,18 @@ def visit(self, node): #instructions.append(mips.LoadWordNode(reg, obj_location)) #instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, offset))) - instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) + + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(reg, offset))) + + reg = self.memory_manager.get_reg_for_var(dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[1], self.get_var_location(dest))) + else: + instructions.append(mips.MoveNode(reg, mips.ARG_REGISTERS[1])) # instructions.append(mips.StoreWordNode(reg, dst_location)) #instructions.append(mips.MoveNode(reg2, reg3)) - self.memory_manager.value_updated(dest) - # self.memory_manager.free(reg1) - # self.memory_manager.free(reg3) #self.free_reg(reg) return instructions @@ -683,37 +607,30 @@ def visit(self, node): obj = node.obj if type(node.obj) == str else node.obj.name comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name - reg1 = self.memory_manager.get_register(obj) - load = self.memory_manager.load_in_register(reg1, obj) - instructions.extend(load) - self.memory_manager.lock(reg1) - #obj_location = self.get_var_location(obj) tp = self._types[comp_type] offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE - #instructions.append(mips.LoadWordNode(reg2, obj_location)) + reg1 = self.memory_manager.get_reg_for_var(obj) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(obj))) reg2 = None if type(node.value) == int: - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg2, node.value) - instructions.extend(load) #instructions.append(mips.LoadInmediateNode(reg1, node.value)) + reg2 = instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.value)) else: - reg2 = self.memory_manager.get_register(node.value) - load = self.memory_manager.load_in_register(reg2, node.value) - instructions.extend(load) #src_location = self.get_var_location(node.value) #instructions.append(mips.LoadWordNode(reg1, src_location)) - - instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) + reg2 = self.memory_manager.get_reg_for_var(node.value) + if reg2 is None: + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.value))) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) - + instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) # self.free_reg(reg1) # self.free_reg(reg2) @@ -728,32 +645,31 @@ def visit(self, node): #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() - reg1 = self.memory_manager.get_register(node.source) - load = self.memory_manager.load_in_register(reg1, node.source) - instructions.extend(load) - self.memory_manager.lock(reg1) - - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.use_register_for_value(reg2) - instructions.extend(load) - + pushed = False + reg = self.memory_manager.get_reg_for_var(node.source) + if reg is None: + reg = self.memory_manager.get_reg_unusued() + instructions.extend(mips.push_register(reg)) + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) + pushed = True + #src_location = self.get_var_location(node.source) #instructions.append(mips.LoadWordNode(reg1, src_location)) - instructions.extend(mips.copy_object(reg1, reg2)) + instructions.extend(mips.copy_object(reg, mips.ARG_REGISTERS[3])) - reg3 = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg3, node.dest) - instructions.extend(load) + if pushed: + instructions.extend(mips.pop_register(reg)) #dst_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) - instructions.append(mips.MoveNode(reg3, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) #self.free_reg(reg1) #self.free_reg(reg2) @@ -772,12 +688,12 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], 0)) else: #location = self.get_var_location(node.left) - reg = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg, node.left) - instructions.extend(load) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) - + reg = self.memory_manager.get_reg_for_var(node.left) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) @@ -785,21 +701,22 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], 0)) else: #location = self.get_var_location(node.right) - reg = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg, node.right) - instructions.extend(load) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + reg = self.memory_manager.get_reg_for_var(node.right) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.right))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) instructions.append(mips.JumpAndLinkNode("equals")) #dest_location = self.get_var_location(node.dest) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -808,28 +725,32 @@ def visit(self, node): instructions = [] #location = self.get_var_location(node.left) - reg = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg, node.left) - instructions.extend(load) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) #location = self.get_var_location(node.right) - reg = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg, node.right) - instructions.extend(load) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + reg = self.memory_manager.get_reg_for_var(node.left) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + + reg = self.memory_manager.get_reg_for_var(node.right) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.right))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) instructions.append(mips.JumpAndLinkNode("equal_str")) #dest_location = self.get_var_location(node.dest) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -844,14 +765,16 @@ def visit(self, node): instructions = [] #reg = self.get_free_reg() - reg = self.memory_manager.get_register(node.condition) - load = self.memory_manager.load_in_register(reg, node.condition) - instructions.extend(load) mips_label = self.get_mips_label(node.label) #location = self.get_var_location(node.condition) #instructions.append(mips.LoadWordNode(reg, location)) + reg = self.memory_manager.get_reg_for_var(node.condition) + if reg is None: + reg = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.condition))) + instructions.append(mips.BranchOnNotEqualNode(reg, mips.ZERO_REG, mips_label)) #self.free_reg(reg) @@ -868,23 +791,23 @@ def visit(self, node): instructions = [] #reg = self.get_free_reg() - reg1 = self.memory_manager.get_register(node.obj) - load = self.memory_manager.load_in_register(reg1, node.obj) - instructions.extend(load) - - reg2 = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg2, node.dest) - instructions.extend(load) - - #obj_location = self.get_var_location(node.obj) #instructions.append(mips.LoadWordNode(reg, obj_location)) - instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0))) - self.memory_manager.value_updated(node.dest) + + reg1 = self.memory_manager.get_reg_for_var(node.obj) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(reg, dest_location)) + reg2 = self.memory_manager.get_reg_for_var(node.dest) + if reg2 is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.dest))) + else: + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0))) #self.free_reg(reg) @@ -896,28 +819,15 @@ def visit(self, node): #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() - reg1 = self.memory_manager.get_register(node.type) - load = self.memory_manager.load_in_register(reg1, node.type) - instructions.extend(load) - self.memory_manager.lock(reg1) - - reg4 = self.memory_manager.get_register(node.dest) - self.memory_manager.lock(reg4) - - - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.use_register_for_value(reg2) - instructions.extend(load) - - reg3 = self.memory_manager.get_register_for_value() - load = self.memory_manager.use_register_for_value(reg3) - instructions.extend(load) - comp_tp = self._types[node.computed_type] method_index = list(comp_tp.methods).index(node.method) #dest_location = self.get_var_location(node.dest) + reg = self.memory_manager.get_reg_for_var(node.type) + if reg is None: + reg = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.type))) #tp_location = self.get_var_location(node.type) #instructions.append(mips.LoadAddressNode(reg1, mips.PROTO_TABLE_LABEL)) @@ -931,26 +841,21 @@ def visit(self, node): #instructions.append(mips.JumpRegisterAndLinkNode(reg1)) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - instructions.append(mips.LoadAddressNode(reg2, mips.PROTO_TABLE_LABEL)) - instructions.append(mips.ShiftLeftLogicalNode(reg3, reg1, 2)) - instructions.append(mips.AddUnsignedNode(reg3, reg3, reg2)) - instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) - instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 8))) - instructions.append(mips.AddInmediateUnsignedNode(reg3, reg3, method_index*4)) - instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) - instructions.append(mips.JumpRegisterAndLinkNode(reg3)) - - load = self.memory_manager.use_register(reg4, node.dest) - instructions.extend(load) - - instructions.append(mips.MoveNode(reg4, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[1], mips.PROTO_TABLE_LABEL)) + instructions.append(mips.ShiftLeftLogicalNode(mips.ARG_REGISTERS[2], reg, 2)) + instructions.append(mips.AddUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[2])) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 8))) + instructions.append(mips.AddInmediateUnsignedNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], method_index * 4)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) + instructions.append(mips.JumpRegisterAndLinkNode(mips.ARG_REGISTERS[1])) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) - self.memory_manager.free(reg3) - self.memory_manager.free(reg4) - #self.free_reg(reg1) #self.free_reg(reg2) if self._pushed_args > 0: @@ -980,25 +885,22 @@ def visit(self, node): instructions = [] #reg = self.get_free_reg() - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.use_register_for_value(reg1) - instructions.extend(load) - - instructions.append(mips.LoadAddressNode(reg1, mips.TYPENAMES_TABLE_LABEL)) + save = False + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + reg = mips.ARG_REGISTERS[0] + save = True + + instructions.append(mips.LoadAddressNode(reg, mips.TYPENAMES_TABLE_LABEL)) tp_number = self._types[node.name].index - instructions.append(mips.AddInmediateUnsignedNode(reg1, reg1, tp_number*4)) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.AddInmediateUnsignedNode(reg, reg, tp_number*4)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) - reg2 = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg2, node.dest) - instructions.extend(load) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(reg, dest_location)) - instructions.append(mips.MoveNode(reg2, reg1)) - - self.memory_manager.value_updated(node.dest) - self.memory_manager.free(reg1) + if save: + instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest))) #self.free_reg(reg) @@ -1010,55 +912,40 @@ def visit(self, node): #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() - reg1 = None - reg2 = None - reg3 = self.memory_manager.get_register(node.dest) + reg1, reg2 = None, None + if type(node.left) == int: + #instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #reg1 = self.memory_manager.get + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + #left_location = self.get_var_location(node.left) + #instructions.append(mips.LoadWordNode(reg1, left_location)) + reg1 = self.memory_manager.get_reg_for_var(node.left) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) - if isinstance(node.left, str): - reg1 = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg1, node.left) - instructions.extend(load) - self.memory_manager.lock(reg1) - - if isinstance(node.right, str): - reg2 = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg2, node.right) - instructions.extend(load) - self.memory_manager.lock(reg2) - - if isinstance(node.left, int): - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, node.left) - instructions.extend(load) - - if isinstance(node.right, int): - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg2, node.right) - instructions.extend(load) - - load = self.memory_manager.use_register(reg3, node.dest) - instructions.extend(load) - - instructions.append(mips.AddNode(reg3, reg2, reg1)) - self.memory_manager.value_updated(node.dest) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) - - #if type(node.left) == int: - # #instructions.append(mips.LoadInmediateNode(reg1, node.left)) - # reg1 = self.memory_manager.get - #else: - # left_location = self.get_var_location(node.left) - # instructions.append(mips.LoadWordNode(reg1, left_location)) - - #if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(reg2, node.right)) - #else: - # right_location = self.get_var_location(node.right) - # instructions.append(mips.LoadWordNode(reg2, right_location)) + if type(node.right) == int: + #instructions.append(mips.LoadInmediateNode(reg2, node.right)) + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + #right_location = self.get_var_location(node.right) + #instructions.append(mips.LoadWordNode(reg2, right_location)) + reg2 = self.memory_manager.get_reg_for_var(node.right) + if reg2 is None: + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.AddNode(mips.ARG_REGISTERS[0], reg1, reg2)) + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.AddNode(reg3, reg1, reg2)) #instructions.append(mips.AddNode(reg1, reg1, reg2)) - #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(reg1, dest_location)) @@ -1071,61 +958,42 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = None - reg2 = None - reg3 = self.memory_manager.get_register(node.dest) - self.memory_manager.lock(reg3) - - if isinstance(node.left, str): - reg1 = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg1, node.left) - instructions.extend(load) - self.memory_manager.lock(reg1) - - if isinstance(node.right, str): - reg2 = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg2, node.right) - instructions.extend(load) - self.memory_manager.lock(reg2) - - if isinstance(node.left, int): - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, node.left) - instructions.extend(load) - - if isinstance(node.right, int): - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg2, node.right) - instructions.extend(load) - - load = self.memory_manager.use_register(reg3, node.dest) - instructions.extend(load) - - instructions.append(mips.SubNode(reg3, reg1, reg2)) - self.memory_manager.value_updated(node.dest) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) - self.memory_manager.free(reg3) #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() - #if type(node.left) == int: - # instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #else: - # left_location = self.get_var_location(node.left) - # instructions.append(mips.LoadWordNode(reg1, left_location)) - - #if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(reg2, node.right)) - #else: - # right_location = self.get_var_location(node.right) - # instructions.append(mips.LoadWordNode(reg2, right_location)) - - #instructions.append(mips.SubNode(reg1, reg1, reg2)) + reg1, reg2 = None, None + if type(node.left) == int: + #instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #reg1 = self.memory_manager.get + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + #left_location = self.get_var_location(node.left) + #instructions.append(mips.LoadWordNode(reg1, left_location)) + reg1 = self.memory_manager.get_reg_for_var(node.left) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(reg1, dest_location)) + if type(node.right) == int: + #instructions.append(mips.LoadInmediateNode(reg2, node.right)) + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + #right_location = self.get_var_location(node.right) + #instructions.append(mips.LoadWordNode(reg2, right_location)) + reg2 = self.memory_manager.get_reg_for_var(node.right) + if reg2 is None: + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.SubNode(mips.ARG_REGISTERS[0], reg1, reg2)) + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.SubNode(reg3, reg1, reg2)) #self.free_reg(reg1) #self.free_reg(reg2) @@ -1136,59 +1004,43 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = None - reg2 = None - reg3 = self.memory_manager.get_register(node.dest) - - if isinstance(node.left, str): - reg1 = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg1, node.left) - instructions.extend(load) - self.memory_manager.lock(reg1) - - if isinstance(node.right, str): - reg2 = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg2, node.right) - instructions.extend(load) - self.memory_manager.lock(reg2) - - if isinstance(node.left, int): - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, node.left) - instructions.extend(load) - - if isinstance(node.right, int): - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg2, node.right) - instructions.extend(load) - - load = self.memory_manager.use_register(reg3, node.dest) - instructions.extend(load) - - instructions.append(mips.MultiplyNode(reg3, reg2, reg1)) - self.memory_manager.value_updated(node.dest) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() - #if type(node.left) == int: - # instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #else: - # left_location = self.get_var_location(node.left) - # instructions.append(mips.LoadWordNode(reg1, left_location)) - - #if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(reg2, node.right)) - #else: - # right_location = self.get_var_location(node.right) - # instructions.append(mips.LoadWordNode(reg2, right_location)) + reg1, reg2 = None, None + if type(node.left) == int: + #instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #reg1 = self.memory_manager.get + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + #left_location = self.get_var_location(node.left) + #instructions.append(mips.LoadWordNode(reg1, left_location)) + reg1 = self.memory_manager.get_reg_for_var(node.left) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) - #instructions.append(mips.MultiplyNode(reg1, reg1, reg2)) + if type(node.right) == int: + #instructions.append(mips.LoadInmediateNode(reg2, node.right)) + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + #right_location = self.get_var_location(node.right) + #instructions.append(mips.LoadWordNode(reg2, right_location)) + reg2 = self.memory_manager.get_reg_for_var(node.right) + if reg2 is None: + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.MultiplyNode(mips.ARG_REGISTERS[0], reg1, reg2)) + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.MultiplyNode(reg3, reg1, reg2)) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(reg1, dest_location)) #self.free_reg(reg1) #self.free_reg(reg2) @@ -1199,62 +1051,42 @@ def visit(self, node): def visit(self, node): instructions = [] - reg1 = None - reg2 = None - reg3 = self.memory_manager.get_register(node.dest) - - if isinstance(node.left, str): - reg1 = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg1, node.left) - instructions.extend(load) - self.memory_manager.lock(reg1) - - if isinstance(node.right, str): - reg2 = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg2, node.right) - instructions.extend(load) - self.memory_manager.lock(reg2) - - if isinstance(node.left, int): - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, node.left) - instructions.extend(load) - - if isinstance(node.right, int): - reg2 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg2, node.right) - instructions.extend(load) - - load = self.memory_manager.use_register(reg3, node.dest) - instructions.extend(load) - - instructions.append(mips.DivideNode(reg1, reg2)) - instructions.append(mips.MoveFromLowNode(reg3)) - self.memory_manager.value_updated(node.dest) - self.memory_manager.free(reg1) - self.memory_manager.free(reg2) - #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() - #if type(node.left) == int: - # instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #else: - # left_location = self.get_var_location(node.left) - # instructions.append(mips.LoadWordNode(reg1, left_location)) - - #if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(reg2, node.right)) - #else: - # right_location = self.get_var_location(node.right) - # instructions.append(mips.LoadWordNode(reg2, right_location)) + reg1, reg2 = None, None + if type(node.left) == int: + #instructions.append(mips.LoadInmediateNode(reg1, node.left)) + #reg1 = self.memory_manager.get + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + #left_location = self.get_var_location(node.left) + #instructions.append(mips.LoadWordNode(reg1, left_location)) + reg1 = self.memory_manager.get_reg_for_var(node.left) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) - #instructions.append(mips.DivideNode(reg1, reg2)) + if type(node.right) == int: + #instructions.append(mips.LoadInmediateNode(reg2, node.right)) + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + #right_location = self.get_var_location(node.right) + #instructions.append(mips.LoadWordNode(reg2, right_location)) + reg2 = self.memory_manager.get_reg_for_var(node.right) + if reg2 is None: + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) - #dest_location = self.get_var_location(node.dest) - - #instructions.append(mips.MoveFromLowNode(reg1)) - #instructions.append(mips.StoreWordNode(reg1, dest_location)) + instructions.append(mips.DivideNode(reg1, reg2)) + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.MoveFromLowNode(mips.ARG_REGISTERS[0])) + instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveFromLowNode(reg3)) #self.free_reg(reg1) #self.free_reg(reg2) @@ -1266,34 +1098,34 @@ def visit(self, node): instructions = [] reg1 = None - if type(node.obj) == int: #instructions.append(mips.LoadInmediateNode(reg1, node.obj)) - reg1 = self.memory_manager.get_register_for_value() - load = self.memory_manager.load_value_in_register(reg1, node.obj) - instructions.extend(load) + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.obj)) else: #left_location = self.get_var_location(node.obj) #instructions.append(mips.LoadWordNode(reg1, left_location)) - reg1 = self.memory_manager.get_register(node.obj) - load = self.memory_manager.load_in_register(reg1, node.obj) - instructions.extend(load) + reg1 = self.memory_manager.get_reg_for_var(node.obj) + if reg1 is None: + reg1 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) #dest_location = self.get_var_location(node.dest) - reg2 = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg2, node.dest) - instructions.extend(load) + reg2 = self.memory_manager.get_reg_for_var(node.dest) + if reg2 is None: + reg2 = mips.ARG_REGISTERS[1] + instructions.append(mips.ComplementNode(reg2, reg1)) + instructions.append(mips.AddInmediateNode(reg2, reg2, 1)) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest))) + else: + instructions.append(mips.ComplementNode(reg2, reg1)) + instructions.append(mips.AddInmediateNode(reg2, reg2, 1)) #instructions.append(mips.ComplementNode(reg1, reg1)) #instructions.append(mips.AddInmediateNode(reg1, reg1, 1)) #instructions.append(mips.StoreWordNode(reg1, dest_location)) - instructions.append(mips.ComplementNode(reg2, reg1)) - instructions.append(mips.AddInmediateNode(reg2, reg2, 1)) - self.memory_manager.value_updated(node.dest) - - self.memory_manager.free(reg1) #self.free_reg(reg1) @@ -1306,47 +1138,37 @@ def visit(self, node): instructions = [] #Save $a0, $a1, $v0 - if isinstance(node.left, str): - reg = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg, node.left) - instructions.extend(load) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) - - if isinstance(node.right, str): - reg = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg, node.right) - instructions.extend(load) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) - if isinstance(node.left, int): + if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) - - if isinstance(node.right, int): + else: + #left_location = self.get_var_location(node.left) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) + reg = self.memory_manager.get_reg_for_var(node.left) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + + if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) - + else: + #right_location = self.get_var_location(node.right) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) + reg = self.memory_manager.get_reg_for_var(node.right) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.right))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + instructions.append(mips.JumpAndLinkNode('less_equal')) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) - - - #if type(node.left) == int: - # instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #else: - # left_location = self.get_var_location(node.left) - # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) - - #if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(reg2, node.right)) - #else: - # right_location = self.get_var_location(node.right) - # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) - - #instructions.append(mips.JumpAndLinkNode('less_equal')) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -1355,46 +1177,37 @@ def visit(self, node): instructions = [] #Save $a0, $a1, $v0 - if isinstance(node.left, str): - reg = self.memory_manager.get_register(node.left) - load = self.memory_manager.load_in_register(reg, node.left) - instructions.extend(load) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) - - if isinstance(node.right, str): - reg = self.memory_manager.get_register(node.right) - load = self.memory_manager.load_in_register(reg, node.right) - instructions.extend(load) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) - - if isinstance(node.left, int): + if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) - - if isinstance(node.right, int): + else: + #left_location = self.get_var_location(node.left) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) + reg = self.memory_manager.get_reg_for_var(node.left) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + + if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) - - instructions.append(mips.JumpAndLinkNode('less')) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) - - #if type(node.left) == int: - # instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #else: - # left_location = self.get_var_location(node.left) - # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) - - #if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(reg2, node.right)) - #else: - # right_location = self.get_var_location(node.right) - # instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) - - #instructions.append(mips.JumpAndLinkNode('less')) + else: + #right_location = self.get_var_location(node.right) + #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) + reg = self.memory_manager.get_reg_for_var(node.right) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.right))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + + instructions.append(mips.JumpAndLinkNode('lessl')) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) + return instructions @@ -1406,10 +1219,11 @@ def visit(self, node): #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -1418,22 +1232,23 @@ def visit(self, node): instructions = [] #save $a0 $v0 #src_location = self.get_var_location(node.source) - reg = self.memory_manager.get_register(node.source) - load = self.memory_manager.load_in_register(reg, node.source) - instructions.extend(load) #dest_location = self.get_var_location(node.dest) + reg = self.memory_manager.get_reg_for_var(node.source) + if reg is None: + reg = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], src_location)) instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) instructions.append(mips.JumpAndLinkNode("len")) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -1442,15 +1257,15 @@ def visit(self, node): instructions = [] #save $v0 #dest_location = self.get_var_location(node.dest) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.load_in_register(reg, node.dest) - instructions.extend(load) instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) instructions.append(mips.SyscallNode()) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -1463,34 +1278,36 @@ def visit(self, node): #prefix_location = self.get_var_location(node.prefix) #suffix_location = self.get_var_location(node.suffix) #lenght_location = self.get_var_location(node.length) - reg1 = self.memory_manager.get_register(node.prefix) - load = self.memory_manager.load_in_register(reg1, node.prefix) - instructions.extend(load) - - reg2 = self.memory_manager.get_register(node.suffix) - load = self.memory_manager.load_in_register(reg2, node.suffix) - instructions.extend(load) - - reg3 = self.memory_manager.get_register(node.length) - load = self.memory_manager.load_in_register(reg3, node.length) - instructions.extend(load) + reg = self.memory_manager.get_reg_for_var(node.prefix) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.prefix))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + + reg = self.memory_manager.get_reg_for_var(node.suffix) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.suffix))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + reg = self.memory_manager.get_reg_for_var(node.length) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], self.get_var_location(node.lenght))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg)) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], prefix_location)) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], suffix_location)) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg1)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg2)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg3)) instructions.append(mips.JumpAndLinkNode("concat")) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions @@ -1501,23 +1318,25 @@ def visit(self, node): #save $a0, $a1, $a2, $v0 #str_location = self.get_var_location(node.str_value) - reg = self.memory_manager.get_register(node.str_value) - load = self.memory_manager.load_in_register(reg, node.str_value) - instructions.extend(load) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], str_location)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + reg = self.memory_manager.get_reg_for_var(node.str_value) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.str_value))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) if type(node.index) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.index)) else: #index_location = self.get_var_location(node.index) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], index_location)) - reg = self.memory_manager.get_register(node.index) - load = self.memory_manager.load_in_register(reg, node.index) - instructions.extend(load) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) + reg = self.memory_manager.get_reg_for_var(node.index) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.index))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) if type(node.length) == int: @@ -1525,19 +1344,20 @@ def visit(self, node): else: #lenght_location = self.get_var_location(node.length) #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) - reg = self.memory_manager.get_register(node.length) - load = self.memory_manager.load_in_register(reg, node.length) - instructions.extend(load) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg)) + reg = self.memory_manager.get_reg_for_var(node.length) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], self.get_var_location(node.length))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg)) + instructions.append(mips.JumpAndLinkNode("substr")) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - reg = self.memory_manager.get_register(node.dest) - load = self.memory_manager.use_register(reg, node.dest) - instructions.extend(load) - instructions.append(mips.MoveNode(reg, mips.V0_REG)) - self.memory_manager.value_updated(node.dest) - + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) return instructions From 5fedff151bb81f2cdef2ac0419aad4a25dc15f82 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Fri, 20 Nov 2020 11:09:14 -0500 Subject: [PATCH 453/520] fix errors translating CIL code and fix error creating flow graph between basic blocks --- src/core/cmp/cil_to_mips.py | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 2b3c294f..045db7f9 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -5,7 +5,6 @@ from random import choice from collections import defaultdict from core.cmp.utils import CountDict -from pprint import pprint USED = 1 @@ -39,13 +38,9 @@ def __repr__(self): return str(len([0 for r in self.registers if self.registers[r] == USED ])) class MemoryManager: - def __init__(self, registers, vars_with_addresses, function_for_assign): + def __init__(self, registers, function_for_assign): self.registers = registers self.func = function_for_assign - self.place_in_memory = dict(vars_with_addresses) - self.updated_in_mem = {var : True for var, _ in vars_with_addresses} - self.vars_in_reg = { reg : [] for reg in registers } - self.locked = [] def get_reg_for_var(self, var): @@ -213,8 +208,7 @@ def visit(self, node): localvars = [local.name for local in node.localvars] size_for_locals = len(localvars) * mips.ATTR_SIZE - for ins in enumerate(node.instructions): - print(ins) + new_func = mips.FunctionNode(label, params, localvars) self.register_function(node.name, new_func) @@ -222,12 +216,8 @@ def visit(self, node): ra = RegistersAllocator() reg_for_var = ra.get_registers_for_variables(node.instructions, node.params, len(mips.REGISTERS)) - - vars_with_addresses = [] - for var in params + localvars: - vars_with_addresses.append((var, self.get_var_location(var))) - self.memory_manager = MemoryManager(mips.REGISTERS, vars_with_addresses, lambda x : reg_for_var[x]) + self.memory_manager = MemoryManager(mips.REGISTERS, lambda x : reg_for_var[x]) @@ -266,12 +256,8 @@ def visit(self, node): final_instructions = [] for param in params: - if "main" in node.name: - print(param) reg = self.memory_manager.get_reg_for_var(param) if reg is not None: - if "main" in node.name: - print(f"{param} in {reg}") code_instructions.insert(0,mips.LoadWordNode(reg, self.get_var_location(param))) if not self.in_entry_function(): @@ -296,9 +282,6 @@ def visit(self, node): func_instructions = list(itt.chain(initial_instructions, code_instructions, final_instructions)) new_func.add_instructions(func_instructions) - #print(mips.PrintVisitor().visit(new_func)) - if "main" in node.name: - input() self.finish_functions() @@ -846,7 +829,7 @@ def visit(self, node): instructions.append(mips.AddUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[2])) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 8))) - instructions.append(mips.AddInmediateUnsignedNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], method_index * 4)) + instructions.append(mips.AddInmediateUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], method_index * 4)) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) instructions.append(mips.JumpRegisterAndLinkNode(mips.ARG_REGISTERS[1])) @@ -1199,7 +1182,7 @@ def visit(self, node): else: instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) - instructions.append(mips.JumpAndLinkNode('lessl')) + instructions.append(mips.JumpAndLinkNode('less')) #dest_location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) @@ -1583,7 +1566,7 @@ def add(set1, set2): return in_out @staticmethod - def create_flow_graph(blocks, debug = False): #graph between blocks in a same function does not include relations between functions + def create_flow_graph(blocks): #graph between blocks in a same function does not include relations between functions graph = [[-1 for _ in range(len(blocks))] for _ in range(len(blocks)) ] labels = {b[0].label : i for i, b in enumerate(blocks) if isinstance(b[0], cil.LabelNode)} @@ -1593,6 +1576,9 @@ def create_flow_graph(blocks, debug = False): #graph between blocks in a same fu elif isinstance(block[-1], cil.GotoIfNode): graph[i][labels[block[-1].label]] = 1 graph[i][i + 1] = 1 if i + 1 < len(blocks) else -1 + elif i != len(blocks) - 1: + graph[i][i + 1] = 1 + return graph @staticmethod From 45d6df29f8b404daa1e3762901976c74521da567 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 28 Nov 2020 16:40:04 -0500 Subject: [PATCH 454/520] Fix typo --- src/core/cmp/cil_to_mips.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 045db7f9..b163fa2e 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -356,14 +356,14 @@ def visit(self, node): reg1 = self.memory_manager.get_reg_for_var(node.source) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] - instructions.append(LoadWordNode(reg1, self.get_var_location(node.source))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source))) #location = self.get_var_location(node.dest) #instructions.append(mips.StoreWordNode(reg2, location)) #self.free_reg(reg) reg2 = self.memory_manager.get_reg_for_var(node.dest) if reg2 is None: - instructions.append(mips.StoreWordNode(reg1, self.get_var_locations(node.dest))) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) else: instructions.append(mips.MoveNode(reg2, reg1)) From b5249c4499aec70519067cce2eaca7657f78a1a8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 17:43:41 -0500 Subject: [PATCH 455/520] [smantic] - Semantic method errors changed Now some errors are TypeError and AttributeError --- src/core/cmp/semantic.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index 583a624f..1a4f241b 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -49,11 +49,11 @@ def get_attribute(self, name:str): return next(attr for attr in self.attributes if attr.name == name) except StopIteration: if self.parent is None: - raise SemanticError(f'Attribute "{name}" is not defined in {self.name}.') + raise AttributeError(f'Attribute "{name}" is not defined in {self.name}.') try: return self.parent.get_attribute(name) - except SemanticError: - raise SemanticError(f'Attribute "{name}" is not defined in {self.name}.') + except AttributeError: + raise AttributeError(f'Attribute "{name}" is not defined in {self.name}.') def define_attribute(self, name:str, typex): try: @@ -70,11 +70,11 @@ def get_method(self, name:str): return self.methods[name] except KeyError: if self.parent is None: - raise SemanticError(f'Method "{name}" is not defined in {self.name}.') + raise AttributeError(f'Method "{name}" is not defined in {self.name}.') try: return self.parent.get_method(name) - except SemanticError: - raise SemanticError(f'Method "{name}" is not defined in {self.name}.') + except AttributeError: + raise AttributeError(f'Method "{name}" is not defined in {self.name}.') def define_method(self, name:str, param_names:list, param_types:list, return_type): # //TODO: Remove the below if clause @@ -217,7 +217,7 @@ def get_type(self, name:str): try: return self.types[name] except KeyError: - raise SemanticError(f'Type "{name}" is not defined.') + raise TypeError(f'Type "{name}" is not defined.') def __str__(self): return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' From 6a1963405db15670dd0ed6507bd60119d4764d0e Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 17:48:59 -0500 Subject: [PATCH 456/520] [visitors] - Saving the error instead of its message The name of the error is necessary for display them correctly --- src/core/cmp/visitors.py | 105 ++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 3955d43f..4855ccd4 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -210,7 +210,7 @@ def get_type_level(typex): if parent == 0: node = self.parent[typex] node.parent = "Object" - self.errors.append(('Cyclic heritage.', node.tparent)) + self.errors.append(SemanticError('Cyclic heritage.'), node.tparent) elif type(parent) is not int: self.type_level[typex] = 0 if parent else 1 if type(parent) is str: @@ -237,10 +237,10 @@ def make_a_duplicate(): if node.id not in built_in_types: try: new_type() except SemanticError as ex: - self.errors.append((ex.text, node.tid)) + self.errors.append((ex, node.tid)) make_a_duplicate() else: - self.errors.append((f'{node.id} is an invalid class name', node.tid)) + self.errors.append((SemanticError(f'{node.id} is an invalid class name'), node.tid)) make_a_duplicate() # Type Builder @@ -268,11 +268,11 @@ def visit(self, node): method = main.methods['main'] tmethod = self.methods['Main']['main'] if method.param_names: - self.errors.append(('Method "main" must takes no formal parameters', tmethod)) + self.errors.append((SemanticError('Method "main" must takes no formal parameters'), tmethod)) except SemanticError: - self.errors.append(('No definition for class "Main"', empty_token)) + self.errors.append((SemanticError('No definition for class "Main"'), empty_token)) except KeyError: - self.errors.append(('Class "Main" must have a method "main"', main_token)) + self.errors.append((SemanticError('Class "Main" must have a method "main"'), main_token)) @visitor.when(ClassDeclarationNode) def visit(self, node): @@ -280,13 +280,13 @@ def visit(self, node): if node.parent: if node.parent in sealed: - self.errors.append((f'Is not possible to inherits from "{node.parent}"', node.tparent)) + self.errors.append((SemanticError(f'Is not possible to inherits from "{node.parent}"'), node.tparent)) node.parent = 'Object' try: parent_type = self.context.get_type(node.parent) self.current_type.set_parent(parent_type) - except SemanticError as ex: - self.errors.append((ex.text, node.tparent)) + except TypeError as ex: + self.errors.append((ex, node.tparent)) for feature in node.features: self.visit(feature) @@ -295,8 +295,8 @@ def visit(self, node): def visit(self, node): try: attr_type = self.context.get_type(node.type) - except SemanticError as ex: - self.errors.append((ex.text, node.ttype)) + except TypeError as ex: + self.errors.append((ex, node.ttype)) attr_type = ErrorType() node.attr_type = attr_type @@ -305,7 +305,7 @@ def visit(self, node): attr.node = node node.attr = attr except SemanticError as ex: - self.errors.append((ex.text, node.tid)) + self.errors.append((ex, node.tid)) @visitor.when(FuncDeclarationNode) def visit(self, node): @@ -315,11 +315,11 @@ def visit(self, node): try: assert typex != ST arg_type = self.context.get_type(typex) - except SemanticError as ex: - self.errors.append((ex.text, node.params[i].ttype)) + except TypeError as ex: + self.errors.append((ex, node.params[i].ttype)) arg_type = ErrorType() except AssertionError: - self.errors.append((INVALID_PARAMETER % (idx), node.params[i].ttype)) + self.errors.append((SemanticError(INVALID_PARAMETER % (idx)), node.params[i].ttype)) arg_type = ErrorType() arg_names.append(idx) @@ -330,8 +330,8 @@ def visit(self, node): try: ret_type = self.context.get_type(node.type) - except SemanticError as ex: - self.errors.append((ex.text, node.ttype)) + except TypeError as ex: + self.errors.append((ex, node.ttype)) ret_type = ErrorType() node.ret_type = ret_type node.arg_types = arg_types @@ -349,7 +349,7 @@ def visit(self, node): self.methods[self.current_type.name] = {} self.methods[self.current_type.name][node.id] = node.tid except SemanticError as ex: - self.errors.append((ex.text, node.tid)) + self.errors.append((ex, node.tid)) # Compute the Lowest Common Ancestor in # the type hierarchy tree @@ -456,7 +456,7 @@ def visit(self, node, scope): node.info = [expr_type, real_type] if not expr_type.conforms_to(real_type): - self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, real_type.name), node.arrow)) + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (expr_type.name, real_type.name)), node.arrow)) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -473,7 +473,7 @@ def visit(self, node, scope): node.info = [body_type, method_rtn_type] if not body_type.conforms_to(method_rtn_type): - self.errors.append((INCOMPATIBLE_TYPES % (body_type.name, method_rtn_type.name), node.ttype)) + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (body_type.name, method_rtn_type.name)), node.ttype)) @visitor.when(AssignNode) def visit(self, node, scope): @@ -484,15 +484,15 @@ def visit(self, node, scope): try: if not scope.is_defined(node.id): scope.define_variable(node.id, ErrorType()) - raise SemanticError(VARIABLE_NOT_DEFINED % (node.id)) + raise NameError(VARIABLE_NOT_DEFINED % (node.id)) var = scope.find_variable(node.id) var_type = var.type if var.name == 'self': raise SemanticError(SELF_IS_READONLY) if not node_type.conforms_to(var.type): - raise SemanticError(INCOMPATIBLE_TYPES % (node_type.name, var.type.name)) - except SemanticError as ex: - self.errors.append((ex.text, node.tid)) + raise TypeError(INCOMPATIBLE_TYPES % (node_type.name, var.type.name)) + except Exception as ex: + self.errors.append((ex, node.tid)) node_type = ErrorType() node.info = [node_type, var_type] @@ -506,7 +506,8 @@ def visit(self, node, scope): branches = set() for case in node.branches: if case.type in branches: - self.errors.append((DUPLICATED_BRANCH % (case.type), case.ttype)) + # //TODO: Check this again after the Inference process + self.errors.append((SemanticError(DUPLICATED_BRANCH % (case.type)), case.ttype)) branches.add(case.type) self.visit(case, scope.create_child()) types_list.append(case.computed_type) @@ -519,11 +520,11 @@ def visit(self, node, scope): try: assert node.type != ST branch_type = self.context.get_type(node.type) - except SemanticError as ex: - self.errors.append((ex.text, node.ttype)) + except TypeError as ex: + self.errors.append((ex, node.ttype)) branch_type = ErrorType() except AssertionError: - self.errors.append((INVALID_BRANCH % node.id, node.ttype)) + self.errors.append((SemanticError(INVALID_BRANCH % node.id), node.ttype)) branch_type = ErrorType() node.branch_type = branch_type @@ -549,15 +550,15 @@ def visit(self, node, scope): node_type = self.context.get_type(node.type) if node_type.name == ST: node_type = SelfType(self.current_type) - except SemanticError as ex: - self.errors.append((ex.text, node.ttype)) + except TypeError as ex: + self.errors.append((ex, node.ttype)) node_type = ErrorType() node.attr_type = node_type node.scope = None correct_declaration = not scope.is_local(node.id) if not correct_declaration: - self.errors.append((LOCAL_ALREADY_DEFINED % (node.id, self.current_method), node.tid)) + self.errors.append((SemanticError(LOCAL_ALREADY_DEFINED % (node.id, self.current_method)), node.tid)) if node.expr: self.visit(node.expr, scope) @@ -565,7 +566,7 @@ def visit(self, node, scope): node.info = [expr_type, node_type] if not expr_type.conforms_to(node_type): - self.errors.append((INCOMPATIBLE_TYPES % (expr_type.name, node_type.name), node.arrow)) + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (expr_type.name, node_type.name)), node.arrow)) if correct_declaration: var = scope.define_variable(node.id, node_type) @@ -577,7 +578,7 @@ def visit(self, node, scope): node.cond_type = node.condition.computed_type if not node.cond_type.conforms_to(BOOL): - self.errors.append((CONDITION_NOT_BOOL % ('If', cond_type.name), node.token)) + self.errors.append((TypeError(CONDITION_NOT_BOOL % ('If', cond_type.name)), node.token)) self.visit(node.if_body, scope) if_type = node.if_body.computed_type @@ -600,7 +601,7 @@ def visit(self, node, scope): node.cond_type = node.condition.computed_type if not node.cond_type.conforms_to(BOOL): - self.errors.append((CONDITION_NOT_BOOL % ('While', cond_type.name), node.token)) + self.errors.append((TypeError(CONDITION_NOT_BOOL % ('While', cond_type.name)), node.token)) self.visit(node.body, scope) node.computed_type = OBJ @@ -626,7 +627,7 @@ def visit(self, node, scope): if cast_type.name == AT: raise SemanticError('Is not possible to use AUTO_TYPE in a cast') if not obj_type.conforms_to(cast_type): - raise SemanticError(INCOMPATIBLE_TYPES % (obj_type.name, node.type)) + raise TypeError(INCOMPATIBLE_TYPES % (obj_type.name, node.type)) obj_type = cast_type assert obj_type @@ -638,7 +639,7 @@ def visit(self, node, scope): real_types.append(param_type) if not arg.conforms_to(param_type): - self.errors.append((INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}"), token)) + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}")), token)) error = True else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') @@ -646,11 +647,11 @@ def visit(self, node, scope): node_type = obj_method.return_type if node_type.name == ST: node_type = obj_type - except SemanticError as ex: - self.errors.append((ex.text, token)) - node_type = ErrorType() except AssertionError: node_type = ErrorType() + except Exception as ex: + self.errors.append((ex, token)) + node_type = ErrorType() node.info = [arg_types, real_types] node.computed_type = node_type @@ -675,7 +676,7 @@ def visit(self, node, scope): real_types.append(param_type) if not arg.conforms_to(param_type): - self.errors.append((INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}"), token)) + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}")), token)) error = True else: raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') @@ -683,11 +684,11 @@ def visit(self, node, scope): node_type = obj_method.return_type if node_type.name == ST: node_type = obj_type - except SemanticError as ex: - self.errors.append((ex.text, token)) - node_type = ErrorType() except AssertionError: node_type = ErrorType() + except Exception as ex: + self.errors.append((ex, token)) + node_type = ErrorType() node.info = [arg_types, real_types] node.computed_type = node_type @@ -702,7 +703,7 @@ def visit(self, node, scope): node.info = [left_type, right_type] if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): - self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) + self.errors.append((TypeError(INVALID_OPERATION % (left_type.name, right_type.name)), node.symbol)) node.computed_type = [BOOL, INT][isinstance(node, ArithmeticNode)] @@ -724,7 +725,7 @@ def visit(self, node, scope): node_type = scope.find_variable(node.lex).type else: scope.define_variable(node.lex, ErrorType()) - self.errors.append((VARIABLE_NOT_DEFINED % (node.lex), node.token)) + self.errors.append((NameError(VARIABLE_NOT_DEFINED % (node.lex)), node.token)) node_type = ErrorType() node.computed_type = node_type @@ -735,8 +736,8 @@ def visit(self, node, scope): node_type = self.context.get_type(node.type) if node.type == ST: node_type = SelfType(self.current_type) - except SemanticError as ex: - self.errors.append((ex.text, node.ttype)) + except TypeError as ex: + self.errors.append((ex, node.ttype)) node_type = ErrorType() node.computed_type = node_type @@ -753,7 +754,7 @@ def visit(self, node, scope): node.expr_type = expr_type if not expr_type.conforms_to(INT): - self.errors.append(("Complement works only for Int", node.symbol)) + self.errors.append((TypeError("Complement works only for Int"), node.symbol)) node.computed_type = INT @visitor.when(NotNode) @@ -763,7 +764,7 @@ def visit(self, node, scope): node.expr_type = expr_type if not expr_type.conforms_to(BOOL): - self.errors.append(("Not operator works only for Bool", node.symbol)) + self.errors.append((TypeError("Not operator works only for Bool"), node.symbol)) node.computed_type = BOOL @visitor.when(EqualNode) @@ -785,7 +786,7 @@ def visit(self, node, scope): break except ValueError: pass except AssertionError: - self.errors.append((INVALID_OPERATION % (left_type.name, right_type.name), node.symbol)) + self.errors.append((TypeError(INVALID_OPERATION % (left_type.name, right_type.name)), node.symbol)) node.computed_type = BOOL @@ -907,7 +908,7 @@ def visit(self, node, scope=None): self.context_update(auto, D1) infered += 1 except AssertionError: - self.errors.append((f'Bad use of AUTO_TYPE detected', auto.ttype)) + self.errors.append((SemanticError(f'Bad use of AUTO_TYPE detected'), auto.ttype)) if not infered: for auto in pending: auto.type = OBJ.name @@ -1094,4 +1095,4 @@ def visit(self, node): except SemanticError: pass except AssertionError: - self.errors.append((f'Method "{m1.name}" already defined in {self.current_type.name} with a different signature.', node.tid)) \ No newline at end of file + self.errors.append((SemanticError(f'Method "{m1.name}" already defined in {self.current_type.name} with a different signature.'), node.tid)) \ No newline at end of file From fa29c47bba704dec4919302b623e3a98e9a399ea Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 17:49:56 -0500 Subject: [PATCH 457/520] [main] - Print the type cheking errors accordingly to the new format --- src/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index 0a3d50ca..c039fe1b 100644 --- a/src/main.py +++ b/src/main.py @@ -71,8 +71,8 @@ def main(args): errors.append(e) if errors: - for (msg, token) in errors: - print(f"({token.row},{token.column}) - SemanticError: {msg}") + for (ex, token) in errors: + print(f"({token.row},{token.column}) - {type(ex).__name__}: {str(ex)}") exit(1) # else: # print(FormatVisitor().visit(ast)) From 0a46e2a027173f930429c36d33e48a9eba3f238f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 28 Nov 2020 20:15:03 -0500 Subject: [PATCH 458/520] Add regex case to lexer for newlines in strings state --- src/core/cmp/lex.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/lex.py b/src/core/cmp/lex.py index 03c366af..a7ccca41 100644 --- a/src/core/cmp/lex.py +++ b/src/core/cmp/lex.py @@ -163,9 +163,12 @@ def t_strings_NULL(self, t): self.add_line_column(t) return t - - def t_strings_newline(self, t): + def t_strings_newline1(self, t): r'\\n' + self.string += '\n' + + def t_strings_newline2(self, t): + r'\\\n' t.lexer.lineno+=1 self.string += '\n' From 03072586094a2b34d120cf80a3c2853c31293439 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sat, 28 Nov 2020 20:17:13 -0500 Subject: [PATCH 459/520] Fix interference_compute static method for add all keys to the dict before any action --- src/core/cmp/cil_to_mips.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index b163fa2e..6668ad92 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -1477,7 +1477,13 @@ def liveness_analysis(self, graph, params): @staticmethod def interference_compute(gk, in_out): - neigs = defaultdict(set) + neigs = {} + for g, k in gk: + for v in g: + neigs[v] = set() + for v in k: + neigs[v] = set() + for i,(_, k) in enumerate(gk): for v in k: neigs[v].update(in_out[i][1]) From d9f04e832dbc232a064fd0bbed8e05639cf71a45 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 21:20:02 -0500 Subject: [PATCH 460/520] [cool-to-cil][semantic] - Change exception SemanticError exception catch to AttibuteError --- src/core/cmp/cool_to_cil.py | 4 ++-- src/core/cmp/semantic.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index df55f5d4..7b6425cf 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -560,7 +560,7 @@ def visit(self, node, scope): try: self.current_type.get_attribute(node.id) self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr, self.current_type.name)) - except SemanticError: + except AttributeError: vname = None param_names = [pn.name for pn in self.current_function.params] if node.id in param_names: @@ -920,7 +920,7 @@ def visit(self, node, scope): attr = self.register_local(VariableInfo(node.lex, None)) self.register_instruction(cil.GetAttribNode(attr, self.vself.name, node.lex, self.current_type.name)) scope.ret_expr = attr - except SemanticError: + except AttributeError: param_names = [pn.name for pn in self.current_function.params] if node.lex in param_names: for n in param_names: diff --git a/src/core/cmp/semantic.py b/src/core/cmp/semantic.py index f7719aed..b396d2a7 100644 --- a/src/core/cmp/semantic.py +++ b/src/core/cmp/semantic.py @@ -59,7 +59,7 @@ def get_attribute(self, name:str): def define_attribute(self, name:str, typex): try: self.get_attribute(name) - except SemanticError: + except AttributeError: attribute = Attribute(name, typex) self.attributes.append(attribute) return attribute @@ -83,7 +83,7 @@ def define_method(self, name:str, param_names:list, param_types:list, return_typ raise SemanticError(f'Method "{name}" already defined in {self.name}') try: method = self.get_method(name) - except SemanticError: + except AttributeError: pass else: if method.return_type != return_type or method.param_types != param_types: From 524758d15c733cbdece085f52b01dece857dc5f4 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 21:23:22 -0500 Subject: [PATCH 461/520] [collector] - Change the cyclic heritage error to follow the new convention --- src/core/cmp/visitors.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 2cb21831..c2f15766 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -201,7 +201,7 @@ def visit(self, node): self.visit(def_class) # comparison for sort node.declarations - def get_type_level(typex): + def get_type_level(typex, error_token=empty_token): try: parent = self.type_level[typex] except KeyError: @@ -210,12 +210,12 @@ def get_type_level(typex): if parent == 0: node = self.parent[typex] node.parent = "Object" - self.errors.append(SemanticError('Cyclic heritage.'), node.tparent) + self.errors.append((SemanticError('Cyclic heritage.'), error_token)) elif type(parent) is not int: self.type_level[typex] = 0 if parent else 1 if type(parent) is str: - self.type_level[typex] = get_type_level(parent) + 1 - + self.type_level[typex] = get_type_level(parent, self.parent[typex].tid) + 1 + return self.type_level[typex] node.declarations.sort(key = lambda node: get_type_level(node.id)) From 9557291a8f8f79688222c05567a1b536fe5f56b7 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 21:24:49 -0500 Subject: [PATCH 462/520] [visitor] - Change a forgotten SemanticError to TypeError --- src/core/cmp/visitors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index c2f15766..6f40d9c7 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -269,7 +269,7 @@ def visit(self, node): tmethod = self.methods['Main']['main'] if method.param_names: self.errors.append((SemanticError('Method "main" must takes no formal parameters'), tmethod)) - except SemanticError: + except TypeError: self.errors.append((SemanticError('No definition for class "Main"'), empty_token)) except KeyError: self.errors.append((SemanticError('Class "Main" must have a method "main"'), main_token)) From eee46a7a54bd7405c9d7ec8b8e028465587f7759 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 21:25:28 -0500 Subject: [PATCH 463/520] [visitor] - Fix an old typo error --- src/core/cmp/visitors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 6f40d9c7..cabfa615 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -578,7 +578,7 @@ def visit(self, node, scope): node.cond_type = node.condition.computed_type if not node.cond_type.conforms_to(BOOL): - self.errors.append((TypeError(CONDITION_NOT_BOOL % ('If', cond_type.name)), node.token)) + self.errors.append((TypeError(CONDITION_NOT_BOOL % ('If', node.cond_type.name)), node.token)) self.visit(node.if_body, scope) if_type = node.if_body.computed_type @@ -601,7 +601,7 @@ def visit(self, node, scope): node.cond_type = node.condition.computed_type if not node.cond_type.conforms_to(BOOL): - self.errors.append((TypeError(CONDITION_NOT_BOOL % ('While', cond_type.name)), node.token)) + self.errors.append((TypeError(CONDITION_NOT_BOOL % ('While', node.cond_type.name)), node.token)) self.visit(node.body, scope) node.computed_type = OBJ From fa5efda964cc60c48767d78ac99d71f0396e3615 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 21:33:30 -0500 Subject: [PATCH 464/520] [visitors] - Avoid using self keyword - As attribute - As method parameter - In case branch definition - In let variable definitions - Method name --- src/core/cmp/visitors.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index cabfa615..50f23088 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -301,6 +301,8 @@ def visit(self, node): node.attr_type = attr_type try: + if node.id == 'self': + raise SemanticError(SELF_IS_READONLY) attr = self.current_type.define_attribute(node.id, attr_type) attr.node = node node.attr = attr @@ -322,6 +324,8 @@ def visit(self, node): self.errors.append((SemanticError(INVALID_PARAMETER % (idx)), node.params[i].ttype)) arg_type = ErrorType() + if idx == 'self': + self.errors.append((SemanticError('"self" cannot be the name of a formal parameter'), node.params[i].ttype)) arg_names.append(idx) arg_types.append(arg_type) arg_nodes.append(arg) @@ -339,6 +343,8 @@ def visit(self, node): node.arg_nodes = arg_nodes try: + if node.id == 'self': + raise SemanticError('"self" is an invalid method name') method = self.current_type.define_method(node.id, arg_names, arg_types, ret_type) method.nodes = arg_nodes method.ret_node = node @@ -527,9 +533,12 @@ def visit(self, node, scope): self.errors.append((SemanticError(INVALID_BRANCH % node.id), node.ttype)) branch_type = ErrorType() node.branch_type = branch_type - - var = scope.define_variable(node.id, branch_type) - var.node = node + + if node.id == 'self': + self.errors.append((SemanticError(SELF_IS_READONLY), node.id)) + else: + var = scope.define_variable(node.id, branch_type) + var.node = node self.visit(node.expr, scope) node.computed_type = node.expr.computed_type @@ -567,8 +576,9 @@ def visit(self, node, scope): if not expr_type.conforms_to(node_type): self.errors.append((TypeError(INCOMPATIBLE_TYPES % (expr_type.name, node_type.name)), node.arrow)) - - if correct_declaration: + if node.id == 'self': + self.errors.append((SemanticError(SELF_IS_READONLY), node.tid)) + else: var = scope.define_variable(node.id, node_type) var.node = node From e69f286b5ca34d10ade67989ea4da38f3427886a Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 21:34:23 -0500 Subject: [PATCH 465/520] [builder] - Avoid redefined method parameter names --- src/core/cmp/visitors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 50f23088..8b346a3f 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -323,9 +323,11 @@ def visit(self, node): except AssertionError: self.errors.append((SemanticError(INVALID_PARAMETER % (idx)), node.params[i].ttype)) arg_type = ErrorType() - + if idx == 'self': self.errors.append((SemanticError('"self" cannot be the name of a formal parameter'), node.params[i].ttype)) + if idx in arg_names: + self.errors.append((SemanticError(f'Formal parameter {idx} redefined'), node.params[i].ttype)) arg_names.append(idx) arg_types.append(arg_type) arg_nodes.append(arg) From 88a1a7061c2819384109f5af88f59e1278b5d493 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 21:38:05 -0500 Subject: [PATCH 466/520] [checker] - Allow let variable name re-usability in the same expresion In the let part for define variables if some names are used several times, the last one was the one available in the let-in body part --- src/core/cmp/visitors.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py index 8b346a3f..f1615c21 100644 --- a/src/core/cmp/visitors.py +++ b/src/core/cmp/visitors.py @@ -546,13 +546,13 @@ def visit(self, node, scope): @visitor.when(LetInNode) def visit(self, node, scope): - child = scope.create_child() - node.scope = child + node.scope = scope for expr in node.let_body: - self.visit(expr, child) + node.scope = node.scope.create_child() + self.visit(expr, node.scope) - self.visit(node.in_body, child) + self.visit(node.in_body, node.scope) node.computed_type = node.in_body.computed_type @visitor.when(LetAttributeNode) @@ -567,10 +567,6 @@ def visit(self, node, scope): node.attr_type = node_type node.scope = None - correct_declaration = not scope.is_local(node.id) - if not correct_declaration: - self.errors.append((SemanticError(LOCAL_ALREADY_DEFINED % (node.id, self.current_method)), node.tid)) - if node.expr: self.visit(node.expr, scope) expr_type = node.expr.computed_type From d7a1ab604721ae9812470ea51994ea3677368d20 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sat, 28 Nov 2020 22:01:57 -0500 Subject: [PATCH 467/520] [makefile] - Change the deafult rule main rule use code.cl that is ignored, that is the main reason of failure of every github job. Some of them will continue failing :( --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 498fdd60..3b14065c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,4 @@ -.DEFAULT_GOAL := main +.DEFAULT_GOAL := help .PHONY: clean, info CODE := code.cl From 6a89bc81e7f8c830f98826540ae15606b79f48d6 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sat, 28 Nov 2020 23:54:18 -0500 Subject: [PATCH 468/520] [cil] - Add support for generate the code of parent's attrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate `init_attr` for this purpose 😛 --- src/core/cmp/cool_to_cil.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 53f71690..76b64a76 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -327,11 +327,24 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.AllocateNode(node.id, instance)) - scope.ret_expr = instance + scope.ret_expr = instance + + func = self.current_function + vtemp = self.define_internal_local() + #init_attr + self.current_function = self.register_function(self.to_function_name('init_attr', node.id)) + if node.parent != 'object': + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.parent), vtemp)) attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) for feature in attr_declarations: self.visit(feature, scope) + + self.current_function = func + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.id), vtemp)) + self.register_instruction(cil.ReturnNode(instance)) self.current_function = None @@ -379,7 +392,7 @@ def visit(self, node, scope): # (Handle RETURN) if scope.ret_expr is None: self.register_instruction(cil.ReturnNode('')) - elif self.current_function.name is 'entry': + elif self.current_function.name == 'entry': self.register_instruction(cil.ReturnNode(0)) else: self.register_instruction(cil.ReturnNode(scope.ret_expr)) @@ -957,7 +970,7 @@ def visit(self, node, scope): ############################### # node.lex -> str ############################### - if node.lex is 'true': + if node.lex == 'true': scope.ret_expr = 1 else: scope.ret_expr = 0 From c1b89d2574f2221429cae79d8cd06c87c9beff73 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 00:03:27 -0500 Subject: [PATCH 469/520] [cil] - Fix typo in init generation of IO type --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 8b6ef355..4baeff7a 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -114,7 +114,7 @@ def register_built_in(self): self.current_function = self.register_function(self.to_function_name('init', 'IO')) instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode('Object', instance)) + self.register_instruction(cil.AllocateNode('IO', instance)) self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('out_string', 'IO')) From b3f1487fd8ac598455ac507cfd7d6658efe98884 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 00:16:59 -0500 Subject: [PATCH 470/520] [cil] - Fix a typo --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 4baeff7a..f88cc61a 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -334,7 +334,7 @@ def visit(self, node, scope): #init_attr self.current_function = self.register_function(self.to_function_name('init_attr', node.id)) - if node.parent != 'object': + if node.parent != 'Object': self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.parent), vtemp)) attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) From dec266c9747e28dda906f240bda1213ec21d564e Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 00:28:28 -0500 Subject: [PATCH 471/520] [cil] - Don't call init_attr if parent is IO --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index f88cc61a..8326a76f 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -334,7 +334,7 @@ def visit(self, node, scope): #init_attr self.current_function = self.register_function(self.to_function_name('init_attr', node.id)) - if node.parent != 'Object': + if node.parent != 'Object' and node.parent != 'IO': self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.parent), vtemp)) attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) From 4663fd4d00c6d0b8eacd5fced7311e5c550c2876 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 29 Nov 2020 11:05:30 -0500 Subject: [PATCH 472/520] Assign registers only if there are instructions in the function --- src/core/cmp/cil_to_mips.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 6668ad92..3eb99463 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -215,7 +215,9 @@ def visit(self, node): self.init_function(new_func) ra = RegistersAllocator() - reg_for_var = ra.get_registers_for_variables(node.instructions, node.params, len(mips.REGISTERS)) + + if len(node.instructions): + reg_for_var = ra.get_registers_for_variables(node.instructions, node.params, len(mips.REGISTERS)) self.memory_manager = MemoryManager(mips.REGISTERS, lambda x : reg_for_var[x]) From 8855520b3f2e77469f24fa8923b18e011a41e0f9 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 29 Nov 2020 11:12:52 -0500 Subject: [PATCH 473/520] Change output file name to use the same name of the input file --- src/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index 0a8ef061..1f8058b8 100644 --- a/src/main.py +++ b/src/main.py @@ -96,7 +96,11 @@ def main(args): printer = PrintVisitor() mips_code = printer.visit(mips_ast) - with open("compiled.asm", 'w') as f: + out_file = args.file.split(".") + out_file[-1] = "mips" + out_file = ".".join(out_file) + + with open(out_file, 'w') as f: f.write(mips_code) with open("./core/cmp/mips_lib.asm") as f2: f.write("".join(f2.readlines())) From e66e7437587bff59a6af97d13ed50d43f8b32d98 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 11:28:41 -0500 Subject: [PATCH 474/520] [cil] - Update generation of `init_attr` method Add self param --- src/core/cmp/cool_to_cil.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 8326a76f..1082e248 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -324,7 +324,7 @@ def visit(self, node, scope): #init self.current_function = self.register_function(self.to_function_name('init', node.id)) #allocate - instance = self.define_internal_local() + instance = self.register_local(VariableInfo('instance', None)) self.register_instruction(cil.AllocateNode(node.id, instance)) scope.ret_expr = instance @@ -334,8 +334,9 @@ def visit(self, node, scope): #init_attr self.current_function = self.register_function(self.to_function_name('init_attr', node.id)) + self.register_param(self.vself) if node.parent != 'Object' and node.parent != 'IO': - self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.ArgNode(self.vself)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.parent), vtemp)) attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) for feature in attr_declarations: From d7bea0f7814737acac93c57f68f0b2de6d038db9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 12:08:16 -0500 Subject: [PATCH 475/520] [cil] - Update visit of AttrDeclarationNode SETATTR receive now 'self' instead of instance --- src/core/cmp/cool_to_cil.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 1082e248..b8c114c3 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -325,9 +325,7 @@ def visit(self, node, scope): self.current_function = self.register_function(self.to_function_name('init', node.id)) #allocate instance = self.register_local(VariableInfo('instance', None)) - self.register_instruction(cil.AllocateNode(node.id, instance)) - - scope.ret_expr = instance + self.register_instruction(cil.AllocateNode(node.id, instance)) func = self.current_function vtemp = self.define_internal_local() @@ -336,7 +334,7 @@ def visit(self, node, scope): self.current_function = self.register_function(self.to_function_name('init_attr', node.id)) self.register_param(self.vself) if node.parent != 'Object' and node.parent != 'IO': - self.register_instruction(cil.ArgNode(self.vself)) + self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.parent), vtemp)) attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) for feature in attr_declarations: @@ -358,15 +356,13 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - instance = scope.ret_expr if node.expr: self.visit(node.expr, scope) - self.register_instruction(cil.SetAttribNode(instance, node.id, scope.ret_expr, self.current_type)) + self.register_instruction(cil.SetAttribNode(self.vself.name, node.id, scope.ret_expr, self.current_type)) elif node.type in self.value_types: vtemp = self.define_internal_local() self.register_instruction(cil.AllocateNode(node.type, vtemp)) - self.register_instruction(cil.SetAttribNode(instance, node.id, vtemp, self.current_type)) - scope.ret_expr = instance + self.register_instruction(cil.SetAttribNode(self.vself.name, node.id, vtemp, self.current_type)) @visitor.when(cool.FuncDeclarationNode) def visit(self, node, scope): @@ -575,7 +571,7 @@ def visit(self, node, scope): try: self.current_type.get_attribute(node.id) - self.register_instruction(cil.SetAttribNode(self.vself, node.id, scope.ret_expr, self.current_type.name)) + self.register_instruction(cil.SetAttribNode(self.vself.name, node.id, scope.ret_expr, self.current_type.name)) except AttributeError: vname = None param_names = [pn.name for pn in self.current_function.params] @@ -907,7 +903,7 @@ def visit(self, node, scope): if node.type == 'SELF_TYPE': vtype = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(self.vself, vtype)) + self.register_instruction(cil.TypeOfNode(self.vself.name, vtype)) self.register_instruction(cil.AllocateNode(vtype, instance)) elif node.type == 'Int' or node.type == 'Bool': self.register_instruction(cil.ArgNode(0)) From e7c99ab2feb19d8f4219e61d6d319cb3fc24ebff Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 15:14:38 -0500 Subject: [PATCH 476/520] [cil] - Fix some calls to cil.EqualNode --- src/core/cmp/cool_to_cil.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index b8c114c3..3451e4ae 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -489,7 +489,6 @@ def visit(self, node, scope): ############################################## vexpr = self.register_local(VariableInfo('case_expr_value', None)) vtype = self.register_local(VariableInfo('typeName_value', None)) - vequal = self.register_local(VariableInfo('equal_node_value', None)) vcond = self.register_local(VariableInfo('equal_value', None)) vret = self.register_local(VariableInfo('case_value', None)) self.visit(node.expr, scope) @@ -519,8 +518,7 @@ def visit(self, node, scope): for t in h: vbranch_type_name = self.register_local(VariableInfo('branch_type_name', None)) self.register_instruction(cil.NameNode(vbranch_type_name, t)) - self.register_instruction(cil.EqualNode(vequal, vtype, vbranch_type_name)) - self.register_instruction(cil.GetAttribNode(vcond, vequal, 'value', 'Bool')) + self.register_instruction(cil.EqualNode(vcond, vtype, vbranch_type_name)) self.register_instruction(cil.GotoIfNode(vcond, labels[-1].label)) #Raise runtime error if no Goto was executed @@ -662,7 +660,6 @@ def visit(self, node, scope): type_bool = self.define_internal_local() type_string = self.define_internal_local() equal_result = self.define_internal_local() - equal_value = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() instance = self.define_internal_local() @@ -682,14 +679,11 @@ def visit(self, node, scope): reference_node = self.register_label('reference_label') continue_node = self.register_label('continue_label') self.register_instruction(cil.EqualNode(equal_result, type_left, type_int)) - self.register_instruction(cil.GetAttribNode(equal_value, equal_result, 'value', 'Bool')) - self.register_instruction(cil.GotoIfNode(equal_value, int_node.label)) + self.register_instruction(cil.GotoIfNode(equal_result, int_node.label)) self.register_instruction(cil.EqualNode(equal_result, type_left, type_bool)) - self.register_instruction(cil.GetAttribNode(equal_value, equal_result, 'value', 'Bool')) - self.register_instruction(cil.GotoIfNode(equal_value, int_node.label)) + self.register_instruction(cil.GotoIfNode(equal_result, int_node.label)) self.register_instruction(cil.EqualNode(equal_result, type_left, type_string)) - self.register_instruction(cil.GetAttribNode(equal_value, equal_result, 'value', 'Bool')) - self.register_instruction(cil.GotoIfNode(equal_value, string_node.label)) + self.register_instruction(cil.GotoIfNode(equal_result, string_node.label)) self.register_instruction(cil.GotoNode(reference_node.label)) self.register_instruction(int_node) From 5134fd571add51a6acbffb60cc1a9f4b2e17e9b1 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 29 Nov 2020 15:19:51 -0500 Subject: [PATCH 477/520] Add default value to Strign type --- src/core/cmp/cil_to_mips.py | 10 +++++++--- src/core/cmp/mips.py | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 3eb99463..741507bf 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -169,7 +169,8 @@ def visit(self, node): def visit(self, node): #Get functions names self.collect_func_names(node) - + + self._data_section["default_str"] = mips.StringConst("default_str", "") #Convert CIL ProgramNode to MIPS ProgramNode for tp in node.dottypes: self.visit(tp) @@ -189,7 +190,10 @@ def visit(self, node): type_label = self.generate_type_label() methods = {key: self._name_func_map[value] for key, value in node.methods} - new_type = mips.MIPSType(type_label, name_label, node.attributes, methods, len(self._types)) + defaults = [] + if node.name == "String": + defaults = [('value', 'default_str')] + new_type = mips.MIPSType(type_label, name_label, node.attributes, methods, len(self._types), default=defaults) self._types[node.name] = new_type @@ -219,7 +223,7 @@ def visit(self, node): if len(node.instructions): reg_for_var = ra.get_registers_for_variables(node.instructions, node.params, len(mips.REGISTERS)) - self.memory_manager = MemoryManager(mips.REGISTERS, lambda x : reg_for_var[x]) + self.memory_manager = MemoryManager(mips.REGISTERS, lambda x : reg_for_var[x]) diff --git a/src/core/cmp/mips.py b/src/core/cmp/mips.py index 3bb94ec2..284fbc3c 100644 --- a/src/core/cmp/mips.py +++ b/src/core/cmp/mips.py @@ -218,10 +218,11 @@ def __init__(self, reg): class MIPSType: - def __init__(self, label, name_addr, attributes, methods, index): + def __init__(self, label, name_addr, attributes, methods, index, default = []): self._label = label self._name = name_addr self._attributes = attributes + self._default_attributes = dict(default) self._methods = methods self._index = index @@ -383,7 +384,7 @@ def visit(self, node): methods = "\n".join([f"\t.word\t {node.methods[k]}" for k in node.methods]) dispatch_table = f"{node.label}_dispatch:\n{methods}" proto_begin = f"{node.label}_proto:\n\t.word\t{node.index}\n\t.word\t{node.size}\n\t.word\t{node.label}_dispatch" - proto_attr = "\n".join(['\t.word\t0' for _ in node.attributes]) + proto_attr = "\n".join([f'\t.word\t{node._default_attributes.get(attr, "0")}' for attr in node.attributes]) proto_end = f"\t.word\t{OBJECT_MARK}" proto = f"{proto_begin}\n{proto_attr}\n{proto_end}" if proto_attr != "" else f"{proto_begin}\n{proto_end}" From 229cee5dea6c8c411ce1739fbc3a857fcec43e31 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 29 Nov 2020 15:29:03 -0500 Subject: [PATCH 478/520] Set missing values in mips_library These are not the right values but how gc is not triggered, they are not used --- src/core/cmp/cil_to_mips.py | 1 - src/core/cmp/mips_lib.asm | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 741507bf..0c066d0b 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -222,7 +222,6 @@ def visit(self, node): if len(node.instructions): reg_for_var = ra.get_registers_for_variables(node.instructions, node.params, len(mips.REGISTERS)) - self.memory_manager = MemoryManager(mips.REGISTERS, lambda x : reg_for_var[x]) diff --git a/src/core/cmp/mips_lib.asm b/src/core/cmp/mips_lib.asm index bb1df72b..596dd995 100644 --- a/src/core/cmp/mips_lib.asm +++ b/src/core/cmp/mips_lib.asm @@ -18,6 +18,9 @@ object_expanded = -2 reachable = 1 new_line = 10 str_size_treshold = 1024 +int_type = 0 +string_type = 0 +type_number = 0 From e9b454d47ba3926157ee8c5b3a0125804bc4b42c Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 29 Nov 2020 16:33:33 -0500 Subject: [PATCH 479/520] [doc] - Grammar .tex and .pdf --- src/doc/grammar.pdf | Bin 0 -> 40851 bytes src/doc/grammar.tex | 90 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/doc/grammar.pdf create mode 100644 src/doc/grammar.tex diff --git a/src/doc/grammar.pdf b/src/doc/grammar.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bdf52cdc81481ba3aa8ed426af3f670739811964 GIT binary patch literal 40851 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 Date: Sun, 29 Nov 2020 17:12:30 -0500 Subject: [PATCH 480/520] [grammar] - Fix an error creating IsVoidNode and ComplementNode --- src/core/cmp/CoolUtils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index ffbbe3a8..ce0b3cf0 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -289,8 +289,8 @@ def __iter__(self): special_term %= unary_expr, lambda h, s: s[1] # -unary_expr %= isvoid + unary_expr, lambda h, s: IsVoidNode(s[1], s[3], s[2]) -unary_expr %= compl + unary_expr, lambda h, s: ComplementNode(s[1], s[3], s[2]) +unary_expr %= isvoid + unary_expr, lambda h, s: IsVoidNode(s[2], s[1]) +unary_expr %= compl + unary_expr, lambda h, s: ComplementNode(s[2], s[1]) unary_expr %= final_expr, lambda h, s: s[1] # From 1a4e8be96f01f1e105517fa2cd0378c7e2aeefad Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Sun, 29 Nov 2020 17:18:17 -0500 Subject: [PATCH 481/520] [functions] - Add a 'symbol' token to get_token method --- src/core/cmp/functions.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/core/cmp/functions.py b/src/core/cmp/functions.py index 185553e1..2f5346e9 100644 --- a/src/core/cmp/functions.py +++ b/src/core/cmp/functions.py @@ -259,10 +259,7 @@ def _build_parsing_table(self): self.ok &= upd_table(self.goto, idx, next_symbol, node[next_symbol.Name][0].idx) def get_token(node): - try: - return node.tid - except AttributeError: - try: - return node.token - except AttributeError: - return node.ttype + for attr in ['tid', 'token', 'ttype', 'symbol']: + if hasattr(node, attr): + return getattr(node, attr) + raise Exception(f'{node} has no token') From 420f1b72c822d59664b86f2ea8000632bee7fcb9 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 29 Nov 2020 17:29:32 -0500 Subject: [PATCH 482/520] Add case to ReturnNode for when node.value is Void --- src/core/cmp/cil_to_mips.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/cmp/cil_to_mips.py b/src/core/cmp/cil_to_mips.py index 0c066d0b..f6442d95 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/cmp/cil_to_mips.py @@ -422,8 +422,10 @@ def visit(self, node): if node.value is None: instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0)) - elif type(node.value) == int: + elif isinstance(node.value, int): instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value)) + elif isinstance(node.value, cil.VoidNode): + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0)) else: #location = self.get_var_location(node.value) #instructions.append(mips.LoadWordNode(mips.V0_REG, location)) From a3a9ae7a54987d22107e4c6a054d370285879179 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 17:42:59 -0500 Subject: [PATCH 483/520] [cil] - Create specials init and init_attr methods for types --- src/core/cmp/cool_to_cil.py | 76 ++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3451e4ae..6b860872 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -52,6 +52,11 @@ def register_instruction(self, instruction): def to_function_name(self, method_name, type_name): return f'function_{method_name}_at_{type_name}' + + def init_name(self, type_name, attr=False): + if attr: + return f'init_attr_at_{type_name}' + return f'init_at_{type_name}' def register_function(self, function_name): function_node = cil.FunctionNode(function_name, [], [], []) @@ -78,7 +83,7 @@ def register_built_in(self): #Object type_node = self.register_type('Object') - self.current_function = self.register_function(self.to_function_name('init', 'Object')) + self.current_function = self.register_function(self.init_name('Object')) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('Object', instance)) self.register_instruction(cil.ReturnNode(instance)) @@ -97,7 +102,7 @@ def register_built_in(self): self.register_instruction(cil.TypeNameNode(result, self.vself.name)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('copy', 'Object')) @@ -106,13 +111,14 @@ def register_built_in(self): self.register_instruction(cil.CopyNode(result, self.vself.name)) self.register_instruction(cil.ReturnNode(result)) - type_node.methods = [(name, self.to_function_name(name, 'Object')) for name in ['init', 'abort', 'type_name', 'copy']] + type_node.methods = [(name, self.to_function_name(name, 'Object')) for name in ['abort', 'type_name', 'copy']] + type_node.methods += [('init', self.init_name('Object'))] obj_methods = ['abort', 'type_name', 'copy'] #IO type_node = self.register_type('IO') - self.current_function = self.register_function(self.to_function_name('init', 'IO')) + self.current_function = self.register_function(self.init_name('IO')) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('IO', instance)) self.register_instruction(cil.ReturnNode(instance)) @@ -139,7 +145,7 @@ def register_built_in(self): self.register_instruction(cil.ReadStrNode(result)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) @@ -148,17 +154,18 @@ def register_built_in(self): self.register_instruction(cil.ReadIntNode(result)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] - type_node.methods += [(name, self.to_function_name(name, 'IO')) for name in ['init', 'out_string', 'out_int', 'in_string', 'in_int']] + type_node.methods += [(name, self.to_function_name(name, 'IO')) for name in ['out_string', 'out_int', 'in_string', 'in_int']] + type_node.methods += [('init', self.init_name('IO'))] #String type_node = self.register_type('String') type_node.attributes = ['value', 'length'] - self.current_function = self.register_function(self.to_function_name('init', 'String')) + self.current_function = self.register_function(self.init_name('String')) self.register_param(VariableInfo('val', None)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('String', instance)) @@ -167,7 +174,7 @@ def register_built_in(self): self.register_instruction(cil.LengthNode(result, 'val')) attr = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), attr)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), attr)) self.register_instruction(cil.SetAttribNode(instance, 'length', attr, 'String')) self.register_instruction(cil.ReturnNode(instance)) @@ -196,7 +203,7 @@ def register_built_in(self): self.register_instruction(cil.ConcatNode(result, str_1, str_2, length_1)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('substr', 'String')) @@ -221,17 +228,18 @@ def register_built_in(self): self.register_instruction(cil.SubstringNode(result, str_value, index_value, length_value)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] - type_node.methods += [(name, self.to_function_name(name, 'String')) for name in ['init', 'length', 'concat', 'substr']] + type_node.methods += [(name, self.to_function_name(name, 'String')) for name in ['length', 'concat', 'substr']] + type_node.methods += [('init', self.init_name('String'))] #Int type_node = self.register_type('Int') type_node.attributes = ['value'] - self.current_function = self.register_function(self.to_function_name('init', 'Int')) + self.current_function = self.register_function(self.init_name('Int')) self.register_param(VariableInfo('val', None)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('Int', instance)) @@ -239,13 +247,13 @@ def register_built_in(self): self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] - type_node.methods += [('init', self.to_function_name('init', 'Int'))] + type_node.methods += [('init', self.init_name('Int'))] #Bool type_node = self.register_type('Bool') type_node.attributes = ['value'] - self.current_function = self.register_function(self.to_function_name('init', 'Bool')) + self.current_function = self.register_function(self.init_name('Bool')) self.register_param(VariableInfo('val', None)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('Bool', instance)) @@ -253,7 +261,7 @@ def register_built_in(self): self.register_instruction(cil.ReturnNode(instance)) type_node.methods = [(method, self.to_function_name(method, 'Object')) for method in obj_methods] - type_node.methods += [('init', self.to_function_name('init', 'Bool'))] + type_node.methods += [('init', self.init_name('Bool'))] def register_runtime_error(self, condition, msg): error_node = self.register_label('error_label') @@ -288,7 +296,7 @@ def visit(self, node, scope): self.current_function = self.register_function('entry') result = self.define_internal_local() instance = self.register_local(VariableInfo('instance', None)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Main'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Main'), instance)) self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) @@ -322,7 +330,7 @@ def visit(self, node, scope): self.visit(feature, child_scope) #init - self.current_function = self.register_function(self.to_function_name('init', node.id)) + self.current_function = self.register_function(self.init_name(node.id)) #allocate instance = self.register_local(VariableInfo('instance', None)) self.register_instruction(cil.AllocateNode(node.id, instance)) @@ -331,18 +339,18 @@ def visit(self, node, scope): vtemp = self.define_internal_local() #init_attr - self.current_function = self.register_function(self.to_function_name('init_attr', node.id)) + self.current_function = self.register_function(self.init_name(node.id, attr=True)) self.register_param(self.vself) if node.parent != 'Object' and node.parent != 'IO': self.register_instruction(cil.ArgNode(self.vself.name)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.parent), vtemp)) + self.register_instruction(cil.StaticCallNode(self.init_name(node.parent, attr=True), vtemp)) attr_declarations = (f for f in node.features if isinstance(f, cool.AttrDeclarationNode)) for feature in attr_declarations: self.visit(feature, scope) self.current_function = func self.register_instruction(cil.ArgNode(instance)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init_attr', node.id), vtemp)) + self.register_instruction(cil.StaticCallNode(self.init_name(node.id, attr=True), vtemp)) self.register_instruction(cil.ReturnNode(instance)) self.current_function = None @@ -599,7 +607,7 @@ def visit(self, node, scope): self.register_instruction(cil.MinusNode(vname, 1, value)) self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), instance)) scope.ret_expr = instance @visitor.when(cool.LessEqualNode) @@ -622,7 +630,7 @@ def visit(self, node, scope): self.register_instruction(cil.LessEqualNode(vname, left_value, right_value)) self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), instance)) scope.ret_expr = instance @visitor.when(cool.LessNode) @@ -645,7 +653,7 @@ def visit(self, node, scope): self.register_instruction(cil.LessNode(vname, left_value, right_value)) self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), instance)) scope.ret_expr = instance @visitor.when(cool.EqualNode) @@ -703,7 +711,7 @@ def visit(self, node, scope): self.register_instruction(continue_node) self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), instance)) scope.ret_expr = instance @visitor.when(cool.PlusNode) @@ -722,7 +730,7 @@ def visit(self, node, scope): self.register_instruction(cil.PlusNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) scope.ret_expr = instance @visitor.when(cool.MinusNode) @@ -741,7 +749,7 @@ def visit(self, node, scope): self.register_instruction(cil.MinusNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) scope.ret_expr = instance @visitor.when(cool.StarNode) @@ -760,7 +768,7 @@ def visit(self, node, scope): self.register_instruction(cil.StarNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) scope.ret_expr = instance @visitor.when(cool.DivNode) @@ -786,7 +794,7 @@ def visit(self, node, scope): self.register_instruction(cil.DivNode(vname, vleft, vright)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) scope.ret_expr = instance @visitor.when(cool.IsVoidNode) @@ -814,7 +822,7 @@ def visit(self, node, scope): self.register_instruction(cil.GetAttribNode(value, scope.ret_expr, 'value', 'Int')) self.register_instruction(cil.ComplementNode(vname, value)) self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) scope.ret_expr = instance @visitor.when(cool.FunctionCallNode) @@ -907,7 +915,7 @@ def visit(self, node, scope): self.register_instruction(cil.LoadNode(vmsg, data_node)) self.register_instruction(cil.ArgNode(vmsg)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', node.type), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name(node.type), instance)) scope.ret_expr = instance @visitor.when(cool.IntegerNode) @@ -917,7 +925,7 @@ def visit(self, node, scope): ############################### instance = self.define_internal_local() self.register_instruction(cil.ArgNode(int(node.lex))) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Int'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) scope.ret_expr = instance @visitor.when(cool.IdNode) @@ -956,7 +964,7 @@ def visit(self, node, scope): instance = self.define_internal_local() self.register_instruction(cil.LoadNode(vmsg, data_node)) self.register_instruction(cil.ArgNode(vmsg)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'String'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) scope.ret_expr = instance @visitor.when(cool.BoolNode) @@ -970,5 +978,5 @@ def visit(self, node, scope): scope.ret_expr = 0 instance = self.define_internal_local() self.register_instruction(cil.ArgNode(scope.ret_expr)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('init', 'Bool'), instance)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), instance)) scope.ret_expr = instance From 7a12f1ca5452f60e91f08097c0b82219effc10f8 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Sun, 29 Nov 2020 18:20:33 -0500 Subject: [PATCH 484/520] Fix cool.IsVoidNode to return a Bool object --- src/core/cmp/cool_to_cil.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 6b860872..e23e03e8 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -808,6 +808,8 @@ def visit(self, node, scope): self.register_instruction(cil.AssignNode(value, scope.ret_expr)) result = self.define_internal_local() self.register_instruction(cil.EqualNode(result, value, void)) + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), result)) scope.ret_expr = result @visitor.when(cool.ComplementNode) From a5096f0f9107d2a32ac33196705a8b3a856e105c Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 18:47:09 -0500 Subject: [PATCH 485/520] [cil] - Fix bug related to params in visit of `IdNode` --- src/core/cmp/cool_to_cil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index e23e03e8..864e5ed9 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -944,7 +944,7 @@ def visit(self, node, scope): param_names = [pn.name for pn in self.current_function.params] if node.lex in param_names: for n in param_names: - if node.lex in n.split("_"): + if node.lex in n: scope.ret_expr = n break else: From 7deb1ac13fa2768afb3e8a17d60952288abc72e2 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Sun, 29 Nov 2020 18:51:46 -0500 Subject: [PATCH 486/520] [cil] - Update abort method of Object --- src/core/cmp/cool_to_cil.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 864e5ed9..a5906d80 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -89,10 +89,13 @@ def register_built_in(self): self.register_instruction(cil.ReturnNode(instance)) self.current_function = self.register_function(self.to_function_name('abort', 'Object')) + self.register_param(self.vself) vname = self.define_internal_local() - data_node = [dn for dn in self.dotdata if dn.value == 'Program aborted'][0] + data_node = [dn for dn in self.dotdata if dn.value == 'Abort called from class '][0] self.register_instruction(cil.LoadNode(vname, data_node)) self.register_instruction(cil.PrintStrNode(vname)) + self.register_instruction(cil.TypeNameNode(vname, self.vself.name)) + self.register_instruction(cil.PrintStrNode(vname)) self.register_instruction(cil.ExitNode()) # No need for RETURN here right?? @@ -301,7 +304,7 @@ def visit(self, node, scope): self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), result)) self.register_instruction(cil.ReturnNode(0)) # Error message raised by Object:abort() - self.register_data('Program aborted') + self.register_data('Abort called from class ') self.register_built_in() self.current_function = None From fee1b7a9524d210a365b4dbdf1b316f6af702908 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 30 Nov 2020 01:10:35 -0500 Subject: [PATCH 487/520] [cil] - Fix naming issue --- src/core/cmp/cil.py | 2 +- src/core/cmp/cool_to_cil.py | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index f70d73d2..824fe9da 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -289,7 +289,7 @@ def visit(self, node): def visit(self, node): params = '\n\t'.join(self.visit(x) for x in node.params) localvars = '\n\t'.join(self.visit(x) for x in node.localvars) - instructions = '\n\t'.join(self.visit(x) for x in node.instructions) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions if self.visit(x) != []) return f'function {node.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index a5906d80..3ad58026 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -36,7 +36,10 @@ def register_param(self, vinfo): return vinfo.name def register_local(self, vinfo): - vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + if len(self.current_function.name) >= 8 and self.current_function.name[:8] == 'function': + vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + else: + vinfo.name = f'local_{self.current_function.name[5:]}_{vinfo.name}_{len(self.localvars)}' local_node = cil.LocalNode(vinfo.name) self.localvars.append(local_node) return vinfo.name @@ -947,14 +950,31 @@ def visit(self, node, scope): param_names = [pn.name for pn in self.current_function.params] if node.lex in param_names: for n in param_names: - if node.lex in n: + if node.lex == n: scope.ret_expr = n break else: for n in [lv.name for lv in self.current_function.localvars]: - if node.lex in n.split("_"): - scope.ret_expr = n - break + n_splited = n.split("_") + lex_splited = node.lex.split('_') + i_lex = 0 + for s in range(len(n_splited)): + if lex_splited[i_lex] == n_splited[s]: + i_lex += 1 + if i_lex == len(lex_splited): + scope.ret_expr = n + break + if not (i_lex + 1 < len(lex_splited) and s + 1 < len(n_splited) and lex_splited[i_lex + 1] == n_splited[s + 1]): + i_lex = 0 + # if node.lex == s: + # scope.ret_expr = n + # break + # if node.lex in n.split("_"): + # scope.ret_expr = n + # break + # if node.lex in n: + # scope.ret_expr = n + # break @visitor.when(cool.StringNode) def visit(self, node, scope): From eb6b936cef2ad40c9e7c29e75219c69a74c60667 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 30 Nov 2020 01:42:24 -0500 Subject: [PATCH 488/520] [cil] - Print a newline after abort message and fix naming issue --- src/core/cmp/cool_to_cil.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3ad58026..3ba6cf66 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -99,6 +99,9 @@ def register_built_in(self): self.register_instruction(cil.PrintStrNode(vname)) self.register_instruction(cil.TypeNameNode(vname, self.vself.name)) self.register_instruction(cil.PrintStrNode(vname)) + data_node = self.register_data('\n') + self.register_instruction(cil.LoadNode(vname, data_node)) + self.register_instruction(cil.PrintStrNode(vname)) self.register_instruction(cil.ExitNode()) # No need for RETURN here right?? @@ -964,17 +967,8 @@ def visit(self, node, scope): if i_lex == len(lex_splited): scope.ret_expr = n break - if not (i_lex + 1 < len(lex_splited) and s + 1 < len(n_splited) and lex_splited[i_lex + 1] == n_splited[s + 1]): + if not (i_lex < len(lex_splited) and s + 1 < len(n_splited) and lex_splited[i_lex] == n_splited[s + 1]): i_lex = 0 - # if node.lex == s: - # scope.ret_expr = n - # break - # if node.lex in n.split("_"): - # scope.ret_expr = n - # break - # if node.lex in n: - # scope.ret_expr = n - # break @visitor.when(cool.StringNode) def visit(self, node, scope): From 74a307ae00a54465daeb2aa8688b968e73356e34 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 09:05:01 -0500 Subject: [PATCH 489/520] [makefile] - Add `install` rule for install the project dependencies --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index 3b14065c..9df82533 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,6 +31,9 @@ coolc: ## Run the code.cl file using coolc @coolc $(CODE) @coolc_spim $(COOLC_ASM) +install: ## Install the project dependencies + pip install ../requeriments.txt + save: ## Save the code.cl as a test @cat code.cl > $(TEST_DIR)$(TEST).cl From d1a3c49fc254e94a4b1ba61cdfb14b3c8d23e619 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 09:47:37 -0500 Subject: [PATCH 490/520] [cmp] - Delete unused files Also delete some duplicated an unsed imports in main.py --- src/core/cmp/ast.py | 62 ----------- src/core/cmp/languages.py | 228 -------------------------------------- src/core/cmp/nbpackage.py | 87 --------------- src/main.py | 10 +- 4 files changed, 3 insertions(+), 384 deletions(-) delete mode 100644 src/core/cmp/ast.py delete mode 100644 src/core/cmp/languages.py delete mode 100644 src/core/cmp/nbpackage.py diff --git a/src/core/cmp/ast.py b/src/core/cmp/ast.py deleted file mode 100644 index 3d5b3b9f..00000000 --- a/src/core/cmp/ast.py +++ /dev/null @@ -1,62 +0,0 @@ -import core.cmp.visitor as visitor - -class Node: - def evaluate(self): - raise NotImplementedError() - -class AtomicNode(Node): - def __init__(self, lex): - self.lex = lex - -class UnaryNode(Node): - def __init__(self, node): - self.node = node - - def evaluate(self): - value = self.node.evaluate() - return self.operate(value) - - @staticmethod - def operate(value): - raise NotImplementedError() - -class BinaryNode(Node): - def __init__(self, left, right): - self.left = left - self.right = right - - def evaluate(self): - lvalue = self.left.evaluate() - rvalue = self.right.evaluate() - return self.operate(lvalue, rvalue) - - @staticmethod - def operate(lvalue, rvalue): - raise NotImplementedError() - -def get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode, ): - - class PrintVisitor(object): - @visitor.on('node') - def visit(self, node, tabs): - pass - - @visitor.when(UnaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__}' - child = self.visit(node.node, tabs + 1) - return f'{ans}\n{child}' - - @visitor.when(BinaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' - left = self.visit(node.left, tabs + 1) - right = self.visit(node.right, tabs + 1) - return f'{ans}\n{left}\n{right}' - - @visitor.when(AtomicNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' - - printer = PrintVisitor() - return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/core/cmp/languages.py b/src/core/cmp/languages.py deleted file mode 100644 index f53cb0ef..00000000 --- a/src/core/cmp/languages.py +++ /dev/null @@ -1,228 +0,0 @@ -from core.cmp.pycompiler import Sentence, Production -from core.cmp.utils import ContainerSet, Token, UnknownToken -from core.cmp.tools.parsing import build_parsing_table, metodo_predictivo_no_recursivo - -class BasicXCool: - def __init__(self, G): - self.G = G - self.fixed_tokens = { lex: Token(lex, G[lex]) for lex in '+ - * / ( )'.split() } - - @property - def firsts(self): - G = self.G - return { - G['+']: ContainerSet(G['+'] , contains_epsilon=False), - G['-']: ContainerSet(G['-'] , contains_epsilon=False), - G['*']: ContainerSet(G['*'] , contains_epsilon=False), - G['/']: ContainerSet(G['/'] , contains_epsilon=False), - G['(']: ContainerSet(G['('] , contains_epsilon=False), - G[')']: ContainerSet(G[')'] , contains_epsilon=False), - G['num']: ContainerSet(G['num'] , contains_epsilon=False), - G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), - G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), - Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), - Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), - G.Epsilon: ContainerSet( contains_epsilon=True), - Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), - Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), - Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), - Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) - } - - @property - def follows(self): - G = self.G - return { - G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['T']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False), - G['F']: ContainerSet(G['-'], G.EOF, G['*'], G['/'], G[')'], G['+'] , contains_epsilon=False), - G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['Y']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False) - } - - @property - def table(self): - G = self.G - return { - ( G['E'], G['num'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['X'], G['+'], ): [ Production(G['X'], Sentence(G['+'], G['T'], G['X'])), ], - ( G['X'], G['-'], ): [ Production(G['X'], Sentence(G['-'], G['T'], G['X'])), ], - ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], - ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], - ( G['T'], G['num'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['Y'], G['*'], ): [ Production(G['Y'], Sentence(G['*'], G['F'], G['Y'])), ], - ( G['Y'], G['/'], ): [ Production(G['Y'], Sentence(G['/'], G['F'], G['Y'])), ], - ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G['-'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G['+'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['F'], G['num'], ): [ Production(G['F'], Sentence(G['num'])), ], - ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['('], G['E'], G[')'])), ] - } - - @property - def tokenizer(self): - G = self.G - fixed_tokens = self.fixed_tokens - - def tokenize_text(text): - tokens = [] - for item in text.split(): - try: - float(item) - token = Token(item, G['num']) - except ValueError: - try: - token = fixed_tokens[item] - except: - token = UnknownToken(item) - tokens.append(token) - eof = Token('$', G.EOF) - tokens.append(eof) - return tokens - - return tokenize_text - -class PowXCool: - def __init__(self, G): - self.G = G - - @property - def firsts(self): - G = self.G - return { - G['+']: ContainerSet(G['+'] , contains_epsilon=False), - G['-']: ContainerSet(G['-'] , contains_epsilon=False), - G['*']: ContainerSet(G['*'] , contains_epsilon=False), - G['/']: ContainerSet(G['/'] , contains_epsilon=False), - G['^']: ContainerSet(G['^'] , contains_epsilon=False), - G['(']: ContainerSet(G['('] , contains_epsilon=False), - G[')']: ContainerSet(G[')'] , contains_epsilon=False), - G['num']: ContainerSet(G['num'] , contains_epsilon=False), - G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['A']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), - G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), - G['Z']: ContainerSet(G['^'] , contains_epsilon=True), - Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), - Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), - G.Epsilon: ContainerSet( contains_epsilon=True), - Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), - Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), - Sentence(G['A'], G['Z']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['^'], G['F']): ContainerSet(G['^'] , contains_epsilon=False), - Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), - Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) - } - - @property - def follows(self): - G = self.G - return { - G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['T']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['F']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['A']: ContainerSet(G['-'], G['*'], G['/'], G['^'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['Y']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['Z']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False) - } - -class Regex: - def __init__(self, G): - self.G = G - - @property - def firsts(self): - G = self.G - return { - G['|']: ContainerSet(G['|'] , contains_epsilon=False), - G['*']: ContainerSet(G['*'] , contains_epsilon=False), - G['(']: ContainerSet(G['('] , contains_epsilon=False), - G[')']: ContainerSet(G[')'] , contains_epsilon=False), - G['symbol']: ContainerSet(G['symbol'] , contains_epsilon=False), - G['ε']: ContainerSet(G['ε'] , contains_epsilon=False), - G['E']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - G['T']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - G['F']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - G['A']: ContainerSet(G['ε'], G['symbol'], G['('] , contains_epsilon=False), - G['X']: ContainerSet(G['|'] , contains_epsilon=True), - G['Y']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=True), - G['Z']: ContainerSet(G['*'] , contains_epsilon=True), - Sentence(G['T'], G['X']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['|'], G['E']): ContainerSet(G['|'] , contains_epsilon=False), - G.Epsilon: ContainerSet( contains_epsilon=True), - Sentence(G['F'], G['Y']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['T']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['A'], G['Z']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['*']): ContainerSet(G['*'] , contains_epsilon=False), - Sentence(G['symbol']): ContainerSet(G['symbol'] , contains_epsilon=False), - Sentence(G['ε']): ContainerSet(G['ε'] , contains_epsilon=False), - Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) - } - - @property - def follows(self): - G = self.G - return { - G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['T']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), - G['F']: ContainerSet(G[')'], G.EOF, G['symbol'], G['|'], G['ε'], G['('] , contains_epsilon=False), - G['A']: ContainerSet(G.EOF, G['|'], G['*'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False), - G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['Y']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), - G['Z']: ContainerSet(G.EOF, G['|'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False) - } - - @property - def table(self): - G = self.G - return { - ( G['E'], G['symbol'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['E'], G['ε'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['X'], G['|'], ): [ Production(G['X'], Sentence(G['|'], G['E'])), ], - ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], - ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], - ( G['T'], G['symbol'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['T'], G['ε'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['Y'], G['symbol'], ): [ Production(G['Y'], Sentence(G['T'])), ], - ( G['Y'], G['ε'], ): [ Production(G['Y'], Sentence(G['T'])), ], - ( G['Y'], G['('], ): [ Production(G['Y'], Sentence(G['T'])), ], - ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G['|'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['F'], G['symbol'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], - ( G['F'], G['ε'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], - ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], - ( G['Z'], G['*'], ): [ Production(G['Z'], Sentence(G['*'])), ], - ( G['Z'], G.EOF, ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['|'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['('], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G[')'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['symbol'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['ε'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['A'], G['symbol'], ): [ Production(G['A'], Sentence(G['symbol'])), ], - ( G['A'], G['ε'], ): [ Production(G['A'], Sentence(G['ε'])), ], - ( G['A'], G['('], ): [ Production(G['A'], Sentence(G['('], G['E'], G[')'])), ] - } - - @property - def parser(self): - firsts = self.firsts - follows = self.follows - M = build_parsing_table(self.G, firsts, follows) - parser = metodo_predictivo_no_recursivo(self.G, M) - return parser \ No newline at end of file diff --git a/src/core/cmp/nbpackage.py b/src/core/cmp/nbpackage.py deleted file mode 100644 index e89c62ad..00000000 --- a/src/core/cmp/nbpackage.py +++ /dev/null @@ -1,87 +0,0 @@ -import io, os, sys, types - -from IPython import get_ipython -from nbformat import read -from IPython.core.interactiveshell import InteractiveShell - -def find_notebook(fullname, path=None): - """find a notebook, given its fully qualified name and an optional path - - This turns "foo.bar" into "foo/bar.ipynb" - and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar - does not exist. - """ - name = fullname.rsplit('.', 1)[-1] - if not path: - path = [''] - for d in path: - nb_path = os.path.join(d, name + ".ipynb") - if os.path.isfile(nb_path): - return nb_path - # let import Notebook_Name find "Notebook Name.ipynb" - nb_path = nb_path.replace("_", " ") - if os.path.isfile(nb_path): - return nb_path - -class NotebookLoader(object): - """Module Loader for Jupyter Notebooks""" - def __init__(self, path=None): - self.shell = InteractiveShell.instance() - self.path = path - - def load_module(self, fullname): - """import a notebook as a module""" - path = find_notebook(fullname, self.path) - - print ("importing Jupyter notebook from %s" % path) - - # load the notebook object - with io.open(path, 'r', encoding='utf-8') as f: - nb = read(f, 4) - - - # create the module and add it to sys.modules - # if name in sys.modules: - # return sys.modules[name] - mod = types.ModuleType(fullname) - mod.__file__ = path - mod.__loader__ = self - mod.__dict__['get_ipython'] = get_ipython - sys.modules[fullname] = mod - - # extra work to ensure that magics that would affect the user_ns - # actually affect the notebook module's ns - save_user_ns = self.shell.user_ns - self.shell.user_ns = mod.__dict__ - - try: - for cell in nb.cells: - if cell.cell_type == 'code': - # transform the input to executable Python - code = self.shell.input_transformer_manager.transform_cell(cell.source) - # run the code in themodule - exec(code, mod.__dict__) - finally: - self.shell.user_ns = save_user_ns - return mod - -class NotebookFinder(object): - """Module finder that locates Jupyter Notebooks""" - def __init__(self): - self.loaders = {} - - def find_module(self, fullname, path=None): - nb_path = find_notebook(fullname, path) - if not nb_path: - return - - key = path - if path: - # lists aren't hashable - key = os.path.sep.join(path) - - if key not in self.loaders: - self.loaders[key] = NotebookLoader(path) - return self.loaders[key] - -sys.meta_path.append(NotebookFinder()) \ No newline at end of file diff --git a/src/main.py b/src/main.py index 67917355..38e48272 100644 --- a/src/main.py +++ b/src/main.py @@ -1,16 +1,12 @@ from sys import exit -from pprint import pprint from core.cmp.visitors import * from core.cmp.lex import CoolLexer -from core.cmp.evaluation import evaluate_reverse_parse -from core.cmp.CoolUtils import tokenize_text, CoolParser -from core.cmp.lex import CoolLexer -from core.cmp.evaluation import * +from core.cmp.mips import PrintVisitor from core.cmp.cil import get_formatter -from pprint import pprint +from core.cmp.CoolUtils import CoolParser from core.cmp.cool_to_cil import COOLToCILVisitor from core.cmp.cil_to_mips import CILToMIPSVisitor -from core.cmp.mips import PrintVisitor +from core.cmp.evaluation import evaluate_reverse_parse def main(args): From 6b57e1f6f853e30d3270d0837e5782cb9722138a Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 09:48:44 -0500 Subject: [PATCH 491/520] [report] - initial report structure --- src/doc/Report.md | 235 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 src/doc/Report.md diff --git a/src/doc/Report.md b/src/doc/Report.md new file mode 100644 index 00000000..f251430a --- /dev/null +++ b/src/doc/Report.md @@ -0,0 +1,235 @@ +# Datos Generales +## Autores +- Miguel Tenorio Potrony +- Mauricio Lázaro Perdomo Cortéz +- Lázaro Raúl Iglesias Vera + +## Sobre el proyecto +Para la implementación de este proyecto se tomaron como base, los proyectos realizados durante 3er año, donde se desarrollaron las fases de chequeo e inferencia de tipos. El código de dichos proyectos conserva su estructura pero estuvo sujeto a cambios y mejoras. + +La mayoría de nuestras implementaciones siguen las ideas y utilizan las herramientas dadas en clase durante 3er año. + +Todas las fases del proceso de compilación y ejecución serán explicadas a continuación. + + +# Pipeline +Como se puede apreciar en [main.py](../main.py) el pipeline de nuestro poceso de compilación es: + +1. Lexer +2. Tokenización +3. Parsing +4. Recolección de tipos +5. Construcción de tipos +6. Chequeo/Inferencia de tipos +7. Verificación de tipos +8. Traducción de Cool a CIL +9. Traducción de CIL a MIPS + +Cada parte del proceso será discutida en detalle durante las siguientes secciones. + +Como se puede apreciar en la etapa #6 del proceso, el chequeo e inferencia de tipos se realizan al unísono, sin embargo cada parte se explicará en secciones separadas y se hará notar por qué se decidió unificarlas. + +## Lexer +**Pendig** + +## Tokenización +**Pendig** + +## Parsing +Para el proceso de parsing se utilizó el parser LR1 y la gramática de Cool que fueron implementados para el proyecto de 3er año sobre chequeo de tipos. + +Fue necesario modificar la salida del Parser para poder devolver la información referente al token de error en caso de que alguna falla fuera detectada. + +Dado que los proyectos llevados a cabo previamente fueron desarrollados para mini-Cool, se hizo necesario modificar la gramática, y se obtuvo como resultado: + +### Gramática de Cool +**Terminales** : class, type, inherits, id, let, in, isvoid, not, new, case, of, esac, if, then, else, fi, while, loop, pool + + +| No terminal | Derivación | +| :-----------: | :------------------------------------------------: | +| program | class_list | +| class_list | class_def class_list | +| class_list | class_def | +| class_def | class type { feature_list } ; | +| class_def | class type inherits type { feature_list } ; | +| feature_list | feature feature_list | +| feature_list | e | +| feature | id : type ; | +| feature | id : type ← expr ; | +| feature | id ( param_list ) : type { expr } ; | +| feature | id ( ) : type { expr } ; | +| param_list | param | +| param_list | param , param_list | +| param | id : type | +| block | expr ; | +| block | expr ; block | +| let_list | id : type | +| let_list | id : type ← expr | +| let_list | id : type , let_list | +| let_list | id : type ← expr , let_list | +| case_list | id : type ⇒ expr ; | +| case_list | id : type ⇒ expr ; case_list | +| func_call | @ type . id ( arg_list ) | +| func_call | @ type . id ( ) | +| func_call | . id ( arg_list ) | +| func_call | . id ( ) | +| arg_list | expr | +| arg_list | expr , arg_list | +| member_call | id ( arg_list ) | +| member_call | id ( ) | +| expr | special | +| expr | cmp_expr | +| special | arith <= special_arith | +| special | arith < special_arith | +| special | arith = special_arith | +| special | special_arith | +| special_arith | arith + special_term | +| special_arith | arith - special_term | +| special_arith | special_term | +| special_term | term * special_unary | +| special_term | term / special_unary | +| special_term | special_unary | +| special_unary | isvoid special_unary | +| special_unary | ~ special_unary | +| special_unary | final_expr | +| final_expr | let let_list in expr | +| final_expr | id ← expr | +| final_expr | not expr | +| cmp_expr | arith <= arith | +| cmp_expr | arith < arith | +| cmp_expr | arith = arith | +| cmp_expr | arith | +| arith | arith + term | +| arith | arith - term | +| arith | term | +| term | term * unary | +| term | term / unary | +| term | unary | +| unary | isvoid unary | +| unary | ~ unary | +| unary | func_expr | +| func_expr | func_expr func_call | +| func_expr | atom | +| atom | member_call | +| atom | new type | +| atom | ( expr ) | +| atom | id | +| atom | integer | +| atom | string | +| atom | bool | +| atom | { block } | +| atom | if expr then expr else expr fi | +| atom | while expr loop expr pool | +| atom | case expr of case_list esac | + + +## Recolección de tipos +Durante la recolección de tipos se visitan todas las declaraciones de clases, se crean los tipos asociados a ellas y se valida la correctitud de las mismas. + +**Errores detectados**: +- Herencia cíclica +- Redefinición de clases +- Nombres de clase no válidos + +## Construcción de tipos +A los tipos creados en la fase anterior se le añaden todos sus atributos y métodos. Además se verifica que se cumplan los requerimientos de un programa válido de Cool q son tener una clase `Main` con su método `main`. + +**Errores detectados**: +- Problemas de nombrado de atributos y métodos +- Redefinición de atributos +- Redefinición incorrecta de métodos +- Uso de tipos no definidos +- No definición de la clase `Main` o su método `main` +- Incorrecta definición del método `main` +- Mal uso de herencia + +## Chequeo de tipos +En esta fase se evalúa la correctitud de todas las expresiones del lenguaje y se decide el tipo estático de cada una de ellas según lo establecido en el manual de [Cool](../../doc/cool-manual.pdf). + +**Errores detectados**: +- Incompatibilidad de tipos +- Uso de tipos no definidos +- Uso de variables, tipos y métodos no definidas +- mal usos de `self` y `SELF_TYPE` +- mal usos del `case` + +## Inferencia de tipos +Para la implementación de esta fase se expandió el comportamiento del visitor encargado del chequeo de tipos, razón por la cual ambos procesos se realizan en la misma fase. + +Para lograr la inferencia de tipos, se realizó un algoritmo de punto fijo en el cual mediante repeticiones sucesivas del proceso de inferencia se irán definiendo los tipos de aquellas variables declaradas como `AUTO_TYPE`. + +### Idea +Una variable en Cool dada su utilización puede definir dos conjuntos + +1. Tipos a los que se conforma (**Ancestros**) +2. Tipos que se conforman a ella (**Descendientes**) + +Dados los dos conjuntos anteriores se puede decidir si una variable `AUTO_TYPE` puede ser inferida correctamente o no. + +Ambos conjuntos recibieron un nombre intuitivo mencionada anteriormente en **negrita** para hacer referencia a su contenido. + +El tipo que se decida otorgar a la variable en cuestión, llamésmole _**T**_, deberá conformarse a todos los tipos que conforman el conjunto #1. Al mismo tiempo todos los tipos del conjunto #2 deberán conformarse a él. + +Dicho lo anterior y dado el hecho de que un tipo *A* se conforma a un tipo *B* solamente si *B* es ancestro de *A*, podemos notar que: + +1. El tipo a seleccionar debe ser un ancestro del **Menor Ancestro Común** (**LCA** por sus siglas en inglés) a todos los nodos del conjunto #2, llamémosle *N*. En otras palabras el primer tipo que es ancestro de todos los tipos en el conjunto #2. +2. Como todos los tipos del conjunto #1 necesitan ser ancestros de _**T**_, todos pertenecerán al camino que se forma desde _**T**_ hasta *Object* en el árbol de tipos, por tanto _**T**_ necesita ser descendiente del primero que aparezca en el camino mencionado y pertenezca al conjunto #1, llamémosle *M*. +3. Tomando el operador **<=** para referirnos a la relación *ser ancestro de de*, se puede afirmar que _**T**_ es de la forma _**N <= T <= M**_, o lo que es lo mismo _**T**_ podría ser cualquier tipo en el camino de *N* a *M*. + +> El nodo que representa el **LCA** siempre existe dado que el árbol de tipos es único, por tanto en caso extremo *Object* siempre será válido como ancestro a todos los tipos. + +El algortimo implementado tras cada recorrido del **AST**(Árbol de sintaxis abstracta) infiere el tipo de todas aquellas variables de las cual se tenga información, seleccionando como tipo inferido siempre el que representa a *N*. + +Al ser este algoritmo una extensión del chequeo de tipos, mientras se van infiriendo los tipos se valida que los mismos no ocasionen error. +> En todo lo anterior se asume que todo tipo es ancestro y descendiente de sí mismo. + +**Errores detectados**: +- Mal usos de `AUTO_TYPE` en casos donde no se cumpla que _**N <= M**_ o todos los tipos en el conjunto #1 no se encuentren en un camino del árbol de tipos +- Todos los errores de chequeo semánticos que existan en el código o se provoquen tras la inferencia de una o varias variables. + +## Verificación de tipos +Esta fase surge dado que tras el proceso de inferencia puede haber ocurrido un error que durante el chequeo semántico no se valida. Dado que permitimos *AUTO_TYPE* en los parametros de las funciones al terminar la inferencia pueden generarse conflictos de mala redefinión de métodos, los cuales son chequeados en la fase de Construcción de los tipos(#5). Por tanto la única funcion de esta fase es verifacar la correctitud de los tipos. + +**Errores detectados**: +- Mala redefinición de métodos ocacionada por la inferencia de tipos + +## Traducción a CIL +pending + +## Traducción a MIPS +pending + +# Ejecución +Para ejectur el proyecto se necesita tener instalado python y el conjunto de dependencias listado en [requirements.txt](../../requirements.txt). + +Para instalar las dependencias puede utilizar: +```bash +make install +``` +Una vez esten instaladas las dependencias puede compilar y ejecutar cualquier achivo de código cool utilizando el comando: +```bash +make main CODE=.cl +``` +>Para usar `make` necesita estar el la dirección `/src` + +# Estructura +Los archivos del proyecto se encuentran modularizados de la siguiente manera: + +1. **cool_cmp** + 1. **cmp** + 2. **lexer** + 3. **visitors** + 1. **type_check** + 2. **cil** + 3. **mips** + +**cmp** tiene todos los archivos y submodulos heredados de las clases de 3er año y proyectos anteriores. + +**lexer** todo lo referente a lexer y tokenización + +**type_checking** fases de la #4 a la #7 + +**cil** traducción a cil + +**mips** traducción a mips \ No newline at end of file From 3956b2a960d4c0373edce5e10356e1a098ab459a Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 10:43:05 -0500 Subject: [PATCH 492/520] [cil] - Sorting the case branches before to ensure the correct branch selection --- src/core/cmp/cool_to_cil.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3ba6cf66..115cfd14 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -523,7 +523,19 @@ def visit(self, node, scope): end_label = self.register_label('end_label') labels = [] old = {} - for idx, b in enumerate(node.branches): + + # sorting the branches + order = [] + for b in node.branches: + count = 0 + t1 = self.context.get_type(b.type) + for other in node.branches: + t2 = self.context.get_type(other.type) + count += t2.conforms_to(t1) + order.append((count, b)) + order.sort(key=lambda x: x[0]) + + for idx, (_, b) in enumerate(order): labels.append(self.register_label(f'{idx}_label')) h = self.buildHierarchy(b.type) if not h: @@ -544,9 +556,9 @@ def visit(self, node, scope): for idx, l in enumerate(labels): self.register_instruction(l) - vid = self.register_local(VariableInfo(node.branches[idx].id, None)) + vid = self.register_local(VariableInfo(order[idx][1].id, None)) self.register_instruction(cil.AssignNode(vid, vexpr)) - self.visit(node.branches[idx], scope) + self.visit(order[idx][1], scope) self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) self.register_instruction(cil.GotoNode(end_label.label)) From 9a75d8a96c873521b6d1a82a2e17d24987ca1289 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 30 Nov 2020 11:31:33 -0500 Subject: [PATCH 493/520] [cil] - Leave return value of case in scope.ret_expr --- src/core/cmp/cool_to_cil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 115cfd14..3b4ecbc8 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -562,6 +562,7 @@ def visit(self, node, scope): self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) self.register_instruction(cil.GotoNode(end_label.label)) + scope.ret_expr = vret self.register_instruction(end_label) @visitor.when(cool.CaseExpressionNode) From 7f7081ff38706d4f8ae074c7012400533a72fcd1 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 12:05:39 -0500 Subject: [PATCH 494/520] [makefile] - Run the correct mips file in rule main --- src/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 3b14065c..9fa0557f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,8 +2,9 @@ .PHONY: clean, info CODE := code.cl -COOLC_ASM := $(shell echo $(CODE) | cut -d '.' -f 1).s -ASM := compiled.asm +FILE_NAME := $(shell echo $(CODE) | cut -d '.' -f 1) +COOLC_ASM := $(FILE_NAME).s +ASM := $(FILE_NAME).mips ORG_NAME := 2kodevs PROJECT_NAME := CoolCompiler APP_VERSION := v0.1 From 88bb05f9684ff8e868485557898bf2f80a0f87a8 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 30 Nov 2020 13:50:59 -0500 Subject: [PATCH 495/520] [cil] - Upgrade visit of `IdNode` --- src/core/cmp/cil.py | 1 + src/core/cmp/cool_to_cil.py | 43 +++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/core/cmp/cil.py b/src/core/cmp/cil.py index 824fe9da..08f027db 100755 --- a/src/core/cmp/cil.py +++ b/src/core/cmp/cil.py @@ -27,6 +27,7 @@ def __init__(self, fname, params, localvars, instructions): self.params = params self.localvars = localvars self.instructions = instructions + self.ids = dict() self.labels_count = 0 class ParamNode(Node): diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3b4ecbc8..3b19ff69 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -23,6 +23,10 @@ def params(self): @property def localvars(self): return self.current_function.localvars + + @property + def ids(self): + return self.current_function.ids @property def instructions(self): @@ -35,14 +39,18 @@ def register_param(self, vinfo): self.params.append(param_node) return vinfo.name - def register_local(self, vinfo): + def register_local(self, vinfo, id=False): + new_vinfo = VariableInfo('', None) if len(self.current_function.name) >= 8 and self.current_function.name[:8] == 'function': - vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + new_vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' else: - vinfo.name = f'local_{self.current_function.name[5:]}_{vinfo.name}_{len(self.localvars)}' - local_node = cil.LocalNode(vinfo.name) + new_vinfo.name = f'local_{self.current_function.name[5:]}_{vinfo.name}_{len(self.localvars)}' + + local_node = cil.LocalNode(new_vinfo.name) + if id: + self.ids[vinfo.name] = new_vinfo.name self.localvars.append(local_node) - return vinfo.name + return new_vinfo.name def define_internal_local(self): vinfo = VariableInfo('internal', None) @@ -556,7 +564,7 @@ def visit(self, node, scope): for idx, l in enumerate(labels): self.register_instruction(l) - vid = self.register_local(VariableInfo(order[idx][1].id, None)) + vid = self.register_local(VariableInfo(order[idx][1].id, None), id=True) self.register_instruction(cil.AssignNode(vid, vexpr)) self.visit(order[idx][1], scope) self.register_instruction(cil.AssignNode(vret, scope.ret_expr)) @@ -581,7 +589,7 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - vname = self.register_local(VariableInfo(node.id, node.type)) + vname = self.register_local(VariableInfo(node.id, node.type), id=True) if node.expr: self.visit(node.expr, scope) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) @@ -860,11 +868,11 @@ def visit(self, node, scope): args = [] for arg in node.args: - vname = self.register_local(VariableInfo(f'{node.id}_arg', None)) + vname = self.register_local(VariableInfo(f'{node.id}_arg', None), id=True) self.visit(arg, scope) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) args.append(cil.ArgNode(vname)) - result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None)) + result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None), id=True) vobj = self.define_internal_local() self.visit(node.obj, scope) @@ -888,7 +896,7 @@ def visit(self, node, scope): self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, node.type), result)) else: #Call of type .(,...,) - type_of_node = self.register_local(VariableInfo(f'{node.id}_type', None)) + type_of_node = self.register_local(VariableInfo(f'{node.id}_type', None), id=True) self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) computed_type = node.obj.computed_type if computed_type.name == 'SELF_TYPE': @@ -959,7 +967,7 @@ def visit(self, node, scope): ############################### try: self.current_type.get_attribute(node.lex) - attr = self.register_local(VariableInfo(node.lex, None)) + attr = self.register_local(VariableInfo(node.lex, None), id=True) self.register_instruction(cil.GetAttribNode(attr, self.vself.name, node.lex, self.current_type.name)) scope.ret_expr = attr except AttributeError: @@ -970,18 +978,7 @@ def visit(self, node, scope): scope.ret_expr = n break else: - for n in [lv.name for lv in self.current_function.localvars]: - n_splited = n.split("_") - lex_splited = node.lex.split('_') - i_lex = 0 - for s in range(len(n_splited)): - if lex_splited[i_lex] == n_splited[s]: - i_lex += 1 - if i_lex == len(lex_splited): - scope.ret_expr = n - break - if not (i_lex < len(lex_splited) and s + 1 < len(n_splited) and lex_splited[i_lex] == n_splited[s + 1]): - i_lex = 0 + scope.ret_expr = self.ids[node.lex] @visitor.when(cool.StringNode) def visit(self, node, scope): From 8f09e814b126f6640c8f6befc5625c53d12220fa Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 30 Nov 2020 14:26:28 -0500 Subject: [PATCH 496/520] [cil] - Use VCALL instead of CALL in member calls --- src/core/cmp/cool_to_cil.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/cmp/cool_to_cil.py b/src/core/cmp/cool_to_cil.py index 3b19ff69..fb4ec58c 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/cmp/cool_to_cil.py @@ -911,21 +911,24 @@ def visit(self, node, scope): # node.id -> str # node.args -> [ ExpressionNode ... ] ###################################### - method = [self.to_function_name(method.name, xtype.name) for method, xtype in self.current_type.all_methods() if method.name == node.id][0] + #method = [self.to_function_name(method.name, xtype.name) for method, xtype in self.current_type.all_methods() if method.name == node.id][0] args = [] for arg in node.args: - vname = self.register_local(VariableInfo(f'{node.id}_arg', None)) + vname = self.register_local(VariableInfo(f'{node.id}_arg', None), id=True) self.visit(arg, scope) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) args.append(cil.ArgNode(vname)) - result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None)) + result = self.register_local(VariableInfo(f'return_value_of_{node.id}', None), id=True) self.register_instruction(cil.ArgNode(self.vself.name)) for arg in args: self.register_instruction(arg) - self.register_instruction(cil.StaticCallNode(method, result)) + type_of_node = self.register_local(VariableInfo(f'{self.vself.name}_type', None)) + self.register_instruction(cil.TypeOfNode(self.vself.name, type_of_node)) + self.register_instruction(cil.DynamicCallNode(type_of_node, node.id, result, self.current_type.name)) + #self.register_instruction(cil.StaticCallNode(method, result)) scope.ret_expr = result @visitor.when(cool.NewNode) From 9f262b5210f9cbd124742a15ee5b146b7404db37 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 19:16:35 -0500 Subject: [PATCH 497/520] [structure][check] - New type_check module --- src/core/visitors/type_check/__init__.py | 6 + src/core/visitors/type_check/ast_printer.py | 122 ++++++ src/core/visitors/type_check/builder.py | 119 ++++++ src/core/visitors/type_check/checker.py | 409 ++++++++++++++++++++ src/core/visitors/type_check/collector.py | 93 +++++ src/core/visitors/type_check/inferencer.py | 278 +++++++++++++ src/core/visitors/type_check/utils.py | 65 ++++ src/core/visitors/type_check/verifier.py | 44 +++ 8 files changed, 1136 insertions(+) create mode 100644 src/core/visitors/type_check/__init__.py create mode 100644 src/core/visitors/type_check/ast_printer.py create mode 100644 src/core/visitors/type_check/builder.py create mode 100644 src/core/visitors/type_check/checker.py create mode 100644 src/core/visitors/type_check/collector.py create mode 100644 src/core/visitors/type_check/inferencer.py create mode 100644 src/core/visitors/type_check/utils.py create mode 100644 src/core/visitors/type_check/verifier.py diff --git a/src/core/visitors/type_check/__init__.py b/src/core/visitors/type_check/__init__.py new file mode 100644 index 00000000..2dea8414 --- /dev/null +++ b/src/core/visitors/type_check/__init__.py @@ -0,0 +1,6 @@ +from .checker import TypeChecker +from .builder import TypeBuilder +from .verifier import TypeVerifier +from .collector import TypeCollector +from .inferencer import InferenceVisitor +from .ast_printer import FormatVisitor diff --git a/src/core/visitors/type_check/ast_printer.py b/src/core/visitors/type_check/ast_printer.py new file mode 100644 index 00000000..ccba3697 --- /dev/null +++ b/src/core/visitors/type_check/ast_printer.py @@ -0,0 +1,122 @@ +from ...visitors import visitor +from ...cmp import CoolUtils as cool + +#AST Printer +class FormatVisitor: + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' + statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) + return f'{ans}\n{statements}' + + @visitor.when(cool.ClassDeclarationNode) + def visit(self, node, tabs=0): + parent = '' if node.parent is None else f"inherits {node.parent}" + ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' + features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) + return f'{ans}\n{features}' + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node, tabs=0): + sons = [node.expr] if node.expr else [] + text = '<- ' if node.expr else '' + ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.id} : {node.type} {text}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' if body else f'{ans}' + + @visitor.when(cool.FuncDeclarationNode) + def visit(self, node, tabs=0): + params = ', '.join(' : '.join(param) for param in node.params) + ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {node.type} {{}}' + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{body}' + + @visitor.when(cool.IfThenElseNode) + def visit(self, node, tabs=0): + sons = [node.condition, node.if_body, node.else_body] + ans = '\t' * tabs + f'\\__IfThenElseNode: if then else fi' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(cool.WhileLoopNode) + def visit(self, node, tabs=0): + sons = [node.condition, node.body] + ans = '\t' * tabs + f'\\__WhileLoopNode: while loop pool' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(cool.BlockNode) + def visit(self, node, tabs=0): + sons = node.exprs + ans = '\t' * tabs + f'\\__BlockNode: {{ ... }}' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(cool.LetInNode) + def visit(self, node, tabs=0): + sons = node.let_body + [node.in_body] + ans = '\t' * tabs + f'\\__LetInNode: let {{ ... }} in ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(cool.CaseOfNode) + def visit(self, node, tabs=0): + sons = [node.expr] + node.branches + ans = '\t' * tabs + f'\\__CaseOfNode: case of {{ ... }} esac' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(cool.CaseExpressionNode) + def visit(self, node, tabs=0): + sons = [node.expr] + ans = '\t' * tabs + f'\\__CaseExpressionNode: {node.id} : {node.type} => ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(cool.AssignNode) + def visit(self, node, tabs=0): + sons = [node.expr] + ans = '\t' * tabs + f'\\__AssignNode: {node.id} = ' + body = '\n'.join(self.visit(child, tabs + 1) for child in sons) + return f'{ans}\n{body}' + + @visitor.when(cool.UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.symbol.lex} ' + right = self.visit(node.expr, tabs + 1) + return f'{ans}\n{right}' + + @visitor.when(cool.BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.symbol.lex} ' + left = self.visit(node.left, tabs + 1) + right = self.visit(node.right, tabs + 1) + return f'{ans}\n{left}\n{right}' + + @visitor.when(cool.AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' + + @visitor.when(cool.FunctionCallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + ans = '\t' * tabs + f'\\__FunctionCallNode: .{node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + ans = f'{ans}\n{obj}' + if args: ans += f'\n{args}' + return ans + + @visitor.when(cool.MemberCallNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__MemberCallNode: {node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + if args: ans += f'\n{args}' + return ans + + @visitor.when(cool.NewNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__NewNode: new {node.type}()' diff --git a/src/core/visitors/type_check/builder.py b/src/core/visitors/type_check/builder.py new file mode 100644 index 00000000..81416b26 --- /dev/null +++ b/src/core/visitors/type_check/builder.py @@ -0,0 +1,119 @@ +from .utils import * +from ...visitors import visitor +from ...cmp import CoolUtils as cool, SemanticError, empty_token, ErrorType + +# Type Builder +class TypeBuilder: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + self.methods = {} + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node): + main_token = None + for def_class in node.declarations: + self.visit(def_class) + if def_class.id == 'Main': + main_token = def_class.tid + + try: + main = self.context.get_type('Main') + method = main.methods['main'] + tmethod = self.methods['Main']['main'] + if method.param_names: + self.errors.append((SemanticError('Method "main" must takes no formal parameters'), tmethod)) + except TypeError: + self.errors.append((SemanticError('No definition for class "Main"'), empty_token)) + except KeyError: + self.errors.append((SemanticError('Class "Main" must have a method "main"'), main_token)) + + @visitor.when(cool.ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + + if node.parent: + if node.parent in sealed: + self.errors.append((SemanticError(f'Is not possible to inherits from "{node.parent}"'), node.tparent)) + node.parent = 'Object' + try: + parent_type = self.context.get_type(node.parent) + self.current_type.set_parent(parent_type) + except TypeError as ex: + self.errors.append((ex, node.tparent)) + + for feature in node.features: + self.visit(feature) + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node): + try: + attr_type = self.context.get_type(node.type) + except TypeError as ex: + self.errors.append((ex, node.ttype)) + attr_type = ErrorType() + node.attr_type = attr_type + + try: + if node.id == 'self': + raise SemanticError(SELF_IS_READONLY) + attr = self.current_type.define_attribute(node.id, attr_type) + attr.node = node + node.attr = attr + except SemanticError as ex: + self.errors.append((ex, node.tid)) + + @visitor.when(cool.FuncDeclarationNode) + def visit(self, node): + arg_names, arg_types, arg_nodes = [], [], [] + for i, arg in enumerate(node.params): + idx, typex = arg + try: + assert typex != ST + arg_type = self.context.get_type(typex) + except TypeError as ex: + self.errors.append((ex, node.params[i].ttype)) + arg_type = ErrorType() + except AssertionError: + self.errors.append((SemanticError(INVALID_PARAMETER % (idx)), node.params[i].ttype)) + arg_type = ErrorType() + + if idx == 'self': + self.errors.append((SemanticError('"self" cannot be the name of a formal parameter'), node.params[i].ttype)) + if idx in arg_names: + self.errors.append((SemanticError(f'Formal parameter {idx} redefined'), node.params[i].ttype)) + arg_names.append(idx) + arg_types.append(arg_type) + arg_nodes.append(arg) + arg.idx = i + arg.method_types = arg_types + + try: + ret_type = self.context.get_type(node.type) + except TypeError as ex: + self.errors.append((ex, node.ttype)) + ret_type = ErrorType() + node.ret_type = ret_type + node.arg_types = arg_types + node.arg_names = arg_names + node.arg_nodes = arg_nodes + + try: + if node.id == 'self': + raise SemanticError('"self" is an invalid method name') + method = self.current_type.define_method(node.id, arg_names, arg_types, ret_type) + method.nodes = arg_nodes + method.ret_node = node + node.method = method + for arg in node.params: + arg.method = method + if not self.current_type.name in self.methods: + self.methods[self.current_type.name] = {} + self.methods[self.current_type.name][node.id] = node.tid + except SemanticError as ex: + self.errors.append((ex, node.tid)) diff --git a/src/core/visitors/type_check/checker.py b/src/core/visitors/type_check/checker.py new file mode 100644 index 00000000..229472bb --- /dev/null +++ b/src/core/visitors/type_check/checker.py @@ -0,0 +1,409 @@ +from .utils import * +from ...visitors import visitor +from ...cmp import CoolUtils as cool +from ...cmp import IntType, BoolType, StringType, SemanticError, ErrorType, SelfType, Scope + +# Type Checker +class TypeChecker: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.current_method = None + self.errors = errors + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node, scope=None): + scope = Scope() + for declaration in node.declarations: + self.visit(declaration, scope.create_child()) + return scope + + @visitor.when(cool.ClassDeclarationNode) + def visit(self, node, scope): + self.current_type = self.context.get_type(node.id) + + scope.define_variable('self', SelfType(self.current_type)) + cur_type = self.current_type + while True: + for attr in cur_type.attributes: + vtype = attr.type + if vtype.name == ST: + vtype = SelfType(self.current_type) + var = scope.define_variable(attr.name, vtype) + var.node = attr.node + if not cur_type.parent: + break + cur_type = cur_type.parent + + cur_type = self.current_type + pending, count = [], 0 + for feature in node.features: + if isinstance(feature, cool.AttrDeclarationNode): + self.visit(feature, scope) + if not scope.is_defined(feature.id): + vtype = cur_type.attributes[count].type + if vtype.name == ST: + vtype = SelfType(self.current_type) + var = scope.define_variable(feature.id, vtype) + var.node = cur_type.attributes[count].node + count += 1 + else: + pending.append(feature) + + for feature in pending: + self.visit(feature, scope.create_child()) + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node, scope): + if not node.expr: + return + + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + real_type = node.attr_type + node.info = [expr_type, real_type] + + if not expr_type.conforms_to(real_type): + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (expr_type.name, real_type.name)), node.arrow)) + + @visitor.when(cool.FuncDeclarationNode) + def visit(self, node, scope): + self.current_method = node.id + + for pname, ptype, pnode in zip(node.arg_names, node.arg_types, node.arg_nodes): + var = scope.define_variable(pname, ptype) + var.node = pnode + + self.visit(node.body, scope) + + body_type = node.body.computed_type + method_rtn_type = node.ret_type + node.info = [body_type, method_rtn_type] + + if not body_type.conforms_to(method_rtn_type): + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (body_type.name, method_rtn_type.name)), node.ttype)) + + @visitor.when(cool.AssignNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + node_type = node.expr.computed_type + var_type = None + + try: + if not scope.is_defined(node.id): + scope.define_variable(node.id, ErrorType()) + raise NameError(VARIABLE_NOT_DEFINED % (node.id)) + var = scope.find_variable(node.id) + var_type = var.type + if var.name == 'self': + raise SemanticError(SELF_IS_READONLY) + if not node_type.conforms_to(var.type): + raise TypeError(INCOMPATIBLE_TYPES % (node_type.name, var.type.name)) + except Exception as ex: + self.errors.append((ex, node.tid)) + node_type = ErrorType() + + node.info = [node_type, var_type] + node.computed_type = node_type + + @visitor.when(cool.CaseOfNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + + types_list = [] + branches = set() + for case in node.branches: + if case.type in branches: + # //TODO: Check this again after the Inference process + self.errors.append((SemanticError(DUPLICATED_BRANCH % (case.type)), case.ttype)) + branches.add(case.type) + self.visit(case, scope.create_child()) + types_list.append(case.computed_type) + + node.computed_type = LCA(types_list) + + @visitor.when(cool.CaseExpressionNode) + def visit(self, node, scope): + node.scope = scope + try: + assert node.type != ST + branch_type = self.context.get_type(node.type) + except TypeError as ex: + self.errors.append((ex, node.ttype)) + branch_type = ErrorType() + except AssertionError: + self.errors.append((SemanticError(INVALID_BRANCH % node.id), node.ttype)) + branch_type = ErrorType() + node.branch_type = branch_type + + if node.id == 'self': + self.errors.append((SemanticError(SELF_IS_READONLY), node.id)) + else: + var = scope.define_variable(node.id, branch_type) + var.node = node + self.visit(node.expr, scope) + node.computed_type = node.expr.computed_type + + @visitor.when(cool.LetInNode) + def visit(self, node, scope): + node.scope = scope + + for expr in node.let_body: + node.scope = node.scope.create_child() + self.visit(expr, node.scope) + + self.visit(node.in_body, node.scope) + node.computed_type = node.in_body.computed_type + + @visitor.when(cool.LetAttributeNode) + def visit(self, node, scope): + try: + node_type = self.context.get_type(node.type) + if node_type.name == ST: + node_type = SelfType(self.current_type) + except TypeError as ex: + self.errors.append((ex, node.ttype)) + node_type = ErrorType() + node.attr_type = node_type + node.scope = None + + if node.expr: + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + node.info = [expr_type, node_type] + + if not expr_type.conforms_to(node_type): + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (expr_type.name, node_type.name)), node.arrow)) + if node.id == 'self': + self.errors.append((SemanticError(SELF_IS_READONLY), node.tid)) + else: + var = scope.define_variable(node.id, node_type) + var.node = node + + @visitor.when(cool.IfThenElseNode) + def visit(self, node, scope): + self.visit(node.condition, scope) + node.cond_type = node.condition.computed_type + + BOOL = self.context.get_type('Bool') + if not node.cond_type.conforms_to(BOOL): + self.errors.append((TypeError(CONDITION_NOT_BOOL % ('If', node.cond_type.name)), node.token)) + + self.visit(node.if_body, scope) + if_type = node.if_body.computed_type + + self.visit(node.else_body, scope) + else_type = node.else_body.computed_type + node.computed_type = LCA([if_type, else_type]) + + @visitor.when(cool.BlockNode) + def visit(self, node, scope): + for expr in node.exprs: + self.visit(expr, scope) + + last_expr = node.exprs[-1] + node.computed_type = last_expr.computed_type + + @visitor.when(cool.WhileLoopNode) + def visit(self, node, scope): + self.visit(node.condition, scope) + node.cond_type = node.condition.computed_type + + BOOL = self.context.get_type('Bool') + if not node.cond_type.conforms_to(BOOL): + self.errors.append((TypeError(CONDITION_NOT_BOOL % ('While', node.cond_type.name)), node.token)) + + self.visit(node.body, scope) + node.computed_type = self.context.get_type('Object') + + @visitor.when(cool.FunctionCallNode) + def visit(self, node, scope): + self.visit(node.obj, scope) + obj_type = node.obj.computed_type + + error = False + + arg_types, real_types = [], [] + for arg in node.args: + self.visit(arg, scope) + arg_types.append(arg.computed_type) + + try: + if node.type: + token = node.ttype + cast_type = self.context.get_type(node.type) + if cast_type.name == ST: + raise SemanticError("Invalid use of SELF_TYPE") + if cast_type.name == AT: + raise SemanticError('Is not possible to use AUTO_TYPE in a cast') + if not obj_type.conforms_to(cast_type): + raise TypeError(INCOMPATIBLE_TYPES % (obj_type.name, node.type)) + obj_type = cast_type + + assert obj_type + token = node.tid + obj_method = obj_type.get_method(node.id) + node.obj_method = obj_method + if len(node.args) == len(obj_method.param_types): + for idx, (arg, param_type) in enumerate(zip(arg_types, obj_method.param_types)): + real_types.append(param_type) + + if not arg.conforms_to(param_type): + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}")), token)) + error = True + else: + raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + assert not error + node_type = obj_method.return_type + if node_type.name == ST: + node_type = obj_type + except AssertionError: + node_type = ErrorType() + except Exception as ex: + self.errors.append((ex, token)) + node_type = ErrorType() + + node.info = [arg_types, real_types] + node.computed_type = node_type + + @visitor.when(cool.MemberCallNode) + def visit(self, node, scope): + obj_type = SelfType(self.current_type) + + error = False + + arg_types, real_types = [], [] + for arg in node.args: + self.visit(arg, scope) + arg_types.append(arg.computed_type) + + try: + token = node.tid + obj_method = obj_type.get_method(node.id) + node.obj_method = obj_method + if len(node.args) == len(obj_method.param_types): + for arg, param_type in zip(arg_types, obj_method.param_types): + real_types.append(param_type) + + if not arg.conforms_to(param_type): + self.errors.append((TypeError(INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}")), token)) + error = True + else: + raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') + assert not error + node_type = obj_method.return_type + if node_type.name == ST: + node_type = obj_type + except AssertionError: + node_type = ErrorType() + except Exception as ex: + self.errors.append((ex, token)) + node_type = ErrorType() + + node.info = [arg_types, real_types] + node.computed_type = node_type + + @visitor.when(cool.BinaryNode) + def visit(self, node, scope): + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + node.info = [left_type, right_type] + + INT = self.context.get_type('Int') + BOOL = self.context.get_type('Bool') + if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): + self.errors.append((TypeError(INVALID_OPERATION % (left_type.name, right_type.name)), node.symbol)) + + node.computed_type = [BOOL, INT][isinstance(node, cool.ArithmeticNode)] + + @visitor.when(cool.IntegerNode) + def visit(self, node, scope): + node.computed_type = self.context.get_type('Int') + + @visitor.when(cool.StringNode) + def visit(self, node, scope): + node.computed_type = self.context.get_type('String') + + @visitor.when(cool.BoolNode) + def visit(self, node, scope): + node.computed_type = self.context.get_type('Bool') + + @visitor.when(cool.IdNode) + def visit(self, node, scope): + if scope.is_defined(node.lex): + node_type = scope.find_variable(node.lex).type + else: + scope.define_variable(node.lex, ErrorType()) + self.errors.append((NameError(VARIABLE_NOT_DEFINED % (node.lex)), node.token)) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(cool.NewNode) + def visit(self, node, scope): + try: + node_type = self.context.get_type(node.type) + if node.type == ST: + node_type = SelfType(self.current_type) + except TypeError as ex: + self.errors.append((ex, node.ttype)) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(cool.IsVoidNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + node.computed_type = self.context.get_type('Bool') + + @visitor.when(cool.ComplementNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + node.expr_type = expr_type + + INT = self.context.get_type('Int') + if not expr_type.conforms_to(INT): + self.errors.append((TypeError("Complement works only for Int"), node.symbol)) + node.computed_type = INT + + @visitor.when(cool.NotNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + node.expr_type = expr_type + + BOOL = self.context.get_type('Bool') + if not expr_type.conforms_to(BOOL): + self.errors.append((TypeError("Not operator works only for Bool"), node.symbol)) + node.computed_type = BOOL + + @visitor.when(cool.EqualNode) + def visit(self, node, scope): + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + node.info = [left_type, right_type] + + valid_types = [IntType(), BoolType(), StringType()] + try: + cur_types = [right_type, left_type] + for op_type in valid_types: + try: + cur_types.remove(op_type) + assert cur_types[0].conforms_to(op_type) + break + except ValueError: pass + except AssertionError: + self.errors.append((TypeError(INVALID_OPERATION % (left_type.name, right_type.name)), node.symbol)) + + node.computed_type = self.context.get_type('Bool') diff --git a/src/core/visitors/type_check/collector.py b/src/core/visitors/type_check/collector.py new file mode 100644 index 00000000..4075a93a --- /dev/null +++ b/src/core/visitors/type_check/collector.py @@ -0,0 +1,93 @@ +from .utils import * +from ...visitors import visitor +from ...cmp import CoolUtils as cool, empty_token, Context, SemanticError +from ...cmp import IntType, StringType, BoolType, IOType, VoidType, AutoType, SelfType + +def define_built_in_types(context): + obj = context.create_type('Object') + i = context.append_type(IntType()) + i.set_parent(obj) + s = context.append_type(StringType()) + s.set_parent(obj) + b = context.append_type(BoolType()) + b.set_parent(obj) + io = context.append_type(IOType()) + io.set_parent(obj) + st = context.append_type(SelfType()) + context.append_type(AutoType()) + + obj.define_method('abort', [], [], obj) + obj.define_method('type_name', [], [], s) + obj.define_method('copy', [], [], st) + + io.define_method('out_string', ['x'], [s], st) + io.define_method('out_int', ['x'], [i], st) + io.define_method('in_string', [], [], s) + io.define_method('in_int', [], [], i) + + s.define_method('length', [], [], i) + s.define_method('concat', ['s'], [s], s) + s.define_method('substr', ['i', 'l'], [i, i], s) + +# Type Collector +class TypeCollector: + def __init__(self, errors=[]): + self.context = None + self.errors = errors + self.type_level = {} + self.parent = {} + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node): + self.context = Context() + define_built_in_types(self.context) + + for def_class in node.declarations: + self.visit(def_class) + + # comparison for sort node.declarations + def get_type_level(typex, error_token=empty_token): + try: + parent = self.type_level[typex] + except KeyError: + return 0 + + if parent == 0: + node = self.parent[typex] + node.parent = "Object" + self.errors.append((SemanticError('Cyclic heritage.'), error_token)) + elif type(parent) is not int: + self.type_level[typex] = 0 if parent else 1 + if type(parent) is str: + self.type_level[typex] = get_type_level(parent, self.parent[typex].tid) + 1 + + return self.type_level[typex] + + node.declarations.sort(key = lambda node: get_type_level(node.id)) + + @visitor.when(cool.ClassDeclarationNode) + def visit(self, node): + def new_type(): + self.context.create_type(node.id) + self.type_level[node.id] = node.parent + self.parent[node.id] = node + + def make_a_duplicate(): + while True: + node.id = '1' + node.id + try: new_type() + except SemanticError: pass + else: break + + if node.id not in built_in_types: + try: new_type() + except SemanticError as ex: + self.errors.append((ex, node.tid)) + make_a_duplicate() + else: + self.errors.append((SemanticError(f'{node.id} is an invalid class name'), node.tid)) + make_a_duplicate() diff --git a/src/core/visitors/type_check/inferencer.py b/src/core/visitors/type_check/inferencer.py new file mode 100644 index 00000000..caec0f3b --- /dev/null +++ b/src/core/visitors/type_check/inferencer.py @@ -0,0 +1,278 @@ +from .utils import * +from ...visitors import visitor +from .checker import TypeChecker +from ...cmp import CoolUtils as cool +from ...cmp.utils import InferenceSets +from ...cmp import SemanticError, AutoType, SelfType + +# Type Inference Visitor +class InferenceVisitor(TypeChecker): + def __init__(self, context, errors=[]): + super().__init__(context, errors) + self.variable = {} + + def inference(self, node, ntype, conforms=True): + try: + self.variable[node].add(ntype, conforms) + except KeyError: + self.variable[node] = InferenceSets().add(ntype, conforms) + + @visitor.on('node') + def context_update(self, node, ntype): + pass + + @visitor.when(cool.Node) + def context_update(self, node, ntype): + pass + + @visitor.when(cool.Param) + def context_update(self, node, ntype): + node.method_types[node.idx] = ntype + try: node.method.param_types[node.idx] = ntype + except AttributeError: pass + + @visitor.when(cool.AttrDeclarationNode) + def context_update(self, node, ntype): + try: node.attr_type = ntype + except AttributeError: pass + try: node.branch_type = ntype + except AttributeError: pass + try: node.attr.type = ntype + except AttributeError: pass + + @visitor.when(cool.FuncDeclarationNode) + def context_update(self, node, ntype): + node.ret_type = ntype + try: node.method.return_type = ntype + except AttributeError: pass + + @visitor.on('node') + def update(self, node, scope, ntype): + pass + + @visitor.when(cool.AssignNode) + def update(self, node, scope, ntype): + self.update(node.expr, scope, ntype) + + @visitor.when(cool.CaseOfNode) + def update(self, node, scope, ntype): + for branch in node.branches: + if isinstance(branch.computed_type, AutoType): + self.update(branch, scope, ntype) + + @visitor.when(cool.CaseExpressionNode) + def update(self, node, scope, ntype): + self.update(node.expr, node.scope, ntype) + + @visitor.when(cool.LetInNode) + def update(self, node, scope, ntype): + self.update(node.in_body, node.scope, ntype) + + @visitor.when(cool.IfThenElseNode) + def update(self, node, scope, ntype): + if isinstance(node.if_body.computed_type, AutoType): + self.update(node.if_body, scope, ntype) + if isinstance(node.else_body.computed_type, AutoType): + self.update(node.else_body, scope, ntype) + + @visitor.when(cool.BlockNode) + def update(self, node, scope, ntype): + self.update(node.exprs[-1], scope, ntype) + + @visitor.when(cool.FunctionCallNode) + def update(self, node, scope, ntype): + self.inference(node.obj_method.ret_node, ntype) + + @visitor.when(cool.MemberCallNode) + def update(self, node, scope, ntype): + self.inference(node.obj_method.ret_node, ntype) + + @visitor.when(cool.IdNode) + def update(self, node, scope, ntype): + self.inference(scope.find_variable(node.lex).node, ntype) + + # Visit + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(cool.Node) + def visit(self, node, scope): + if not issubclass(node.__class__, cool.BinaryNode): + super().visit(node, scope) + + @visitor.when(cool.ProgramNode) + def visit(self, node, scope=None): + scope = super().visit(node, scope) + + infered = 0 + pending = [] + OBJ = self.context.get_type('Object') + for (auto, sets) in self.variable.items(): + try: + if (len(sets.D) + len(sets.S) == 1): + pending.append(auto) + continue + ok, D1 = check_path(sets.D, OBJ) + assert ok + if len(sets.S) and not isinstance(D1, SelfType): + candidate = LCA(sets.S) + assert LCA([candidate, D1]) == D1 + D1 = candidate + auto.type = D1.name + self.context_update(auto, D1) + infered += 1 + except AssertionError: + self.errors.append((SemanticError(f'Bad use of AUTO_TYPE detected'), auto.ttype)) + if not infered: + for auto in pending: + auto.type = OBJ.name + self.context_update(auto, OBJ) + self.variable.clear() + return infered, scope + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.attr_type, AutoType): + self.inference(node, self.context.get_type('Object')) + + if not node.expr: + return + + expr, rtype = node.info + if update_condition(rtype, expr): + self.inference(node, expr, False) + if update_condition(expr, rtype): + self.update(node.expr, scope, rtype) + + @visitor.when(cool.FuncDeclarationNode) + def visit(self, node, scope): + super().visit(node, scope) + + body, rtn = node.info + OBJ = self.context.get_type('Object') + if isinstance(rtn, AutoType): + self.inference(node, OBJ) + for ptype, pnode in zip(node.arg_types, node.arg_nodes): + if isinstance(ptype, AutoType): + self.inference(pnode, OBJ) + if update_condition(rtn, body): + self.inference(node, body, False) + if update_condition(body, rtn): + self.update(node.body, scope, rtn) + + @visitor.when(cool.AssignNode) + def visit(self, node, scope): + super().visit(node, scope) + + node_type, var = node.info + if update_condition(var, node_type): + self.inference(scope.find_variable(node.id).node, node_type, False) + if update_condition(node_type, var): + self.update(node.expr, scope, var) + + @visitor.when(cool.CaseExpressionNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.branch_type, AutoType): + self.inference(node, self.context.get_type('Object')) + + @visitor.when(cool.LetAttributeNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.attr_type, AutoType): + self.inference(node, self.context.get_type('Object')) + + if not node.expr: + return + + expr, rtype = node.info + if update_condition(rtype, expr): + self.inference(scope.find_variable(node.id).node, expr, False) + if update_condition(expr, rtype): + self.update(node.expr, scope, rtype) + + @visitor.when(cool.IfThenElseNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.cond_type, AutoType): + self.update(node.condition, scope, OBJ = self.context.get_type('Bool')) + + @visitor.when(cool.WhileLoopNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.cond_type, AutoType): + self.update(node.condition, scope, self.context.get_type('Bool')) + + @visitor.when(cool.FunctionCallNode) + def visit(self, node, scope): + super().visit(node, scope) + + args, real = node.info + if not real: + return + + for idx, (atype, rtype) in enumerate(zip(args, real)): + if update_condition(rtype, atype): + self.inference(node.obj_method.nodes[idx], atype, False) + if update_condition(atype, rtype): + self.update(node.args[idx], scope, rtype) + + @visitor.when(cool.MemberCallNode) + def visit(self, node, scope): + super().visit(node, scope) + + args, real = node.info + if not real: + return + + for idx, (atype, rtype) in enumerate(zip(args, real)): + if update_condition(rtype, atype): + self.inference(node.obj_method.nodes[idx], atype, False) + if update_condition(atype, rtype): + self.update(node.args[idx], scope, rtype) + + @visitor.when(cool.BinaryNode) + def visit(self, node, scope): + super().visit(node, scope) + + left, right = node.info + INT = self.context.get_type('Int') + if isinstance(left, AutoType): + self.update(node.left, scope, INT) + if isinstance(right, AutoType): + self.update(node.right, scope, INT) + + @visitor.when(cool.ComplementNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.expr_type, AutoType): + self.update(node.expr, scope, self.context.get_type('Int')) + + @visitor.when(cool.NotNode) + def visit(self, node, scope): + super().visit(node, scope) + + if isinstance(node.expr_type, AutoType): + self.update(node.expr, scope, self.context.get_type('Bool')) + + @visitor.when(cool.EqualNode) + def visit(self, node, scope): + super().visit(node, scope) + + left, right = node.info + INT = self.context.get_type('Int') + BOOL = self.context.get_type('Bool') + STRING = self.context.get_type('String') + if update_condition(left, right) and right in [INT, BOOL, STRING]: + self.update(node.left, scope, right) + if update_condition(right, left) and left in [INT, BOOL, STRING]: + self.update(node.right, scope, left) + \ No newline at end of file diff --git a/src/core/visitors/type_check/utils.py b/src/core/visitors/type_check/utils.py new file mode 100644 index 00000000..ed13b906 --- /dev/null +++ b/src/core/visitors/type_check/utils.py @@ -0,0 +1,65 @@ +from ...cmp import AutoType, SelfType, ErrorType + +WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' +SELF_IS_READONLY = 'Variable "self" is read-only.' +LOCAL_ALREADY_DEFINED = 'Variable "%s" is already defined in method "%s".' +INCOMPATIBLE_TYPES = 'Cannot convert "%s" into "%s".' +VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined.' +INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' +CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s".' +INVALID_PARAMETER = 'Formal parameter "%s" cannot have type SELF_TYPE.' +INVALID_BRANCH = 'Identifier "%s" declared with type SELF_TYPE in case branch.' +DUPLICATED_BRANCH = 'Duplicate branch "%s" in case statement.' + +ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] +sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] +built_in_types = [ 'Int', 'String', 'Bool', 'Object', 'IO', 'SELF_TYPE', 'AUTO_TYPE'] + +def fixed_type(cur_type): + try: return cur_type.fixed + except AttributeError: return cur_type + +def update_condition(target, value): + c1 = isinstance(target, AutoType) + c2 = (not isinstance(value, AutoType)) and value + return c1 and c2 + +# Compute the Lowest Common Ancestor in +# the type hierarchy tree +def LCA(type_list): + counter = {} + + def check(target): + return [isinstance(t, target) for t in type_list] + + if all(check(SelfType)): + return SelfType(type_list[0].fixed) + if any(check(AutoType)): + return AutoType() + if any(check(ErrorType)): + return ErrorType() + type_list = [fixed_type(t) for t in type_list] + for typex in type_list: + node = typex + while True: + try: + counter[node.name] += 1 + except KeyError: + counter[node.name] = 1 + if counter[node.name] == len(type_list): + return node + if not node.parent: + break + node = node.parent + +def check_path(D, ans): + if any([(t.name == ST) for t in D]): + return True, SelfType() + for t in D: + l = [ans, t] + lca = LCA(l) + try: l.remove(lca) + except ValueError: + return False, None + ans = l[0] + return True, ans diff --git a/src/core/visitors/type_check/verifier.py b/src/core/visitors/type_check/verifier.py new file mode 100644 index 00000000..0e2e5561 --- /dev/null +++ b/src/core/visitors/type_check/verifier.py @@ -0,0 +1,44 @@ +from .utils import * +from ...visitors import visitor +from ...cmp import CoolUtils as cool, SemanticError + +# Type Verifier +class TypeVerifier: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node): + for def_class in node.declarations: + self.visit(def_class) + + @visitor.when(cool.ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + + for feature in node.features: + self.visit(feature) + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node): + pass + + @visitor.when(cool.FuncDeclarationNode) + def visit(self, node): + try: + m1 = node.method + m2 = self.current_type.parent.get_method(m1.name) + assert m1.return_type == m2.return_type and m1.param_types == m2.param_types + except AttributeError: + pass + except SemanticError: + pass + except AssertionError: + self.errors.append((SemanticError(f'Method "{m1.name}" already defined in {self.current_type.name} with a different signature.'), node.tid)) + \ No newline at end of file From 1a2ae065e4df1e357287cf3e6eaca63e81e2b3c8 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 19:17:29 -0500 Subject: [PATCH 498/520] [structure][mips] - New mips module --- src/core/visitors/mips/__init__.py | 2 + src/core/visitors/mips/ast_printer.py | 153 ++++++++++++++++ .../{cmp => visitors/mips}/cil_to_mips.py | 123 ++----------- src/core/{cmp => visitors/mips}/mips.py | 164 +----------------- src/core/{cmp => visitors/mips}/mips_lib.asm | 0 src/core/visitors/mips/test.py | 20 +++ 6 files changed, 191 insertions(+), 271 deletions(-) create mode 100644 src/core/visitors/mips/__init__.py create mode 100644 src/core/visitors/mips/ast_printer.py rename src/core/{cmp => visitors/mips}/cil_to_mips.py (98%) rename src/core/{cmp => visitors/mips}/mips.py (62%) rename src/core/{cmp => visitors/mips}/mips_lib.asm (100%) create mode 100644 src/core/visitors/mips/test.py diff --git a/src/core/visitors/mips/__init__.py b/src/core/visitors/mips/__init__.py new file mode 100644 index 00000000..7ee6cc80 --- /dev/null +++ b/src/core/visitors/mips/__init__.py @@ -0,0 +1,2 @@ +from .ast_printer import PrintVisitor +from .cil_to_mips import CILToMIPSVisitor diff --git a/src/core/visitors/mips/ast_printer.py b/src/core/visitors/mips/ast_printer.py new file mode 100644 index 00000000..174ca704 --- /dev/null +++ b/src/core/visitors/mips/ast_printer.py @@ -0,0 +1,153 @@ +from .mips import * +from ...visitors import visitor + +class PrintVisitor: + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(Register) + def visit(self, node): + return f'${node.name}' + + @visitor.when(int) + def visit(self, node): + return str(node) + + @visitor.when(str) + def visit(self, node): + return node + + @visitor.when(ProgramNode) + def visit(self, node): + data_section_header = "\t.data" + static_strings = '\n'.join([self.visit(string_const) for string_const in node.data]) + + names_table = f"{TYPENAMES_TABLE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.string_name_label}" for tp in node.types]) + proto_table = f"{PROTO_TABLE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.label}_proto" for tp in node.types]) + + types = "\n\n".join([self.visit(tp) for tp in node.types]) + + code = "\n".join([self.visit(func) for func in node.functions]) + return f'{data_section_header}\n{static_strings}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\t.text\n\t.globl main\n{code}' + + @visitor.when(StringConst) + def visit(self, node): + return f'{node.label}: .asciiz "{node.string}"' + + @visitor.when(MIPSType) + def visit(self, node): + methods = "\n".join([f"\t.word\t {node.methods[k]}" for k in node.methods]) + dispatch_table = f"{node.label}_dispatch:\n{methods}" + proto_begin = f"{node.label}_proto:\n\t.word\t{node.index}\n\t.word\t{node.size}\n\t.word\t{node.label}_dispatch" + proto_attr = "\n".join([f'\t.word\t{node._default_attributes.get(attr, "0")}' for attr in node.attributes]) + proto_end = f"\t.word\t{OBJECT_MARK}" + proto = f"{proto_begin}\n{proto_attr}\n{proto_end}" if proto_attr != "" else f"{proto_begin}\n{proto_end}" + + return f'{dispatch_table}\n\n{proto}' + + @visitor.when(SyscallNode) + def visit(self, node): + return 'syscall' + + @visitor.when(LabelRelativeLocation) + def visit(self, node): + return f'{node.label} + {node.offset}' + + @visitor.when(RegisterRelativeLocation) + def visit(self, node): + return f'{node.offset}({self.visit(node.register)})' + + @visitor.when(FunctionNode) + def visit(self, node): + instr = [self.visit(instruction) for instruction in node.instructions] + #TODO la linea de abajo sobra, es necesaria mientras la traduccion del AST de CIL este incompleta + instr2 = [inst for inst in instr if type(inst) == str] + instructions = "\n\t".join(instr2) + return f'{node.label}:\n\t{instructions}' + + @visitor.when(AddInmediateNode) + def visit(self, node): + return f'addi {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.value)}' + + @visitor.when(StoreWordNode) + def visit(self, node): + return f'sw {self.visit(node.reg)}, {self.visit(node.addr)}' + + @visitor.when(LoadInmediateNode) + def visit(self, node): + return f'li {self.visit(node.reg)}, {self.visit(node.value)}' + + @visitor.when(JumpAndLinkNode) + def visit(self, node): + return f'jal {node.label}' + + @visitor.when(JumpRegister) + def visit(self, node): + return f'jr {self.visit(node.reg)}' + + @visitor.when(JumpRegisterAndLinkNode) + def visit(self, node): + return f'jal {self.visit(node.reg)}' + + @visitor.when(LoadWordNode) + def visit(self, node): + return f'lw {self.visit(node.reg)}, {self.visit(node.addr)}' + + @visitor.when(LoadAddressNode) + def visit(self, node): + return f'la {self.visit(node.reg)}, {self.visit(node.label)}' + + @visitor.when(MoveNode) + def visit(self, node): + return f'move {self.visit(node.reg1)} {self.visit(node.reg2 )}' + + @visitor.when(ShiftLeftLogicalNode) + def visit(self, node): + return f"sll {self.visit(node.dest)} {self.visit(node.src)} {node.bits}" + + @visitor.when(AddInmediateUnsignedNode) + def visit(self, node): + return f"addiu {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.value)}" + + @visitor.when(AddUnsignedNode) + def visit(self, node): + return f"addu {self.visit(node.dest)} {self.visit(node.sum1)} {self.visit(node.sum2)}" + + @visitor.when(LabelNode) + def visit(self, node): + return f"{node.name}:" + + @visitor.when(BranchOnNotEqualNode) + def visit(self, node): + return f"bne {self.visit(node.reg1)} {self.visit(node.reg2)} {node.label}" + + @visitor.when(JumpNode) + def visit(self, node): + return f"j {node.label}" + + @visitor.when(AddNode) + def visit(self, node): + return f"add {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(SubNode) + def visit(self, node): + return f"sub {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(MultiplyNode) + def visit(self, node): + return f"mul {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(DivideNode) + def visit(self, node): + return f"div {self.visit(node.reg1)} {self.visit(node.reg2)}" + + @visitor.when(ComplementNode) + def visit(self, node): + return f"not {self.visit(node.reg1)} {self.visit(node.reg2)}" + + @visitor.when(MoveFromLowNode) + def visit(self, node): + return f"mflo {self.visit(node.reg)}" + \ No newline at end of file diff --git a/src/core/cmp/cil_to_mips.py b/src/core/visitors/mips/cil_to_mips.py similarity index 98% rename from src/core/cmp/cil_to_mips.py rename to src/core/visitors/mips/cil_to_mips.py index f6442d95..a688573b 100644 --- a/src/core/cmp/cil_to_mips.py +++ b/src/core/visitors/mips/cil_to_mips.py @@ -1,10 +1,10 @@ import itertools as itt -import core.cmp.visitor as visitor -import core.cmp.cil as cil -import core.cmp.mips as mips + +from ...visitors import visitor +from ..cil import cil +from ..mips import mips from random import choice from collections import defaultdict -from core.cmp.utils import CountDict USED = 1 @@ -20,8 +20,7 @@ def get_free_reg(self): if state == UNUSED: self.registers[reg] = USED return reg - raise Exception("not free register") - + raise Exception("not free register") def free_reg(self, reg): self.registers[reg] = UNUSED @@ -37,12 +36,12 @@ def get_registers_for_save(self): def __repr__(self): return str(len([0 for r in self.registers if self.registers[r] == USED ])) + class MemoryManager: def __init__(self, registers, function_for_assign): self.registers = registers self.func = function_for_assign - def get_reg_for_var(self, var): index = self.func(var) if index == -1: @@ -54,8 +53,6 @@ def get_reg_unusued(self, used = []): return choice(possibles) - - class LabelGenerator: def __init__(self): self.data_count = 0 @@ -156,7 +153,7 @@ def collect_labels_in_func(self, node): mips_label = self.generate_code_label() self.register_label(node.label, mips_label) - + @visitor.on('node') def visit(self, node): pass @@ -205,14 +202,11 @@ def visit(self, node): @visitor.when(cil.FunctionNode) def visit(self, node): used_regs_finder = UsedRegisterFinder() - label = self._name_func_map[node.name] params = [param.name for param in node.params] localvars = [local.name for local in node.localvars] - size_for_locals = len(localvars) * mips.ATTR_SIZE - - + size_for_locals = len(localvars) * mips.ATTR_SIZE new_func = mips.FunctionNode(label, params, localvars) self.register_function(node.name, new_func) @@ -223,9 +217,6 @@ def visit(self, node): if len(node.instructions): reg_for_var = ra.get_registers_for_variables(node.instructions, node.params, len(mips.REGISTERS)) self.memory_manager = MemoryManager(mips.REGISTERS, lambda x : reg_for_var[x]) - - - for instruction in node.instructions: self.collect_labels_in_func(instruction) @@ -234,7 +225,6 @@ def visit(self, node): if self.in_entry_function(): initial_instructions.append(mips.JumpAndLinkNode("mem_manager_init")) - initial_instructions.extend(mips.push_register(mips.FP_REG)) initial_instructions.append(mips.AddInmediateNode(mips.FP_REG, mips.SP_REG, 4)) initial_instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals)) @@ -243,7 +233,6 @@ def visit(self, node): #This try-except block is for debuggin purposes - try: code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) @@ -254,8 +243,7 @@ def visit(self, node): # print(node.instructions) print(e) print(node.name) - - + #code_instructions.extend(self.memory_manager.save_values()) final_instructions = [] @@ -282,14 +270,11 @@ def visit(self, node): final_instructions.append(mips.JumpRegister(mips.RA_REG)) else: final_instructions.extend(mips.exit_program()) - - func_instructions = list(itt.chain(initial_instructions, code_instructions, final_instructions)) new_func.add_instructions(func_instructions) - self.finish_functions() - + self.finish_functions() @visitor.when(cil.ArgNode) def visit(self, node): @@ -523,8 +508,6 @@ def visit(self, node): instructions.append(mips.StoreWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.dest))) else: instructions.append(mips.MoveNode(reg2, mips.ARG_REGISTERS[0])) - - #instructions.append(mips.LoadWordNode(reg, src_location)) #instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg1, 0))) @@ -535,9 +518,8 @@ def visit(self, node): #instructions.append(mips.StoreWordNode(reg1, dst_location)) #instructions.append(mips.MoveNode(reg2, reg3)) - -# self.free_reg(reg) -# self.free_reg(reg2) + #self.free_reg(reg) + #self.free_reg(reg2) return instructions @@ -584,7 +566,6 @@ def visit(self, node): # instructions.append(mips.StoreWordNode(reg, dst_location)) #instructions.append(mips.MoveNode(reg2, reg3)) - #self.free_reg(reg) return instructions @@ -660,7 +641,6 @@ def visit(self, node): else: instructions.append(mips.MoveNode(reg, mips.V0_REG)) - #self.free_reg(reg1) #self.free_reg(reg2) @@ -744,7 +724,6 @@ def visit(self, node): return instructions - @visitor.when(cil.LabelNode) def visit(self, node): return [mips.LabelNode(self.get_mips_label(node.label))] @@ -809,7 +788,6 @@ def visit(self, node): #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() - comp_tp = self._types[node.computed_type] method_index = list(comp_tp.methods).index(node.method) @@ -854,7 +832,6 @@ def visit(self, node): return instructions - @visitor.when(cil.ErrorNode) def visit(self, node): instructions = [] @@ -869,7 +846,6 @@ def visit(self, node): return instructions - @visitor.when(cil.NameNode) def visit(self, node): instructions = [] @@ -948,7 +924,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() @@ -994,7 +969,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() #reg2 = self.get_free_reg() @@ -1031,7 +1005,6 @@ def visit(self, node): else: instructions.append(mips.MultiplyNode(reg3, reg1, reg2)) - #self.free_reg(reg1) #self.free_reg(reg2) @@ -1116,19 +1089,15 @@ def visit(self, node): #instructions.append(mips.AddInmediateNode(reg1, reg1, 1)) #instructions.append(mips.StoreWordNode(reg1, dest_location)) - #self.free_reg(reg1) return instructions - - @visitor.when(cil.LessEqualNode) def visit(self, node): instructions = [] #Save $a0, $a1, $v0 - if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) else: @@ -1198,7 +1167,6 @@ def visit(self, node): else: instructions.append(mips.MoveNode(reg, mips.V0_REG)) - return instructions @visitor.when(cil.ReadStrNode) @@ -1327,7 +1295,6 @@ def visit(self, node): instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.index))) else: instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) - if type(node.length) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[2], node.length)) @@ -1339,7 +1306,6 @@ def visit(self, node): instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], self.get_var_location(node.length))) else: instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg)) - instructions.append(mips.JumpAndLinkNode("substr")) #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) @@ -1351,28 +1317,6 @@ def visit(self, node): return instructions - - - - - - - - - - - - - - - - - - - - - - class UsedRegisterFinder: def __init__(self): self.used_registers = set() @@ -1385,7 +1329,6 @@ def get_used_registers(self, instructions): self.used_registers = set.difference(self.used_registers, set([mips.SP_REG, mips.FP_REG, mips.V0_REG])) return [reg for reg in self.used_registers] - @visitor.on('node') def visit(self, node): pass @@ -1414,17 +1357,6 @@ def visit(self, node): def visit(self, node): self.used_registers.add(mips.RA_REG) - - - - - - - - - - - #Change Name class RegistersAllocator: @@ -1478,9 +1410,9 @@ def liveness_analysis(self, graph, params): io = [([], io[0][0])] + io return gk, io - interference = RegistersAllocator.interference_compute(gk, oi) + # interference = RegistersAllocator.interference_compute(gk, oi) - RegistersAllocator.assign_registers(interference, 4) + # RegistersAllocator.assign_registers(interference, 4) @staticmethod def interference_compute(gk, in_out): @@ -1752,30 +1684,3 @@ def mark_leaders(self, instruction): def mark_leaders(self, instruction): instruction.leader = self.mark self.mark = False - - - - - - - - - -#TEST -CIL_TYPE_1 = cil.TypeNode("myType") -CIL_TYPE_1.attributes = ["attr1", "attr2", "attr3"] -CIL_TYPE_1.methods = [("method1", "func1"), ("method2", "func2"), ("method3", "func3"), ("method4", "func4")] -CIL_TYPE_2 = cil.TypeNode("myType2") -CIL_TYPE_2.attributes = ["attr1", "attr2"] -CIL_TYPE_2.methods = [("method1", "func5"), ("method2", "func2"), ("method3", "func6"), ("method4", "func7")] -CIL_AST_TEST = cil.ProgramNode([],[],[]) -CIL_AST_TEST.dottypes = [CIL_TYPE_1, CIL_TYPE_2] - - -# if __name__ == '__main__': -def test(): - conv = CILToMIPSVisitor() - conv.visit(CIL_AST_TEST) - for d in conv.dotdata: - print(d) - diff --git a/src/core/cmp/mips.py b/src/core/visitors/mips/mips.py similarity index 62% rename from src/core/cmp/mips.py rename to src/core/visitors/mips/mips.py index 284fbc3c..98eda471 100644 --- a/src/core/cmp/mips.py +++ b/src/core/visitors/mips/mips.py @@ -1,5 +1,4 @@ from itertools import chain -import core.cmp.visitor as visitor ATTR_SIZE = 4 RESGISTER_SIZE = 4 @@ -83,7 +82,6 @@ def get_var_location(self, name): except ValueError: return self.get_local_stack_location(name) - class DataNode(Node): def __init__(self, label): self._label = label @@ -101,7 +99,6 @@ def __init__(self, label, string): def string(self): return self._string - class InstructionNode(Node): pass @@ -214,8 +211,7 @@ def __init__(self, reg1, reg2): class MoveFromLowNode(InstructionNode): def __init__(self, reg): self.reg = reg - - + class MIPSType: def __init__(self, label, name_addr, attributes, methods, index, default = []): @@ -252,8 +248,6 @@ def index(self): return self._index - - class MemoryLocation: pass @@ -338,158 +332,4 @@ def copy_object(reg1, reg2): instructions.append(JumpAndLinkNode("copy")) return instructions - - - -class PrintVisitor: - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(Register) - def visit(self, node): - return f'${node.name}' - - @visitor.when(int) - def visit(self, node): - return str(node) - - @visitor.when(str) - def visit(self, node): - return node - - @visitor.when(ProgramNode) - def visit(self, node): - data_section_header = "\t.data" - static_strings = '\n'.join([self.visit(string_const) for string_const in node.data]) - - - names_table = f"{TYPENAMES_TABLE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.string_name_label}" for tp in node.types]) - proto_table = f"{PROTO_TABLE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.label}_proto" for tp in node.types]) - - - - types = "\n\n".join([self.visit(tp) for tp in node.types]) - - code = "\n".join([self.visit(func) for func in node.functions]) - return f'{data_section_header}\n{static_strings}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\t.text\n\t.globl main\n{code}' - - @visitor.when(StringConst) - def visit(self, node): - return f'{node.label}: .asciiz "{node.string}"' - - @visitor.when(MIPSType) - def visit(self, node): - methods = "\n".join([f"\t.word\t {node.methods[k]}" for k in node.methods]) - dispatch_table = f"{node.label}_dispatch:\n{methods}" - proto_begin = f"{node.label}_proto:\n\t.word\t{node.index}\n\t.word\t{node.size}\n\t.word\t{node.label}_dispatch" - proto_attr = "\n".join([f'\t.word\t{node._default_attributes.get(attr, "0")}' for attr in node.attributes]) - proto_end = f"\t.word\t{OBJECT_MARK}" - proto = f"{proto_begin}\n{proto_attr}\n{proto_end}" if proto_attr != "" else f"{proto_begin}\n{proto_end}" - - return f'{dispatch_table}\n\n{proto}' - - @visitor.when(SyscallNode) - def visit(self, node): - return 'syscall' - - @visitor.when(LabelRelativeLocation) - def visit(self, node): - return f'{node.label} + {node.offset}' - - @visitor.when(RegisterRelativeLocation) - def visit(self, node): - return f'{node.offset}({self.visit(node.register)})' - - @visitor.when(FunctionNode) - def visit(self, node): - instr = [self.visit(instruction) for instruction in node.instructions] - #TODO la linea de abajo sobra, es necesaria mientras la traduccion del AST de CIL este incompleta - instr2 = [inst for inst in instr if type(inst) == str] - instructions = "\n\t".join(instr2) - return f'{node.label}:\n\t{instructions}' - - @visitor.when(AddInmediateNode) - def visit(self, node): - return f'addi {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.value)}' - - @visitor.when(StoreWordNode) - def visit(self, node): - return f'sw {self.visit(node.reg)}, {self.visit(node.addr)}' - - @visitor.when(LoadInmediateNode) - def visit(self, node): - return f'li {self.visit(node.reg)}, {self.visit(node.value)}' - - @visitor.when(JumpAndLinkNode) - def visit(self, node): - return f'jal {node.label}' - - @visitor.when(JumpRegister) - def visit(self, node): - return f'jr {self.visit(node.reg)}' - - @visitor.when(JumpRegisterAndLinkNode) - def visit(self, node): - return f'jal {self.visit(node.reg)}' - - @visitor.when(LoadWordNode) - def visit(self, node): - return f'lw {self.visit(node.reg)}, {self.visit(node.addr)}' - - @visitor.when(LoadAddressNode) - def visit(self, node): - return f'la {self.visit(node.reg)}, {self.visit(node.label)}' - - @visitor.when(MoveNode) - def visit(self, node): - return f'move {self.visit(node.reg1)} {self.visit(node.reg2 )}' - - @visitor.when(ShiftLeftLogicalNode) - def visit(self, node): - return f"sll {self.visit(node.dest)} {self.visit(node.src)} {node.bits}" - - @visitor.when(AddInmediateUnsignedNode) - def visit(self, node): - return f"addiu {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.value)}" - - @visitor.when(AddUnsignedNode) - def visit(self, node): - return f"addu {self.visit(node.dest)} {self.visit(node.sum1)} {self.visit(node.sum2)}" - - @visitor.when(LabelNode) - def visit(self, node): - return f"{node.name}:" - - @visitor.when(BranchOnNotEqualNode) - def visit(self, node): - return f"bne {self.visit(node.reg1)} {self.visit(node.reg2)} {node.label}" - - @visitor.when(JumpNode) - def visit(self, node): - return f"j {node.label}" - - @visitor.when(AddNode) - def visit(self, node): - return f"add {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" - - @visitor.when(SubNode) - def visit(self, node): - return f"sub {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" - - @visitor.when(MultiplyNode) - def visit(self, node): - return f"mul {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" - - @visitor.when(DivideNode) - def visit(self, node): - return f"div {self.visit(node.reg1)} {self.visit(node.reg2)}" - - @visitor.when(ComplementNode) - def visit(self, node): - return f"not {self.visit(node.reg1)} {self.visit(node.reg2)}" - - @visitor.when(MoveFromLowNode) - def visit(self, node): - return f"mflo {self.visit(node.reg)}" \ No newline at end of file + \ No newline at end of file diff --git a/src/core/cmp/mips_lib.asm b/src/core/visitors/mips/mips_lib.asm similarity index 100% rename from src/core/cmp/mips_lib.asm rename to src/core/visitors/mips/mips_lib.asm diff --git a/src/core/visitors/mips/test.py b/src/core/visitors/mips/test.py new file mode 100644 index 00000000..da7d4f04 --- /dev/null +++ b/src/core/visitors/mips/test.py @@ -0,0 +1,20 @@ +from ..cil import cil +from .cil_to_mips import CILToMIPSVisitor + +#TEST +CIL_TYPE_1 = cil.TypeNode("myType") +CIL_TYPE_1.attributes = ["attr1", "attr2", "attr3"] +CIL_TYPE_1.methods = [("method1", "func1"), ("method2", "func2"), ("method3", "func3"), ("method4", "func4")] +CIL_TYPE_2 = cil.TypeNode("myType2") +CIL_TYPE_2.attributes = ["attr1", "attr2"] +CIL_TYPE_2.methods = [("method1", "func5"), ("method2", "func2"), ("method3", "func6"), ("method4", "func7")] +CIL_AST_TEST = cil.ProgramNode([],[],[]) +CIL_AST_TEST.dottypes = [CIL_TYPE_1, CIL_TYPE_2] + +# if __name__ == '__main__': +def test(): + conv = CILToMIPSVisitor() + conv.visit(CIL_AST_TEST) + for d in conv.dotdata: + print(d) + \ No newline at end of file From d80babd56b20a1565c1eef050b344ecaef1120cd Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 19:17:56 -0500 Subject: [PATCH 499/520] [structure][cil] - New cil module --- src/core/visitors/cil/__init__.py | 2 + src/core/visitors/cil/ast_printer.py | 184 +++++++++++++++++ src/core/{cmp => visitors/cil}/cil.py | 185 ------------------ src/core/{cmp => visitors/cil}/cool_to_cil.py | 8 +- 4 files changed, 189 insertions(+), 190 deletions(-) create mode 100644 src/core/visitors/cil/__init__.py create mode 100644 src/core/visitors/cil/ast_printer.py rename src/core/{cmp => visitors/cil}/cil.py (51%) mode change 100755 => 100644 rename src/core/{cmp => visitors/cil}/cool_to_cil.py (99%) diff --git a/src/core/visitors/cil/__init__.py b/src/core/visitors/cil/__init__.py new file mode 100644 index 00000000..3f7ada13 --- /dev/null +++ b/src/core/visitors/cil/__init__.py @@ -0,0 +1,2 @@ +from .ast_printer import get_formatter +from .cool_to_cil import COOLToCILVisitor diff --git a/src/core/visitors/cil/ast_printer.py b/src/core/visitors/cil/ast_printer.py new file mode 100644 index 00000000..5658a8dc --- /dev/null +++ b/src/core/visitors/cil/ast_printer.py @@ -0,0 +1,184 @@ +from .cil import * +from ...visitors import visitor + +def get_formatter(): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + dottypes = '\n'.join(self.visit(t) for t in node.dottypes) + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(TypeNode) + def visit(self, node): + attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) + methods = '\n\t'.join(f'method {x}: {y}' for x,y in node.methods) + + return f'type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}' + + @visitor.when(DataNode) + def visit(self, node): + return f'{node.name} = {node.value}' + + @visitor.when(FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions if self.visit(x) != []) + + return f'function {node.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(ParamNode) + def visit(self, node): + return f'PARAM {node.name}' + + @visitor.when(LocalNode) + def visit(self, node): + return f'LOCAL {node.name}' + + @visitor.when(AssignNode) + def visit(self, node): + return f'{node.dest} = {node.source}' + + @visitor.when(PlusNode) + def visit(self, node): + return f'{node.dest} = {node.left} + {node.right}' + + @visitor.when(MinusNode) + def visit(self, node): + return f'{node.dest} = {node.left} - {node.right}' + + @visitor.when(StarNode) + def visit(self, node): + return f'{node.dest} = {node.left} * {node.right}' + + @visitor.when(DivNode) + def visit(self, node): + return f'{node.dest} = {node.left} / {node.right}' + + @visitor.when(LessEqualNode) + def visit(self, node): + return f'{node.dest} = {node.left} <= {node.right}' + + @visitor.when(LessNode) + def visit(self, node): + return f'{node.dest} = {node.left} < {node.right}' + + @visitor.when(EqualNode) + def visit(self, node): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(GetAttribNode) + def visit(self, node): + return f'{node.dest} = GETATTR {node.obj} {node.attr}' + + @visitor.when(SetAttribNode) + def visit(self, node): + return f'SETATTR {node.obj} {node.attr} {node.value}' + + @visitor.when(AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.obj}' + + @visitor.when(LabelNode) + def visit(self, node): + return f'LABEL {node.label}' + + @visitor.when(GotoNode) + def visit(self, node): + return f'GOTO {node.label}' + + @visitor.when(GotoIfNode) + def visit(self, node): + return f'IF {node.condition} GOTO {node.label}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method}' + + @visitor.when(ArgNode) + def visit(self, node): + return f'ARG {node.name}' + + @visitor.when(ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + @visitor.when(LoadNode) + def visit(self, node): + return f'{node.dest} = Load {node.msg}' + + @visitor.when(ExitNode) + def visit(self, node): + return f'EXIT' + + @visitor.when(TypeNameNode) + def visit(self, node): + return f'{node.dest} = TYPENAME {node.source}' + + @visitor.when(NameNode) + def visit(self, node): + return f'{node.dest} = NAME {node.name}' + + @visitor.when(CopyNode) + def visit(self, node): + return f'{node.dest} = COPY {node.source}' + + @visitor.when(LengthNode) + def visit(self, node): + return f'{node.dest} = LENGTH {node.source}' + + @visitor.when(ConcatNode) + def visit(self, node): + return f'{node.dest} = CONCAT {node.prefix} {node.suffix}' + + @visitor.when(SubstringNode) + def visit(self, node): + return f'{node.dest} = SUBSTRING {node.index} {node.length}' + + @visitor.when(ReadStrNode) + def visit(self, node): + return f'{node.dest} = READSTR' + + @visitor.when(ReadIntNode) + def visit(self, node): + return f'{node.dest} = READINT' + + @visitor.when(PrintStrNode) + def visit(self, node): + return f'PRINT {node.value}' + + @visitor.when(PrintIntNode) + def visit(self, node): + return f'PRINT {node.value}' + + @visitor.when(ComplementNode) + def visit(self, node): + return f'{node.dest} = COMPL {node.obj}' + + @visitor.when(VoidNode) + def visit(self, node): + return 'VOID' + + @visitor.when(ErrorNode) + def visit(self, node): + return f'ERROR {node.data_node}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) + \ No newline at end of file diff --git a/src/core/cmp/cil.py b/src/core/visitors/cil/cil.py old mode 100755 new mode 100644 similarity index 51% rename from src/core/cmp/cil.py rename to src/core/visitors/cil/cil.py index 08f027db..8c9cc2fa --- a/src/core/cmp/cil.py +++ b/src/core/visitors/cil/cil.py @@ -1,5 +1,3 @@ -import core.cmp.visitor as visitor - #AST class Node: pass @@ -42,7 +40,6 @@ class InstructionNode(Node): def __init__(self): self.leader = False - class AssignNode(InstructionNode): def __init__(self, dest, source): self.dest = dest @@ -259,185 +256,3 @@ def __init__(self, data_node): def __repr__(self): return f"ERROR {self.data_node}" - -def get_formatter(): - - class PrintVisitor(object): - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - dottypes = '\n'.join(self.visit(t) for t in node.dottypes) - dotdata = '\n'.join(self.visit(t) for t in node.dotdata) - dotcode = '\n'.join(self.visit(t) for t in node.dotcode) - - return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' - - @visitor.when(TypeNode) - def visit(self, node): - attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) - methods = '\n\t'.join(f'method {x}: {y}' for x,y in node.methods) - - return f'type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}' - - @visitor.when(DataNode) - def visit(self, node): - return f'{node.name} = {node.value}' - - @visitor.when(FunctionNode) - def visit(self, node): - params = '\n\t'.join(self.visit(x) for x in node.params) - localvars = '\n\t'.join(self.visit(x) for x in node.localvars) - instructions = '\n\t'.join(self.visit(x) for x in node.instructions if self.visit(x) != []) - - return f'function {node.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' - - @visitor.when(ParamNode) - def visit(self, node): - return f'PARAM {node.name}' - - @visitor.when(LocalNode) - def visit(self, node): - return f'LOCAL {node.name}' - - @visitor.when(AssignNode) - def visit(self, node): - return f'{node.dest} = {node.source}' - - @visitor.when(PlusNode) - def visit(self, node): - return f'{node.dest} = {node.left} + {node.right}' - - @visitor.when(MinusNode) - def visit(self, node): - return f'{node.dest} = {node.left} - {node.right}' - - @visitor.when(StarNode) - def visit(self, node): - return f'{node.dest} = {node.left} * {node.right}' - - @visitor.when(DivNode) - def visit(self, node): - return f'{node.dest} = {node.left} / {node.right}' - - @visitor.when(LessEqualNode) - def visit(self, node): - return f'{node.dest} = {node.left} <= {node.right}' - - @visitor.when(LessNode) - def visit(self, node): - return f'{node.dest} = {node.left} < {node.right}' - - @visitor.when(EqualNode) - def visit(self, node): - return f'{node.dest} = {node.left} == {node.right}' - - @visitor.when(GetAttribNode) - def visit(self, node): - return f'{node.dest} = GETATTR {node.obj} {node.attr}' - - @visitor.when(SetAttribNode) - def visit(self, node): - return f'SETATTR {node.obj} {node.attr} {node.value}' - - @visitor.when(AllocateNode) - def visit(self, node): - return f'{node.dest} = ALLOCATE {node.type}' - - @visitor.when(TypeOfNode) - def visit(self, node): - return f'{node.dest} = TYPEOF {node.obj}' - - @visitor.when(LabelNode) - def visit(self, node): - return f'LABEL {node.label}' - - @visitor.when(GotoNode) - def visit(self, node): - return f'GOTO {node.label}' - - @visitor.when(GotoIfNode) - def visit(self, node): - return f'IF {node.condition} GOTO {node.label}' - - @visitor.when(StaticCallNode) - def visit(self, node): - return f'{node.dest} = CALL {node.function}' - - @visitor.when(DynamicCallNode) - def visit(self, node): - return f'{node.dest} = VCALL {node.type} {node.method}' - - @visitor.when(ArgNode) - def visit(self, node): - return f'ARG {node.name}' - - @visitor.when(ReturnNode) - def visit(self, node): - return f'RETURN {node.value if node.value is not None else ""}' - - @visitor.when(LoadNode) - def visit(self, node): - return f'{node.dest} = Load {node.msg}' - - @visitor.when(ExitNode) - def visit(self, node): - return f'EXIT' - - @visitor.when(TypeNameNode) - def visit(self, node): - return f'{node.dest} = TYPENAME {node.source}' - - @visitor.when(NameNode) - def visit(self, node): - return f'{node.dest} = NAME {node.name}' - - @visitor.when(CopyNode) - def visit(self, node): - return f'{node.dest} = COPY {node.source}' - - @visitor.when(LengthNode) - def visit(self, node): - return f'{node.dest} = LENGTH {node.source}' - - @visitor.when(ConcatNode) - def visit(self, node): - return f'{node.dest} = CONCAT {node.prefix} {node.suffix}' - - @visitor.when(SubstringNode) - def visit(self, node): - return f'{node.dest} = SUBSTRING {node.index} {node.length}' - - @visitor.when(ReadStrNode) - def visit(self, node): - return f'{node.dest} = READSTR' - - @visitor.when(ReadIntNode) - def visit(self, node): - return f'{node.dest} = READINT' - - @visitor.when(PrintStrNode) - def visit(self, node): - return f'PRINT {node.value}' - - @visitor.when(PrintIntNode) - def visit(self, node): - return f'PRINT {node.value}' - - @visitor.when(ComplementNode) - def visit(self, node): - return f'{node.dest} = COMPL {node.obj}' - - @visitor.when(VoidNode) - def visit(self, node): - return 'VOID' - - @visitor.when(ErrorNode) - def visit(self, node): - return f'ERROR {node.data_node}' - - printer = PrintVisitor() - return (lambda ast: printer.visit(ast)) - diff --git a/src/core/cmp/cool_to_cil.py b/src/core/visitors/cil/cool_to_cil.py similarity index 99% rename from src/core/cmp/cool_to_cil.py rename to src/core/visitors/cil/cool_to_cil.py index fb4ec58c..b7826480 100644 --- a/src/core/cmp/cool_to_cil.py +++ b/src/core/visitors/cil/cool_to_cil.py @@ -1,8 +1,6 @@ -import core.cmp.visitor as visitor -import core.cmp.cil as cil -import core.cmp.CoolUtils as cool -from core.cmp.semantic import Attribute, Method, Type, VariableInfo, SemanticError -from core.cmp.functions import get_token +from ..cil import cil +from ...visitors import visitor +from ...cmp import CoolUtils as cool, VariableInfo, get_token class BaseCOOLToCILVisitor: def __init__(self, context): From a971247d51e15b851ead97d16804e7f8fd061513 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 19:19:05 -0500 Subject: [PATCH 500/520] [structure][visitor] - New visitors module Submodules: - cil - mips - type_check --- src/core/cmp/visitors.py | 1106 ------------------------- src/core/visitors/__init__.py | 3 + src/core/{cmp => visitors}/visitor.py | 0 3 files changed, 3 insertions(+), 1106 deletions(-) delete mode 100644 src/core/cmp/visitors.py create mode 100644 src/core/visitors/__init__.py rename src/core/{cmp => visitors}/visitor.py (100%) diff --git a/src/core/cmp/visitors.py b/src/core/cmp/visitors.py deleted file mode 100644 index f1615c21..00000000 --- a/src/core/cmp/visitors.py +++ /dev/null @@ -1,1106 +0,0 @@ -import core.cmp.visitor as visitor -from core.cmp.CoolUtils import * -from core.cmp.utils import InferenceSets -from core.cmp.semantic import SemanticError -from core.cmp.semantic import Attribute, Method, Type -from core.cmp.semantic import ErrorType, IntType, StringType, BoolType, IOType, VoidType, AutoType, SelfType -from core.cmp.semantic import Context, Scope - -WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' -SELF_IS_READONLY = 'Variable "self" is read-only.' -LOCAL_ALREADY_DEFINED = 'Variable "%s" is already defined in method "%s".' -INCOMPATIBLE_TYPES = 'Cannot convert "%s" into "%s".' -VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined.' -INVALID_OPERATION = 'Operation is not defined between "%s" and "%s".' -CONDITION_NOT_BOOL = '"%s" conditions return type must be Bool not "%s".' -INVALID_PARAMETER = 'Formal parameter "%s" cannot have type SELF_TYPE.' -INVALID_BRANCH = 'Identifier "%s" declared with type SELF_TYPE in case branch.' -DUPLICATED_BRANCH = 'Duplicate branch "%s" in case statement.' - -ST, AT = ['SELF_TYPE', 'AUTO_TYPE'] -sealed = ['Int', 'String', 'Bool', 'SELF_TYPE', 'AUTO_TYPE'] -built_in_types = [ 'Int', 'String', 'Bool', 'Object', 'IO', 'SELF_TYPE', 'AUTO_TYPE'] -INT, STRING, BOOL, OBJ, SELF_TYPE = None, None, None, None, None - -def define_built_in_types(context): - obj = context.create_type('Object') - i = context.append_type(IntType()) - i.set_parent(obj) - s = context.append_type(StringType()) - s.set_parent(obj) - b = context.append_type(BoolType()) - b.set_parent(obj) - io = context.append_type(IOType()) - io.set_parent(obj) - st = context.append_type(SelfType()) - context.append_type(AutoType()) - - obj.define_method('abort', [], [], obj) - obj.define_method('type_name', [], [], s) - obj.define_method('copy', [], [], st) - - io.define_method('out_string', ['x'], [s], st) - io.define_method('out_int', ['x'], [i], st) - io.define_method('in_string', [], [], s) - io.define_method('in_int', [], [], i) - - s.define_method('length', [], [], i) - s.define_method('concat', ['s'], [s], s) - s.define_method('substr', ['i', 'l'], [i, i], s) - - global INT, STRING, BOOL, OBJ, SELF_TYPE - INT, STRING, BOOL, OBJ, SELF_TYPE = i, s, b, obj, st - -def fixed_type(cur_type): - try: return cur_type.fixed - except AttributeError: return cur_type - -def update_condition(target, value): - c1 = isinstance(target, AutoType) - c2 = (not isinstance(value, AutoType)) and value - return c1 and c2 - -#AST Printer -class FormatVisitor: - @visitor.on('node') - def visit(self, node, tabs): - pass - - @visitor.when(ProgramNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' - statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) - return f'{ans}\n{statements}' - - @visitor.when(ClassDeclarationNode) - def visit(self, node, tabs=0): - parent = '' if node.parent is None else f"inherits {node.parent}" - ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' - features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) - return f'{ans}\n{features}' - - @visitor.when(AttrDeclarationNode) - def visit(self, node, tabs=0): - sons = [node.expr] if node.expr else [] - text = '<- ' if node.expr else '' - ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.id} : {node.type} {text}' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' if body else f'{ans}' - - @visitor.when(FuncDeclarationNode) - def visit(self, node, tabs=0): - params = ', '.join(' : '.join(param) for param in node.params) - ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {node.type} {{}}' - body = self.visit(node.body, tabs + 1) - return f'{ans}\n{body}' - - @visitor.when(IfThenElseNode) - def visit(self, node, tabs=0): - sons = [node.condition, node.if_body, node.else_body] - ans = '\t' * tabs + f'\\__IfThenElseNode: if then else fi' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(WhileLoopNode) - def visit(self, node, tabs=0): - sons = [node.condition, node.body] - ans = '\t' * tabs + f'\\__WhileLoopNode: while loop pool' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(BlockNode) - def visit(self, node, tabs=0): - sons = node.exprs - ans = '\t' * tabs + f'\\__BlockNode: {{ ... }}' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(LetInNode) - def visit(self, node, tabs=0): - sons = node.let_body + [node.in_body] - ans = '\t' * tabs + f'\\__LetInNode: let {{ ... }} in ' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(CaseOfNode) - def visit(self, node, tabs=0): - sons = [node.expr] + node.branches - ans = '\t' * tabs + f'\\__CaseOfNode: case of {{ ... }} esac' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(CaseExpressionNode) - def visit(self, node, tabs=0): - sons = [node.expr] - ans = '\t' * tabs + f'\\__CaseExpressionNode: {node.id} : {node.type} => ' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(AssignNode) - def visit(self, node, tabs=0): - sons = [node.expr] - ans = '\t' * tabs + f'\\__AssignNode: {node.id} = ' - body = '\n'.join(self.visit(child, tabs + 1) for child in sons) - return f'{ans}\n{body}' - - @visitor.when(UnaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.symbol.lex} ' - right = self.visit(node.expr, tabs + 1) - return f'{ans}\n{right}' - - @visitor.when(BinaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__{node.__class__.__name__}: {node.symbol.lex} ' - left = self.visit(node.left, tabs + 1) - right = self.visit(node.right, tabs + 1) - return f'{ans}\n{left}\n{right}' - - @visitor.when(AtomicNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' - - @visitor.when(FunctionCallNode) - def visit(self, node, tabs=0): - obj = self.visit(node.obj, tabs + 1) - ans = '\t' * tabs + f'\\__FunctionCallNode: .{node.id}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - ans = f'{ans}\n{obj}' - if args: ans += f'\n{args}' - return ans - - @visitor.when(MemberCallNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__MemberCallNode: {node.id}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - if args: ans += f'\n{args}' - return ans - - @visitor.when(NewNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__NewNode: new {node.type}()' - -# Type Collector -class TypeCollector: - def __init__(self, errors=[]): - self.context = None - self.errors = errors - self.type_level = {} - self.parent = {} - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - self.context = Context() - define_built_in_types(self.context) - - for def_class in node.declarations: - self.visit(def_class) - - # comparison for sort node.declarations - def get_type_level(typex, error_token=empty_token): - try: - parent = self.type_level[typex] - except KeyError: - return 0 - - if parent == 0: - node = self.parent[typex] - node.parent = "Object" - self.errors.append((SemanticError('Cyclic heritage.'), error_token)) - elif type(parent) is not int: - self.type_level[typex] = 0 if parent else 1 - if type(parent) is str: - self.type_level[typex] = get_type_level(parent, self.parent[typex].tid) + 1 - - return self.type_level[typex] - - node.declarations.sort(key = lambda node: get_type_level(node.id)) - - @visitor.when(ClassDeclarationNode) - def visit(self, node): - def new_type(): - self.context.create_type(node.id) - self.type_level[node.id] = node.parent - self.parent[node.id] = node - - def make_a_duplicate(): - while True: - node.id = '1' + node.id - try: new_type() - except SemanticError: pass - else: break - - if node.id not in built_in_types: - try: new_type() - except SemanticError as ex: - self.errors.append((ex, node.tid)) - make_a_duplicate() - else: - self.errors.append((SemanticError(f'{node.id} is an invalid class name'), node.tid)) - make_a_duplicate() - -# Type Builder -class TypeBuilder: - def __init__(self, context, errors=[]): - self.context = context - self.current_type = None - self.errors = errors - self.methods = {} - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - main_token = None - for def_class in node.declarations: - self.visit(def_class) - if def_class.id == 'Main': - main_token = def_class.tid - - try: - main = self.context.get_type('Main') - method = main.methods['main'] - tmethod = self.methods['Main']['main'] - if method.param_names: - self.errors.append((SemanticError('Method "main" must takes no formal parameters'), tmethod)) - except TypeError: - self.errors.append((SemanticError('No definition for class "Main"'), empty_token)) - except KeyError: - self.errors.append((SemanticError('Class "Main" must have a method "main"'), main_token)) - - @visitor.when(ClassDeclarationNode) - def visit(self, node): - self.current_type = self.context.get_type(node.id) - - if node.parent: - if node.parent in sealed: - self.errors.append((SemanticError(f'Is not possible to inherits from "{node.parent}"'), node.tparent)) - node.parent = 'Object' - try: - parent_type = self.context.get_type(node.parent) - self.current_type.set_parent(parent_type) - except TypeError as ex: - self.errors.append((ex, node.tparent)) - - for feature in node.features: - self.visit(feature) - - @visitor.when(AttrDeclarationNode) - def visit(self, node): - try: - attr_type = self.context.get_type(node.type) - except TypeError as ex: - self.errors.append((ex, node.ttype)) - attr_type = ErrorType() - node.attr_type = attr_type - - try: - if node.id == 'self': - raise SemanticError(SELF_IS_READONLY) - attr = self.current_type.define_attribute(node.id, attr_type) - attr.node = node - node.attr = attr - except SemanticError as ex: - self.errors.append((ex, node.tid)) - - @visitor.when(FuncDeclarationNode) - def visit(self, node): - arg_names, arg_types, arg_nodes = [], [], [] - for i, arg in enumerate(node.params): - idx, typex = arg - try: - assert typex != ST - arg_type = self.context.get_type(typex) - except TypeError as ex: - self.errors.append((ex, node.params[i].ttype)) - arg_type = ErrorType() - except AssertionError: - self.errors.append((SemanticError(INVALID_PARAMETER % (idx)), node.params[i].ttype)) - arg_type = ErrorType() - - if idx == 'self': - self.errors.append((SemanticError('"self" cannot be the name of a formal parameter'), node.params[i].ttype)) - if idx in arg_names: - self.errors.append((SemanticError(f'Formal parameter {idx} redefined'), node.params[i].ttype)) - arg_names.append(idx) - arg_types.append(arg_type) - arg_nodes.append(arg) - arg.idx = i - arg.method_types = arg_types - - try: - ret_type = self.context.get_type(node.type) - except TypeError as ex: - self.errors.append((ex, node.ttype)) - ret_type = ErrorType() - node.ret_type = ret_type - node.arg_types = arg_types - node.arg_names = arg_names - node.arg_nodes = arg_nodes - - try: - if node.id == 'self': - raise SemanticError('"self" is an invalid method name') - method = self.current_type.define_method(node.id, arg_names, arg_types, ret_type) - method.nodes = arg_nodes - method.ret_node = node - node.method = method - for arg in node.params: - arg.method = method - if not self.current_type.name in self.methods: - self.methods[self.current_type.name] = {} - self.methods[self.current_type.name][node.id] = node.tid - except SemanticError as ex: - self.errors.append((ex, node.tid)) - -# Compute the Lowest Common Ancestor in -# the type hierarchy tree -def LCA(type_list): - counter = {} - - def check(target): - return [isinstance(t, target) for t in type_list] - - if all(check(SelfType)): - return SelfType(type_list[0].fixed) - if any(check(AutoType)): - return AutoType() - if any(check(ErrorType)): - return ErrorType() - type_list = [fixed_type(t) for t in type_list] - for typex in type_list: - node = typex - while True: - try: - counter[node.name] += 1 - except KeyError: - counter[node.name] = 1 - if counter[node.name] == len(type_list): - return node - if not node.parent: - break - node = node.parent - -def check_path(D, ans): - if any([(t.name == ST) for t in D]): - return True, SelfType() - for t in D: - l = [ans, t] - lca = LCA(l) - try: l.remove(lca) - except ValueError: - return False, None - ans = l[0] - return True, ans - -# Type Checker -class TypeChecker: - def __init__(self, context, errors=[]): - self.context = context - self.current_type = None - self.current_method = None - self.errors = errors - - @visitor.on('node') - def visit(self, node, scope): - pass - - @visitor.when(ProgramNode) - def visit(self, node, scope=None): - scope = Scope() - for declaration in node.declarations: - self.visit(declaration, scope.create_child()) - return scope - - @visitor.when(ClassDeclarationNode) - def visit(self, node, scope): - self.current_type = self.context.get_type(node.id) - - scope.define_variable('self', SelfType(self.current_type)) - cur_type = self.current_type - while True: - for attr in cur_type.attributes: - vtype = attr.type - if vtype.name == ST: - vtype = SelfType(self.current_type) - var = scope.define_variable(attr.name, vtype) - var.node = attr.node - if not cur_type.parent: - break - cur_type = cur_type.parent - - cur_type = self.current_type - pending, count = [], 0 - for idx, feature in enumerate(node.features): - if isinstance(feature, AttrDeclarationNode): - self.visit(feature, scope) - if not scope.is_defined(feature.id): - vtype = cur_type.attributes[count].type - if vtype.name == ST: - vtype = SelfType(self.current_type) - var = scope.define_variable(feature.id, vtype) - var.node = cur_type.attributes[count].node - count += 1 - else: - pending.append(feature) - - for feature in pending: - self.visit(feature, scope.create_child()) - - @visitor.when(AttrDeclarationNode) - def visit(self, node, scope): - if not node.expr: - return - - self.visit(node.expr, scope) - expr_type = node.expr.computed_type - real_type = node.attr_type - node.info = [expr_type, real_type] - - if not expr_type.conforms_to(real_type): - self.errors.append((TypeError(INCOMPATIBLE_TYPES % (expr_type.name, real_type.name)), node.arrow)) - - @visitor.when(FuncDeclarationNode) - def visit(self, node, scope): - self.current_method = node.id - - for pname, ptype, pnode in zip(node.arg_names, node.arg_types, node.arg_nodes): - var = scope.define_variable(pname, ptype) - var.node = pnode - - self.visit(node.body, scope) - - body_type = node.body.computed_type - method_rtn_type = node.ret_type - node.info = [body_type, method_rtn_type] - - if not body_type.conforms_to(method_rtn_type): - self.errors.append((TypeError(INCOMPATIBLE_TYPES % (body_type.name, method_rtn_type.name)), node.ttype)) - - @visitor.when(AssignNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - node_type = node.expr.computed_type - var_type = None - - try: - if not scope.is_defined(node.id): - scope.define_variable(node.id, ErrorType()) - raise NameError(VARIABLE_NOT_DEFINED % (node.id)) - var = scope.find_variable(node.id) - var_type = var.type - if var.name == 'self': - raise SemanticError(SELF_IS_READONLY) - if not node_type.conforms_to(var.type): - raise TypeError(INCOMPATIBLE_TYPES % (node_type.name, var.type.name)) - except Exception as ex: - self.errors.append((ex, node.tid)) - node_type = ErrorType() - - node.info = [node_type, var_type] - node.computed_type = node_type - - @visitor.when(CaseOfNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - - types_list = [] - branches = set() - for case in node.branches: - if case.type in branches: - # //TODO: Check this again after the Inference process - self.errors.append((SemanticError(DUPLICATED_BRANCH % (case.type)), case.ttype)) - branches.add(case.type) - self.visit(case, scope.create_child()) - types_list.append(case.computed_type) - - node.computed_type = LCA(types_list) - - @visitor.when(CaseExpressionNode) - def visit(self, node, scope): - node.scope = scope - try: - assert node.type != ST - branch_type = self.context.get_type(node.type) - except TypeError as ex: - self.errors.append((ex, node.ttype)) - branch_type = ErrorType() - except AssertionError: - self.errors.append((SemanticError(INVALID_BRANCH % node.id), node.ttype)) - branch_type = ErrorType() - node.branch_type = branch_type - - if node.id == 'self': - self.errors.append((SemanticError(SELF_IS_READONLY), node.id)) - else: - var = scope.define_variable(node.id, branch_type) - var.node = node - self.visit(node.expr, scope) - node.computed_type = node.expr.computed_type - - @visitor.when(LetInNode) - def visit(self, node, scope): - node.scope = scope - - for expr in node.let_body: - node.scope = node.scope.create_child() - self.visit(expr, node.scope) - - self.visit(node.in_body, node.scope) - node.computed_type = node.in_body.computed_type - - @visitor.when(LetAttributeNode) - def visit(self, node, scope): - try: - node_type = self.context.get_type(node.type) - if node_type.name == ST: - node_type = SelfType(self.current_type) - except TypeError as ex: - self.errors.append((ex, node.ttype)) - node_type = ErrorType() - node.attr_type = node_type - node.scope = None - - if node.expr: - self.visit(node.expr, scope) - expr_type = node.expr.computed_type - node.info = [expr_type, node_type] - - if not expr_type.conforms_to(node_type): - self.errors.append((TypeError(INCOMPATIBLE_TYPES % (expr_type.name, node_type.name)), node.arrow)) - if node.id == 'self': - self.errors.append((SemanticError(SELF_IS_READONLY), node.tid)) - else: - var = scope.define_variable(node.id, node_type) - var.node = node - - @visitor.when(IfThenElseNode) - def visit(self, node, scope): - self.visit(node.condition, scope) - node.cond_type = node.condition.computed_type - - if not node.cond_type.conforms_to(BOOL): - self.errors.append((TypeError(CONDITION_NOT_BOOL % ('If', node.cond_type.name)), node.token)) - - self.visit(node.if_body, scope) - if_type = node.if_body.computed_type - - self.visit(node.else_body, scope) - else_type = node.else_body.computed_type - node.computed_type = LCA([if_type, else_type]) - - @visitor.when(BlockNode) - def visit(self, node, scope): - for expr in node.exprs: - self.visit(expr, scope) - - last_expr = node.exprs[-1] - node.computed_type = last_expr.computed_type - - @visitor.when(WhileLoopNode) - def visit(self, node, scope): - self.visit(node.condition, scope) - node.cond_type = node.condition.computed_type - - if not node.cond_type.conforms_to(BOOL): - self.errors.append((TypeError(CONDITION_NOT_BOOL % ('While', node.cond_type.name)), node.token)) - - self.visit(node.body, scope) - node.computed_type = OBJ - - @visitor.when(FunctionCallNode) - def visit(self, node, scope): - self.visit(node.obj, scope) - obj_type = node.obj.computed_type - - error = False - - arg_types, real_types = [], [] - for arg in node.args: - self.visit(arg, scope) - arg_types.append(arg.computed_type) - - try: - if node.type: - token = node.ttype - cast_type = self.context.get_type(node.type) - if cast_type.name == ST: - raise SemanticError("Invalid use of SELF_TYPE") - if cast_type.name == AT: - raise SemanticError('Is not possible to use AUTO_TYPE in a cast') - if not obj_type.conforms_to(cast_type): - raise TypeError(INCOMPATIBLE_TYPES % (obj_type.name, node.type)) - obj_type = cast_type - - assert obj_type - token = node.tid - obj_method = obj_type.get_method(node.id) - node.obj_method = obj_method - if len(node.args) == len(obj_method.param_types): - for idx, (arg, param_type) in enumerate(zip(arg_types, obj_method.param_types)): - real_types.append(param_type) - - if not arg.conforms_to(param_type): - self.errors.append((TypeError(INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}")), token)) - error = True - else: - raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - assert not error - node_type = obj_method.return_type - if node_type.name == ST: - node_type = obj_type - except AssertionError: - node_type = ErrorType() - except Exception as ex: - self.errors.append((ex, token)) - node_type = ErrorType() - - node.info = [arg_types, real_types] - node.computed_type = node_type - - @visitor.when(MemberCallNode) - def visit(self, node, scope): - obj_type = SelfType(self.current_type) - - error = False - - arg_types, real_types = [], [] - for arg in node.args: - self.visit(arg, scope) - arg_types.append(arg.computed_type) - - try: - token = node.tid - obj_method = obj_type.get_method(node.id) - node.obj_method = obj_method - if len(node.args) == len(obj_method.param_types): - for arg, param_type in zip(arg_types, obj_method.param_types): - real_types.append(param_type) - - if not arg.conforms_to(param_type): - self.errors.append((TypeError(INCOMPATIBLE_TYPES % (arg.name, param_type.name + f" in the argument #{idx} of {node.id}")), token)) - error = True - else: - raise SemanticError(f'Method "{obj_method.name}" of "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)') - assert not error - node_type = obj_method.return_type - if node_type.name == ST: - node_type = obj_type - except AssertionError: - node_type = ErrorType() - except Exception as ex: - self.errors.append((ex, token)) - node_type = ErrorType() - - node.info = [arg_types, real_types] - node.computed_type = node_type - - @visitor.when(BinaryNode) - def visit(self, node, scope): - self.visit(node.left, scope) - left_type = node.left.computed_type - - self.visit(node.right, scope) - right_type = node.right.computed_type - node.info = [left_type, right_type] - - if not (right_type.conforms_to(INT) and left_type.conforms_to(INT)): - self.errors.append((TypeError(INVALID_OPERATION % (left_type.name, right_type.name)), node.symbol)) - - node.computed_type = [BOOL, INT][isinstance(node, ArithmeticNode)] - - @visitor.when(IntegerNode) - def visit(self, node, scope): - node.computed_type = INT - - @visitor.when(StringNode) - def visit(self, node, scope): - node.computed_type = STRING - - @visitor.when(BoolNode) - def visit(self, node, scope): - node.computed_type = BOOL - - @visitor.when(IdNode) - def visit(self, node, scope): - if scope.is_defined(node.lex): - node_type = scope.find_variable(node.lex).type - else: - scope.define_variable(node.lex, ErrorType()) - self.errors.append((NameError(VARIABLE_NOT_DEFINED % (node.lex)), node.token)) - node_type = ErrorType() - - node.computed_type = node_type - - @visitor.when(NewNode) - def visit(self, node, scope): - try: - node_type = self.context.get_type(node.type) - if node.type == ST: - node_type = SelfType(self.current_type) - except TypeError as ex: - self.errors.append((ex, node.ttype)) - node_type = ErrorType() - - node.computed_type = node_type - - @visitor.when(IsVoidNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - node.computed_type = BOOL - - @visitor.when(ComplementNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - expr_type = node.expr.computed_type - node.expr_type = expr_type - - if not expr_type.conforms_to(INT): - self.errors.append((TypeError("Complement works only for Int"), node.symbol)) - node.computed_type = INT - - @visitor.when(NotNode) - def visit(self, node, scope): - self.visit(node.expr, scope) - expr_type = node.expr.computed_type - node.expr_type = expr_type - - if not expr_type.conforms_to(BOOL): - self.errors.append((TypeError("Not operator works only for Bool"), node.symbol)) - node.computed_type = BOOL - - @visitor.when(EqualNode) - def visit(self, node, scope): - self.visit(node.left, scope) - left_type = node.left.computed_type - - self.visit(node.right, scope) - right_type = node.right.computed_type - node.info = [left_type, right_type] - - valid_types = [IntType(), BoolType(), StringType()] - try: - cur_types = [right_type, left_type] - for op_type in valid_types: - try: - cur_types.remove(op_type) - assert cur_types[0].conforms_to(op_type) - break - except ValueError: pass - except AssertionError: - self.errors.append((TypeError(INVALID_OPERATION % (left_type.name, right_type.name)), node.symbol)) - - node.computed_type = BOOL - - -# Type Inference Visitor -class InferenceVisitor(TypeChecker): - def __init__(self, context, errors=[]): - super().__init__(context, errors) - self.variable = {} - - def inference(self, node, ntype, conforms=True): - try: - self.variable[node].add(ntype, conforms) - except KeyError: - self.variable[node] = InferenceSets().add(ntype, conforms) - - @visitor.on('node') - def context_update(self, node, ntype): - pass - - @visitor.when(Node) - def context_update(self, node, ntype): - pass - - @visitor.when(Param) - def context_update(self, node, ntype): - node.method_types[node.idx] = ntype - try: node.method.param_types[node.idx] = ntype - except AttributeError: pass - - @visitor.when(AttrDeclarationNode) - def context_update(self, node, ntype): - try: node.attr_type = ntype - except AttributeError: pass - try: node.branch_type = ntype - except AttributeError: pass - try: node.attr.type = ntype - except AttributeError: pass - - @visitor.when(FuncDeclarationNode) - def context_update(self, node, ntype): - node.ret_type = ntype - try: node.method.return_type = ntype - except AttributeError: pass - - @visitor.on('node') - def update(self, node, scope, ntype): - pass - - @visitor.when(AssignNode) - def update(self, node, scope, ntype): - self.update(node.expr, scope, ntype) - - @visitor.when(CaseOfNode) - def update(self, node, scope, ntype): - for branch in node.branches: - if isinstance(branch.computed_type, AutoType): - self.update(branch, scope, ntype) - - @visitor.when(CaseExpressionNode) - def update(self, node, scope, ntype): - self.update(node.expr, node.scope, ntype) - - @visitor.when(LetInNode) - def update(self, node, scope, ntype): - self.update(node.in_body, node.scope, ntype) - - @visitor.when(IfThenElseNode) - def update(self, node, scope, ntype): - if isinstance(node.if_body.computed_type, AutoType): - self.update(node.if_body, scope, ntype) - if isinstance(node.else_body.computed_type, AutoType): - self.update(node.else_body, scope, ntype) - - @visitor.when(BlockNode) - def update(self, node, scope, ntype): - self.update(node.exprs[-1], scope, ntype) - - @visitor.when(FunctionCallNode) - def update(self, node, scope, ntype): - self.inference(node.obj_method.ret_node, ntype) - - @visitor.when(MemberCallNode) - def update(self, node, scope, ntype): - self.inference(node.obj_method.ret_node, ntype) - - @visitor.when(IdNode) - def update(self, node, scope, ntype): - self.inference(scope.find_variable(node.lex).node, ntype) - - # Visit - @visitor.on('node') - def visit(self, node, scope): - pass - - @visitor.when(Node) - def visit(self, node, scope): - if not issubclass(node.__class__, BinaryNode): - super().visit(node, scope) - - @visitor.when(ProgramNode) - def visit(self, node, scope=None): - scope = super().visit(node, scope) - - infered = 0 - pending = [] - for (auto, sets) in self.variable.items(): - try: - if (len(sets.D) + len(sets.S) == 1): - pending.append(auto) - continue - ok, D1 = check_path(sets.D, OBJ) - assert ok - if len(sets.S) and not isinstance(D1, SelfType): - candidate = LCA(sets.S) - assert LCA([candidate, D1]) == D1 - D1 = candidate - auto.type = D1.name - self.context_update(auto, D1) - infered += 1 - except AssertionError: - self.errors.append((SemanticError(f'Bad use of AUTO_TYPE detected'), auto.ttype)) - if not infered: - for auto in pending: - auto.type = OBJ.name - self.context_update(auto, OBJ) - self.variable.clear() - return infered, scope - - @visitor.when(AttrDeclarationNode) - def visit(self, node, scope): - super().visit(node, scope) - - if isinstance(node.attr_type, AutoType): - self.inference(node, OBJ) - - if not node.expr: - return - - expr, rtype = node.info - if update_condition(rtype, expr): - self.inference(node, expr, False) - if update_condition(expr, rtype): - self.update(node.expr, scope, rtype) - - @visitor.when(FuncDeclarationNode) - def visit(self, node, scope): - super().visit(node, scope) - - body, rtn = node.info - if isinstance(rtn, AutoType): - self.inference(node, OBJ) - for ptype, pnode in zip(node.arg_types, node.arg_nodes): - if isinstance(ptype, AutoType): - self.inference(pnode, OBJ) - if update_condition(rtn, body): - self.inference(node, body, False) - if update_condition(body, rtn): - self.update(node.body, scope, rtn) - - @visitor.when(AssignNode) - def visit(self, node, scope): - super().visit(node, scope) - - node_type, var = node.info - if update_condition(var, node_type): - self.inference(scope.find_variable(node.id).node, node_type, False) - if update_condition(node_type, var): - self.update(node.expr, scope, var) - - @visitor.when(CaseExpressionNode) - def visit(self, node, scope): - super().visit(node, scope) - - if isinstance(node.branch_type, AutoType): - self.inference(node, OBJ) - - @visitor.when(LetAttributeNode) - def visit(self, node, scope): - super().visit(node, scope) - - if isinstance(node.attr_type, AutoType): - self.inference(node, OBJ) - - if not node.expr: - return - - expr, rtype = node.info - if update_condition(rtype, expr): - self.inference(scope.find_variable(node.id).node, expr, False) - if update_condition(expr, rtype): - self.update(node.expr, scope, rtype) - - @visitor.when(IfThenElseNode) - def visit(self, node, scope): - super().visit(node, scope) - - if isinstance(node.cond_type, AutoType): - self.update(node.condition, scope, BOOL) - - @visitor.when(WhileLoopNode) - def visit(self, node, scope): - super().visit(node, scope) - - if isinstance(node.cond_type, AutoType): - self.update(node.condition, scope, BOOL) - - @visitor.when(FunctionCallNode) - def visit(self, node, scope): - super().visit(node, scope) - - args, real = node.info - if not real: - return - - for idx, (atype, rtype) in enumerate(zip(args, real)): - if update_condition(rtype, atype): - self.inference(node.obj_method.nodes[idx], atype, False) - if update_condition(atype, rtype): - self.update(node.args[idx], scope, rtype) - - @visitor.when(MemberCallNode) - def visit(self, node, scope): - super().visit(node, scope) - - args, real = node.info - if not real: - return - - for idx, (atype, rtype) in enumerate(zip(args, real)): - if update_condition(rtype, atype): - self.inference(node.obj_method.nodes[idx], atype, False) - if update_condition(atype, rtype): - self.update(node.args[idx], scope, rtype) - - @visitor.when(BinaryNode) - def visit(self, node, scope): - super().visit(node, scope) - - left, right = node.info - if isinstance(left, AutoType): - self.update(node.left, scope, INT) - if isinstance(right, AutoType): - self.update(node.right, scope, INT) - - @visitor.when(ComplementNode) - def visit(self, node, scope): - super().visit(node, scope) - - if isinstance(node.expr_type, AutoType): - self.update(node.expr, scope, INT) - - @visitor.when(NotNode) - def visit(self, node, scope): - super().visit(node, scope) - - if isinstance(node.expr_type, AutoType): - self.update(node.expr, scope, BOOL) - - @visitor.when(EqualNode) - def visit(self, node, scope): - super().visit(node, scope) - - left, right = node.info - if update_condition(left, right) and right in [INT, BOOL, STRING]: - self.update(node.left, scope, right) - if update_condition(right, left) and left in [INT, BOOL, STRING]: - self.update(node.right, scope, left) - -# Type Verifier -class TypeVerifier: - def __init__(self, context, errors=[]): - self.context = context - self.current_type = None - self.errors = errors - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - main_token = None - for def_class in node.declarations: - self.visit(def_class) - - @visitor.when(ClassDeclarationNode) - def visit(self, node): - self.current_type = self.context.get_type(node.id) - - for feature in node.features: - self.visit(feature) - - @visitor.when(AttrDeclarationNode) - def visit(self, node): - pass - - @visitor.when(FuncDeclarationNode) - def visit(self, node): - try: - m1 = node.method - m2 = self.current_type.parent.get_method(m1.name) - assert m1.return_type == m2.return_type and m1.param_types == m2.param_types - except AttributeError: - pass - except SemanticError: - pass - except AssertionError: - self.errors.append((SemanticError(f'Method "{m1.name}" already defined in {self.current_type.name} with a different signature.'), node.tid)) \ No newline at end of file diff --git a/src/core/visitors/__init__.py b/src/core/visitors/__init__.py new file mode 100644 index 00000000..d1398e75 --- /dev/null +++ b/src/core/visitors/__init__.py @@ -0,0 +1,3 @@ +from .cil import * +from .mips import * +from .type_check import * diff --git a/src/core/cmp/visitor.py b/src/core/visitors/visitor.py similarity index 100% rename from src/core/cmp/visitor.py rename to src/core/visitors/visitor.py From a9af9fad000f8d216723a13e8727d880fe9b9d80 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 19:19:42 -0500 Subject: [PATCH 501/520] [structure][lexer] - New lexer module --- src/core/lexer/__init__.py | 1 + src/core/{cmp => lexer}/lex.py | 31 +++++++------------------------ 2 files changed, 8 insertions(+), 24 deletions(-) create mode 100644 src/core/lexer/__init__.py rename src/core/{cmp => lexer}/lex.py (98%) diff --git a/src/core/lexer/__init__.py b/src/core/lexer/__init__.py new file mode 100644 index 00000000..5872b253 --- /dev/null +++ b/src/core/lexer/__init__.py @@ -0,0 +1 @@ +from .lex import CoolLexer diff --git a/src/core/cmp/lex.py b/src/core/lexer/lex.py similarity index 98% rename from src/core/cmp/lex.py rename to src/core/lexer/lex.py index a7ccca41..d611eeac 100644 --- a/src/core/cmp/lex.py +++ b/src/core/lexer/lex.py @@ -1,14 +1,14 @@ import ply.lex as lex import re -from .utils import Token -from .CoolUtils import * + +from ..cmp import Token +from ..cmp.CoolUtils import * class CoolLexer: states = ( ('comments', 'exclusive'), ('strings', 'exclusive'), - ) reserved = { @@ -79,7 +79,6 @@ class CoolLexer: "STRING": string, } - tokens = [ 'NUMBER', 'TYPEIDENTIFIER', @@ -105,8 +104,6 @@ class CoolLexer: 'DOT', 'AT', 'ERROR' - - ] + list(reserved.values()) # t_EQUALS = r'=' @@ -140,7 +137,6 @@ def build(self, **kwargs): self.lexer.eof= (1,1) self.comment_level = 0 self.string = "" - def t_comments_COMMENTOUT(self, t): r'\*\)' @@ -182,8 +178,6 @@ def t_strings_invalid_new_line(self, t): t.value = f"({line},{column}) - LexicographicError: Unterminated string constant" self.add_line_column(t) return t - - def t_strings_escaped_special_character(self, t): r'\\(b|t|f)' @@ -214,11 +208,6 @@ def t_strings_eof(self, t): self.add_line_column(t) return t - - - - - def t_TYPEIDENTIFIER(self, t): r'[A-Z][a-zA-Z0-9|_]*' l_value = t.value.lower() @@ -279,7 +268,6 @@ def compute_column(self, token): line_start = self.text.rfind('\n', 0, token.lexpos) + 1 return (token.lexpos - line_start) + 1 - def t_LARROW(self, t): r'<-' self.add_line_column(t) @@ -330,14 +318,11 @@ def t_RPAREN(self, t): self.add_line_column(t) return t - - def t_LESS(self, t): r'<' self.add_line_column(t) return t - def t_LCBRA(self, t): r'{' self.add_line_column(t) @@ -363,8 +348,6 @@ def t_COMPLEMENT(self, t): self.add_line_column(t) return t - - def t_COMMA(self, t): r',' self.add_line_column(t) @@ -379,6 +362,7 @@ def t_AT(self, t): r'@' self.add_line_column(t) return t + def t_error(self, t): line = t.lexer.lineno column = self.compute_column(t) @@ -388,12 +372,10 @@ def t_error(self, t): t.value = f"({line},{column}) - LexicographicError: \"{error_text}\"" self.add_line_column(t) return t - - + def t_comments_error(self, t): t.lexer.skip(1) - def tokenize(self, text): self.text = text self.lexer.input(text) @@ -412,4 +394,5 @@ def tokenize(self, text): def add_line_column(self, t): t.row = t.lexer.lineno - t.column = self.compute_column(t) \ No newline at end of file + t.column = self.compute_column(t) + \ No newline at end of file From 8cd5a9bb187a418ce8722950101e4c0177eaa574 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 19:23:59 -0500 Subject: [PATCH 502/520] [structure][cmp] - New module cmp Also some unused files deleted: - nbpackage - languages - ast --- src/core/__init__.py | 2 + src/core/cmp/CoolUtils.py | 6 +- src/core/cmp/__init__.py | 8 ++ src/core/cmp/ast.py | 62 ---------- src/core/cmp/evaluation.py | 4 +- src/core/cmp/functions.py | 6 +- src/core/cmp/grammartools.py | 7 +- src/core/cmp/languages.py | 228 ----------------------------------- src/core/cmp/nbpackage.py | 87 ------------- src/core/cmp/utils.py | 2 +- 10 files changed, 22 insertions(+), 390 deletions(-) create mode 100644 src/core/__init__.py delete mode 100644 src/core/cmp/ast.py delete mode 100644 src/core/cmp/languages.py delete mode 100644 src/core/cmp/nbpackage.py diff --git a/src/core/__init__.py b/src/core/__init__.py new file mode 100644 index 00000000..7ebcadac --- /dev/null +++ b/src/core/__init__.py @@ -0,0 +1,2 @@ +from .visitors import * +from .lexer import CoolLexer diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/CoolUtils.py index ce0b3cf0..f2b4ec82 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/CoolUtils.py @@ -1,6 +1,6 @@ -from core.cmp.pycompiler import Grammar -from core.cmp.functions import LR1Parser -from core.cmp.utils import Token, tokenizer +from .pycompiler import Grammar +from .functions import LR1Parser +from .utils import Token, tokenizer empty_token = Token("", "") empty_token.row, empty_token.column = (0, 0) diff --git a/src/core/cmp/__init__.py b/src/core/cmp/__init__.py index e69de29b..0e3dba0a 100644 --- a/src/core/cmp/__init__.py +++ b/src/core/cmp/__init__.py @@ -0,0 +1,8 @@ +from .automata import * +from .CoolUtils import * +from .evaluation import * +from .functions import * +from .grammartools import * +from .pycompiler import * +from .semantic import * +from .utils import * diff --git a/src/core/cmp/ast.py b/src/core/cmp/ast.py deleted file mode 100644 index 3d5b3b9f..00000000 --- a/src/core/cmp/ast.py +++ /dev/null @@ -1,62 +0,0 @@ -import core.cmp.visitor as visitor - -class Node: - def evaluate(self): - raise NotImplementedError() - -class AtomicNode(Node): - def __init__(self, lex): - self.lex = lex - -class UnaryNode(Node): - def __init__(self, node): - self.node = node - - def evaluate(self): - value = self.node.evaluate() - return self.operate(value) - - @staticmethod - def operate(value): - raise NotImplementedError() - -class BinaryNode(Node): - def __init__(self, left, right): - self.left = left - self.right = right - - def evaluate(self): - lvalue = self.left.evaluate() - rvalue = self.right.evaluate() - return self.operate(lvalue, rvalue) - - @staticmethod - def operate(lvalue, rvalue): - raise NotImplementedError() - -def get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode, ): - - class PrintVisitor(object): - @visitor.on('node') - def visit(self, node, tabs): - pass - - @visitor.when(UnaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__}' - child = self.visit(node.node, tabs + 1) - return f'{ans}\n{child}' - - @visitor.when(BinaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' - left = self.visit(node.left, tabs + 1) - right = self.visit(node.right, tabs + 1) - return f'{ans}\n{left}\n{right}' - - @visitor.when(AtomicNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' - - printer = PrintVisitor() - return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/core/cmp/evaluation.py b/src/core/cmp/evaluation.py index cadd324d..5b3aa6a6 100644 --- a/src/core/cmp/evaluation.py +++ b/src/core/cmp/evaluation.py @@ -1,5 +1,5 @@ -from core.cmp.pycompiler import EOF -from core.cmp.utils import ShiftReduceParser +from .pycompiler import EOF +from .utils import ShiftReduceParser def evaluate_reverse_parse(right_parse, operations, tokens): if not right_parse or not operations or not tokens: diff --git a/src/core/cmp/functions.py b/src/core/cmp/functions.py index 2f5346e9..49ab6320 100644 --- a/src/core/cmp/functions.py +++ b/src/core/cmp/functions.py @@ -1,6 +1,6 @@ -from core.cmp.pycompiler import * -from core.cmp.automata import * -from core.cmp.utils import * +from .pycompiler import * +from .automata import * +from .utils import * def compute_local_first(firsts, alpha): diff --git a/src/core/cmp/grammartools.py b/src/core/cmp/grammartools.py index dfc076dd..78095da9 100644 --- a/src/core/cmp/grammartools.py +++ b/src/core/cmp/grammartools.py @@ -1,6 +1,6 @@ -from core.cmp.pycompiler import * -from core.cmp.automata import * -from core.cmp.utils import * +from .pycompiler import * +from .automata import * +from .utils import * class BadTextFormatException(Exception): """ @@ -439,7 +439,6 @@ def regexp_from_automaton(automaton): return e - class Action(tuple): SHIFT = 'SHIFT' REDUCE = 'REDUCE' diff --git a/src/core/cmp/languages.py b/src/core/cmp/languages.py deleted file mode 100644 index f53cb0ef..00000000 --- a/src/core/cmp/languages.py +++ /dev/null @@ -1,228 +0,0 @@ -from core.cmp.pycompiler import Sentence, Production -from core.cmp.utils import ContainerSet, Token, UnknownToken -from core.cmp.tools.parsing import build_parsing_table, metodo_predictivo_no_recursivo - -class BasicXCool: - def __init__(self, G): - self.G = G - self.fixed_tokens = { lex: Token(lex, G[lex]) for lex in '+ - * / ( )'.split() } - - @property - def firsts(self): - G = self.G - return { - G['+']: ContainerSet(G['+'] , contains_epsilon=False), - G['-']: ContainerSet(G['-'] , contains_epsilon=False), - G['*']: ContainerSet(G['*'] , contains_epsilon=False), - G['/']: ContainerSet(G['/'] , contains_epsilon=False), - G['(']: ContainerSet(G['('] , contains_epsilon=False), - G[')']: ContainerSet(G[')'] , contains_epsilon=False), - G['num']: ContainerSet(G['num'] , contains_epsilon=False), - G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), - G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), - Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), - Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), - G.Epsilon: ContainerSet( contains_epsilon=True), - Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), - Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), - Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), - Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) - } - - @property - def follows(self): - G = self.G - return { - G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['T']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False), - G['F']: ContainerSet(G['-'], G.EOF, G['*'], G['/'], G[')'], G['+'] , contains_epsilon=False), - G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['Y']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False) - } - - @property - def table(self): - G = self.G - return { - ( G['E'], G['num'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['X'], G['+'], ): [ Production(G['X'], Sentence(G['+'], G['T'], G['X'])), ], - ( G['X'], G['-'], ): [ Production(G['X'], Sentence(G['-'], G['T'], G['X'])), ], - ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], - ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], - ( G['T'], G['num'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['Y'], G['*'], ): [ Production(G['Y'], Sentence(G['*'], G['F'], G['Y'])), ], - ( G['Y'], G['/'], ): [ Production(G['Y'], Sentence(G['/'], G['F'], G['Y'])), ], - ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G['-'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G['+'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['F'], G['num'], ): [ Production(G['F'], Sentence(G['num'])), ], - ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['('], G['E'], G[')'])), ] - } - - @property - def tokenizer(self): - G = self.G - fixed_tokens = self.fixed_tokens - - def tokenize_text(text): - tokens = [] - for item in text.split(): - try: - float(item) - token = Token(item, G['num']) - except ValueError: - try: - token = fixed_tokens[item] - except: - token = UnknownToken(item) - tokens.append(token) - eof = Token('$', G.EOF) - tokens.append(eof) - return tokens - - return tokenize_text - -class PowXCool: - def __init__(self, G): - self.G = G - - @property - def firsts(self): - G = self.G - return { - G['+']: ContainerSet(G['+'] , contains_epsilon=False), - G['-']: ContainerSet(G['-'] , contains_epsilon=False), - G['*']: ContainerSet(G['*'] , contains_epsilon=False), - G['/']: ContainerSet(G['/'] , contains_epsilon=False), - G['^']: ContainerSet(G['^'] , contains_epsilon=False), - G['(']: ContainerSet(G['('] , contains_epsilon=False), - G[')']: ContainerSet(G[')'] , contains_epsilon=False), - G['num']: ContainerSet(G['num'] , contains_epsilon=False), - G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['A']: ContainerSet(G['num'], G['('] , contains_epsilon=False), - G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), - G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), - G['Z']: ContainerSet(G['^'] , contains_epsilon=True), - Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), - Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), - G.Epsilon: ContainerSet( contains_epsilon=True), - Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), - Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), - Sentence(G['A'], G['Z']): ContainerSet(G['num'], G['('] , contains_epsilon=False), - Sentence(G['^'], G['F']): ContainerSet(G['^'] , contains_epsilon=False), - Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), - Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) - } - - @property - def follows(self): - G = self.G - return { - G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['T']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['F']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['A']: ContainerSet(G['-'], G['*'], G['/'], G['^'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['Y']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), - G['Z']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False) - } - -class Regex: - def __init__(self, G): - self.G = G - - @property - def firsts(self): - G = self.G - return { - G['|']: ContainerSet(G['|'] , contains_epsilon=False), - G['*']: ContainerSet(G['*'] , contains_epsilon=False), - G['(']: ContainerSet(G['('] , contains_epsilon=False), - G[')']: ContainerSet(G[')'] , contains_epsilon=False), - G['symbol']: ContainerSet(G['symbol'] , contains_epsilon=False), - G['ε']: ContainerSet(G['ε'] , contains_epsilon=False), - G['E']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - G['T']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - G['F']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - G['A']: ContainerSet(G['ε'], G['symbol'], G['('] , contains_epsilon=False), - G['X']: ContainerSet(G['|'] , contains_epsilon=True), - G['Y']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=True), - G['Z']: ContainerSet(G['*'] , contains_epsilon=True), - Sentence(G['T'], G['X']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['|'], G['E']): ContainerSet(G['|'] , contains_epsilon=False), - G.Epsilon: ContainerSet( contains_epsilon=True), - Sentence(G['F'], G['Y']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['T']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['A'], G['Z']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), - Sentence(G['*']): ContainerSet(G['*'] , contains_epsilon=False), - Sentence(G['symbol']): ContainerSet(G['symbol'] , contains_epsilon=False), - Sentence(G['ε']): ContainerSet(G['ε'] , contains_epsilon=False), - Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) - } - - @property - def follows(self): - G = self.G - return { - G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['T']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), - G['F']: ContainerSet(G[')'], G.EOF, G['symbol'], G['|'], G['ε'], G['('] , contains_epsilon=False), - G['A']: ContainerSet(G.EOF, G['|'], G['*'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False), - G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), - G['Y']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), - G['Z']: ContainerSet(G.EOF, G['|'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False) - } - - @property - def table(self): - G = self.G - return { - ( G['E'], G['symbol'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['E'], G['ε'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], - ( G['X'], G['|'], ): [ Production(G['X'], Sentence(G['|'], G['E'])), ], - ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], - ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], - ( G['T'], G['symbol'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['T'], G['ε'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], - ( G['Y'], G['symbol'], ): [ Production(G['Y'], Sentence(G['T'])), ], - ( G['Y'], G['ε'], ): [ Production(G['Y'], Sentence(G['T'])), ], - ( G['Y'], G['('], ): [ Production(G['Y'], Sentence(G['T'])), ], - ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], - ( G['Y'], G['|'], ): [ Production(G['Y'], G.Epsilon), ], - ( G['F'], G['symbol'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], - ( G['F'], G['ε'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], - ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], - ( G['Z'], G['*'], ): [ Production(G['Z'], Sentence(G['*'])), ], - ( G['Z'], G.EOF, ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['|'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['('], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G[')'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['symbol'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['Z'], G['ε'], ): [ Production(G['Z'], G.Epsilon), ], - ( G['A'], G['symbol'], ): [ Production(G['A'], Sentence(G['symbol'])), ], - ( G['A'], G['ε'], ): [ Production(G['A'], Sentence(G['ε'])), ], - ( G['A'], G['('], ): [ Production(G['A'], Sentence(G['('], G['E'], G[')'])), ] - } - - @property - def parser(self): - firsts = self.firsts - follows = self.follows - M = build_parsing_table(self.G, firsts, follows) - parser = metodo_predictivo_no_recursivo(self.G, M) - return parser \ No newline at end of file diff --git a/src/core/cmp/nbpackage.py b/src/core/cmp/nbpackage.py deleted file mode 100644 index e89c62ad..00000000 --- a/src/core/cmp/nbpackage.py +++ /dev/null @@ -1,87 +0,0 @@ -import io, os, sys, types - -from IPython import get_ipython -from nbformat import read -from IPython.core.interactiveshell import InteractiveShell - -def find_notebook(fullname, path=None): - """find a notebook, given its fully qualified name and an optional path - - This turns "foo.bar" into "foo/bar.ipynb" - and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar - does not exist. - """ - name = fullname.rsplit('.', 1)[-1] - if not path: - path = [''] - for d in path: - nb_path = os.path.join(d, name + ".ipynb") - if os.path.isfile(nb_path): - return nb_path - # let import Notebook_Name find "Notebook Name.ipynb" - nb_path = nb_path.replace("_", " ") - if os.path.isfile(nb_path): - return nb_path - -class NotebookLoader(object): - """Module Loader for Jupyter Notebooks""" - def __init__(self, path=None): - self.shell = InteractiveShell.instance() - self.path = path - - def load_module(self, fullname): - """import a notebook as a module""" - path = find_notebook(fullname, self.path) - - print ("importing Jupyter notebook from %s" % path) - - # load the notebook object - with io.open(path, 'r', encoding='utf-8') as f: - nb = read(f, 4) - - - # create the module and add it to sys.modules - # if name in sys.modules: - # return sys.modules[name] - mod = types.ModuleType(fullname) - mod.__file__ = path - mod.__loader__ = self - mod.__dict__['get_ipython'] = get_ipython - sys.modules[fullname] = mod - - # extra work to ensure that magics that would affect the user_ns - # actually affect the notebook module's ns - save_user_ns = self.shell.user_ns - self.shell.user_ns = mod.__dict__ - - try: - for cell in nb.cells: - if cell.cell_type == 'code': - # transform the input to executable Python - code = self.shell.input_transformer_manager.transform_cell(cell.source) - # run the code in themodule - exec(code, mod.__dict__) - finally: - self.shell.user_ns = save_user_ns - return mod - -class NotebookFinder(object): - """Module finder that locates Jupyter Notebooks""" - def __init__(self): - self.loaders = {} - - def find_module(self, fullname, path=None): - nb_path = find_notebook(fullname, path) - if not nb_path: - return - - key = path - if path: - # lists aren't hashable - key = os.path.sep.join(path) - - if key not in self.loaders: - self.loaders[key] = NotebookLoader(path) - return self.loaders[key] - -sys.meta_path.append(NotebookFinder()) \ No newline at end of file diff --git a/src/core/cmp/utils.py b/src/core/cmp/utils.py index 8e240e94..bd15b246 100644 --- a/src/core/cmp/utils.py +++ b/src/core/cmp/utils.py @@ -1,4 +1,4 @@ -from core.cmp.pycompiler import Production, Sentence, Symbol, EOF, Epsilon +from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon class ContainerSet: def __init__(self, *values, contains_epsilon=False): From 40301ce0032c27ec3de1e79b6d7f5d9588f43302 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 19:24:18 -0500 Subject: [PATCH 503/520] [main] - Update main imports --- src/main.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main.py b/src/main.py index 4f0e4da5..dd8d3140 100644 --- a/src/main.py +++ b/src/main.py @@ -1,17 +1,10 @@ from sys import exit -from pprint import pprint -from core.cmp.visitors import * -from core.cmp.lex import CoolLexer -from core.cmp.evaluation import evaluate_reverse_parse -from core.cmp.CoolUtils import tokenize_text, CoolParser -from core.cmp.lex import CoolLexer -from core.cmp.evaluation import * -from core.cmp.cil import get_formatter -from pprint import pprint -from core.cmp.cool_to_cil import COOLToCILVisitor -from core.cmp.cil_to_mips import CILToMIPSVisitor -from core.cmp.mips import PrintVisitor +from core.cmp import evaluate_reverse_parse, CoolParser + +from core import CoolLexer +from core import TypeBuilder, TypeCollector, TypeVerifier, InferenceVisitor, COOLToCILVisitor, CILToMIPSVisitor +from core import PrintVisitor, FormatVisitor, get_formatter def main(args): # Read code @@ -102,7 +95,7 @@ def main(args): with open(out_file, 'w') as f: f.write(mips_code) - with open("./core/cmp/mips_lib.asm") as f2: + with open("./core/visitors/mips/mips_lib.asm") as f2: f.write("".join(f2.readlines())) exit(0) From f81a33e27968c89b4943a0aebb00b3df65a4a8c0 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 30 Nov 2020 19:36:53 -0500 Subject: [PATCH 504/520] Add Lexer and MIPS part to report --- src/core/cmp/lex.py | 20 --------------- src/doc/Report.md | 59 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/core/cmp/lex.py b/src/core/cmp/lex.py index a7ccca41..615237de 100644 --- a/src/core/cmp/lex.py +++ b/src/core/cmp/lex.py @@ -109,26 +109,6 @@ class CoolLexer: ] + list(reserved.values()) - # t_EQUALS = r'=' - # t_PLUS = r'\+' - # t_MINUS = r'-' - # t_TIMES = r'\*' - # t_DIVIDE = r'/' - # t_LPAREN = r'\(' - # t_RPAREN = r'\)' - # t_LESS = r'<' - # t_LESSEQ = r'<=' - # t_LCBRA = r'{' - # t_RCBRA = r'}' - # t_COLON = r':' - # t_SEMICOLON = r';' - # t_COMPLEMENT = r'~' - # t_RARROW = r'=>' - # t_LARROW = r'<-' - # t_COMMA = r',' - # t_DOT = r'\.' - # t_AT = r'@' - t_ignore = ' \t\f\r\t\v' t_comments_ignore = '' diff --git a/src/doc/Report.md b/src/doc/Report.md index f251430a..a33b74a3 100644 --- a/src/doc/Report.md +++ b/src/doc/Report.md @@ -1,7 +1,7 @@ # Datos Generales ## Autores - Miguel Tenorio Potrony -- Mauricio Lázaro Perdomo Cortéz +- Mauricio Lázaro Perdomo Cortés - Lázaro Raúl Iglesias Vera ## Sobre el proyecto @@ -33,7 +33,14 @@ Como se puede apreciar en la etapa #6 del proceso, el chequeo e inferencia de ti **Pendig** ## Tokenización -**Pendig** +Para el proceso de tokenización se utilizó el paquete PLY, se creó un un lexer que consta de tres estados: + + - INITIAL + - comments + - strings + +Para cada uno de estos estados se definieron las expresiones regulares que representan cada uno de los tokens posibles y se +manejan otras variables que conforman el estado del lexer, como la línea actual. ## Parsing Para el proceso de parsing se utilizó el parser LR1 y la gramática de Cool que fueron implementados para el proyecto de 3er año sobre chequeo de tipos. @@ -198,7 +205,53 @@ Esta fase surge dado que tras el proceso de inferencia puede haber ocurrido un e pending ## Traducción a MIPS -pending +En la fase de generación de código `MIPS` enfrentamos tres problemas fundamentales: + + - Estructura de los objetos en memoria. + - Definición de tipos en memoria. + - Elección de registros. + +### Estructura de los objetos en memoria. +Determinar el modelo que seguirían nuestros objetos en la memoria fue un paso fundamental para la toma de múltiples decisiones tanto en la generación de código `CIL` como `MIPS`. Los objetos en memoria siguen el siguiente modelo: + +```| Tipo | Tamaño | Tabla de dispatch | -- Atributos -- | Marca de objeto |``` + - Tipo: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un entero e indica el tipo del objeto. + - Tamaño: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un enter e indica el tamaño en `palabras` del objeto. + - Tabla de dispatch: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como una dirección de memoria e indica el inicio de la tabla de dispatch del objeto. La tabla de dispatch del objeto es un segmento de la memoria donde interpretamos cada `palabra` como la dirección a uno de los métodos del objeto. + - Atributos: Esta sección tiene tamaño **N** `palabras` donde **N** será la cantidad de atributos que conforman el objeto, cada una de las `palabras` que conforman esta sección representa el valor de un atributo del objeto. + - Marca de objeto: Esta sección tiene tamaño 1 `palabra`, es un valor usado para marcar que esta zona de la memoria corresponde a un objeto, se añadió con el objetivo de hacer menos propenso a fallos la tarea de identificar objetos en memoria en el `Garbage Collector`. + +### Definición de tipos en memoria. +Un tipo está representado por tres estructuras en la memoria: + - Una dirección a una cadena alfanumérica que representa el nombre del tipo. + - Un prototipo que es una especie de plantilla que utilizamos en la creación de los objetos. Cuando se crea un objeto este prototipo es copiado al segmento de memoria asignado al objeto. Un prototipo es un objeto válido por lo que tiene exactamente la misma estructura explicada anteriormente. El prototipo es también nuestra solución a los valores por defecto de los objetos. + - Una tabla de dispatch que como explicamos anteriormente contiene las direcciones de los métodos del objeto. +Existe una tabla de prototipos (nombres) donde podemos encontrar el prototipo (nombre) de un tipo específico utilizando como índice el valor que representa al tipo. + +### Elección de registros. +La elección de registros fue un proceso que decidimos optimizar para disminuir la utilización de las operaciones `lw` y `sw` en `MIPS` que como sabemos añaden una demora considerable a nuestros programas por el tiempo que tarda en realizarse un operación de escritura o lectura en la memoria. +El proceso de elección de registros se realiza para cada función y consta de los siguientes pasos: + - Separación del código en bloques básicos: + + Para obtener los bloques básicos primero se hace un recorrido por las instrucciones de la función marcando los líderes. Son considerados líderes las instrucciones de tipo `Label` y las instrucciones que tengan como predecesor un instrucción de tipo `Goto` o `Goto if`. Luego de tener marcado los líderes se obtienen los bloques que serán los conjuntos de instrucciones consecutivas que comienzan con un líder y terminan con la primera instrucción que sea predecesor de un líder (notar que un bloque puede estar formado por una sola instrucción). + + - Creación del grafo de flujo: + + Este es un grafo dirigido que indica los caminos posibles entre los bloques básicos, su elaboración es bastante sencilla, si la última instrucción de un bloque es un `Goto` entonces se añadirá una arista desde este bloque hacia el bloque iniciado por la instrucción `Label` a la que hace referencia el `Goto`; si la última instrucción es de tipo `Goto if` entonces se añadirán dos aristas una hacia el bloque que comienza con la instrucción `Label` a la que se hace referencia y otra hacia el bloque que comienza con la instrucción siguiente en la función; en el caso de que la última instrucción sea de cualquier otro tipo se colocará una sola arista desde el bloque actual hacia el bloque que comienza con la instrucción siguiente en la función. + + - Análisis de vida de las variables: + + En este procedimiento computaremos cuatro conjuntos para cada instrucción **I**: `succ`, `gen`, `kill`, `in` y `out`. `succ` contiene las instrucciones que se pueden ejecutar inmediatamente después de la instrucción **I**; `gen` contiene las variables de las que se necesita el valor en la instrucción **I**; `kill` contiene las variables a las que se les asigna un valor en la instrucción **I**; `in` contiene las variables que están pueden estar vivas al llegar a la instrucción **I** y `out` contiene las variables que pueden estar vivas luego de ejecutada la instrucción **I**. + + - Creación del grafo de interferencia: + + Los vértices de este grafo serán las variables que se utilizan en la función y existirá una arista entre los vértices **v** y **y** si las variables que representan esos nodos interfieren. Dos variables interfieren si existe alguna instrucción **i** tal que **x** pertenezca al `kill` de **i** y **y** pertenezca al `out` de **i**. + + - Asignación de registros: + + Contando con el grafo de interferencia asignaremos registros a las variables de forma tal que dos variables que interfieran no se les asigne el mismo registro, esto puede verse como el problema de colorear un grafo con **N** colores siendo **N** la cantidad de registros que tenemos. Es conocido que este problema es *NP* por lo que para asignar los registros usaremos una heurística muy sencilla que consistirá en lo siguiente: + + Primero iremos eliminando del grafo y colocando en una pila cada nodo que tenga menos de N vecinos, notamos que todos estos elementos pueden ser coloreados sin problemas. Si en algún momento no existe nigún nodo con menos de N vecinos se tomará un nodo al azar; este proceso terminará cuando no nos queden nodos en el grafo. Luego iremos sacando cada nodo de la pila y le asignaremos un registro que no esté usado por ninguno de los nodos que eran vecinos de este en el momento en que se eliminó del grafo, en el caso de que existan más de un nodo posible le asignaremos el menor, en caso de que no exista nodo posible la variable no tendrá registro y su valor permanecerá en la memoria. # Ejecución Para ejectur el proyecto se necesita tener instalado python y el conjunto de dependencias listado en [requirements.txt](../../requirements.txt). From 454667c3b5780e582254028264a589ea514800ad Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 30 Nov 2020 20:04:11 -0500 Subject: [PATCH 505/520] Auto stash before merge of "project_report" and "origin/project_report_mips" --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 9df82533..503a54ee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,7 +8,7 @@ ORG_NAME := 2kodevs PROJECT_NAME := CoolCompiler APP_VERSION := v0.1 APP_DESCRIPTION := $(ORG_NAME) - $(PROJECT_NAME)$(APP_VERSION) -DEVELOPERS := Lázaro Raúl Iglesias Vera, Miguel Tenorio Potrony, Mauricio Lázaro Perdomo Cortéz +DEVELOPERS := Lázaro Raúl Iglesias Vera, Miguel Tenorio Potrony, Mauricio Lázaro Perdomo Cortés COPYRIGHT := Copyright © 2020: $(DEVELOPERS) TEST_DIR := core/cmp/Stuff/tests/ TEST := From a872b7309b97efeadc3778ead5712435fe34b62b Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 21:51:48 -0500 Subject: [PATCH 506/520] [structure][cool] - New Module cool --- src/core/cmp/cool/__init__.py | 2 + src/core/cmp/cool/ast.py | 178 +++++++++++ .../cmp/{CoolUtils.py => cool/grammar.py} | 289 +----------------- src/core/cmp/cool/parser.py | 4 + 4 files changed, 186 insertions(+), 287 deletions(-) create mode 100644 src/core/cmp/cool/__init__.py create mode 100644 src/core/cmp/cool/ast.py rename src/core/cmp/{CoolUtils.py => cool/grammar.py} (51%) create mode 100644 src/core/cmp/cool/parser.py diff --git a/src/core/cmp/cool/__init__.py b/src/core/cmp/cool/__init__.py new file mode 100644 index 00000000..4d235e06 --- /dev/null +++ b/src/core/cmp/cool/__init__.py @@ -0,0 +1,2 @@ +from .parser import CoolParser +from .grammar import CoolGrammar diff --git a/src/core/cmp/cool/ast.py b/src/core/cmp/cool/ast.py new file mode 100644 index 00000000..8dcfc41b --- /dev/null +++ b/src/core/cmp/cool/ast.py @@ -0,0 +1,178 @@ +from ..utils import Token, empty_token + +# AST Classes +class Node: + pass + +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations + +class DeclarationNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None): + self.id = idx.lex + self.tid = idx + self.features = features + if not parent: + parent = Token("Object", "type") + parent.row = idx.row + parent.column = idx.column + self.parent = parent.lex + self.tparent = parent + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr=None, arrow=empty_token): + self.id = idx.lex + self.tid = idx + self.type = typex.lex + self.ttype = typex + self.arrow = arrow + self.expr = expr + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body): + self.id = idx.lex + self.tid = idx + self.params = params + self.type = return_type.lex + self.ttype = return_type + self.body = body + +class ExpressionNode(Node): + pass + +class IfThenElseNode(ExpressionNode): + def __init__(self, condition, if_body, if_token, else_body): + self.token = if_token + self.condition = condition + self.if_body = if_body + self.else_body = else_body + +class WhileLoopNode(ExpressionNode): + def __init__(self, condition, body, token): + self.token = token + self.condition = condition + self.body = body + +class BlockNode(ExpressionNode): + def __init__(self, exprs): + self.exprs = exprs + +class LetInNode(ExpressionNode): + def __init__(self, let_body, in_body): + self.let_body = let_body + self.in_body = in_body + +class CaseOfNode(ExpressionNode): + def __init__(self, expr, branches): + self.expr = expr + self.branches = branches + +class CaseExpressionNode(AttrDeclarationNode): + pass + +class LetAttributeNode(AttrDeclarationNode): + pass + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr): + self.id = idx.lex + self.tid = idx + self.expr= expr + +class UnaryNode(ExpressionNode): + def __init__(self, expr, symbol): + self.symbol = symbol + self.expr = expr + +class NotNode(UnaryNode): + pass + +class BinaryNode(ExpressionNode): + def __init__(self, left, right, symbol): + self.symbol = symbol + self.left = left + self.right = right + +class ComparisonNode(BinaryNode): + pass + +class LessEqualNode(ComparisonNode): + pass + +class LessNode(ComparisonNode): + pass + +class EqualNode(ComparisonNode): + pass + +class ArithmeticNode(BinaryNode): + pass + +class PlusNode(ArithmeticNode): + pass + +class MinusNode(ArithmeticNode): + pass + +class StarNode(ArithmeticNode): + pass + +class DivNode(ArithmeticNode): + pass + +class IsVoidNode(UnaryNode): + pass + +class ComplementNode(UnaryNode): + pass + +class FunctionCallNode(ExpressionNode): + def __init__(self, obj, idx, args, typex=empty_token): + self.obj = obj + self.id = idx.lex + self.tid = idx + self.args = args + self.type = typex.lex + self.ttype = typex + +class MemberCallNode(ExpressionNode): + def __init__(self, idx, args): + self.id = idx.lex + self.tid = idx + self.args = args + +class NewNode(ExpressionNode): + def __init__(self, typex): + self.type = typex.lex + self.ttype = typex + +class AtomicNode(ExpressionNode): + def __init__(self, token): + self.lex = token.lex + self.token = token + +class IntegerNode(AtomicNode): + pass + +class IdNode(AtomicNode): + pass + +class StringNode(AtomicNode): + pass + +class BoolNode(AtomicNode): + pass + +class Param(Node): + def __init__(self, tid, ttype): + self.tid = tid + self.ttype = ttype + self.type = ttype.lex + + def __iter__(self): + yield self.tid.lex + yield self.type diff --git a/src/core/cmp/CoolUtils.py b/src/core/cmp/cool/grammar.py similarity index 51% rename from src/core/cmp/CoolUtils.py rename to src/core/cmp/cool/grammar.py index f2b4ec82..838c60e9 100644 --- a/src/core/cmp/CoolUtils.py +++ b/src/core/cmp/cool/grammar.py @@ -1,188 +1,6 @@ -from .pycompiler import Grammar -from .functions import LR1Parser -from .utils import Token, tokenizer +from ..pycompiler import Grammar +from .ast import * -empty_token = Token("", "") -empty_token.row, empty_token.column = (0, 0) - -# AST Classes -class Node: - pass - -class ProgramNode(Node): - def __init__(self, declarations): - self.declarations = declarations - -class DeclarationNode(Node): - pass - -class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent=None): - self.id = idx.lex - self.tid = idx - self.features = features - if not parent: - parent = Token("Object", "type") - parent.row = idx.row - parent.column = idx.column - self.parent = parent.lex - self.tparent = parent - -class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expr=None, arrow=empty_token): - self.id = idx.lex - self.tid = idx - self.type = typex.lex - self.ttype = typex - self.arrow = arrow - self.expr = expr - -class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx, params, return_type, body): - self.id = idx.lex - self.tid = idx - self.params = params - self.type = return_type.lex - self.ttype = return_type - self.body = body - -class ExpressionNode(Node): - pass - -class IfThenElseNode(ExpressionNode): - def __init__(self, condition, if_body, if_token, else_body): - self.token = if_token - self.condition = condition - self.if_body = if_body - self.else_body = else_body - -class WhileLoopNode(ExpressionNode): - def __init__(self, condition, body, token): - self.token = token - self.condition = condition - self.body = body - -class BlockNode(ExpressionNode): - def __init__(self, exprs): - self.exprs = exprs - -class LetInNode(ExpressionNode): - def __init__(self, let_body, in_body): - self.let_body = let_body - self.in_body = in_body - -class CaseOfNode(ExpressionNode): - def __init__(self, expr, branches): - self.expr = expr - self.branches = branches - -class CaseExpressionNode(AttrDeclarationNode): - pass - -class LetAttributeNode(AttrDeclarationNode): - pass - -class AssignNode(ExpressionNode): - def __init__(self, idx, expr): - self.id = idx.lex - self.tid = idx - self.expr= expr - -class UnaryNode(ExpressionNode): - def __init__(self, expr, symbol): - self.symbol = symbol - self.expr = expr - -class NotNode(UnaryNode): - pass - -class BinaryNode(ExpressionNode): - def __init__(self, left, right, symbol): - self.symbol = symbol - self.left = left - self.right = right - -class ComparisonNode(BinaryNode): - pass - -class LessEqualNode(ComparisonNode): - pass - -class LessNode(ComparisonNode): - pass - -class EqualNode(ComparisonNode): - pass - -class ArithmeticNode(BinaryNode): - pass - -class PlusNode(ArithmeticNode): - pass - -class MinusNode(ArithmeticNode): - pass - -class StarNode(ArithmeticNode): - pass - -class DivNode(ArithmeticNode): - pass - -class IsVoidNode(UnaryNode): - pass - -class ComplementNode(UnaryNode): - pass - -class FunctionCallNode(ExpressionNode): - def __init__(self, obj, idx, args, typex=empty_token): - self.obj = obj - self.id = idx.lex - self.tid = idx - self.args = args - self.type = typex.lex - self.ttype = typex - -class MemberCallNode(ExpressionNode): - def __init__(self, idx, args): - self.id = idx.lex - self.tid = idx - self.args = args - -class NewNode(ExpressionNode): - def __init__(self, typex): - self.type = typex.lex - self.ttype = typex - -class AtomicNode(ExpressionNode): - def __init__(self, token): - self.lex = token.lex - self.token = token - -class IntegerNode(AtomicNode): - pass - -class IdNode(AtomicNode): - pass - -class StringNode(AtomicNode): - pass - -class BoolNode(AtomicNode): - pass - -class Param(Node): - def __init__(self, tid, ttype): - self.tid = tid - self.ttype = ttype - self.type = ttype.lex - - def __iter__(self): - yield self.tid.lex - yield self.type - - # Grammar CoolGrammar = Grammar() @@ -338,106 +156,3 @@ def __iter__(self): atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[1], s[6]) atom %= whilex + expr + loop + expr + pool, lambda h, s: WhileLoopNode(s[2], s[4], s[1]) atom %= case + expr + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) - -# Parser -CoolParser = LR1Parser(CoolGrammar) - -# Tokenizer - -fixed_tokens = { t.Name: Token(t.Name, t) for t in CoolGrammar.terminals if t not in { idx, integer, typex, string, boolx}} -booleans = ['false', 'true'] -previous = [new, colon, classx, inherits] - -@tokenizer(CoolGrammar, fixed_tokens) -def tokenize_text(token): - lex = token.lex - try: - float(lex) - return token.transform_to(integer) - except ValueError: - if lex[0].islower() and lex.lower() in booleans: - return token.transform_to(boolx) - if lex.count('"') >= 2 and lex[0] == '"' and lex[-1] == '"': - return token.transform_to(string) - return token.transform_to(idx) - -deprecated_tokenize_text = tokenize_text - -def tokenize_text(text): - tokens = deprecated_tokenize_text(text) - if tokens: - for i in range(1, len(tokens)): - if tokens[i].token_type == idx and tokens[i-1].token_type in previous: - tokens[i] = Token(tokens[i].lex, typex) - return tokens - -def pprint_tokens(tokens): - indent = 0 - pending = [] - for token in tokens: - pending.append(token) - if token.token_type in { ocur, ccur, semi }: - if token.token_type == ccur: - indent -= 1 - print(' '*indent + ' '.join(str(t.token_type) for t in pending)) - pending.clear() - if token.token_type == ocur: - indent += 1 - print(' '.join([str(t.token_type) for t in pending])) - -def format_tokens(tokens): - indent = 0 - pending = [] - txt = '' - for token in tokens: - pending.append(token) - if token.token_type in { ocur, ccur, semi }: - if token.token_type == ccur: - indent -= 1 - txt += ' '*indent + ' '.join(str(t.token_type) for t in pending) + '\n' - pending.clear() - if token.token_type == ocur: - indent += 1 - txt += ' '.join([str(t.token_type) for t in pending]) - return txt - -# Example text -#//TODO: [stdevMauricio1802] Add COOL codes here -_text1 = ''' -class Main { - main ( console : IO ) : AUTO_TYPE { - let x : AUTO_TYPE <- 3 + 2 in { - case a of { - x : Int => 3 ; - p : string => "OK" ; - } esac ; - } ; - } ; -} ; -''' -_text2 = ''' -class Main inherits IO { - main() : String { - foo(42) - }; - - foo(i : Int) : String { - if i = 0 then "" else - (let next : Int <- i / 10 in - foo(next).concat(foo(i - next * 10)) - ) - fi - }; -}; -''' - -_text3 = ''' -class B : A { - c : int ; - def f ( d : int , a : A ) : void { - let f : int = 8 ; - let c = new A ( ) . suma ( 5 , f ) ; - c ; - } -} -''' diff --git a/src/core/cmp/cool/parser.py b/src/core/cmp/cool/parser.py new file mode 100644 index 00000000..d76ea4b4 --- /dev/null +++ b/src/core/cmp/cool/parser.py @@ -0,0 +1,4 @@ +from ..parser import LR1Parser +from .grammar import CoolGrammar + +CoolParser = LR1Parser(CoolGrammar) From f39218aead394d096194167f18051e35288f1253 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 21:52:20 -0500 Subject: [PATCH 507/520] [structure][parser] - New module parser --- src/core/cmp/parser/LR1.py | 30 ++++++++++++ src/core/cmp/parser/__init__.py | 2 + src/core/cmp/parser/shift_reduce.py | 49 +++++++++++++++++++ .../cmp/{functions.py => parser/utils.py} | 42 ++-------------- 4 files changed, 84 insertions(+), 39 deletions(-) create mode 100644 src/core/cmp/parser/LR1.py create mode 100644 src/core/cmp/parser/__init__.py create mode 100644 src/core/cmp/parser/shift_reduce.py rename src/core/cmp/{functions.py => parser/utils.py} (80%) diff --git a/src/core/cmp/parser/LR1.py b/src/core/cmp/parser/LR1.py new file mode 100644 index 00000000..d5b4078d --- /dev/null +++ b/src/core/cmp/parser/LR1.py @@ -0,0 +1,30 @@ +from .shift_reduce import ShiftReduceParser +from .utils import build_LR1_automaton, upd_table + +class LR1Parser(ShiftReduceParser): + def _build_parsing_table(self): + self.ok = True + G = self.Augmented = self.G.AugmentedGrammar(True) + + automaton = self.automaton = build_LR1_automaton(G) + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + node.tag = f'I{i}' + + for node in automaton: + idx = node.idx + for item in node.state: + if item.IsReduceItem: + prod = item.production + if prod.Left == G.startSymbol: + self.ok &= upd_table(self.action, idx, G.EOF, (ShiftReduceParser.OK, '')) + else: + for lookahead in item.lookaheads: + self.ok &= upd_table(self.action, idx, lookahead, (ShiftReduceParser.REDUCE, prod)) + else: + next_symbol = item.NextSymbol + if next_symbol.IsTerminal: + self.ok &= upd_table(self.action, idx, next_symbol, (ShiftReduceParser.SHIFT, node[next_symbol.Name][0].idx)) + else: + self.ok &= upd_table(self.goto, idx, next_symbol, node[next_symbol.Name][0].idx) diff --git a/src/core/cmp/parser/__init__.py b/src/core/cmp/parser/__init__.py new file mode 100644 index 00000000..0dd59a64 --- /dev/null +++ b/src/core/cmp/parser/__init__.py @@ -0,0 +1,2 @@ +from .LR1 import LR1Parser +from .shift_reduce import ShiftReduceParser diff --git a/src/core/cmp/parser/shift_reduce.py b/src/core/cmp/parser/shift_reduce.py new file mode 100644 index 00000000..03acaaaa --- /dev/null +++ b/src/core/cmp/parser/shift_reduce.py @@ -0,0 +1,49 @@ +class ShiftReduceParser: + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + + def __init__(self, G, verbose=False): + self.G = G + self.verbose = verbose + self.action = {} + self.goto = {} + self._build_parsing_table() + + def _build_parsing_table(self): + raise NotImplementedError() + + def __call__(self, w, get_shift_reduce=False): + stack = [0] + cursor = 0 + output = [] + operations = [] + + while True: + state = stack[-1] + lookahead = w[cursor].token_type + if self.verbose: print(stack, w[cursor:]) + + # Your code here!!! (Detect error) + if state not in self.action or lookahead not in self.action[state]: + return None, (True, w[cursor]) #//TODO: Build the correct error using `w[cursor]` + + action, tag = list(self.action[state][lookahead])[0] + # Your code here!!! (Shift case) + if action is ShiftReduceParser.SHIFT: + operations.append(ShiftReduceParser.SHIFT) + stack.append(tag) + cursor += 1 + # Your code here!!! (Reduce case) + elif action is ShiftReduceParser.REDUCE: + operations.append(ShiftReduceParser.REDUCE) + if len(tag.Right): + stack = stack[:-len(tag.Right)] + stack.append(list(self.goto[stack[-1]][tag.Left])[0]) + output.append(tag) + # Your code here!!! (OK case) + elif action is ShiftReduceParser.OK: + return (output if not get_shift_reduce else(output,operations)), (False, None) + # Your code here!!! (Invalid case) + else: + raise ValueError diff --git a/src/core/cmp/functions.py b/src/core/cmp/parser/utils.py similarity index 80% rename from src/core/cmp/functions.py rename to src/core/cmp/parser/utils.py index 49ab6320..ba414acc 100644 --- a/src/core/cmp/functions.py +++ b/src/core/cmp/parser/utils.py @@ -1,7 +1,6 @@ -from .pycompiler import * -from .automata import * -from .utils import * - +from ..pycompiler import Item +from ..automata import State +from ..utils import ContainerSet def compute_local_first(firsts, alpha): first_alpha = ContainerSet() @@ -228,38 +227,3 @@ def build_LR1_automaton(G): automaton.set_formatter(lambda x: "") return automaton - - -class LR1Parser(ShiftReduceParser): - def _build_parsing_table(self): - self.ok = True - G = self.Augmented = self.G.AugmentedGrammar(True) - - automaton = self.automaton = build_LR1_automaton(G) - for i, node in enumerate(automaton): - if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') - node.idx = i - node.tag = f'I{i}' - - for node in automaton: - idx = node.idx - for item in node.state: - if item.IsReduceItem: - prod = item.production - if prod.Left == G.startSymbol: - self.ok &= upd_table(self.action, idx, G.EOF, (ShiftReduceParser.OK, '')) - else: - for lookahead in item.lookaheads: - self.ok &= upd_table(self.action, idx, lookahead, (ShiftReduceParser.REDUCE, prod)) - else: - next_symbol = item.NextSymbol - if next_symbol.IsTerminal: - self.ok &= upd_table(self.action, idx, next_symbol, (ShiftReduceParser.SHIFT, node[next_symbol.Name][0].idx)) - else: - self.ok &= upd_table(self.goto, idx, next_symbol, node[next_symbol.Name][0].idx) - -def get_token(node): - for attr in ['tid', 'token', 'ttype', 'symbol']: - if hasattr(node, attr): - return getattr(node, attr) - raise Exception(f'{node} has no token') From 64b7b6c5126c391dac1703713a26ce104f1bb244 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 21:54:33 -0500 Subject: [PATCH 508/520] [structure][cmp] - Update cmp module New submodules: - cool - parser Deleted: - grammartools - functions Cleaned: - pycompiler - utils - automata --- src/core/cmp/__init__.py | 10 +- src/core/cmp/automata.py | 9 - src/core/cmp/evaluation.py | 2 +- src/core/cmp/grammartools.py | 752 ----------------------------------- src/core/cmp/pycompiler.py | 1 - src/core/cmp/utils.py | 208 +--------- 6 files changed, 10 insertions(+), 972 deletions(-) delete mode 100644 src/core/cmp/grammartools.py diff --git a/src/core/cmp/__init__.py b/src/core/cmp/__init__.py index 0e3dba0a..8ce243d5 100644 --- a/src/core/cmp/__init__.py +++ b/src/core/cmp/__init__.py @@ -1,8 +1,8 @@ -from .automata import * -from .CoolUtils import * -from .evaluation import * -from .functions import * -from .grammartools import * +from .evaluation import evaluate_reverse_parse +from .cool import ast as cool_ast +from .automata import State from .pycompiler import * from .semantic import * +from .parser import * from .utils import * +from .cool import * diff --git a/src/core/cmp/automata.py b/src/core/cmp/automata.py index f42b6f00..02b23f80 100644 --- a/src/core/cmp/automata.py +++ b/src/core/cmp/automata.py @@ -196,12 +196,3 @@ def _repr_svg_(self): def write_to(self, fname): return self.graph().write_svg(fname) - -def multiline_formatter(state): - return '\n'.join(str(item) for item in state) - -def lr0_formatter(state): - try: - return '\n'.join(str(item)[:-4] for item in state) - except TypeError: - return str(state)[:-4] \ No newline at end of file diff --git a/src/core/cmp/evaluation.py b/src/core/cmp/evaluation.py index 5b3aa6a6..8866b381 100644 --- a/src/core/cmp/evaluation.py +++ b/src/core/cmp/evaluation.py @@ -1,5 +1,5 @@ from .pycompiler import EOF -from .utils import ShiftReduceParser +from .parser import ShiftReduceParser def evaluate_reverse_parse(right_parse, operations, tokens): if not right_parse or not operations or not tokens: diff --git a/src/core/cmp/grammartools.py b/src/core/cmp/grammartools.py deleted file mode 100644 index 78095da9..00000000 --- a/src/core/cmp/grammartools.py +++ /dev/null @@ -1,752 +0,0 @@ -from .pycompiler import * -from .automata import * -from .utils import * - -class BadTextFormatException(Exception): - """ - Class for wrong format in texts - used to convert to a grammar - """ - pass - -class GrammarTools: - @staticmethod - def grammar_from_text(text: str): - """ - Transform a string in this format: - - S --> A B - A --> a A | epsilon - B --> b B | epsilon - - to a Grammar object - """ - terminals, nonTerminals, productions = [], [], [] - - try: - lines = text.split('\n') - - for line in lines: - head, bodies = line.split('-->') - - head, = head.split() - nonTerminals.append(head) - - for body in bodies.split('|'): - productions.append({'Head': head, 'Body': list(body.split())}) - terminals.extend(productions[-1]['Body']) - - except: - raise BadTextFormatException() - - sterminals, snonTerminals = set(terminals).difference(nonTerminals + ['epsilon']), set(nonTerminals) - - data = json.dumps({ - 'NonTerminals': [nt for nt in nonTerminals if nt in snonTerminals and snonTerminals.discard(nt) is None], - 'Terminals': [t for t in terminals if t in sterminals and sterminals.discard(t) is None], - 'Productions': productions - }) - - return Grammar.from_json(data) - - @staticmethod - def is_not_null(G: Grammar): - """ - Check if the given grammar genere - the empty language - """ - accepted = set() - visited = set() - - def dfs(symbol): - visited.add(symbol) - acc = False - - if isinstance(symbol, Terminal): - acc = True - else: - for production in symbol.productions: - for s in production.Right: - if s not in visited: - dfs(s) - acc |= all(s in accepted for s in production.Right) - - if acc: - accepted.add(symbol) - - dfs(G.startSymbol) - - return G.startSymbol in accepted - - @staticmethod - def clone_grammar(G: Grammar): - NG = Grammar() - symbols = {nonTerminal: NG.NonTerminal(nonTerminal.Name, nonTerminal == G.startSymbol) for nonTerminal in G.nonTerminals} - symbols.update({terminal: NG.Terminal(terminal.Name) for terminal in G.terminals}) - for p in G.Productions: - x = symbols[p.Left] - if isinstance(p.Right, Epsilon): - x %= NG.Epsilon - else: - x %= Sentence(*[symbols[symbol] for symbol in p.Right]) - - return NG - - @staticmethod - def remove_left_recursion(G: Grammar): - """ - Transform G for remove inmediate - left recursion - """ - G.Productions = [] - - for nonTerminal in G.nonTerminals: - recursion = [p.Right[1:] for p in nonTerminal.productions if len(p.Right) > 0 and p.Right[0] == nonTerminal] - no_recursion = [p.Right for p in nonTerminal.productions if len(p.Right) == 0 or p.Right[0] != nonTerminal] - - if len(recursion) > 0: - nonTerminal.productions = [] - aux = G.NonTerminal(f'{nonTerminal.Name}0') - - for p in no_recursion: - nonTerminal %= Sentence(*p) + aux - - for p in recursion: - aux %= Sentence(*p) + aux - - aux %= G.Epsilon - else: - G.Productions.extend(nonTerminal.productions) - - @staticmethod - def factorize_grammar(G: Grammar): - """ - Transform G for remove common - prefixes - """ - G.Productions = [] - - pending = G.nonTerminals.copy() - - while pending: - nonTerminal = pending.pop() - - productions = nonTerminal.productions.copy() - nonTerminal.productions = [] - - visited = set() - - for i, p in enumerate(productions): - if p not in visited: - n = len(p.Right) - same_prefix = [] - - for p2 in productions[i:]: - m = 0 - - for s1, s2 in zip(p.Right, p2.Right): - if s1 == s2: - m += 1 - else: - break - - if m > 0: - same_prefix.append(p2) - n = min(n, m) - - if len(same_prefix) > 1: - visited.update(same_prefix) - aux = G.NonTerminal(f'{nonTerminal.Name}{i + 1}') - - nonTerminal %= Sentence(*p.Right[:n]) + aux - for p2 in same_prefix: - if n == len(p2.Right): - aux %= G.Epsilon - else: - aux %= Sentence(*p2.Right[n:]) - - pending.append(aux) - else: - visited.add(p) - nonTerminal %= p.Right - - @staticmethod - def compute_local_first(firsts, alpha): - """ - Computes First(alpha), given First(Vt) and First(Vn) - alpha in (Vt U Vn)* - """ - first_alpha = ContainerSet() - - try: - alpha_is_epsilon = alpha.IsEpsilon - except: - alpha_is_epsilon = False - - # alpha == epsilon ? First(alpha) = { epsilon } - if alpha_is_epsilon: - first_alpha.set_epsilon() - - # alpha = X1 ... XN - # First(Xi) subset of First(alpha) - # epsilon in First(X1)...First(Xi) ? First(Xi+1) subset of First(X) & First(alpha) - # epsilon in First(X1)...First(XN) ? epsilon in First(X) & First(alpha) - else: - for symbol in alpha: - first_symbol = firsts[symbol] - first_alpha.update(first_symbol) - if not first_symbol.contains_epsilon: - break - else: - first_alpha.set_epsilon() - - return first_alpha - - @staticmethod - def compute_firsts(G: Grammar): - """ - Computes First(Vt) U First(Vn) U First(alpha) - P: X -> alpha - """ - firsts = {} - change = True - - # init First(Vt) - for terminal in G.terminals: - firsts[terminal] = ContainerSet(terminal) - - # init First(Vn) - for nonterminal in G.nonTerminals: - firsts[nonterminal] = ContainerSet() - - while change: - change = False - - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - - # get current First(X) - first_X = firsts[X] - - # init First(alpha) - try: - first_alpha = firsts[alpha] - except: - first_alpha = firsts[alpha] = ContainerSet() - - # CurrentFirst(alpha)??? - local_first = GrammarTools.compute_local_first(firsts, alpha) - - # update First(X) and First(alpha) from CurrentFirst(alpha) - change |= first_alpha.hard_update(local_first) - change |= first_X.hard_update(local_first) - - # First(Vt) + First(Vt) + First(RightSides) - return firsts - - @staticmethod - def compute_follows(G: Grammar, firsts): - """ - Computes Follow(Vn) - """ - follows = { } - change = True - - local_firsts = {} - - # init Follow(Vn) - for nonterminal in G.nonTerminals: - follows[nonterminal] = ContainerSet() - follows[G.startSymbol] = ContainerSet(G.EOF) - - while change: - change = False - - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - - follow_X = follows[X] - - # X -> zeta Y beta - # First(beta) - { epsilon } subset of Follow(Y) - # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) - for i, symbol in enumerate(alpha): - if symbol.IsNonTerminal: - follow_symbol = follows[symbol] - beta = alpha[i + 1:] - try: - first_beta = local_firsts[beta] - except KeyError: - first_beta = local_firsts[beta] = GrammarTools.compute_local_first(firsts, beta) - change |= follow_symbol.update(first_beta) - if first_beta.contains_epsilon or len(beta) == 0: - change |= follow_symbol.update(follow_X) - - return follows - - @staticmethod - def _register(table, state, symbol, value): - if state not in table: - table[state] = dict() - - row = table[state] - - if symbol not in row: - row[symbol] = set() - - cell = row[symbol] - - cell.add(value) - - return len(cell) == 1 - - @staticmethod - def build_ll1_table(G: Grammar, firsts, follows): - """ - Computes Parsing Table for a - Parser LL(1) - """ - # init parsing table - M = {} - is_ll1 = True - - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - - # working with symbols on First(alpha) ... - first_alpha = firsts[alpha] - for symbol in first_alpha: - is_ll1 &= GrammarTools._register(M, X, symbol, production) - - # working with epsilon... - if first_alpha.contains_epsilon: - for symbol in follows[X]: - is_ll1 &= GrammarTools._register(M, X, symbol, production) - - # parsing table is ready!!! - return M, is_ll1 - - @staticmethod - def build_automaton(G: Grammar): - """ - Build the finite automaton for - a regular grammar - """ - states = { nonTerminal: State(nonTerminal.Name) for nonTerminal in G.nonTerminals } - final_state = State('F\'', True) - - start_in_right = False - epsilon_production = False - - for nonTerminal in G.nonTerminals: - for production in nonTerminal.productions: - right = production.Right - - # Start Symbol produces epsilon - if isinstance(right, Epsilon) and nonTerminal == G.startSymbol: - epsilon_production = True - continue - - start_in_right |= G.startSymbol in right - n = len(right) - - # X --> w - if n == 1 and isinstance(right[0], Terminal): - states[nonTerminal].add_transition(right[0].Name, final_state) - continue - - # X --> w Y - if n == 2 and isinstance(right[0], Terminal) and isinstance(right[1], NonTerminal): - states[nonTerminal].add_transition(right[0].Name, states[right[1]]) - continue - - return states[G.startSymbol], False - - states[G.startSymbol].final = epsilon_production - return states[G.startSymbol], not (start_in_right and epsilon_production) - - epsilon = 'ε' - - @staticmethod - def regex_union(regex, other): - if regex is None: - return other - - if other is None: - return regex - - if regex == other: - return regex - - return f'({regex}|{other})' - - @staticmethod - def regex_concat(regex, other): - if regex is None or other is None: - return None - - if regex is GrammarTools.epsilon: - return other - - if other is GrammarTools.epsilon: - return regex - - return f'{regex}{other}' - - @staticmethod - def regex_star(regex): - if regex is None or regex is GrammarTools.epsilon: - return regex - - return f'({regex})*' - - @staticmethod - def regexp_from_automaton(automaton): - """ - Build the regular expresion for - a NFA - """ - states = list(automaton) - states_index = {state: i for i, state in enumerate(states)} - n = len(states) - - R = [[[None for k in range(n + 1)] for j in range(n)] for i in range(n)] - - for i in range(n): - R[i][i][0] = GrammarTools.epsilon - - for i, state in enumerate(states): - for symbol, transitions in state.transitions.items(): - for state2 in transitions: - j = states_index[state2] - R[i][j][0] = GrammarTools.regex_union(R[i][j][0], symbol) - - for k in range(n): - for i in range(n): - for j in range(n): - R[i][j][k + 1] = GrammarTools.regex_union(R[i][j][k], GrammarTools.regex_concat(R[i][k][k], GrammarTools.regex_concat(GrammarTools.regex_star(R[k][k][k]), R[k][j][k]))) - - e = None - for i in range(n): - if states[i].final: - e = GrammarTools.regex_union(e, R[0][i][n]) - - return e - -class Action(tuple): - SHIFT = 'SHIFT' - REDUCE = 'REDUCE' - OK = 'OK' - - def __str__(self): - try: - action, tag = self - return f"{'S' if action == Action.SHIFT else 'OK' if action == Action.OK else ''}{tag}" - except: - return str(tuple(self)) - - __repr__ = __str__ - -class ShiftReduceParser: - def __init__(self, G, verbose=False): - self.G = G - self.verbose = verbose - self.action = {} - self.goto = {} - self._build_parsing_table() - - def _build_parsing_table(self): - raise NotImplementedError() - - def __call__(self, w): - stack = [ 0 ] - cursor = 0 - output = [] - - while True: - state = stack[-1] - lookahead = w[cursor] - if self.verbose: print(stack, w[cursor:]) - - # (Detect error) - try: - print(type(state), type(lookahead)) - action, tag = list(self.action[state][lookahead])[0] - # (Shift case) - if action == Action.SHIFT: - stack.append(tag) - cursor += 1 - # (Reduce case) - elif action == Action.REDUCE: - for _ in range(len(tag.Right)): stack.pop() - stack.append(self.goto[stack[-1]][tag.Left]) - output.append(tag) - # (OK case) - elif action == Action.OK: - return output - # (Invalid case) - else: - assert False, 'Must be something wrong!' - except KeyError: - raise Exception('Aborting parsing, item is not viable.') - -class SLR1Parser(ShiftReduceParser): - def build_LR0_automaton(self): - G = self.augmentedG = self.G.AugmentedGrammar(True) - - start_production = G.startSymbol.productions[0] - start_item = Item(start_production, 0) - - automaton = State(start_item, True) - - pending = [ start_item ] - visited = { start_item: automaton } - - while pending: - current_item = pending.pop() - if current_item.IsReduceItem: - continue - - # (Decide which transitions to add) - next_symbol = current_item.NextSymbol - - next_item = current_item.NextItem() - if not next_item in visited: - pending.append(next_item) - visited[next_item] = State(next_item, True) - - if next_symbol.IsNonTerminal: - for prod in next_symbol.productions: - next_item = Item(prod, 0) - if not next_item in visited: - pending.append(next_item) - visited[next_item] = State(next_item, True) - - current_state = visited[current_item] - - # (Add the decided transitions) - current_state.add_transition(next_symbol.Name, visited[current_item.NextItem()]) - - if next_symbol.IsNonTerminal: - for prod in next_symbol.productions: - current_state.add_epsilon_transition(visited[Item(prod, 0)]) - - self.automaton = automaton.to_deterministic() - - def _build_parsing_table(self): - self.is_slr1 = True - self.build_LR0_automaton() - - firsts = GrammarTools.compute_firsts(self.augmentedG) - follows = GrammarTools.compute_follows(self.augmentedG, firsts) - - for i, node in enumerate(self.automaton): - if self.verbose: print(i, node) - node.idx = i - node.tag = f'I{i}' - - for node in self.automaton: - idx = node.idx - for state in node.state: - item = state.state - - # - Fill `self.Action` and `self.Goto` according to `item`) - # - Feel free to use `self._register(...)`) - if item.IsReduceItem: - prod = item.production - if prod.Left == self.augmentedG.startSymbol: - self.is_slr1 &= GrammarTools._register(self.action, idx, self.augmentedG.EOF, - Action((Action.OK, ''))) - else: - for symbol in follows[prod.Left]: - self.is_slr1 &= GrammarTools._register(self.action, idx, symbol, - Action((Action.REDUCE, prod))) - else: - next_symbol = item.NextSymbol - if next_symbol.IsTerminal: - self.is_slr1 &= GrammarTools._register(self.action, idx, next_symbol, - Action((Action.SHIFT, node[next_symbol.Name][0].idx))) - else: - self.is_slr1 &= GrammarTools._register(self.goto, idx, next_symbol, - node[next_symbol.Name][0].idx) - -class LR1Parser(ShiftReduceParser): - @staticmethod - def expand(item, firsts): - next_symbol = item.NextSymbol - if next_symbol is None or not next_symbol.IsNonTerminal: - return [] - - lookaheads = ContainerSet() - # (Compute lookahead for child items) - for preview in item.Preview(): - lookaheads.hard_update(GrammarTools.compute_local_first(firsts, preview)) - - assert not lookaheads.contains_epsilon - # (Build and return child items) - return [Item(prod, 0, lookaheads) for prod in next_symbol.productions] - - @staticmethod - def compress(items): - centers = {} - - for item in items: - center = item.Center() - try: - lookaheads = centers[center] - except KeyError: - centers[center] = lookaheads = set() - lookaheads.update(item.lookaheads) - - return { Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items() } - - @staticmethod - def closure_lr1(items, firsts): - closure = ContainerSet(*items) - - changed = True - while changed: - changed = False - - new_items = ContainerSet() - for item in closure: - new_items.extend(LR1Parser.expand(item, firsts)) - - changed = closure.update(new_items) - - return LR1Parser.compress(closure) - - @staticmethod - def goto_lr1(items, symbol, firsts=None, just_kernel=False): - assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' - items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) - return items if just_kernel else LR1Parser.closure_lr1(items, firsts) - - - def build_LR1_automaton(self): - G = self.augmentedG = self.G.AugmentedGrammar(True) - - firsts = GrammarTools.compute_firsts(G) - firsts[G.EOF] = ContainerSet(G.EOF) - - start_production = G.startSymbol.productions[0] - start_item = Item(start_production, 0, lookaheads=(G.EOF,)) - start = frozenset([start_item]) - - closure = LR1Parser.closure_lr1(start, firsts) - automaton = State(frozenset(closure), True) - - pending = [ start ] - visited = { start: automaton } - - while pending: - current = pending.pop() - current_state = visited[current] - - for symbol in G.terminals + G.nonTerminals: - # (Get/Build `next_state`) - kernels = LR1Parser.goto_lr1(current_state.state, symbol, just_kernel=True) - - if not kernels: - continue - - try: - next_state = visited[kernels] - except KeyError: - pending.append(kernels) - visited[pending[-1]] = next_state = State(frozenset(LR1Parser.goto_lr1(current_state.state, symbol, firsts)), True) - - current_state.add_transition(symbol.Name, next_state) - - self.automaton = automaton - - def _build_parsing_table(self): - self.is_lr1 = True - self.build_LR1_automaton() - - for i, node in enumerate(self.automaton): - if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') - node.idx = i - node.tag = f'I{i}' - - for node in self.automaton: - idx = node.idx - for item in node.state: - # - Fill `self.Action` and `self.Goto` according to `item`) - # - Feel free to use `self._register(...)`) - if item.IsReduceItem: - prod = item.production - if prod.Left == self.augmentedG.startSymbol: - self.is_lr1 &= GrammarTools._register(self.action, idx, self.augmentedG.EOF, - Action((Action.OK, ''))) - else: - for lookahead in item.lookaheads: - self.is_lr1 &= GrammarTools._register(self.action, idx, lookahead, - Action((Action.REDUCE, prod))) - else: - next_symbol = item.NextSymbol - if next_symbol.IsTerminal: - self.is_lr1 &= GrammarTools._register(self.action, idx, next_symbol, - Action((Action.SHIFT, node[next_symbol.Name][0].idx))) - else: - self.is_lr1 &= GrammarTools._register(self.goto, idx, next_symbol, - node[next_symbol.Name][0].idx) - pass - -class LALR1Parser(LR1Parser): - @staticmethod - def mergue_items_lookaheads(items, others): - if len(items) != len(others): - return False - - new_lookaheads = [] - for item in items: - for item2 in others: - if item.Center() == item2.Center(): - new_lookaheads.append(item2.lookaheads) - break - else: - return False - - for item, new_lookahead in zip(items, new_lookaheads): - item.lookaheads = item.lookaheads.union(new_lookahead) - - return True - - def build_LR1_automaton(self): - super().build_LR1_automaton() - - states = list(self.automaton) - new_states = [] - visited = {} - - for i, state in enumerate(states): - if state not in visited: - # creates items - items = [item.Center() for item in state.state] - - # check for states with same center - for state2 in states[i:]: - if LALR1Parser.mergue_items_lookaheads(items, state2.state): - visited[state2] = len(new_states) - - # add new state - new_states.append(State(frozenset(items), True)) - - # making transitions - for state in states: - new_state = new_states[visited[state]] - for symbol, transitions in state.transitions.items(): - for state2 in transitions: - new_state2 = new_states[visited[state2]] - # check if the transition already exists - if symbol not in new_state.transitions or new_state2 not in new_state.transitions[symbol]: - new_state.add_transition(symbol, new_state2) - - self.automaton = new_states[0] diff --git a/src/core/cmp/pycompiler.py b/src/core/cmp/pycompiler.py index 3ef02dee..8d885f12 100644 --- a/src/core/cmp/pycompiler.py +++ b/src/core/cmp/pycompiler.py @@ -184,7 +184,6 @@ def __or__(self, other): if isinstance(other, Symbol): return self | Sentence(other) - class Epsilon(Terminal, Sentence): def __init__(self, grammar): diff --git a/src/core/cmp/utils.py b/src/core/cmp/utils.py index bd15b246..efd6b884 100644 --- a/src/core/cmp/utils.py +++ b/src/core/cmp/utils.py @@ -58,51 +58,6 @@ def __eq__(self, other): return self.set == other return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon - -def inspect(item, grammar_name='G', mapper=None): - try: - return mapper[item] - except (TypeError, KeyError ): - if isinstance(item, dict): - items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) - return f'{{\n {items} \n}}' - elif isinstance(item, ContainerSet): - args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' - return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' - elif isinstance(item, EOF): - return f'{grammar_name}.EOF' - elif isinstance(item, Epsilon): - return f'{grammar_name}.Epsilon' - elif isinstance(item, Symbol): - return f"G['{item.Name}']" - elif isinstance(item, Sentence): - items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) - return f'Sentence({items})' - elif isinstance(item, Production): - left = inspect(item.Left, grammar_name, mapper) - right = inspect(item.Right, grammar_name, mapper) - return f'Production({left}, {right})' - elif isinstance(item, tuple) or isinstance(item, list): - ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') - return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' - else: - raise ValueError(f'Invalid: {item}') - -def pprint(item, header=""): - if header: - print(header) - - if isinstance(item, dict): - for key, value in item.items(): - print(f'{key} ---> {value}') - elif isinstance(item, list): - print('[') - for x in item: - print(f' {repr(x)}') - print(']') - else: - print(item) - class Token: """ Basic token class. @@ -128,165 +83,7 @@ def __repr__(self): @property def is_valid(self): return True - -class UnknownToken(Token): - def __init__(self, lex): - Token.__init__(self, lex, None) - - def transform_to(self, token_type): - return Token(self.lex, token_type) - - @property - def is_valid(self): - return False - -def tokenizer(G, fixed_tokens): - def decorate(func): - def tokenize_text(text): - tokens = [] - for lex in text.split(): - try: - token = fixed_tokens[lex] - except KeyError: - token = UnknownToken(lex) - try: - token = func(token) - except TypeError: - pass - tokens.append(token) - tokens.append(Token('$', G.EOF)) - return tokens - - if hasattr(func, '__call__'): - return tokenize_text - elif isinstance(func, str): - return tokenize_text(func) - else: - raise TypeError('Argument must be "str" or a callable object.') - return decorate - -class DisjointSet: - def __init__(self, *items): - self.nodes = { x: DisjointNode(x) for x in items } - - def merge(self, items): - items = (self.nodes[x] for x in items) - try: - head, *others = items - for other in others: - head.merge(other) - except ValueError: - pass - - @property - def representatives(self): - return { n.representative for n in self.nodes.values() } - - @property - def groups(self): - return [[n for n in self.nodes.values() if n.representative == r] for r in self.representatives] - - def __len__(self): - return len(self.representatives) - - def __getitem__(self, item): - return self.nodes[item] - - def __str__(self): - return str(self.groups) - - def __repr__(self): - return str(self) - -class DisjointNode: - def __init__(self, value): - self.value = value - self.parent = self - - @property - def representative(self): - if self.parent != self: - self.parent = self.parent.representative - return self.parent - - def merge(self, other): - other.representative.parent = self.representative - - def __str__(self): - return str(self.value) - - def __repr__(self): - return str(self) - - -class ShiftReduceParser: - SHIFT = 'SHIFT' - REDUCE = 'REDUCE' - OK = 'OK' - - def __init__(self, G, verbose=False): - self.G = G - self.verbose = verbose - self.action = {} - self.goto = {} - self._build_parsing_table() - - def _build_parsing_table(self): - raise NotImplementedError() - - def __call__(self, w, get_shift_reduce=False): - stack = [0] - cursor = 0 - output = [] - operations = [] - - while True: - state = stack[-1] - lookahead = w[cursor].token_type - if self.verbose: print(stack, w[cursor:]) - - # Your code here!!! (Detect error) - if state not in self.action or lookahead not in self.action[state]: - return None, (True, w[cursor]) #//TODO: Build the correct error using `w[cursor]` - - action, tag = list(self.action[state][lookahead])[0] - # Your code here!!! (Shift case) - if action is ShiftReduceParser.SHIFT: - operations.append(ShiftReduceParser.SHIFT) - stack.append(tag) - cursor += 1 - # Your code here!!! (Reduce case) - elif action is ShiftReduceParser.REDUCE: - operations.append(ShiftReduceParser.REDUCE) - if len(tag.Right): - stack = stack[:-len(tag.Right)] - stack.append(list(self.goto[stack[-1]][tag.Left])[0]) - output.append(tag) - # Your code here!!! (OK case) - elif action is ShiftReduceParser.OK: - return (output if not get_shift_reduce else(output,operations)), (False, None) - # Your code here!!! (Invalid case) - else: - raise ValueError - -class CountDict(): - def __init__(self): - self._dict = {} - self._count = 0 - - def add(self, key, value): - try: - oldv = self._dict[key] - except: - self._count += 1 - self._dict[key] = value - - def get(self, key): - return self._dict[key] - - def __len__(self): - return self._count - + class InferenceSets: def __init__(self): self.D = [] @@ -296,3 +93,6 @@ def add(self, new_type, conforms=True): cur = [self.S, self.D][conforms] cur.append(new_type) return self + +empty_token = Token("", "") +empty_token.row, empty_token.column = (0, 0) From 476276f1392716d7dadb09876d04ee1cde59ff47 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 21:57:09 -0500 Subject: [PATCH 509/520] [all] - All imports referring to Cool updated --- src/core/lexer/lex.py | 90 ++++++++++----------- src/core/visitors/cil/cool_to_cil.py | 3 +- src/core/visitors/cil/utils.py | 5 ++ src/core/visitors/type_check/ast_printer.py | 2 +- src/core/visitors/type_check/builder.py | 2 +- src/core/visitors/type_check/checker.py | 2 +- src/core/visitors/type_check/collector.py | 2 +- src/core/visitors/type_check/inferencer.py | 5 +- src/core/visitors/type_check/verifier.py | 2 +- 9 files changed, 59 insertions(+), 54 deletions(-) create mode 100644 src/core/visitors/cil/utils.py diff --git a/src/core/lexer/lex.py b/src/core/lexer/lex.py index d611eeac..a340908b 100644 --- a/src/core/lexer/lex.py +++ b/src/core/lexer/lex.py @@ -2,7 +2,7 @@ import re from ..cmp import Token -from ..cmp.CoolUtils import * +from ..cmp.cool import grammar as G class CoolLexer: @@ -34,49 +34,49 @@ class CoolLexer: } tokenType = { - "CLASS": classx, - "ELSE": elsex, - "FI": fi, - "IF": ifx, - "IN": inx, - "INHERITS": inherits, - "ISVOID": isvoid, - "LET": let, - "LOOP": loop, - "POOL": pool, - "THEN": then, - "WHILE": whilex, - "CASE": case, - "ESAC": esac, - "NEW": new, - "OF": of, - "NOT": notx, - "OBJECTIDENTIFIER": idx, - "TYPEIDENTIFIER": typex, - "LCBRA": ocur, - "RCBRA": ccur, - "LPAREN": opar, - "RPAREN": cpar, - "COLON": colon, - "SEMICOLON": semi, - "NUMBER": integer, - "eof": eof, - "PLUS": plus, - "MINUS": minus, - "DIVIDE": div, - "TIMES": star, - "LESS": less, - "LESSEQ": leq, - "EQUALS": equal, - "TRUE": boolx, - "FALSE": boolx, - "COMPLEMENT": compl, - "RARROW": rarrow, - "LARROW": larrow, - "COMMA": comma, - "DOT": dot, - "AT": at, - "STRING": string, + "CLASS": G.classx, + "ELSE": G.elsex, + "FI": G.fi, + "IF": G.ifx, + "IN": G.inx, + "INHERITS": G.inherits, + "ISVOID": G.isvoid, + "LET": G.let, + "LOOP": G.loop, + "POOL": G.pool, + "THEN": G.then, + "WHILE": G.whilex, + "CASE": G.case, + "ESAC": G.esac, + "NEW": G.new, + "OF": G.of, + "NOT": G.notx, + "OBJECTIDENTIFIER": G.idx, + "TYPEIDENTIFIER": G.typex, + "LCBRA": G.ocur, + "RCBRA": G.ccur, + "LPAREN": G.opar, + "RPAREN": G.cpar, + "COLON": G.colon, + "SEMICOLON": G.semi, + "NUMBER": G.integer, + "eof": G.eof, + "PLUS": G.plus, + "MINUS": G.minus, + "DIVIDE": G.div, + "TIMES": G.star, + "LESS": G.less, + "LESSEQ": G.leq, + "EQUALS": G.equal, + "TRUE": G.boolx, + "FALSE": G.boolx, + "COMPLEMENT": G.compl, + "RARROW": G.rarrow, + "LARROW": G.larrow, + "COMMA": G.comma, + "DOT": G.dot, + "AT": G.at, + "STRING": G.string, } tokens = [ @@ -388,7 +388,7 @@ def tokenize(self, text): tokens.append(Token(token.value, self.tokenType[token.type])) tokens[-1].row = token.row tokens[-1].column = token.column - EOF = Token('$', eof) + EOF = Token('$', G.eof) EOF.row, EOF.column = self.lexer.eof return tokens + [EOF] diff --git a/src/core/visitors/cil/cool_to_cil.py b/src/core/visitors/cil/cool_to_cil.py index b7826480..ecfce903 100644 --- a/src/core/visitors/cil/cool_to_cil.py +++ b/src/core/visitors/cil/cool_to_cil.py @@ -1,6 +1,7 @@ from ..cil import cil +from .utils import get_token from ...visitors import visitor -from ...cmp import CoolUtils as cool, VariableInfo, get_token +from ...cmp import cool_ast as cool, VariableInfo class BaseCOOLToCILVisitor: def __init__(self, context): diff --git a/src/core/visitors/cil/utils.py b/src/core/visitors/cil/utils.py new file mode 100644 index 00000000..09208171 --- /dev/null +++ b/src/core/visitors/cil/utils.py @@ -0,0 +1,5 @@ +def get_token(node): + for attr in ['tid', 'token', 'ttype', 'symbol']: + if hasattr(node, attr): + return getattr(node, attr) + raise Exception(f'{node} has no token') diff --git a/src/core/visitors/type_check/ast_printer.py b/src/core/visitors/type_check/ast_printer.py index ccba3697..14fd8444 100644 --- a/src/core/visitors/type_check/ast_printer.py +++ b/src/core/visitors/type_check/ast_printer.py @@ -1,5 +1,5 @@ from ...visitors import visitor -from ...cmp import CoolUtils as cool +from ...cmp import cool_ast as cool #AST Printer class FormatVisitor: diff --git a/src/core/visitors/type_check/builder.py b/src/core/visitors/type_check/builder.py index 81416b26..034b4321 100644 --- a/src/core/visitors/type_check/builder.py +++ b/src/core/visitors/type_check/builder.py @@ -1,6 +1,6 @@ from .utils import * from ...visitors import visitor -from ...cmp import CoolUtils as cool, SemanticError, empty_token, ErrorType +from ...cmp import cool_ast as cool, SemanticError, empty_token, ErrorType # Type Builder class TypeBuilder: diff --git a/src/core/visitors/type_check/checker.py b/src/core/visitors/type_check/checker.py index 229472bb..3d8fe285 100644 --- a/src/core/visitors/type_check/checker.py +++ b/src/core/visitors/type_check/checker.py @@ -1,6 +1,6 @@ from .utils import * from ...visitors import visitor -from ...cmp import CoolUtils as cool +from ...cmp import cool_ast as cool from ...cmp import IntType, BoolType, StringType, SemanticError, ErrorType, SelfType, Scope # Type Checker diff --git a/src/core/visitors/type_check/collector.py b/src/core/visitors/type_check/collector.py index 4075a93a..de6e0cf3 100644 --- a/src/core/visitors/type_check/collector.py +++ b/src/core/visitors/type_check/collector.py @@ -1,6 +1,6 @@ from .utils import * from ...visitors import visitor -from ...cmp import CoolUtils as cool, empty_token, Context, SemanticError +from ...cmp import cool_ast as cool, empty_token, Context, SemanticError from ...cmp import IntType, StringType, BoolType, IOType, VoidType, AutoType, SelfType def define_built_in_types(context): diff --git a/src/core/visitors/type_check/inferencer.py b/src/core/visitors/type_check/inferencer.py index caec0f3b..23b97ea7 100644 --- a/src/core/visitors/type_check/inferencer.py +++ b/src/core/visitors/type_check/inferencer.py @@ -1,9 +1,8 @@ from .utils import * from ...visitors import visitor from .checker import TypeChecker -from ...cmp import CoolUtils as cool -from ...cmp.utils import InferenceSets -from ...cmp import SemanticError, AutoType, SelfType +from ...cmp import cool_ast as cool +from ...cmp import SemanticError, AutoType, SelfType, InferenceSets # Type Inference Visitor class InferenceVisitor(TypeChecker): diff --git a/src/core/visitors/type_check/verifier.py b/src/core/visitors/type_check/verifier.py index 0e2e5561..c63bd884 100644 --- a/src/core/visitors/type_check/verifier.py +++ b/src/core/visitors/type_check/verifier.py @@ -1,6 +1,6 @@ from .utils import * from ...visitors import visitor -from ...cmp import CoolUtils as cool, SemanticError +from ...cmp import cool_ast as cool, SemanticError # Type Verifier class TypeVerifier: From 30e7921e17c8077c8a247347c00a807da2728125 Mon Sep 17 00:00:00 2001 From: Lazaro Raul Date: Mon, 30 Nov 2020 21:57:35 -0500 Subject: [PATCH 510/520] [main] - Delete the old projects main --- src/older_main.py | 137 ---------------------------------------------- 1 file changed, 137 deletions(-) delete mode 100644 src/older_main.py diff --git a/src/older_main.py b/src/older_main.py deleted file mode 100644 index 22ba5541..00000000 --- a/src/older_main.py +++ /dev/null @@ -1,137 +0,0 @@ -import eel -import logging -from time import sleep -from core.cmp.visitors import * -from core.cmp.evaluation import * -from core.cmp.lex import CoolLexer -from core.cmp.cil import get_formatter -from core.cmp.CoolUtils import CoolGrammar -import sys -from os.path import basename - - -def build_AST(G, text): - data, err = [], False - ast = None - txt = '================== TOKENS =====================\n' - tokens = tokenize_text(text) - txt += format_tokens(tokens) - data.append(txt) - txt = '=================== PARSE =====================\n' - #print(parser([t.token_type for t in tokens], get_shift_reduce=True)) - try: - parse, operations = CoolParser([t.token_type for t in tokens], get_shift_reduce=True) - except: - err = True - txt += 'Impossible to parse\n' - #print('\n'.join(repr(x) for x in parse)) - data.append(txt) - if not err: - txt = '==================== AST ======================\n' - ast = evaluate_reverse_parse(parse, operations, tokens) - formatter = FormatVisitor() - tree = formatter.visit(ast) - txt += str(tree) - data.append(txt) - return ast, '\n\n'.join(data) - -def error_formatter(errors): - txt = 'Errors: [\n' - for error in errors: - txt += f'\t{error}\n' - txt += ']\n' - return txt - -def run_pipeline(G, text): - data, err = [], False - ast, txt = build_AST(G, text) - errors = context = scope = None - data.append(txt) - if ast: - txt = '============== COLLECTING TYPES ===============\n' - errors = [] - collector = TypeCollector(errors) - collector.visit(ast) - context = collector.context - if len(errors): - txt += error_formatter(errors) - err = True - txt += 'Context:\n' - txt += str(context) - data.append(txt) - errors.clear() - txt = '=============== BUILDING TYPES ================\n' - builder = TypeBuilder(context, errors) - builder.visit(ast) - if len(errors): - txt += error_formatter(errors) - err = True - errors.clear() - data.append(txt) - txt = '=============== CHECKING TYPES ================\n' - checker = TypeChecker(context, errors) - scope = checker.visit(ast) - if len(errors): - txt += error_formatter(errors) - err = True - errors.clear() - data.append(txt) - txt = '=============== INFERING TYPES ================\n' - inferer = InferenceVisitor(context, errors) - while True: - old = scope.count_auto() - scope = inferer.visit(ast) - if old == scope.count_auto(): - break - errors.clear() - scope = inferer.visit(ast) - if len(errors): - txt += error_formatter(errors) - err = True - errors.clear() - txt += 'Context:\n' - txt += str(context) + '\n' - formatter = ComputedVisitor() - if not err: - tree = formatter.visit(ast) - txt += 'AST:\n' + str(tree) - data.append(txt) - txt = '============= TRANSFORMING TO CIL =============\n' - cool_to_cil = COOLToCILVisitor(context) - cil_ast = cool_to_cil.visit(ast, scope) - formatter = get_formatter() - ast_cil = formatter(cil_ast) - txt += 'AST-CIL:\n' + str(ast_cil) - data.append(txt) - return '\n\n'.join(data) - -@eel.expose -def compile(text): - sleep(2) - return run_pipeline(CoolGrammar, text) - -def main(): - eel.init('web') - - eel_options = {'port': 8045} - eel.start('index.html', size=(1000, 860), options=eel_options, block=False) - - while True: - eel.sleep(0.1) - - -if __name__ == '__main__': - # main() - input_file = sys.argv[1] - text = "" - with open(input_file, 'r') as f: - text = f.read() - # lex = CoolLexer() - # lex.build() - # toks, errors = lex.tokenize(text) - - # for token in toks: - # if token.token_type == "ERROR": - # print(token.lex) - print(run_pipeline(CoolGrammar, text)) - \ No newline at end of file From a4e789923b3ad8b778af3b7bf2b12532553a7122 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 30 Nov 2020 22:49:01 -0500 Subject: [PATCH 511/520] Add JumpRegisterAndLinkNode case to UsedRegistersFinder --- src/core/visitors/mips/cil_to_mips.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/visitors/mips/cil_to_mips.py b/src/core/visitors/mips/cil_to_mips.py index a688573b..c46fda9e 100644 --- a/src/core/visitors/mips/cil_to_mips.py +++ b/src/core/visitors/mips/cil_to_mips.py @@ -1356,6 +1356,10 @@ def visit(self, node): @visitor.when(mips.JumpAndLinkNode) def visit(self, node): self.used_registers.add(mips.RA_REG) + + @visitor.when(mips.JumpRegisterAndLinkNode) + def visit(self, node): + self.used_registers.add(mips.RA_REG) #Change Name From dc208f67eacce0ee13f1318e497c317e4e1a2d76 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 30 Nov 2020 22:52:02 -0500 Subject: [PATCH 512/520] Fix error in out_in_compute --- src/core/visitors/mips/cil_to_mips.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/visitors/mips/cil_to_mips.py b/src/core/visitors/mips/cil_to_mips.py index c46fda9e..c7f0ecf6 100644 --- a/src/core/visitors/mips/cil_to_mips.py +++ b/src/core/visitors/mips/cil_to_mips.py @@ -1493,9 +1493,7 @@ def out_in_compute(suc, gk): next_in_out = [(set(), set()) for _ in range(n_instructions)] def add(set1, set2): - new = not set1 == set2 - set1.update(set2) - return new + return not set2.issubset(set1) changed = True while changed: @@ -1503,13 +1501,18 @@ def add(set1, set2): for i in range(n_instructions)[::-1]: for i_suc in suc[i]: if i_suc < i: - changed = add(next_in_out[i][1], in_out[i_suc][0]) + changed |= add(next_in_out[i][1], in_out[i_suc][0]) + next_in_out[i][1] = next_in_out[i][1].union(in_out[i_suc][0]) else: - changed = add(next_in_out[i][1], next_in_out[i_suc][0]) + changed |= add(next_in_out[i][1], next_in_out[i_suc][0]) + next_in_out[i][1] = next_in_out[i][1].union(next_in_out[i_suc][0]) + g_i = set(gk[i][0]) k_i = set(gk[i][1]) new = g_i.union(next_in_out[i][1].difference(k_i)) - changed = add(next_in_out[i][0], new) + changed |= add(next_in_out[i][0], new) + next_in_out[i][0] = next_in_out[i][0].union(new) + in_out = next_in_out return in_out From 84dbcda8662b3ed0e32bf1e5edc1189655c478f9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 30 Nov 2020 22:54:02 -0500 Subject: [PATCH 513/520] [report] - Final draft --- src/doc/Informe.md | 232 +++++++++++++++++++++++++++++++++++ src/doc/Informe.pdf | Bin 0 -> 101753 bytes src/doc/Report.md | 288 -------------------------------------------- 3 files changed, 232 insertions(+), 288 deletions(-) create mode 100644 src/doc/Informe.md create mode 100644 src/doc/Informe.pdf delete mode 100644 src/doc/Report.md diff --git a/src/doc/Informe.md b/src/doc/Informe.md new file mode 100644 index 00000000..13da033e --- /dev/null +++ b/src/doc/Informe.md @@ -0,0 +1,232 @@ +# Informe de Complementos de Compilación +## Datos Generales +### Autores +- Miguel Tenorio Potrony +- Mauricio Lázaro Perdomo Cortés +- Lázaro Raúl Iglesias Vera + +### Sobre el proyecto +Para la implementación de este proyecto se tomaron como base, los proyectos realizados durante 3er año, donde se desarrollaron las fases de chequeo e inferencia de tipos, además de parsing. El código de dichos proyectos conserva su estructura pero estuvo sujeto a cambios y mejoras. + +La mayoría de nuestras implementaciones siguen las ideas y utilizan las herramientas dadas en clase durante 3er año. + +Todas las fases del proceso de compilación y ejecución serán explicadas a continuación. + + +## Pipeline +Como se puede apreciar en [main.py](https://github.com/2kodevs/cool-compiler-2020/blob/master/src/main.py) el pipeline de nuestro proceso de compilación es: + +1. Lexer +2. Parsing +3. Recolección de tipos +4. Construcción de tipos +5. Chequeo/Inferencia de tipos +6. Verificación de tipos +7. Traducción de Cool a CIL +8. Traducción de CIL a MIPS + +Cada parte del proceso será discutida en detalle durante las siguientes secciones. + +Como se puede apreciar en la etapa #5 del proceso, el chequeo e inferencia de tipos se realizan al unísono, sin embargo cada parte se explicará en secciones separadas y se hará notar por qué se decidió realizarlas al mismo tiempo. + +## Lexer + +Para el proceso de lexer y tokenización se utilizó el paquete PLY. Se creó un un lexer que consta de tres estados: + + - INITIAL + - comments + - strings + +Para cada uno de estos estados se definieron las expresiones regulares que representan cada uno de los tokens posibles, y se +manejan otras variables que conforman el estado del lexer, como la línea actual. + +## Parsing +Para el proceso de parsing se utilizó el parser LR1 y la gramática de Cool que fueron implementados para el proyecto de 3er año sobre chequeo de tipos. + +Fue necesario modificar la salida del Parser para poder devolver la información referente al token de error en caso de que alguna falla fuera detectada. + +Dado que los proyectos llevados a cabo previamente fueron desarrollados para mini-Cool, se hizo necesario modificar la gramática, y se obtuvo como resultado: + +### Gramática de Cool +La grámatica implementada es S-Atributada. Una descripción de los símbolos y producciones de la grámatica, se puede ver en [grammar](https://github.com/2kodevs/cool-compiler-2020/blob/master/src/doc/grammar.pdf) + +## Recolección de tipos +Durante la recolección de tipos se visitan todas las declaraciones de clases, se crean los tipos asociados a ellas y se valida la correctitud de las mismas. + +**Errores detectados**: +- Herencia cíclica +- Redefinición de clases +- Nombres de clase no válidos + +## Construcción de tipos +A los tipos creados en la fase anterior se le añaden todos sus atributos y métodos. Además se verifica que se cumplan los requerimientos de un programa válido de Cool q son tener una clase `Main` con su método `main`. + +**Errores detectados**: +- Problemas de nombrado de atributos y métodos +- Redefinición de atributos +- Redefinición incorrecta de métodos +- Uso de tipos no definidos +- No definición de la clase `Main` o su método `main` +- Incorrecta definición del método `main` +- Mal uso de herencia + +## Chequeo de tipos +En esta fase se evalúa la correctitud de todas las expresiones del lenguaje y se decide el tipo estático de cada una de ellas según lo establecido en el manual de [Cool](https://github.com/2kodevs/cool-compiler-2020/blob/master/doc/cool-manual.pdf). + +**Errores detectados**: +- Incompatibilidad de tipos +- Uso de tipos no definidos +- Uso de variables, tipos y métodos no definidos +- mal usos de `self` y `SELF_TYPE` +- mal usos del `case` + +## Inferencia de tipos +Para la implementación de esta fase se expandió el comportamiento del visitor encargado del chequeo de tipos, razón por la cual ambos procesos se realizan en la misma fase. + +Para lograr la inferencia de tipos, se realizó un algoritmo de punto fijo en el cual mediante repeticiones sucesivas del proceso de inferencia se van definiendo los tipos de aquellas variables declaradas como `AUTO_TYPE`. + +### Idea +Una variable en Cool dada su utilización puede definir dos conjuntos + +1. Tipos a los que se conforma (**Ancestros**) +2. Tipos que se conforman a ella (**Descendientes**) + +Dados los dos conjuntos anteriores se puede decidir si una variable `AUTO_TYPE` puede ser inferida correctamente o no. + +Ambos conjuntos recibieron un nombre intuitivo mencionado anteriormente en **negrita** para hacer referencia a su contenido. + +El tipo que se decida otorgar(inferir) a la variable en cuestión, llamémosle _**T**_, deberá conformarse a todos los tipos del conjunto 1. Al mismo tiempo todos los tipos del conjunto 2 deberán conformarse a él. + +Dicho lo anterior y dado el hecho de que un tipo *A* se conforma a un tipo *B* solamente si *B* es ancestro de *A*, podemos notar que: + +1. El tipo a seleccionar debe ser un ancestro del **Menor Ancestro Común** (**LCA** por sus siglas en inglés) a todos los nodos del conjunto 2, llamémosle *N*. En otras palabras el primer tipo que es ancestro de todos los tipos en el conjunto 2. +2. Como todos los tipos del conjunto 1 necesitan ser ancestros de _**T**_, todos pertenecerán al camino que se forma desde _**T**_ hasta *Object* en el árbol de tipos, por tanto _**T**_ necesita ser descendiente del primero que aparezca en el camino mencionado y pertenezca al conjunto 1, llamémosle *M*. +3. Tomando el operador **<=** para referirnos a la relación *ser ancestro de*, se puede afirmar que _**T**_ es de la forma _**N <= T <= M**_, o lo que es lo mismo _**T**_ podría ser cualquier tipo en el camino de *N* a *M*. + +> El nodo que representa el **LCA** siempre existe dado que el árbol de tipos es único, por tanto en caso extremo *Object* siempre será válido como ancestro a todos los tipos. + +El algortimo implementado tras cada recorrido del **AST**(Árbol de sintaxis abstracta) infiere el tipo de todas aquellas variables de las cuales se tenga información, seleccionando como tipo inferido siempre el que representa a *N*. + +Al ser este algoritmo una extensión del chequeo de tipos, mientras se van infiriendo los tipos se valida que los mismos no ocasionen error. +> En todo lo anterior se asume que todo tipo es ancestro y descendiente de sí mismo. + +**Errores detectados**: +- Mal usos de `AUTO_TYPE` en casos donde no se cumpla que _**N <= M**_ o todos los tipos en el conjunto 1 no se encuentren en un camino del árbol de tipos +- Todos los errores de chequeo semántico que existan en el código o surgan tras la inferencia de una o varias variables. + +## Verificación de tipos +Esta fase surge dado que tras el proceso de inferencia puede haber ocurrido un error que durante el chequeo semántico no se valida. Dado que permitimos *AUTO_TYPE* en los parametros de las funciones, al terminar la inferencia pueden generarse conflictos de mala redefinición de métodos, los cuales son chequeados en la fase de Construcción de los tipos (etapa #4). Por tanto la única función de esta fase es verificar la correctitud de los tipos. + +**Errores detectados**: +- Mala redefinición de métodos ocacionada por la inferencia de tipos + +## Traducción a CIL +En esta etapa del proceso de compilación, requirió especial atención la generación de las expresiones *case*. Para ello se requiere ordenar las instrucciones de tal modo que se asegure el emparejamiento del tipo de la expresión principal con el tipo más específico declarado en las ramas del *case*. + +Primero por cada rama **b** se cuentan cuántos tipos declarados en las demás ramas se conforman a **b**, creando de este modo una tupla `(cantidad, tipo declarado en b)`. +Luego se ordenan todas estas tuplas por su primer elemento, obteniendo así una secuencia ordenada donde el primero elemento representa la rama cuyo tipo declarado se encuentra en el nivel más bajo en la jerarquía de tipos del programa. + +Luego por cada rama **b** de esta secuencia, se obtienen todos los tipos del programa que conforman a **b**, y por cada uno de estos que no haya sido tomado en cuenta en el procesamiento de ramas anteriores, se generan las instrucciones necesarias para comprobar si el tipo de la expresión principal del *case* coincide con él. En caso de coincidencia, se salta al bloque de las instrucciones generadas por el cuerpo de **b**; si no entonces se procede a comprobar con el tipo siguiente. Nótese que no se repiten comprobaciones. + +**Errores detectados**: +- Dispatch estático o dinámico desde un objeto void +- Expresión principal de un *case* tiene valor `void` +- Ejecución de un *case* sin que ocurra algún emparejamiento con alguna rama. +- División por cero +- Substring fuera de rango + +> Aunque estos errores realmente se detectan en ejecución, es en esta fase que se genera el código que permite detectarlos. + +## Traducción a MIPS +En la fase de generación de código `MIPS` se enfrentaron tres problemas fundamentales: + + - Estructura de los objetos en memoria. + - Definición de tipos en memoria. + - Elección de registros. + +### Estructura de los objetos en memoria. +Determinar el modelo que seguirían los objetos en la memoria fue un paso fundamental para la toma de múltiples decisiones tanto en la generación de código `CIL` como `MIPS`. Los objetos en memoria siguen el siguiente modelo: + +```| Tipo | Tamaño | Tabla de dispatch | -- Atributos -- | Marca de objeto |``` + - Tipo: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un entero e indica el tipo del objeto. + - Tamaño: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un entero e indica el tamaño en `palabras` del objeto. + - Tabla de dispatch: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como una dirección de memoria e indica el inicio de la tabla de dispatch del objeto. La tabla de dispatch del objeto es un segmento de la memoria donde interpretamos cada `palabra` como la dirección a uno de los métodos del objeto. + - Atributos: Esta sección tiene tamaño **N** `palabras` donde **N** es la cantidad de atributos que conforman el objeto, cada una de las `palabras` que conforman esta sección representa el valor de un atributo del objeto. + - Marca de objeto: Esta sección tiene tamaño 1 `palabra`, es un valor usado para marcar que esta zona de la memoria corresponde a un objeto, se añadió con el objetivo de hacer menos propenso a fallos la tarea de identificar objetos en memoria en el `Garbage Collector`. + +### Definición de tipos en memoria. +Un tipo está representado por tres estructuras en la memoria: + - Una dirección a una cadena alfanumérica que representa el nombre del tipo. + - Un prototipo que es una especie de plantilla que se utiliza en la creación de los objetos. Cuando se crea un objeto este prototipo es copiado al segmento de memoria asignado al objeto. Un prototipo es un objeto válido por lo que tiene exactamente la misma estructura explicada anteriormente. El prototipo es también la solución escogida para el problema de los valores por defecto de los objetos. + - Una tabla de dispatch que como se explicó anteriormente contiene las direcciones de los métodos del objeto. +Existe una tabla de prototipos (nombres) donde se puede encontrar el prototipo (nombre) de un tipo específico, utilizando como índice el valor que representa al tipo. + +### Elección de registros. +La elección de registros fue un proceso que se decidió optimizar para disminuir la utilización de las operaciones `lw` y `sw` en `MIPS` que como se sabe, añaden una demora considerable a nuestros programas por el tiempo que tarda en realizarse un operación de escritura o lectura en la memoria. +El proceso de elección de registros se realiza para cada función y consta de los siguientes pasos: + - Separación del código en bloques básicos: + + Para obtener los bloques básicos primero se hace un recorrido por las instrucciones de la función marcando los líderes. Son considerados líderes las instrucciones de tipo `Label` y las instrucciones que tengan como predecesor un instrucción de tipo `Goto` o `Goto if`. Luego de tener marcados los líderes, se obtienen los bloques que serán los conjuntos de instrucciones consecutivas que comienzan con un líder y terminan con la primera instrucción que sea predecesor de un líder (notar que un bloque puede estar formado por una sola instrucción). + + - Creación del grafo de flujo: + + Este es un grafo dirigido que indica los caminos posibles entre los bloques básicos su elaboración es bastante sencilla: si la última instrucción de un bloque es un `Goto`, entonces se añadirá una arista desde este bloque hacia el bloque iniciado por la instrucción `Label` a la que hace referencia el `Goto`; si la última instrucción es de tipo `Goto if`, entonces se añadirán dos aristas una hacia el bloque que comienza con la instrucción `Label` a la que se hace referencia, y otra hacia el bloque que comienza con la instrucción siguiente en la función; en el caso de que la última instrucción sea de cualquier otro tipo, se colocará una sola arista desde el bloque actual hacia el bloque que comienza con la instrucción siguiente en la función. + + - Análisis de vida de las variables: + + En este procedimiento se computan cinco conjuntos para cada instrucción **I**: `succ`, `gen`, `kill`, `in` y `out`. `succ` contiene las instrucciones que se pueden ejecutar inmediatamente después de la instrucción **I**; `gen` contiene las variables de las que se necesita el valor en la instrucción **I**; `kill` contiene las variables a las que se les asigna un valor en la instrucción **I**; `in` contiene las variables que pueden estar vivas al llegar a la instrucción **I**, y `out` contiene las variables que pueden estar vivas luego de ejecutada la instrucción **I**. + + - Creación del grafo de interferencia: + + Los vértices de este grafo serán las variables que se utilizan en la función y existirá una arista entre los vértices **x** y **y**, si las variables que representan esos nodos interfieren. Dos variables interfieren si existe alguna instrucción **I** tal que **x** pertenezca al `kill` de **I** y **y** pertenezca al `out` de **I**. + + - Asignación de registros: + + Contando con el grafo de interferencia, se asignan registros a las variables de forma tal que dos variables que interfieran no se les asigne el mismo registro, esto puede verse como el problema de colorear un grafo con **N** colores siendo **N** la cantidad de registros que se tienen. Es conocido que este problema es *NP* por lo que para asignar los registros se usa una heurística muy sencilla que consiste en lo siguiente: + + Primero se va eliminando del grafo y colocando en una pila cada nodo que tenga menos de N vecinos, se nota que todos estos elementos pueden ser coloreados sin problemas. Si en algún momento no existe algún nodo con menos de N vecinos, se tomará un nodo al azar; este proceso terminará cuando no queden nodos en el grafo. Luego se va sacando cada nodo de la pila y se le asigna un registro que no esté usado por alguno de los nodos que eran vecinos de este en el momento en que se eliminó del grafo, en el caso de que existan más de un nodo posible, se le asigna el menor, en caso de que no exista nodo posible la variable no tendrá registro y su valor permanecerá en la memoria. + +**Errores detectados**: +- Heap overflow + +## Ejecución +Para ejecutar el proyecto se necesita tener instalado `Python` y el conjunto de dependencias listado en [requirements.txt](https://github.com/2kodevs/cool-compiler-2020/blob/master/requirements.txt). + +Para instalar las dependencias puede utilizar: +```bash +make install +``` +Una vez estén instaladas las dependencias, puede compilar y ejecutar cualquier archivo de código cool utilizando el comando: +```bash +make main CODE=.cl +``` +>Para usar `make` necesita estar en la dirección `/src` + +## Estructura +Los archivos del proyecto se encuentran modularizados de la siguiente manera: + +1. **core** + 1. **cmp** + 1. **cool** + 2. **parser** + 2. **lexer** + 3. **visitors** + 1. **type_check** + 2. **cil** + 3. **mips** + +**cmp** contiene todos los archivos heredados de las clases de 3er año y proyectos anteriores. + +**cool** contiene el *AST*, Gramática y Parser de Cool + +**parser** contiene la implementación parser LR1 utilizada + +**lexer** todo lo referente a lexer y tokenización + +**visitor** contiene la implementación del patrón visitor + +**type_checking** fases de la #3 a la #6 + +**cil** traducción a cil + +**mips** traducción a mips diff --git a/src/doc/Informe.pdf b/src/doc/Informe.pdf new file mode 100644 index 0000000000000000000000000000000000000000..30f0f7cdb8d3138c0824ecca2fd6eb51a649523f GIT binary patch literal 101753 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 AIsgCw literal 0 HcmV?d00001 diff --git a/src/doc/Report.md b/src/doc/Report.md deleted file mode 100644 index a33b74a3..00000000 --- a/src/doc/Report.md +++ /dev/null @@ -1,288 +0,0 @@ -# Datos Generales -## Autores -- Miguel Tenorio Potrony -- Mauricio Lázaro Perdomo Cortés -- Lázaro Raúl Iglesias Vera - -## Sobre el proyecto -Para la implementación de este proyecto se tomaron como base, los proyectos realizados durante 3er año, donde se desarrollaron las fases de chequeo e inferencia de tipos. El código de dichos proyectos conserva su estructura pero estuvo sujeto a cambios y mejoras. - -La mayoría de nuestras implementaciones siguen las ideas y utilizan las herramientas dadas en clase durante 3er año. - -Todas las fases del proceso de compilación y ejecución serán explicadas a continuación. - - -# Pipeline -Como se puede apreciar en [main.py](../main.py) el pipeline de nuestro poceso de compilación es: - -1. Lexer -2. Tokenización -3. Parsing -4. Recolección de tipos -5. Construcción de tipos -6. Chequeo/Inferencia de tipos -7. Verificación de tipos -8. Traducción de Cool a CIL -9. Traducción de CIL a MIPS - -Cada parte del proceso será discutida en detalle durante las siguientes secciones. - -Como se puede apreciar en la etapa #6 del proceso, el chequeo e inferencia de tipos se realizan al unísono, sin embargo cada parte se explicará en secciones separadas y se hará notar por qué se decidió unificarlas. - -## Lexer -**Pendig** - -## Tokenización -Para el proceso de tokenización se utilizó el paquete PLY, se creó un un lexer que consta de tres estados: - - - INITIAL - - comments - - strings - -Para cada uno de estos estados se definieron las expresiones regulares que representan cada uno de los tokens posibles y se -manejan otras variables que conforman el estado del lexer, como la línea actual. - -## Parsing -Para el proceso de parsing se utilizó el parser LR1 y la gramática de Cool que fueron implementados para el proyecto de 3er año sobre chequeo de tipos. - -Fue necesario modificar la salida del Parser para poder devolver la información referente al token de error en caso de que alguna falla fuera detectada. - -Dado que los proyectos llevados a cabo previamente fueron desarrollados para mini-Cool, se hizo necesario modificar la gramática, y se obtuvo como resultado: - -### Gramática de Cool -**Terminales** : class, type, inherits, id, let, in, isvoid, not, new, case, of, esac, if, then, else, fi, while, loop, pool - - -| No terminal | Derivación | -| :-----------: | :------------------------------------------------: | -| program | class_list | -| class_list | class_def class_list | -| class_list | class_def | -| class_def | class type { feature_list } ; | -| class_def | class type inherits type { feature_list } ; | -| feature_list | feature feature_list | -| feature_list | e | -| feature | id : type ; | -| feature | id : type ← expr ; | -| feature | id ( param_list ) : type { expr } ; | -| feature | id ( ) : type { expr } ; | -| param_list | param | -| param_list | param , param_list | -| param | id : type | -| block | expr ; | -| block | expr ; block | -| let_list | id : type | -| let_list | id : type ← expr | -| let_list | id : type , let_list | -| let_list | id : type ← expr , let_list | -| case_list | id : type ⇒ expr ; | -| case_list | id : type ⇒ expr ; case_list | -| func_call | @ type . id ( arg_list ) | -| func_call | @ type . id ( ) | -| func_call | . id ( arg_list ) | -| func_call | . id ( ) | -| arg_list | expr | -| arg_list | expr , arg_list | -| member_call | id ( arg_list ) | -| member_call | id ( ) | -| expr | special | -| expr | cmp_expr | -| special | arith <= special_arith | -| special | arith < special_arith | -| special | arith = special_arith | -| special | special_arith | -| special_arith | arith + special_term | -| special_arith | arith - special_term | -| special_arith | special_term | -| special_term | term * special_unary | -| special_term | term / special_unary | -| special_term | special_unary | -| special_unary | isvoid special_unary | -| special_unary | ~ special_unary | -| special_unary | final_expr | -| final_expr | let let_list in expr | -| final_expr | id ← expr | -| final_expr | not expr | -| cmp_expr | arith <= arith | -| cmp_expr | arith < arith | -| cmp_expr | arith = arith | -| cmp_expr | arith | -| arith | arith + term | -| arith | arith - term | -| arith | term | -| term | term * unary | -| term | term / unary | -| term | unary | -| unary | isvoid unary | -| unary | ~ unary | -| unary | func_expr | -| func_expr | func_expr func_call | -| func_expr | atom | -| atom | member_call | -| atom | new type | -| atom | ( expr ) | -| atom | id | -| atom | integer | -| atom | string | -| atom | bool | -| atom | { block } | -| atom | if expr then expr else expr fi | -| atom | while expr loop expr pool | -| atom | case expr of case_list esac | - - -## Recolección de tipos -Durante la recolección de tipos se visitan todas las declaraciones de clases, se crean los tipos asociados a ellas y se valida la correctitud de las mismas. - -**Errores detectados**: -- Herencia cíclica -- Redefinición de clases -- Nombres de clase no válidos - -## Construcción de tipos -A los tipos creados en la fase anterior se le añaden todos sus atributos y métodos. Además se verifica que se cumplan los requerimientos de un programa válido de Cool q son tener una clase `Main` con su método `main`. - -**Errores detectados**: -- Problemas de nombrado de atributos y métodos -- Redefinición de atributos -- Redefinición incorrecta de métodos -- Uso de tipos no definidos -- No definición de la clase `Main` o su método `main` -- Incorrecta definición del método `main` -- Mal uso de herencia - -## Chequeo de tipos -En esta fase se evalúa la correctitud de todas las expresiones del lenguaje y se decide el tipo estático de cada una de ellas según lo establecido en el manual de [Cool](../../doc/cool-manual.pdf). - -**Errores detectados**: -- Incompatibilidad de tipos -- Uso de tipos no definidos -- Uso de variables, tipos y métodos no definidas -- mal usos de `self` y `SELF_TYPE` -- mal usos del `case` - -## Inferencia de tipos -Para la implementación de esta fase se expandió el comportamiento del visitor encargado del chequeo de tipos, razón por la cual ambos procesos se realizan en la misma fase. - -Para lograr la inferencia de tipos, se realizó un algoritmo de punto fijo en el cual mediante repeticiones sucesivas del proceso de inferencia se irán definiendo los tipos de aquellas variables declaradas como `AUTO_TYPE`. - -### Idea -Una variable en Cool dada su utilización puede definir dos conjuntos - -1. Tipos a los que se conforma (**Ancestros**) -2. Tipos que se conforman a ella (**Descendientes**) - -Dados los dos conjuntos anteriores se puede decidir si una variable `AUTO_TYPE` puede ser inferida correctamente o no. - -Ambos conjuntos recibieron un nombre intuitivo mencionada anteriormente en **negrita** para hacer referencia a su contenido. - -El tipo que se decida otorgar a la variable en cuestión, llamésmole _**T**_, deberá conformarse a todos los tipos que conforman el conjunto #1. Al mismo tiempo todos los tipos del conjunto #2 deberán conformarse a él. - -Dicho lo anterior y dado el hecho de que un tipo *A* se conforma a un tipo *B* solamente si *B* es ancestro de *A*, podemos notar que: - -1. El tipo a seleccionar debe ser un ancestro del **Menor Ancestro Común** (**LCA** por sus siglas en inglés) a todos los nodos del conjunto #2, llamémosle *N*. En otras palabras el primer tipo que es ancestro de todos los tipos en el conjunto #2. -2. Como todos los tipos del conjunto #1 necesitan ser ancestros de _**T**_, todos pertenecerán al camino que se forma desde _**T**_ hasta *Object* en el árbol de tipos, por tanto _**T**_ necesita ser descendiente del primero que aparezca en el camino mencionado y pertenezca al conjunto #1, llamémosle *M*. -3. Tomando el operador **<=** para referirnos a la relación *ser ancestro de de*, se puede afirmar que _**T**_ es de la forma _**N <= T <= M**_, o lo que es lo mismo _**T**_ podría ser cualquier tipo en el camino de *N* a *M*. - -> El nodo que representa el **LCA** siempre existe dado que el árbol de tipos es único, por tanto en caso extremo *Object* siempre será válido como ancestro a todos los tipos. - -El algortimo implementado tras cada recorrido del **AST**(Árbol de sintaxis abstracta) infiere el tipo de todas aquellas variables de las cual se tenga información, seleccionando como tipo inferido siempre el que representa a *N*. - -Al ser este algoritmo una extensión del chequeo de tipos, mientras se van infiriendo los tipos se valida que los mismos no ocasionen error. -> En todo lo anterior se asume que todo tipo es ancestro y descendiente de sí mismo. - -**Errores detectados**: -- Mal usos de `AUTO_TYPE` en casos donde no se cumpla que _**N <= M**_ o todos los tipos en el conjunto #1 no se encuentren en un camino del árbol de tipos -- Todos los errores de chequeo semánticos que existan en el código o se provoquen tras la inferencia de una o varias variables. - -## Verificación de tipos -Esta fase surge dado que tras el proceso de inferencia puede haber ocurrido un error que durante el chequeo semántico no se valida. Dado que permitimos *AUTO_TYPE* en los parametros de las funciones al terminar la inferencia pueden generarse conflictos de mala redefinión de métodos, los cuales son chequeados en la fase de Construcción de los tipos(#5). Por tanto la única funcion de esta fase es verifacar la correctitud de los tipos. - -**Errores detectados**: -- Mala redefinición de métodos ocacionada por la inferencia de tipos - -## Traducción a CIL -pending - -## Traducción a MIPS -En la fase de generación de código `MIPS` enfrentamos tres problemas fundamentales: - - - Estructura de los objetos en memoria. - - Definición de tipos en memoria. - - Elección de registros. - -### Estructura de los objetos en memoria. -Determinar el modelo que seguirían nuestros objetos en la memoria fue un paso fundamental para la toma de múltiples decisiones tanto en la generación de código `CIL` como `MIPS`. Los objetos en memoria siguen el siguiente modelo: - -```| Tipo | Tamaño | Tabla de dispatch | -- Atributos -- | Marca de objeto |``` - - Tipo: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un entero e indica el tipo del objeto. - - Tamaño: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un enter e indica el tamaño en `palabras` del objeto. - - Tabla de dispatch: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como una dirección de memoria e indica el inicio de la tabla de dispatch del objeto. La tabla de dispatch del objeto es un segmento de la memoria donde interpretamos cada `palabra` como la dirección a uno de los métodos del objeto. - - Atributos: Esta sección tiene tamaño **N** `palabras` donde **N** será la cantidad de atributos que conforman el objeto, cada una de las `palabras` que conforman esta sección representa el valor de un atributo del objeto. - - Marca de objeto: Esta sección tiene tamaño 1 `palabra`, es un valor usado para marcar que esta zona de la memoria corresponde a un objeto, se añadió con el objetivo de hacer menos propenso a fallos la tarea de identificar objetos en memoria en el `Garbage Collector`. - -### Definición de tipos en memoria. -Un tipo está representado por tres estructuras en la memoria: - - Una dirección a una cadena alfanumérica que representa el nombre del tipo. - - Un prototipo que es una especie de plantilla que utilizamos en la creación de los objetos. Cuando se crea un objeto este prototipo es copiado al segmento de memoria asignado al objeto. Un prototipo es un objeto válido por lo que tiene exactamente la misma estructura explicada anteriormente. El prototipo es también nuestra solución a los valores por defecto de los objetos. - - Una tabla de dispatch que como explicamos anteriormente contiene las direcciones de los métodos del objeto. -Existe una tabla de prototipos (nombres) donde podemos encontrar el prototipo (nombre) de un tipo específico utilizando como índice el valor que representa al tipo. - -### Elección de registros. -La elección de registros fue un proceso que decidimos optimizar para disminuir la utilización de las operaciones `lw` y `sw` en `MIPS` que como sabemos añaden una demora considerable a nuestros programas por el tiempo que tarda en realizarse un operación de escritura o lectura en la memoria. -El proceso de elección de registros se realiza para cada función y consta de los siguientes pasos: - - Separación del código en bloques básicos: - - Para obtener los bloques básicos primero se hace un recorrido por las instrucciones de la función marcando los líderes. Son considerados líderes las instrucciones de tipo `Label` y las instrucciones que tengan como predecesor un instrucción de tipo `Goto` o `Goto if`. Luego de tener marcado los líderes se obtienen los bloques que serán los conjuntos de instrucciones consecutivas que comienzan con un líder y terminan con la primera instrucción que sea predecesor de un líder (notar que un bloque puede estar formado por una sola instrucción). - - - Creación del grafo de flujo: - - Este es un grafo dirigido que indica los caminos posibles entre los bloques básicos, su elaboración es bastante sencilla, si la última instrucción de un bloque es un `Goto` entonces se añadirá una arista desde este bloque hacia el bloque iniciado por la instrucción `Label` a la que hace referencia el `Goto`; si la última instrucción es de tipo `Goto if` entonces se añadirán dos aristas una hacia el bloque que comienza con la instrucción `Label` a la que se hace referencia y otra hacia el bloque que comienza con la instrucción siguiente en la función; en el caso de que la última instrucción sea de cualquier otro tipo se colocará una sola arista desde el bloque actual hacia el bloque que comienza con la instrucción siguiente en la función. - - - Análisis de vida de las variables: - - En este procedimiento computaremos cuatro conjuntos para cada instrucción **I**: `succ`, `gen`, `kill`, `in` y `out`. `succ` contiene las instrucciones que se pueden ejecutar inmediatamente después de la instrucción **I**; `gen` contiene las variables de las que se necesita el valor en la instrucción **I**; `kill` contiene las variables a las que se les asigna un valor en la instrucción **I**; `in` contiene las variables que están pueden estar vivas al llegar a la instrucción **I** y `out` contiene las variables que pueden estar vivas luego de ejecutada la instrucción **I**. - - - Creación del grafo de interferencia: - - Los vértices de este grafo serán las variables que se utilizan en la función y existirá una arista entre los vértices **v** y **y** si las variables que representan esos nodos interfieren. Dos variables interfieren si existe alguna instrucción **i** tal que **x** pertenezca al `kill` de **i** y **y** pertenezca al `out` de **i**. - - - Asignación de registros: - - Contando con el grafo de interferencia asignaremos registros a las variables de forma tal que dos variables que interfieran no se les asigne el mismo registro, esto puede verse como el problema de colorear un grafo con **N** colores siendo **N** la cantidad de registros que tenemos. Es conocido que este problema es *NP* por lo que para asignar los registros usaremos una heurística muy sencilla que consistirá en lo siguiente: - - Primero iremos eliminando del grafo y colocando en una pila cada nodo que tenga menos de N vecinos, notamos que todos estos elementos pueden ser coloreados sin problemas. Si en algún momento no existe nigún nodo con menos de N vecinos se tomará un nodo al azar; este proceso terminará cuando no nos queden nodos en el grafo. Luego iremos sacando cada nodo de la pila y le asignaremos un registro que no esté usado por ninguno de los nodos que eran vecinos de este en el momento en que se eliminó del grafo, en el caso de que existan más de un nodo posible le asignaremos el menor, en caso de que no exista nodo posible la variable no tendrá registro y su valor permanecerá en la memoria. - -# Ejecución -Para ejectur el proyecto se necesita tener instalado python y el conjunto de dependencias listado en [requirements.txt](../../requirements.txt). - -Para instalar las dependencias puede utilizar: -```bash -make install -``` -Una vez esten instaladas las dependencias puede compilar y ejecutar cualquier achivo de código cool utilizando el comando: -```bash -make main CODE=.cl -``` ->Para usar `make` necesita estar el la dirección `/src` - -# Estructura -Los archivos del proyecto se encuentran modularizados de la siguiente manera: - -1. **cool_cmp** - 1. **cmp** - 2. **lexer** - 3. **visitors** - 1. **type_check** - 2. **cil** - 3. **mips** - -**cmp** tiene todos los archivos y submodulos heredados de las clases de 3er año y proyectos anteriores. - -**lexer** todo lo referente a lexer y tokenización - -**type_checking** fases de la #4 a la #7 - -**cil** traducción a cil - -**mips** traducción a mips \ No newline at end of file From 75ec7817da5d634529b3a98cb81dc637c34feaaa Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Mon, 30 Nov 2020 22:56:08 -0500 Subject: [PATCH 514/520] Change in_out from tuples to list --- src/core/visitors/mips/cil_to_mips.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/visitors/mips/cil_to_mips.py b/src/core/visitors/mips/cil_to_mips.py index c7f0ecf6..22c565b9 100644 --- a/src/core/visitors/mips/cil_to_mips.py +++ b/src/core/visitors/mips/cil_to_mips.py @@ -1489,8 +1489,8 @@ def myLen(l): @staticmethod def out_in_compute(suc, gk): n_instructions = len(gk) - in_out = [(set(), set()) for _ in range(n_instructions)] - next_in_out = [(set(), set()) for _ in range(n_instructions)] + in_out = [[set(), set()] for _ in range(n_instructions)] + next_in_out = [[set(), set()] for _ in range(n_instructions)] def add(set1, set2): return not set2.issubset(set1) From 3fc7af8dd77d2b616d0aa3c7e8e9a4c417c0b046 Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 1 Dec 2020 00:50:57 -0500 Subject: [PATCH 515/520] Add zero as a valid end to read_str --- src/core/visitors/mips/mips_lib.asm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/visitors/mips/mips_lib.asm b/src/core/visitors/mips/mips_lib.asm index 596dd995..60853431 100644 --- a/src/core/visitors/mips/mips_lib.asm +++ b/src/core/visitors/mips/mips_lib.asm @@ -854,12 +854,16 @@ read_str_reading: read_str_look_nl: lb $t2 0($t0) - beq $t2 $zero read_str_no_nl beq $t2 new_line read_str_nl_founded + beq $t2 $zero read_str_zero_founded#read_str_no_nl addi $t1 $t1 1 addi $t0 $t0 1 j read_str_look_nl +read_str_zero_founded: + blt $t1 $t3 read_str_nl_founded + j read_str_no_nl + read_str_nl_founded: sb $zero 0($t0) addi $t1 $t1 1 From 742534ccbe64640736247876c983994ed6948c99 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 1 Dec 2020 19:08:11 -0500 Subject: [PATCH 516/520] [cil] - Reuse variable created by id of LetAttributeNode --- src/core/visitors/cil/cool_to_cil.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/visitors/cil/cool_to_cil.py b/src/core/visitors/cil/cool_to_cil.py index ecfce903..7605f25a 100644 --- a/src/core/visitors/cil/cool_to_cil.py +++ b/src/core/visitors/cil/cool_to_cil.py @@ -588,7 +588,10 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - vname = self.register_local(VariableInfo(node.id, node.type), id=True) + if node.id in self.ids: + vname = self.ids[node.id] + else: + vname = self.register_local(VariableInfo(node.id, node.type), id=True) if node.expr: self.visit(node.expr, scope) self.register_instruction(cil.AssignNode(vname, scope.ret_expr)) From 56142cff4f8f7ca436fc6b23c2ac120897a4af7b Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 1 Dec 2020 19:08:18 -0500 Subject: [PATCH 517/520] Add default value to string length --- src/core/visitors/mips/cil_to_mips.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/visitors/mips/cil_to_mips.py b/src/core/visitors/mips/cil_to_mips.py index 22c565b9..646eb6f6 100644 --- a/src/core/visitors/mips/cil_to_mips.py +++ b/src/core/visitors/mips/cil_to_mips.py @@ -189,7 +189,7 @@ def visit(self, node): methods = {key: self._name_func_map[value] for key, value in node.methods} defaults = [] if node.name == "String": - defaults = [('value', 'default_str')] + defaults = [('value', 'default_str'), ('length', 'type_4_proto')] new_type = mips.MIPSType(type_label, name_label, node.attributes, methods, len(self._types), default=defaults) self._types[node.name] = new_type @@ -243,6 +243,11 @@ def visit(self, node): # print(node.instructions) print(e) print(node.name) + + if "evolve" in node.name: + print("!!!!!!!!!!!!!!!!!!!!!!!", node.name) + for ins in node.instructions: + print(ins) #code_instructions.extend(self.memory_manager.save_values()) From 1d0fea0b7624978b63597fa0af3c459dd06d054f Mon Sep 17 00:00:00 2001 From: Mauricio Perdomo Date: Tue, 1 Dec 2020 19:23:54 -0500 Subject: [PATCH 518/520] Remove unused code --- src/core/visitors/mips/cil_to_mips.py | 336 +------------------------- 1 file changed, 4 insertions(+), 332 deletions(-) diff --git a/src/core/visitors/mips/cil_to_mips.py b/src/core/visitors/mips/cil_to_mips.py index 646eb6f6..88408382 100644 --- a/src/core/visitors/mips/cil_to_mips.py +++ b/src/core/visitors/mips/cil_to_mips.py @@ -7,36 +7,6 @@ from collections import defaultdict -USED = 1 -UNUSED = 0 - - -class SimpleRegistersManager: - def __init__(self, registers): - self.registers = {reg: UNUSED for reg in registers} - - def get_free_reg(self): - for reg, state in self.registers.items(): - if state == UNUSED: - self.registers[reg] = USED - return reg - raise Exception("not free register") - - def free_reg(self, reg): - self.registers[reg] = UNUSED - - def is_used(self, reg): - return self.registers[reg] == USED - - def get_registers_for_save(self): - regs = [reg for reg, state in self.registers.items() if state == USED] - regs.extend([mips.RA_REG]) - return regs - - def __repr__(self): - return str(len([0 for r in self.registers if self.registers[r] == USED ])) - - class MemoryManager: def __init__(self, registers, function_for_assign): self.registers = registers @@ -73,9 +43,8 @@ def generate_code_label(self): class CILToMIPSVisitor: - def __init__(self, label_generator = LabelGenerator(), regiters_manager = SimpleRegistersManager(mips.REGISTERS)): + def __init__(self, label_generator = LabelGenerator()): self._label_generator = label_generator - self._registers_manager = regiters_manager self.memory_manager = None self._types = {} self._data_section = {} @@ -231,26 +200,8 @@ def visit(self, node): code_instructions = [] - #This try-except block is for debuggin purposes - - try: - code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) + code_instructions = list(itt.chain.from_iterable([self.visit(instruction) for instruction in node.instructions])) - except Exception as e: - raise e - if node.name == "function_a2i_aux_at_A2I": - print(e) - # print(node.instructions) - print(e) - print(node.name) - - if "evolve" in node.name: - print("!!!!!!!!!!!!!!!!!!!!!!!", node.name) - for ins in node.instructions: - print(ins) - - #code_instructions.extend(self.memory_manager.save_values()) - final_instructions = [] for param in params: @@ -260,11 +211,9 @@ def visit(self, node): if not self.in_entry_function(): used_regs = used_regs_finder.get_used_registers(code_instructions) - #TODO change this to grow the stack just once for reg in used_regs: initial_instructions.extend(mips.push_register(reg)) - #TODO change this to shrink the stack just once for reg in used_regs[::-1]: final_instructions.extend(mips.pop_register(reg)) @@ -286,76 +235,49 @@ def visit(self, node): self.push_arg() instructions = [] if type(node.name) == int: - #reg = self.get_free_reg() - #load_value = mips.LoadInmediateNode(reg, node.name) - #instructions.append(load_value) instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.name)) instructions.extend(mips.push_register(mips.ARG_REGISTERS[0])) else: - #reg = self.get_free_reg() - # if not loaded: - #value_address = self.get_var_location(node.name) - #load_value = mips.LoadWordNode(reg, value_address) - #instructions.append(load_value) reg = self.memory_manager.get_reg_for_var(node.name) if reg is None: reg = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name))) instructions.extend(mips.push_register(reg)) - #self.free_reg(reg) return instructions @visitor.when(cil.StaticCallNode) def visit(self, node): - instructions = [] - label = self._name_func_map[node.function] - instructions.append(mips.JumpAndLinkNode(label)) - #dst_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) else: instructions.append(mips.MoveNode(reg, mips.V0_REG)) - #instructions.append(mips.MoveNode(reg, mips.V0_REG)) if self._pushed_args > 0: instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) self.clean_pushed_args() - return instructions @visitor.when(cil.AssignNode) def visit(self, node): instructions = [] - #reg = self.get_free_reg() - reg1 = None if type(node.source) == cil.VoidNode: - #instructions.append(mips.LoadInmediateNode(reg, 0)) reg1 = mips.ZERO_REG elif node.source.isnumeric(): - #load_value = mips.LoadInmediateNode(reg, int(node.source)) - #instructions.append(load_value) reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadInmediateNode(reg1, int(node.source))) else: - #value_location = self.get_var_location(node.source) - #load_value = mips.LoadWordNode(reg, value_location) - #instructions.append(load_value) reg1 = self.memory_manager.get_reg_for_var(node.source) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source))) - #location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(reg2, location)) - #self.free_reg(reg) reg2 = self.memory_manager.get_reg_for_var(node.dest) if reg2 is None: instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) @@ -366,13 +288,8 @@ def visit(self, node): @visitor.when(cil.AllocateNode) def visit(self, node): - #TODO Save $a0, $a1 and $a2 registers if are beign used - instructions = [] - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() - tp = 0 if node.type.isnumeric(): tp = node.type @@ -384,14 +301,12 @@ def visit(self, node): instructions.extend(mips.push_register(reg1)) instructions.extend(mips.push_register(reg2)) - #instructions.append(mips.LoadInmediateNode(reg1, tp)) instructions.append(mips.LoadInmediateNode(reg1, tp)) instructions.extend(mips.create_object(reg1, reg2)) instructions.extend(mips.pop_register(reg2)) instructions.extend(mips.pop_register(reg1)) - #location = self.get_var_location(node.dest) reg3 = self.memory_manager.get_reg_for_var(node.dest) if reg3 is None: @@ -399,11 +314,6 @@ def visit(self, node): else: instructions.append(mips.MoveNode(reg3, mips.V0_REG)) - #instructions.append(mips.StoreWordNode(mips.V0_REG, location)) - - #self.free_reg(reg1) - #self.free_reg(reg2) - return instructions @visitor.when(cil.ReturnNode) @@ -417,8 +327,6 @@ def visit(self, node): elif isinstance(node.value, cil.VoidNode): instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0)) else: - #location = self.get_var_location(node.value) - #instructions.append(mips.LoadWordNode(mips.V0_REG, location)) reg = self.memory_manager.get_reg_for_var(node.value) if reg is None: instructions.append(mips.LoadWordNode(mips.V0_REG, self.get_var_location(node.value))) @@ -430,12 +338,7 @@ def visit(self, node): def visit(self, node): instructions = [] - #reg = self.get_free_reg() string_location = mips.LabelRelativeLocation(self._data_section[node.msg.name].label, 0) - #instructions.append(mips.LoadAddressNode(reg, string_location)) - - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(reg, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], string_location)) @@ -443,7 +346,6 @@ def visit(self, node): else: instructions.append(mips.LoadAddressNode(reg, string_location)) - #self.free_reg(reg) return instructions @visitor.when(cil.PrintIntNode) @@ -451,9 +353,6 @@ def visit(self, node): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1)) - #TODO save $a0 if is beign used - #location = self.get_var_location(node.value) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) reg = self.memory_manager.get_reg_for_var(node.value) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value))) @@ -469,9 +368,6 @@ def visit(self, node): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) - #TODO save $a0 if is beign used - #location = self.get_var_location(node.value) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) reg = self.memory_manager.get_reg_for_var(node.value) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0]. self.get_var_location(node.value))) @@ -485,11 +381,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #reg = self.get_free_reg() - #reg2 = self.get_free_reg() - - #src_location = self.get_var_location(node.source) - #dst_location = self.get_var_location(node.dest) reg1 = self.memory_manager.get_reg_for_var(node.source) pushed = False if reg1 is None: @@ -514,18 +405,6 @@ def visit(self, node): else: instructions.append(mips.MoveNode(reg2, mips.ARG_REGISTERS[0])) - #instructions.append(mips.LoadWordNode(reg, src_location)) - #instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg1, 0))) - #instructions.append(mips.ShiftLeftLogicalNode(reg3, reg3, 2)) - #instructions.append(mips.LoadAddressNode(reg4, mips.TYPENAMES_TABLE_LABEL)) - #instructions.append(mips.AddUnsignedNode(reg3, reg3, reg4)) - #instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg3, 0))) - #instructions.append(mips.StoreWordNode(reg1, dst_location)) - #instructions.append(mips.MoveNode(reg2, reg3)) - - #self.free_reg(reg) - #self.free_reg(reg2) - return instructions @visitor.when(cil.ExitNode) @@ -540,13 +419,9 @@ def visit(self, node): def visit(self, node): instructions = [] - #reg = self.get_free_reg() - dest = node.dest if type(node.dest) == str else node.dest.name obj = node.obj if type(node.obj) == str else node.obj.name comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name - #obj_location = self.get_var_location(obj) - #dst_location = self.get_var_location(dest) reg = self.memory_manager.get_reg_for_var(obj) if reg is None: @@ -555,11 +430,6 @@ def visit(self, node): tp = self._types[comp_type] offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE - - #instructions.append(mips.LoadWordNode(reg, obj_location)) - - #instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, offset))) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(reg, offset))) reg = self.memory_manager.get_reg_for_var(dest) @@ -568,27 +438,18 @@ def visit(self, node): else: instructions.append(mips.MoveNode(reg, mips.ARG_REGISTERS[1])) - # instructions.append(mips.StoreWordNode(reg, dst_location)) - #instructions.append(mips.MoveNode(reg2, reg3)) - - #self.free_reg(reg) return instructions @visitor.when(cil.SetAttribNode) def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() obj = node.obj if type(node.obj) == str else node.obj.name comp_type = node.computed_type if type(node.computed_type) == str else node.computed_type.name - #obj_location = self.get_var_location(obj) - tp = self._types[comp_type] offset = (tp.attributes.index(node.attr) + 3) * mips.ATTR_SIZE - #instructions.append(mips.LoadWordNode(reg2, obj_location)) reg1 = self.memory_manager.get_reg_for_var(obj) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] @@ -596,20 +457,14 @@ def visit(self, node): reg2 = None if type(node.value) == int: - #instructions.append(mips.LoadInmediateNode(reg1, node.value)) reg2 = instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.value)) else: - #src_location = self.get_var_location(node.value) - #instructions.append(mips.LoadWordNode(reg1, src_location)) reg2 = self.memory_manager.get_reg_for_var(node.value) if reg2 is None: reg2 = mips.ARG_REGISTERS[1] instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.value))) instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) - - # self.free_reg(reg1) - # self.free_reg(reg2) return instructions @@ -617,11 +472,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #Save $a0, $a1, $a2 - - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() - pushed = False reg = self.memory_manager.get_reg_for_var(node.source) if reg is None: @@ -630,40 +480,28 @@ def visit(self, node): instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) pushed = True - #src_location = self.get_var_location(node.source) - #instructions.append(mips.LoadWordNode(reg1, src_location)) instructions.extend(mips.copy_object(reg, mips.ARG_REGISTERS[3])) if pushed: instructions.extend(mips.pop_register(reg)) - #dst_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dst_location)) - reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) else: instructions.append(mips.MoveNode(reg, mips.V0_REG)) - #self.free_reg(reg1) - #self.free_reg(reg2) - return instructions @visitor.when(cil.EqualNode) def visit(self, node): instructions = [] - #TODO save $a0 $a1 $v0 - if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) elif type(node.left) == cil.VoidNode: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], 0)) else: - #location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) reg = self.memory_manager.get_reg_for_var(node.left) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) @@ -675,8 +513,6 @@ def visit(self, node): elif type(node.right) == cil.VoidNode: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], 0)) else: - #location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) reg = self.memory_manager.get_reg_for_var(node.right) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.right))) @@ -685,8 +521,6 @@ def visit(self, node): instructions.append(mips.JumpAndLinkNode("equals")) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -699,11 +533,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], location)) - - #location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], location)) reg = self.memory_manager.get_reg_for_var(node.left) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) @@ -718,9 +547,6 @@ def visit(self, node): instructions.append(mips.JumpAndLinkNode("equal_str")) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -735,23 +561,16 @@ def visit(self, node): @visitor.when(cil.GotoIfNode) def visit(self, node): - instructions = [] - #reg = self.get_free_reg() - mips_label = self.get_mips_label(node.label) - #location = self.get_var_location(node.condition) - #instructions.append(mips.LoadWordNode(reg, location)) reg = self.memory_manager.get_reg_for_var(node.condition) if reg is None: reg = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.condition))) instructions.append(mips.BranchOnNotEqualNode(reg, mips.ZERO_REG, mips_label)) - - #self.free_reg(reg) return instructions @@ -764,18 +583,11 @@ def visit(self, node): def visit(self, node): instructions = [] - #reg = self.get_free_reg() - - #obj_location = self.get_var_location(node.obj) - #instructions.append(mips.LoadWordNode(reg, obj_location)) - reg1 = self.memory_manager.get_reg_for_var(node.obj) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(reg, dest_location)) reg2 = self.memory_manager.get_reg_for_var(node.dest) if reg2 is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(reg1, 0))) @@ -783,37 +595,19 @@ def visit(self, node): else: instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0))) - #self.free_reg(reg) - return instructions @visitor.when(cil.DynamicCallNode) def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() - comp_tp = self._types[node.computed_type] method_index = list(comp_tp.methods).index(node.method) - #dest_location = self.get_var_location(node.dest) reg = self.memory_manager.get_reg_for_var(node.type) if reg is None: reg = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.type))) - #tp_location = self.get_var_location(node.type) - #instructions.append(mips.LoadAddressNode(reg1, mips.PROTO_TABLE_LABEL)) - #instructions.append(mips.LoadWordNode(reg2, tp_location)) - #instructions.append(mips.ShiftLeftLogicalNode(reg2, reg2, 2)) - #instructions.append(mips.AddUnsignedNode(reg1, reg1, reg2 )) - #instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) - #instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8))) - #instructions.append(mips.AddInmediateUnsignedNode(reg1, reg1, method_index*4)) - #instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) - #instructions.append(mips.JumpRegisterAndLinkNode(reg1)) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) - instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[1], mips.PROTO_TABLE_LABEL)) instructions.append(mips.ShiftLeftLogicalNode(mips.ARG_REGISTERS[2], reg, 2)) instructions.append(mips.AddUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[2])) @@ -829,8 +623,6 @@ def visit(self, node): else: instructions.append(mips.MoveNode(reg, mips.V0_REG)) - #self.free_reg(reg1) - #self.free_reg(reg2) if self._pushed_args > 0: instructions.append(mips.AddInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.ATTR_SIZE)) self.clean_pushed_args() @@ -855,7 +647,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #reg = self.get_free_reg() save = False reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: @@ -868,42 +659,29 @@ def visit(self, node): instructions.append(mips.AddInmediateUnsignedNode(reg, reg, tp_number*4)) instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(reg, dest_location)) if save: instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest))) - #self.free_reg(reg) - return instructions @visitor.when(cil.PlusNode) def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() reg1, reg2 = None, None if type(node.left) == int: - #instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #reg1 = self.memory_manager.get reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadInmediateNode(reg1, node.left)) else: - #left_location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(reg1, left_location)) reg1 = self.memory_manager.get_reg_for_var(node.left) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) if type(node.right) == int: - #instructions.append(mips.LoadInmediateNode(reg2, node.right)) reg2 = mips.ARG_REGISTERS[1] instructions.append(mips.LoadInmediateNode(reg2, node.right)) else: - #right_location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(reg2, right_location)) reg2 = self.memory_manager.get_reg_for_var(node.right) if reg2 is None: reg2 = mips.ARG_REGISTERS[1] @@ -916,43 +694,25 @@ def visit(self, node): else: instructions.append(mips.AddNode(reg3, reg1, reg2)) - #instructions.append(mips.AddNode(reg1, reg1, reg2)) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(reg1, dest_location)) - - #self.free_reg(reg1) - #self.free_reg(reg2) - return instructions @visitor.when(cil.MinusNode) def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() - reg1, reg2 = None, None if type(node.left) == int: - #instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #reg1 = self.memory_manager.get reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadInmediateNode(reg1, node.left)) else: - #left_location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(reg1, left_location)) reg1 = self.memory_manager.get_reg_for_var(node.left) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) if type(node.right) == int: - #instructions.append(mips.LoadInmediateNode(reg2, node.right)) - reg2 = mips.ARG_REGISTERS[1] instructions.append(mips.LoadInmediateNode(reg2, node.right)) else: - #right_location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(reg2, right_location)) reg2 = self.memory_manager.get_reg_for_var(node.right) if reg2 is None: reg2 = mips.ARG_REGISTERS[1] @@ -965,39 +725,26 @@ def visit(self, node): else: instructions.append(mips.SubNode(reg3, reg1, reg2)) - #self.free_reg(reg1) - #self.free_reg(reg2) - return instructions @visitor.when(cil.StarNode) def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() - reg1, reg2 = None, None if type(node.left) == int: - #instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #reg1 = self.memory_manager.get reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadInmediateNode(reg1, node.left)) else: - #left_location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(reg1, left_location)) reg1 = self.memory_manager.get_reg_for_var(node.left) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) if type(node.right) == int: - #instructions.append(mips.LoadInmediateNode(reg2, node.right)) reg2 = mips.ARG_REGISTERS[1] instructions.append(mips.LoadInmediateNode(reg2, node.right)) else: - #right_location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(reg2, right_location)) reg2 = self.memory_manager.get_reg_for_var(node.right) if reg2 is None: reg2 = mips.ARG_REGISTERS[1] @@ -1010,39 +757,26 @@ def visit(self, node): else: instructions.append(mips.MultiplyNode(reg3, reg1, reg2)) - #self.free_reg(reg1) - #self.free_reg(reg2) - return instructions @visitor.when(cil.DivNode) def visit(self, node): instructions = [] - #reg1 = self.get_free_reg() - #reg2 = self.get_free_reg() - reg1, reg2 = None, None if type(node.left) == int: - #instructions.append(mips.LoadInmediateNode(reg1, node.left)) - #reg1 = self.memory_manager.get reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadInmediateNode(reg1, node.left)) else: - #left_location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(reg1, left_location)) reg1 = self.memory_manager.get_reg_for_var(node.left) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) if type(node.right) == int: - #instructions.append(mips.LoadInmediateNode(reg2, node.right)) reg2 = mips.ARG_REGISTERS[1] instructions.append(mips.LoadInmediateNode(reg2, node.right)) else: - #right_location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(reg2, right_location)) reg2 = self.memory_manager.get_reg_for_var(node.right) if reg2 is None: reg2 = mips.ARG_REGISTERS[1] @@ -1056,9 +790,6 @@ def visit(self, node): else: instructions.append(mips.MoveFromLowNode(reg3)) - #self.free_reg(reg1) - #self.free_reg(reg2) - return instructions @visitor.when(cil.ComplementNode) @@ -1068,18 +799,14 @@ def visit(self, node): reg1 = None if type(node.obj) == int: - #instructions.append(mips.LoadInmediateNode(reg1, node.obj)) reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadInmediateNode(reg1, node.obj)) else: - #left_location = self.get_var_location(node.obj) - #instructions.append(mips.LoadWordNode(reg1, left_location)) reg1 = self.memory_manager.get_reg_for_var(node.obj) if reg1 is None: reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) - #dest_location = self.get_var_location(node.dest) reg2 = self.memory_manager.get_reg_for_var(node.dest) if reg2 is None: reg2 = mips.ARG_REGISTERS[1] @@ -1090,12 +817,6 @@ def visit(self, node): instructions.append(mips.ComplementNode(reg2, reg1)) instructions.append(mips.AddInmediateNode(reg2, reg2, 1)) - #instructions.append(mips.ComplementNode(reg1, reg1)) - #instructions.append(mips.AddInmediateNode(reg1, reg1, 1)) - #instructions.append(mips.StoreWordNode(reg1, dest_location)) - - #self.free_reg(reg1) - return instructions @visitor.when(cil.LessEqualNode) @@ -1106,8 +827,6 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) else: - #left_location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) reg = self.memory_manager.get_reg_for_var(node.left) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) @@ -1117,8 +836,6 @@ def visit(self, node): if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) else: - #right_location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) reg = self.memory_manager.get_reg_for_var(node.right) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.right))) @@ -1126,8 +843,6 @@ def visit(self, node): instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) instructions.append(mips.JumpAndLinkNode('less_equal')) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -1139,13 +854,10 @@ def visit(self, node): @visitor.when(cil.LessNode) def visit(self, node): instructions = [] - #Save $a0, $a1, $v0 if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.left)) else: - #left_location = self.get_var_location(node.left) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], left_location)) reg = self.memory_manager.get_reg_for_var(node.left) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.left))) @@ -1155,8 +867,6 @@ def visit(self, node): if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.right)) else: - #right_location = self.get_var_location(node.right) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], right_location)) reg = self.memory_manager.get_reg_for_var(node.right) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.right))) @@ -1164,8 +874,6 @@ def visit(self, node): instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg)) instructions.append(mips.JumpAndLinkNode('less')) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -1177,11 +885,8 @@ def visit(self, node): @visitor.when(cil.ReadStrNode) def visit(self, node): instructions = [] - #Save $v0 instructions.append(mips.JumpAndLinkNode("read_str")) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -1193,20 +898,15 @@ def visit(self, node): @visitor.when(cil.LengthNode) def visit(self, node): instructions = [] - #save $a0 $v0 - #src_location = self.get_var_location(node.source) - #dest_location = self.get_var_location(node.dest) reg = self.memory_manager.get_reg_for_var(node.source) if reg is None: reg = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], src_location)) instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) instructions.append(mips.JumpAndLinkNode("len")) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -1218,12 +918,9 @@ def visit(self, node): @visitor.when(cil.ReadIntNode) def visit(self, node): instructions = [] - #save $v0 - #dest_location = self.get_var_location(node.dest) instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) instructions.append(mips.SyscallNode()) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -1236,11 +933,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #save $a0, $a1, $a2, $v0 - - #prefix_location = self.get_var_location(node.prefix) - #suffix_location = self.get_var_location(node.suffix) - #lenght_location = self.get_var_location(node.length) reg = self.memory_manager.get_reg_for_var(node.prefix) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.prefix))) @@ -1258,13 +950,8 @@ def visit(self, node): instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], self.get_var_location(node.lenght))) else: instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg)) - #dest_location = self.get_var_location(node.dest) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], prefix_location)) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], suffix_location)) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) instructions.append(mips.JumpAndLinkNode("concat")) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: @@ -1278,12 +965,6 @@ def visit(self, node): def visit(self, node): instructions = [] - #save $a0, $a1, $a2, $v0 - - #str_location = self.get_var_location(node.str_value) - #dest_location = self.get_var_location(node.dest) - - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], str_location)) reg = self.memory_manager.get_reg_for_var(node.str_value) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.str_value))) @@ -1293,8 +974,6 @@ def visit(self, node): if type(node.index) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[1], node.index)) else: - #index_location = self.get_var_location(node.index) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], index_location)) reg = self.memory_manager.get_reg_for_var(node.index) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[1], self.get_var_location(node.index))) @@ -1304,8 +983,6 @@ def visit(self, node): if type(node.length) == int: instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[2], node.length)) else: - #lenght_location = self.get_var_location(node.length) - #instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], lenght_location)) reg = self.memory_manager.get_reg_for_var(node.length) if reg is None: instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[2], self.get_var_location(node.length))) @@ -1313,7 +990,6 @@ def visit(self, node): instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg)) instructions.append(mips.JumpAndLinkNode("substr")) - #instructions.append(mips.StoreWordNode(mips.V0_REG, dest_location)) reg = self.memory_manager.get_reg_for_var(node.dest) if reg is None: instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) @@ -1373,8 +1049,7 @@ def __init__(self): self.mark = False def get_registers_for_variables(self, instructions, params, n): - self.numered_instructions(instructions) - self.mark_leaders(instructions) + self.numbered_instructions(instructions) basic_blocks = self.divide_basics_blocks(instructions) flow_graph = RegistersAllocator.create_flow_graph(basic_blocks) gk, io = self.liveness_analysis((basic_blocks, flow_graph), params) @@ -1419,9 +1094,6 @@ def liveness_analysis(self, graph, params): io = [([], io[0][0])] + io return gk, io - # interference = RegistersAllocator.interference_compute(gk, oi) - - # RegistersAllocator.assign_registers(interference, 4) @staticmethod def interference_compute(gk, in_out): @@ -1539,7 +1211,7 @@ def create_flow_graph(blocks): #graph between blocks in a same function does not return graph @staticmethod - def numered_instructions(instructions): + def numbered_instructions(instructions): for i, instr in enumerate(instructions): instr.number = i From a1dc9ae0fe839bc60a5e27f6c818985ac53587b9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 1 Dec 2020 19:34:07 -0500 Subject: [PATCH 519/520] [report] - Add report files to ./doc --- doc/Informe/Informe.md | 232 ++++++++++++++++++++++++++++++++++++++++ doc/Informe/Informe.pdf | Bin 0 -> 101753 bytes doc/Informe/grammar.pdf | Bin 0 -> 40851 bytes doc/Informe/grammar.tex | 90 ++++++++++++++++ 4 files changed, 322 insertions(+) create mode 100644 doc/Informe/Informe.md create mode 100644 doc/Informe/Informe.pdf create mode 100644 doc/Informe/grammar.pdf create mode 100644 doc/Informe/grammar.tex diff --git a/doc/Informe/Informe.md b/doc/Informe/Informe.md new file mode 100644 index 00000000..13da033e --- /dev/null +++ b/doc/Informe/Informe.md @@ -0,0 +1,232 @@ +# Informe de Complementos de Compilación +## Datos Generales +### Autores +- Miguel Tenorio Potrony +- Mauricio Lázaro Perdomo Cortés +- Lázaro Raúl Iglesias Vera + +### Sobre el proyecto +Para la implementación de este proyecto se tomaron como base, los proyectos realizados durante 3er año, donde se desarrollaron las fases de chequeo e inferencia de tipos, además de parsing. El código de dichos proyectos conserva su estructura pero estuvo sujeto a cambios y mejoras. + +La mayoría de nuestras implementaciones siguen las ideas y utilizan las herramientas dadas en clase durante 3er año. + +Todas las fases del proceso de compilación y ejecución serán explicadas a continuación. + + +## Pipeline +Como se puede apreciar en [main.py](https://github.com/2kodevs/cool-compiler-2020/blob/master/src/main.py) el pipeline de nuestro proceso de compilación es: + +1. Lexer +2. Parsing +3. Recolección de tipos +4. Construcción de tipos +5. Chequeo/Inferencia de tipos +6. Verificación de tipos +7. Traducción de Cool a CIL +8. Traducción de CIL a MIPS + +Cada parte del proceso será discutida en detalle durante las siguientes secciones. + +Como se puede apreciar en la etapa #5 del proceso, el chequeo e inferencia de tipos se realizan al unísono, sin embargo cada parte se explicará en secciones separadas y se hará notar por qué se decidió realizarlas al mismo tiempo. + +## Lexer + +Para el proceso de lexer y tokenización se utilizó el paquete PLY. Se creó un un lexer que consta de tres estados: + + - INITIAL + - comments + - strings + +Para cada uno de estos estados se definieron las expresiones regulares que representan cada uno de los tokens posibles, y se +manejan otras variables que conforman el estado del lexer, como la línea actual. + +## Parsing +Para el proceso de parsing se utilizó el parser LR1 y la gramática de Cool que fueron implementados para el proyecto de 3er año sobre chequeo de tipos. + +Fue necesario modificar la salida del Parser para poder devolver la información referente al token de error en caso de que alguna falla fuera detectada. + +Dado que los proyectos llevados a cabo previamente fueron desarrollados para mini-Cool, se hizo necesario modificar la gramática, y se obtuvo como resultado: + +### Gramática de Cool +La grámatica implementada es S-Atributada. Una descripción de los símbolos y producciones de la grámatica, se puede ver en [grammar](https://github.com/2kodevs/cool-compiler-2020/blob/master/src/doc/grammar.pdf) + +## Recolección de tipos +Durante la recolección de tipos se visitan todas las declaraciones de clases, se crean los tipos asociados a ellas y se valida la correctitud de las mismas. + +**Errores detectados**: +- Herencia cíclica +- Redefinición de clases +- Nombres de clase no válidos + +## Construcción de tipos +A los tipos creados en la fase anterior se le añaden todos sus atributos y métodos. Además se verifica que se cumplan los requerimientos de un programa válido de Cool q son tener una clase `Main` con su método `main`. + +**Errores detectados**: +- Problemas de nombrado de atributos y métodos +- Redefinición de atributos +- Redefinición incorrecta de métodos +- Uso de tipos no definidos +- No definición de la clase `Main` o su método `main` +- Incorrecta definición del método `main` +- Mal uso de herencia + +## Chequeo de tipos +En esta fase se evalúa la correctitud de todas las expresiones del lenguaje y se decide el tipo estático de cada una de ellas según lo establecido en el manual de [Cool](https://github.com/2kodevs/cool-compiler-2020/blob/master/doc/cool-manual.pdf). + +**Errores detectados**: +- Incompatibilidad de tipos +- Uso de tipos no definidos +- Uso de variables, tipos y métodos no definidos +- mal usos de `self` y `SELF_TYPE` +- mal usos del `case` + +## Inferencia de tipos +Para la implementación de esta fase se expandió el comportamiento del visitor encargado del chequeo de tipos, razón por la cual ambos procesos se realizan en la misma fase. + +Para lograr la inferencia de tipos, se realizó un algoritmo de punto fijo en el cual mediante repeticiones sucesivas del proceso de inferencia se van definiendo los tipos de aquellas variables declaradas como `AUTO_TYPE`. + +### Idea +Una variable en Cool dada su utilización puede definir dos conjuntos + +1. Tipos a los que se conforma (**Ancestros**) +2. Tipos que se conforman a ella (**Descendientes**) + +Dados los dos conjuntos anteriores se puede decidir si una variable `AUTO_TYPE` puede ser inferida correctamente o no. + +Ambos conjuntos recibieron un nombre intuitivo mencionado anteriormente en **negrita** para hacer referencia a su contenido. + +El tipo que se decida otorgar(inferir) a la variable en cuestión, llamémosle _**T**_, deberá conformarse a todos los tipos del conjunto 1. Al mismo tiempo todos los tipos del conjunto 2 deberán conformarse a él. + +Dicho lo anterior y dado el hecho de que un tipo *A* se conforma a un tipo *B* solamente si *B* es ancestro de *A*, podemos notar que: + +1. El tipo a seleccionar debe ser un ancestro del **Menor Ancestro Común** (**LCA** por sus siglas en inglés) a todos los nodos del conjunto 2, llamémosle *N*. En otras palabras el primer tipo que es ancestro de todos los tipos en el conjunto 2. +2. Como todos los tipos del conjunto 1 necesitan ser ancestros de _**T**_, todos pertenecerán al camino que se forma desde _**T**_ hasta *Object* en el árbol de tipos, por tanto _**T**_ necesita ser descendiente del primero que aparezca en el camino mencionado y pertenezca al conjunto 1, llamémosle *M*. +3. Tomando el operador **<=** para referirnos a la relación *ser ancestro de*, se puede afirmar que _**T**_ es de la forma _**N <= T <= M**_, o lo que es lo mismo _**T**_ podría ser cualquier tipo en el camino de *N* a *M*. + +> El nodo que representa el **LCA** siempre existe dado que el árbol de tipos es único, por tanto en caso extremo *Object* siempre será válido como ancestro a todos los tipos. + +El algortimo implementado tras cada recorrido del **AST**(Árbol de sintaxis abstracta) infiere el tipo de todas aquellas variables de las cuales se tenga información, seleccionando como tipo inferido siempre el que representa a *N*. + +Al ser este algoritmo una extensión del chequeo de tipos, mientras se van infiriendo los tipos se valida que los mismos no ocasionen error. +> En todo lo anterior se asume que todo tipo es ancestro y descendiente de sí mismo. + +**Errores detectados**: +- Mal usos de `AUTO_TYPE` en casos donde no se cumpla que _**N <= M**_ o todos los tipos en el conjunto 1 no se encuentren en un camino del árbol de tipos +- Todos los errores de chequeo semántico que existan en el código o surgan tras la inferencia de una o varias variables. + +## Verificación de tipos +Esta fase surge dado que tras el proceso de inferencia puede haber ocurrido un error que durante el chequeo semántico no se valida. Dado que permitimos *AUTO_TYPE* en los parametros de las funciones, al terminar la inferencia pueden generarse conflictos de mala redefinición de métodos, los cuales son chequeados en la fase de Construcción de los tipos (etapa #4). Por tanto la única función de esta fase es verificar la correctitud de los tipos. + +**Errores detectados**: +- Mala redefinición de métodos ocacionada por la inferencia de tipos + +## Traducción a CIL +En esta etapa del proceso de compilación, requirió especial atención la generación de las expresiones *case*. Para ello se requiere ordenar las instrucciones de tal modo que se asegure el emparejamiento del tipo de la expresión principal con el tipo más específico declarado en las ramas del *case*. + +Primero por cada rama **b** se cuentan cuántos tipos declarados en las demás ramas se conforman a **b**, creando de este modo una tupla `(cantidad, tipo declarado en b)`. +Luego se ordenan todas estas tuplas por su primer elemento, obteniendo así una secuencia ordenada donde el primero elemento representa la rama cuyo tipo declarado se encuentra en el nivel más bajo en la jerarquía de tipos del programa. + +Luego por cada rama **b** de esta secuencia, se obtienen todos los tipos del programa que conforman a **b**, y por cada uno de estos que no haya sido tomado en cuenta en el procesamiento de ramas anteriores, se generan las instrucciones necesarias para comprobar si el tipo de la expresión principal del *case* coincide con él. En caso de coincidencia, se salta al bloque de las instrucciones generadas por el cuerpo de **b**; si no entonces se procede a comprobar con el tipo siguiente. Nótese que no se repiten comprobaciones. + +**Errores detectados**: +- Dispatch estático o dinámico desde un objeto void +- Expresión principal de un *case* tiene valor `void` +- Ejecución de un *case* sin que ocurra algún emparejamiento con alguna rama. +- División por cero +- Substring fuera de rango + +> Aunque estos errores realmente se detectan en ejecución, es en esta fase que se genera el código que permite detectarlos. + +## Traducción a MIPS +En la fase de generación de código `MIPS` se enfrentaron tres problemas fundamentales: + + - Estructura de los objetos en memoria. + - Definición de tipos en memoria. + - Elección de registros. + +### Estructura de los objetos en memoria. +Determinar el modelo que seguirían los objetos en la memoria fue un paso fundamental para la toma de múltiples decisiones tanto en la generación de código `CIL` como `MIPS`. Los objetos en memoria siguen el siguiente modelo: + +```| Tipo | Tamaño | Tabla de dispatch | -- Atributos -- | Marca de objeto |``` + - Tipo: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un entero e indica el tipo del objeto. + - Tamaño: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como un entero e indica el tamaño en `palabras` del objeto. + - Tabla de dispatch: Esta sección tiene tamaño 1 `palabra`, el valor aquí encontrado se interpreta como una dirección de memoria e indica el inicio de la tabla de dispatch del objeto. La tabla de dispatch del objeto es un segmento de la memoria donde interpretamos cada `palabra` como la dirección a uno de los métodos del objeto. + - Atributos: Esta sección tiene tamaño **N** `palabras` donde **N** es la cantidad de atributos que conforman el objeto, cada una de las `palabras` que conforman esta sección representa el valor de un atributo del objeto. + - Marca de objeto: Esta sección tiene tamaño 1 `palabra`, es un valor usado para marcar que esta zona de la memoria corresponde a un objeto, se añadió con el objetivo de hacer menos propenso a fallos la tarea de identificar objetos en memoria en el `Garbage Collector`. + +### Definición de tipos en memoria. +Un tipo está representado por tres estructuras en la memoria: + - Una dirección a una cadena alfanumérica que representa el nombre del tipo. + - Un prototipo que es una especie de plantilla que se utiliza en la creación de los objetos. Cuando se crea un objeto este prototipo es copiado al segmento de memoria asignado al objeto. Un prototipo es un objeto válido por lo que tiene exactamente la misma estructura explicada anteriormente. El prototipo es también la solución escogida para el problema de los valores por defecto de los objetos. + - Una tabla de dispatch que como se explicó anteriormente contiene las direcciones de los métodos del objeto. +Existe una tabla de prototipos (nombres) donde se puede encontrar el prototipo (nombre) de un tipo específico, utilizando como índice el valor que representa al tipo. + +### Elección de registros. +La elección de registros fue un proceso que se decidió optimizar para disminuir la utilización de las operaciones `lw` y `sw` en `MIPS` que como se sabe, añaden una demora considerable a nuestros programas por el tiempo que tarda en realizarse un operación de escritura o lectura en la memoria. +El proceso de elección de registros se realiza para cada función y consta de los siguientes pasos: + - Separación del código en bloques básicos: + + Para obtener los bloques básicos primero se hace un recorrido por las instrucciones de la función marcando los líderes. Son considerados líderes las instrucciones de tipo `Label` y las instrucciones que tengan como predecesor un instrucción de tipo `Goto` o `Goto if`. Luego de tener marcados los líderes, se obtienen los bloques que serán los conjuntos de instrucciones consecutivas que comienzan con un líder y terminan con la primera instrucción que sea predecesor de un líder (notar que un bloque puede estar formado por una sola instrucción). + + - Creación del grafo de flujo: + + Este es un grafo dirigido que indica los caminos posibles entre los bloques básicos su elaboración es bastante sencilla: si la última instrucción de un bloque es un `Goto`, entonces se añadirá una arista desde este bloque hacia el bloque iniciado por la instrucción `Label` a la que hace referencia el `Goto`; si la última instrucción es de tipo `Goto if`, entonces se añadirán dos aristas una hacia el bloque que comienza con la instrucción `Label` a la que se hace referencia, y otra hacia el bloque que comienza con la instrucción siguiente en la función; en el caso de que la última instrucción sea de cualquier otro tipo, se colocará una sola arista desde el bloque actual hacia el bloque que comienza con la instrucción siguiente en la función. + + - Análisis de vida de las variables: + + En este procedimiento se computan cinco conjuntos para cada instrucción **I**: `succ`, `gen`, `kill`, `in` y `out`. `succ` contiene las instrucciones que se pueden ejecutar inmediatamente después de la instrucción **I**; `gen` contiene las variables de las que se necesita el valor en la instrucción **I**; `kill` contiene las variables a las que se les asigna un valor en la instrucción **I**; `in` contiene las variables que pueden estar vivas al llegar a la instrucción **I**, y `out` contiene las variables que pueden estar vivas luego de ejecutada la instrucción **I**. + + - Creación del grafo de interferencia: + + Los vértices de este grafo serán las variables que se utilizan en la función y existirá una arista entre los vértices **x** y **y**, si las variables que representan esos nodos interfieren. Dos variables interfieren si existe alguna instrucción **I** tal que **x** pertenezca al `kill` de **I** y **y** pertenezca al `out` de **I**. + + - Asignación de registros: + + Contando con el grafo de interferencia, se asignan registros a las variables de forma tal que dos variables que interfieran no se les asigne el mismo registro, esto puede verse como el problema de colorear un grafo con **N** colores siendo **N** la cantidad de registros que se tienen. Es conocido que este problema es *NP* por lo que para asignar los registros se usa una heurística muy sencilla que consiste en lo siguiente: + + Primero se va eliminando del grafo y colocando en una pila cada nodo que tenga menos de N vecinos, se nota que todos estos elementos pueden ser coloreados sin problemas. Si en algún momento no existe algún nodo con menos de N vecinos, se tomará un nodo al azar; este proceso terminará cuando no queden nodos en el grafo. Luego se va sacando cada nodo de la pila y se le asigna un registro que no esté usado por alguno de los nodos que eran vecinos de este en el momento en que se eliminó del grafo, en el caso de que existan más de un nodo posible, se le asigna el menor, en caso de que no exista nodo posible la variable no tendrá registro y su valor permanecerá en la memoria. + +**Errores detectados**: +- Heap overflow + +## Ejecución +Para ejecutar el proyecto se necesita tener instalado `Python` y el conjunto de dependencias listado en [requirements.txt](https://github.com/2kodevs/cool-compiler-2020/blob/master/requirements.txt). + +Para instalar las dependencias puede utilizar: +```bash +make install +``` +Una vez estén instaladas las dependencias, puede compilar y ejecutar cualquier archivo de código cool utilizando el comando: +```bash +make main CODE=.cl +``` +>Para usar `make` necesita estar en la dirección `/src` + +## Estructura +Los archivos del proyecto se encuentran modularizados de la siguiente manera: + +1. **core** + 1. **cmp** + 1. **cool** + 2. **parser** + 2. **lexer** + 3. **visitors** + 1. **type_check** + 2. **cil** + 3. **mips** + +**cmp** contiene todos los archivos heredados de las clases de 3er año y proyectos anteriores. + +**cool** contiene el *AST*, Gramática y Parser de Cool + +**parser** contiene la implementación parser LR1 utilizada + +**lexer** todo lo referente a lexer y tokenización + +**visitor** contiene la implementación del patrón visitor + +**type_checking** fases de la #3 a la #6 + +**cil** traducción a cil + +**mips** traducción a mips diff --git a/doc/Informe/Informe.pdf b/doc/Informe/Informe.pdf new file mode 100644 index 0000000000000000000000000000000000000000..30f0f7cdb8d3138c0824ecca2fd6eb51a649523f GIT binary patch literal 101753 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 AIsgCw literal 0 HcmV?d00001 diff --git a/doc/Informe/grammar.pdf b/doc/Informe/grammar.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bdf52cdc81481ba3aa8ed426af3f670739811964 GIT binary patch literal 40851 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 Date: Wed, 2 Dec 2020 17:05:06 -0500 Subject: [PATCH 520/520] Add missing cases to UsedRegisterFinder visitor --- src/core/visitors/mips/cil_to_mips.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/core/visitors/mips/cil_to_mips.py b/src/core/visitors/mips/cil_to_mips.py index 88408382..47cd52d8 100644 --- a/src/core/visitors/mips/cil_to_mips.py +++ b/src/core/visitors/mips/cil_to_mips.py @@ -1042,6 +1042,33 @@ def visit(self, node): def visit(self, node): self.used_registers.add(mips.RA_REG) + @visitor.when(mips.AddUnsignedNode) + def visit(self, node): + self.used_registers.add(node.dest) + + @visitor.when(mips.ShiftLeftLogicalNode) + def visit(self, node): + self.used_registers.add(node.dest) + + @visitor.when(mips.AddNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.SubNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.MultiplyNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.ComplementNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.MoveFromLowNode) + def visit(self, node): + self.used_registers.add(node.reg) #Change Name class RegistersAllocator: