From 978129e57ae8a2c364946e3f78becc55f9d10249 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera <49727740+cicr99@users.noreply.github.com> Date: Tue, 1 Sep 2020 12:15:05 -0400 Subject: [PATCH 001/138] Initial commit --- .gitignore | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 21 +++++++++ README.md | 1 + 3 files changed, 151 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b6e47617d --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..37ad62385 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Carmen I Cabrera + +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. diff --git a/README.md b/README.md new file mode 100644 index 000000000..132cbb39d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# TypeInferencer \ No newline at end of file From b77ebfcca09ca86e29014bfa249516d2370822db Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 2 Sep 2020 21:12:48 -0400 Subject: [PATCH 002/138] Creating the nodes of the AST --- cmp/ast.py | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 cmp/ast.py diff --git a/cmp/ast.py b/cmp/ast.py new file mode 100644 index 000000000..373ef2e94 --- /dev/null +++ b/cmp/ast.py @@ -0,0 +1,125 @@ +class Node: + pass + +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations + +class DeclarationNode(Node): + pass +class ExpressionNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None): + self.id = idx + self.parent = parent + self.features = features + +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 AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr=None): + self.id = idx + self.type = typex + self.expr = expr + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr): + self.id = idx + self.expr = expr + +class CallNode(ExpressionNode): + def __init__(self, obj, idx, args, cast_type=None): + self.obj = obj + self.id = idx + self.args = args + self.type = cast_type + +class CaseNode(ExpressionNode): + def __init__(self, expr, branch_list): + self.expr = expr + self.branch_list = branch_list + +class BlockNode(ExpressionNode): + def __init__(self, expr_list): + self.expr_list = expr_list + +class LoopNode(ExpressionNode): + def __init__(self, cond, body): + self.condition = cond + self.body = body + +class ConditionalNode(ExpressionNode): + def __init__(self, cond, then_body, else_body): + self.condition = cond + self.then_body = then_body + self.else_body = else_body + +class LetNode(ExpressionNode): + def __init__(self, id_list, body): + self.id_list = id_list + self.body = body + +class AtomicNode(ExpressionNode): + def __init__(self, lex): + self.lex = lex + +class BinaryNode(ExpressionNode): + def __init__(self, left, right): + self.left = left + self.right = right + +class UnaryNode(ExpressionNode): + def __init__(self, expr): + self.expr = expr + +class ConstantNumNode(AtomicNode): + pass + +class ConstantStringNode(AtomicNode): + pass + +class ConstantBoolNode(AtomicNode): + pass + +class VariableNode(AtomicNode): + pass + +class InstantiateNode(AtomicNode): + pass + +class PlusNode(BinaryNode): + pass + +class MinusNode(BinaryNode): + pass + +class StarNode(BinaryNode): + pass + +class DivNode(BinaryNode): + pass + +class LeqNode(BinaryNode): + pass + +class LessNode(BinaryNode): + pass + +class EqualNode(BinaryNode): + pass + +class VoidNode(UnaryNode): + pass + +class NotNode(UnaryNode): + pass + +class NegNode(UnaryNode): + pass \ No newline at end of file From 84d1b36e3f7389a98cba424a7ac2ae6884d83e57 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 2 Sep 2020 21:18:08 -0400 Subject: [PATCH 003/138] Creating the attributed grammar for COOL - pycompiler.py contains all the classes that are necessary to create a grammar - grammar.py contains the declaration of the language: terminals, non terminals, main symbol and the productions, each of them with its attribute. --- cmp/grammar.py | 136 ++++++++++++ cmp/pycompiler.py | 512 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 648 insertions(+) create mode 100644 cmp/grammar.py create mode 100644 cmp/pycompiler.py diff --git a/cmp/grammar.py b/cmp/grammar.py new file mode 100644 index 000000000..29b517f6a --- /dev/null +++ b/cmp/grammar.py @@ -0,0 +1,136 @@ +from pycompiler import Grammar +from ast import * + +# grammar +G = Grammar() + + +# non-terminals +program = G.NonTerminal('', startSymbol=True) +class_list, def_class = G.NonTerminals(' ') +feature_list, def_attr, def_func = G.NonTerminals(' ') +param_list, param, expr_list = G.NonTerminals(' ') +expr, comp, arith, term, factor, atom = G.NonTerminals(' ') +s_comp, s_arith, s_term, s_factor = G.NonTerminals(' ') +func_call, arg_list, args = G.NonTerminals(' ') +case_def, block_def, loop_def, cond_def, let_def, assign_def = G.NonTerminals(' ') +branch_list, branch = G.NonTerminals(' ') +iden_list, iden = G.NonTerminals(' ') + + +# terminals +classx, inherits = G.Terminals('class inherits') +let, inx = G.Terminals('let in') +case, of, esac = G.Terminals('case of esac') +whilex, loop, pool = G.Terminals('while loop pool') +ifx, then, elsex, fi = G.Terminals('if then else fi') +isvoid, notx = G.Terminals('isvoid not') +semi, colon, comma, dot, opar, cpar, ocur, ccur, larrow, rarrow, at = G.Terminals('; : , . ( ) { } <- => @') +equal, plus, minus, star, div, less, leq, neg = G.Terminals('= + - * / < <= ~') +idx, num, stringx, boolx, new = G.Terminals('id int string bool new') + + +# productions + +program %= class_list, lambda h,s: ProgramNode(s[1]) + +class_list %= def_class, lambda h,s: [s[1]] +class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] + +def_class %= classx + idx + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[4]) +def_class %= classx + idx + inherits + idx + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[6], s[4]) + +feature_list %= G.Epsilon, lambda h,s: [] +feature_list %= def_attr + feature_list, lambda h,s: [s[1]] + s[2] +feature_list %= def_func + feature_list, lambda h,s: [s[1]] + s[2] + +def_attr %= idx + colon + idx + semi, lambda h,s: AttrDeclarationNode(s[1], s[3]) +def_attr %= idx + colon + idx + larrow + expr + semi, lambda h,s: AttrDeclarationNode(s[1], s[3], s[5]) + +def_func %= idx + opar + cpar + colon + idx + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], [], s[5], s[7]) +def_func %= idx + opar + param_list + cpar + colon + idx + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) + +param_list %= param, lambda h,s: [s[1]] +param_list %= param + comma + param_list, lambda h,s: [s[1]] + s[3] +param %= idx + colon + idx + +expr %= comp, lambda h,s: s[1] +expr %= s_comp, lambda h,s: s[1] + +comp %= arith, lambda h,s: s[1] +comp %= arith + leq + arith, lambda h,s: LeqNode(s[1], s[3]) +comp %= arith + less + arith, lambda h,s: LessNode(s[1], s[3]) +comp %= arith + equal + arith, lambda h,s: EqualNode(s[1], s[3]) + +arith %= term, 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]) + +term %= factor, 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]) + +factor %= atom, lambda h,s: s[1] +factor %= opar + expr + cpar, lambda h,s: s[2] +factor %= isvoid + factor, lambda h,s: VoidNode(s[2]) +factor %= neg + factor, lambda h,s: NegNode(s[2]) +factor %= func_call, lambda h,s: s[1] +factor %= case_def, lambda h,s: s[1] +factor %= block_def, lambda h,s: s[1] +factor %= loop_def, lambda h,s: s[1] +factor %= cond_def, lambda h,s: s[1] + +atom %= num, lambda h,s: ConstantNumNode(s[1]) +atom %= stringx, lambda h,s: ConstantStringNode(s[1]) +atom %= boolx, lambda h,s: ConstantBoolNode(s[1]) +atom %= idx, lambda h,s: VariableNode(s[1]) +atom %= new + idx, lambda h,s: InstantiateNode(s[2]) + +func_call %= idx + opar + arg_list + cpar, lambda h,s: CallNode(VariableNode('self'), s[1], s[3]) +func_call %= factor + dot + idx + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[3], s[5]) +func_call %= factor + at + idx + dot + idx + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[5], s[7], s[3]) + +arg_list %= G.Epsilon, lambda h,s: [] +arg_list %= args, lambda h,s: s[1] +args %= expr, lambda h,s: [s[1]] +args %= expr + comma + args , lambda h,s: [s[1]] + s[3] + +case_def %= case + expr + of + branch_list + esac, lambda h,s: CaseNode(s[2], s[4]) +branch_list %= branch, lambda h,s: [s[1]] +branch_list %= branch + branch_list, lambda h,s: [s[1]] + s[2] +branch %= idx + colon + idx + rarrow + expr + semi, lambda h,s: (s[1], s[3], s[5]) + +block_def %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2]) +expr_list %= expr + semi, lambda h,s: [s[1]] +expr_list %= expr + semi + expr_list, lambda h,s: [s[1]] + s[3] + +loop_def %= whilex + expr + loop + expr + pool, lambda h,s: LoopNode(s[2], s[4]) + +cond_def %= ifx + expr + then + expr + elsex + expr + fi, lambda h,s: ConditionalNode(s[2], s[4], s[6]) + +s_comp %= s_arith, lambda h,s: s[1] +s_comp %= arith + leq + s_arith, lambda h,s: LeqNode(s[1], s[3]) +s_comp %= arith + less + s_arith, lambda h,s: LessNode(s[1], s[3]) +s_comp %= arith + equal + s_arith, lambda h,s: EqualNode(s[1], s[3]) + +s_arith %= s_term, lambda h,s: s[1] +s_arith %= arith + plus + s_term, lambda h,s: PlusNode(s[1], s[3]) +s_arith %= arith + minus + s_term, lambda h,s: MinusNode(s[1], s[3]) + +s_term %= s_factor, lambda h,s: s[1] +s_term %= term + star + s_factor, lambda h,s: StarNode(s[1], s[3]) +s_term %= term + div + s_factor, lambda h,s: DivNode(s[1], s[3]) + +s_factor %= notx + expr, lambda h,s: NotNode(s[2]) +s_factor %= let_def, lambda h,s: s[1] +s_factor %= assign_def, lambda h,s: s[1] +s_factor %= isvoid + s_factor, lambda h,s: VoidNode(s[2]) +s_factor %= neg + s_factor, lambda h,s: NegNode(s[2]) + +let_def %= let + iden_list + inx + expr, lambda h,s: LetNode(s[2], s[4]) +iden_list %= iden, lambda h,s: [s[1]] +iden_list %= iden + comma + iden_list, lambda h,s: [s[1]] + s[3] +iden %= idx + colon + idx, lambda h,s: (s[1], s[3], None) +iden %= idx + colon + idx + larrow + expr, lambda h,s: (s[1], s[3], s[5]) + +assign_def %= idx + larrow + expr, lambda h,s: AssignNode(s[1], s[3]) \ No newline at end of file diff --git a/cmp/pycompiler.py b/cmp/pycompiler.py new file mode 100644 index 000000000..3ef02deeb --- /dev/null +++ b/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 From c2d833ac000c3ee2748ab4e3e873b2858c5bce01 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 2 Sep 2020 21:19:38 -0400 Subject: [PATCH 004/138] Adding files to compute an LR1Parser --- cmp/automata.py | 217 ++++++++++++++++++++++++++++++++++++++ cmp/tools.py | 184 ++++++++++++++++++++++++++++++++ cmp/utils.py | 275 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 676 insertions(+) create mode 100644 cmp/automata.py create mode 100644 cmp/tools.py create mode 100644 cmp/utils.py diff --git a/cmp/automata.py b/cmp/automata.py new file mode 100644 index 000000000..a8d56f8c5 --- /dev/null +++ b/cmp/automata.py @@ -0,0 +1,217 @@ +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 _repr_png_(self, name): + try: + return self.graph().write_png(f'{name}.png') + 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] + + +def empty_formatter(state): + return '' \ No newline at end of file diff --git a/cmp/tools.py b/cmp/tools.py new file mode 100644 index 000000000..b019777c3 --- /dev/null +++ b/cmp/tools.py @@ -0,0 +1,184 @@ +from utils import ShiftReduceParser, ContainerSet +from pycompiler import Item +from automata import State + + +def upd_table(table, head, symbol, production): + if not head in table: + table[head] = {} + if not symbol in table[head]: + table[head][symbol] = [] + if production not in table[head][symbol]: + table[head][symbol].append(production) + return (len(table[head][symbol]) <= 1) + +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 KeyError: + 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 expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() + for preview in item.Preview(): + lookaheads.hard_update(compute_local_first(firsts, preview)) + + assert not lookaheads.contains_epsilon + 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: + 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/cmp/utils.py b/cmp/utils.py new file mode 100644 index 000000000..a5b6c7ae1 --- /dev/null +++ b/cmp/utils.py @@ -0,0 +1,275 @@ +from 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:]) + + 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] + if action is self.SHIFT: + operations.append(self.SHIFT) + stack.append(tag) + cursor += 1 + elif action is self.REDUCE: + operations.append(self.REDUCE) + if len(tag.Right): + stack = stack[:-len(tag.Right)] + stack.append(list(self.goto[stack[-1]][tag.Left])[0]) + output.append(tag) + elif action is ShiftReduceParser.OK: + stack.pop() + assert stack.pop() == self.G.startSymbol + assert len(stack) == 1 + return output if not get_shift_reduce else (output, operations) + else: + raise ValueError + + From 27213aab008670f0e49c2a53dcf0c8ea6b22072f Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 2 Sep 2020 21:25:13 -0400 Subject: [PATCH 005/138] Adding visitor class --- cmp/visitor.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 cmp/visitor.py diff --git a/cmp/visitor.py b/cmp/visitor.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/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) From 56eda5c7e732f509d9fca0a7d529b25942fc8cc1 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sat, 5 Sep 2020 20:38:18 -0400 Subject: [PATCH 006/138] Fixing the file names from where the code was importing --- cmp/grammar.py | 6 +++--- cmp/tools.py | 6 +++--- cmp/utils.py | 3 --- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cmp/grammar.py b/cmp/grammar.py index 29b517f6a..cbe7aaa2f 100644 --- a/cmp/grammar.py +++ b/cmp/grammar.py @@ -1,5 +1,5 @@ -from pycompiler import Grammar -from ast import * +from cmp.pycompiler import Grammar +from cmp.ast import * # grammar G = Grammar() @@ -52,7 +52,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 + idx +param %= idx + colon + idx, lambda h,s: (s[1], s[3]) expr %= comp, lambda h,s: s[1] expr %= s_comp, lambda h,s: s[1] diff --git a/cmp/tools.py b/cmp/tools.py index b019777c3..9e6f16a6b 100644 --- a/cmp/tools.py +++ b/cmp/tools.py @@ -1,6 +1,6 @@ -from utils import ShiftReduceParser, ContainerSet -from pycompiler import Item -from automata import State +from cmp.utils import ShiftReduceParser, ContainerSet +from cmp.pycompiler import Item +from cmp.automata import State def upd_table(table, head, symbol, production): diff --git a/cmp/utils.py b/cmp/utils.py index a5b6c7ae1..3c608744c 100644 --- a/cmp/utils.py +++ b/cmp/utils.py @@ -265,9 +265,6 @@ def __call__(self, w, get_shift_reduce=False): stack.append(list(self.goto[stack[-1]][tag.Left])[0]) output.append(tag) elif action is ShiftReduceParser.OK: - stack.pop() - assert stack.pop() == self.G.startSymbol - assert len(stack) == 1 return output if not get_shift_reduce else (output, operations) else: raise ValueError From 35e14417f158ee1b02583e900313a18ee2a1e040 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sat, 5 Sep 2020 20:42:17 -0400 Subject: [PATCH 007/138] Adding a tokenizer This is a provisional tokenizer. The COOL code must be written separated by spaces, so that it can create the list of tokens. This tokenizer can be replaced for a lexer later. --- cmp/lexer.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 cmp/lexer.py diff --git a/cmp/lexer.py b/cmp/lexer.py new file mode 100644 index 000000000..cbbfe7106 --- /dev/null +++ b/cmp/lexer.py @@ -0,0 +1,27 @@ +from cmp.utils import Token, tokenizer +from cmp.grammar import * + +fixed_tokens = { t.Name: Token(t.Name, t) for t in G.terminals if t not in { idx, num }} + +@tokenizer(G, fixed_tokens) +def tokenize_text(token): + lex = token.lex + try: + float(lex) + return token.transform_to(num) + except ValueError: + return token.transform_to(idx) + +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])) From 071a12aee84c3cfd0380073e75e0708af741badd Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sat, 5 Sep 2020 20:43:48 -0400 Subject: [PATCH 008/138] Adding the methods to create the AST --- cmp/evaluation.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 cmp/evaluation.py diff --git a/cmp/evaluation.py b/cmp/evaluation.py new file mode 100644 index 000000000..034033567 --- /dev/null +++ b/cmp/evaluation.py @@ -0,0 +1,33 @@ +from cmp.pycompiler import EOF +from cmp.tools 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 From e8e00cc5c9a42a3cc1ed59478473e1505aa33408 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sat, 5 Sep 2020 20:45:09 -0400 Subject: [PATCH 009/138] Adding Format Visitor This visitor allows to print each node of the ast, which includes information about it --- cmp/formatter.py | 113 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 cmp/formatter.py diff --git a/cmp/formatter.py b/cmp/formatter.py new file mode 100644 index 000000000..5102d4e73 --- /dev/null +++ b/cmp/formatter.py @@ -0,0 +1,113 @@ +import cmp.visitor as visitor +from cmp.ast import * + +class FormatVisitor(object): + @visitor.on('node') + def visit(self, node, tabs=0): + 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(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(AttrDeclarationNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {node.type}' + if node.expr is not None: + expr = self.visit(node.expr, tabs + 1) + ans = f'{ans} <- \n{expr}' + return f'{ans}' + + @visitor.when(AssignNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AssignNode: {node.id} <- ' + expr = self.visit(node.expr, tabs + 1) + return f'{ans}\n{expr}' + + @visitor.when(CallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + cast = '' if node.type is None else f'@{node.type}' + ans = '\t' * tabs + f'\\__CallNode: {cast}.{node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{obj}\n{args}' + + @visitor.when(CaseNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__CaseNode: case of [ , ..., ]' + expr = self.visit(node.expr, tabs + 1) + branches = [] + for branch in node.branch_list: + branches.append('\t' * (tabs + 1) + f'{branch[0]} : {branch[1]} => \n{self.visit(branch[2], tabs + 2)}') + branches = '\n'.join(branches) + return f'{ans}\n{expr}\n{branches}' + + @visitor.when(BlockNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__BlockNode: {{ ; ... ; ; }}' + exprs = '\n'.join(self.visit(expr, tabs + 1) for expr in node.expr_list) + return f'{ans}\n{exprs}' + + @visitor.when(LoopNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__LoopNode: while loop pool' + cond = self.visit(node.condition, tabs + 1) + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{cond}\n{body}' + + @visitor.when(ConditionalNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ConditionalNode: if then else fi' + cond = self.visit(node.condition, tabs + 1) + then_body = self.visit(node.then_body, tabs + 1) + else_body = self.visit(node.else_body, tabs + 1) + return f'{ans}\n{cond}\n{then_body}\n{else_body}' + + @visitor.when(LetNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__LetNode: let in ' + expr = self.visit(node.body, tabs + 1) + iden_list = [] + for item in node.id_list: + iden = '\t' * (tabs + 1) + f'{item[0]} : {item[1]}' + if item[2] is not None: + iden = f'{iden} <- \n{self.visit(item[2], tabs + 2)}' + iden_list.append(iden) + iden_list = '\n'.join(iden_list) + return f'{ans}\n{iden_list}\n{expr}' + + @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(UnaryNode) + def visit(self, node, tabs=0): + expr = self.visit(node.expr, tabs + 1) + return '\t' * tabs + f'\\__ {node.__class__.__name__} \n{expr}' + + @visitor.when(InstantiateNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ InstantiateNode: new {node.lex}' \ No newline at end of file From 710eb5d4981f0d9452a9f8609711d705aab6fcf8 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sat, 5 Sep 2020 20:51:03 -0400 Subject: [PATCH 010/138] Creating the pipeline the main.py file contains the initial method run_pipeline. Right now, this method takes the text (COOL code) and takes it to the point of constructing the ast. It also contains a case to test the program --- main.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 000000000..1678d1845 --- /dev/null +++ b/main.py @@ -0,0 +1,51 @@ +from cmp.grammar import G +from cmp.lexer import tokenize_text, pprint_tokens +from cmp.tools import LR1Parser +from cmp.evaluation import evaluate_reverse_parse +from cmp.formatter import FormatVisitor + + +def run_pipeline(G, text): + print('=================== TEXT ======================') + print(text) + print('================== TOKENS =====================') + tokens = list(tokenize_text(text)) + pprint_tokens(tokens) + print('=================== PARSE =====================') + parser = LR1Parser(G) + parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) + print('\n'.join(repr(x) for x in parse)) + print('==================== AST ======================') + ast = evaluate_reverse_parse(parse, operations, tokens) + formatter = FormatVisitor() + tree = formatter.visit(ast) + print(tree) + return ast + + +text = ''' + class A { + a : Z ; + suma ( a : int , b : B ) : int { + a + b + } ; + b : int <- 9 ; + c : C ; + } ; + + class B inherits A { + c : A ; + f ( d : int , a : A ) : void { + { + let f : int <- 8 in f + 3 * d ; + c <- suma ( 5 , f ) ; + } + } ; + z : int ; + } ; + + class C inherits Z { + } ; +''' + +if __name__ == '__main__': ast = run_pipeline(G, text) \ No newline at end of file From f7cc5f65b2a30cf1acd36f7164b8beef664b1ea6 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 7 Sep 2020 14:34:08 -0400 Subject: [PATCH 011/138] Adding files to ignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index b6e47617d..75c6a61ea 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,7 @@ dmypy.json # Pyre type checker .pyre/ + +result.txt + +test_cases/ From 1c32fb33ba1194a3d1b72949c60209918f9d5e16 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Tue, 8 Sep 2020 23:15:36 -0400 Subject: [PATCH 012/138] refs #2 - Semantic file added This file contains the classes to manage the types, attributes and methods declared in the code. It also contains the class Context which handles all the mentioned classes. In addition, ObjectType, IOType, IntType, StringType and BoolType were created to represent the built-in types of COOL --- cmp/semantic.py | 248 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 cmp/semantic.py diff --git a/cmp/semantic.py b/cmp/semantic.py new file mode 100644 index 000000000..c15a7037b --- /dev/null +++ b/cmp/semantic.py @@ -0,0 +1,248 @@ +import itertools as itt +from collections import OrderedDict + + +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): + 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 next(method for method in self.methods if method.name == name) + except StopIteration: + 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 (method.name for method in self.methods): + raise SemanticError(f'Method "{name}" already defined in {self.name}') + + method = Method(name, param_names, param_types, return_type) + self.methods.append(method) + 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) + + def bypass(self): + return False + + def can_be_inherited(self): + return True + + 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) + 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 ObjectType(Type): + def __init__(self): + Type.__init__(self, 'object') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, ObjectType) + +class IOType(Type): + def __init__(self): + Type.__init__(self, 'io') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, IOType) + +class StringType(Type): + def __init__(self): + Type.__init__(self, 'string') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, StringType) + + def can_be_inherited(self): + return False + +class BoolType(Type): + def __init__(self): + Type.__init__(self, 'bool') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, BoolType) + + def can_be_inherited(self): + return False + +class IntType(Type): + def __init__(self): + Type.__init__(self, 'int') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, IntType) + + def can_be_inherited(self): + return False + +class SelfType(Type): + def __init__(self): + Type.__init__(self, 'SELF_TYPE') + + + +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 is None 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) From a2c36f6bd0fbe9bd4f163c3387cacfbc5821eeb3 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Tue, 8 Sep 2020 23:26:00 -0400 Subject: [PATCH 013/138] refs #2 - Type Collector Visitor -This visitor runs through the AST collecting the types defined by the programmer. - After collecting all the types defined and adding to the context the built-in types of COOL, the method check_parents() is in charge of setting each type parent: if the parent is None, it sets Object as the parent of this type; if the parent declared is not defined then it saves the error and sets Object as the parent of this type. - Lastly check_cyclic_inheritance looks for cycles in the inheritance hierarchy; in case of finding any, it saves the error and sets object as the parent of the type who closes the cycle. The result of this method depends on the way the types were defined in the code --- cmp/type_collector.py | 125 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 cmp/type_collector.py diff --git a/cmp/type_collector.py b/cmp/type_collector.py new file mode 100644 index 000000000..c7a3a4233 --- /dev/null +++ b/cmp/type_collector.py @@ -0,0 +1,125 @@ +import cmp.visitor as visitor +from cmp.semantic import SemanticError, Type, Context, ObjectType, IOType, StringType, IntType, BoolType, SelfType +from cmp.ast import ProgramNode, ClassDeclarationNode + +built_in_types = [] + + +class TypeCollector(object): + def __init__(self, errors=[]): + self.context = None + self.errors = errors + self.parent = {} + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + self.context = Context() + self.define_built_in_types() + + for declaration in node.declarations: + self.visit(declaration) + + # Adding built-in types to context + for typex in built_in_types: + self.context.types[typex.name] = typex + + self.check_parents() + self.check_cyclic_inheritance() + + + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + if node.id in [typex.name for typex in built_in_types]: + self.errors.append('Invalid class name') + else: + try: + self.context.create_type(node.id) + self.parent[node.id] = node.parent + except SemanticError as ex: + self.errors.append(ex.text) + + + def define_built_in_types(self): + objectx = ObjectType() + iox = IOType() + intx = IntType() + stringx = StringType() + boolx = BoolType() + self_type = SelfType() + + # Object Methods + objectx.define_method('abort', [], [], objectx) + objectx.define_method('type_name', [], [], stringx) + objectx.define_method('copy', [], [], self_type) + + # IO Methods + iox.define_method('out_string', ['x'], [stringx], self_type) + iox.define_method('out_int', ['x'], [intx], self_type) + iox.define_method('in_string', [], [], stringx) + iox.define_method('in_int', [], [], intx) + + # String Methods + stringx.define_method('length', [], [], intx) + stringx.define_method('concat', ['s'], [stringx], stringx) + stringx.define_method('substr', ['i', 'l'], [intx, intx], stringx) + + # Setting Object as parent + iox.set_parent(objectx) + stringx.set_parent(objectx) + intx.set_parent(objectx) + boolx.set_parent(objectx) + + built_in_types.extend([objectx, iox, stringx, intx, boolx]) + + def check_parents(self): + for item in self.parent.keys(): + item_type = self.context.get_type(item) + if self.parent[item] is None: + item_type.set_parent(built_in_types[0]) + else: + try: + typex = self.context.get_type(self.parent[item]) + if not typex.can_be_inherited(): + self.errors.append(f'Class {item.name} can not inherit from {typex.name}') + typex = built_in_types[0] + item_type.set_parent(typex) + except SemanticError as ex: + self.errors.append(ex.text) + item_type.set_parent(built_in_types[0]) + + + def check_cyclic_inheritance(self): + flag = [] + + def find(item): + for i, typex in enumerate(flag): + if typex.name == item.name: + return i + return len(flag) + + def check_path(idx, item): + while True: + flag.append(item) + parent = item.parent + if parent is None: + break + pos = find(parent) + if pos < len(flag): + if pos >= idx: + self.errors.append(f'Class {item.name} can not inherit from {parent.name}') + item.parent = built_in_types[0] + break + item = parent + + for item in self.context.types.values(): + idx = find(item) + if idx == len(flag): + check_path(idx, item) + + + From fa9c989460d12b7640ce3253d10882255418b661 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Tue, 8 Sep 2020 23:28:51 -0400 Subject: [PATCH 014/138] refs #2 - Extending the pipeline Now that the type collector was added, the pipeline was modified so that after printing the ast it shows the results of running the type collector visitor --- main.py | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/main.py b/main.py index 1678d1845..6c5164ef6 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ from cmp.tools import LR1Parser from cmp.evaluation import evaluate_reverse_parse from cmp.formatter import FormatVisitor +from cmp.type_collector import TypeCollector def run_pipeline(G, text): @@ -20,32 +21,26 @@ def run_pipeline(G, text): formatter = FormatVisitor() tree = formatter.visit(ast) print(tree) + print('============== COLLECTING TYPES ===============') + errors = [] + collector = TypeCollector(errors) + collector.visit(ast) + context = collector.context + print('Errors:', errors) + print('Context:') + print(context) return ast text = ''' - class A { - a : Z ; - suma ( a : int , b : B ) : int { - a + b - } ; - b : int <- 9 ; - c : C ; - } ; - - class B inherits A { - c : A ; - f ( d : int , a : A ) : void { - { - let f : int <- 8 in f + 3 * d ; - c <- suma ( 5 , f ) ; - } - } ; - z : int ; - } ; - - class C inherits Z { - } ; +class C inherits B { } ; +class A inherits B { } ; +class B inherits A { } ; +class C { } ; +class D inherits E { } ; +class E inherits F { } ; +class F inherits D { } ; +class G inherits F { } ; ''' if __name__ == '__main__': ast = run_pipeline(G, text) \ No newline at end of file From e3348a15f8cfdc99c525e1282e7b24eee5bb8e50 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Tue, 8 Sep 2020 23:30:23 -0400 Subject: [PATCH 015/138] refs #2 - Test Cases added This file was added in order to change the text of the main file easily, and to have cases to test each stage of the code --- .gitignore | 2 -- test_cases/cyclic_inheritance.txt | 11 +++++++++++ test_cases/test1.txt | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 test_cases/cyclic_inheritance.txt create mode 100644 test_cases/test1.txt diff --git a/.gitignore b/.gitignore index 75c6a61ea..2d5ac8865 100644 --- a/.gitignore +++ b/.gitignore @@ -129,5 +129,3 @@ dmypy.json .pyre/ result.txt - -test_cases/ diff --git a/test_cases/cyclic_inheritance.txt b/test_cases/cyclic_inheritance.txt new file mode 100644 index 000000000..b7c354b3a --- /dev/null +++ b/test_cases/cyclic_inheritance.txt @@ -0,0 +1,11 @@ + +text = ''' +class C inherits B { } ; +class A inherits B { } ; +class B inherits A { } ; +class C { } ; +class D inherits E { } ; +class E inherits F { } ; +class F inherits D { } ; +class G inherits F { } ; +''' \ No newline at end of file diff --git a/test_cases/test1.txt b/test_cases/test1.txt new file mode 100644 index 000000000..886efaccd --- /dev/null +++ b/test_cases/test1.txt @@ -0,0 +1,24 @@ +text = ''' + class A { + a : Z ; + suma ( a : int , b : B ) : int { + a + b + } ; + b : int <- 9 ; + c : C ; + } ; + + class B inherits A { + c : A ; + f ( d : int , a : A ) : void { + { + let f : int <- 8 in f + 3 * d ; + c <- suma ( 5 , f ) ; + } + } ; + z : int ; + } ; + + class C inherits Z { + } ; +''' \ No newline at end of file From 362d419bd4001a9c72b82245100be475ac7671f0 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 9 Sep 2020 23:19:57 -0400 Subject: [PATCH 016/138] refs #3 - Adding self_type to context If self_type is not added to context, it is considered as an error when the returned type of a method is SELF_TYPE, since this type is not defined in the context. If there is a better way to handle self_type it will be fixed later. --- cmp/type_collector.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmp/type_collector.py b/cmp/type_collector.py index c7a3a4233..2d361f750 100644 --- a/cmp/type_collector.py +++ b/cmp/type_collector.py @@ -28,8 +28,7 @@ def visit(self, node): self.context.types[typex.name] = typex self.check_parents() - self.check_cyclic_inheritance() - + self.check_cyclic_inheritance() @visitor.when(ClassDeclarationNode) @@ -74,7 +73,7 @@ def define_built_in_types(self): intx.set_parent(objectx) boolx.set_parent(objectx) - built_in_types.extend([objectx, iox, stringx, intx, boolx]) + built_in_types.extend([objectx, iox, stringx, intx, boolx, self_type]) def check_parents(self): for item in self.parent.keys(): From 4cc115697b960fc4f9d50cc5676c12e68b36c38f Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 9 Sep 2020 23:21:58 -0400 Subject: [PATCH 017/138] refs #3 - Adding Type Builder class The type builder runs through the ast an sets for every type that was defined its attributes and methods. When this process is finished it also checks that the program contains a Main class, and that this class contains a method main --- cmp/type_builder.py | 88 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 cmp/type_builder.py diff --git a/cmp/type_builder.py b/cmp/type_builder.py new file mode 100644 index 000000000..560d24f8c --- /dev/null +++ b/cmp/type_builder.py @@ -0,0 +1,88 @@ +import cmp.visitor as visitor +from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from cmp.semantic import SemanticError, ErrorType + + +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 dec in node.declarations: + self.visit(dec) + + self.check_main_class() + + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + try: + self.current_type = self.context.get_type(node.id) + for feat in node.features: + self.visit(feat) + except SemanticError: + pass + + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + + ## Building param-names and param-types of the method + param_names = [] + param_types = [] + for param in node.params: + n, t = param + param_names.append(n) + try: + t = self.context.get_type(t) + except SemanticError as ex: + self.errors.append(ex.text) + t = ErrorType() + param_types.append(t) + + # Checking return type + try: + rtype = self.context.get_type(node.type) + except SemanticError as ex: + self.errors.append(ex.text) + rtype = ErrorType() + + # Defining the method in the current type. There can not be another method with the same name. + try: + self.current_type.define_method(node.id, param_names, param_types, rtype) + except SemanticError as ex: + self.errors.append(ex.text) + + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + # Checking attribute type + try: + attr_type = self.context.get_type(node.type) + except SemanticError as ex: + self.errors.append(ex.text) + attr_type = ErrorType() + + # Checking attribute name. No other attribute can have the same name + try: + self.current_type.define_attribute(node.id, attr_type) + except SemanticError as ex: + self.errors.append(ex.text) + + + + def check_main_class(self): + try: + typex = self.context.get_type('Main') + if not any(method.name == 'main' for method in typex.methods): + self.errors.append('Class Main must contain a method main') + except SemanticError: + self.errors.append('Program must contain a class Main') \ No newline at end of file From 6ede708248a44aae525975ad7cb75c4cdf2edb5b Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 9 Sep 2020 23:24:48 -0400 Subject: [PATCH 018/138] refs #3 - Type builder visitor added to the pipeline After collecting all the types, run_pipeline method calls type builder visitor and prints the resulting stage of the context and errors --- main.py | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index 6c5164ef6..68b575dfd 100644 --- a/main.py +++ b/main.py @@ -4,6 +4,7 @@ from cmp.evaluation import evaluate_reverse_parse from cmp.formatter import FormatVisitor from cmp.type_collector import TypeCollector +from cmp.type_builder import TypeBuilder def run_pipeline(G, text): @@ -29,18 +30,46 @@ def run_pipeline(G, text): print('Errors:', errors) print('Context:') print(context) + print('=============== BUILDING TYPES ================') + builder = TypeBuilder(context, errors) + builder.visit(ast) + print('Errors: [') + for error in errors: + print('\t', error) + print(']') + print('Context:') + print(context) return ast text = ''' -class C inherits B { } ; -class A inherits B { } ; -class B inherits A { } ; -class C { } ; -class D inherits E { } ; -class E inherits F { } ; -class F inherits D { } ; -class G inherits F { } ; + class A { + a : C ; + suma ( a : int , b : B ) : int { + a + b + } ; + b : int <- 9 ; + c : C ; + } ; + + class B inherits A { + f ( d : int , a : A ) : B { + { + let f : int <- 8 in f + 3 * d ; + c <- suma ( 5 , f ) ; + } + } ; + z : int ; + } ; + + class C inherits A { + } ; + + class Main inherits A { + main ( ) : SELF_TYPE { + a . copy ( ) + } ; + } ; ''' if __name__ == '__main__': ast = run_pipeline(G, text) \ No newline at end of file From 580754ceba320082daf2033117ee49776d1fb1be Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 9 Sep 2020 23:25:30 -0400 Subject: [PATCH 019/138] refs #3 - Test cases added More test cases were added to check the new features --- test_cases/cyclic_inheritance.txt | 5 +++ test_cases/test1.txt | 67 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/test_cases/cyclic_inheritance.txt b/test_cases/cyclic_inheritance.txt index b7c354b3a..c7b56a726 100644 --- a/test_cases/cyclic_inheritance.txt +++ b/test_cases/cyclic_inheritance.txt @@ -8,4 +8,9 @@ class D inherits E { } ; class E inherits F { } ; class F inherits D { } ; class G inherits F { } ; +''' + +text = ''' +class B inherits B { } ; +class A inherits B { } ; ''' \ No newline at end of file diff --git a/test_cases/test1.txt b/test_cases/test1.txt index 886efaccd..0d4d983e1 100644 --- a/test_cases/test1.txt +++ b/test_cases/test1.txt @@ -1,3 +1,5 @@ + +//Expected errors text = ''' class A { a : Z ; @@ -21,4 +23,69 @@ text = ''' class C inherits Z { } ; +''' + + + +//No method main in Main class, even though Main class inherits from A, which does contain a method main +text = ''' + class A { + a : C ; + suma ( a : int , b : B ) : int { + a + b + } ; + b : int <- 9 ; + c : C ; + main ( ) : SELF_TYPE { + a . copy ( ) + } ; + } ; + + class B inherits A { + f ( d : int , a : A ) : B { + { + let f : int <- 8 in f + 3 * d ; + c <- suma ( 5 , f ) ; + } + } ; + z : int ; + } ; + + class C inherits A { + } ; + + class Main inherits A { } ; +''' + + + +//No errors +text = ''' + class A { + a : C ; + suma ( a : int , b : B ) : int { + a + b + } ; + b : int <- 9 ; + c : C ; + } ; + + class B inherits A { + f ( d : int , a : A ) : B { + { + let f : int <- 8 in f + 3 * d ; + c <- suma ( 5 , f ) ; + } + } ; + z : int ; + } ; + + class C inherits A { + } ; + + class Main inherits A { + main ( ) : SELF_TYPE { + a . copy ( ) + } ; + } ; ''' \ No newline at end of file From 12d4eace49f868b488524f5e28db35e6ff5d3537 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 23 Nov 2020 12:41:30 -0500 Subject: [PATCH 020/138] refs #6 - Adding Arithmetic and Comparison nodes to AST Both classes inherit from Binary Node - Arithmetic Node refers to the operations: plus, minus, star and div. These are supposed to receive two integers and they return an integer as well. - Comparison Node refers to LeqNode and LessNode. They both receive two integers and return a bool value. - The behavior of Equal Node can variate depending on the type of the elements it receives, so it inherits from Binary Node instead. --- cmp/ast.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/cmp/ast.py b/cmp/ast.py index 373ef2e94..86725ee9a 100644 --- a/cmp/ast.py +++ b/cmp/ast.py @@ -44,6 +44,7 @@ def __init__(self, obj, idx, args, cast_type=None): class CaseNode(ExpressionNode): def __init__(self, expr, branch_list): self.expr = expr + #[(id, type, expr), ...] self.branch_list = branch_list class BlockNode(ExpressionNode): @@ -63,6 +64,7 @@ def __init__(self, cond, then_body, else_body): class LetNode(ExpressionNode): def __init__(self, id_list, body): + #[(id, type, expr), ...] self.id_list = id_list self.body = body @@ -70,14 +72,20 @@ class AtomicNode(ExpressionNode): def __init__(self, lex): self.lex = lex +class UnaryNode(ExpressionNode): + def __init__(self, expr): + self.expr = expr + class BinaryNode(ExpressionNode): def __init__(self, left, right): self.left = left self.right = right -class UnaryNode(ExpressionNode): - def __init__(self, expr): - self.expr = expr +class ArithmeticNode(BinaryNode): + pass +class ComparisonNode(BinaryNode): + pass + class ConstantNumNode(AtomicNode): pass @@ -94,22 +102,22 @@ class VariableNode(AtomicNode): class InstantiateNode(AtomicNode): pass -class PlusNode(BinaryNode): +class PlusNode(ArithmeticNode): pass -class MinusNode(BinaryNode): +class MinusNode(ArithmeticNode): pass -class StarNode(BinaryNode): +class StarNode(ArithmeticNode): pass -class DivNode(BinaryNode): +class DivNode(ArithmeticNode): pass -class LeqNode(BinaryNode): +class LeqNode(ComparisonNode): pass -class LessNode(BinaryNode): +class LessNode(ComparisonNode): pass class EqualNode(BinaryNode): From e3d87afd2cb73ffb70190b4319665b4c9a11c475 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 23 Nov 2020 13:06:59 -0500 Subject: [PATCH 021/138] refs #6 - Modifying Type Collector - When checking a Class Declaration Node, if it doesn't have a valid id, its id will be modified until the type of the class can be added to the context, so the information of that class doesn't get lost - Order Types method added: After setting parents of every class the graph that represents the hierarchy of types is a tree; hence this method will change the order of the list of class declarations in the program node according to their depth in the mentioned tree --- cmp/type_collector.py | 56 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/cmp/type_collector.py b/cmp/type_collector.py index 2d361f750..54fb8da64 100644 --- a/cmp/type_collector.py +++ b/cmp/type_collector.py @@ -20,28 +20,40 @@ def visit(self, node): self.context = Context() self.define_built_in_types() - for declaration in node.declarations: - self.visit(declaration) - # Adding built-in types to context for typex in built_in_types: self.context.types[typex.name] = typex + for declaration in node.declarations: + self.visit(declaration) + self.check_parents() - self.check_cyclic_inheritance() - + self.check_cyclic_inheritance() + + # Order class declarations according to their depth in the inheritance tree + node.declarations = self.order_types(node) + @visitor.when(ClassDeclarationNode) def visit(self, node): - if node.id in [typex.name for typex in built_in_types]: - self.errors.append('Invalid class name') - else: + # flag will be True if the class is succesfully added to the context + flag = False + try: + self.context.create_type(node.id) + flag = True + self.parent[node.id] = node.parent + except SemanticError as ex: + self.errors.append(ex.text) + + # changing class id so it can be added to context + while not flag: + node.id = f'1{node.id}' try: self.context.create_type(node.id) + flag = True self.parent[node.id] = node.parent - except SemanticError as ex: - self.errors.append(ex.text) - + except SemanticError: + pass def define_built_in_types(self): objectx = ObjectType() @@ -79,6 +91,7 @@ def check_parents(self): for item in self.parent.keys(): item_type = self.context.get_type(item) if self.parent[item] is None: + # setting Object as parent item_type.set_parent(built_in_types[0]) else: try: @@ -122,3 +135,24 @@ def check_path(idx, item): + def order_types(self, node): + sorted_declarations = [] + flag = [False] * len(node.declarations) + obj_name = built_in_types[0].name + + change = True + while change: + change = False + + current = [] + for i, dec in enumerate(node.declarations): + if not flag[i]: + typex = self.context.get_type(dec.id) + if typex.parent.name in [item.id for item in sorted_declarations] or typex.parent.name == obj_name: + current.append(dec) + flag[i] = True + change = True + + sorted_declarations.extend(current) + + return sorted_declarations \ No newline at end of file From 139cdf7a3b788e219d4a8ca2b3eb3976beae46c8 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 23 Nov 2020 13:11:47 -0500 Subject: [PATCH 022/138] refs #6 - Modifyng Type Builder - Code added in the method of Attribute Declaration Node, so if the id of the attribute wasn't valid for some reason, it will change the id until it can be defined in the current type --- cmp/type_builder.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cmp/type_builder.py b/cmp/type_builder.py index 560d24f8c..341f127ba 100644 --- a/cmp/type_builder.py +++ b/cmp/type_builder.py @@ -24,12 +24,9 @@ def visit(self, node): @visitor.when(ClassDeclarationNode) def visit(self, node): - try: - self.current_type = self.context.get_type(node.id) - for feat in node.features: - self.visit(feat) - except SemanticError: - pass + self.current_type = self.context.get_type(node.id) + for feat in node.features: + self.visit(feat) @visitor.when(FuncDeclarationNode) @@ -72,11 +69,20 @@ def visit(self, node): attr_type = ErrorType() # Checking attribute name. No other attribute can have the same name + flag = False try: self.current_type.define_attribute(node.id, attr_type) + flag = True except SemanticError as ex: self.errors.append(ex.text) + while not flag: + node.id = f'1{node.id}' + try: + self.current_type.define_attribute(node.id, attr_type) + flag = True + except SemanticError: + pass def check_main_class(self): From 108a01fdcb2fd2f4c1bd5654c1539be8ec834270 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 23 Nov 2020 13:13:38 -0500 Subject: [PATCH 023/138] refs #6 - Adding Type Checker class It hasn't been tested yet --- cmp/type_checker.py | 380 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 cmp/type_checker.py diff --git a/cmp/type_checker.py b/cmp/type_checker.py new file mode 100644 index 000000000..fc97654c6 --- /dev/null +++ b/cmp/type_checker.py @@ -0,0 +1,380 @@ +import cmp.visitor as visitor +from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType +from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from cmp.ast import ArithmeticNode, ComparisonNode, EqualNode +from cmp.ast import VoidNode, NotNode, NegNode +from cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode + + +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".' +INVALID_TYPE = 'SELF_TYPE is not valid' + +class TypeChecker: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.current_method = None + self.errors = errors + + #built-in types + self.obj_type = self.context.get_type('object') + self.int_type = self.context.get_type('int') + self.bool_type = self.context.get_type('bool') + self.string_type = self.context.get_type('string') + + @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) + attributes = self.current_type.all_attributes() + for values in attributes: + attr, _ = values + 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): + attr_type = self.context.get_type(node.type) + + if node.expr is not None: + computed_type = self.visit(node.expr, scope) + if not computed_type.conforms_to(attr_type) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(attr_type)): + self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, attr_type.name)) + + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + self.current_method = self.current_type.get_method(node.id) + + #checking overwriting + try: + method = self.current_type.parent.get_method(node.id) + if not len(self.current_method.param_types) == len(method.param_types): + self.errors.append(WRONG_SIGNATURE %(node.id, self.current_type.name)) + else: + for i, t in enumerate(self.current_method.param_types): + if not method.param_types[i] == t: + self.errors.append(WRONG_SIGNATURE %(node.id, self.current_type.name)) + break + else: + if not self.current_method.return_type == method.return_type: + self.errors.append(WRONG_SIGNATURE %(node.id, self.current_type.name)) + except SemanticError: + pass + + #defining variables in new scope + for i, var in enumerate(self.current_method.param_names): + if scope.is_local(var): + self.errors.append(LOCAL_ALREADY_DEFINED %(var, self.current_method.name)) + else: + scope.define_variable(var, self.current_method.param_types[i]) + + computed_type = self.visit(node.body, scope) + + #checking return type + rtype = self.current_method.return_type + if not computed_type.conforms_to(rtype) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(rtype)): + self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, self.current_method.return_type.name)) + + + @visitor.when(AssignNode) + def visit(self, node, scope): + if node.id == "self": + self.errors.append(SELF_IS_READONLY) + + #checking variable is defined + var = scope.find_variable(node.id) + if var is None: + self.errors.append(VARIABLE_NOT_DEFINED %(node.id, self.current_type.name)) + var = scope.define_variable(node.id, ErrorType()) + + computed_type = self.visit(node.expr, scope.create_child()) + + if not computed_type.conforms_to(var.type) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(var.type)): + self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, var.type.name)) + + return computed_type + + + @visitor.when(CallNode) + def visit(self, node, scope): + # Evaluate object + obj_type = self.visit(node.obj, scope) + + # Check object type conforms to cast type + cast_type = obj_type + if node.type is not None: + try: + cast_type = self.context.get_type(node.type) + except SemanticError as ex: + cast_type = ErrorType() + self.errors.append(ex.text) + + if not obj_type.conforms_to(cast_type) and (isinstance(obj_type, SelfType) and not self.current_type.conforms_to(cast_type)): + self.errors.append(INCOMPATIBLE_TYPES %(obj_type.name, cast_type.name)) + + # Check this function is defined for cast_type + try: + method = cast_type.get_method(node.id) + if not len(node.args) == len(method.param_types): + self.errors.append(INVALID_OPERATION %(method.name, cast_type.name)) + return ErrorType() + for i, arg in enumerate(node.args): + computed_type = self.visit(arg, scope) + if not computed_type.conforms_to(method.param_types[i]) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(method.param_types[i])): + self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, method.param_types[i].name)) + + #check self_type + rtype = method.return_type + if isinstance(rtype, SelfType): + rtype = obj_type + + return rtype + + except SemanticError as ex: + self.errors.append(ex.text) + return ErrorType() + + + @visitor.when(CaseNode) + def visit(self, node, scope): + #check expression + self.visit(node.expr, scope) + + nscope = scope.create_child() + + #check branches + types = [] + for branch in node.branch_list: + idx, typex, expr = branch + + #check idx is not self + if idx == 'self': + self.errors.append(SELF_IS_READONLY) + + try: + var_type = self.context.get_type(typex) + except SemanticError as ex: + self.errors.append(ex.text) + var_type = ErrorType() + + new_scope = nscope.create_child() + new_scope.define_variable(idx, var_type) + + computed_type = self.visit(expr, new_scope) + types.append(computed_type) + + return self.LCA(types) + + + @visitor.when(BlockNode) + def visit(self, node, scope): + nscope = scope.create_child() + + #Check expressions + computed_type = None + for expr in node.expr_list: + computed_type = self.visit(expr, nscope) + + #return the type of the last expression of the list + return computed_type + + + @visitor.when(LoopNode) + def visit(self, node, scope): + nscope = scope.create_child() + + #checking condition: it must conform to bool + cond_type = self.visit(node.condition, nscope) + if not cond_type.conforms_to(self.bool_type): + self.errors.append(INCOMPATIBLE_TYPES %(cond_type.name, self.bool_type.name)) + + #checking body + self.visit(node.body, nscope) + + return self.obj_type + + + @visitor.when(ConditionalNode) + def visit(self, node, scope): + nscope = scope.create_child() + + #check condition conforms to bool + cond_type = self.visit(node.condition, nscope) + if not cond_type.conforms_to(self.bool_type): + self.errors.append(INCOMPATIBLE_TYPES %(cond_type.name, self.bool_type.name)) + + then_type = self.visit(node.then_body, nscope) + else_type = self.visit(node.else_body, nscope) + + return self.LCA([then_type, else_type]) + + + @visitor.when(LetNode) + def visit(self, node, scope): + nscope = scope.create_child() + + for item in node.id_list: + idx, typex, expr = item + + if idx == 'self': + self.errors.append(SELF_IS_READONLY) + + try: + typex = self.context.get_type(typex) + except SemanticError as ex: + self.errors.append(ex.text) + typex = ErrorType() + + if expr is not None: + expr_type = self.visit(expr, nscope) + if not expr_type.conforms_to(typex) and (isinstance(expr_type, SelfType) and not self.current_type.conforms_to(typex)): + self.errors.append(INCOMPATIBLE_TYPES %(expr_type.name, typex.name)) + + nscope.define_variable(idx, typex) + + return self.visit(node.body, nscope) + + + @visitor.when(ArithmeticNode) + def visit(self, node, scope): + self.check_expr(node, scope) + return self.int_type + + @visitor.when(ComparisonNode) + def visit(self, node, scope): + self.check_expr(node, scope) + return self.bool_type + + @visitor.when(EqualNode) + def visit(self, node, scope): + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + + types = [self.int_type, self.bool_type, self.string_type] + def check_equal(typex, other): + for t in types: + if typex.conforms_to(t): + if not other.conforms_to(t): + self.errors.append(INCOMPATIBLE_TYPES %(other.name, t.name)) + return True + return False + + ok = check_equal(left, right) + if not ok: + check_equal(right, left) + + return self.bool_type + + + @visitor.when(VoidNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + + return self.bool_type + + @visitor.when(NotNode) + def visit(self, node, scope): + typex = self.visit(node.expr, scope) + if not typex.conforms_to(self.bool_type): + self.errors.append(INCOMPATIBLE_TYPES %(typex.name, self.bool_type.name)) + + return self.bool_type + + @visitor.when(NegNode) + def visit(self, node, scope): + typex = self.visit(node.expr, scope) + if not typex.conforms_to(self.int_type): + self.errors.append(INCOMPATIBLE_TYPES %(typex.name, self.int_type.name)) + + return self.int_type + + + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + return self.int_type + + @visitor.when(ConstantBoolNode) + def visit(self, node, scope): + return self.bool_type + + @visitor.when(ConstantStringNode) + def visit(self, node, scope): + return self.string_type + + @visitor.when(VariableNode) + def visit(self, node, scope): + var = scope.find_variable(node.lex) + if var is None: + self.errors.append(VARIABLE_NOT_DEFINED %(node.lex, self.current_type)) + var = scope.define_variable(node.lex, ErrorType()) + + return var.type + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + try: + typex = self.context.get_type(node.lex) + except SemanticError as ex: + self.errors.append(ex.text) + typex = ErrorType() + + return typex + + + def LCA(self, types): + #check ErrorType: + if any(isinstance(item, ErrorType) for item in types): + return ErrorType() + + #check SELF_TYPE: + if all(isinstance(item, SelfType) for item in types): + return types[0] + + for i, item in enumerate(types): + if isinstance(item, SelfType): + types[i] = self.current_type + + current = types[0] + while current: + for item in types: + if not item.conforms_to(current): + break + else: + return current + current = current.parent + + #This part of the code is suposed to be unreachable + return None + + + def check_expr(self, node, scope): + #checking left expr + left = self.visit(node.left, scope) + if not left.conforms_to(self.int_type): + self.errors.append(INCOMPATIBLE_TYPES %(left.name, self.int_type.name)) + + #checking right expr + right = self.visit(node.right, scope) + if not right.conforms_to(self.int_type): + self.errors.append(INCOMPATIBLE_TYPES %(right.name, self.int_type.name)) From f5cf8c76d374f5ee5b562f4d9fcbec50a34e7607 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 23 Nov 2020 13:15:02 -0500 Subject: [PATCH 024/138] refs #6 - Change text in main to test inheritance and order --- main.py | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/main.py b/main.py index 68b575dfd..62fde18d1 100644 --- a/main.py +++ b/main.py @@ -43,33 +43,10 @@ def run_pipeline(G, text): text = ''' - class A { - a : C ; - suma ( a : int , b : B ) : int { - a + b - } ; - b : int <- 9 ; - c : C ; - } ; - - class B inherits A { - f ( d : int , a : A ) : B { - { - let f : int <- 8 in f + 3 * d ; - c <- suma ( 5 , f ) ; - } - } ; - z : int ; - } ; - - class C inherits A { - } ; - - class Main inherits A { - main ( ) : SELF_TYPE { - a . copy ( ) - } ; - } ; + class A inherits C { } ; + class C inherits B { } ; + class B { } ; + class B inherits A { } ; ''' if __name__ == '__main__': ast = run_pipeline(G, text) \ No newline at end of file From 75c2083d237f72f434b3b271ced36c10aedfd146 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 25 Nov 2020 16:11:59 -0500 Subject: [PATCH 025/138] refs #6 - Fix bug in type collector error message --- cmp/type_collector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmp/type_collector.py b/cmp/type_collector.py index 54fb8da64..35b352d42 100644 --- a/cmp/type_collector.py +++ b/cmp/type_collector.py @@ -97,7 +97,7 @@ def check_parents(self): try: typex = self.context.get_type(self.parent[item]) if not typex.can_be_inherited(): - self.errors.append(f'Class {item.name} can not inherit from {typex.name}') + self.errors.append(f'Class {item} can not inherit from {typex.name}') typex = built_in_types[0] item_type.set_parent(typex) except SemanticError as ex: From c20561f879493a919e7322938d7005202435149e Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 25 Nov 2020 16:28:51 -0500 Subject: [PATCH 026/138] refs #6 - Condition for checking conformance to a certain type --- cmp/type_checker.py | 58 +++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index fc97654c6..a6492aee0 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -22,7 +22,7 @@ def __init__(self, context, errors=[]): self.current_method = None self.errors = errors - #built-in types + # built-in types self.obj_type = self.context.get_type('object') self.int_type = self.context.get_type('int') self.bool_type = self.context.get_type('bool') @@ -58,7 +58,7 @@ def visit(self, node, scope): if node.expr is not None: computed_type = self.visit(node.expr, scope) - if not computed_type.conforms_to(attr_type) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(attr_type)): + if not self.check_conformance(computed_type, attr_type): self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, attr_type.name)) @@ -66,7 +66,7 @@ def visit(self, node, scope): def visit(self, node, scope): self.current_method = self.current_type.get_method(node.id) - #checking overwriting + # checking overwriting try: method = self.current_type.parent.get_method(node.id) if not len(self.current_method.param_types) == len(method.param_types): @@ -82,7 +82,7 @@ def visit(self, node, scope): except SemanticError: pass - #defining variables in new scope + # defining variables in new scope for i, var in enumerate(self.current_method.param_names): if scope.is_local(var): self.errors.append(LOCAL_ALREADY_DEFINED %(var, self.current_method.name)) @@ -91,9 +91,9 @@ def visit(self, node, scope): computed_type = self.visit(node.body, scope) - #checking return type + # checking return type rtype = self.current_method.return_type - if not computed_type.conforms_to(rtype) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(rtype)): + if not self.check_conformance(computed_type, rtype): self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, self.current_method.return_type.name)) @@ -102,7 +102,7 @@ def visit(self, node, scope): if node.id == "self": self.errors.append(SELF_IS_READONLY) - #checking variable is defined + # checking variable is defined var = scope.find_variable(node.id) if var is None: self.errors.append(VARIABLE_NOT_DEFINED %(node.id, self.current_type.name)) @@ -110,7 +110,7 @@ def visit(self, node, scope): computed_type = self.visit(node.expr, scope.create_child()) - if not computed_type.conforms_to(var.type) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(var.type)): + if not self.check_conformance(computed_type, var.type): self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, var.type.name)) return computed_type @@ -129,8 +129,7 @@ def visit(self, node, scope): except SemanticError as ex: cast_type = ErrorType() self.errors.append(ex.text) - - if not obj_type.conforms_to(cast_type) and (isinstance(obj_type, SelfType) and not self.current_type.conforms_to(cast_type)): + if not self.check_conformance(obj_type, cast_type): self.errors.append(INCOMPATIBLE_TYPES %(obj_type.name, cast_type.name)) # Check this function is defined for cast_type @@ -141,10 +140,10 @@ def visit(self, node, scope): return ErrorType() for i, arg in enumerate(node.args): computed_type = self.visit(arg, scope) - if not computed_type.conforms_to(method.param_types[i]) and (isinstance(computed_type, SelfType) and not self.current_type.conforms_to(method.param_types[i])): + if not self.check_conformance(computed_type, method.param_types[i]): self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, method.param_types[i].name)) - #check self_type + # check self_type rtype = method.return_type if isinstance(rtype, SelfType): rtype = obj_type @@ -158,17 +157,17 @@ def visit(self, node, scope): @visitor.when(CaseNode) def visit(self, node, scope): - #check expression + # check expression self.visit(node.expr, scope) nscope = scope.create_child() - #check branches + # check branches types = [] for branch in node.branch_list: idx, typex, expr = branch - #check idx is not self + # check idx is not self if idx == 'self': self.errors.append(SELF_IS_READONLY) @@ -191,12 +190,12 @@ def visit(self, node, scope): def visit(self, node, scope): nscope = scope.create_child() - #Check expressions + # Check expressions computed_type = None for expr in node.expr_list: computed_type = self.visit(expr, nscope) - #return the type of the last expression of the list + # return the type of the last expression of the list return computed_type @@ -204,12 +203,12 @@ def visit(self, node, scope): def visit(self, node, scope): nscope = scope.create_child() - #checking condition: it must conform to bool + # checking condition: it must conform to bool cond_type = self.visit(node.condition, nscope) if not cond_type.conforms_to(self.bool_type): self.errors.append(INCOMPATIBLE_TYPES %(cond_type.name, self.bool_type.name)) - #checking body + # checking body self.visit(node.body, nscope) return self.obj_type @@ -219,7 +218,7 @@ def visit(self, node, scope): def visit(self, node, scope): nscope = scope.create_child() - #check condition conforms to bool + # check condition conforms to bool cond_type = self.visit(node.condition, nscope) if not cond_type.conforms_to(self.bool_type): self.errors.append(INCOMPATIBLE_TYPES %(cond_type.name, self.bool_type.name)) @@ -248,7 +247,7 @@ def visit(self, node, scope): if expr is not None: expr_type = self.visit(expr, nscope) - if not expr_type.conforms_to(typex) and (isinstance(expr_type, SelfType) and not self.current_type.conforms_to(typex)): + if not self.check_conformance(expr_type, typex): self.errors.append(INCOMPATIBLE_TYPES %(expr_type.name, typex.name)) nscope.define_variable(idx, typex) @@ -343,11 +342,11 @@ def visit(self, node, scope): def LCA(self, types): - #check ErrorType: + # check ErrorType: if any(isinstance(item, ErrorType) for item in types): return ErrorType() - #check SELF_TYPE: + # check SELF_TYPE: if all(isinstance(item, SelfType) for item in types): return types[0] @@ -364,17 +363,24 @@ def LCA(self, types): return current current = current.parent - #This part of the code is suposed to be unreachable + # This part of the code is supposed to be unreachable return None def check_expr(self, node, scope): - #checking left expr + # checking left expr left = self.visit(node.left, scope) if not left.conforms_to(self.int_type): self.errors.append(INCOMPATIBLE_TYPES %(left.name, self.int_type.name)) - #checking right expr + # checking right expr right = self.visit(node.right, scope) if not right.conforms_to(self.int_type): self.errors.append(INCOMPATIBLE_TYPES %(right.name, self.int_type.name)) + + + def check_conformance(self, computed_type, attr_type): + return ( + computed_type.conforms_to(attr_type) + or (isinstance(computed_type, SelfType) and self.current_type.conforms_to(attr_type)) + ) \ No newline at end of file From c2b5e15d4ea95d88e83765545152f5cd61df77d7 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 25 Nov 2020 17:50:03 -0500 Subject: [PATCH 027/138] refs #6 - Adding new scopes for conditional branches When visiting then and else branches in a Conditional Node, a child scope should be created in both cases --- cmp/type_checker.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index a6492aee0..747155a7f 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -216,15 +216,14 @@ def visit(self, node, scope): @visitor.when(ConditionalNode) def visit(self, node, scope): - nscope = scope.create_child() # check condition conforms to bool - cond_type = self.visit(node.condition, nscope) + cond_type = self.visit(node.condition, scope) if not cond_type.conforms_to(self.bool_type): self.errors.append(INCOMPATIBLE_TYPES %(cond_type.name, self.bool_type.name)) - then_type = self.visit(node.then_body, nscope) - else_type = self.visit(node.else_body, nscope) + then_type = self.visit(node.then_body, scope.create_child()) + else_type = self.visit(node.else_body, scope.create_child()) return self.LCA([then_type, else_type]) From 4db34e8b38b3ba4f79b42b6c7b39c19f9e5a278a Mon Sep 17 00:00:00 2001 From: kikeXD Date: Thu, 26 Nov 2020 15:25:12 -0500 Subject: [PATCH 028/138] [Checking self] ress#8_Fixing "self" cases in type builder --- cmp/type_builder.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmp/type_builder.py b/cmp/type_builder.py index 341f127ba..396d6830b 100644 --- a/cmp/type_builder.py +++ b/cmp/type_builder.py @@ -3,6 +3,9 @@ from cmp.semantic import SemanticError, ErrorType +SELF_IS_READONLY = 'Variable "self" is read-only.' + + class TypeBuilder: def __init__(self, context, errors=[]): self.context = context @@ -37,6 +40,11 @@ def visit(self, node): param_types = [] for param in node.params: n, t = param + + # Checking param name can't be self + if n == "self": + self.errors.append(SELF_IS_READONLY) + param_names.append(n) try: t = self.context.get_type(t) @@ -67,6 +75,10 @@ def visit(self, node): except SemanticError as ex: self.errors.append(ex.text) attr_type = ErrorType() + + #Checking attribute can't be named self + if node.id == "self": + self.errors.append(SELF_IS_READONLY) # Checking attribute name. No other attribute can have the same name flag = False From 249d234d4935028fed5766f17793ff2089ac47ad Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Thu, 26 Nov 2020 18:25:03 -0500 Subject: [PATCH 029/138] refs #9 - Adding Auto Type class to built-in types - [semantic.py] Auto Types class inherits from Types - [Type Controller] Adding Auto Type in built-in types --- cmp/semantic.py | 10 ++++++++++ cmp/type_collector.py | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index c15a7037b..086a5d46d 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -184,6 +184,16 @@ def can_be_inherited(self): class SelfType(Type): def __init__(self): Type.__init__(self, 'SELF_TYPE') + + def can_be_inherited(self): + return False + +class AutoType(Type): + def __init__(self): + Type.__init__(self, 'AUTO_TYPE') + + def can_be_inherited(self): + return False diff --git a/cmp/type_collector.py b/cmp/type_collector.py index 35b352d42..9422785d9 100644 --- a/cmp/type_collector.py +++ b/cmp/type_collector.py @@ -1,5 +1,5 @@ import cmp.visitor as visitor -from cmp.semantic import SemanticError, Type, Context, ObjectType, IOType, StringType, IntType, BoolType, SelfType +from cmp.semantic import SemanticError, Type, Context, ObjectType, IOType, StringType, IntType, BoolType, SelfType, AutoType from cmp.ast import ProgramNode, ClassDeclarationNode built_in_types = [] @@ -62,6 +62,7 @@ def define_built_in_types(self): stringx = StringType() boolx = BoolType() self_type = SelfType() + autotype = AutoType() # Object Methods objectx.define_method('abort', [], [], objectx) @@ -85,7 +86,7 @@ def define_built_in_types(self): intx.set_parent(objectx) boolx.set_parent(objectx) - built_in_types.extend([objectx, iox, stringx, intx, boolx, self_type]) + built_in_types.extend([objectx, iox, stringx, intx, boolx, self_type, autotype]) def check_parents(self): for item in self.parent.keys(): From 7e66915ff05758bad99ad39ee81dffb08ffc58db Mon Sep 17 00:00:00 2001 From: kikeXD Date: Thu, 26 Nov 2020 19:10:49 -0500 Subject: [PATCH 030/138] refs #8 - changing "self" names in params in function declaration --- cmp/type_builder.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmp/type_builder.py b/cmp/type_builder.py index 396d6830b..403a741e3 100644 --- a/cmp/type_builder.py +++ b/cmp/type_builder.py @@ -45,7 +45,12 @@ def visit(self, node): if n == "self": self.errors.append(SELF_IS_READONLY) - param_names.append(n) + while True: + if n in param_names: + n = f'1{n}' + else: + param_names.append(n) + break try: t = self.context.get_type(t) except SemanticError as ex: From 1f1367ef42da50348b6901dc035a9abc03e79ef2 Mon Sep 17 00:00:00 2001 From: kikeXD Date: Thu, 26 Nov 2020 21:10:21 -0500 Subject: [PATCH 031/138] refs #11 - Implementing UI --- cmp/lexer.py | 14 +++++++++++--- main.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/cmp/lexer.py b/cmp/lexer.py index cbbfe7106..8d4da7167 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -12,16 +12,24 @@ def tokenize_text(token): except ValueError: return token.transform_to(idx) -def pprint_tokens(tokens): +def pprint_tokens(tokens, get=False): indent = 0 pending = [] + result = '' 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)) + if get: + result += ' '*indent + ' '.join(str(t.token_type) for t in pending) + '\n' + else: + 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])) + if get: + result += ' '.join([str(t.token_type) for t in pending]) + '\n' + return result + else: + print(' '.join([str(t.token_type) for t in pending])) diff --git a/main.py b/main.py index 62fde18d1..a7bd2b1cf 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import streamlit as st from cmp.grammar import G from cmp.lexer import tokenize_text, pprint_tokens from cmp.tools import LR1Parser @@ -45,8 +46,56 @@ def run_pipeline(G, text): text = ''' class A inherits C { } ; class C inherits B { } ; + class B inherits A { } ; class B { } ; class B inherits A { } ; ''' -if __name__ == '__main__': ast = run_pipeline(G, text) \ No newline at end of file +def main(G): + st.title('Type Inferer') + + st.sidebar.markdown('''Produced by: + Carmen Irene Cabrera Rodríguez + Enrique Martínez González''') + + text = st.text_area('Input your code here:') + + if text: + try: + st.title('Results:') + + st.subheader('Tokens') + tokens = list(tokenize_text(text)) + p_tokens = pprint_tokens(tokens, get=True) + st.text(p_tokens) + + st.subheader('Parse') + parser = LR1Parser(G) + parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) + p_parse = '\n'.join(repr(x) for x in parse) + st.text(p_parse) + + st.subheader('AST') + ast = evaluate_reverse_parse(parse, operations, tokens) + formatter = FormatVisitor() + tree = formatter.visit(ast) + st.text(tree) + + st.subheader('Collecting types') + errors = [] + collector = TypeCollector(errors) + collector.visit(ast) + context = collector.context + for e in errors: + st.error(e) + st.text('Context:') + st.text(context) + + + except Exception as e: + st.error(f'Unexpected error!!! You probably did something wrong :wink:') + + +if __name__ == '__main__': + main(G) + # ast = run_pipeline(G, text) \ No newline at end of file From e0bdd28edef76c5b66c20b3fb3c77e3a519fa2c8 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Fri, 27 Nov 2020 18:06:20 -0500 Subject: [PATCH 032/138] Adding a comment in ast file --- cmp/ast.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cmp/ast.py b/cmp/ast.py index 86725ee9a..c0e950a91 100644 --- a/cmp/ast.py +++ b/cmp/ast.py @@ -19,6 +19,7 @@ def __init__(self, idx, features, parent=None): class FuncDeclarationNode(DeclarationNode): def __init__(self, idx, params, return_type, body): self.id = idx + # param = (name, type) self.params = params self.type = return_type self.body = body From da36b4249f496b074d18b728fa3eed261bab871d Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Fri, 27 Nov 2020 18:11:35 -0500 Subject: [PATCH 033/138] refs #9 - Adding Inferencer Manager - Inferencer Manager class is the one that controls the information of variables and methods which are auto_type - A property idx is added to Variable Info, Attribute and Method, since this will be the identifier of this item in the manager, in case they are auto_type. In case, they are not this idx = None - The definition of equal was changed in Type class. - The Auto_Type class must overwrite bypass and conforms_to method in order to have the same behavior of Error Type: it conforms to every type and every type conforms to it --- cmp/semantic.py | 74 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index 086a5d46d..cf3a3c95f 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -8,9 +8,10 @@ def text(self): return self.args[0] class Attribute: - def __init__(self, name, typex): + def __init__(self, name, typex, idx=None): self.name = name self.type = typex + self.idx = idx def __str__(self): return f'[attrib] {self.name} : {self.type.name};' @@ -19,11 +20,13 @@ def __repr__(self): return str(self) class Method: - def __init__(self, name, param_names, params_types, return_type): + def __init__(self, name, param_names, param_types, return_type, param_idx, ridx=None): self.name = name self.param_names = param_names - self.param_types = params_types + self.param_types = param_types + self.param_idx = param_idx self.return_type = return_type + self.ridx = ridx def __str__(self): params = ', '.join(f'{n}:{t.name}' for n,t in zip(self.param_names, self.param_types)) @@ -78,11 +81,11 @@ def get_method(self, name:str): 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): + def define_method(self, name:str, param_names:list, param_types:list, return_type, param_idx:list, ridx=None): if name in (method.name for method in self.methods): raise SemanticError(f'Method "{name}" already defined in {self.name}') - method = Method(name, param_names, param_types, return_type) + method = Method(name, param_names, param_types, return_type, param_idx, ridx) self.methods.append(method) return method @@ -123,6 +126,9 @@ def __str__(self): def __repr__(self): return str(self) + def __eq__(self, other): + return self.conforms_to(other) and other.conforms_to(self) + class ErrorType(Type): def __init__(self): Type.__init__(self, '') @@ -195,7 +201,12 @@ def __init__(self): def can_be_inherited(self): return False + def conforms_to(self, other): + return True + def bypass(self): + return True + class Context: def __init__(self): @@ -220,9 +231,10 @@ def __repr__(self): return str(self) class VariableInfo: - def __init__(self, name, vtype): + def __init__(self, name, vtype, idx): self.name = name self.type = vtype + self.idx = idx class Scope: def __init__(self, parent=None): @@ -239,8 +251,8 @@ def create_child(self): self.children.append(child) return child - def define_variable(self, vname, vtype): - info = VariableInfo(vname, vtype) + def define_variable(self, vname, vtype, idx=None): + info = VariableInfo(vname, vtype, idx) self.locals.append(info) return info @@ -256,3 +268,49 @@ def is_defined(self, vname): def is_local(self, vname): return any(True for x in self.locals if x.name == vname) + + +class InferencerManager: + def __init__(self, context): + # given a type represented by int idx, types[idx] = (A, B), where A and B are sets + # if x in A then idx.conforms_to(x) + # if x in B then x.conforms_to(idx) + self.conforms_to = [] + self.conformed_by = [] + self.count = 0 + self.context = context + + self.obj = 'object' + + + def assign_id(self): + idx = self.count + self.conforms_to.append({self.obj}) + self.conformed_by.append(set()) + self.count += 1 + + return idx + + def upd_conforms_to(self, idx, other): + sz = len(self.conforms_to[idx]) + self.conforms_to[idx].update(other) + + return sz != len(self.conforms_to[idx]) + + def upd_conformed_by(self, idx, other): + sz = len(self.conformed_by[idx]) + self.conformed_by[idx].update(other) + + return sz != len(self.conformed_by[idx]) + + def auto_to_type(self, idx, typex): + sz = len(self.conforms_to[idx]) + self.conforms_to[idx].add(typex.name) + + return sz != len(self.conforms_to[idx]) + + def type_to_auto(self, idx, typex): + sz = len(self.conformed_by[idx]) + self.conformed_by[idx].add(typex.name) + + return sz != len(self.conformed_by[idx]) \ No newline at end of file From d9429e969e55e937d21f5d01db6b9106a6be0867 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Fri, 27 Nov 2020 18:18:20 -0500 Subject: [PATCH 034/138] refs #9 - Adding Inferencer Manager to Type Builder - An instance of the class Inferencer Manager is created. -Every time a new attribute or function is visited, if the auto_type is involved it creates a new id through the manager and it adds this id to the node and to the type is being checked --- cmp/type_builder.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cmp/type_builder.py b/cmp/type_builder.py index 341f127ba..007242c3f 100644 --- a/cmp/type_builder.py +++ b/cmp/type_builder.py @@ -1,6 +1,6 @@ import cmp.visitor as visitor from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from cmp.semantic import SemanticError, ErrorType +from cmp.semantic import SemanticError, ErrorType, InferencerManager, AutoType class TypeBuilder: @@ -8,6 +8,8 @@ def __init__(self, context, errors=[]): self.context = context self.current_type = None self.errors = errors + self.manager = InferencerManager(context) + @visitor.on('node') def visit(self, node): @@ -35,11 +37,15 @@ def visit(self, node): ## Building param-names and param-types of the method param_names = [] param_types = [] + node.index = [] for param in node.params: n, t = param param_names.append(n) + node.index.append(None) try: t = self.context.get_type(t) + if isinstance(t, AutoType): + node.index[-1] = self.manager.assign_id() except SemanticError as ex: self.errors.append(ex.text) t = ErrorType() @@ -52,9 +58,11 @@ def visit(self, node): self.errors.append(ex.text) rtype = ErrorType() + node.idx = self.manager.assign_id() if isinstance(rtype, AutoType) else None + # Defining the method in the current type. There can not be another method with the same name. try: - self.current_type.define_method(node.id, param_names, param_types, rtype) + self.current_type.define_method(node.id, param_names, param_types, rtype, node.index, node.idx) except SemanticError as ex: self.errors.append(ex.text) @@ -67,11 +75,13 @@ def visit(self, node): except SemanticError as ex: self.errors.append(ex.text) attr_type = ErrorType() + + node.idx = self.manager.assign_id() if isinstance(attr_type, AutoType) else None # Checking attribute name. No other attribute can have the same name flag = False try: - self.current_type.define_attribute(node.id, attr_type) + self.current_type.define_attribute(node.id, attr_type, node.idx) flag = True except SemanticError as ex: self.errors.append(ex.text) @@ -79,7 +89,7 @@ def visit(self, node): while not flag: node.id = f'1{node.id}' try: - self.current_type.define_attribute(node.id, attr_type) + self.current_type.define_attribute(node.id, attr_type, node.idx) flag = True except SemanticError: pass From 987e04ef28c19e1dc068561e783d7cea632d70a0 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Fri, 27 Nov 2020 18:29:10 -0500 Subject: [PATCH 035/138] refs #9 - Adding the Inferencer Manager in Type Checker - The inferencer manager is received from the Type Builder, so it has already created an id for all the variables that are auto_type in methods and attributes - In this class it adds the id to the definition of the variable in the scope - It adds the variables created in a Let Node - When the object of a dispatch is auto_type, the method can not be found in this class, so the decision is to return auto_type as the type of the dispatch. This will be checked again after inferring the type of the object --- cmp/type_checker.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 747155a7f..17d2c4bf6 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -1,5 +1,5 @@ import cmp.visitor as visitor -from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType +from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode from cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode from cmp.ast import ArithmeticNode, ComparisonNode, EqualNode @@ -16,11 +16,12 @@ INVALID_TYPE = 'SELF_TYPE is not valid' class TypeChecker: - def __init__(self, context, errors=[]): + def __init__(self, context, manager, errors=[]): self.context = context self.current_type = None self.current_method = None self.errors = errors + self.manager = manager # built-in types self.obj_type = self.context.get_type('object') @@ -46,7 +47,7 @@ def visit(self, node, scope): attributes = self.current_type.all_attributes() for values in attributes: attr, _ = values - scope.define_variable(attr.name, attr.type) + scope.define_variable(attr.name, attr.type, attr.idx) for feature in node.features: self.visit(feature, scope.create_child()) @@ -87,7 +88,7 @@ def visit(self, node, scope): if scope.is_local(var): self.errors.append(LOCAL_ALREADY_DEFINED %(var, self.current_method.name)) else: - scope.define_variable(var, self.current_method.param_types[i]) + scope.define_variable(var, self.current_method.param_types[i], self.current_method.param_idx[i]) computed_type = self.visit(node.body, scope) @@ -131,6 +132,10 @@ def visit(self, node, scope): self.errors.append(ex.text) if not self.check_conformance(obj_type, cast_type): self.errors.append(INCOMPATIBLE_TYPES %(obj_type.name, cast_type.name)) + + # if the obj that is calling the function is autotype, let it pass + if isinstance(cast_type, AutoType): + return cast_type # Check this function is defined for cast_type try: @@ -232,6 +237,7 @@ def visit(self, node, scope): def visit(self, node, scope): nscope = scope.create_child() + node.idx_list = [] for item in node.id_list: idx, typex, expr = item @@ -244,12 +250,16 @@ def visit(self, node, scope): self.errors.append(ex.text) typex = ErrorType() + node.idx_list.append(None) + if isinstance(typex, AutoType): + node.idx_list[-1] = self.manager.assign_id() + if expr is not None: expr_type = self.visit(expr, nscope) if not self.check_conformance(expr_type, typex): self.errors.append(INCOMPATIBLE_TYPES %(expr_type.name, typex.name)) - nscope.define_variable(idx, typex) + nscope.define_variable(idx, typex, node.idx_list[-1]) return self.visit(node.body, nscope) @@ -345,6 +355,10 @@ def LCA(self, types): if any(isinstance(item, ErrorType) for item in types): return ErrorType() + # check AUTO_TYPE + if any(isinstance(item, AutoType) for item in types): + return AutoType() + # check SELF_TYPE: if all(isinstance(item, SelfType) for item in types): return types[0] From b979cb10aaf22a6674f6f551635f1374b8557c8a Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Fri, 27 Nov 2020 18:30:25 -0500 Subject: [PATCH 036/138] refs #9 - Modifying pipeline to add Type Checker and Inferencer Manager --- main.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 62fde18d1..7bd03b499 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ from cmp.formatter import FormatVisitor from cmp.type_collector import TypeCollector from cmp.type_builder import TypeBuilder +from cmp.type_checker import TypeChecker def run_pipeline(G, text): @@ -33,17 +34,26 @@ def run_pipeline(G, text): print('=============== BUILDING TYPES ================') builder = TypeBuilder(context, errors) builder.visit(ast) + manager = builder.manager print('Errors: [') for error in errors: print('\t', error) print(']') print('Context:') print(context) + print('=============== CHECKING TYPES ================') + checker = TypeChecker(context, manager, errors) + checker.visit(ast, None) + print('Errors: [') + for error in errors: + print('\t', error) + print(']') + return ast text = ''' - class A inherits C { } ; + class A inherits io { } ; class C inherits B { } ; class B { } ; class B inherits A { } ; From 83168119935be1af31cfe21ee53e186e227e8a6b Mon Sep 17 00:00:00 2001 From: kikeXD Date: Fri, 27 Nov 2020 22:41:35 -0500 Subject: [PATCH 037/138] refs #12 - Checking AUTO_TYPE errors --- cmp/type_checker.py | 8 +++++++- cmp/type_collector.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 747155a7f..00d668e01 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -1,5 +1,5 @@ import cmp.visitor as visitor -from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType +from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode from cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode from cmp.ast import ArithmeticNode, ComparisonNode, EqualNode @@ -126,6 +126,8 @@ def visit(self, node, scope): if node.type is not None: try: cast_type = self.context.get_type(node.type) + if isinstance(cast_type, AutoType): + raise SemanticError('AUTO_TYPE can\'t be the type on this type of dispatch') except SemanticError as ex: cast_type = ErrorType() self.errors.append(ex.text) @@ -173,6 +175,8 @@ def visit(self, node, scope): try: var_type = self.context.get_type(typex) + if isinstance(var_type, AutoType): + raise SemanticError('The type of branch in case can\'t be AUTO_TYPE') except SemanticError as ex: self.errors.append(ex.text) var_type = ErrorType() @@ -333,6 +337,8 @@ def visit(self, node, scope): def visit(self, node, scope): try: typex = self.context.get_type(node.lex) + if isinstance(typex, AutoType): + raise SemanticError('AUTO_TYPE can\'t be instanciate with new') except SemanticError as ex: self.errors.append(ex.text) typex = ErrorType() diff --git a/cmp/type_collector.py b/cmp/type_collector.py index 35b352d42..5c25665c8 100644 --- a/cmp/type_collector.py +++ b/cmp/type_collector.py @@ -39,6 +39,8 @@ def visit(self, node): # flag will be True if the class is succesfully added to the context flag = False try: + if node.id == 'AUTO_TYPE': + raise SemanticError('Name of class can\'t be autotype') self.context.create_type(node.id) flag = True self.parent[node.id] = node.parent From 3140d8f60e153795515fcd604e26a0ba00c2a1d5 Mon Sep 17 00:00:00 2001 From: kikeXD Date: Sat, 28 Nov 2020 12:25:22 -0500 Subject: [PATCH 038/138] refs #12 - removing autotype error from case node --- cmp/type_checker.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 00d668e01..68b4de916 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -175,8 +175,6 @@ def visit(self, node, scope): try: var_type = self.context.get_type(typex) - if isinstance(var_type, AutoType): - raise SemanticError('The type of branch in case can\'t be AUTO_TYPE') except SemanticError as ex: self.errors.append(ex.text) var_type = ErrorType() From de56358f108291cefc498cef9356e8352c04d93a Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 22:50:21 -0500 Subject: [PATCH 039/138] refs #9 - Fixing built-in types names Classes names must begin with a Capital letter --- cmp/semantic.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index cf3a3c95f..b04a32ae3 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -145,21 +145,21 @@ def __eq__(self, other): class ObjectType(Type): def __init__(self): - Type.__init__(self, 'object') + Type.__init__(self, 'Object') def __eq__(self, other): return other.name == self.name or isinstance(other, ObjectType) class IOType(Type): def __init__(self): - Type.__init__(self, 'io') + Type.__init__(self, 'IO') def __eq__(self, other): return other.name == self.name or isinstance(other, IOType) class StringType(Type): def __init__(self): - Type.__init__(self, 'string') + Type.__init__(self, 'String') def __eq__(self, other): return other.name == self.name or isinstance(other, StringType) @@ -169,7 +169,7 @@ def can_be_inherited(self): class BoolType(Type): def __init__(self): - Type.__init__(self, 'bool') + Type.__init__(self, 'Bool') def __eq__(self, other): return other.name == self.name or isinstance(other, BoolType) @@ -179,7 +179,7 @@ def can_be_inherited(self): class IntType(Type): def __init__(self): - Type.__init__(self, 'int') + Type.__init__(self, 'Int') def __eq__(self, other): return other.name == self.name or isinstance(other, IntType) From 078a4232c8c985a1e621cc685e769bdf05fa519a Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 22:55:47 -0500 Subject: [PATCH 040/138] refs #9 - Adding the index as a parameter to define an attribute in Type --- cmp/semantic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index b04a32ae3..258c8d519 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -60,11 +60,11 @@ def get_attribute(self, name:str): except SemanticError: raise SemanticError(f'Attribute "{name}" is not defined in {self.name}.') - def define_attribute(self, name:str, typex): + def define_attribute(self, name:str, typex, idx=None): try: self.get_attribute(name) except SemanticError: - attribute = Attribute(name, typex) + attribute = Attribute(name, typex, idx) self.attributes.append(attribute) return attribute else: From 03fb79c5bc6b1c339220481f7a42456d3efe4d3c Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:01:34 -0500 Subject: [PATCH 041/138] refs #9 - Adding methods to Scope and Type Classes - [Scope class] Update variable, finds the given variable which has type Auto Type and changes it by the given type. - [Type Class] The methods were added to modify in the same way attributes, method parameters and method return type which were Auto Type --- cmp/semantic.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cmp/semantic.py b/cmp/semantic.py index 258c8d519..063af905c 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -101,6 +101,24 @@ def all_methods(self, clean=True): plain[method.name] = (method, self) return plain.values() if clean else plain + def update_attr(self, attr_name, attr_type): + for i, item in enumerate(self.attributes): + if item.name == attr_name: + self.attributes[i] = Attribute(attr_name, attr_type) + break + + def update_method_rtype(self, method_name, rtype): + for i, item in enumerate(self.methods): + if item.name == method_name: + self.methods[i].return_type = rtype + break + + def update_method_param(self, method_name, param_type, param_idx): + for i, item in enumerate(self.methods): + if item.name == method_name: + self.methods[i].param_types[param_idx] = param_type + break + def conforms_to(self, other): return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) @@ -269,6 +287,13 @@ def is_defined(self, vname): def is_local(self, vname): return any(True for x in self.locals if x.name == vname) + def update_variable(self, vname, vtype, index=None): + locals = self.locals if index is None else itt.islice(self.locals, index) + for i, item in enumerate(locals): + if item.name == vname: + self.locals[i] = VariableInfo(vname, vtype, item.idx) + return True + return self.parent.update_variable(vname, vtype, self.index) if self.parent is not None else False class InferencerManager: def __init__(self, context): From 3412e0cc93f003a9c7abe6dcd96998c4a4ee61ad Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:03:39 -0500 Subject: [PATCH 042/138] refs #9 - Self Type class modified A fixed type was added to the Self Type class, so whenever a variable has type SelfType, fixed_type will save the specific type of current_type at the moment of its creation --- cmp/semantic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index 063af905c..1171513d9 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -206,8 +206,9 @@ def can_be_inherited(self): return False class SelfType(Type): - def __init__(self): + def __init__(self, fixed=None): Type.__init__(self, 'SELF_TYPE') + self.fixed_type = fixed def can_be_inherited(self): return False From 340be813eb29486f910b7b5316ed947292a79f14 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:04:26 -0500 Subject: [PATCH 043/138] refs #9 - Fixing bug when finding a variable in the scope --- cmp/semantic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index 1171513d9..ae3d53458 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -280,7 +280,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 is None 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 31ab4d9ecea2aa2d3737a2158071032bd5aaa23e Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:15:49 -0500 Subject: [PATCH 044/138] refs #9 - Changing LCA function to semantic file LCA function computes the Lowest Common Ancestor. Since it is used by Type Checker, Type Inferencer and Inferencer Manager, all classes in different files, the location of the function was changed. Also, note that when using self_type it now changes it for the fixed type of the specific instance of the class --- cmp/semantic.py | 45 ++++++++++++++++++++++------------- cmp/type_checker.py | 58 +++++++-------------------------------------- 2 files changed, 36 insertions(+), 67 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index ae3d53458..42bee7b38 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -323,20 +323,31 @@ def upd_conforms_to(self, idx, other): return sz != len(self.conforms_to[idx]) - def upd_conformed_by(self, idx, other): - sz = len(self.conformed_by[idx]) - self.conformed_by[idx].update(other) - - return sz != len(self.conformed_by[idx]) - - def auto_to_type(self, idx, typex): - sz = len(self.conforms_to[idx]) - self.conforms_to[idx].add(typex.name) - - return sz != len(self.conforms_to[idx]) - - def type_to_auto(self, idx, typex): - sz = len(self.conformed_by[idx]) - self.conformed_by[idx].add(typex.name) - - return sz != len(self.conformed_by[idx]) \ No newline at end of file +def LCA(types): + # check ErrorType: + if any(isinstance(item, ErrorType) for item in types): + return ErrorType() + + # check AUTO_TYPE + if any(isinstance(item, AutoType) for item in types): + return AutoType() + + # check SELF_TYPE: + if all(isinstance(item, SelfType) for item in types): + return types[0] + + for i, item in enumerate(types): + if isinstance(item, SelfType): + types[i] = item.fixed_type + + current = types[0] + while current: + for item in types: + if not item.conforms_to(current): + break + else: + return current + current = current.parent + + # This part of the code is supposed to be unreachable + return None \ No newline at end of file diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 17d2c4bf6..2d8ac6851 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -1,5 +1,5 @@ import cmp.visitor as visitor -from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType +from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode from cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode from cmp.ast import ArithmeticNode, ComparisonNode, EqualNode @@ -30,7 +30,7 @@ def __init__(self, context, manager, errors=[]): self.string_type = self.context.get_type('string') @visitor.on('node') - def visit(self, node, scope): + def visit(self, node, scope=None): pass @visitor.when(ProgramNode) @@ -40,7 +40,6 @@ def visit(self, node, scope=None): 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) @@ -51,8 +50,7 @@ def visit(self, node, scope): for feature in node.features: self.visit(feature, scope.create_child()) - - + @visitor.when(AttrDeclarationNode) def visit(self, node, scope): attr_type = self.context.get_type(node.type) @@ -62,7 +60,6 @@ def visit(self, node, scope): if not self.check_conformance(computed_type, attr_type): self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, attr_type.name)) - @visitor.when(FuncDeclarationNode) def visit(self, node, scope): self.current_method = self.current_type.get_method(node.id) @@ -96,8 +93,7 @@ def visit(self, node, scope): rtype = self.current_method.return_type if not self.check_conformance(computed_type, rtype): self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, self.current_method.return_type.name)) - - + @visitor.when(AssignNode) def visit(self, node, scope): if node.id == "self": @@ -115,8 +111,7 @@ def visit(self, node, scope): self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, var.type.name)) return computed_type - - + @visitor.when(CallNode) def visit(self, node, scope): # Evaluate object @@ -159,7 +154,6 @@ def visit(self, node, scope): self.errors.append(ex.text) return ErrorType() - @visitor.when(CaseNode) def visit(self, node, scope): # check expression @@ -188,7 +182,7 @@ def visit(self, node, scope): computed_type = self.visit(expr, new_scope) types.append(computed_type) - return self.LCA(types) + return LCA(types) @visitor.when(BlockNode) @@ -203,7 +197,6 @@ def visit(self, node, scope): # return the type of the last expression of the list return computed_type - @visitor.when(LoopNode) def visit(self, node, scope): nscope = scope.create_child() @@ -218,7 +211,6 @@ def visit(self, node, scope): return self.obj_type - @visitor.when(ConditionalNode) def visit(self, node, scope): @@ -230,8 +222,7 @@ def visit(self, node, scope): then_type = self.visit(node.then_body, scope.create_child()) else_type = self.visit(node.else_body, scope.create_child()) - return self.LCA([then_type, else_type]) - + return LCA([then_type, else_type]) @visitor.when(LetNode) def visit(self, node, scope): @@ -263,7 +254,6 @@ def visit(self, node, scope): return self.visit(node.body, nscope) - @visitor.when(ArithmeticNode) def visit(self, node, scope): self.check_expr(node, scope) @@ -294,7 +284,6 @@ def check_equal(typex, other): return self.bool_type - @visitor.when(VoidNode) def visit(self, node, scope): self.visit(node.expr, scope) @@ -317,7 +306,6 @@ def visit(self, node, scope): return self.int_type - @visitor.when(ConstantNumNode) def visit(self, node, scope): return self.int_type @@ -343,6 +331,7 @@ def visit(self, node, scope): def visit(self, node, scope): try: typex = self.context.get_type(node.lex) + if isinstance(typex, SelfType): except SemanticError as ex: self.errors.append(ex.text) typex = ErrorType() @@ -350,36 +339,6 @@ def visit(self, node, scope): return typex - def LCA(self, types): - # check ErrorType: - if any(isinstance(item, ErrorType) for item in types): - return ErrorType() - - # check AUTO_TYPE - if any(isinstance(item, AutoType) for item in types): - return AutoType() - - # check SELF_TYPE: - if all(isinstance(item, SelfType) for item in types): - return types[0] - - for i, item in enumerate(types): - if isinstance(item, SelfType): - types[i] = self.current_type - - current = types[0] - while current: - for item in types: - if not item.conforms_to(current): - break - else: - return current - current = current.parent - - # This part of the code is supposed to be unreachable - return None - - def check_expr(self, node, scope): # checking left expr left = self.visit(node.left, scope) @@ -391,7 +350,6 @@ def check_expr(self, node, scope): if not right.conforms_to(self.int_type): self.errors.append(INCOMPATIBLE_TYPES %(right.name, self.int_type.name)) - def check_conformance(self, computed_type, attr_type): return ( computed_type.conforms_to(attr_type) From 9b58455d744247b81c667ef0ade9100642e0b1e6 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:18:58 -0500 Subject: [PATCH 045/138] refs #9 - Changes because of built-in types capital letters --- cmp/type_checker.py | 8 ++++---- cmp/type_collector.py | 25 +++++++++++-------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 2d8ac6851..bb0e65e97 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -24,10 +24,10 @@ def __init__(self, context, manager, errors=[]): self.manager = manager # built-in types - self.obj_type = self.context.get_type('object') - self.int_type = self.context.get_type('int') - self.bool_type = self.context.get_type('bool') - self.string_type = self.context.get_type('string') + self.obj_type = self.context.get_type('Object') + self.int_type = self.context.get_type('Int') + self.bool_type = self.context.get_type('Bool') + self.string_type = self.context.get_type('String') @visitor.on('node') def visit(self, node, scope=None): diff --git a/cmp/type_collector.py b/cmp/type_collector.py index 9422785d9..e7c24bead 100644 --- a/cmp/type_collector.py +++ b/cmp/type_collector.py @@ -33,7 +33,6 @@ def visit(self, node): # Order class declarations according to their depth in the inheritance tree node.declarations = self.order_types(node) - @visitor.when(ClassDeclarationNode) def visit(self, node): # flag will be True if the class is succesfully added to the context @@ -55,6 +54,7 @@ def visit(self, node): except SemanticError: pass + def define_built_in_types(self): objectx = ObjectType() iox = IOType() @@ -65,20 +65,20 @@ def define_built_in_types(self): autotype = AutoType() # Object Methods - objectx.define_method('abort', [], [], objectx) - objectx.define_method('type_name', [], [], stringx) - objectx.define_method('copy', [], [], self_type) + objectx.define_method('abort', [], [], objectx, []) + objectx.define_method('type_name', [], [], stringx, []) + objectx.define_method('copy', [], [], self_type, []) # IO Methods - iox.define_method('out_string', ['x'], [stringx], self_type) - iox.define_method('out_int', ['x'], [intx], self_type) - iox.define_method('in_string', [], [], stringx) - iox.define_method('in_int', [], [], intx) + iox.define_method('out_string', ['x'], [stringx], self_type, [None]) + iox.define_method('out_int', ['x'], [intx], self_type, [None]) + iox.define_method('in_string', [], [], stringx, []) + iox.define_method('in_int', [], [], intx, []) # String Methods - stringx.define_method('length', [], [], intx) - stringx.define_method('concat', ['s'], [stringx], stringx) - stringx.define_method('substr', ['i', 'l'], [intx, intx], stringx) + stringx.define_method('length', [], [], intx, []) + stringx.define_method('concat', ['s'], [stringx], stringx, [None]) + stringx.define_method('substr', ['i', 'l'], [intx, intx], stringx, [None]) # Setting Object as parent iox.set_parent(objectx) @@ -105,7 +105,6 @@ def check_parents(self): self.errors.append(ex.text) item_type.set_parent(built_in_types[0]) - def check_cyclic_inheritance(self): flag = [] @@ -134,8 +133,6 @@ def check_path(idx, item): if idx == len(flag): check_path(idx, item) - - def order_types(self, node): sorted_declarations = [] flag = [False] * len(node.declarations) From 6d289eb6ddce0270288f7bd63dfb1ea46b74d753 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:21:08 -0500 Subject: [PATCH 046/138] refs #9 - Adding index and fixed types of Self Type --- cmp/type_builder.py | 23 ++++++++++++++--------- cmp/type_checker.py | 20 +++++++++++++++++--- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/cmp/type_builder.py b/cmp/type_builder.py index 007242c3f..07065a0f3 100644 --- a/cmp/type_builder.py +++ b/cmp/type_builder.py @@ -1,6 +1,6 @@ import cmp.visitor as visitor from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from cmp.semantic import SemanticError, ErrorType, InferencerManager, AutoType +from cmp.semantic import SemanticError, ErrorType, InferencerManager, AutoType, SelfType class TypeBuilder: @@ -8,7 +8,9 @@ def __init__(self, context, errors=[]): self.context = context self.current_type = None self.errors = errors - self.manager = InferencerManager(context) + self.manager = InferencerManager() + + self.obj_type = self.context.get_type('Object') @visitor.on('node') @@ -23,14 +25,12 @@ def visit(self, node): self.check_main_class() - @visitor.when(ClassDeclarationNode) def visit(self, node): self.current_type = self.context.get_type(node.id) for feat in node.features: self.visit(feat) - @visitor.when(FuncDeclarationNode) def visit(self, node): @@ -44,8 +44,10 @@ def visit(self, node): node.index.append(None) try: t = self.context.get_type(t) - if isinstance(t, AutoType): - node.index[-1] = self.manager.assign_id() + if isinstance(t, SelfType): + t = SelfType(self.current_type) + elif isinstance(t, AutoType): + node.index[-1] = self.manager.assign_id(self.obj_type) except SemanticError as ex: self.errors.append(ex.text) t = ErrorType() @@ -54,11 +56,13 @@ def visit(self, node): # Checking return type try: rtype = self.context.get_type(node.type) + if isinstance(rtype, SelfType): + rtype = SelfType(self.current_type) except SemanticError as ex: self.errors.append(ex.text) rtype = ErrorType() - node.idx = self.manager.assign_id() if isinstance(rtype, AutoType) else None + node.idx = self.manager.assign_id(self.obj_type) if isinstance(rtype, AutoType) else None # Defining the method in the current type. There can not be another method with the same name. try: @@ -66,17 +70,18 @@ def visit(self, node): except SemanticError as ex: self.errors.append(ex.text) - @visitor.when(AttrDeclarationNode) def visit(self, node): # Checking attribute type try: attr_type = self.context.get_type(node.type) + if isinstance(attr_type, SelfType): + attr_type = SelfType(self.current_type) except SemanticError as ex: self.errors.append(ex.text) attr_type = ErrorType() - node.idx = self.manager.assign_id() if isinstance(attr_type, AutoType) else None + node.idx = self.manager.assign_id(self.obj_type) if isinstance(attr_type, AutoType) else None # Checking attribute name. No other attribute can have the same name flag = False diff --git a/cmp/type_checker.py b/cmp/type_checker.py index bb0e65e97..86cb75a8e 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -53,7 +53,8 @@ def visit(self, node, scope): @visitor.when(AttrDeclarationNode) def visit(self, node, scope): - attr_type = self.context.get_type(node.type) + var = scope.find_variable(node.id) + attr_type = var.type if node.expr is not None: computed_type = self.visit(node.expr, scope) @@ -122,6 +123,8 @@ def visit(self, node, scope): if node.type is not None: try: cast_type = self.context.get_type(node.type) + if isinstance(cast_type, SelfType): + cast_type = SelfType(self.current_type) except SemanticError as ex: cast_type = ErrorType() self.errors.append(ex.text) @@ -163,28 +166,36 @@ def visit(self, node, scope): # check branches types = [] + node.branch_idx = [] for branch in node.branch_list: idx, typex, expr = branch + node.branch_idx.append(None) + # check idx is not self if idx == 'self': self.errors.append(SELF_IS_READONLY) try: var_type = self.context.get_type(typex) + if isinstance(var_type, SelfType): + var_type = SelfType(self.current_type) except SemanticError as ex: self.errors.append(ex.text) var_type = ErrorType() + + # check type is autotype and assign an id in the manager + if isinstance(var_type, AutoType): + node.branch_idx[-1] = self.manager.assign_id() new_scope = nscope.create_child() - new_scope.define_variable(idx, var_type) + new_scope.define_variable(idx, var_type, node.branch_idx[-1]) computed_type = self.visit(expr, new_scope) types.append(computed_type) return LCA(types) - @visitor.when(BlockNode) def visit(self, node, scope): nscope = scope.create_child() @@ -237,6 +248,8 @@ def visit(self, node, scope): try: typex = self.context.get_type(typex) + if isinstance(typex, SelfType): + typex = SelfType(self.current_type) except SemanticError as ex: self.errors.append(ex.text) typex = ErrorType() @@ -332,6 +345,7 @@ def visit(self, node, scope): try: typex = self.context.get_type(node.lex) if isinstance(typex, SelfType): + typex = SelfType(self.current_type) except SemanticError as ex: self.errors.append(ex.text) typex = ErrorType() From c4ca68a2b3d3e318f973f0ae4cf1c2dd41ec41ef Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:21:43 -0500 Subject: [PATCH 047/138] refs #9 - Methods of Inferencer Manager --- cmp/semantic.py | 96 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index 42bee7b38..124dd8b23 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -296,32 +296,106 @@ def update_variable(self, vname, vtype, index=None): return True return self.parent.update_variable(vname, vtype, self.index) if self.parent is not None else False + class InferencerManager: - def __init__(self, context): + def __init__(self): # given a type represented by int idx, types[idx] = (A, B), where A and B are sets # if x in A then idx.conforms_to(x) # if x in B then x.conforms_to(idx) self.conforms_to = [] self.conformed_by = [] + self.infered_type = [] self.count = 0 - self.context = context - - self.obj = 'object' - - def assign_id(self): + def assign_id(self, obj_type): idx = self.count - self.conforms_to.append({self.obj}) - self.conformed_by.append(set()) + self.conforms_to.append([obj_type]) + self.conformed_by.append([]) + self.infered_type.append(None) self.count += 1 return idx def upd_conforms_to(self, idx, other): - sz = len(self.conforms_to[idx]) - self.conforms_to[idx].update(other) + for item in other: + self.auto_to_type(idx, item) + + def upd_conformed_by(self, idx, other): + for item in other: + self.type_to_auto(idx, item) + + def auto_to_type(self, idx, typex): + if isinstance(typex, SelfType): + typex = typex.fixed_type + if not isinstance(typex, ErrorType): + self.conforms_to[idx].append(typex) + + def type_to_auto(self, idx, typex): + if isinstance(typex, SelfType): + typex = typex.fixed_type + if not isinstance(typex, ErrorType): + self.conformed_by[idx].append(typex) + + def infer(self, idx): + try: + assert self.infered_type[idx] is None + assert len(self.conforms_to[idx]) > 1 or len(self.conformed_by[idx]) > 0 + + try: + start = self.get_min(self.conforms_to[idx]) + self.infered_type[idx] = start + + if len(self.conformed_by[idx]) > 0: + final = LCA(self.conformed_by[idx]) + assert final.conforms_to(start) + self.infered_type[idx] = final + + except AssertionError: + self.infered_type[idx] = ErrorType() + + return True + except AssertionError: + return False + + def infer_all(self): + change = False + for i in range(self.count): + change |= self.infer(i) + + return change + + def infer_object(self, obj_type): + for i in range(self.count): + if self.infered_type[i] is None: + self.infered_type[i] = obj_type + + def get_min(self, types): + path = [] + + def find(typex): + for i, item in enumerate(path): + if item.name == typex.name: + return i + return len(path) + + + for item in types: + current = [] + while item is not None: + idx = find(item) + if idx == len(path): + current.append(item) + item = item.parent + continue + + assert idx == len(path) - 1 or len(current) == 0 + break + current.reverse() + path.extend(current) + + return path[-1] + - return sz != len(self.conforms_to[idx]) def LCA(types): # check ErrorType: From 80b62a3b97bdf2234fc85db0fd77cb6ec9de03bb Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:23:07 -0500 Subject: [PATCH 048/138] refs #9 - Type Inferencer class Type Inferencer is a class that visits all the nodes of the ast searching for those elements that are autotype to infer their static type --- cmp/type_inferencer.py | 470 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 cmp/type_inferencer.py diff --git a/cmp/type_inferencer.py b/cmp/type_inferencer.py new file mode 100644 index 000000000..357d722af --- /dev/null +++ b/cmp/type_inferencer.py @@ -0,0 +1,470 @@ +import cmp.visitor as visitor +from cmp.semantic import SemanticError, ErrorType, SelfType, AutoType, LCA +from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from cmp.ast import ArithmeticNode, ComparisonNode, EqualNode +from cmp.ast import VoidNode, NotNode, NegNode +from cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode + + +TYPE_CONFORMANCE = 'Type "%s" can not conform to type "%s"' +AUTOTYPE_ERROR = 'Incorrect use of AUTO_TYPE' + +class TypeInferencer: + def __init__(self, context, manager, errors=[]): + self.context = context + self.errors = errors + self.manager = manager + + self.current_type = None + self.current_method = None + self.scope_children_id = 0 + + # built-in types + self.obj_type = self.context.get_type('Object') + self.int_type = self.context.get_type('Int') + self.bool_type = self.context.get_type('Bool') + self.string_type = self.context.get_type('String') + + @visitor.on('node') + def visit(self, node, scope, types=None): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope, types=None): + if types is None: + types = [] + + change = self.manager.count > 0 + while change: + change = False + + for declaration, child_scope in zip(node.declarations, scope.children): + self.scope_children_id = 0 + self.visit(declaration, child_scope, types) + + change = self.manager.infer_all() + + self.manager.infer_object(self.obj_type) + for declaration, child_scope in zip(node.declarations, scope.children): + self.scope_children_id = 0 + self.visit(declaration, child_scope, types) + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope, types): + self.current_type = self.context.get_type(node.id) + + for feature, child_scope in zip(node.features, scope.children): + self.scope_children_id = 0 + self.visit(feature, child_scope, types) + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope, types): + var_attr = scope.find_variable(node.id) + attr_type = var_attr.type + idx = var_attr.idx + + if isinstance(attr_type, AutoType): + inf_type = self.manager.infered_type[idx] + if inf_type is not None: + if isinstance(inf_type, ErrorType): + self.errors.append(AUTOTYPE_ERROR) + else: + node.type = inf_type.name + self.current_type.update_attr(node.id, inf_type) + scope.update_variable(node.id, inf_type) + attr_type = inf_type + + conforms_to_types = [] + if isinstance(attr_type, AutoType): + conforms_to_types.extend(self.manager.conforms_to[idx]) + else: + conforms_to_types.append(attr_type) + + if node.expr is not None: + _, computed_types = self.visit(node.expr, scope, conforms_to_types) + if isinstance(attr_type, AutoType): + self.manager.upd_conformed_by(node.idx, computed_types) + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope, types): + self.current_method = self.current_type.get_method(node.id) + + method_params = [] + for i, t in enumerate(self.current_method.param_types): + idx = self.current_method.param_idx[i] + name = self.current_method.param_names[i] + if isinstance(t, AutoType): + inf_type = self.manager.infered_type[idx] + if inf_type is not None: + if isinstance(inf_type, ErrorType): + self.errors.append(AUTOTYPE_ERROR) + else: + node.params[i][1] = inf_type.name + self.current_type.update_method_param(name, inf_type, i) + scope.update_variable(name, inf_type) + t = inf_type + method_params.append(t) + + rtype = self.current_method.return_type + idx = self.current_method.ridx + if isinstance(rtype, AutoType): + inf_type = self.manager.infered_type[idx] + if inf_type is not None: + if isinstance(inf_type, ErrorType): + self.errors.append(AUTOTYPE_ERROR) + else: + node.type = inf_type.name + self.current_type.update_method_rtype(self.current_method.name, inf_type) + rtype = inf_type + + # checking overwriting + try: + method = self.current_type.parent.get_method(node.id) + + for i, t in enumerate(method_params): + if not isinstance(method.param_types[i], AutoType) and isinstance(t, AutoType): + self.manager.auto_to_type(self.current_method.param_idx[i], method.param_types[i]) + self.manager.type_to_auto(self.current_method.param_idx[i], method.param_types[i]) + + if isinstance(rtype, AutoType) and not isinstance(method.return_type, AutoType): + self.manager.auto_to_type(idx, method.return_type) + self.manager.type_to_auto(idx, method.return_type) + except SemanticError: + pass + + # checking return type in computed types of the expression + conforms_to_types = [] + if isinstance(rtype, AutoType): + conforms_to_types.extend(self.manager.conforms_to[idx]) + else: + conforms_to_types.append(rtype) + _, computed_types = self.visit(node.body, scope, conforms_to_types) + if isinstance(rtype, AutoType): + self.manager.upd_conformed_by(self.current_method.ridx, computed_types) + + @visitor.when(AssignNode) + def visit(self, node, scope, types): + var = scope.find_variable(node.id) + # obtaining defined variable + if isinstance(var.type, AutoType): + inf_type = self.manager.infered_type[var.idx] + if inf_type is not None: + scope.update_variable(var.name, inf_type) + var.type = inf_type + + conforms_to_types = [] + if isinstance(var.type, AutoType): + conforms_to_types.extend(self.manager.conforms_to[var.idx]) + else: + conforms_to_types.append(var.type) + conforms_to_types.extend(types) + + scope_index = self.scope_children_id + self.scope_children_id = 0 + + typex, computed_types = self.visit(node.expr, scope.children[scope_index], conforms_to_types) + if isinstance(var.type, AutoType): + self.manager.upd_conformed_by(var.idx, computed_types) + + self.scope_children_id = scope_index + 1 + return typex, computed_types + + @visitor.when(CallNode) + def visit(self, node, scope, types): + # Check cast type + cast_type = None + if node.type is not None: + try: + cast_type = self.context.get_type(node.type) + if isinstance(cast_type, AutoType): + cast_type = None + elif isinstance(cast_type, SelfType): + cast_type = SelfType(self.current_type) + except SemanticError: + pass + + conforms_to_types = [] if cast_type is None else [cast_type] + # Check object + obj_type, computed_types = self.visit(node.obj, scope, conforms_to_types) + + if cast_type is None: + cast_type = obj_type + + # if the obj that is calling the function is autotype, let it pass + if isinstance(cast_type, AutoType): + return cast_type, [] + + # Check this function is defined for cast_type + try: + method = cast_type.get_method(node.id) + if not len(node.args) == len(method.param_types): + return ErrorType(), [] + for i, arg in enumerate(node.args): + arg_type = method.param_types[i] + if isinstance(arg_type, AutoType): + inf_type = self.manager.infered_type[method.param_idx[i]] + if inf_type is not None: + arg_type = inf_type + + conforms_to_types = [] + if isinstance(arg_type, AutoType): + conforms_to_types.extend(self.manager.conforms_to[method.param_idx[i]]) + else: + conforms_to_types.append(arg_type) + _, computed_types = self.visit(arg, scope, conforms_to_types) + if isinstance(arg_type, AutoType): + self.manager.upd_conformed_by(method.param_idx[i], computed_types) + + # check return_type + computed_types = [] + rtype = method.return_type + if isinstance(rtype, SelfType): + rtype = obj_type + + if isinstance(rtype, AutoType): + self.manager.upd_conforms_to(method.ridx, types) + computed_types.extend(self.manager.conformed_by[method.ridx]) + else: + computed_types.append(rtype) + + return rtype, computed_types + + except SemanticError: + return ErrorType(), [] + + @visitor.when(CaseNode) + def visit(self, node, scope, types): + # check expression + self.visit(node.expr, scope, set()) + + scope_index = self.scope_children_id + nscope = scope.children[scope_index] + + # check branches + expr_types = [] + for i, (branch, child_scope) in enumerate(zip(node.branch_list, nscope.children)): + branch_name, branch_type, expr = branch + if isinstance(branch_type, AutoType): + inf_type = self.manager.infered_type[node.branch_idx[i]] + if inf_type is not None: + if isinstance(inf_type, ErrorType): + self.errors.append(AUTOTYPE_ERROR) + else: + node.branch_list[i][1] = inf_type.name + scope.update_variable(branch_name, inf_type) + + self.scope_children_id = 0 + + _, computed_types = self.visit(expr, child_scope, types) + expr_types.extend(computed_types) + + self.scope_children_id = scope_index + 1 + return LCA(expr_types), expr_types + + @visitor.when(BlockNode) + def visit(self, node, scope, types): + scope_index = self.scope_children_id + nscope = scope.children[scope_index] + self.scope_children_id = 0 + + # Check expressions but last one + sz = len(node.expr_list) - 1 + for expr in node.expr_list[:sz]: + self.visit(expr, nscope, []) + + # Check last expression + typex, computed_types = self.visit(node.expr_list[-1], nscope, types) + + # return the type of the last expression of the list + self.scope_children_id = scope_index + return typex, computed_types + + @visitor.when(LoopNode) + def visit(self, node, scope, types): + scope_index = self.scope_children_id + nscope = scope.children[scope_index] + self.scope_children_id = 0 + + # checking condition: it must conform to bool + self.visit(node.condition, nscope, [self.bool_type]) + + # checking body + self.visit(node.body, nscope, []) + + self.scope_children_id = scope_index + return self.obj_type, [self.obj_type] + + @visitor.when(ConditionalNode) + def visit(self, node, scope, types): + # check condition conforms to bool + self.visit(node.condition, scope, [self.bool_type]) + + branch_types = [] + + scope_index = self.scope_children_id + self.scope_children_id = 0 + _, then_types = self.visit(node.then_body, scope.children[scope_index], types) + scope_index += 1 + self.scope_children_id = 0 + _, else_types = self.visit(node.else_body, scope.children[scope_index], types) + + branch_types.extend(then_types) + branch_types.extend(else_types) + + self.scope_children_id = scope_index + 1 + return LCA(branch_types), branch_types + + @visitor.when(LetNode) + def visit(self, node, scope, types): + scope_index = self.scope_children_id + nscope = scope.children[scope_index] + self.scope_children_id = 0 + + for i, item in enumerate(node.id_list): + var_name, _, expr = item + var = scope.find_variable(var_name) + + if isinstance(var.type, AutoType): + inf_type = self.manager.infered_type[node.idx_list[i]] + if inf_type is not None: + if isinstance(inf_type, ErrorType): + self.errors.append(AUTOTYPE_ERROR) + else: + node.id_list[i] = inf_type.name + scope.update_variable(var_name, inf_type) + var.type = inf_type + + conforms_to_types = [] + if isinstance(var.type, AutoType): + conforms_to_types.extend(self.manager.conforms_to[node.idx_list[i]]) + else: + conforms_to_types.append(var.type) + if expr is not None: + _, computed_types = self.visit(expr, nscope, conforms_to_types) + if isinstance(var.type, AutoType): + self.manager.upd_conformed_by(node.idx_list[i], computed_types) + + expr_type, computed_types = self.visit(node.body, nscope, types) + + self.scope_children_id = scope_index + 1 + return expr_type, computed_types + + @visitor.when(ArithmeticNode) + def visit(self, node, scope, types): + self.check_expr(node, scope) + + # Check int type conforms to all the types in types + self.check_conformance(types, self.int_type) + + return self.int_type, [self.int_type] + + @visitor.when(ComparisonNode) + def visit(self, node, scope, types): + self.check_expr(node, scope) + + # Check bool type conforms to all the types in types + self.check_conformance(types, self.bool_type) + + return self.bool_type, [self.bool_type] + + @visitor.when(EqualNode) + def visit(self, node, scope, types): + left, _ = self.visit(node.left, scope, []) + right, _ = self.visit(node.right, scope, []) + + fixed_types = [self.int_type, self.bool_type, self.string_type] + def check_equal(typex): + if not isinstance(typex, AutoType) and not isinstance(typex, ErrorType): + for t in fixed_types: + if typex.conforms_to(t): + return True + return False + + if check_equal(left): + self.visit(node.right, scope, [left]) + elif check_equal(right): + self.visit(node.left, scope, [right]) + + self.check_conformance(types, self.bool_type) + + return self.bool_type, [self.bool_type] + + @visitor.when(VoidNode) + def visit(self, node, scope, types): + self.visit(node.expr, scope, []) + self.check_conformance(types, self.bool_type) + + return self.bool_type, [self.bool_type] + + @visitor.when(NotNode) + def visit(self, node, scope, types): + self.visit(node.expr, scope, [self.bool_type]) + self.check_conformance(types, self.bool_type) + + return self.bool_type, [self.bool_type] + + @visitor.when(NegNode) + def visit(self, node, scope, types): + self.visit(node.expr, scope, [self.int_type]) + self.check_conformance(types, self.int_type) + + return self.int_type, [self.int_type] + + @visitor.when(ConstantNumNode) + def visit(self, node, scope, types): + self.check_conformance(types, self.int_type) + return self.int_type, [self.int_type] + + @visitor.when(ConstantBoolNode) + def visit(self, node, scope, types): + self.check_conformance(types, self.bool_type) + return self.bool_type, [self.bool_type] + + @visitor.when(ConstantStringNode) + def visit(self, node, scope, types): + self.check_conformance(types, self.string_type) + return self.string_type, [self.string_type] + + @visitor.when(VariableNode) + def visit(self, node, scope, types): + var = scope.find_variable(node.lex) + if isinstance(var.type, AutoType): + inf_type = self.manager.infered_type[var.idx] + if inf_type is not None: + scope.update_variable(var.name, inf_type) + var.type = inf_type + + conformed_by = [] + if isinstance(var.type, AutoType): + self.manager.upd_conforms_to(var.idx, types) + conformed_by.extend(self.manager.conformed_by[var.idx]) + else: + self.check_conformance(types, var.type) + conformed_by.append(var.type) + + return var.type, conformed_by + + @visitor.when(InstantiateNode) + def visit(self, node, scope, types): + try: + typex = self.context.get_type(node.lex) + if isinstance(typex, SelfType): + typex = SelfType(self.current_type) + except SemanticError: + typex = ErrorType() + + self.check_conformance(types, typex) + + return typex, [typex] + + + + def check_expr(self, node, scope): + self.visit(node.left, scope, [self.int_type]) + self.visit(node.right, scope, [self.int_type]) + + def check_conformance(self, types, typex): + for item in types: + if not typex.conforms_to(item): + self.errors.append(TYPE_CONFORMANCE %(typex.name, item.name)) From 74b6b107ae00f05b9f5e493d33da747bc96742f9 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 29 Nov 2020 23:24:10 -0500 Subject: [PATCH 049/138] refs #9 - Changes in the pipeline to add type inferencer visitor Also some test cases were also added --- main.py | 31 ++++++++++++++++++++++++++----- test_cases/inference.txt | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 test_cases/inference.txt diff --git a/main.py b/main.py index 7bd03b499..84566081f 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ from cmp.type_collector import TypeCollector from cmp.type_builder import TypeBuilder from cmp.type_checker import TypeChecker +from cmp.type_inferencer import TypeInferencer def run_pipeline(G, text): @@ -42,21 +43,41 @@ def run_pipeline(G, text): print('Context:') print(context) print('=============== CHECKING TYPES ================') + checker = TypeChecker(context, manager, []) + scope = checker.visit(ast) + print('=============== INFERING TYPES ================') + temp_errors = [] + inferencer = TypeInferencer(context, manager, temp_errors) + inferencer.visit(ast, scope) + print('Errors: [') + for error in temp_errors: + print('\t', error) + print(']') + print('=============== LAST CHECK ================') + errors.extend(temp_errors) checker = TypeChecker(context, manager, errors) - checker.visit(ast, None) + checker.visit(ast) print('Errors: [') for error in errors: print('\t', error) print(']') + formatter = FormatVisitor() + tree = formatter.visit(ast) + print(tree) return ast text = ''' - class A inherits io { } ; - class C inherits B { } ; - class B { } ; - class B inherits A { } ; + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- c . substr ( 0 , a ) ; + } } ; + } ; + ''' if __name__ == '__main__': ast = run_pipeline(G, text) \ No newline at end of file diff --git a/test_cases/inference.txt b/test_cases/inference.txt new file mode 100644 index 000000000..f0b9261f5 --- /dev/null +++ b/test_cases/inference.txt @@ -0,0 +1,24 @@ +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- c . concat ( a ) ; + 1 ; + } } ; + } ; + +''' + +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- c . substr ( 0 , a ) ; + } } ; + } ; + +''' \ No newline at end of file From 9b58d4fa149c7db62c1f8b06a6f1d9ecc1f55f87 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 00:47:09 -0500 Subject: [PATCH 050/138] refs #9 - Fixing bugs - [Type Inferencer] Tuples can not be modified the way it was - [Type checker] Adding variable self to scope and fixing arguments passed to manager to assign a new index --- cmp/type_checker.py | 3 +- cmp/type_inferencer.py | 2 +- test_cases/inference.txt | 128 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 86cb75a8e..cc8a051f6 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -43,6 +43,7 @@ def visit(self, node, scope=None): @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)) attributes = self.current_type.all_attributes() for values in attributes: attr, _ = values @@ -186,7 +187,7 @@ def visit(self, node, scope): # check type is autotype and assign an id in the manager if isinstance(var_type, AutoType): - node.branch_idx[-1] = self.manager.assign_id() + node.branch_idx[-1] = self.manager.assign_id(self.obj_type) new_scope = nscope.create_child() new_scope.define_variable(idx, var_type, node.branch_idx[-1]) diff --git a/cmp/type_inferencer.py b/cmp/type_inferencer.py index 357d722af..f9270538b 100644 --- a/cmp/type_inferencer.py +++ b/cmp/type_inferencer.py @@ -100,7 +100,7 @@ def visit(self, node, scope, types): if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) else: - node.params[i][1] = inf_type.name + node.params[i] = (node.params[i][0], inf_type.name) self.current_type.update_method_param(name, inf_type, i) scope.update_variable(name, inf_type) t = inf_type diff --git a/test_cases/inference.txt b/test_cases/inference.txt index f0b9261f5..9a2398480 100644 --- a/test_cases/inference.txt +++ b/test_cases/inference.txt @@ -21,4 +21,132 @@ text = ''' } } ; } ; +''' + +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- isvoid a ; + a ; + } } ; + } ; + +''' + +se_jodio_el_self = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + case self of + x : Int => x ; + y : AUTO_TYPE => y ; + esac ; + } } ; + } ; + +''' + +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- a <- isvoid a ; + 1 ; + } } ; + } ; + +''' + +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- foo ( a ) ; + 1 ; + } } ; + + foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; + } ; + +''' + +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- foo ( a ) ; + b ; + } } ; + + foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; + } ; + +''' + +text = ''' + class B { + k : AUTO_TYPE ; + } ; + + class A inherits B { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b <- k + 1 ; + b ; + } } ; + + foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; + } ; + +''' + +text = ''' + class B { + k : AUTO_TYPE ; + } ; + + class A inherits B { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b + k + a ; + 1 ; + } } ; + + foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; + } ; + +''' + +text = ''' + class B { + k : AUTO_TYPE ; + } ; + + class A inherits B { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + c : String ; + f ( ) : Int { { + b + a ; + k <- b . copy ( ) ; + } } ; + + foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; + } ; + ''' \ No newline at end of file From d5e94971d0c13bfb0c49f4bb46fd95b524ed9dbb Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 11:16:10 -0500 Subject: [PATCH 051/138] refs #14 - New scope for every variable defined in let node [Type Checker] When visiting a let node for the first time, it creates a new child scope for every variable that it is defined in it. This allows the redefinition of variables in the let so that the new ones hide the existence of the older ones. [Type Inferencer] When visiting a Let Node, since the variables and the scope is already created, we need to take into consideration that for every variable defined a child scope was created --- cmp/type_checker.py | 16 ++++++++++------ cmp/type_inferencer.py | 16 +++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index cc8a051f6..67cdd7c78 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -240,12 +240,16 @@ def visit(self, node, scope): def visit(self, node, scope): nscope = scope.create_child() - node.idx_list = [] - for item in node.id_list: + node.idx_list = [None] * len(node.id_list) + for i, item in enumerate(node.id_list): idx, typex, expr = item + # create a new_scope for every variable defined + new_scope = nscope.create_child() if idx == 'self': self.errors.append(SELF_IS_READONLY) + idx = f'1{idx}' + node.id_list[i] = (idx, typex, expr) try: typex = self.context.get_type(typex) @@ -255,16 +259,16 @@ def visit(self, node, scope): self.errors.append(ex.text) typex = ErrorType() - node.idx_list.append(None) if isinstance(typex, AutoType): - node.idx_list[-1] = self.manager.assign_id() + node.idx_list[i] = self.manager.assign_id(self.obj_type) if expr is not None: - expr_type = self.visit(expr, nscope) + expr_type = self.visit(expr, new_scope) if not self.check_conformance(expr_type, typex): self.errors.append(INCOMPATIBLE_TYPES %(expr_type.name, typex.name)) - nscope.define_variable(idx, typex, node.idx_list[-1]) + new_scope.define_variable(idx, typex, node.idx_list[i]) + nscope = new_scope return self.visit(node.body, nscope) diff --git a/cmp/type_inferencer.py b/cmp/type_inferencer.py index f9270538b..786bb2f4c 100644 --- a/cmp/type_inferencer.py +++ b/cmp/type_inferencer.py @@ -322,8 +322,12 @@ def visit(self, node, scope, types): self.scope_children_id = 0 for i, item in enumerate(node.id_list): + temp_scope_index = self.scope_children_id + new_scope = nscope.children[temp_scope_index] + self.scope_children_id = 0 + var_name, _, expr = item - var = scope.find_variable(var_name) + var = new_scope.find_variable(var_name) if isinstance(var.type, AutoType): inf_type = self.manager.infered_type[node.idx_list[i]] @@ -331,8 +335,8 @@ def visit(self, node, scope, types): if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) else: - node.id_list[i] = inf_type.name - scope.update_variable(var_name, inf_type) + node.id_list[i] = (var_name, inf_type.name, expr) + new_scope.update_variable(var_name, inf_type) var.type = inf_type conforms_to_types = [] @@ -340,13 +344,15 @@ def visit(self, node, scope, types): conforms_to_types.extend(self.manager.conforms_to[node.idx_list[i]]) else: conforms_to_types.append(var.type) + if expr is not None: - _, computed_types = self.visit(expr, nscope, conforms_to_types) + _, computed_types = self.visit(expr, new_scope, conforms_to_types) if isinstance(var.type, AutoType): self.manager.upd_conformed_by(node.idx_list[i], computed_types) - expr_type, computed_types = self.visit(node.body, nscope, types) + nscope = new_scope + expr_type, computed_types = self.visit(node.body, nscope, types) self.scope_children_id = scope_index + 1 return expr_type, computed_types From 5a3de401933fd2f0907d325418017ec6e3744160 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 11:17:53 -0500 Subject: [PATCH 052/138] refs #14 - Fixing methods for adding types to the inferencer manager When adding a type to the list of conformed by or conforms to, it now checks the type to add is not already in the list; if that is the case it does not add it. --- cmp/semantic.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index 124dd8b23..8ccb7d5c1 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -327,14 +327,24 @@ def upd_conformed_by(self, idx, other): def auto_to_type(self, idx, typex): if isinstance(typex, SelfType): typex = typex.fixed_type - if not isinstance(typex, ErrorType): + try: + assert not isinstance(typex, ErrorType) + assert not any(item.name == typex.name for item in self.conforms_to[idx]) + self.conforms_to[idx].append(typex) + except AssertionError: + pass def type_to_auto(self, idx, typex): if isinstance(typex, SelfType): typex = typex.fixed_type - if not isinstance(typex, ErrorType): + try: + assert not isinstance(typex, ErrorType) + assert not any(item.name == typex.name for item in self.conformed_by[idx]) + self.conformed_by[idx].append(typex) + except AssertionError: + pass def infer(self, idx): try: @@ -352,7 +362,6 @@ def infer(self, idx): except AssertionError: self.infered_type[idx] = ErrorType() - return True except AssertionError: return False From 328f283161f3d596f20dfd723ec6e1981e2cb16c Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 11:18:41 -0500 Subject: [PATCH 053/138] refs #14- Testing the inference using let node Some test cases were added --- main.py | 6 +++--- test_cases/let_inference.txt | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 test_cases/let_inference.txt diff --git a/main.py b/main.py index 84566081f..684e43515 100644 --- a/main.py +++ b/main.py @@ -72,9 +72,9 @@ def run_pipeline(G, text): class A { b : AUTO_TYPE ; a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- c . substr ( 0 , a ) ; + + f ( ) : AUTO_TYPE { { + let x : String , x : AUTO_TYPE <- a + b , self : AUTO_TYPE , x : AUTO_TYPE <- let c : AUTO_TYPE in a in x ; } } ; } ; diff --git a/test_cases/let_inference.txt b/test_cases/let_inference.txt new file mode 100644 index 000000000..b25763c1f --- /dev/null +++ b/test_cases/let_inference.txt @@ -0,0 +1,35 @@ +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + + f ( ) : Int { { + let x : String , x : AUTO_TYPE <- a + b in x ; + } } ; + } ; + +''' + +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + + f ( ) : Int { { + let x : String , x : AUTO_TYPE <- a + b , self : AUTO_TYPE , x : AUTO_TYPE <- let c : AUTO_TYPE in a in x ; + } } ; + } ; + +''' + +text = ''' + class A { + b : AUTO_TYPE ; + a : AUTO_TYPE ; + + f ( ) : AUTO_TYPE { { + let x : String , x : AUTO_TYPE <- a + b , self : AUTO_TYPE , x : AUTO_TYPE <- let c : AUTO_TYPE in a in x ; + } } ; + } ; + +''' \ No newline at end of file From 8018b8b77050894b9b91a8d7c8f59e5305106798 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 14:32:19 -0500 Subject: [PATCH 054/138] refs # 17 - Adding string and bool constants to the lexer --- cmp/lexer.py | 6 ++++- cmp/semantic.py | 61 ++++++++++++++++++++++++--------------------- cmp/type_builder.py | 1 - cmp/type_checker.py | 3 +++ 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/cmp/lexer.py b/cmp/lexer.py index cbbfe7106..0e91c6b8e 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -1,7 +1,7 @@ from cmp.utils import Token, tokenizer from cmp.grammar import * -fixed_tokens = { t.Name: Token(t.Name, t) for t in G.terminals if t not in { idx, num }} +fixed_tokens = { t.Name: Token(t.Name, t) for t in G.terminals if t not in { idx, num, stringx, boolx }} @tokenizer(G, fixed_tokens) def tokenize_text(token): @@ -10,6 +10,10 @@ def tokenize_text(token): float(lex) return token.transform_to(num) except ValueError: + if lex[0] == '"' and lex[-1] == '"': + return token.transform_to(stringx) + if lex == 'true' or lex == 'false': + return token.transform_to(boolx) return token.transform_to(idx) def pprint_tokens(tokens): diff --git a/cmp/semantic.py b/cmp/semantic.py index 8ccb7d5c1..e4a1e1836 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -144,8 +144,8 @@ def __str__(self): def __repr__(self): return str(self) - def __eq__(self, other): - return self.conforms_to(other) and other.conforms_to(self) + # def __eq__(self, other): + # return self.conforms_to(other) and other.conforms_to(self) class ErrorType(Type): def __init__(self): @@ -407,30 +407,33 @@ def find(typex): def LCA(types): - # check ErrorType: - if any(isinstance(item, ErrorType) for item in types): - return ErrorType() - - # check AUTO_TYPE - if any(isinstance(item, AutoType) for item in types): - return AutoType() - - # check SELF_TYPE: - if all(isinstance(item, SelfType) for item in types): - return types[0] - - for i, item in enumerate(types): - if isinstance(item, SelfType): - types[i] = item.fixed_type - - current = types[0] - while current: - for item in types: - if not item.conforms_to(current): - break - else: - return current - current = current.parent - - # This part of the code is supposed to be unreachable - return None \ No newline at end of file + if len(types) == 0: + return None + + # check ErrorType: + if any(isinstance(item, ErrorType) for item in types): + return ErrorType() + + # check AUTO_TYPE + if any(isinstance(item, AutoType) for item in types): + return AutoType() + + # check SELF_TYPE: + if all(isinstance(item, SelfType) for item in types): + return types[0] + + for i, item in enumerate(types): + if isinstance(item, SelfType): + types[i] = item.fixed_type + + current = types[0] + while current: + for item in types: + if not item.conforms_to(current): + break + else: + return current + current = current.parent + + # This part of the code is supposed to be unreachable + return None \ No newline at end of file diff --git a/cmp/type_builder.py b/cmp/type_builder.py index d40505958..8a6d67191 100644 --- a/cmp/type_builder.py +++ b/cmp/type_builder.py @@ -36,7 +36,6 @@ def visit(self, node): @visitor.when(FuncDeclarationNode) def visit(self, node): - ## Building param-names and param-types of the method param_names = [] param_types = [] diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 67cdd7c78..0254a0372 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -135,6 +135,9 @@ def visit(self, node, scope): # if the obj that is calling the function is autotype, let it pass if isinstance(cast_type, AutoType): return cast_type + + if isinstance(cast_type, SelfType): + cast_type = self.current_type # Check this function is defined for cast_type try: From 5cb4b88e6f650ffa344b76d23778495e5782b96d Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 14:33:35 -0500 Subject: [PATCH 055/138] refs #17 - Fixing bug in Type Collector When ordering the declarations in the Program Node, types can inherit from a built in type apart from Object --- cmp/type_collector.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmp/type_collector.py b/cmp/type_collector.py index e7c24bead..adc324516 100644 --- a/cmp/type_collector.py +++ b/cmp/type_collector.py @@ -136,7 +136,6 @@ def check_path(idx, item): def order_types(self, node): sorted_declarations = [] flag = [False] * len(node.declarations) - obj_name = built_in_types[0].name change = True while change: @@ -146,7 +145,7 @@ def order_types(self, node): for i, dec in enumerate(node.declarations): if not flag[i]: typex = self.context.get_type(dec.id) - if typex.parent.name in [item.id for item in sorted_declarations] or typex.parent.name == obj_name: + if typex.parent.name in [item.id for item in sorted_declarations] or any(typex.parent.name == bit.name for bit in built_in_types): current.append(dec) flag[i] = True change = True From 70db4daed179ab2baabae8b1a48f483fea607b0f Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 14:34:13 -0500 Subject: [PATCH 056/138] refs #17 - Orientation test cases passed --- cmp/type_inferencer.py | 6 +- main.py | 19 +++--- test_cases/orientation_examples.txt | 91 +++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 test_cases/orientation_examples.txt diff --git a/cmp/type_inferencer.py b/cmp/type_inferencer.py index 786bb2f4c..1779efadc 100644 --- a/cmp/type_inferencer.py +++ b/cmp/type_inferencer.py @@ -180,16 +180,18 @@ def visit(self, node, scope, types): if isinstance(cast_type, AutoType): cast_type = None elif isinstance(cast_type, SelfType): - cast_type = SelfType(self.current_type) + cast_type = self.current_type except SemanticError: pass - conforms_to_types = [] if cast_type is None else [cast_type] # Check object + conforms_to_types = [] if cast_type is None else [cast_type] obj_type, computed_types = self.visit(node.obj, scope, conforms_to_types) if cast_type is None: cast_type = obj_type + if isinstance(cast_type, SelfType): + cast_type = self.current_type # if the obj that is calling the function is autotype, let it pass if isinstance(cast_type, AutoType): diff --git a/main.py b/main.py index 684e43515..214de17c3 100644 --- a/main.py +++ b/main.py @@ -69,15 +69,18 @@ def run_pipeline(G, text): text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - - f ( ) : AUTO_TYPE { { - let x : String , x : AUTO_TYPE <- a + b , self : AUTO_TYPE , x : AUTO_TYPE <- let c : AUTO_TYPE in a in x ; - } } ; +class A { + f ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { + if ( a = 1 ) then b else + g ( a + 1 , b / 2 ) + fi } ; - + g ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { + if ( b = 1 ) then a else + f ( a / 2 , b + 1 ) + fi + } ; +} ; ''' if __name__ == '__main__': ast = run_pipeline(G, text) \ No newline at end of file diff --git a/test_cases/orientation_examples.txt b/test_cases/orientation_examples.txt new file mode 100644 index 000000000..876492428 --- /dev/null +++ b/test_cases/orientation_examples.txt @@ -0,0 +1,91 @@ +class Main inherits IO { + main ( ) : AUTO_TYPE { + let x : AUTO_TYPE <- 3 + 2 in { + case x of + y : Int => out_string ( "Ok" ) ; + esac ; + } + } ; +} ; + +class Point { + x : AUTO_TYPE ; + y : AUTO_TYPE ; + init ( n : Int , m : Int ) : SELF_TYPE { { + x <- n ; + y <- m ; + } } ; +} ; + + +class Point { + x : AUTO_TYPE ; + y : AUTO_TYPE ; + succ ( n : Int ) : AUTO_TYPE { n + 1 } ; +} ; + + +class Point { + x : AUTO_TYPE ; + y : AUTO_TYPE ; + succ ( n : AUTO_TYPE ) : AUTO_TYPE { n + 1 } ; +} ; + + +######################################## + +class Point { + succ ( n : AUTO_TYPE ) : AUTO_TYPE { n + 1 } ; + translate ( n : AUTO_TYPE , m : AUTO_TYPE ) : SELF_TYPE { self } ; +} ; + +class Main { + step ( p : AUTO_TYPE ) : AUTO_TYPE { p . translate ( 1 , 1 ) } ; + + main ( ) : Object { + let p : AUTO_TYPE <- new Point in { + step ( p ) ; + } + } ; +} ; + + +######################################### + +class A { + fact ( n : AUTO_TYPE ) : AUTO_TYPE { + if ( n < 0 ) then 1 else n * fact ( n - 1 ) fi + } ; +} ; + + +######################################### + +class A { + ackermann ( m : AUTO_TYPE , n : AUTO_TYPE ) : AUTO_TYPE { + if ( m = 0 ) then n + 1 else + if ( n = 0 ) then ackermann ( m - 1 , 1 ) else + ackermann ( m - 1 , ackermann ( m , n - 1 ) ) + fi + fi + } ; +} ; + + +######################################## + +class A { + f ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { + if ( a = 1 ) then b else + g ( a + 1 , b / 2 ) + fi + } ; + g ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { + if ( b = 1 ) then a else + f ( a / 2 , b + 1 ) + fi + } ; +} ; + + + From 73f6900307828b7ebe0734a33af8e60a7495148e Mon Sep 17 00:00:00 2001 From: kikeXD Date: Mon, 30 Nov 2020 18:36:27 -0500 Subject: [PATCH 057/138] refs #11 - Completing pipeline in the UI --- main.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 517713d9c..0a12e6563 100644 --- a/main.py +++ b/main.py @@ -124,11 +124,42 @@ def main(G): st.text('Context:') st.text(context) + st.subheader('Building types') + builder = TypeBuilder(context, errors) + builder.visit(ast) + manager = builder.manager + for e in errors: + st.error(e) + st.text('Context:') + st.text(context) + + st.subheader('Checking types') + checker = TypeChecker(context, manager, []) + scope = checker.visit(ast) + + st.subheader('Infering types') + temp_errors = [] + inferencer = TypeInferencer(context, manager, temp_errors) + inferencer.visit(ast, scope) + for e in temp_errors: + st.error(e) + + st.subheader('Las check') + errors.extend(temp_errors) + checker = TypeChecker(context, manager, errors) + checker.visit(ast) + for e in errors: + st.error(e) + + formatter = FormatVisitor() + tree = formatter.visit(ast) + st.text(tree) + except Exception as e: st.error(f'Unexpected error!!! You probably did something wrong :wink:') if __name__ == '__main__': - main(G) - # ast = run_pipeline(G, text) \ No newline at end of file + # main(G) + ast = run_pipeline(G, text) \ No newline at end of file From 7a7e62e2e3af3dbe8bdcb8924fd07417d3898e93 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 18:36:46 -0500 Subject: [PATCH 058/138] refs #20 - Readme file --- README.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 132cbb39d..47640ea03 100644 --- a/README.md +++ b/README.md @@ -1 +1,101 @@ -# TypeInferencer \ No newline at end of file +# TypeInferencer +El objetivo de este proyecto es la implementación de un intérprete de COOL que permita la inferencia de tipos a partir del uso de AUTO_TYPE. Para el desarrollo del mismo se utilizaron como base los contenidos vistos en Clase Práctica durante el curso de Compilación. + + + +### Requerimientos 📋 +El proyecto fue desarrollado haciendo uso de: + +- `python v-3.7.2` +- `streamlit v-0.56.0` + +Se necesita también algún navegador web instalado como `chrome` o `firefox` + + +### Modo de uso +Para ejecutar el proyecto, solamente abra la consola tal que la dirección raíz sea la del fichero donde se encuentra el mismo y ejecute la siguiente línea: + +``` +streamlit run main.py +``` + +Para insertar el texto que representará el código de COOL debe tener en cuenta que todas las palabras deberán estar separadas por espacio, como se muestra en el siguiente ejemplo: + +``` +class Point { + x : AUTO_TYPE ; + y : AUTO_TYPE ; + init ( n : Int , m : Int ) : SELF_TYPE { { + x <- n ; + y <- m ; + } } ; +} ; +``` + +El lexer del proyecto separa el texto de entrada por los espacios para generar los tokens; en caso de que no reciba el texto correctamente espaciado, no podrá parsearlo satisfactoriamente. Note que este comportamiento también se aplica a las constantes de tipo String: deben encontrarse cerradas por doble comilla y no deben existir espacios entre las palabras que la conformen; puede sustituir los espacios por guiones. + +Si ocurre algún error durante el parsing, debido a que no se tuvieron en cuenta los espacios o las reglas de sintaxis de COOL, este será reportado y no se llevará a cabo ningún análisis posterior. De lo contrario, se mostrarán los errores semánticos, si los hay, y se mostrará el árbol resultante del análisis donde todos los AUTO_TYPES habrán sido reemplazados por el tipo estático inferido. + +Si un AUTO_TYPE es usado de forma incorrecta, este no se verá sustituido en la salida del programa; no obstante el error sí es reportado. + +### Pipeline +Una vez terminada la fase de *parsing* se llevan a cabo los siguientes pasos: + +- Recolección de tipos +- Construcción de tipos +- Chequeo / Inferencia de tipos + +A continuación se explicará el funcionamiento de cada uno: + +#### Recolección de tipos +Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes pasos: + +- Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : Int, String, Bool, IO, Object; incluyendo la definición de sus métodos. Además se añaden como tipos SELF_TYPE, AUTO_TYPE. +- Recorrido por las declaraciones hechas en el programa colectando los tipos creados. +- Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna Object como padre. +- Chequeo de herencia cíclica. En caso de detectar algún ciclo en la jerarquía de tipos, se reporta el error, y a la clase por la cual hubo problema se le asigna Object como padre, para continuar el análisis. +- Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. + +#### Construcción de tipos +La construcción de tipos se desarrolla empleando la clase Type Builder. Esta se encarga de visitar los *features* de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. + +Además se encarga de chequear la existencia del tipo Main con su método main correspondiente, como es requerido en COOL. + +En esta clase también se hace uso de la clase Inferencer Manager que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO_TYPE se le asigna un *id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. + +#### Chequeo e Inferencia de tipos +En primer lugar se utiliza la clase Type Checker para validar el correcto uso de los tipos definidos. Toma la instancia de clase Inferencer Manager utilizada en el Type Builder para continuar la asignación de id a otros elementos en el código que también pueden estar definidos como AUTO_TYPE, como es el caso de las variables definidas en la expresión Let. Las variables definidas en el Scope se encargarán de guardar el id asignado; en caso de que no se les haya asignado ninguno, el id será *None*. + +La instancia de Scope creada en el Type Checker, así como la de Inferencer Manager se pasarán al Type Inferencer para realizar la inferencia de tipos. + +Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El id asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. + +Sea una variable con id = i, que está definida como AUTO_TYPE y sea A el tipo estático que se ha de inferir: + +- `conforms_to[i]` guarda una lista con los tipos a los que debe conformarse A; note que esta lista contiene al menos al tipo Object. El hecho de que A se conforme a estos tipos, implica que todos ellos deben encontrarse en el camino de él a Object en el árbol de jerarquía de tipos. En caso contrario se puede decir que hubo algún error en la utilización del AUTO_TYPE para esta variable. Sea B el tipo más lejano a Object de los que aparecen en la lista. +- `conformed_by[i]` almacena una lista con los tipos que deben conformarse a A. Luego el menor ancestro común (*LCA - Lowest Common Ancestor*) de dichos tipos deberá conformarse a A. Note que este siempre existirá, pues en caso peor será Object, que es la raíz del árbol de tipos. Sea C el LCA de los tipos guardados. Note que si la lista está vacía, (que puede suceder) C será *None*. +- Como C se conforma a A y A se conforma B, tiene que ocurrir que C se conforma a B. En caso contrario, se reporta un uso incorrecto de AUTO_TYPE para esa variable. Todos los tipos en el camino entre B y C son válidos para inferir A; pues cumplen con todas las restricciones que impone el programa. En nuestro caso se elige C, que es el tipo más restringido, para la inferencia. En caso de que C sea *None* se toma B como tipo de inferencia. +- `infered_type[i]` guardará el tipo inferido una vez realizado el procedimiento anterior; mientra tanto su valor es *None*. + +La clase Inferencer Manager además, está equipada con métodos para actualizar las listas dado un id, y para realizar la inferencia dados los tipos almacenados. + +El Type Inferencer por su parte, realizará un método de punto fijo para llevar a cabo la inferencia: + +1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. +2. Infiere todos los tipos que pueda con la información recogida. +3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. + - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último rerrido asignando tipo Object a todos los AUTO_TYPES que no pudieron ser inferidos. + +**Nota:** Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista *conforms_to* contiene a otro tipo distinto de Object o su lista *conformed_by* contiene al menos un tipo. + +Por último se realiza un nuevo recorrido del AST con el Type Checker para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los AUTO_TYPES sustituidos por el tipo inferido. + + +## Authors ✒️ + +* **Carmen Irene Cabrera Rodríguez** - [cicr99](https://github.com/cicr99) +* **Enrique Martínez González** - [kikeXD](https://github.com/kikeXD) + +## License + +This project is under the License (MIT License) - see the file [LICENSE.md](LICENSE.md) for details. \ No newline at end of file From 9137ff38c4cbb4a7866858a76919701cbc607e01 Mon Sep 17 00:00:00 2001 From: kikeXD Date: Mon, 30 Nov 2020 18:38:04 -0500 Subject: [PATCH 059/138] refs #11 - uncomenting main call --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 0a12e6563..5c6bcab2b 100644 --- a/main.py +++ b/main.py @@ -161,5 +161,5 @@ def main(G): if __name__ == '__main__': - # main(G) - ast = run_pipeline(G, text) \ No newline at end of file + main(G) + # ast = run_pipeline(G, text) \ No newline at end of file From 18f426f8a52d4d2b489421bab639d17516823416 Mon Sep 17 00:00:00 2001 From: kikeXD Date: Mon, 30 Nov 2020 18:48:33 -0500 Subject: [PATCH 060/138] refs #11 - Showing only errors and final tree on UI --- main.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/main.py b/main.py index 5c6bcab2b..ab12d86d4 100644 --- a/main.py +++ b/main.py @@ -97,54 +97,54 @@ def main(G): try: st.title('Results:') - st.subheader('Tokens') + # st.subheader('Tokens') tokens = list(tokenize_text(text)) p_tokens = pprint_tokens(tokens, get=True) - st.text(p_tokens) + # st.text(p_tokens) - st.subheader('Parse') + # st.subheader('Parse') parser = LR1Parser(G) parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) p_parse = '\n'.join(repr(x) for x in parse) - st.text(p_parse) + # st.text(p_parse) - st.subheader('AST') + # st.subheader('AST') ast = evaluate_reverse_parse(parse, operations, tokens) formatter = FormatVisitor() tree = formatter.visit(ast) - st.text(tree) + # st.text(tree) - st.subheader('Collecting types') + # st.subheader('Collecting types') errors = [] collector = TypeCollector(errors) collector.visit(ast) context = collector.context - for e in errors: - st.error(e) - st.text('Context:') - st.text(context) + # for e in errors: + # st.error(e) + # st.text('Context:') + # st.text(context) - st.subheader('Building types') + # st.subheader('Building types') builder = TypeBuilder(context, errors) builder.visit(ast) manager = builder.manager - for e in errors: - st.error(e) - st.text('Context:') - st.text(context) + # for e in errors: + # st.error(e) + # st.text('Context:') + # st.text(context) - st.subheader('Checking types') + # st.subheader('Checking types') checker = TypeChecker(context, manager, []) scope = checker.visit(ast) - st.subheader('Infering types') + # st.subheader('Infering types') temp_errors = [] inferencer = TypeInferencer(context, manager, temp_errors) inferencer.visit(ast, scope) - for e in temp_errors: - st.error(e) + # for e in temp_errors: + # st.error(e) - st.subheader('Las check') + # st.subheader('Last check') errors.extend(temp_errors) checker = TypeChecker(context, manager, errors) checker.visit(ast) From 1670efffd2a4eb8a85f3e8289f416e7491c9630f Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 19:46:10 -0500 Subject: [PATCH 061/138] Modifying Read_me file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 47640ea03..fa2324488 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ La clase Inferencer Manager además, está equipada con métodos para actualizar El Type Inferencer por su parte, realizará un método de punto fijo para llevar a cabo la inferencia: -1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. +1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un *ExpressionNode*, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el *manager*. 2. Infiere todos los tipos que pueda con la información recogida. 3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último rerrido asignando tipo Object a todos los AUTO_TYPES que no pudieron ser inferidos. From 4ff5eef14e297bd9398f6405a90bd20ced65c469 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 19:47:07 -0500 Subject: [PATCH 062/138] Adding a piece of the pipeline that went missing --- main.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.py b/main.py index 9eb00e4d0..775da4045 100644 --- a/main.py +++ b/main.py @@ -95,6 +95,11 @@ def main(G): if text: try: + tokens = list(tokenize_text(text)) + parser = LR1Parser(G) + parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) + ast = evaluate_reverse_parse(parse, operations, tokens) + st.title('Results:') errors = [] From 26c73591dcfef825f3bd10156f6390543629aae8 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 20:16:54 -0500 Subject: [PATCH 063/138] Change in method conforms_to in Type class One of the conditions is self == other. This checks references; but what is necessary is only the name of the types are the same --- cmp/semantic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmp/semantic.py b/cmp/semantic.py index e4a1e1836..e4c9eaa45 100644 --- a/cmp/semantic.py +++ b/cmp/semantic.py @@ -120,7 +120,7 @@ def update_method_param(self, method_name, param_type, param_idx): break def conforms_to(self, other): - return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) + return other.bypass() or self.name == other.name or self.parent is not None and self.parent.conforms_to(other) def bypass(self): return False @@ -144,8 +144,8 @@ def __str__(self): def __repr__(self): return str(self) - # def __eq__(self, other): - # return self.conforms_to(other) and other.conforms_to(self) + def __eq__(self, other): + return self.conforms_to(other) and other.conforms_to(self) class ErrorType(Type): def __init__(self): From feb00086e34c97527561eb4406ea81dc3bcfbab7 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 20:50:50 -0500 Subject: [PATCH 064/138] Remove check_conformance from TypeInferencer The errors that were caught thanks to check conformance in the Type Inferencer are also caught in the Type Checker, so the error appeared twice --- cmp/type_inferencer.py | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/cmp/type_inferencer.py b/cmp/type_inferencer.py index 1779efadc..59a5d5fa9 100644 --- a/cmp/type_inferencer.py +++ b/cmp/type_inferencer.py @@ -7,7 +7,6 @@ from cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode -TYPE_CONFORMANCE = 'Type "%s" can not conform to type "%s"' AUTOTYPE_ERROR = 'Incorrect use of AUTO_TYPE' class TypeInferencer: @@ -361,19 +360,11 @@ def visit(self, node, scope, types): @visitor.when(ArithmeticNode) def visit(self, node, scope, types): self.check_expr(node, scope) - - # Check int type conforms to all the types in types - self.check_conformance(types, self.int_type) - return self.int_type, [self.int_type] @visitor.when(ComparisonNode) def visit(self, node, scope, types): self.check_expr(node, scope) - - # Check bool type conforms to all the types in types - self.check_conformance(types, self.bool_type) - return self.bool_type, [self.bool_type] @visitor.when(EqualNode) @@ -394,44 +385,33 @@ def check_equal(typex): elif check_equal(right): self.visit(node.left, scope, [right]) - self.check_conformance(types, self.bool_type) - return self.bool_type, [self.bool_type] @visitor.when(VoidNode) def visit(self, node, scope, types): self.visit(node.expr, scope, []) - self.check_conformance(types, self.bool_type) - return self.bool_type, [self.bool_type] @visitor.when(NotNode) def visit(self, node, scope, types): self.visit(node.expr, scope, [self.bool_type]) - self.check_conformance(types, self.bool_type) - return self.bool_type, [self.bool_type] @visitor.when(NegNode) def visit(self, node, scope, types): self.visit(node.expr, scope, [self.int_type]) - self.check_conformance(types, self.int_type) - return self.int_type, [self.int_type] @visitor.when(ConstantNumNode) def visit(self, node, scope, types): - self.check_conformance(types, self.int_type) return self.int_type, [self.int_type] @visitor.when(ConstantBoolNode) def visit(self, node, scope, types): - self.check_conformance(types, self.bool_type) return self.bool_type, [self.bool_type] @visitor.when(ConstantStringNode) def visit(self, node, scope, types): - self.check_conformance(types, self.string_type) return self.string_type, [self.string_type] @visitor.when(VariableNode) @@ -448,7 +428,6 @@ def visit(self, node, scope, types): self.manager.upd_conforms_to(var.idx, types) conformed_by.extend(self.manager.conformed_by[var.idx]) else: - self.check_conformance(types, var.type) conformed_by.append(var.type) return var.type, conformed_by @@ -461,18 +440,10 @@ def visit(self, node, scope, types): typex = SelfType(self.current_type) except SemanticError: typex = ErrorType() - - self.check_conformance(types, typex) return typex, [typex] - - + def check_expr(self, node, scope): self.visit(node.left, scope, [self.int_type]) self.visit(node.right, scope, [self.int_type]) - - def check_conformance(self, types, typex): - for item in types: - if not typex.conforms_to(item): - self.errors.append(TYPE_CONFORMANCE %(typex.name, item.name)) From d19908d886e8b48b24ff51e0f51da27b88b0f619 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 30 Nov 2020 22:31:46 -0500 Subject: [PATCH 065/138] Fixing bug in Case Node in Type Inferencer --- cmp/type_inferencer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmp/type_inferencer.py b/cmp/type_inferencer.py index 59a5d5fa9..0fdca7913 100644 --- a/cmp/type_inferencer.py +++ b/cmp/type_inferencer.py @@ -246,14 +246,16 @@ def visit(self, node, scope, types): expr_types = [] for i, (branch, child_scope) in enumerate(zip(node.branch_list, nscope.children)): branch_name, branch_type, expr = branch + var = child_scope.find_variable(branch_name) + branch_type = var.type if isinstance(branch_type, AutoType): inf_type = self.manager.infered_type[node.branch_idx[i]] if inf_type is not None: if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) else: - node.branch_list[i][1] = inf_type.name - scope.update_variable(branch_name, inf_type) + node.branch_list[i] = (branch_name, inf_type.name, expr) + child_scope.update_variable(branch_name, inf_type) self.scope_children_id = 0 From e729aedaad326a9659d22a6623723fe6bb53068a Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera <49727740+cicr99@users.noreply.github.com> Date: Mon, 30 Nov 2020 23:11:38 -0500 Subject: [PATCH 066/138] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fa2324488..2047051fa 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Se necesita también algún navegador web instalado como `chrome` o `firefox` ### Modo de uso -Para ejecutar el proyecto, solamente abra la consola tal que la dirección raíz sea la del fichero donde se encuentra el mismo y ejecute la siguiente línea: +Para ejecutar solamente abra la consola tal que la dirección raíz sea la del fichero donde se encuentra el proyecto y ejecute la siguiente línea: ``` streamlit run main.py @@ -51,7 +51,7 @@ A continuación se explicará el funcionamiento de cada uno: Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes pasos: - Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : Int, String, Bool, IO, Object; incluyendo la definición de sus métodos. Además se añaden como tipos SELF_TYPE, AUTO_TYPE. -- Recorrido por las declaraciones hechas en el programa colectando los tipos creados. +- Recorrido por las declaraciones hechas en el programa recolectando los tipos creados. - Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna Object como padre. - Chequeo de herencia cíclica. En caso de detectar algún ciclo en la jerarquía de tipos, se reporta el error, y a la clase por la cual hubo problema se le asigna Object como padre, para continuar el análisis. - Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. @@ -79,23 +79,23 @@ Sea una variable con id = i, que está definida como AUTO_TYPE y sea A el tipo e La clase Inferencer Manager además, está equipada con métodos para actualizar las listas dado un id, y para realizar la inferencia dados los tipos almacenados. -El Type Inferencer por su parte, realizará un método de punto fijo para llevar a cabo la inferencia: +El Type Inferencer por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: 1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un *ExpressionNode*, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el *manager*. 2. Infiere todos los tipos que pueda con la información recogida. 3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último rerrido asignando tipo Object a todos los AUTO_TYPES que no pudieron ser inferidos. -**Nota:** Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista *conforms_to* contiene a otro tipo distinto de Object o su lista *conformed_by* contiene al menos un tipo. +> Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista *conforms_to* contiene a otro tipo distinto de Object o su lista *conformed_by* contiene al menos un tipo. Por último se realiza un nuevo recorrido del AST con el Type Checker para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los AUTO_TYPES sustituidos por el tipo inferido. -## Authors ✒️ +## Autores ✒️ * **Carmen Irene Cabrera Rodríguez** - [cicr99](https://github.com/cicr99) * **Enrique Martínez González** - [kikeXD](https://github.com/kikeXD) -## License +## Licencia -This project is under the License (MIT License) - see the file [LICENSE.md](LICENSE.md) for details. \ No newline at end of file +Este proyecto se encuentra bajo la Licencia (MIT License) - ver el archivo [LICENSE.md](LICENSE.md) para más detalles. From f14682b490a2a9338e8f0767732513d602f1beed Mon Sep 17 00:00:00 2001 From: kikeXD Date: Wed, 9 Dec 2020 09:59:00 -0500 Subject: [PATCH 067/138] refs#1 - Adding lexer code of 1st project --- cmp/lexer.py | 138 +++++- cmp/nfa_dfa.py | 107 +++++ cmp/regex.py | 92 ++++ cmp/tools2.py | 1244 ++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 50 +- 5 files changed, 1605 insertions(+), 26 deletions(-) create mode 100644 cmp/nfa_dfa.py create mode 100644 cmp/regex.py create mode 100644 cmp/tools2.py diff --git a/cmp/lexer.py b/cmp/lexer.py index 33767400d..03ed4bd1a 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -3,18 +3,18 @@ fixed_tokens = { t.Name: Token(t.Name, t) for t in G.terminals if t not in { idx, num, stringx, boolx }} -@tokenizer(G, fixed_tokens) -def tokenize_text(token): - lex = token.lex - try: - float(lex) - return token.transform_to(num) - except ValueError: - if lex[0] == '"' and lex[-1] == '"': - return token.transform_to(stringx) - if lex == 'true' or lex == 'false': - return token.transform_to(boolx) - return token.transform_to(idx) +# @tokenizer(G, fixed_tokens) +# def tokenize_text(token): +# lex = token.lex +# try: +# float(lex) +# return token.transform_to(num) +# except ValueError: +# if lex[0] == '"' and lex[-1] == '"': +# return token.transform_to(stringx) +# if lex == 'true' or lex == 'false': +# return token.transform_to(boolx) +# return token.transform_to(idx) def pprint_tokens(tokens, get=False): indent = 0 @@ -37,3 +37,117 @@ def pprint_tokens(tokens, get=False): return result else: print(' '.join([str(t.token_type) for t in pending])) + + +from cmp.utils import Token +from cmp.regex import Regex +from cmp.automata import State + + +class Lexer: + def __init__(self, table, eof): + self.eof = eof + self.regexs = self._build_regexs(table) + self.automaton = self._build_automaton() + + def _build_regexs(self, table): + regexs = [] + for n, (token_type, regex) in enumerate(table): + dfa = Regex(regex) + automaton_list = State.from_nfa(dfa.automaton, 'texto random pra que haga lo que quiero') + for i in automaton_list[1]: + if(i.final): + i.tag = (n, token_type) + regexs.append(automaton_list[0]) + + return regexs + + def _build_automaton(self): + start = State('start') + automatons = self.regexs + for i in automatons: + start.add_epsilon_transition(i) + final = start.to_deterministic() + return final + + + def _walk(self, string): + state = self.automaton + final = state if state.final else None + final_lex = lex = '' + + for symbol in string: + try: + state = state[symbol][0] + lex += symbol + if(state.final): + final = state + final_lex = lex + except TypeError: + # print(symbol, lex, string, state) + break + + return final, final_lex + + def _tokenize(self, text): + pos = 0 + while(len(text) > 0): + temp = self._walk(text) + # print(temp) + + if(temp[1] == ''): + assert 1, 0 + + pos = len(temp[1]) + text = text[pos:len(text)] + mi = 9999 + final = None + for i in temp[0].state: + if i.final: + if i.tag[0] < mi: + final = i + mi = i.tag[0] + yield temp[1], final.tag[1] + + yield '$', self.eof + + def __call__(self, text): + return [ Token(lex, ttype) for lex, ttype in self._tokenize(text) ] + + +nonzero_digits = '|'.join(str(n) for n in range(1,10)) +alp = [chr(n) for n in range(ord('a'),ord('z') + 1)] +alp.extend([chr(n) for n in range(ord('A'),ord('Z') + 1)]) +letters = '|'.join(alp) + +alphabet_before = [chr(n) for n in range(32, ord('~') + 1) if n != 34 and n != 124] +alphabet = '|'.join(alphabet_before) + +def eliminate_regex_conflict(text): + result = '' + for i in text: + if i in {'(', ')', '*', '\\'}: + result += '\\' + result += i + return result + + +def tokenize_text(text): + lexer = Lexer( + [(t, eliminate_regex_conflict(t.Name)) for t in G.terminals if t not in { idx, num, stringx, boolx }] + + [(boolx, 'true|false'), + (num, f'0|({nonzero_digits})(0|{nonzero_digits})*'), + ('salto', '\n'), + ('space', ' *'), + (idx, f'({letters}|_)(_|{letters}|0|{nonzero_digits})*'), + (stringx, f'"({eliminate_regex_conflict(alphabet)})*"')], + G.EOF) + + tokens = lexer(text) + # print('tokens', tokens) + tokens_filtrado = [] + for i in tokens: + if i.token_type != 'salto' and i.token_type != 'space': + tokens_filtrado.append(i) + # print('tokens filtrados', tokens_filtrado) + return tokens_filtrado \ No newline at end of file diff --git a/cmp/nfa_dfa.py b/cmp/nfa_dfa.py new file mode 100644 index 000000000..645aa2732 --- /dev/null +++ b/cmp/nfa_dfa.py @@ -0,0 +1,107 @@ +import pydot + +class NFA: + def __init__(self, states, finals, transitions, start=0): + self.states = states + self.start = start + self.finals = set(finals) + self.map = transitions + self.vocabulary = set() + self.transitions = { state: {} for state in range(states) } + + for (origin, symbol), destinations in transitions.items(): + assert hasattr(destinations, '__iter__'), 'Invalid collection of states' + self.transitions[origin][symbol] = destinations + self.vocabulary.add(symbol) + + self.vocabulary.discard('') + + def epsilon_transitions(self, state): + assert state in self.transitions, 'Invalid state' + try: + return self.transitions[state][''] + except KeyError: + return () + + def graph(self): + G = pydot.Dot(rankdir='LR', margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + for (start, tran), destinations in self.map.items(): + tran = 'ε' if tran == '' else tran + G.add_node(pydot.Node(start, shape='circle', style='bold' if start in self.finals else '')) + for end in destinations: + G.add_node(pydot.Node(end, shape='circle', style='bold' if end in self.finals else '')) + G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) + + G.add_edge(pydot.Edge('start', self.start, label='', style='dashed')) + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + + def _repr_png_(self): + try: + return self.graph() + except: + pass + + + + + +class DFA(NFA): + + def graph(self): + G = pydot.Dot(rankdir='LR', margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + for (start, tran), destinations in self.map.items(): + tran = 'ε' if tran == '' else tran + G.add_node(pydot.Node(start, shape='circle', style='bold' if start in self.finals else '')) + for end in destinations: + G.add_node(pydot.Node(end, shape='circle', style='bold' if end in self.finals else '')) + G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) + + G.add_edge(pydot.Edge('start', self.start, label='', style='dashed')) + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + + def _repr_png_(self): + try: + return self.graph() + except: + pass + + def __init__(self, states, finals, transitions, start=0): + assert all(isinstance(value, int) for value in transitions.values()) + assert all(len(symbol) > 0 for origin, symbol in transitions) + + transitions = { key: [value] for key, value in transitions.items() } + NFA.__init__(self, states, finals, transitions, start) + self.current = start + + def _move(self, symbol): + try: + self.current = self.transitions[self.current][symbol][0] + return True + except KeyError: + return False + + def _reset(self): + self.current = self.start + + def recognize(self, string): + self._reset() + for symbol in string: + if not self._move(symbol): + return False + return self.current in self.finals \ No newline at end of file diff --git a/cmp/regex.py b/cmp/regex.py new file mode 100644 index 000000000..fc5e40efd --- /dev/null +++ b/cmp/regex.py @@ -0,0 +1,92 @@ +from cmp.pycompiler import Grammar +from cmp.utils import Token +from cmp.tools2 import evaluate_parse +from cmp.tools2 import metodo_predictivo_no_recursivo +from cmp.tools2 import nfa_to_dfa +from cmp.tools2 import automata_union, automata_concatenation, automata_closure, automata_minimization +from cmp.tools2 import get_printer +from cmp.tools2 import Node, AtomicNode, UnaryNode, BinaryNode, EpsilonNode, SymbolNode, ClosureNode, UnionNode, ConcatNode +from pprint import pprint as pp +import pydot + +class Regex: + def __init__(self, regular_exp): + + self.regular_exp = regular_exp + self.tokens = regex_tokenizer(regular_exp, G, False) + self.left_parse = parser(self.tokens) + self.ast = evaluate_parse(self.left_parse, self.tokens) + self.nfa = self.ast.evaluate() + self.dfa = nfa_to_dfa(self.nfa) + self.mini = automata_minimization(self.dfa) + self.automaton = self.mini + + def __call__(self, string): + return self.mini.recognize(string) + + def __repr__(self): + return 'Regex: ' + self.regular_exp + + +G = Grammar() + +E = G.NonTerminal('E', True) +T, F, A, X, Y, Z = G.NonTerminals('T F A X Y Z') +pipe, star, opar, cpar, symbol, epsilon = G.Terminals('| * ( ) symbol ε') + +# > PRODUCTIONS??? +E %= T + X, lambda h, s: s[2], None, lambda h, s: s[1] + +X %= pipe + T + X, lambda h, s: s[3], None, None, lambda h, s: UnionNode(h[0], s[2]) +X %= G.Epsilon, lambda h, s: h[0] + +T %= F + Y, lambda h, s: s[2], None, lambda h, s: s[1] + +Y %= F + Y, lambda h, s: s[2], None, lambda h, s: ConcatNode(h[0], s[1]) +Y %= G.Epsilon, lambda h, s: h[0] + +F %= A + Z, lambda h, s: s[2], None, lambda h, s: s[1] + +Z %= star + Z, lambda h, s: s[2], None, lambda h, s: ClosureNode(h[0]) +Z %= G.Epsilon, lambda h, s: h[0] + +A %= symbol, lambda h, s: SymbolNode(s[1]) +A %= opar + E + cpar, lambda h, s: s[2], None, None, None +A %= epsilon, lambda h, s: EpsilonNode(s[1]) + + + +def regex_tokenizer(text, G, skip_whitespaces=True): + tokens = [] + jump = False + + # print(text) + + for char in text: + if skip_whitespaces and char.isspace(): + continue + elif(char == '\\' and not jump): + jump = True + elif(char == '*' and not jump): + tokens.append(Token('*', star)) + elif(char == '(' and not jump): + tokens.append(Token('(', opar)) + elif(char == ')' and not jump): + tokens.append(Token(')', cpar)) + elif(char == '|' and not jump): + tokens.append(Token('|', pipe)) + elif(char == 'ε' and not jump): + tokens.append(Token('ε', epsilon)) + else: + tokens.append(Token(char, symbol)) + jump = False + + tokens.append(Token('$', G.EOF)) + return tokens + + +parser = metodo_predictivo_no_recursivo(G) + + + +printer = get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode) diff --git a/cmp/tools2.py b/cmp/tools2.py new file mode 100644 index 000000000..ecdd35434 --- /dev/null +++ b/cmp/tools2.py @@ -0,0 +1,1244 @@ +from cmp.utils import * +from cmp.nfa_dfa import * +from cmp.automata import * +from cmp.pycompiler import * +from itertools import islice +import 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() + +class EpsilonNode(AtomicNode): + def evaluate(self): + return NFA(1, [0], {}) + +class SymbolNode(AtomicNode): + def evaluate(self): + s = self.lex + return NFA(2, [1], {(0, s) : [1],}) + +class ClosureNode(UnaryNode): + @staticmethod + def operate(value): + return automata_closure(value) + +class UnionNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return automata_union(lvalue, rvalue) + +class ConcatNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return automata_concatenation(lvalue, rvalue) + + +#this method is used for updating ll1 table as well as action-goto table +def upd_table(table, head, symbol, production): + if not head in table: + table[head] = {} + if not symbol in table[head]: + table[head][symbol] = [] + if production not in table[head][symbol]: + table[head][symbol].append(production) + return (len(table[head][symbol]) <= 1) + + +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 KeyError: + 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.IsNonTerminal: + try: + beta_f = local_firsts[alpha, i] + except KeyError: + beta_f = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None)) + change |= follows[Y].update(beta_f) + if beta_f.contains_epsilon: + change |= follows[Y].update(follow_X) + + return follows + +def build_parsing_table(G, firsts, follows): + M = {} + ok = True + + for production in G.Productions: + X = production.Left + alpha = production.Right + + for t in firsts[alpha]: + ok &= upd_table(M, X, t, production) + + if firsts[alpha].contains_epsilon: + for t in follows[X]: + ok &= upd_table(M, X, t, production) + + return M, ok + +def deprecated_metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): + + if M is None: + if firsts is None: + firsts = compute_firsts(G) + if follows is None: + follows = compute_follows(G, firsts) + M, _ = build_parsing_table(G, firsts, follows) + + def parser(w): + + stack = [G.EOF, G.startSymbol] + cursor = 0 + output = [] + + while True: + top = stack.pop() + a = w[cursor] + + if top.IsEpsilon: + pass + elif top.IsTerminal: + assert top == a + if top == G.EOF: + break; + cursor += 1 + else: + production = M[top][a][0] + output.append(production) + production = list(production.Right) + stack.extend(production[::-1]) + + return output + + return parser + +def metodo_predictivo_no_recursivo(G, M = None): + parser = deprecated_metodo_predictivo_no_recursivo(G, M) + def updated(tokens): + return parser([t.token_type for t in tokens]) + return updated + +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)) + +def evaluate_parse(left_parse, tokens): + if not left_parse or not tokens: + return + + left_parse = iter(left_parse) + tokens = iter(tokens) + result = evaluate(next(left_parse), left_parse, tokens) + + assert isinstance(next(tokens).token_type, EOF) + return result + +def evaluate(production, left_parse, tokens, inherited_value=None): + head, body = production + attributes = production.attributes + + synteticed = [None] * (len(body) + 1) + inherited = [None] * (len(body) + 1) + inherited[0] = inherited_value + + for i, symbol in enumerate(body, 1): + if symbol.IsTerminal: + assert inherited[i] is None + synteticed[i] = next(tokens).lex + else: + next_production = next(left_parse) + assert symbol == next_production.Left + attr = attributes[i] + if attr is not None: + inherited[i] = attr(inherited, synteticed) + synteticed[i] = evaluate(next_production, left_parse, tokens, inherited[i]) + + attr = attributes[0] + if attr is None: + return None + return attr(inherited, synteticed) + +def move(automaton, states, symbol): + moves = set() + for state in states: + try: + for item in automaton.transitions[state][symbol]: + # aux = automaton.transitions[state][symbol] + moves.add(item) + except KeyError: + pass + return moves + +def epsilon_closure(automaton, states): + pending = [ s for s in states ] # equivalente a list(states) pero me gusta así :p + closure = { s for s in states } # equivalente a set(states) pero me gusta así :p + + while pending: + state = pending.pop() + + l = move(automaton, [state], '') + for i in l: + if(i in closure): + pass + else: + closure.add(i) + pending.append(i) + + return ContainerSet(*closure) + +def nfa_to_dfa(automaton): + transitions = {} + + start = epsilon_closure(automaton, [automaton.start]) + start.id = 0 + start.is_final = any(s in automaton.finals for s in start) + states = [ start ] + + pending = [ start ] + while pending: + state = pending.pop() + + for symbol in automaton.vocabulary: + new_state = move(automaton, state, symbol) + new_state = epsilon_closure(automaton, new_state) + + if not new_state: + continue + + for s in states: + if(s == new_state): + new_state = s + break + else: + new_state.id = len(states) + new_state.is_final = any(s in automaton.finals for s in new_state) + pending.append(new_state) + states.append(new_state) + + try: + transitions[state.id, symbol] + assert False, 'Invalid DFA!!!' + except KeyError: + transitions[state.id, symbol] = new_state.id + + finals = [ state.id for state in states if state.is_final ] + dfa = DFA(len(states), finals, transitions) + return dfa + +def automata_union(a1, a2): + transitions = {} + + start = 0 + d1 = 1 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + other = [q + d1 for q in destinations] + transitions[origin + d1, symbol] = other + + for (origin, symbol), destinations in a2.map.items(): + other = [q + d2 for q in destinations] + transitions[origin + d2, symbol] = other + + + transitions[start, ''] = [a1.start + d1, a2.start + d2] + for i in a1.finals: + try: + transitions[i + d1, ''].add(final) + except KeyError: + transitions[i + d1, ''] = [final] + for i in a2.finals: + try: + transitions[i + d2, ''].add(final) + except KeyError: + transitions[i + d2, ''] = [final] + + states = a1.states + a2.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_concatenation(a1, a2): + transitions = {} + + start = 0 + d1 = 0 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + other = [q + d1 for q in destinations] + transitions[origin + d1, symbol] = other + + + for (origin, symbol), destinations in a2.map.items(): + other = [q + d2 for q in destinations] + transitions[origin + d2, symbol] = other + + for i in a1.finals: + try: + transitions[i + d1, ''].add(a2.start + d2) + except KeyError: + transitions[i + d1, ''] = [a2.start + d2] + for i in a2.finals: + try: + transitions[i + d2, ''].append(final) + except KeyError: + transitions[i + d2, ''] = [final] + + states = a1.states + a2.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_closure(a1): + transitions = {} + + start = 0 + d1 = 1 + final = a1.states + d1 + + for (origin, symbol), destinations in a1.map.items(): + other = [q + d1 for q in destinations] + transitions[origin + d1, symbol] = other + + transitions[start, ''] = [final, a1.start + d1] + + for i in a1.finals: + try: + transitions[i + d1, ''].add(final) + except KeyError: + transitions[i + d1, ''] = [final] + + try: + transitions[final, ''].add(start) + except: + transitions[final,''] = [start] + + states = a1.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + +def distinguish_states(group, automaton, partition): + split = {} + vocabulary = tuple(automaton.vocabulary) + + transition = automaton.transitions + + for member in group: + for item in split.keys(): + for symbol in vocabulary: + q1 = None + q2 = None + try: + q1 = partition[transition[item][symbol][0]].representative + except KeyError: + q1 = None + try: + q2 = partition[transition[member.value][symbol][0]].representative + except KeyError: + q2 = None + if q1 != q2: + break + else: + split[item].append(member.value) + break + else: + split[member.value] = [member.value] + + + return [ group for group in split.values()] + +def state_minimization(automaton): + partition = DisjointSet(*range(automaton.states)) + + ## partition = { NON-FINALS | FINALS } + finals = list(automaton.finals) + non_finals = [state for state in range(automaton.states) if not state in automaton.finals] + partition.merge(finals) + partition.merge(non_finals) + + while True: + new_partition = DisjointSet(*range(automaton.states)) + + ## Split each group if needed (use distinguish_states(group, automaton, partition)) + for group in partition.groups: + new_groups = distinguish_states(group, automaton, partition) + for new_group in new_groups: + new_partition.merge(new_group) + + if len(new_partition) == len(partition): + break + + partition = new_partition + + return partition + +def automata_minimization(automaton): + partition = state_minimization(automaton) + + states = [s for s in partition.representatives] + + transitions = {} + for i, state in enumerate(states): + origin = state.value + for symbol, destinations in automaton.transitions[origin].items(): + new_dest = states.index(partition[destinations[0]].representative) + + try: + transitions[i,symbol] + assert False + except KeyError: + transitions[i, symbol] = new_dest + pass + + start = states.index(partition[automaton.start].representative) + finals = set([i for i in range(len(states)) if states[i].value in automaton.finals]) + + return DFA(len(states), finals, transitions, start) + + +#methods to simplify the grammar +def remove_non_terminating_productions(G): + terminals = set(G.terminals) + + change = True + while change: + change = False + for prod in G.Productions: + if prod.Left not in terminals and all([s in terminals for s in prod.Right]): + terminals.add(prod.Left) + change = True + + nt = G.nonTerminals.copy() + G.nonTerminals = [] + G.Productions = [] + + for s in nt: + if s in terminals: + G.nonTerminals.append(s) + productions = s.productions.copy() + s.productions = [] + for prod in productions: + if all([item in terminals for item in prod.Right]): + G.Productions.append(prod) + s.productions.append(prod) + +def remove_useless_productions(G): + flag = [False] * (len(G.nonTerminals) + len(G.terminals)) + mp = {} + + for i, nt in enumerate(G.nonTerminals): + mp[nt] = i + sz = len(G.nonTerminals) + for i, t in enumerate(G.terminals): + mp[t] = i + sz + + def dfs(S): + flag[mp[S]] = True + for prod in S.productions: + _, right = prod + for symbol in right: + if isinstance(symbol, Terminal): + flag[mp[symbol]] = True + if isinstance(symbol, NonTerminal) and not flag[mp[symbol]]: + dfs(symbol) + + dfs(G.startSymbol) + nt = G.nonTerminals.copy() + G.nonTerminals = [] + G.Productions = [] + + for item in nt: + if flag[mp[item]]: + G.nonTerminals.append(item) + G.Productions.extend(item.productions) + + t = G.terminals.copy() + G.terminals = [] + for item in t: + if flag[mp[item]]: + G.terminals.append(item) + +def remove_null_productions(G): + null_prod = set() + + change = True + while change: + change = False + for prod in G.Productions: + if prod.Left not in null_prod and all([s in null_prod for s in prod.Right]): + null_prod.add(prod.Left) + change = True + if G.startSymbol in null_prod: + return + + def add_prod(head, body, i, prod): + if i == len(body): + if len(prod) > 0: + sentence = G.Epsilon + for s in prod: + sentence += s + head %= sentence + return + + if body[i] not in null_prod: + prod.append(body[i]) + add_prod(head, body, i + 1, prod) + prod.pop() + else: + add_prod(head, body, i + 1, prod) + prod.append(body[i]) + add_prod(head, body, i + 1, prod) + prod.pop() + + G.Productions = [] + for nt in G.nonTerminals: + nt_prod = nt.productions.copy() + nt.productions = [] + for prod in nt_prod: + head, body = prod.Left, prod.Right + if any([s in null_prod for s in body]): + production = [] + add_prod(head, body, 0, production) + elif len(body) > 0: + head %= body + + # if G.startSymbol in null_prod: + # G.startSymbol %= G.Epsilon + +def remove_unit_productions(G): + guf = {nt : set() for nt in G.nonTerminals} + unit_prod = set() + + for prod in G.Productions: + head, body = prod.Left, prod.Right + if len(body) == 1 and isinstance(body[0], NonTerminal): + unit_prod.add(prod) + else: + guf[head].add(body) + + change = True + while change: + change = False + for prod in unit_prod: + head, body = prod.Left, prod.Right + sz = len(guf[head]) + #guf[head].update(guf[body[0]]) + for item in guf[body[0]]: + if not item: + guf[head].add(G.Epsilon) + else: + guf[head].add(item) + change |= (sz < len(guf[head])) + + G.Productions = [] + for nt in G.nonTerminals: + nt.productions = [] + for item in guf[nt]: + nt %= item + + +def remove_common_prefix(G): + unsolved = set([nt for nt in G.nonTerminals]) + + id = 1 + while unsolved: + nonTerminals = G.nonTerminals.copy() + for nt in nonTerminals: + if nt in unsolved: + nt_prod = nt.productions.copy() + nt.productions = [] + flag = set() + + for item in nt_prod: + if item not in flag: + flag.add(item) + if not item.IsEpsilon: + common = set() + symbols = [] + s = item.Right[0] + symbols.append(s) + for p in nt_prod: + if p not in flag and len(p.Right) > 0: + if p.Right[0] == s: + flag.add(p) + common.add(p) + if len(common) == 0: + item.Left %= item.Right + else: + for i in range(1, len(item.Right)): + for c in common: + if i == len(c.Right) or c.Right[i] != item.Right[i]: + break + else: + symbols.append(item.Right[i]) + continue + break + sentence = G.Epsilon + for symbol in symbols: + sentence += symbol + new_nt = G.NonTerminal(nt.Name + '_' + str(id)) + id += 1 + nt %= sentence + new_nt + common.add(item) + for c in common: + sent = G.Epsilon + for i in range(len(symbols), len(c.Right)): + sent += c.Right[i] + new_nt %= sent + unsolved.add(new_nt) + else: + item.Left %= item.Right + + unsolved.remove(nt) + + G.Productions = [] + for nt in G.nonTerminals: + for p in nt.productions: + G.Productions.append(p) + +def remove_immediate_recursion(G): + G.Productions = [] + + non_terminals = G.nonTerminals.copy() + for item in non_terminals: + bad_prod = [Sentence(*prod.Right[1:]) for prod in item.productions if len(prod.Right) > 0 and prod.Right[0] == item] + good_prod = [Sentence(*prod.Right) for prod in item.productions if len(prod.Right) == 0 or prod.Right[0] != item] + + if len(bad_prod) > 0: + nsymbol = G.NonTerminal(item.Name + '_0') + item.productions = [] + + for prod in good_prod: + item %= prod + nsymbol + + for prod in bad_prod: + nsymbol %= prod + nsymbol + + nsymbol %= G.Epsilon + + else: + G.Productions.extend(item.productions) + +def grammar_from_input(input): + terminals, nonTerminals, productions = [], [], [] + + input = input.split('\n') + lines = [l for l in input if l != ''] + + l = lines[0].split() + start_symbol = l[-1] + nonTerminals.append(start_symbol) + + l = lines[1].replace(',', ' ').split() + if len(l) > 4: + for i in range(3, len(l) - 1): + nonTerminals.append(l[i]) + + l = lines[2].replace(',', ' ').split() + if len(l) > 4: + for i in range(3, len(l) - 1): + terminals.append(l[i]) + else: + raise Exception('Invalid sentence') + + lines = lines[3:] + for prod in lines: + right, sentences = prod.split('=') + right, = right.split() + + sentences = sentences.split(';') + # sentences = sentences.split('|') + for s in sentences: + s = s.replace('+', ' ').split() + # s = s.split() + productions.append({'Head': right, 'Body': s}) + + d = dict() + d['NonTerminals'] = nonTerminals + d['Terminals'] = terminals + d['Productions'] = productions + + G = Grammar.from_json(json.dumps(d)) + + if G.startSymbol is None: + for nt in G.nonTerminals: + if nt.Name == start_symbol: + G.startSymbol = nt + break + + return G + +def simplifying_grammar(G): + remove_null_productions(G) + remove_unit_productions(G) + remove_non_terminating_productions(G) + remove_useless_productions(G) + remove_immediate_recursion(G) + remove_common_prefix(G) + + + +def validate_conflict(w, M, G): + stack = [G.EOF, G.startSymbol] + cursor = 0 + + while True: + top = stack.pop() + a = w[cursor] + + if top.IsEpsilon: + pass + elif top.IsTerminal: + assert top == a + if top == G.EOF: + break; + cursor += 1 + else: + if len(M[top][a]) > 1: + return True + production = M[top][a][0] + production = list(production.Right) + stack.extend(production[::-1]) + + return False + +def ll1_conflict(G, M): + queue = [] + + queue.append(([G.startSymbol], [], False)) + while queue: + prod, word, conflict = queue.pop(0) + while prod and isinstance(prod[0], Terminal): + word.append(prod.pop(0)) + + if not prod: + if conflict: + if validate_conflict(word + [G.EOF], M, G): + w = ' '.join([symbol.Name for symbol in word]) + return w + continue + + symbol = prod.pop(0) + for terminal in M[symbol]: + c = conflict or len(M[symbol][terminal]) > 1 + for p in M[symbol][terminal]: + body = list(p.Right) + body.extend(prod) + queue.append((body, word.copy(), c)) + + + +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 + +class SLR1Parser(ShiftReduceParser): + def _build_parsing_table(self): + self.ok = True + G = self.Augmented = self.G.AugmentedGrammar(True) + firsts = compute_firsts(G) + follows = compute_follows(G, firsts) + + self.automaton = build_LR0_automaton(G).to_deterministic(lambda x: "") + 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 + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self.ok &= upd_table(self.action, idx, G.EOF, (SLR1Parser.OK, '')) + else: + for terminal in follows[item.production.Left]: + self.ok &= upd_table(self.action, idx, terminal, (SLR1Parser.REDUCE, item.production)) + else: + symbol = item.NextSymbol + + if symbol.IsTerminal: + self.ok &= upd_table(self.action, idx, symbol, (SLR1Parser.SHIFT, node[symbol.Name][0].idx)) + else: + self.ok &= upd_table(self.goto, idx, symbol, node[symbol.Name][0].idx) + +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) + +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_LALR1_automaton(G): + lr1_automaton = build_LR1_automaton(G) + states = list(lr1_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 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) + + new_states[0].set_formatter(empty_formatter) + return new_states[0] + +class LALR1Parser(ShiftReduceParser): + def _build_parsing_table(self): + self.ok = True + G = self.Augmented = self.G.AugmentedGrammar(True) + + automaton = self.automaton = build_LALR1_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 action_goto_conflict(action, goto): + # (stack, word, conflict, terminal) + queue = [([0], [], False, None)] + + while queue: + stack, word, conflict, terminal = queue.pop(0) + state = stack[-1] + try: + if terminal is not None: + actions = action[state][terminal] + if any([act for act, _ in actions if act == SLR1Parser.OK]) and conflict: + return ' '.join(word) + + conflict |= (len(actions) > 1) + for act, tag in actions: + if act == SLR1Parser.SHIFT: + queue.append((stack + [tag], word + [terminal.Name], conflict, None)) + elif act == SLR1Parser.REDUCE: + s = stack.copy() + if not tag.IsEpsilon: + s = s[:-len(tag.Right)] + data = goto[s[-1]][tag.Left] + c = (len(data) > 1) or conflict + for go in data: + queue.append((s + [go], word.copy(), c, terminal)) + else: + for symbol in action[state]: + queue.append((stack, word.copy(), conflict, symbol)) + except Exception as e: + print(f'FAILURE {e}') + + +# def action_goto_conflict(action, goto): +# # (stack, word, conflict, terminal) +# queue = [([0], '', False, None)] + +# while queue: +# stack, word, conflict, terminal = queue.pop(0) +# state = stack[-1] +# try: +# if terminal is not None: +# actions = action[state][terminal] +# if any([act for act, _ in actions if act == SLR1Parser.OK]) and conflict: +# return word + +# conflict |= (len(actions) > 1) +# for act, tag in actions: +# if act == SLR1Parser.SHIFT: +# queue.append((stack + [tag], word + terminal.Name, conflict, None)) +# elif act == SLR1Parser.REDUCE: +# s = stack.copy() +# if not tag.IsEpsilon: +# s = s[:-len(tag.Right)] +# data = goto[s[-1]][tag.Left] +# c = (len(data) > 1) or conflict +# for go in data: +# queue.append((s + [go], word, c, terminal)) +# else: +# for symbol in action[state]: +# queue.append((stack, word, conflict, symbol)) +# except Exception as e: +# print(f'FAILURE {e}') + + + +def ll1_analysis(G): + firsts = compute_firsts(G) + follows = compute_follows(G, firsts) + + M, ok = build_parsing_table(G, firsts, follows) + + return firsts, follows, M, ok + + + +def derivation_tree(d): + def add_trans(cur, transitions): + for symbol in transitions: + if symbol.IsTerminal: + cur.add_transition('', State(symbol, True)) + else: + s = State(symbol, True) + try: + old[symbol].append(s) + except KeyError: + old[symbol] = [s] + cur.add_transition('', s) + if len(transitions) == 0: + cur.add_transition('', State(transitions, True)) + + p1 = d[0] + old = {} + root = State(p1.Left.Name, True) + add_trans(root, p1.Right) + + for p in d[1:]: + node = old[p.Left].pop() + add_trans(node, p.Right) + + return root + +def parse_string(G, word): + m = {t.Name:t for t in G.terminals} + word = word.split() + w = [m[item] for item in word] + w.append(G.EOF) + return w + +def make_tree_LL1(G, w, M, firsts, follows): + try: + w = parse_string(G, w) + return derivation_tree(deprecated_metodo_predictivo_no_recursivo(G, M, firsts, follows)(w)) + except Exception as e: + return 'String not recognized' + +def make_tree(G, w, parser): + try: + w = parse_string(G, w) + d = parser(w) + d.reverse() + return derivation_tree(d) + + except Exception as e: + return "String not recognized" \ No newline at end of file diff --git a/main.py b/main.py index 775da4045..43be50b0f 100644 --- a/main.py +++ b/main.py @@ -70,18 +70,40 @@ def run_pipeline(G, text): text = ''' -class A { - f ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { - if ( a = 1 ) then b else - g ( a + 1 , b / 2 ) - fi - } ; - g ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { - if ( b = 1 ) then a else - f ( a / 2 , b + 1 ) - fi - } ; -} ; +class Main inherits IO { + number: Int <- 5; + + main () : Object { + testing_fibonacci(number) + }; + + testing_fibonacci(n: Int) : IO {{ + out_string("Iterative Fibonacci : "); + out_int(iterative_fibonacci(5)); + out_string("asdf"); + + out_string("Recursive Fibonacci : "); + out_int(recursive_fibonacci(5)); + out_string("asdfn"); + }}; + + recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { + if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi + }; + + iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { + let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { + while i < n loop + let temp: Int <- n2 in { + n2 <- n2 + n1; + n1 <- temp; + i <- i + 1; + } + pool; + n2; + } + }; +}; ''' def main(G): @@ -149,5 +171,5 @@ def main(G): if __name__ == '__main__': - main(G) - # ast = run_pipeline(G, text) \ No newline at end of file + # main(G) + ast = run_pipeline(G, text) \ No newline at end of file From 8d0c62fce2e35df2d79d77c365fc653a2aecd3d1 Mon Sep 17 00:00:00 2001 From: kikeXD Date: Wed, 9 Dec 2020 11:12:41 -0500 Subject: [PATCH 068/138] refs #1 - Now lexer can take '\' in strings --- cmp/lexer.py | 2 +- main.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmp/lexer.py b/cmp/lexer.py index 03ed4bd1a..4b55138ef 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -120,7 +120,7 @@ def __call__(self, text): alp.extend([chr(n) for n in range(ord('A'),ord('Z') + 1)]) letters = '|'.join(alp) -alphabet_before = [chr(n) for n in range(32, ord('~') + 1) if n != 34 and n != 124] +alphabet_before = [chr(n) for n in range(1, ord('~') + 1) if n != 34 and n != 124] alphabet = '|'.join(alphabet_before) def eliminate_regex_conflict(text): diff --git a/main.py b/main.py index 43be50b0f..4ac7c001b 100644 --- a/main.py +++ b/main.py @@ -80,11 +80,11 @@ class Main inherits IO { testing_fibonacci(n: Int) : IO {{ out_string("Iterative Fibonacci : "); out_int(iterative_fibonacci(5)); - out_string("asdf"); + out_string("\n"); out_string("Recursive Fibonacci : "); out_int(recursive_fibonacci(5)); - out_string("asdfn"); + out_string("\n"); }}; recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { @@ -171,5 +171,5 @@ def main(G): if __name__ == '__main__': - # main(G) - ast = run_pipeline(G, text) \ No newline at end of file + main(G) + # ast = run_pipeline(G, text) \ No newline at end of file From 9c9809eee3451afdeb076bd3558f24f4b70cc359 Mon Sep 17 00:00:00 2001 From: kikeXD Date: Wed, 9 Dec 2020 11:45:00 -0500 Subject: [PATCH 069/138] refs #1 - Now strings can have '|' inside --- cmp/lexer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmp/lexer.py b/cmp/lexer.py index 4b55138ef..4c5e4aa67 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -120,17 +120,17 @@ def __call__(self, text): alp.extend([chr(n) for n in range(ord('A'),ord('Z') + 1)]) letters = '|'.join(alp) -alphabet_before = [chr(n) for n in range(1, ord('~') + 1) if n != 34 and n != 124] -alphabet = '|'.join(alphabet_before) def eliminate_regex_conflict(text): result = '' for i in text: - if i in {'(', ')', '*', '\\'}: + if i in {'(', ')', '*', '\\', '|'}: result += '\\' result += i return result +alphabet_before = [eliminate_regex_conflict(chr(n)) for n in range(1, ord('~') + 1) if n != 34] +alphabet = '|'.join(alphabet_before) def tokenize_text(text): lexer = Lexer( @@ -140,7 +140,7 @@ def tokenize_text(text): ('salto', '\n'), ('space', ' *'), (idx, f'({letters}|_)(_|{letters}|0|{nonzero_digits})*'), - (stringx, f'"({eliminate_regex_conflict(alphabet)})*"')], + (stringx, f'"({alphabet})*"')], G.EOF) tokens = lexer(text) From 86a1b5c3cf3975f7ba2cf17195e63ccf352a3c85 Mon Sep 17 00:00:00 2001 From: kikeXD Date: Wed, 9 Dec 2020 12:15:28 -0500 Subject: [PATCH 070/138] refs #1 - Now can have in lower and upper names of terminals --- cmp/lexer.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/cmp/lexer.py b/cmp/lexer.py index 4c5e4aa67..6e7312337 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -129,14 +129,30 @@ def eliminate_regex_conflict(text): result += i return result +def get_all_combinations(text, first_lower=False): + result = '' + + first = text[0] + first = eliminate_regex_conflict(first) + + if first_lower: + result += f'{first.lower()}' + else: + result += f'({first.lower()}|{first.upper()})' + for i in text[1:]: + temp = eliminate_regex_conflict(i) + result += f'({temp.lower()}|{temp.upper()})' + + return result + alphabet_before = [eliminate_regex_conflict(chr(n)) for n in range(1, ord('~') + 1) if n != 34] alphabet = '|'.join(alphabet_before) def tokenize_text(text): lexer = Lexer( - [(t, eliminate_regex_conflict(t.Name)) for t in G.terminals if t not in { idx, num, stringx, boolx }] + - [(boolx, 'true|false'), - (num, f'0|({nonzero_digits})(0|{nonzero_digits})*'), + [(t, get_all_combinations(t.Name)) for t in G.terminals if t not in { idx, num, stringx, boolx }] + + [(boolx, f'({get_all_combinations("true", True)})|({get_all_combinations("false", True)})'), + (num, f'0|(({nonzero_digits})(0|{nonzero_digits})*)'), ('salto', '\n'), ('space', ' *'), (idx, f'({letters}|_)(_|{letters}|0|{nonzero_digits})*'), From b9056da18fee88977eb6d2dd1ef63bc2cc9056a9 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 9 Dec 2020 12:15:43 -0500 Subject: [PATCH 071/138] Changing title in UI --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 4ac7c001b..eb6106b72 100644 --- a/main.py +++ b/main.py @@ -107,7 +107,7 @@ class Main inherits IO { ''' def main(G): - st.title('Type Inferer') + st.title('Type Inferencer') st.sidebar.markdown('''Produced by: Carmen Irene Cabrera Rodríguez From ffabf68cf89267531c0fdebda4f368e263e7454d Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Wed, 9 Dec 2020 21:11:51 -0500 Subject: [PATCH 072/138] Organize project structure --- cmp/lexer.py | 66 +-- cmp/regex.py | 16 +- cmp/tools.py | 475 ++++++++++++++++++- cmp/tools2.py | 1244 ------------------------------------------------- cmp/utils.py | 27 +- 5 files changed, 498 insertions(+), 1330 deletions(-) delete mode 100644 cmp/tools2.py diff --git a/cmp/lexer.py b/cmp/lexer.py index 4b55138ef..207fb120e 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -1,44 +1,4 @@ -from cmp.utils import Token, tokenizer from cmp.grammar import * - -fixed_tokens = { t.Name: Token(t.Name, t) for t in G.terminals if t not in { idx, num, stringx, boolx }} - -# @tokenizer(G, fixed_tokens) -# def tokenize_text(token): -# lex = token.lex -# try: -# float(lex) -# return token.transform_to(num) -# except ValueError: -# if lex[0] == '"' and lex[-1] == '"': -# return token.transform_to(stringx) -# if lex == 'true' or lex == 'false': -# return token.transform_to(boolx) -# return token.transform_to(idx) - -def pprint_tokens(tokens, get=False): - indent = 0 - pending = [] - result = '' - for token in tokens: - pending.append(token) - if token.token_type in { ocur, ccur, semi }: - if token.token_type == ccur: - indent -= 1 - if get: - result += ' '*indent + ' '.join(str(t.token_type) for t in pending) + '\n' - else: - print(' '*indent + ' '.join(str(t.token_type) for t in pending)) - pending.clear() - if token.token_type == ocur: - indent += 1 - if get: - result += ' '.join([str(t.token_type) for t in pending]) + '\n' - return result - else: - print(' '.join([str(t.token_type) for t in pending])) - - from cmp.utils import Token from cmp.regex import Regex from cmp.automata import State @@ -69,7 +29,6 @@ def _build_automaton(self): start.add_epsilon_transition(i) final = start.to_deterministic() return final - def _walk(self, string): state = self.automaton @@ -150,4 +109,27 @@ def tokenize_text(text): if i.token_type != 'salto' and i.token_type != 'space': tokens_filtrado.append(i) # print('tokens filtrados', tokens_filtrado) - return tokens_filtrado \ No newline at end of file + return tokens_filtrado + + +def pprint_tokens(tokens, get=False): + indent = 0 + pending = [] + result = '' + for token in tokens: + pending.append(token) + if token.token_type in { ocur, ccur, semi }: + if token.token_type == ccur: + indent -= 1 + if get: + result += ' '*indent + ' '.join(str(t.token_type) for t in pending) + '\n' + else: + print(' '*indent + ' '.join(str(t.token_type) for t in pending)) + pending.clear() + if token.token_type == ocur: + indent += 1 + if get: + result += ' '.join([str(t.token_type) for t in pending]) + '\n' + return result + else: + print(' '.join([str(t.token_type) for t in pending])) diff --git a/cmp/regex.py b/cmp/regex.py index fc5e40efd..04421669b 100644 --- a/cmp/regex.py +++ b/cmp/regex.py @@ -1,11 +1,11 @@ from cmp.pycompiler import Grammar from cmp.utils import Token -from cmp.tools2 import evaluate_parse -from cmp.tools2 import metodo_predictivo_no_recursivo -from cmp.tools2 import nfa_to_dfa -from cmp.tools2 import automata_union, automata_concatenation, automata_closure, automata_minimization -from cmp.tools2 import get_printer -from cmp.tools2 import Node, AtomicNode, UnaryNode, BinaryNode, EpsilonNode, SymbolNode, ClosureNode, UnionNode, ConcatNode +from cmp.tools import evaluate_parse +from cmp.tools import metodo_predictivo_no_recursivo +from cmp.tools import nfa_to_dfa +from cmp.tools import automata_union, automata_concatenation, automata_closure, automata_minimization +from cmp.tools import get_printer +from cmp.tools import Node, AtomicNode, UnaryNode, BinaryNode, EpsilonNode, SymbolNode, ClosureNode, UnionNode, ConcatNode from pprint import pprint as pp import pydot @@ -34,7 +34,7 @@ def __repr__(self): T, F, A, X, Y, Z = G.NonTerminals('T F A X Y Z') pipe, star, opar, cpar, symbol, epsilon = G.Terminals('| * ( ) symbol ε') -# > PRODUCTIONS??? +# > PRODUCTIONS E %= T + X, lambda h, s: s[2], None, lambda h, s: s[1] X %= pipe + T + X, lambda h, s: s[3], None, None, lambda h, s: UnionNode(h[0], s[2]) @@ -87,6 +87,4 @@ def regex_tokenizer(text, G, skip_whitespaces=True): parser = metodo_predictivo_no_recursivo(G) - - printer = get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode) diff --git a/cmp/tools.py b/cmp/tools.py index 9e6f16a6b..eea67bdec 100644 --- a/cmp/tools.py +++ b/cmp/tools.py @@ -1,6 +1,70 @@ -from cmp.utils import ShiftReduceParser, ContainerSet -from cmp.pycompiler import Item +from cmp.utils import ShiftReduceParser, ContainerSet, DisjointSet +from cmp.pycompiler import Item, EOF from cmp.automata import State +from cmp.nfa_dfa import DFA, NFA +import cmp.visitor as visitor +from itertools import islice + + + +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() + +class EpsilonNode(AtomicNode): + def evaluate(self): + return NFA(1, [0], {}) + +class SymbolNode(AtomicNode): + def evaluate(self): + s = self.lex + return NFA(2, [1], {(0, s) : [1],}) + +class ClosureNode(UnaryNode): + @staticmethod + def operate(value): + return automata_closure(value) + +class UnionNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return automata_union(lvalue, rvalue) + +class ConcatNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return automata_concatenation(lvalue, rvalue) + def upd_table(table, head, symbol, production): @@ -64,6 +128,134 @@ def compute_firsts(G): 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.IsNonTerminal: + try: + beta_f = local_firsts[alpha, i] + except KeyError: + beta_f = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None)) + change |= follows[Y].update(beta_f) + if beta_f.contains_epsilon: + change |= follows[Y].update(follow_X) + + return follows + +def build_parsing_table(G, firsts, follows): + M = {} + ok = True + + for production in G.Productions: + X = production.Left + alpha = production.Right + + for t in firsts[alpha]: + ok &= upd_table(M, X, t, production) + + if firsts[alpha].contains_epsilon: + for t in follows[X]: + ok &= upd_table(M, X, t, production) + + return M, ok + +def deprecated_metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): + + if M is None: + if firsts is None: + firsts = compute_firsts(G) + if follows is None: + follows = compute_follows(G, firsts) + M, _ = build_parsing_table(G, firsts, follows) + + def parser(w): + + stack = [G.EOF, G.startSymbol] + cursor = 0 + output = [] + + while True: + top = stack.pop() + a = w[cursor] + + if top.IsEpsilon: + pass + elif top.IsTerminal: + assert top == a + if top == G.EOF: + break + cursor += 1 + else: + production = M[top][a][0] + output.append(production) + production = list(production.Right) + stack.extend(production[::-1]) + + return output + + return parser + +def metodo_predictivo_no_recursivo(G, M = None): + parser = deprecated_metodo_predictivo_no_recursivo(G, M) + def updated(tokens): + return parser([t.token_type for t in tokens]) + return updated + + + +def evaluate_parse(left_parse, tokens): + if not left_parse or not tokens: + return + + left_parse = iter(left_parse) + tokens = iter(tokens) + result = evaluate(next(left_parse), left_parse, tokens) + + assert isinstance(next(tokens).token_type, EOF) + return result + +def evaluate(production, left_parse, tokens, inherited_value=None): + _, body = production + attributes = production.attributes + + synteticed = [None] * (len(body) + 1) + inherited = [None] * (len(body) + 1) + inherited[0] = inherited_value + + for i, symbol in enumerate(body, 1): + if symbol.IsTerminal: + assert inherited[i] is None + synteticed[i] = next(tokens).lex + else: + next_production = next(left_parse) + assert symbol == next_production.Left + attr = attributes[i] + if attr is not None: + inherited[i] = attr(inherited, synteticed) + synteticed[i] = evaluate(next_production, left_parse, tokens, inherited[i]) + + attr = attributes[0] + if attr is None: + return None + return attr(inherited, synteticed) + def expand(item, firsts): @@ -78,8 +270,6 @@ def expand(item, firsts): assert not lookaheads.contains_epsilon return [Item(prod, 0, lookaheads) for prod in next_symbol.productions] - - def compress(items): centers = {} @@ -93,7 +283,6 @@ def compress(items): return {Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items()} - def closure_lr1(items, firsts): closure = ContainerSet(*items) @@ -109,14 +298,11 @@ def closure_lr1(items, firsts): 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' @@ -154,7 +340,6 @@ def build_LR1_automaton(G): automaton.set_formatter(lambda x: "") return automaton - class LR1Parser(ShiftReduceParser): def _build_parsing_table(self): self.ok = True @@ -182,3 +367,275 @@ def _build_parsing_table(self): 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 move(automaton, states, symbol): + moves = set() + for state in states: + try: + for item in automaton.transitions[state][symbol]: + moves.add(item) + except KeyError: + pass + return moves + +def epsilon_closure(automaton, states): + pending = [ s for s in states ] # equivalente a list(states) pero me gusta así :p + closure = { s for s in states } # equivalente a set(states) pero me gusta así :p + + while pending: + state = pending.pop() + + l = move(automaton, [state], '') + for i in l: + if(i in closure): + pass + else: + closure.add(i) + pending.append(i) + + return ContainerSet(*closure) + +def nfa_to_dfa(automaton): + transitions = {} + + start = epsilon_closure(automaton, [automaton.start]) + start.id = 0 + start.is_final = any(s in automaton.finals for s in start) + states = [ start ] + + pending = [ start ] + while pending: + state = pending.pop() + + for symbol in automaton.vocabulary: + new_state = move(automaton, state, symbol) + new_state = epsilon_closure(automaton, new_state) + + if not new_state: + continue + + for s in states: + if(s == new_state): + new_state = s + break + else: + new_state.id = len(states) + new_state.is_final = any(s in automaton.finals for s in new_state) + pending.append(new_state) + states.append(new_state) + + try: + transitions[state.id, symbol] + assert False, 'Invalid DFA!!!' + except KeyError: + transitions[state.id, symbol] = new_state.id + + finals = [ state.id for state in states if state.is_final ] + dfa = DFA(len(states), finals, transitions) + return dfa + + + +def distinguish_states(group, automaton, partition): + split = {} + vocabulary = tuple(automaton.vocabulary) + + transition = automaton.transitions + + for member in group: + for item in split.keys(): + for symbol in vocabulary: + q1 = None + q2 = None + try: + q1 = partition[transition[item][symbol][0]].representative + except KeyError: + q1 = None + try: + q2 = partition[transition[member.value][symbol][0]].representative + except KeyError: + q2 = None + if q1 != q2: + break + else: + split[item].append(member.value) + break + else: + split[member.value] = [member.value] + + + return [ group for group in split.values()] + +def state_minimization(automaton): + partition = DisjointSet(*range(automaton.states)) + + ## partition = { NON-FINALS | FINALS } + finals = list(automaton.finals) + non_finals = [state for state in range(automaton.states) if not state in automaton.finals] + partition.merge(finals) + partition.merge(non_finals) + + while True: + new_partition = DisjointSet(*range(automaton.states)) + + ## Split each group if needed (use distinguish_states(group, automaton, partition)) + for group in partition.groups: + new_groups = distinguish_states(group, automaton, partition) + for new_group in new_groups: + new_partition.merge(new_group) + + if len(new_partition) == len(partition): + break + + partition = new_partition + + return partition + +def automata_minimization(automaton): + partition = state_minimization(automaton) + + states = [s for s in partition.representatives] + + transitions = {} + for i, state in enumerate(states): + origin = state.value + for symbol, destinations in automaton.transitions[origin].items(): + new_dest = states.index(partition[destinations[0]].representative) + + try: + transitions[i,symbol] + assert False + except KeyError: + transitions[i, symbol] = new_dest + pass + + start = states.index(partition[automaton.start].representative) + finals = set([i for i in range(len(states)) if states[i].value in automaton.finals]) + + return DFA(len(states), finals, transitions, start) + +def automata_union(a1, a2): + transitions = {} + + start = 0 + d1 = 1 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + other = [q + d1 for q in destinations] + transitions[origin + d1, symbol] = other + + for (origin, symbol), destinations in a2.map.items(): + other = [q + d2 for q in destinations] + transitions[origin + d2, symbol] = other + + + transitions[start, ''] = [a1.start + d1, a2.start + d2] + for i in a1.finals: + try: + transitions[i + d1, ''].add(final) + except KeyError: + transitions[i + d1, ''] = [final] + for i in a2.finals: + try: + transitions[i + d2, ''].add(final) + except KeyError: + transitions[i + d2, ''] = [final] + + states = a1.states + a2.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_concatenation(a1, a2): + transitions = {} + + start = 0 + d1 = 0 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + other = [q + d1 for q in destinations] + transitions[origin + d1, symbol] = other + + + for (origin, symbol), destinations in a2.map.items(): + other = [q + d2 for q in destinations] + transitions[origin + d2, symbol] = other + + for i in a1.finals: + try: + transitions[i + d1, ''].add(a2.start + d2) + except KeyError: + transitions[i + d1, ''] = [a2.start + d2] + for i in a2.finals: + try: + transitions[i + d2, ''].append(final) + except KeyError: + transitions[i + d2, ''] = [final] + + states = a1.states + a2.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_closure(a1): + transitions = {} + + start = 0 + d1 = 1 + final = a1.states + d1 + + for (origin, symbol), destinations in a1.map.items(): + other = [q + d1 for q in destinations] + transitions[origin + d1, symbol] = other + + transitions[start, ''] = [final, a1.start + d1] + + for i in a1.finals: + try: + transitions[i + d1, ''].add(final) + except KeyError: + transitions[i + d1, ''] = [final] + + try: + transitions[final, ''].add(start) + except: + transitions[final,''] = [start] + + states = a1.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + + + +def get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode, ): + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node, tabs=0): + 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)) diff --git a/cmp/tools2.py b/cmp/tools2.py deleted file mode 100644 index ecdd35434..000000000 --- a/cmp/tools2.py +++ /dev/null @@ -1,1244 +0,0 @@ -from cmp.utils import * -from cmp.nfa_dfa import * -from cmp.automata import * -from cmp.pycompiler import * -from itertools import islice -import 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() - -class EpsilonNode(AtomicNode): - def evaluate(self): - return NFA(1, [0], {}) - -class SymbolNode(AtomicNode): - def evaluate(self): - s = self.lex - return NFA(2, [1], {(0, s) : [1],}) - -class ClosureNode(UnaryNode): - @staticmethod - def operate(value): - return automata_closure(value) - -class UnionNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - return automata_union(lvalue, rvalue) - -class ConcatNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - return automata_concatenation(lvalue, rvalue) - - -#this method is used for updating ll1 table as well as action-goto table -def upd_table(table, head, symbol, production): - if not head in table: - table[head] = {} - if not symbol in table[head]: - table[head][symbol] = [] - if production not in table[head][symbol]: - table[head][symbol].append(production) - return (len(table[head][symbol]) <= 1) - - -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 KeyError: - 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.IsNonTerminal: - try: - beta_f = local_firsts[alpha, i] - except KeyError: - beta_f = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None)) - change |= follows[Y].update(beta_f) - if beta_f.contains_epsilon: - change |= follows[Y].update(follow_X) - - return follows - -def build_parsing_table(G, firsts, follows): - M = {} - ok = True - - for production in G.Productions: - X = production.Left - alpha = production.Right - - for t in firsts[alpha]: - ok &= upd_table(M, X, t, production) - - if firsts[alpha].contains_epsilon: - for t in follows[X]: - ok &= upd_table(M, X, t, production) - - return M, ok - -def deprecated_metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): - - if M is None: - if firsts is None: - firsts = compute_firsts(G) - if follows is None: - follows = compute_follows(G, firsts) - M, _ = build_parsing_table(G, firsts, follows) - - def parser(w): - - stack = [G.EOF, G.startSymbol] - cursor = 0 - output = [] - - while True: - top = stack.pop() - a = w[cursor] - - if top.IsEpsilon: - pass - elif top.IsTerminal: - assert top == a - if top == G.EOF: - break; - cursor += 1 - else: - production = M[top][a][0] - output.append(production) - production = list(production.Right) - stack.extend(production[::-1]) - - return output - - return parser - -def metodo_predictivo_no_recursivo(G, M = None): - parser = deprecated_metodo_predictivo_no_recursivo(G, M) - def updated(tokens): - return parser([t.token_type for t in tokens]) - return updated - -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)) - -def evaluate_parse(left_parse, tokens): - if not left_parse or not tokens: - return - - left_parse = iter(left_parse) - tokens = iter(tokens) - result = evaluate(next(left_parse), left_parse, tokens) - - assert isinstance(next(tokens).token_type, EOF) - return result - -def evaluate(production, left_parse, tokens, inherited_value=None): - head, body = production - attributes = production.attributes - - synteticed = [None] * (len(body) + 1) - inherited = [None] * (len(body) + 1) - inherited[0] = inherited_value - - for i, symbol in enumerate(body, 1): - if symbol.IsTerminal: - assert inherited[i] is None - synteticed[i] = next(tokens).lex - else: - next_production = next(left_parse) - assert symbol == next_production.Left - attr = attributes[i] - if attr is not None: - inherited[i] = attr(inherited, synteticed) - synteticed[i] = evaluate(next_production, left_parse, tokens, inherited[i]) - - attr = attributes[0] - if attr is None: - return None - return attr(inherited, synteticed) - -def move(automaton, states, symbol): - moves = set() - for state in states: - try: - for item in automaton.transitions[state][symbol]: - # aux = automaton.transitions[state][symbol] - moves.add(item) - except KeyError: - pass - return moves - -def epsilon_closure(automaton, states): - pending = [ s for s in states ] # equivalente a list(states) pero me gusta así :p - closure = { s for s in states } # equivalente a set(states) pero me gusta así :p - - while pending: - state = pending.pop() - - l = move(automaton, [state], '') - for i in l: - if(i in closure): - pass - else: - closure.add(i) - pending.append(i) - - return ContainerSet(*closure) - -def nfa_to_dfa(automaton): - transitions = {} - - start = epsilon_closure(automaton, [automaton.start]) - start.id = 0 - start.is_final = any(s in automaton.finals for s in start) - states = [ start ] - - pending = [ start ] - while pending: - state = pending.pop() - - for symbol in automaton.vocabulary: - new_state = move(automaton, state, symbol) - new_state = epsilon_closure(automaton, new_state) - - if not new_state: - continue - - for s in states: - if(s == new_state): - new_state = s - break - else: - new_state.id = len(states) - new_state.is_final = any(s in automaton.finals for s in new_state) - pending.append(new_state) - states.append(new_state) - - try: - transitions[state.id, symbol] - assert False, 'Invalid DFA!!!' - except KeyError: - transitions[state.id, symbol] = new_state.id - - finals = [ state.id for state in states if state.is_final ] - dfa = DFA(len(states), finals, transitions) - return dfa - -def automata_union(a1, a2): - transitions = {} - - start = 0 - d1 = 1 - d2 = a1.states + d1 - final = a2.states + d2 - - for (origin, symbol), destinations in a1.map.items(): - other = [q + d1 for q in destinations] - transitions[origin + d1, symbol] = other - - for (origin, symbol), destinations in a2.map.items(): - other = [q + d2 for q in destinations] - transitions[origin + d2, symbol] = other - - - transitions[start, ''] = [a1.start + d1, a2.start + d2] - for i in a1.finals: - try: - transitions[i + d1, ''].add(final) - except KeyError: - transitions[i + d1, ''] = [final] - for i in a2.finals: - try: - transitions[i + d2, ''].add(final) - except KeyError: - transitions[i + d2, ''] = [final] - - states = a1.states + a2.states + 2 - finals = { final } - - return NFA(states, finals, transitions, start) - -def automata_concatenation(a1, a2): - transitions = {} - - start = 0 - d1 = 0 - d2 = a1.states + d1 - final = a2.states + d2 - - for (origin, symbol), destinations in a1.map.items(): - other = [q + d1 for q in destinations] - transitions[origin + d1, symbol] = other - - - for (origin, symbol), destinations in a2.map.items(): - other = [q + d2 for q in destinations] - transitions[origin + d2, symbol] = other - - for i in a1.finals: - try: - transitions[i + d1, ''].add(a2.start + d2) - except KeyError: - transitions[i + d1, ''] = [a2.start + d2] - for i in a2.finals: - try: - transitions[i + d2, ''].append(final) - except KeyError: - transitions[i + d2, ''] = [final] - - states = a1.states + a2.states + 1 - finals = { final } - - return NFA(states, finals, transitions, start) - -def automata_closure(a1): - transitions = {} - - start = 0 - d1 = 1 - final = a1.states + d1 - - for (origin, symbol), destinations in a1.map.items(): - other = [q + d1 for q in destinations] - transitions[origin + d1, symbol] = other - - transitions[start, ''] = [final, a1.start + d1] - - for i in a1.finals: - try: - transitions[i + d1, ''].add(final) - except KeyError: - transitions[i + d1, ''] = [final] - - try: - transitions[final, ''].add(start) - except: - transitions[final,''] = [start] - - states = a1.states + 2 - finals = { final } - - return NFA(states, finals, transitions, start) - -def distinguish_states(group, automaton, partition): - split = {} - vocabulary = tuple(automaton.vocabulary) - - transition = automaton.transitions - - for member in group: - for item in split.keys(): - for symbol in vocabulary: - q1 = None - q2 = None - try: - q1 = partition[transition[item][symbol][0]].representative - except KeyError: - q1 = None - try: - q2 = partition[transition[member.value][symbol][0]].representative - except KeyError: - q2 = None - if q1 != q2: - break - else: - split[item].append(member.value) - break - else: - split[member.value] = [member.value] - - - return [ group for group in split.values()] - -def state_minimization(automaton): - partition = DisjointSet(*range(automaton.states)) - - ## partition = { NON-FINALS | FINALS } - finals = list(automaton.finals) - non_finals = [state for state in range(automaton.states) if not state in automaton.finals] - partition.merge(finals) - partition.merge(non_finals) - - while True: - new_partition = DisjointSet(*range(automaton.states)) - - ## Split each group if needed (use distinguish_states(group, automaton, partition)) - for group in partition.groups: - new_groups = distinguish_states(group, automaton, partition) - for new_group in new_groups: - new_partition.merge(new_group) - - if len(new_partition) == len(partition): - break - - partition = new_partition - - return partition - -def automata_minimization(automaton): - partition = state_minimization(automaton) - - states = [s for s in partition.representatives] - - transitions = {} - for i, state in enumerate(states): - origin = state.value - for symbol, destinations in automaton.transitions[origin].items(): - new_dest = states.index(partition[destinations[0]].representative) - - try: - transitions[i,symbol] - assert False - except KeyError: - transitions[i, symbol] = new_dest - pass - - start = states.index(partition[automaton.start].representative) - finals = set([i for i in range(len(states)) if states[i].value in automaton.finals]) - - return DFA(len(states), finals, transitions, start) - - -#methods to simplify the grammar -def remove_non_terminating_productions(G): - terminals = set(G.terminals) - - change = True - while change: - change = False - for prod in G.Productions: - if prod.Left not in terminals and all([s in terminals for s in prod.Right]): - terminals.add(prod.Left) - change = True - - nt = G.nonTerminals.copy() - G.nonTerminals = [] - G.Productions = [] - - for s in nt: - if s in terminals: - G.nonTerminals.append(s) - productions = s.productions.copy() - s.productions = [] - for prod in productions: - if all([item in terminals for item in prod.Right]): - G.Productions.append(prod) - s.productions.append(prod) - -def remove_useless_productions(G): - flag = [False] * (len(G.nonTerminals) + len(G.terminals)) - mp = {} - - for i, nt in enumerate(G.nonTerminals): - mp[nt] = i - sz = len(G.nonTerminals) - for i, t in enumerate(G.terminals): - mp[t] = i + sz - - def dfs(S): - flag[mp[S]] = True - for prod in S.productions: - _, right = prod - for symbol in right: - if isinstance(symbol, Terminal): - flag[mp[symbol]] = True - if isinstance(symbol, NonTerminal) and not flag[mp[symbol]]: - dfs(symbol) - - dfs(G.startSymbol) - nt = G.nonTerminals.copy() - G.nonTerminals = [] - G.Productions = [] - - for item in nt: - if flag[mp[item]]: - G.nonTerminals.append(item) - G.Productions.extend(item.productions) - - t = G.terminals.copy() - G.terminals = [] - for item in t: - if flag[mp[item]]: - G.terminals.append(item) - -def remove_null_productions(G): - null_prod = set() - - change = True - while change: - change = False - for prod in G.Productions: - if prod.Left not in null_prod and all([s in null_prod for s in prod.Right]): - null_prod.add(prod.Left) - change = True - if G.startSymbol in null_prod: - return - - def add_prod(head, body, i, prod): - if i == len(body): - if len(prod) > 0: - sentence = G.Epsilon - for s in prod: - sentence += s - head %= sentence - return - - if body[i] not in null_prod: - prod.append(body[i]) - add_prod(head, body, i + 1, prod) - prod.pop() - else: - add_prod(head, body, i + 1, prod) - prod.append(body[i]) - add_prod(head, body, i + 1, prod) - prod.pop() - - G.Productions = [] - for nt in G.nonTerminals: - nt_prod = nt.productions.copy() - nt.productions = [] - for prod in nt_prod: - head, body = prod.Left, prod.Right - if any([s in null_prod for s in body]): - production = [] - add_prod(head, body, 0, production) - elif len(body) > 0: - head %= body - - # if G.startSymbol in null_prod: - # G.startSymbol %= G.Epsilon - -def remove_unit_productions(G): - guf = {nt : set() for nt in G.nonTerminals} - unit_prod = set() - - for prod in G.Productions: - head, body = prod.Left, prod.Right - if len(body) == 1 and isinstance(body[0], NonTerminal): - unit_prod.add(prod) - else: - guf[head].add(body) - - change = True - while change: - change = False - for prod in unit_prod: - head, body = prod.Left, prod.Right - sz = len(guf[head]) - #guf[head].update(guf[body[0]]) - for item in guf[body[0]]: - if not item: - guf[head].add(G.Epsilon) - else: - guf[head].add(item) - change |= (sz < len(guf[head])) - - G.Productions = [] - for nt in G.nonTerminals: - nt.productions = [] - for item in guf[nt]: - nt %= item - - -def remove_common_prefix(G): - unsolved = set([nt for nt in G.nonTerminals]) - - id = 1 - while unsolved: - nonTerminals = G.nonTerminals.copy() - for nt in nonTerminals: - if nt in unsolved: - nt_prod = nt.productions.copy() - nt.productions = [] - flag = set() - - for item in nt_prod: - if item not in flag: - flag.add(item) - if not item.IsEpsilon: - common = set() - symbols = [] - s = item.Right[0] - symbols.append(s) - for p in nt_prod: - if p not in flag and len(p.Right) > 0: - if p.Right[0] == s: - flag.add(p) - common.add(p) - if len(common) == 0: - item.Left %= item.Right - else: - for i in range(1, len(item.Right)): - for c in common: - if i == len(c.Right) or c.Right[i] != item.Right[i]: - break - else: - symbols.append(item.Right[i]) - continue - break - sentence = G.Epsilon - for symbol in symbols: - sentence += symbol - new_nt = G.NonTerminal(nt.Name + '_' + str(id)) - id += 1 - nt %= sentence + new_nt - common.add(item) - for c in common: - sent = G.Epsilon - for i in range(len(symbols), len(c.Right)): - sent += c.Right[i] - new_nt %= sent - unsolved.add(new_nt) - else: - item.Left %= item.Right - - unsolved.remove(nt) - - G.Productions = [] - for nt in G.nonTerminals: - for p in nt.productions: - G.Productions.append(p) - -def remove_immediate_recursion(G): - G.Productions = [] - - non_terminals = G.nonTerminals.copy() - for item in non_terminals: - bad_prod = [Sentence(*prod.Right[1:]) for prod in item.productions if len(prod.Right) > 0 and prod.Right[0] == item] - good_prod = [Sentence(*prod.Right) for prod in item.productions if len(prod.Right) == 0 or prod.Right[0] != item] - - if len(bad_prod) > 0: - nsymbol = G.NonTerminal(item.Name + '_0') - item.productions = [] - - for prod in good_prod: - item %= prod + nsymbol - - for prod in bad_prod: - nsymbol %= prod + nsymbol - - nsymbol %= G.Epsilon - - else: - G.Productions.extend(item.productions) - -def grammar_from_input(input): - terminals, nonTerminals, productions = [], [], [] - - input = input.split('\n') - lines = [l for l in input if l != ''] - - l = lines[0].split() - start_symbol = l[-1] - nonTerminals.append(start_symbol) - - l = lines[1].replace(',', ' ').split() - if len(l) > 4: - for i in range(3, len(l) - 1): - nonTerminals.append(l[i]) - - l = lines[2].replace(',', ' ').split() - if len(l) > 4: - for i in range(3, len(l) - 1): - terminals.append(l[i]) - else: - raise Exception('Invalid sentence') - - lines = lines[3:] - for prod in lines: - right, sentences = prod.split('=') - right, = right.split() - - sentences = sentences.split(';') - # sentences = sentences.split('|') - for s in sentences: - s = s.replace('+', ' ').split() - # s = s.split() - productions.append({'Head': right, 'Body': s}) - - d = dict() - d['NonTerminals'] = nonTerminals - d['Terminals'] = terminals - d['Productions'] = productions - - G = Grammar.from_json(json.dumps(d)) - - if G.startSymbol is None: - for nt in G.nonTerminals: - if nt.Name == start_symbol: - G.startSymbol = nt - break - - return G - -def simplifying_grammar(G): - remove_null_productions(G) - remove_unit_productions(G) - remove_non_terminating_productions(G) - remove_useless_productions(G) - remove_immediate_recursion(G) - remove_common_prefix(G) - - - -def validate_conflict(w, M, G): - stack = [G.EOF, G.startSymbol] - cursor = 0 - - while True: - top = stack.pop() - a = w[cursor] - - if top.IsEpsilon: - pass - elif top.IsTerminal: - assert top == a - if top == G.EOF: - break; - cursor += 1 - else: - if len(M[top][a]) > 1: - return True - production = M[top][a][0] - production = list(production.Right) - stack.extend(production[::-1]) - - return False - -def ll1_conflict(G, M): - queue = [] - - queue.append(([G.startSymbol], [], False)) - while queue: - prod, word, conflict = queue.pop(0) - while prod and isinstance(prod[0], Terminal): - word.append(prod.pop(0)) - - if not prod: - if conflict: - if validate_conflict(word + [G.EOF], M, G): - w = ' '.join([symbol.Name for symbol in word]) - return w - continue - - symbol = prod.pop(0) - for terminal in M[symbol]: - c = conflict or len(M[symbol][terminal]) > 1 - for p in M[symbol][terminal]: - body = list(p.Right) - body.extend(prod) - queue.append((body, word.copy(), c)) - - - -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 - -class SLR1Parser(ShiftReduceParser): - def _build_parsing_table(self): - self.ok = True - G = self.Augmented = self.G.AugmentedGrammar(True) - firsts = compute_firsts(G) - follows = compute_follows(G, firsts) - - self.automaton = build_LR0_automaton(G).to_deterministic(lambda x: "") - 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 - if item.IsReduceItem: - if item.production.Left == G.startSymbol: - self.ok &= upd_table(self.action, idx, G.EOF, (SLR1Parser.OK, '')) - else: - for terminal in follows[item.production.Left]: - self.ok &= upd_table(self.action, idx, terminal, (SLR1Parser.REDUCE, item.production)) - else: - symbol = item.NextSymbol - - if symbol.IsTerminal: - self.ok &= upd_table(self.action, idx, symbol, (SLR1Parser.SHIFT, node[symbol.Name][0].idx)) - else: - self.ok &= upd_table(self.goto, idx, symbol, node[symbol.Name][0].idx) - -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) - -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_LALR1_automaton(G): - lr1_automaton = build_LR1_automaton(G) - states = list(lr1_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 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) - - new_states[0].set_formatter(empty_formatter) - return new_states[0] - -class LALR1Parser(ShiftReduceParser): - def _build_parsing_table(self): - self.ok = True - G = self.Augmented = self.G.AugmentedGrammar(True) - - automaton = self.automaton = build_LALR1_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 action_goto_conflict(action, goto): - # (stack, word, conflict, terminal) - queue = [([0], [], False, None)] - - while queue: - stack, word, conflict, terminal = queue.pop(0) - state = stack[-1] - try: - if terminal is not None: - actions = action[state][terminal] - if any([act for act, _ in actions if act == SLR1Parser.OK]) and conflict: - return ' '.join(word) - - conflict |= (len(actions) > 1) - for act, tag in actions: - if act == SLR1Parser.SHIFT: - queue.append((stack + [tag], word + [terminal.Name], conflict, None)) - elif act == SLR1Parser.REDUCE: - s = stack.copy() - if not tag.IsEpsilon: - s = s[:-len(tag.Right)] - data = goto[s[-1]][tag.Left] - c = (len(data) > 1) or conflict - for go in data: - queue.append((s + [go], word.copy(), c, terminal)) - else: - for symbol in action[state]: - queue.append((stack, word.copy(), conflict, symbol)) - except Exception as e: - print(f'FAILURE {e}') - - -# def action_goto_conflict(action, goto): -# # (stack, word, conflict, terminal) -# queue = [([0], '', False, None)] - -# while queue: -# stack, word, conflict, terminal = queue.pop(0) -# state = stack[-1] -# try: -# if terminal is not None: -# actions = action[state][terminal] -# if any([act for act, _ in actions if act == SLR1Parser.OK]) and conflict: -# return word - -# conflict |= (len(actions) > 1) -# for act, tag in actions: -# if act == SLR1Parser.SHIFT: -# queue.append((stack + [tag], word + terminal.Name, conflict, None)) -# elif act == SLR1Parser.REDUCE: -# s = stack.copy() -# if not tag.IsEpsilon: -# s = s[:-len(tag.Right)] -# data = goto[s[-1]][tag.Left] -# c = (len(data) > 1) or conflict -# for go in data: -# queue.append((s + [go], word, c, terminal)) -# else: -# for symbol in action[state]: -# queue.append((stack, word, conflict, symbol)) -# except Exception as e: -# print(f'FAILURE {e}') - - - -def ll1_analysis(G): - firsts = compute_firsts(G) - follows = compute_follows(G, firsts) - - M, ok = build_parsing_table(G, firsts, follows) - - return firsts, follows, M, ok - - - -def derivation_tree(d): - def add_trans(cur, transitions): - for symbol in transitions: - if symbol.IsTerminal: - cur.add_transition('', State(symbol, True)) - else: - s = State(symbol, True) - try: - old[symbol].append(s) - except KeyError: - old[symbol] = [s] - cur.add_transition('', s) - if len(transitions) == 0: - cur.add_transition('', State(transitions, True)) - - p1 = d[0] - old = {} - root = State(p1.Left.Name, True) - add_trans(root, p1.Right) - - for p in d[1:]: - node = old[p.Left].pop() - add_trans(node, p.Right) - - return root - -def parse_string(G, word): - m = {t.Name:t for t in G.terminals} - word = word.split() - w = [m[item] for item in word] - w.append(G.EOF) - return w - -def make_tree_LL1(G, w, M, firsts, follows): - try: - w = parse_string(G, w) - return derivation_tree(deprecated_metodo_predictivo_no_recursivo(G, M, firsts, follows)(w)) - except Exception as e: - return 'String not recognized' - -def make_tree(G, w, parser): - try: - w = parse_string(G, w) - d = parser(w) - d.reverse() - return derivation_tree(d) - - except Exception as e: - return "String not recognized" \ No newline at end of file diff --git a/cmp/utils.py b/cmp/utils.py index 3c608744c..0980ecd8a 100644 --- a/cmp/utils.py +++ b/cmp/utils.py @@ -139,32 +139,7 @@ def transform_to(self, 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 } From 0eda4654d5d308a55ae5e272624724af1a2a890c Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Thu, 10 Dec 2020 12:55:43 -0500 Subject: [PATCH 073/138] Add \t to spaces and display lexer and parser errors --- cmp/lexer.py | 5 ++- cmp/type_checker.py | 2 +- main.py | 104 +++++++++++++++++++++++--------------------- 3 files changed, 59 insertions(+), 52 deletions(-) diff --git a/cmp/lexer.py b/cmp/lexer.py index 6e346d774..a33965f4c 100644 --- a/cmp/lexer.py +++ b/cmp/lexer.py @@ -43,7 +43,8 @@ def _walk(self, string): final = state final_lex = lex except TypeError: - # print(symbol, lex, string, state) + # print("entre ---", symbol ) + # print(lex, string, state) break return final, final_lex @@ -113,7 +114,7 @@ def tokenize_text(text): [(boolx, f'({get_all_combinations("true", True)})|({get_all_combinations("false", True)})'), (num, f'0|(({nonzero_digits})(0|{nonzero_digits})*)'), ('salto', '\n'), - ('space', ' *'), + ('space', '( |\t)( |\t)*'), (idx, f'({letters}|_)(_|{letters}|0|{nonzero_digits})*'), (stringx, f'"({alphabet})*"')], G.EOF) diff --git a/cmp/type_checker.py b/cmp/type_checker.py index 300895383..2a880d92d 100644 --- a/cmp/type_checker.py +++ b/cmp/type_checker.py @@ -345,7 +345,7 @@ def visit(self, node, scope): def visit(self, node, scope): var = scope.find_variable(node.lex) if var is None: - self.errors.append(VARIABLE_NOT_DEFINED %(node.lex, self.current_type)) + self.errors.append(VARIABLE_NOT_DEFINED %(node.lex, self.current_type.name)) var = scope.define_variable(node.lex, ErrorType()) return var.type diff --git a/main.py b/main.py index eb6106b72..e4581eac5 100644 --- a/main.py +++ b/main.py @@ -116,58 +116,64 @@ def main(G): text = st.text_area('Input your code here:') if text: + st.text(text) try: tokens = list(tokenize_text(text)) - parser = LR1Parser(G) - parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) - ast = evaluate_reverse_parse(parse, operations, tokens) - - st.title('Results:') - - errors = [] - collector = TypeCollector(errors) - collector.visit(ast) - context = collector.context - - # for e in errors: - # st.error(e) - # st.text('Context:') - # st.text(context) - - # st.subheader('Building types') - builder = TypeBuilder(context, errors) - builder.visit(ast) - manager = builder.manager - # for e in errors: - # st.error(e) - # st.text('Context:') - # st.text(context) - - # st.subheader('Checking types') - checker = TypeChecker(context, manager, []) - scope = checker.visit(ast) - - # st.subheader('Infering types') - temp_errors = [] - inferencer = TypeInferencer(context, manager, temp_errors) - inferencer.visit(ast, scope) - # for e in temp_errors: - # st.error(e) - - # st.subheader('Last check') - errors.extend(temp_errors) - checker = TypeChecker(context, manager, errors) - checker.visit(ast) - for e in errors: - st.error(e) - - formatter = FormatVisitor() - tree = formatter.visit(ast) - st.text(tree) - - except Exception as e: - st.error(f'Unexpected error!!! You probably did something wrong :wink:') + st.error(f'Lexer Error: {str(e)}') + else: + try: + parser = LR1Parser(G) + parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) + except Exception as e: + st.error(f'Parser Error: {str(e)}') + else: + ast = evaluate_reverse_parse(parse, operations, tokens) + + st.title('Results:') + + errors = [] + collector = TypeCollector(errors) + collector.visit(ast) + context = collector.context + + # for e in errors: + # st.error(e) + # st.text('Context:') + # st.text(context) + + # st.subheader('Building types') + builder = TypeBuilder(context, errors) + builder.visit(ast) + manager = builder.manager + # for e in errors: + # st.error(e) + # st.text('Context:') + # st.text(context) + + # st.subheader('Checking types') + checker = TypeChecker(context, manager, []) + scope = checker.visit(ast) + + # st.subheader('Infering types') + temp_errors = [] + inferencer = TypeInferencer(context, manager, temp_errors) + inferencer.visit(ast, scope) + # for e in temp_errors: + # st.error(e) + + # st.subheader('Last check') + errors.extend(temp_errors) + checker = TypeChecker(context, manager, errors) + checker.visit(ast) + for e in errors: + st.error(e) + + formatter = FormatVisitor() + tree = formatter.visit(ast) + st.text(tree) + # except Exception as e: + # st.error(f'Unexpected error!!! You probably did something wrong :wink:') if __name__ == '__main__': From 5d1510e04eb8bc7aa4d250bdfc081ca53f7cd758 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sat, 12 Dec 2020 15:25:38 -0500 Subject: [PATCH 074/138] Fix ReadMe file --- README.md | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 2047051fa..e9edd192a 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,30 @@ # TypeInferencer -El objetivo de este proyecto es la implementación de un intérprete de COOL que permita la inferencia de tipos a partir del uso de AUTO_TYPE. Para el desarrollo del mismo se utilizaron como base los contenidos vistos en Clase Práctica durante el curso de Compilación. - +El objetivo de este proyecto es la implementación de un intérprete de COOL que permita la inferencia de tipos a partir del uso de AUTO_TYPE. Para el desarrollo del mismo se utilizaron como base los contenidos vistos en Clase Práctica durante el curso de Compilación. ### Requerimientos 📋 + El proyecto fue desarrollado haciendo uso de: - `python v-3.7.2` - `streamlit v-0.56.0` Se necesita también algún navegador web instalado como `chrome` o `firefox` - ### Modo de uso + Para ejecutar solamente abra la consola tal que la dirección raíz sea la del fichero donde se encuentra el proyecto y ejecute la siguiente línea: ``` streamlit run main.py ``` -Para insertar el texto que representará el código de COOL debe tener en cuenta que todas las palabras deberán estar separadas por espacio, como se muestra en el siguiente ejemplo: - -``` -class Point { - x : AUTO_TYPE ; - y : AUTO_TYPE ; - init ( n : Int , m : Int ) : SELF_TYPE { { - x <- n ; - y <- m ; - } } ; -} ; -``` - -El lexer del proyecto separa el texto de entrada por los espacios para generar los tokens; en caso de que no reciba el texto correctamente espaciado, no podrá parsearlo satisfactoriamente. Note que este comportamiento también se aplica a las constantes de tipo String: deben encontrarse cerradas por doble comilla y no deben existir espacios entre las palabras que la conformen; puede sustituir los espacios por guiones. - -Si ocurre algún error durante el parsing, debido a que no se tuvieron en cuenta los espacios o las reglas de sintaxis de COOL, este será reportado y no se llevará a cabo ningún análisis posterior. De lo contrario, se mostrarán los errores semánticos, si los hay, y se mostrará el árbol resultante del análisis donde todos los AUTO_TYPES habrán sido reemplazados por el tipo estático inferido. +Para insertar el texto que representará el código de COOL debe tener en cuenta las reglas de sintaxis de este lenguaje. En caso contrario, ya sea durante el proceso del lexer o de parsing, obtendrá un error que será reportado y no se llevará a cabo ningún análisis posterior. Si el texto es sintácticamente correcto de acuerdo a Cool, se mostrarán los errores semánticos, si los hay, y se mostrará el árbol resultante del análisis donde todos los AUTO_TYPES habrán sido reemplazados por el tipo estático inferido. Si un AUTO_TYPE es usado de forma incorrecta, este no se verá sustituido en la salida del programa; no obstante el error sí es reportado. ### Pipeline + Una vez terminada la fase de *parsing* se llevan a cabo los siguientes pasos: - Recolección de tipos @@ -48,6 +34,7 @@ Una vez terminada la fase de *parsing* se llevan a cabo los siguientes pasos: A continuación se explicará el funcionamiento de cada uno: #### Recolección de tipos + Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes pasos: - Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : Int, String, Bool, IO, Object; incluyendo la definición de sus métodos. Además se añaden como tipos SELF_TYPE, AUTO_TYPE. @@ -57,6 +44,7 @@ Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes - Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. #### Construcción de tipos + La construcción de tipos se desarrolla empleando la clase Type Builder. Esta se encarga de visitar los *features* de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. Además se encarga de chequear la existencia del tipo Main con su método main correspondiente, como es requerido en COOL. @@ -64,11 +52,12 @@ Además se encarga de chequear la existencia del tipo Main con su método main c En esta clase también se hace uso de la clase Inferencer Manager que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO_TYPE se le asigna un *id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. #### Chequeo e Inferencia de tipos + En primer lugar se utiliza la clase Type Checker para validar el correcto uso de los tipos definidos. Toma la instancia de clase Inferencer Manager utilizada en el Type Builder para continuar la asignación de id a otros elementos en el código que también pueden estar definidos como AUTO_TYPE, como es el caso de las variables definidas en la expresión Let. Las variables definidas en el Scope se encargarán de guardar el id asignado; en caso de que no se les haya asignado ninguno, el id será *None*. La instancia de Scope creada en el Type Checker, así como la de Inferencer Manager se pasarán al Type Inferencer para realizar la inferencia de tipos. -Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El id asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. +Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El id asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. Sea una variable con id = i, que está definida como AUTO_TYPE y sea A el tipo estático que se ha de inferir: @@ -79,7 +68,7 @@ Sea una variable con id = i, que está definida como AUTO_TYPE y sea A el tipo e La clase Inferencer Manager además, está equipada con métodos para actualizar las listas dado un id, y para realizar la inferencia dados los tipos almacenados. -El Type Inferencer por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: +El Type Inferencer por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: 1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un *ExpressionNode*, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el *manager*. 2. Infiere todos los tipos que pueda con la información recogida. @@ -90,11 +79,10 @@ El Type Inferencer por su parte, realizará un algoritmo de punto fijo para llev Por último se realiza un nuevo recorrido del AST con el Type Checker para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los AUTO_TYPES sustituidos por el tipo inferido. - ## Autores ✒️ -* **Carmen Irene Cabrera Rodríguez** - [cicr99](https://github.com/cicr99) -* **Enrique Martínez González** - [kikeXD](https://github.com/kikeXD) +- **Carmen Irene Cabrera Rodríguez** - [cicr99](https://github.com/cicr99) +- **Enrique Martínez González** - [kikeXD](https://github.com/kikeXD) ## Licencia From 3f8288afbfa328045dbfad0ecd35cf0b726508b5 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sat, 12 Dec 2020 15:55:14 -0500 Subject: [PATCH 075/138] Modify error message --- main.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index e4581eac5..4cfc683cb 100644 --- a/main.py +++ b/main.py @@ -120,13 +120,13 @@ def main(G): try: tokens = list(tokenize_text(text)) except Exception as e: - st.error(f'Lexer Error: {str(e)}') + st.error(f'Lexer Error: You probably did something wrong :wink:') else: try: parser = LR1Parser(G) parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) except Exception as e: - st.error(f'Parser Error: {str(e)}') + st.error(f'Parser Error: You probably did something wrong :wink:') else: ast = evaluate_reverse_parse(parse, operations, tokens) @@ -172,9 +172,6 @@ def main(G): formatter = FormatVisitor() tree = formatter.visit(ast) st.text(tree) - # except Exception as e: - # st.error(f'Unexpected error!!! You probably did something wrong :wink:') - if __name__ == '__main__': main(G) From c4aa47c21860ddd3431fce12bbb469abdbcd0663 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Thu, 25 Feb 2021 18:07:59 -0500 Subject: [PATCH 076/138] Merged with base project --- README.md | 89 ---------------- {cmp => src/cmp}/ast.py | 0 {cmp => src/cmp}/automata.py | 0 {cmp => src/cmp}/evaluation.py | 0 {cmp => src/cmp}/formatter.py | 0 {cmp => src/cmp}/grammar.py | 0 {cmp => src/cmp}/lexer.py | 0 {cmp => src/cmp}/nfa_dfa.py | 0 {cmp => src/cmp}/pycompiler.py | 0 {cmp => src/cmp}/regex.py | 0 {cmp => src/cmp}/semantic.py | 0 {cmp => src/cmp}/tools.py | 0 {cmp => src/cmp}/type_builder.py | 0 {cmp => src/cmp}/type_checker.py | 0 {cmp => src/cmp}/type_collector.py | 0 {cmp => src/cmp}/type_inferencer.py | 0 {cmp => src/cmp}/utils.py | 0 {cmp => src/cmp}/visitor.py | 0 main.py => src/main.py | 0 test_cases/cyclic_inheritance.txt | 16 --- test_cases/inference.txt | 152 ---------------------------- test_cases/let_inference.txt | 35 ------- test_cases/orientation_examples.txt | 91 ----------------- test_cases/test1.txt | 91 ----------------- 24 files changed, 474 deletions(-) delete mode 100644 README.md rename {cmp => src/cmp}/ast.py (100%) rename {cmp => src/cmp}/automata.py (100%) rename {cmp => src/cmp}/evaluation.py (100%) rename {cmp => src/cmp}/formatter.py (100%) rename {cmp => src/cmp}/grammar.py (100%) rename {cmp => src/cmp}/lexer.py (100%) rename {cmp => src/cmp}/nfa_dfa.py (100%) rename {cmp => src/cmp}/pycompiler.py (100%) rename {cmp => src/cmp}/regex.py (100%) rename {cmp => src/cmp}/semantic.py (100%) rename {cmp => src/cmp}/tools.py (100%) rename {cmp => src/cmp}/type_builder.py (100%) rename {cmp => src/cmp}/type_checker.py (100%) rename {cmp => src/cmp}/type_collector.py (100%) rename {cmp => src/cmp}/type_inferencer.py (100%) rename {cmp => src/cmp}/utils.py (100%) rename {cmp => src/cmp}/visitor.py (100%) rename main.py => src/main.py (100%) delete mode 100644 test_cases/cyclic_inheritance.txt delete mode 100644 test_cases/inference.txt delete mode 100644 test_cases/let_inference.txt delete mode 100644 test_cases/orientation_examples.txt delete mode 100644 test_cases/test1.txt diff --git a/README.md b/README.md deleted file mode 100644 index e9edd192a..000000000 --- a/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# TypeInferencer - -El objetivo de este proyecto es la implementación de un intérprete de COOL que permita la inferencia de tipos a partir del uso de AUTO_TYPE. Para el desarrollo del mismo se utilizaron como base los contenidos vistos en Clase Práctica durante el curso de Compilación. - -### Requerimientos 📋 - -El proyecto fue desarrollado haciendo uso de: - -- `python v-3.7.2` -- `streamlit v-0.56.0` - -Se necesita también algún navegador web instalado como `chrome` o `firefox` - -### Modo de uso - -Para ejecutar solamente abra la consola tal que la dirección raíz sea la del fichero donde se encuentra el proyecto y ejecute la siguiente línea: - -``` -streamlit run main.py -``` - -Para insertar el texto que representará el código de COOL debe tener en cuenta las reglas de sintaxis de este lenguaje. En caso contrario, ya sea durante el proceso del lexer o de parsing, obtendrá un error que será reportado y no se llevará a cabo ningún análisis posterior. Si el texto es sintácticamente correcto de acuerdo a Cool, se mostrarán los errores semánticos, si los hay, y se mostrará el árbol resultante del análisis donde todos los AUTO_TYPES habrán sido reemplazados por el tipo estático inferido. - -Si un AUTO_TYPE es usado de forma incorrecta, este no se verá sustituido en la salida del programa; no obstante el error sí es reportado. - -### Pipeline - -Una vez terminada la fase de *parsing* se llevan a cabo los siguientes pasos: - -- Recolección de tipos -- Construcción de tipos -- Chequeo / Inferencia de tipos - -A continuación se explicará el funcionamiento de cada uno: - -#### Recolección de tipos - -Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes pasos: - -- Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : Int, String, Bool, IO, Object; incluyendo la definición de sus métodos. Además se añaden como tipos SELF_TYPE, AUTO_TYPE. -- Recorrido por las declaraciones hechas en el programa recolectando los tipos creados. -- Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna Object como padre. -- Chequeo de herencia cíclica. En caso de detectar algún ciclo en la jerarquía de tipos, se reporta el error, y a la clase por la cual hubo problema se le asigna Object como padre, para continuar el análisis. -- Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. - -#### Construcción de tipos - -La construcción de tipos se desarrolla empleando la clase Type Builder. Esta se encarga de visitar los *features* de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. - -Además se encarga de chequear la existencia del tipo Main con su método main correspondiente, como es requerido en COOL. - -En esta clase también se hace uso de la clase Inferencer Manager que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO_TYPE se le asigna un *id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. - -#### Chequeo e Inferencia de tipos - -En primer lugar se utiliza la clase Type Checker para validar el correcto uso de los tipos definidos. Toma la instancia de clase Inferencer Manager utilizada en el Type Builder para continuar la asignación de id a otros elementos en el código que también pueden estar definidos como AUTO_TYPE, como es el caso de las variables definidas en la expresión Let. Las variables definidas en el Scope se encargarán de guardar el id asignado; en caso de que no se les haya asignado ninguno, el id será *None*. - -La instancia de Scope creada en el Type Checker, así como la de Inferencer Manager se pasarán al Type Inferencer para realizar la inferencia de tipos. - -Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El id asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. - -Sea una variable con id = i, que está definida como AUTO_TYPE y sea A el tipo estático que se ha de inferir: - -- `conforms_to[i]` guarda una lista con los tipos a los que debe conformarse A; note que esta lista contiene al menos al tipo Object. El hecho de que A se conforme a estos tipos, implica que todos ellos deben encontrarse en el camino de él a Object en el árbol de jerarquía de tipos. En caso contrario se puede decir que hubo algún error en la utilización del AUTO_TYPE para esta variable. Sea B el tipo más lejano a Object de los que aparecen en la lista. -- `conformed_by[i]` almacena una lista con los tipos que deben conformarse a A. Luego el menor ancestro común (*LCA - Lowest Common Ancestor*) de dichos tipos deberá conformarse a A. Note que este siempre existirá, pues en caso peor será Object, que es la raíz del árbol de tipos. Sea C el LCA de los tipos guardados. Note que si la lista está vacía, (que puede suceder) C será *None*. -- Como C se conforma a A y A se conforma B, tiene que ocurrir que C se conforma a B. En caso contrario, se reporta un uso incorrecto de AUTO_TYPE para esa variable. Todos los tipos en el camino entre B y C son válidos para inferir A; pues cumplen con todas las restricciones que impone el programa. En nuestro caso se elige C, que es el tipo más restringido, para la inferencia. En caso de que C sea *None* se toma B como tipo de inferencia. -- `infered_type[i]` guardará el tipo inferido una vez realizado el procedimiento anterior; mientra tanto su valor es *None*. - -La clase Inferencer Manager además, está equipada con métodos para actualizar las listas dado un id, y para realizar la inferencia dados los tipos almacenados. - -El Type Inferencer por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: - -1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un *ExpressionNode*, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el *manager*. -2. Infiere todos los tipos que pueda con la información recogida. -3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. - - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último rerrido asignando tipo Object a todos los AUTO_TYPES que no pudieron ser inferidos. - -> Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista *conforms_to* contiene a otro tipo distinto de Object o su lista *conformed_by* contiene al menos un tipo. - -Por último se realiza un nuevo recorrido del AST con el Type Checker para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los AUTO_TYPES sustituidos por el tipo inferido. - -## Autores ✒️ - -- **Carmen Irene Cabrera Rodríguez** - [cicr99](https://github.com/cicr99) -- **Enrique Martínez González** - [kikeXD](https://github.com/kikeXD) - -## Licencia - -Este proyecto se encuentra bajo la Licencia (MIT License) - ver el archivo [LICENSE.md](LICENSE.md) para más detalles. diff --git a/cmp/ast.py b/src/cmp/ast.py similarity index 100% rename from cmp/ast.py rename to src/cmp/ast.py diff --git a/cmp/automata.py b/src/cmp/automata.py similarity index 100% rename from cmp/automata.py rename to src/cmp/automata.py diff --git a/cmp/evaluation.py b/src/cmp/evaluation.py similarity index 100% rename from cmp/evaluation.py rename to src/cmp/evaluation.py diff --git a/cmp/formatter.py b/src/cmp/formatter.py similarity index 100% rename from cmp/formatter.py rename to src/cmp/formatter.py diff --git a/cmp/grammar.py b/src/cmp/grammar.py similarity index 100% rename from cmp/grammar.py rename to src/cmp/grammar.py diff --git a/cmp/lexer.py b/src/cmp/lexer.py similarity index 100% rename from cmp/lexer.py rename to src/cmp/lexer.py diff --git a/cmp/nfa_dfa.py b/src/cmp/nfa_dfa.py similarity index 100% rename from cmp/nfa_dfa.py rename to src/cmp/nfa_dfa.py diff --git a/cmp/pycompiler.py b/src/cmp/pycompiler.py similarity index 100% rename from cmp/pycompiler.py rename to src/cmp/pycompiler.py diff --git a/cmp/regex.py b/src/cmp/regex.py similarity index 100% rename from cmp/regex.py rename to src/cmp/regex.py diff --git a/cmp/semantic.py b/src/cmp/semantic.py similarity index 100% rename from cmp/semantic.py rename to src/cmp/semantic.py diff --git a/cmp/tools.py b/src/cmp/tools.py similarity index 100% rename from cmp/tools.py rename to src/cmp/tools.py diff --git a/cmp/type_builder.py b/src/cmp/type_builder.py similarity index 100% rename from cmp/type_builder.py rename to src/cmp/type_builder.py diff --git a/cmp/type_checker.py b/src/cmp/type_checker.py similarity index 100% rename from cmp/type_checker.py rename to src/cmp/type_checker.py diff --git a/cmp/type_collector.py b/src/cmp/type_collector.py similarity index 100% rename from cmp/type_collector.py rename to src/cmp/type_collector.py diff --git a/cmp/type_inferencer.py b/src/cmp/type_inferencer.py similarity index 100% rename from cmp/type_inferencer.py rename to src/cmp/type_inferencer.py diff --git a/cmp/utils.py b/src/cmp/utils.py similarity index 100% rename from cmp/utils.py rename to src/cmp/utils.py diff --git a/cmp/visitor.py b/src/cmp/visitor.py similarity index 100% rename from cmp/visitor.py rename to src/cmp/visitor.py diff --git a/main.py b/src/main.py similarity index 100% rename from main.py rename to src/main.py diff --git a/test_cases/cyclic_inheritance.txt b/test_cases/cyclic_inheritance.txt deleted file mode 100644 index c7b56a726..000000000 --- a/test_cases/cyclic_inheritance.txt +++ /dev/null @@ -1,16 +0,0 @@ - -text = ''' -class C inherits B { } ; -class A inherits B { } ; -class B inherits A { } ; -class C { } ; -class D inherits E { } ; -class E inherits F { } ; -class F inherits D { } ; -class G inherits F { } ; -''' - -text = ''' -class B inherits B { } ; -class A inherits B { } ; -''' \ No newline at end of file diff --git a/test_cases/inference.txt b/test_cases/inference.txt deleted file mode 100644 index 9a2398480..000000000 --- a/test_cases/inference.txt +++ /dev/null @@ -1,152 +0,0 @@ -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- c . concat ( a ) ; - 1 ; - } } ; - } ; - -''' - -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- c . substr ( 0 , a ) ; - } } ; - } ; - -''' - -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- isvoid a ; - a ; - } } ; - } ; - -''' - -se_jodio_el_self = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - case self of - x : Int => x ; - y : AUTO_TYPE => y ; - esac ; - } } ; - } ; - -''' - -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- a <- isvoid a ; - 1 ; - } } ; - } ; - -''' - -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- foo ( a ) ; - 1 ; - } } ; - - foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; - } ; - -''' - -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- foo ( a ) ; - b ; - } } ; - - foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; - } ; - -''' - -text = ''' - class B { - k : AUTO_TYPE ; - } ; - - class A inherits B { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b <- k + 1 ; - b ; - } } ; - - foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; - } ; - -''' - -text = ''' - class B { - k : AUTO_TYPE ; - } ; - - class A inherits B { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b + k + a ; - 1 ; - } } ; - - foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; - } ; - -''' - -text = ''' - class B { - k : AUTO_TYPE ; - } ; - - class A inherits B { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - c : String ; - f ( ) : Int { { - b + a ; - k <- b . copy ( ) ; - } } ; - - foo ( k : AUTO_TYPE ) : AUTO_TYPE { k <- a } ; - } ; - -''' \ No newline at end of file diff --git a/test_cases/let_inference.txt b/test_cases/let_inference.txt deleted file mode 100644 index b25763c1f..000000000 --- a/test_cases/let_inference.txt +++ /dev/null @@ -1,35 +0,0 @@ -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - - f ( ) : Int { { - let x : String , x : AUTO_TYPE <- a + b in x ; - } } ; - } ; - -''' - -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - - f ( ) : Int { { - let x : String , x : AUTO_TYPE <- a + b , self : AUTO_TYPE , x : AUTO_TYPE <- let c : AUTO_TYPE in a in x ; - } } ; - } ; - -''' - -text = ''' - class A { - b : AUTO_TYPE ; - a : AUTO_TYPE ; - - f ( ) : AUTO_TYPE { { - let x : String , x : AUTO_TYPE <- a + b , self : AUTO_TYPE , x : AUTO_TYPE <- let c : AUTO_TYPE in a in x ; - } } ; - } ; - -''' \ No newline at end of file diff --git a/test_cases/orientation_examples.txt b/test_cases/orientation_examples.txt deleted file mode 100644 index 876492428..000000000 --- a/test_cases/orientation_examples.txt +++ /dev/null @@ -1,91 +0,0 @@ -class Main inherits IO { - main ( ) : AUTO_TYPE { - let x : AUTO_TYPE <- 3 + 2 in { - case x of - y : Int => out_string ( "Ok" ) ; - esac ; - } - } ; -} ; - -class Point { - x : AUTO_TYPE ; - y : AUTO_TYPE ; - init ( n : Int , m : Int ) : SELF_TYPE { { - x <- n ; - y <- m ; - } } ; -} ; - - -class Point { - x : AUTO_TYPE ; - y : AUTO_TYPE ; - succ ( n : Int ) : AUTO_TYPE { n + 1 } ; -} ; - - -class Point { - x : AUTO_TYPE ; - y : AUTO_TYPE ; - succ ( n : AUTO_TYPE ) : AUTO_TYPE { n + 1 } ; -} ; - - -######################################## - -class Point { - succ ( n : AUTO_TYPE ) : AUTO_TYPE { n + 1 } ; - translate ( n : AUTO_TYPE , m : AUTO_TYPE ) : SELF_TYPE { self } ; -} ; - -class Main { - step ( p : AUTO_TYPE ) : AUTO_TYPE { p . translate ( 1 , 1 ) } ; - - main ( ) : Object { - let p : AUTO_TYPE <- new Point in { - step ( p ) ; - } - } ; -} ; - - -######################################### - -class A { - fact ( n : AUTO_TYPE ) : AUTO_TYPE { - if ( n < 0 ) then 1 else n * fact ( n - 1 ) fi - } ; -} ; - - -######################################### - -class A { - ackermann ( m : AUTO_TYPE , n : AUTO_TYPE ) : AUTO_TYPE { - if ( m = 0 ) then n + 1 else - if ( n = 0 ) then ackermann ( m - 1 , 1 ) else - ackermann ( m - 1 , ackermann ( m , n - 1 ) ) - fi - fi - } ; -} ; - - -######################################## - -class A { - f ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { - if ( a = 1 ) then b else - g ( a + 1 , b / 2 ) - fi - } ; - g ( a : AUTO_TYPE , b : AUTO_TYPE ) : AUTO_TYPE { - if ( b = 1 ) then a else - f ( a / 2 , b + 1 ) - fi - } ; -} ; - - - diff --git a/test_cases/test1.txt b/test_cases/test1.txt deleted file mode 100644 index 0d4d983e1..000000000 --- a/test_cases/test1.txt +++ /dev/null @@ -1,91 +0,0 @@ - -//Expected errors -text = ''' - class A { - a : Z ; - suma ( a : int , b : B ) : int { - a + b - } ; - b : int <- 9 ; - c : C ; - } ; - - class B inherits A { - c : A ; - f ( d : int , a : A ) : void { - { - let f : int <- 8 in f + 3 * d ; - c <- suma ( 5 , f ) ; - } - } ; - z : int ; - } ; - - class C inherits Z { - } ; -''' - - - -//No method main in Main class, even though Main class inherits from A, which does contain a method main -text = ''' - class A { - a : C ; - suma ( a : int , b : B ) : int { - a + b - } ; - b : int <- 9 ; - c : C ; - main ( ) : SELF_TYPE { - a . copy ( ) - } ; - } ; - - class B inherits A { - f ( d : int , a : A ) : B { - { - let f : int <- 8 in f + 3 * d ; - c <- suma ( 5 , f ) ; - } - } ; - z : int ; - } ; - - class C inherits A { - } ; - - class Main inherits A { } ; -''' - - - -//No errors -text = ''' - class A { - a : C ; - suma ( a : int , b : B ) : int { - a + b - } ; - b : int <- 9 ; - c : C ; - } ; - - class B inherits A { - f ( d : int , a : A ) : B { - { - let f : int <- 8 in f + 3 * d ; - c <- suma ( 5 , f ) ; - } - } ; - z : int ; - } ; - - class C inherits A { - } ; - - class Main inherits A { - main ( ) : SELF_TYPE { - a . copy ( ) - } ; - } ; -''' \ No newline at end of file From 22f25c95252f1f7b8fa9ef0b46466b99a93e3f87 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 15 Aug 2021 13:51:10 -0400 Subject: [PATCH 077/138] Create packages within the project and define work guidelines --- src/compiler/__init__.py | 0 src/compiler/cmp/__init__.py | 0 src/{ => compiler}/cmp/ast.py | 0 src/{ => compiler}/cmp/automata.py | 0 src/{ => compiler}/cmp/evaluation.py | 4 ++-- src/{ => compiler}/cmp/grammar.py | 4 ++-- src/{ => compiler}/cmp/nfa_dfa.py | 0 src/{ => compiler}/cmp/pycompiler.py | 0 src/{ => compiler}/cmp/regex.py | 16 +++++++------- src/{ => compiler}/cmp/semantic.py | 0 src/{ => compiler}/cmp/tools.py | 10 ++++----- src/{ => compiler}/cmp/utils.py | 2 +- src/compiler/lexer/__init__.py | 0 src/{cmp => compiler/lexer}/lexer.py | 8 +++---- src/compiler/visitors/__init__.py | 0 src/{cmp => compiler/visitors}/formatter.py | 7 ++++-- .../visitors}/type_builder.py | 6 ++--- .../visitors}/type_checker.py | 14 ++++++------ .../visitors}/type_collector.py | 6 ++--- .../visitors}/type_inferencer.py | 14 ++++++------ src/{cmp => compiler/visitors}/visitor.py | 0 src/guide.md | 19 ++++++++++++++++ src/main.py | 22 +++++++++---------- 23 files changed, 77 insertions(+), 55 deletions(-) create mode 100644 src/compiler/__init__.py create mode 100644 src/compiler/cmp/__init__.py rename src/{ => compiler}/cmp/ast.py (100%) rename src/{ => compiler}/cmp/automata.py (100%) rename src/{ => compiler}/cmp/evaluation.py (93%) rename src/{ => compiler}/cmp/grammar.py (99%) rename src/{ => compiler}/cmp/nfa_dfa.py (100%) rename src/{ => compiler}/cmp/pycompiler.py (100%) rename src/{ => compiler}/cmp/regex.py (84%) rename src/{ => compiler}/cmp/semantic.py (100%) rename src/{ => compiler}/cmp/tools.py (98%) rename src/{ => compiler}/cmp/utils.py (99%) create mode 100644 src/compiler/lexer/__init__.py rename src/{cmp => compiler/lexer}/lexer.py (97%) create mode 100644 src/compiler/visitors/__init__.py rename src/{cmp => compiler/visitors}/formatter.py (93%) rename src/{cmp => compiler/visitors}/type_builder.py (94%) rename src/{cmp => compiler/visitors}/type_checker.py (96%) rename src/{cmp => compiler/visitors}/type_collector.py (95%) rename src/{cmp => compiler/visitors}/type_inferencer.py (96%) rename src/{cmp => compiler/visitors}/visitor.py (100%) create mode 100644 src/guide.md diff --git a/src/compiler/__init__.py b/src/compiler/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/compiler/cmp/__init__.py b/src/compiler/cmp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cmp/ast.py b/src/compiler/cmp/ast.py similarity index 100% rename from src/cmp/ast.py rename to src/compiler/cmp/ast.py diff --git a/src/cmp/automata.py b/src/compiler/cmp/automata.py similarity index 100% rename from src/cmp/automata.py rename to src/compiler/cmp/automata.py diff --git a/src/cmp/evaluation.py b/src/compiler/cmp/evaluation.py similarity index 93% rename from src/cmp/evaluation.py rename to src/compiler/cmp/evaluation.py index 034033567..65e6125ac 100644 --- a/src/cmp/evaluation.py +++ b/src/compiler/cmp/evaluation.py @@ -1,5 +1,5 @@ -from cmp.pycompiler import EOF -from cmp.tools import ShiftReduceParser +from .pycompiler import EOF +from .tools import ShiftReduceParser def evaluate_reverse_parse(right_parse, operations, tokens): if not right_parse or not operations or not tokens: diff --git a/src/cmp/grammar.py b/src/compiler/cmp/grammar.py similarity index 99% rename from src/cmp/grammar.py rename to src/compiler/cmp/grammar.py index cbe7aaa2f..3626f132f 100644 --- a/src/cmp/grammar.py +++ b/src/compiler/cmp/grammar.py @@ -1,5 +1,5 @@ -from cmp.pycompiler import Grammar -from cmp.ast import * +from .pycompiler import Grammar +from .ast import * # grammar G = Grammar() diff --git a/src/cmp/nfa_dfa.py b/src/compiler/cmp/nfa_dfa.py similarity index 100% rename from src/cmp/nfa_dfa.py rename to src/compiler/cmp/nfa_dfa.py diff --git a/src/cmp/pycompiler.py b/src/compiler/cmp/pycompiler.py similarity index 100% rename from src/cmp/pycompiler.py rename to src/compiler/cmp/pycompiler.py diff --git a/src/cmp/regex.py b/src/compiler/cmp/regex.py similarity index 84% rename from src/cmp/regex.py rename to src/compiler/cmp/regex.py index 04421669b..82e490c3e 100644 --- a/src/cmp/regex.py +++ b/src/compiler/cmp/regex.py @@ -1,11 +1,11 @@ -from cmp.pycompiler import Grammar -from cmp.utils import Token -from cmp.tools import evaluate_parse -from cmp.tools import metodo_predictivo_no_recursivo -from cmp.tools import nfa_to_dfa -from cmp.tools import automata_union, automata_concatenation, automata_closure, automata_minimization -from cmp.tools import get_printer -from cmp.tools import Node, AtomicNode, UnaryNode, BinaryNode, EpsilonNode, SymbolNode, ClosureNode, UnionNode, ConcatNode +from .pycompiler import Grammar +from .utils import Token +from .tools import evaluate_parse +from .tools import metodo_predictivo_no_recursivo +from .tools import nfa_to_dfa +from .tools import automata_union, automata_concatenation, automata_closure, automata_minimization +from .tools import get_printer +from .tools import Node, AtomicNode, UnaryNode, BinaryNode, EpsilonNode, SymbolNode, ClosureNode, UnionNode, ConcatNode from pprint import pprint as pp import pydot diff --git a/src/cmp/semantic.py b/src/compiler/cmp/semantic.py similarity index 100% rename from src/cmp/semantic.py rename to src/compiler/cmp/semantic.py diff --git a/src/cmp/tools.py b/src/compiler/cmp/tools.py similarity index 98% rename from src/cmp/tools.py rename to src/compiler/cmp/tools.py index eea67bdec..b4b5f7186 100644 --- a/src/cmp/tools.py +++ b/src/compiler/cmp/tools.py @@ -1,8 +1,8 @@ -from cmp.utils import ShiftReduceParser, ContainerSet, DisjointSet -from cmp.pycompiler import Item, EOF -from cmp.automata import State -from cmp.nfa_dfa import DFA, NFA -import cmp.visitor as visitor +from .utils import ShiftReduceParser, ContainerSet, DisjointSet +from .pycompiler import Item, EOF +from .automata import State +from .nfa_dfa import DFA, NFA +import compiler.visitors.visitor as visitor from itertools import islice diff --git a/src/cmp/utils.py b/src/compiler/cmp/utils.py similarity index 99% rename from src/cmp/utils.py rename to src/compiler/cmp/utils.py index 0980ecd8a..329502679 100644 --- a/src/cmp/utils.py +++ b/src/compiler/cmp/utils.py @@ -1,4 +1,4 @@ -from 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): diff --git a/src/compiler/lexer/__init__.py b/src/compiler/lexer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cmp/lexer.py b/src/compiler/lexer/lexer.py similarity index 97% rename from src/cmp/lexer.py rename to src/compiler/lexer/lexer.py index a33965f4c..b3cb4a167 100644 --- a/src/cmp/lexer.py +++ b/src/compiler/lexer/lexer.py @@ -1,7 +1,7 @@ -from cmp.grammar import * -from cmp.utils import Token -from cmp.regex import Regex -from cmp.automata import State +from ..cmp.grammar import * +from ..cmp.utils import Token +from ..cmp.regex import Regex +from ..cmp.automata import State class Lexer: diff --git a/src/compiler/visitors/__init__.py b/src/compiler/visitors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cmp/formatter.py b/src/compiler/visitors/formatter.py similarity index 93% rename from src/cmp/formatter.py rename to src/compiler/visitors/formatter.py index 5102d4e73..7e29ca89c 100644 --- a/src/cmp/formatter.py +++ b/src/compiler/visitors/formatter.py @@ -1,5 +1,8 @@ -import cmp.visitor as visitor -from cmp.ast import * +import compiler.visitors.visitor as visitor +from ..cmp.ast import ProgramNode, ClassDeclarationNode, FuncDeclarationNode +from ..cmp.ast import AttrDeclarationNode, AssignNode, CallNode +from ..cmp.ast import CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode, InstantiateNode +from ..cmp.ast import UnaryNode, BinaryNode, AtomicNode class FormatVisitor(object): @visitor.on('node') diff --git a/src/cmp/type_builder.py b/src/compiler/visitors/type_builder.py similarity index 94% rename from src/cmp/type_builder.py rename to src/compiler/visitors/type_builder.py index 8a6d67191..c6b08ef1f 100644 --- a/src/cmp/type_builder.py +++ b/src/compiler/visitors/type_builder.py @@ -1,6 +1,6 @@ -import cmp.visitor as visitor -from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from cmp.semantic import SemanticError, ErrorType, InferencerManager, AutoType, SelfType +import compiler.visitors.visitor as visitor +from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from ..cmp.semantic import SemanticError, ErrorType, InferencerManager, AutoType, SelfType SELF_IS_READONLY = 'Variable "self" is read-only.' diff --git a/src/cmp/type_checker.py b/src/compiler/visitors/type_checker.py similarity index 96% rename from src/cmp/type_checker.py rename to src/compiler/visitors/type_checker.py index 2a880d92d..adfa1c351 100644 --- a/src/cmp/type_checker.py +++ b/src/compiler/visitors/type_checker.py @@ -1,10 +1,10 @@ -import cmp.visitor as visitor -from cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA -from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode -from cmp.ast import ArithmeticNode, ComparisonNode, EqualNode -from cmp.ast import VoidNode, NotNode, NegNode -from cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode +import compiler.visitors.visitor as visitor +from ..cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA +from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from ..cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode +from ..cmp.ast import VoidNode, NotNode, NegNode +from ..cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' diff --git a/src/cmp/type_collector.py b/src/compiler/visitors/type_collector.py similarity index 95% rename from src/cmp/type_collector.py rename to src/compiler/visitors/type_collector.py index 4aa7a9808..eff4d24de 100644 --- a/src/cmp/type_collector.py +++ b/src/compiler/visitors/type_collector.py @@ -1,6 +1,6 @@ -import cmp.visitor as visitor -from cmp.semantic import SemanticError, Type, Context, ObjectType, IOType, StringType, IntType, BoolType, SelfType, AutoType -from cmp.ast import ProgramNode, ClassDeclarationNode +import compiler.visitors.visitor as visitor +from ..cmp.semantic import SemanticError, Type, Context, ObjectType, IOType, StringType, IntType, BoolType, SelfType, AutoType +from ..cmp.ast import ProgramNode, ClassDeclarationNode built_in_types = [] diff --git a/src/cmp/type_inferencer.py b/src/compiler/visitors/type_inferencer.py similarity index 96% rename from src/cmp/type_inferencer.py rename to src/compiler/visitors/type_inferencer.py index 0fdca7913..2246a2385 100644 --- a/src/cmp/type_inferencer.py +++ b/src/compiler/visitors/type_inferencer.py @@ -1,10 +1,10 @@ -import cmp.visitor as visitor -from cmp.semantic import SemanticError, ErrorType, SelfType, AutoType, LCA -from cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode -from cmp.ast import ArithmeticNode, ComparisonNode, EqualNode -from cmp.ast import VoidNode, NotNode, NegNode -from cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode +import compiler.visitors.visitor as visitor +from ..cmp.semantic import SemanticError, ErrorType, SelfType, AutoType, LCA +from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from ..cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode +from ..cmp.ast import VoidNode, NotNode, NegNode +from ..cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode AUTOTYPE_ERROR = 'Incorrect use of AUTO_TYPE' diff --git a/src/cmp/visitor.py b/src/compiler/visitors/visitor.py similarity index 100% rename from src/cmp/visitor.py rename to src/compiler/visitors/visitor.py diff --git a/src/guide.md b/src/guide.md new file mode 100644 index 000000000..d45a19abe --- /dev/null +++ b/src/guide.md @@ -0,0 +1,19 @@ +# Work guidelines + +Here is a guide on how to organize the team work. In the [Documentation](#documentation) section you can find a share resources for everyone to study and learn. + +## Documentation + +- [Python packages](https://dev.to/codemouse92/dead-simple-python-project-structure-and-imports-38c6) to use modules in the project + +## Tasks + +No work is going to be pushed to main directly. Each task we develope should be done in a separate branch and reviewed by the others in order to merge it. + +We'll have 3 sections for the tasks: **TODO, IN PROGRESS, DONE**. Each new task goes into **TODO** section. Once someone picks a task, it should be assigned to it. When that person starts working on the task, it should be moved into **IN PROGRESS** section. And of course once the task is finished (when it is merged into main branch), it should be moved into **DONE** section. + +### TODO + +### IN PROGRESS + +### DONE diff --git a/src/main.py b/src/main.py index 4cfc683cb..8804a2af7 100644 --- a/src/main.py +++ b/src/main.py @@ -1,13 +1,13 @@ import streamlit as st -from cmp.grammar import G -from cmp.lexer import tokenize_text, pprint_tokens -from cmp.tools import LR1Parser -from cmp.evaluation import evaluate_reverse_parse -from cmp.formatter import FormatVisitor -from cmp.type_collector import TypeCollector -from cmp.type_builder import TypeBuilder -from cmp.type_checker import TypeChecker -from cmp.type_inferencer import TypeInferencer +from compiler.cmp.grammar import G +from compiler.lexer.lexer import tokenize_text, pprint_tokens +from compiler.cmp.tools import LR1Parser +from compiler.cmp.evaluation import evaluate_reverse_parse +from compiler.visitors.formatter import FormatVisitor +from compiler.visitors.type_collector import TypeCollector +from compiler.visitors.type_builder import TypeBuilder +from compiler.visitors.type_checker import TypeChecker +from compiler.visitors.type_inferencer import TypeInferencer def run_pipeline(G, text): @@ -174,5 +174,5 @@ def main(G): st.text(tree) if __name__ == '__main__': - main(G) - # ast = run_pipeline(G, text) \ No newline at end of file + # main(G) + ast = run_pipeline(G, text) \ No newline at end of file From 0abee00fd18a14cd6241b36e6c7b3bb2baace901 Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 5 Sep 2021 17:18:25 -0400 Subject: [PATCH 078/138] Add tokens to lex and split idx into objectid and typeid --- src/compiler/cmp/grammar.py | 34 ++++++------- src/compiler/lexer/lex.py | 99 +++++++++++++++++++++++++++++++++++++ src/compiler/lexer/lexer.py | 16 +++--- 3 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 src/compiler/lexer/lex.py diff --git a/src/compiler/cmp/grammar.py b/src/compiler/cmp/grammar.py index 3626f132f..b8a4cc895 100644 --- a/src/compiler/cmp/grammar.py +++ b/src/compiler/cmp/grammar.py @@ -27,7 +27,7 @@ isvoid, notx = G.Terminals('isvoid not') semi, colon, comma, dot, opar, cpar, ocur, ccur, larrow, rarrow, at = G.Terminals('; : , . ( ) { } <- => @') equal, plus, minus, star, div, less, leq, neg = G.Terminals('= + - * / < <= ~') -idx, num, stringx, boolx, new = G.Terminals('id int string bool new') +objectid, typeid, num, stringx, boolx, new = G.Terminals('objectid typeid int string bool new') # productions @@ -37,22 +37,22 @@ class_list %= def_class, lambda h,s: [s[1]] class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] -def_class %= classx + idx + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[4]) -def_class %= classx + idx + inherits + idx + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[6], s[4]) +def_class %= classx + objectid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[4]) +def_class %= classx + objectid + inherits + objectid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[6], s[4]) feature_list %= G.Epsilon, lambda h,s: [] feature_list %= def_attr + feature_list, lambda h,s: [s[1]] + s[2] feature_list %= def_func + feature_list, lambda h,s: [s[1]] + s[2] -def_attr %= idx + colon + idx + semi, lambda h,s: AttrDeclarationNode(s[1], s[3]) -def_attr %= idx + colon + idx + larrow + expr + semi, lambda h,s: AttrDeclarationNode(s[1], s[3], s[5]) +def_attr %= typeid + colon + objectid + semi, lambda h,s: AttrDeclarationNode(s[1], s[3]) +def_attr %= typeid + colon + objectid + larrow + expr + semi, lambda h,s: AttrDeclarationNode(s[1], s[3], s[5]) -def_func %= idx + opar + cpar + colon + idx + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], [], s[5], s[7]) -def_func %= idx + opar + param_list + cpar + colon + idx + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) +def_func %= typeid + opar + cpar + colon + objectid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], [], s[5], s[7]) +def_func %= typeid + opar + param_list + cpar + colon + objectid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) param_list %= param, lambda h,s: [s[1]] param_list %= param + comma + param_list, lambda h,s: [s[1]] + s[3] -param %= idx + colon + idx, lambda h,s: (s[1], s[3]) +param %= typeid + colon + objectid, lambda h,s: (s[1], s[3]) expr %= comp, lambda h,s: s[1] expr %= s_comp, lambda h,s: s[1] @@ -83,12 +83,12 @@ atom %= num, lambda h,s: ConstantNumNode(s[1]) atom %= stringx, lambda h,s: ConstantStringNode(s[1]) atom %= boolx, lambda h,s: ConstantBoolNode(s[1]) -atom %= idx, lambda h,s: VariableNode(s[1]) -atom %= new + idx, lambda h,s: InstantiateNode(s[2]) +atom %= typeid, lambda h,s: VariableNode(s[1]) +atom %= new + objectid, lambda h,s: InstantiateNode(s[2]) -func_call %= idx + opar + arg_list + cpar, lambda h,s: CallNode(VariableNode('self'), s[1], s[3]) -func_call %= factor + dot + idx + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[3], s[5]) -func_call %= factor + at + idx + dot + idx + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[5], s[7], s[3]) +func_call %= typeid + opar + arg_list + cpar, lambda h,s: CallNode(VariableNode('self'), s[1], s[3]) +func_call %= factor + dot + typeid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[3], s[5]) +func_call %= factor + at + objectid + dot + typeid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[5], s[7], s[3]) arg_list %= G.Epsilon, lambda h,s: [] arg_list %= args, lambda h,s: s[1] @@ -98,7 +98,7 @@ case_def %= case + expr + of + branch_list + esac, lambda h,s: CaseNode(s[2], s[4]) branch_list %= branch, lambda h,s: [s[1]] branch_list %= branch + branch_list, lambda h,s: [s[1]] + s[2] -branch %= idx + colon + idx + rarrow + expr + semi, lambda h,s: (s[1], s[3], s[5]) +branch %= typeid + colon + objectid + rarrow + expr + semi, lambda h,s: (s[1], s[3], s[5]) block_def %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2]) expr_list %= expr + semi, lambda h,s: [s[1]] @@ -130,7 +130,7 @@ let_def %= let + iden_list + inx + expr, lambda h,s: LetNode(s[2], s[4]) iden_list %= iden, lambda h,s: [s[1]] iden_list %= iden + comma + iden_list, lambda h,s: [s[1]] + s[3] -iden %= idx + colon + idx, lambda h,s: (s[1], s[3], None) -iden %= idx + colon + idx + larrow + expr, lambda h,s: (s[1], s[3], s[5]) +iden %= typeid + colon + objectid, lambda h,s: (s[1], s[3], None) +iden %= typeid + colon + objectid + larrow + expr, lambda h,s: (s[1], s[3], s[5]) -assign_def %= idx + larrow + expr, lambda h,s: AssignNode(s[1], s[3]) \ No newline at end of file +assign_def %= typeid + larrow + expr, lambda h,s: AssignNode(s[1], s[3]) \ No newline at end of file diff --git a/src/compiler/lexer/lex.py b/src/compiler/lexer/lex.py new file mode 100644 index 000000000..b702fd95c --- /dev/null +++ b/src/compiler/lexer/lex.py @@ -0,0 +1,99 @@ +import ply.lex as lex +import re +from ..cmp.grammar import G + + +class CoolLexer(object): + reserved = { + 'class': 'CLASS', + 'inherits': 'INHERITS', + 'let': 'LET', + 'in': 'IN', + 'case': 'CASE', + 'of': 'OF', + 'esac': 'ESAC', + 'while': 'WHILE', + 'loop': 'LOOP', + 'pool': 'POOL', + 'if': 'IF', + 'then': 'THEN', + 'else': 'ELSE', + 'fi': 'FI', + 'isvoid': 'ISVOID', + 'not': 'NOT', + 'new': 'NEW', + 'true': 'TRUE', + 'false': 'FALSE' + } + + tokens = [ + 'SEMICOLON', + 'COLON', + 'COMMA', + 'DOT', + 'OPAR', + 'CPAR', + 'OCUR', + 'CCUR', + 'LARROW', + 'RARROW', + 'AT', + 'EQUAL', + 'PLUS', + 'MINUS', + 'STAR', + 'DIV', + 'LESS', + 'LEQ', + 'NEG', + 'TYPEIDENTIFIER', + 'OBJECTIDENTIFIER', + 'NUMBER', + 'STRING' + ] + list(reserved.values()) + + token_type = { + 'CLASS' : G.classx, + 'INHERITS' : G.inherits, + 'LET' : G.let, + 'IN' : G.inx, + 'CASE': G.case, + 'OF': G.of, + 'ESAC': G.esac, + 'WHILE': G.whilex, + 'LOOP': G.loop, + 'POOL': G.pool, + 'IF': G.ifx, + 'THEN': G.then, + 'ELSE': G.elsex, + 'FI': G.fi, + 'ISVOID': G.isvoid, + 'NOT': G.notx, + 'NEW': G.new, + 'TRUE': G.boolx, + 'FALSE': G.boolx, + 'SEMICOLON': G.semi, + 'COLON': G.colon, + 'COMMA': G.comma, + 'DOT': G.dot, + 'OPAR': G.opar, + 'CPAR': G.cpar, + 'OCUR': G.ocur, + 'CCUR': G.ccur, + 'LARROW': G.larrow, + 'RARROW': G.rarrow, + 'AT': G.at, + 'EQUAL': G.equal, + 'PLUS': G.plus, + 'MINUS': G.minus, + 'STAR': G.star, + 'DIV': G.div, + 'LESS': G.less, + 'LEQ': G.leq, + 'NEG': G.neg, + 'TYPEIDENTIFIER': G.typeid, + 'OBJECTIDENTIFIER': G.objectid, + 'NUMBER': G.num, + 'STRING': G.stringx + } + diff --git a/src/compiler/lexer/lexer.py b/src/compiler/lexer/lexer.py index b3cb4a167..faad57696 100644 --- a/src/compiler/lexer/lexer.py +++ b/src/compiler/lexer/lexer.py @@ -74,13 +74,6 @@ def _tokenize(self, text): def __call__(self, text): return [ Token(lex, ttype) for lex, ttype in self._tokenize(text) ] - -nonzero_digits = '|'.join(str(n) for n in range(1,10)) -alp = [chr(n) for n in range(ord('a'),ord('z') + 1)] -alp.extend([chr(n) for n in range(ord('A'),ord('Z') + 1)]) -letters = '|'.join(alp) - - def eliminate_regex_conflict(text): result = '' for i in text: @@ -105,6 +98,10 @@ def get_all_combinations(text, first_lower=False): return result +nonzero_digits = '|'.join(str(n) for n in range(1,10)) +alp = [chr(n) for n in range(ord('a'),ord('z') + 1)] +alp.extend([chr(n) for n in range(ord('A'),ord('Z') + 1)]) +letters = '|'.join(alp) alphabet_before = [eliminate_regex_conflict(chr(n)) for n in range(1, ord('~') + 1) if n != 34] alphabet = '|'.join(alphabet_before) @@ -113,8 +110,9 @@ def tokenize_text(text): [(t, get_all_combinations(t.Name)) for t in G.terminals if t not in { idx, num, stringx, boolx }] + [(boolx, f'({get_all_combinations("true", True)})|({get_all_combinations("false", True)})'), (num, f'0|(({nonzero_digits})(0|{nonzero_digits})*)'), - ('salto', '\n'), - ('space', '( |\t)( |\t)*'), + ('space', '') + # ('salto', '\n'), + # ('space', '( |\t)( |\t)*'), (idx, f'({letters}|_)(_|{letters}|0|{nonzero_digits})*'), (stringx, f'"({alphabet})*"')], G.EOF) From d7bfcd279872a2981be4e0d445bc9168bb26ee8b Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 5 Sep 2021 19:39:50 -0400 Subject: [PATCH 079/138] fix grammar --- src/compiler/cmp/grammar.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/compiler/cmp/grammar.py b/src/compiler/cmp/grammar.py index b8a4cc895..269119f30 100644 --- a/src/compiler/cmp/grammar.py +++ b/src/compiler/cmp/grammar.py @@ -27,7 +27,7 @@ isvoid, notx = G.Terminals('isvoid not') semi, colon, comma, dot, opar, cpar, ocur, ccur, larrow, rarrow, at = G.Terminals('; : , . ( ) { } <- => @') equal, plus, minus, star, div, less, leq, neg = G.Terminals('= + - * / < <= ~') -objectid, typeid, num, stringx, boolx, new = G.Terminals('objectid typeid int string bool new') +typeid, objectid, num, stringx, boolx, new = G.Terminals('typeid objectid int string bool new') # productions @@ -37,22 +37,22 @@ class_list %= def_class, lambda h,s: [s[1]] class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] -def_class %= classx + objectid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[4]) -def_class %= classx + objectid + inherits + objectid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[6], s[4]) +def_class %= classx + typeid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[4]) +def_class %= classx + typeid + inherits + typeid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[6], s[4]) feature_list %= G.Epsilon, lambda h,s: [] feature_list %= def_attr + feature_list, lambda h,s: [s[1]] + s[2] feature_list %= def_func + feature_list, lambda h,s: [s[1]] + s[2] -def_attr %= typeid + colon + objectid + semi, lambda h,s: AttrDeclarationNode(s[1], s[3]) -def_attr %= typeid + colon + objectid + larrow + expr + semi, lambda h,s: AttrDeclarationNode(s[1], s[3], s[5]) +def_attr %= objectid + colon + typeid + semi, lambda h,s: AttrDeclarationNode(s[1], s[3]) +def_attr %= objectid + colon + typeid + larrow + expr + semi, lambda h,s: AttrDeclarationNode(s[1], s[3], s[5]) -def_func %= typeid + opar + cpar + colon + objectid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], [], s[5], s[7]) -def_func %= typeid + opar + param_list + cpar + colon + objectid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) +def_func %= objectid + opar + cpar + colon + typeid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], [], s[5], s[7]) +def_func %= objectid + opar + param_list + cpar + colon + typeid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) param_list %= param, lambda h,s: [s[1]] param_list %= param + comma + param_list, lambda h,s: [s[1]] + s[3] -param %= typeid + colon + objectid, lambda h,s: (s[1], s[3]) +param %= objectid + colon + typeid, lambda h,s: (s[1], s[3]) expr %= comp, lambda h,s: s[1] expr %= s_comp, lambda h,s: s[1] @@ -83,12 +83,12 @@ atom %= num, lambda h,s: ConstantNumNode(s[1]) atom %= stringx, lambda h,s: ConstantStringNode(s[1]) atom %= boolx, lambda h,s: ConstantBoolNode(s[1]) -atom %= typeid, lambda h,s: VariableNode(s[1]) -atom %= new + objectid, lambda h,s: InstantiateNode(s[2]) +atom %= objectid, lambda h,s: VariableNode(s[1]) +atom %= new + typeid, lambda h,s: InstantiateNode(s[2]) -func_call %= typeid + opar + arg_list + cpar, lambda h,s: CallNode(VariableNode('self'), s[1], s[3]) -func_call %= factor + dot + typeid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[3], s[5]) -func_call %= factor + at + objectid + dot + typeid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[5], s[7], s[3]) +func_call %= objectid + opar + arg_list + cpar, lambda h,s: CallNode(VariableNode('self'), s[1], s[3]) +func_call %= factor + dot + objectid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[3], s[5]) +func_call %= factor + at + typeid + dot + objectid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[5], s[7], s[3]) arg_list %= G.Epsilon, lambda h,s: [] arg_list %= args, lambda h,s: s[1] @@ -98,7 +98,7 @@ case_def %= case + expr + of + branch_list + esac, lambda h,s: CaseNode(s[2], s[4]) branch_list %= branch, lambda h,s: [s[1]] branch_list %= branch + branch_list, lambda h,s: [s[1]] + s[2] -branch %= typeid + colon + objectid + rarrow + expr + semi, lambda h,s: (s[1], s[3], s[5]) +branch %= objectid + colon + typeid + rarrow + expr + semi, lambda h,s: (s[1], s[3], s[5]) block_def %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2]) expr_list %= expr + semi, lambda h,s: [s[1]] @@ -130,7 +130,7 @@ let_def %= let + iden_list + inx + expr, lambda h,s: LetNode(s[2], s[4]) iden_list %= iden, lambda h,s: [s[1]] iden_list %= iden + comma + iden_list, lambda h,s: [s[1]] + s[3] -iden %= typeid + colon + objectid, lambda h,s: (s[1], s[3], None) -iden %= typeid + colon + objectid + larrow + expr, lambda h,s: (s[1], s[3], s[5]) +iden %= objectid + colon + typeid, lambda h,s: (s[1], s[3], None) +iden %= objectid + colon + typeid + larrow + expr, lambda h,s: (s[1], s[3], s[5]) -assign_def %= typeid + larrow + expr, lambda h,s: AssignNode(s[1], s[3]) \ No newline at end of file +assign_def %= objectid + larrow + expr, lambda h,s: AssignNode(s[1], s[3]) \ No newline at end of file From e5836733041c7d3076d40e8e76dc1ada3a87e15a Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Mon, 6 Sep 2021 22:59:13 -0400 Subject: [PATCH 080/138] Add rules for most of the tokens --- src/compiler/lexer/lex.py | 149 +++++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/src/compiler/lexer/lex.py b/src/compiler/lexer/lex.py index b702fd95c..2f6f6056c 100644 --- a/src/compiler/lexer/lex.py +++ b/src/compiler/lexer/lex.py @@ -3,7 +3,17 @@ from ..cmp.grammar import G -class CoolLexer(object): +class CoolLexer(object): + + __init__(self): + self.count = 0 + self.build() + + states = ( + ('string','exclusive'), + ('comment','exclusive'), + ) + reserved = { 'class': 'CLASS', 'inherits': 'INHERITS', @@ -97,3 +107,140 @@ class CoolLexer(object): 'STRING': G.stringx } + def t_NUMBER(t): + r'\d+' + t.value = int(t.value) + self.add_column(t) + return t + + # Define a rule so we can track line numbers + def t_newline(t): + r'\n+' + t.lexer.lineno += len(t.value) + self.count = t.lexpos + len(t.value) + + # A string containing ignored characters (spaces and tabs) + t_ignore = ' \t\f\r\v' + + def t_COMMENTLINE(self, t): + r'--.*' + + def t_TYPEIDENTIFIER(self, t): + r'[A-Z][0-9A-Za-z_]*' + val = t.value.lower() + if val not in ['true', 'false']: + t.type = self.reserved.get(val, 'TYPEIDENTIFIER') + self.add_column(t) + return t + + def t_OBJECTIDENTIFIER(self, t): + r'[a-z][0-9A-Za-z_]*' + val = t.value.lower() + t.type = self.reserved.get(val, 'OBJECTIDENTIFIER') + self.add_column(t) + return t + + def t_SEMICOLON(self, t): + r';' + self.add_column(t) + return t + + def t_COLON(self, t): + r':' + self.add_column(t) + return t + + def t_COMMA(self, t): + r',' + self.add_column(t) + return t + + def t_DOT(self, t): + r'\.' + self.add_column(t) + return t + + def t_OPAR(self, t): + r'\(' + self.add_column(t) + return t + + def t_CPAR(self, t): + r'\)' + self.add_column(t) + return t + + def t_OCUR(self, t): + r'{' + self.add_column(t) + return t + + def t_CCUR(self, t): + r'}' + self.add_column(t) + return t + + def t_LARROW(self, t): + r'<-' + self.add_column(t) + return t + + def t_RARROW(self, t): + r'=>' + self.add_column(t) + return t + + def t_AT(self, t): + r'@' + self.add_column(t) + return t + + def t_EQUAL(self, t): + r'=' + self.add_column(t) + return t + + def t_PLUS(self, t): + r'\+' + self.add_column(t) + return t + + def t_MINUS(self, t): + r'-' + self.add_column(t) + return t + + def t_STAR(self, t): + r'\*' + self.add_column(t) + return t + + def t_DIV(self, t): + r'/' + self.add_column(t) + return t + + def t_LESS(self, t): + r'<' + self.add_column(t) + return t + + def t_LEQ(self, t): + r'<=' + self.add_column(t) + return t + + def T_NEG(self, t): + r'~' + self.add_column(t) + return t + + + # Build the lexer + def build(self,**kwargs): + self.lexer = lex.lex(module=self, **kwargs) + + + def add_column(self, t): + t.col = t.lexpos - self.count + From aa637b52b0a04a67f151205eb89ab6f2a7939f0a Mon Sep 17 00:00:00 2001 From: Carmen I Cabrera Date: Sun, 12 Sep 2021 00:27:09 -0400 Subject: [PATCH 081/138] Lexer rules finished --- src/compiler/lexer/lex.py | 102 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/src/compiler/lexer/lex.py b/src/compiler/lexer/lex.py index 2f6f6056c..c0e0b1bea 100644 --- a/src/compiler/lexer/lex.py +++ b/src/compiler/lexer/lex.py @@ -107,19 +107,105 @@ class CoolLexer(object): 'STRING': G.stringx } - def t_NUMBER(t): + def t_begin_STARTSTRING(self, t): + r'"' + self.string = "" + self.lexer.begin('string') + + def t_string_ENDSTRING(self, t): + r'"' + self.lexer.begin('INITIAL') + t.value = self.string + t.type = 'STRING' + self.add_column(t) + return t + + def t_string_NULL(self, t): + r'\0' + self.lexer.begin('INITIAL') + self.add_column(t) + t.type = 'ERROR' + t.value = f"({t.lineno, t.col}) - LexicographicError: String contains null character" + return t + + def t_string_escaped_newline(self, t): + r'\\\n' + self.string += '\n' + t.lexer.lineno += 1 + + def t_string_invalid_newline1(self, t): + r'\\n' + self.lexer.begin('INITIAL') + self.add_column(t) + t.type = 'ERROR' + t.value = f"({t.lineno, t.col}) - LexicographicError: Unterminated string constant" + return t + + def t_string_invalid_newline2(self, t): + r'\n' + self.lexer.begin('INITIAL') + self.add_column(t) + t.type = 'ERROR' + t.value = f"({t.lineno, t.col}) - LexicographicError: Unterminated string constant" + t.lexer.lineno += 1 + return t + + def t_string_special_character(self, t): + r'\\[btf]' + self.string += t.value + + def t_string_escaped_character(self, t): + r'\\.' + self.string += t.value[1] + + def t_strings_character(self, t): + r'.' + self.string += t.value + + def t_strings_eof(self, t): + t.lexer.begin("INITIAL") + self.add_column(t) + t.type = "ERROR" + t.value = f"({t.lineno},{t.col}) - LexicographicError: EOF in string constant" + return t + + def t_begin_STARTCOMMENT(self, t): + r'\(\*' + self.comment_level = 1 + self.lexer.begin('comment') + + def t_comment_STARTCOMMENT(self, t): + r'\(\*' + self.comment_level += 1 + + def t_comment_ENDCOMMENT(self, t): + r'\*\)' + self.comment_level -= 1 + if self.comment_level == 0: + self.lexer.begin('INITIAL') + + def t_comment_character(self, t): + r'.' + + def t_comment_eof(self, t): + self.add_column(t) + t.type = 'ERROR' + t.value = f'({t.lexer.lineno}, {t.col}) - LexicographicError: EOF in comment' + self.lexer.begin('INITIAL') + return t + + def t_NUMBER(self, t): r'\d+' t.value = int(t.value) self.add_column(t) return t # Define a rule so we can track line numbers - def t_newline(t): + def t_ANY_newline(self, t): r'\n+' t.lexer.lineno += len(t.value) self.count = t.lexpos + len(t.value) - # A string containing ignored characters (spaces and tabs) t_ignore = ' \t\f\r\v' def t_COMMENTLINE(self, t): @@ -235,6 +321,16 @@ def T_NEG(self, t): self.add_column(t) return t + def t_eof(self, t): + t.lexer.eof =(t.lexer.lineno, self.add_column(t)) + return None + + def t_error(self, t): + self.add_column(t) + t.type = 'ERROR' + error_msg = t.value[0] + t.value = f"({t.lineno}, {t.col}) - LexicographicError: ERROR \"{error_msg}\"" + return t # Build the lexer def build(self,**kwargs): From 83416f20f46db67516166eb992436630de79ebf4 Mon Sep 17 00:00:00 2001 From: dgd Date: Tue, 2 Nov 2021 16:21:03 -0400 Subject: [PATCH 082/138] Added ast of cil and cil code printer(visitor) Not tested yet --- src/compiler/cmp/cil_ast.py | 145 +++++++++++++++++++++++++ src/compiler/visitors/cil_formatter.py | 84 ++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 src/compiler/cmp/cil_ast.py create mode 100644 src/compiler/visitors/cil_formatter.py diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py new file mode 100644 index 000000000..73397bae2 --- /dev/null +++ b/src/compiler/cmp/cil_ast.py @@ -0,0 +1,145 @@ +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 + + diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py new file mode 100644 index 000000000..b247d6653 --- /dev/null +++ b/src/compiler/visitors/cil_formatter.py @@ -0,0 +1,84 @@ +import compiler.visitors.visitor as visitor + + +class PrintCILVisitor(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 ""}' + + From 11ec2eed198f5458e4d950de3d0db4dd194ff2cb Mon Sep 17 00:00:00 2001 From: dgd Date: Tue, 2 Nov 2021 17:04:09 -0400 Subject: [PATCH 083/138] Added cool2cil visitor Just added the file --- src/compiler/visitors/cool2cil.py | 271 ++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 src/compiler/visitors/cool2cil.py diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py new file mode 100644 index 000000000..07bc9f48f --- /dev/null +++ b/src/compiler/visitors/cool2cil.py @@ -0,0 +1,271 @@ +import compiler.visitors.visitor as visitor +from ..cmp import cil_ast as cil +from ..cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA +from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from ..cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode +from ..cmp.ast import VoidNode, NotNode, NegNode +from ..cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode +from ..cmp.ast import PlusNode, MinusNode, StarNode, DivNode + +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_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 MiniCOOLToCILVisitor(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() + main_method_name = self.to_function_name('main', 'Main') + self.register_instruction(cil.AllocateNode('Main', instance)) + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(main_method_name, 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) + + # Your code here!!! (Handle all the .TYPE section) + type_node = self.register_type(self.current_type.name) + + visited_func = [] + current = self.current_type + while current is not None: + attributes = [attr.name for attr in current.attributes] + methods = [func.name for func in current.methods if func.name not in visited_func] + visited_func.extend(methods) + type_node.attributes.extend(attributes[::-1]) + type_node.methods.extend([(item, self.to_function_name(item, current.name)) for item in methods[::-1]]) + current = current.parent + + type_node.attributes.reverse() + type_node.methods.reverse() + + + 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) + + # Your code here!!! (Handle PARAMS) + self.current_function = self.register_function(self.to_function_name(self.current_method.name, self.current_type.name)) + self.current_vars = {} + self.params.append(cil.ParamNode('self')) + self.params.extend([cil.ParamNode(p) for p in self.current_method.param_names]) + + value = None + for instruction in node.body: + value = self.visit(instruction, scope) + + # Your code here!!! (Handle RETURN) + if isinstance(self.current_method.return_type, VoidType): + value = None + self.register_instruction(cil.ReturnNode(value)) + self.current_function = None + + self.current_method = None + + @visitor.when(AssignNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + + # Your code here!!! + + #TODO!!!! + + pass + + + @visitor.when(CallNode) + def visit(self, node, scope): + ############################### + # node.obj -> AtomicNode + # node.id -> str + # node.args -> [ ExpressionNode ... ] + ############################### + + # Your code here!!! + pass + + + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + # Your code here!!! (Pretty easy ;-)) + return node.lex + + + @visitor.when(VariableNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + # Your code here!!! + return node.lex + + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + # Your code here!!! + pass + + @visitor.when(PlusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + var = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + self.register_instruction(cil.PlusNode(var, left, right)) + return var + + + + @visitor.when(MinusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + var = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + self.register_instruction(cil.MinusNode(var, left, right)) + return var + + + @visitor.when(StarNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + var = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + self.register_instruction(cil.StarNode(var, left, right)) + return var + + + @visitor.when(DivNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + var = self.define_internal_local() + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + self.register_instruction(cil.DivNode(var, left, right)) + return var + \ No newline at end of file From ec173864ce62a54f34aebcc92244dce2fa87eb7c Mon Sep 17 00:00:00 2001 From: dgd Date: Tue, 2 Nov 2021 17:15:19 -0400 Subject: [PATCH 084/138] Fixing import in cil_formatter --- src/compiler/visitors/cil_formatter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py index b247d6653..54875a195 100644 --- a/src/compiler/visitors/cil_formatter.py +++ b/src/compiler/visitors/cil_formatter.py @@ -1,4 +1,5 @@ import compiler.visitors.visitor as visitor +from ..cmp.cil_ast import * class PrintCILVisitor(object): From 476d3190e86f4ffd89656fe2180dff31ebc46c0d Mon Sep 17 00:00:00 2001 From: dgd Date: Mon, 21 Feb 2022 21:38:44 -0500 Subject: [PATCH 085/138] that commit of months ago This is fvcking chinese --- src/compiler/visitors/cil_formatter.py | 2 +- src/compiler/visitors/cool2cil.py | 53 +++++++++----- src/main.py | 3 +- src/tests.py | 97 ++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 src/tests.py diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py index 54875a195..5c486c604 100644 --- a/src/compiler/visitors/cil_formatter.py +++ b/src/compiler/visitors/cil_formatter.py @@ -64,7 +64,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(StaticCallNode) def visit(self, node): diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 07bc9f48f..8f6f2e394 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -1,6 +1,6 @@ import compiler.visitors.visitor as visitor from ..cmp import cil_ast as cil -from ..cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA +from ..cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA, VariableInfo from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode from ..cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode @@ -37,7 +37,7 @@ def register_local(self, vinfo): return vinfo.name def define_internal_local(self): - vinfo = VariableInfo('internal', None) + vinfo = VariableInfo('internal', None, None) return self.register_local(vinfo) def register_instruction(self, instruction): @@ -66,7 +66,7 @@ def register_data(self, value): -class MiniCOOLToCILVisitor(BaseCOOLToCILVisitor): +class COOLToCILVisitor(BaseCOOLToCILVisitor): @visitor.on('node') def visit(self, node): pass @@ -141,14 +141,10 @@ def visit(self, node, scope): self.current_vars = {} self.params.append(cil.ParamNode('self')) self.params.extend([cil.ParamNode(p) for p in self.current_method.param_names]) - - value = None - for instruction in node.body: - value = self.visit(instruction, scope) + + value = self.visit(node.body,scope) # Your code here!!! (Handle RETURN) - if isinstance(self.current_method.return_type, VoidType): - value = None self.register_instruction(cil.ReturnNode(value)) self.current_function = None @@ -162,11 +158,8 @@ def visit(self, node, scope): ############################### # Your code here!!! - - #TODO!!!! - - pass - + value = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(node.id, value)) @visitor.when(CallNode) def visit(self, node, scope): @@ -174,11 +167,33 @@ def visit(self, node, scope): # node.obj -> AtomicNode # node.id -> str # node.args -> [ ExpressionNode ... ] + # node.type -> str ############################### - + # Your code here!!! - pass - + values = [self.visit(node.obj,scope)] + values.extend([self.visit(arg, scope) for arg in node.args]) + local_vars = [self.define_internal_local() for v in values] + for (var,val) in zip( local_vars,values): + self.register_instruction(cil.AssignNode(var,val)) + + for var in local_vars: + self.register_instruction(cil.ArgNode(var)) + + return_var = self.define_internal_local() + if node.type: # Dynamic Call + self.register_instruction(cil.DynamicCallNode(node.type,node.id,return_var)) + elif values[0] == 'self':#Static call + function = self.to_function_name(node.id,self.current_type.name) + self.register_instruction(cil.StaticCallNode(function,return_var)) + else: #Dynamic Call with type of obj + type_var = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(local_vars[0], type_var)) + self.register_instruction(cil.DynamicCallNode(type_var,node.id,return_var)) + + return return_var + + @visitor.when(ConstantNumNode) def visit(self, node, scope): @@ -207,7 +222,9 @@ def visit(self, node, scope): ############################### # Your code here!!! - pass + var = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.lex, var)) + return var @visitor.when(PlusNode) def visit(self, node, scope): diff --git a/src/main.py b/src/main.py index 8804a2af7..e52da49ab 100644 --- a/src/main.py +++ b/src/main.py @@ -8,7 +8,8 @@ from compiler.visitors.type_builder import TypeBuilder from compiler.visitors.type_checker import TypeChecker from compiler.visitors.type_inferencer import TypeInferencer - +from compiler.visitors.cool2cil import COOLToCILVisitor +from compiler.visitors.cil_formatter import PrintCILVisitor def run_pipeline(G, text): print('=================== TEXT ======================') diff --git a/src/tests.py b/src/tests.py new file mode 100644 index 000000000..751e62e33 --- /dev/null +++ b/src/tests.py @@ -0,0 +1,97 @@ +from compiler.cmp.grammar import G +from compiler.lexer.lexer import tokenize_text, pprint_tokens +from compiler.cmp.tools import LR1Parser +from compiler.cmp.evaluation import evaluate_reverse_parse +from compiler.visitors.formatter import FormatVisitor +from compiler.visitors.type_collector import TypeCollector +from compiler.visitors.type_builder import TypeBuilder +from compiler.visitors.type_checker import TypeChecker +from compiler.visitors.type_inferencer import TypeInferencer +from compiler.visitors.cil_formatter import PrintCILVisitor +from compiler.visitors.cool2cil import COOLToCILVisitor + + +def run_pipeline(G, text): + print('=================== TEXT ======================') + print(text) + print('================== TOKENS =====================') + tokens = list(tokenize_text(text)) + pprint_tokens(tokens) + print('=================== PARSE =====================') + parser = LR1Parser(G) + parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) + print('\n'.join(repr(x) for x in parse)) + print('==================== AST ======================') + ast = evaluate_reverse_parse(parse, operations, tokens) + formatter = FormatVisitor() + tree = formatter.visit(ast) + print(tree) + print('============== COLLECTING TYPES ===============') + errors = [] + collector = TypeCollector(errors) + collector.visit(ast) + context = collector.context + print('Errors:', errors) + print('Context:') + print(context) + print('=============== BUILDING TYPES ================') + builder = TypeBuilder(context, errors) + builder.visit(ast) + manager = builder.manager + print('Errors: [') + for error in errors: + print('\t', error) + print(']') + print('Context:') + print(context) + print('=============== CHECKING TYPES ================') + checker = TypeChecker(context, manager, []) + scope = checker.visit(ast) + print('=============== INFERING TYPES ================') + temp_errors = [] + inferencer = TypeInferencer(context, manager, temp_errors) + inferencer.visit(ast, scope) + print('Errors: [') + for error in temp_errors: + print('\t', error) + print(']') + print('=============== LAST CHECK ================') + errors.extend(temp_errors) + checker = TypeChecker(context, manager, errors) + checker.visit(ast) + print('Errors: [') + for error in errors: + print('\t', error) + print(']') + formatter = FormatVisitor() + tree = formatter.visit(ast) + #print(tree) + + print('=============== CIL CODE ================') + cil_visitor = COOLToCILVisitor(context) + cil_ast = cil_visitor.visit(ast,scope) + cil_formatter = PrintCILVisitor() + print(cil_formatter.visit(cil_ast)) + return ast + + +text = ''' +class Main inherits IO { + number: Int <- 5; + + main (a:Int,b:Int) : Int { + (new Main).suma(a+2,b+1) + }; + + suma (a:Int, b:Int) : Int{ + a+b + }; + + +}; +''' + + + + +ast = run_pipeline(G, text) \ No newline at end of file From 206cf501e3a8f85df6df6370d2068382341e4f75 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 01:06:51 -0500 Subject: [PATCH 086/138] fix bugs on lexer --- src/compiler/lexer/lex.py | 389 ++++++++++++++++++++---------------- src/compiler/lexer/lexer.py | 150 -------------- 2 files changed, 220 insertions(+), 319 deletions(-) delete mode 100644 src/compiler/lexer/lexer.py diff --git a/src/compiler/lexer/lex.py b/src/compiler/lexer/lex.py index c0e0b1bea..2787ca4c7 100644 --- a/src/compiler/lexer/lex.py +++ b/src/compiler/lexer/lex.py @@ -1,342 +1,393 @@ +from distutils.log import debug import ply.lex as lex -import re -from ..cmp.grammar import G +from ..cmp.grammar import * +from ..cmp.utils import Token -class CoolLexer(object): - __init__(self): +class CoolLexer(object): + def __init__(self): self.count = 0 self.build() states = ( - ('string','exclusive'), - ('comment','exclusive'), + ("string", "exclusive"), + ("comment", "exclusive"), ) reserved = { - 'class': 'CLASS', - 'inherits': 'INHERITS', - 'let': 'LET', - 'in': 'IN', - 'case': 'CASE', - 'of': 'OF', - 'esac': 'ESAC', - 'while': 'WHILE', - 'loop': 'LOOP', - 'pool': 'POOL', - 'if': 'IF', - 'then': 'THEN', - 'else': 'ELSE', - 'fi': 'FI', - 'isvoid': 'ISVOID', - 'not': 'NOT', - 'new': 'NEW', - 'true': 'TRUE', - 'false': 'FALSE' + "class": "CLASS", + "inherits": "INHERITS", + "let": "LET", + "in": "IN", + "case": "CASE", + "of": "OF", + "esac": "ESAC", + "while": "WHILE", + "loop": "LOOP", + "pool": "POOL", + "if": "IF", + "then": "THEN", + "else": "ELSE", + "fi": "FI", + "isvoid": "ISVOID", + "not": "NOT", + "new": "NEW", + "true": "TRUE", + "false": "FALSE", } tokens = [ - 'SEMICOLON', - 'COLON', - 'COMMA', - 'DOT', - 'OPAR', - 'CPAR', - 'OCUR', - 'CCUR', - 'LARROW', - 'RARROW', - 'AT', - 'EQUAL', - 'PLUS', - 'MINUS', - 'STAR', - 'DIV', - 'LESS', - 'LEQ', - 'NEG', - 'TYPEIDENTIFIER', - 'OBJECTIDENTIFIER', - 'NUMBER', - 'STRING' + "SEMICOLON", + "COLON", + "COMMA", + "DOT", + "OPAR", + "CPAR", + "OCUR", + "CCUR", + "LARROW", + "RARROW", + "AT", + "EQUAL", + "PLUS", + "MINUS", + "STAR", + "DIV", + "LESS", + "LEQ", + "NEG", + "TYPEIDENTIFIER", + "OBJECTIDENTIFIER", + "NUMBER", + "STRING", + "ERROR", ] + list(reserved.values()) token_type = { - 'CLASS' : G.classx, - 'INHERITS' : G.inherits, - 'LET' : G.let, - 'IN' : G.inx, - 'CASE': G.case, - 'OF': G.of, - 'ESAC': G.esac, - 'WHILE': G.whilex, - 'LOOP': G.loop, - 'POOL': G.pool, - 'IF': G.ifx, - 'THEN': G.then, - 'ELSE': G.elsex, - 'FI': G.fi, - 'ISVOID': G.isvoid, - 'NOT': G.notx, - 'NEW': G.new, - 'TRUE': G.boolx, - 'FALSE': G.boolx, - 'SEMICOLON': G.semi, - 'COLON': G.colon, - 'COMMA': G.comma, - 'DOT': G.dot, - 'OPAR': G.opar, - 'CPAR': G.cpar, - 'OCUR': G.ocur, - 'CCUR': G.ccur, - 'LARROW': G.larrow, - 'RARROW': G.rarrow, - 'AT': G.at, - 'EQUAL': G.equal, - 'PLUS': G.plus, - 'MINUS': G.minus, - 'STAR': G.star, - 'DIV': G.div, - 'LESS': G.less, - 'LEQ': G.leq, - 'NEG': G.neg, - 'TYPEIDENTIFIER': G.typeid, - 'OBJECTIDENTIFIER': G.objectid, - 'NUMBER': G.num, - 'STRING': G.stringx + "CLASS": classx, + "INHERITS": inherits, + "LET": let, + "IN": inx, + "CASE": case, + "OF": of, + "ESAC": esac, + "WHILE": whilex, + "LOOP": loop, + "POOL": pool, + "IF": ifx, + "THEN": then, + "ELSE": elsex, + "FI": fi, + "ISVOID": isvoid, + "NOT": notx, + "NEW": new, + "TRUE": boolx, + "FALSE": boolx, + "SEMICOLON": semi, + "COLON": colon, + "COMMA": comma, + "DOT": dot, + "OPAR": opar, + "CPAR": cpar, + "OCUR": ocur, + "CCUR": ccur, + "LARROW": larrow, + "RARROW": rarrow, + "AT": at, + "EQUAL": equal, + "PLUS": plus, + "MINUS": minus, + "STAR": star, + "DIV": div, + "LESS": less, + "LEQ": leq, + "NEG": neg, + "TYPEIDENTIFIER": typeid, + "OBJECTIDENTIFIER": objectid, + "NUMBER": num, + "STRING": stringx, } def t_begin_STARTSTRING(self, t): r'"' self.string = "" - self.lexer.begin('string') + self.lexer.begin("string") def t_string_ENDSTRING(self, t): r'"' - self.lexer.begin('INITIAL') + self.lexer.begin("INITIAL") t.value = self.string - t.type = 'STRING' + t.type = "STRING" self.add_column(t) return t def t_string_NULL(self, t): - r'\0' - self.lexer.begin('INITIAL') + r"\0" + self.lexer.begin("INITIAL") self.add_column(t) - t.type = 'ERROR' - t.value = f"({t.lineno, t.col}) - LexicographicError: String contains null character" + t.type = "ERROR" + t.value = f"({t.lineno}, {t.col}) - LexicographicError: String contains null character" return t + def t_string_newline1(self, t): + r"\\n" + self.string += "\n" + def t_string_escaped_newline(self, t): - r'\\\n' - self.string += '\n' + r"\\\n" + self.string += "\n" t.lexer.lineno += 1 - - def t_string_invalid_newline1(self, t): - r'\\n' - self.lexer.begin('INITIAL') - self.add_column(t) - t.type = 'ERROR' - t.value = f"({t.lineno, t.col}) - LexicographicError: Unterminated string constant" - return t + self.count = t.lexpos + len(t.value) def t_string_invalid_newline2(self, t): - r'\n' - self.lexer.begin('INITIAL') + r"\n" + self.lexer.begin("INITIAL") self.add_column(t) - t.type = 'ERROR' - t.value = f"({t.lineno, t.col}) - LexicographicError: Unterminated string constant" + self.count = t.lexpos + len(t.value) + t.type = "ERROR" + t.value = ( + f"({t.lineno}, {t.col}) - LexicographicError: Unterminated string constant" + ) t.lexer.lineno += 1 return t def t_string_special_character(self, t): - r'\\[btf]' + r"\\[btf]" self.string += t.value def t_string_escaped_character(self, t): - r'\\.' + r"\\." self.string += t.value[1] - def t_strings_character(self, t): - r'.' + def t_string_character(self, t): + r"." self.string += t.value - def t_strings_eof(self, t): - t.lexer.begin("INITIAL") + def t_string_eof(self, t): self.add_column(t) t.type = "ERROR" t.value = f"({t.lineno},{t.col}) - LexicographicError: EOF in string constant" + t.lexer.begin("INITIAL") return t def t_begin_STARTCOMMENT(self, t): - r'\(\*' + r"\(\*" self.comment_level = 1 - self.lexer.begin('comment') + self.lexer.begin("comment") def t_comment_STARTCOMMENT(self, t): - r'\(\*' + r"\(\*" self.comment_level += 1 - + def t_comment_ENDCOMMENT(self, t): - r'\*\)' + r"\*\)" self.comment_level -= 1 if self.comment_level == 0: - self.lexer.begin('INITIAL') + self.lexer.begin("INITIAL") def t_comment_character(self, t): - r'.' + r"." def t_comment_eof(self, t): self.add_column(t) - t.type = 'ERROR' - t.value = f'({t.lexer.lineno}, {t.col}) - LexicographicError: EOF in comment' - self.lexer.begin('INITIAL') + t.type = "ERROR" + t.value = f"({t.lexer.lineno}, {t.col}) - LexicographicError: EOF in comment" + self.lexer.begin("INITIAL") return t def t_NUMBER(self, t): - r'\d+' + r"\d+" t.value = int(t.value) self.add_column(t) return t - # Define a rule so we can track line numbers + # Rule to track line numbers def t_ANY_newline(self, t): - r'\n+' + r"\n+" t.lexer.lineno += len(t.value) self.count = t.lexpos + len(t.value) - t_ignore = ' \t\f\r\v' + t_ignore = " \t\f\r\v" def t_COMMENTLINE(self, t): - r'--.*' + r"--.*" def t_TYPEIDENTIFIER(self, t): - r'[A-Z][0-9A-Za-z_]*' + r"[A-Z][0-9A-Za-z_]*" val = t.value.lower() - if val not in ['true', 'false']: - t.type = self.reserved.get(val, 'TYPEIDENTIFIER') + if val not in ["true", "false"]: + t.type = self.reserved.get(val, "TYPEIDENTIFIER") self.add_column(t) return t def t_OBJECTIDENTIFIER(self, t): - r'[a-z][0-9A-Za-z_]*' + r"[a-z][0-9A-Za-z_]*" val = t.value.lower() - t.type = self.reserved.get(val, 'OBJECTIDENTIFIER') + t.type = self.reserved.get(val, "OBJECTIDENTIFIER") self.add_column(t) return t - + def t_SEMICOLON(self, t): - r';' + r";" self.add_column(t) return t def t_COLON(self, t): - r':' + r":" self.add_column(t) return t def t_COMMA(self, t): - r',' + r"," self.add_column(t) return t - + def t_DOT(self, t): - r'\.' + r"\." self.add_column(t) return t - + def t_OPAR(self, t): - r'\(' + r"\(" self.add_column(t) return t - + def t_CPAR(self, t): - r'\)' + r"\)" self.add_column(t) return t def t_OCUR(self, t): - r'{' + r"{" self.add_column(t) return t def t_CCUR(self, t): - r'}' + r"}" self.add_column(t) return t def t_LARROW(self, t): - r'<-' + r"<-" self.add_column(t) return t def t_RARROW(self, t): - r'=>' + r"=>" self.add_column(t) return t def t_AT(self, t): - r'@' + r"@" self.add_column(t) return t def t_EQUAL(self, t): - r'=' + r"=" self.add_column(t) return t def t_PLUS(self, t): - r'\+' + r"\+" self.add_column(t) return t def t_MINUS(self, t): - r'-' + r"-" self.add_column(t) return t def t_STAR(self, t): - r'\*' + r"\*" self.add_column(t) return t def t_DIV(self, t): - r'/' + r"/" self.add_column(t) return t def t_LESS(self, t): - r'<' + r"<" self.add_column(t) return t def t_LEQ(self, t): - r'<=' + r"<=" self.add_column(t) return t - def T_NEG(self, t): - r'~' + def t_NEG(self, t): + r"~" self.add_column(t) return t def t_eof(self, t): - t.lexer.eof =(t.lexer.lineno, self.add_column(t)) + t.lexer.eof = (t.lexer.lineno, self.add_column(t)) return None def t_error(self, t): self.add_column(t) - t.type = 'ERROR' + t.type = "ERROR" error_msg = t.value[0] - t.value = f"({t.lineno}, {t.col}) - LexicographicError: ERROR \"{error_msg}\"" + t.value = f'({t.lineno}, {t.col}) - LexicographicError: ERROR "{error_msg}"' + t.lexer.skip(1) return t # Build the lexer - def build(self,**kwargs): - self.lexer = lex.lex(module=self, **kwargs) - + def build(self, **kwargs): + # self.lexer = lex.lex(module=self, **kwargs) + self.lexer = lex.lex( + module=self, errorlog=lex.NullLogger(), debug=False, **kwargs + ) + self.lexer.eof = (1, 1) + self.comment_level = 0 + self.string = "" def add_column(self, t): - t.col = t.lexpos - self.count - + t.col = t.lexpos - self.count + 1 + + def tokenize(self, data): + self.lexer.input(data) + token_list = [] + errors = [] + while True: + tok = self.lexer.token() + if not tok: + break + + if tok.type == "ERROR": + errors.append(tok.value) + else: + token_list.append( + Token(tok.value, self.token_type[tok.type], (tok.lineno, tok.col)) + ) + return token_list, errors + + +def pprint_tokens(tokens, get=False): + indent = 0 + pending = [] + result = "" + for token in tokens: + pending.append(token) + if token.token_type in {ocur, ccur, semi}: + if token.token_type == ccur: + indent -= 1 + if get: + result += ( + " " * indent + + " ".join(str(t.token_type) for t in pending) + + "\n" + ) + else: + print(" " * indent + " ".join(str(t.token_type) for t in pending)) + pending.clear() + if token.token_type == ocur: + indent += 1 + if get: + result += " ".join([str(t.token_type) for t in pending]) + "\n" + return result + else: + print(" ".join([str(t.token_type) for t in pending])) diff --git a/src/compiler/lexer/lexer.py b/src/compiler/lexer/lexer.py deleted file mode 100644 index faad57696..000000000 --- a/src/compiler/lexer/lexer.py +++ /dev/null @@ -1,150 +0,0 @@ -from ..cmp.grammar import * -from ..cmp.utils import Token -from ..cmp.regex import Regex -from ..cmp.automata import State - - -class Lexer: - def __init__(self, table, eof): - self.eof = eof - self.regexs = self._build_regexs(table) - self.automaton = self._build_automaton() - - def _build_regexs(self, table): - regexs = [] - for n, (token_type, regex) in enumerate(table): - dfa = Regex(regex) - automaton_list = State.from_nfa(dfa.automaton, 'texto random pra que haga lo que quiero') - for i in automaton_list[1]: - if(i.final): - i.tag = (n, token_type) - regexs.append(automaton_list[0]) - - return regexs - - def _build_automaton(self): - start = State('start') - automatons = self.regexs - for i in automatons: - start.add_epsilon_transition(i) - final = start.to_deterministic() - return final - - def _walk(self, string): - state = self.automaton - final = state if state.final else None - final_lex = lex = '' - - for symbol in string: - try: - state = state[symbol][0] - lex += symbol - if(state.final): - final = state - final_lex = lex - except TypeError: - # print("entre ---", symbol ) - # print(lex, string, state) - break - - return final, final_lex - - def _tokenize(self, text): - pos = 0 - while(len(text) > 0): - temp = self._walk(text) - # print(temp) - - if(temp[1] == ''): - assert 1, 0 - - pos = len(temp[1]) - text = text[pos:len(text)] - mi = 9999 - final = None - for i in temp[0].state: - if i.final: - if i.tag[0] < mi: - final = i - mi = i.tag[0] - yield temp[1], final.tag[1] - - yield '$', self.eof - - def __call__(self, text): - return [ Token(lex, ttype) for lex, ttype in self._tokenize(text) ] - -def eliminate_regex_conflict(text): - result = '' - for i in text: - if i in {'(', ')', '*', '\\', '|'}: - result += '\\' - result += i - return result - -def get_all_combinations(text, first_lower=False): - result = '' - - first = text[0] - first = eliminate_regex_conflict(first) - - if first_lower: - result += f'{first.lower()}' - else: - result += f'({first.lower()}|{first.upper()})' - for i in text[1:]: - temp = eliminate_regex_conflict(i) - result += f'({temp.lower()}|{temp.upper()})' - - return result - -nonzero_digits = '|'.join(str(n) for n in range(1,10)) -alp = [chr(n) for n in range(ord('a'),ord('z') + 1)] -alp.extend([chr(n) for n in range(ord('A'),ord('Z') + 1)]) -letters = '|'.join(alp) -alphabet_before = [eliminate_regex_conflict(chr(n)) for n in range(1, ord('~') + 1) if n != 34] -alphabet = '|'.join(alphabet_before) - -def tokenize_text(text): - lexer = Lexer( - [(t, get_all_combinations(t.Name)) for t in G.terminals if t not in { idx, num, stringx, boolx }] + - [(boolx, f'({get_all_combinations("true", True)})|({get_all_combinations("false", True)})'), - (num, f'0|(({nonzero_digits})(0|{nonzero_digits})*)'), - ('space', '') - # ('salto', '\n'), - # ('space', '( |\t)( |\t)*'), - (idx, f'({letters}|_)(_|{letters}|0|{nonzero_digits})*'), - (stringx, f'"({alphabet})*"')], - G.EOF) - - tokens = lexer(text) - # print('tokens', tokens) - tokens_filtrado = [] - for i in tokens: - if i.token_type != 'salto' and i.token_type != 'space': - tokens_filtrado.append(i) - # print('tokens filtrados', tokens_filtrado) - return tokens_filtrado - - -def pprint_tokens(tokens, get=False): - indent = 0 - pending = [] - result = '' - for token in tokens: - pending.append(token) - if token.token_type in { ocur, ccur, semi }: - if token.token_type == ccur: - indent -= 1 - if get: - result += ' '*indent + ' '.join(str(t.token_type) for t in pending) + '\n' - else: - print(' '*indent + ' '.join(str(t.token_type) for t in pending)) - pending.clear() - if token.token_type == ocur: - indent += 1 - if get: - result += ' '.join([str(t.token_type) for t in pending]) + '\n' - return result - else: - print(' '.join([str(t.token_type) for t in pending])) From a62682b253f028c87b1a3d46d5bdab350b5a99a7 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 01:08:26 -0500 Subject: [PATCH 087/138] build structure for executing project --- src/coolc.sh | 8 +- src/main.py | 227 +++++++++++++++++++-------------------------------- 2 files changed, 86 insertions(+), 149 deletions(-) diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f9..7d7ae68f0 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -3,9 +3,7 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips -# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos - +echo "codersUP - COOLCompilerv0.0.1" +echo "Copyright (c) 2022: Carmen Irene Cabrera Rodríguez, David Guaty Domínguez, Enrique Martínez González" # Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python3 main.py -f "$INPUT_FILE" diff --git a/src/main.py b/src/main.py index 8804a2af7..4aa2c8ce3 100644 --- a/src/main.py +++ b/src/main.py @@ -1,75 +1,73 @@ -import streamlit as st -from compiler.cmp.grammar import G -from compiler.lexer.lexer import tokenize_text, pprint_tokens -from compiler.cmp.tools import LR1Parser -from compiler.cmp.evaluation import evaluate_reverse_parse -from compiler.visitors.formatter import FormatVisitor -from compiler.visitors.type_collector import TypeCollector -from compiler.visitors.type_builder import TypeBuilder -from compiler.visitors.type_checker import TypeChecker -from compiler.visitors.type_inferencer import TypeInferencer - - -def run_pipeline(G, text): - print('=================== TEXT ======================') - print(text) - print('================== TOKENS =====================') - tokens = list(tokenize_text(text)) - pprint_tokens(tokens) - print('=================== PARSE =====================') - parser = LR1Parser(G) - parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) - print('\n'.join(repr(x) for x in parse)) - print('==================== AST ======================') - ast = evaluate_reverse_parse(parse, operations, tokens) - formatter = FormatVisitor() - tree = formatter.visit(ast) - print(tree) - print('============== COLLECTING TYPES ===============') - errors = [] - collector = TypeCollector(errors) - collector.visit(ast) - context = collector.context - print('Errors:', errors) - print('Context:') - print(context) - print('=============== BUILDING TYPES ================') - builder = TypeBuilder(context, errors) - builder.visit(ast) - manager = builder.manager - print('Errors: [') - for error in errors: - print('\t', error) - print(']') - print('Context:') - print(context) - print('=============== CHECKING TYPES ================') - checker = TypeChecker(context, manager, []) - scope = checker.visit(ast) - print('=============== INFERING TYPES ================') - temp_errors = [] - inferencer = TypeInferencer(context, manager, temp_errors) - inferencer.visit(ast, scope) - print('Errors: [') - for error in temp_errors: - print('\t', error) - print(']') - print('=============== LAST CHECK ================') - errors.extend(temp_errors) - checker = TypeChecker(context, manager, errors) - checker.visit(ast) - print('Errors: [') - for error in errors: - print('\t', error) - print(']') - formatter = FormatVisitor() - tree = formatter.visit(ast) - print(tree) - - return ast +# import streamlit as st +from compiler.cmp.grammar import G -text = ''' +# from compiler.lexer.lexer import tokenize_text, pprint_tokens +from compiler.lexer.lex import CoolLexer +from sys import exit + +# from compiler.cmp.tools import LR1Parser +# from compiler.cmp.evaluation import evaluate_reverse_parse +# from compiler.visitors.formatter import FormatVisitor +# from compiler.visitors.type_collector import TypeCollector +# from compiler.visitors.type_builder import TypeBuilder +# from compiler.visitors.type_checker import TypeChecker +# from compiler.visitors.type_inferencer import TypeInferencer + + +def main(args): + try: + with open(args.file, "r") as fd: + code = fd.read() + except: + print(f"(0,0) - CompilerError: file {args.file} not found") + exit(1) + + lexer = CoolLexer() + tokens, errors = lexer.tokenize(code) + for error in errors: + print(error) + + if errors: + exit(1) + # print('=================== PARSE =====================') + # parser = LR1Parser(G) + # parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) + # print('\n'.join(repr(x) for x in parse)) + # print('==================== AST ======================') + # ast = evaluate_reverse_parse(parse, operations, tokens) + # formatter = FormatVisitor() + # tree = formatter.visit(ast) + # print(tree) + # print('============== COLLECTING TYPES ===============') + # errors = [] + # collector = TypeCollector(errors) + # collector.visit(ast) + # context = collector.context + # print('Errors:', errors) + # print('Context:') + # print(context) + # print('=============== BUILDING TYPES ================') + # builder = TypeBuilder(context, errors) + # builder.visit(ast) + # manager = builder.manager + # print('Errors: [') + # for error in errors: + # print('\t', error) + # print(']') + # print('Context:')import argparse + # print('Errors: [') + # for error in errors: + # print('\t', error) + # print(']') + # formatter = FormatVisitor() + # tree = formatter.visit(ast) + # print(tree) + + # return ast + + +text = """ class Main inherits IO { number: Int <- 5; @@ -80,11 +78,11 @@ class Main inherits IO { testing_fibonacci(n: Int) : IO {{ out_string("Iterative Fibonacci : "); out_int(iterative_fibonacci(5)); - out_string("\n"); + out_string("\\n"); out_string("Recursive Fibonacci : "); out_int(recursive_fibonacci(5)); - out_string("\n"); + out_string("\\n"); }}; recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { @@ -104,75 +102,16 @@ class Main inherits IO { } }; }; -''' - -def main(G): - st.title('Type Inferencer') - - st.sidebar.markdown('''Produced by: - Carmen Irene Cabrera Rodríguez - Enrique Martínez González''') - - text = st.text_area('Input your code here:') - - if text: - st.text(text) - try: - tokens = list(tokenize_text(text)) - except Exception as e: - st.error(f'Lexer Error: You probably did something wrong :wink:') - else: - try: - parser = LR1Parser(G) - parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) - except Exception as e: - st.error(f'Parser Error: You probably did something wrong :wink:') - else: - ast = evaluate_reverse_parse(parse, operations, tokens) - - st.title('Results:') - - errors = [] - collector = TypeCollector(errors) - collector.visit(ast) - context = collector.context - - # for e in errors: - # st.error(e) - # st.text('Context:') - # st.text(context) - - # st.subheader('Building types') - builder = TypeBuilder(context, errors) - builder.visit(ast) - manager = builder.manager - # for e in errors: - # st.error(e) - # st.text('Context:') - # st.text(context) - - # st.subheader('Checking types') - checker = TypeChecker(context, manager, []) - scope = checker.visit(ast) - - # st.subheader('Infering types') - temp_errors = [] - inferencer = TypeInferencer(context, manager, temp_errors) - inferencer.visit(ast, scope) - # for e in temp_errors: - # st.error(e) - - # st.subheader('Last check') - errors.extend(temp_errors) - checker = TypeChecker(context, manager, errors) - checker.visit(ast) - for e in errors: - st.error(e) - - formatter = FormatVisitor() - tree = formatter.visit(ast) - st.text(tree) - -if __name__ == '__main__': - # main(G) - ast = run_pipeline(G, text) \ No newline at end of file +""" + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="CoolCompiler") + parser.add_argument( + "-f", "--file", type=str, default="code.cl", help="File to read cool code from" + ) + + args = parser.parse_args() + + main(args) From 5d47514b445c100193b683226e1517910ef6ac48 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 01:09:04 -0500 Subject: [PATCH 088/138] format all files --- src/compiler/cmp/ast.py | 40 +++- src/compiler/cmp/automata.py | 65 ++++-- src/compiler/cmp/evaluation.py | 13 +- src/compiler/cmp/grammar.py | 284 +++++++++++++---------- src/compiler/cmp/nfa_dfa.py | 63 +++-- src/compiler/cmp/pycompiler.py | 119 +++++----- src/compiler/cmp/regex.py | 53 +++-- src/compiler/cmp/semantic.py | 157 ++++++++----- src/compiler/cmp/tools.py | 176 +++++++++----- src/compiler/cmp/utils.py | 93 +++++--- src/compiler/visitors/formatter.py | 117 ++++++---- src/compiler/visitors/type_builder.py | 74 +++--- src/compiler/visitors/type_checker.py | 197 ++++++++++------ src/compiler/visitors/type_collector.py | 80 ++++--- src/compiler/visitors/type_inferencer.py | 154 +++++++----- src/compiler/visitors/visitor.py | 95 ++++---- 16 files changed, 1090 insertions(+), 690 deletions(-) diff --git a/src/compiler/cmp/ast.py b/src/compiler/cmp/ast.py index c0e950a91..419c5f0e3 100644 --- a/src/compiler/cmp/ast.py +++ b/src/compiler/cmp/ast.py @@ -1,21 +1,27 @@ class Node: pass + class ProgramNode(Node): def __init__(self, declarations): self.declarations = declarations + class DeclarationNode(Node): pass + + class ExpressionNode(Node): pass + class ClassDeclarationNode(DeclarationNode): def __init__(self, idx, features, parent=None): self.id = idx self.parent = parent self.features = features + class FuncDeclarationNode(DeclarationNode): def __init__(self, idx, params, return_type, body): self.id = idx @@ -24,17 +30,20 @@ def __init__(self, idx, params, return_type, body): self.type = return_type self.body = body + class AttrDeclarationNode(DeclarationNode): def __init__(self, idx, typex, expr=None): self.id = idx self.type = typex self.expr = expr + class AssignNode(ExpressionNode): def __init__(self, idx, expr): self.id = idx self.expr = expr + class CallNode(ExpressionNode): def __init__(self, obj, idx, args, cast_type=None): self.obj = obj @@ -42,48 +51,59 @@ def __init__(self, obj, idx, args, cast_type=None): self.args = args self.type = cast_type + class CaseNode(ExpressionNode): def __init__(self, expr, branch_list): self.expr = expr - #[(id, type, expr), ...] + # [(id, type, expr), ...] self.branch_list = branch_list + class BlockNode(ExpressionNode): def __init__(self, expr_list): self.expr_list = expr_list + class LoopNode(ExpressionNode): def __init__(self, cond, body): self.condition = cond self.body = body + class ConditionalNode(ExpressionNode): def __init__(self, cond, then_body, else_body): self.condition = cond self.then_body = then_body self.else_body = else_body + class LetNode(ExpressionNode): def __init__(self, id_list, body): - #[(id, type, expr), ...] + # [(id, type, expr), ...] self.id_list = id_list self.body = body + class AtomicNode(ExpressionNode): def __init__(self, lex): self.lex = lex + class UnaryNode(ExpressionNode): def __init__(self, expr): self.expr = expr + class BinaryNode(ExpressionNode): def __init__(self, left, right): self.left = left self.right = right + class ArithmeticNode(BinaryNode): pass + + class ComparisonNode(BinaryNode): pass @@ -91,44 +111,58 @@ class ComparisonNode(BinaryNode): class ConstantNumNode(AtomicNode): pass + class ConstantStringNode(AtomicNode): pass + class ConstantBoolNode(AtomicNode): pass + class VariableNode(AtomicNode): pass + class InstantiateNode(AtomicNode): pass + class PlusNode(ArithmeticNode): pass + class MinusNode(ArithmeticNode): pass + class StarNode(ArithmeticNode): pass + class DivNode(ArithmeticNode): pass + class LeqNode(ComparisonNode): pass + class LessNode(ComparisonNode): pass + class EqualNode(BinaryNode): pass + class VoidNode(UnaryNode): pass + class NotNode(UnaryNode): pass + class NegNode(UnaryNode): - pass \ No newline at end of file + pass diff --git a/src/compiler/cmp/automata.py b/src/compiler/cmp/automata.py index a8d56f8c5..796ede39e 100644 --- a/src/compiler/cmp/automata.py +++ b/src/compiler/cmp/automata.py @@ -3,8 +3,9 @@ except: pass + class State: - def __init__(self, state, final=False, formatter=lambda x: str(x), shape='circle'): + def __init__(self, state, final=False, formatter=lambda x: str(x), shape="circle"): self.state = state self.final = final self.transitions = {} @@ -14,7 +15,7 @@ def __init__(self, state, final=False, formatter=lambda x: str(x), shape='circle self.shape = shape # The method name is set this way from compatibility issues. - def set_formatter(self, value, attr='formatter', visited=None): + def set_formatter(self, value, attr="formatter", visited=None): if visited is None: visited = set() elif self in visited: @@ -54,20 +55,22 @@ 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 ] + closures = [closure] + states = [start] + pending = [start] while pending: state = pending.pop() - symbols = { symbol for s in state.state for symbol in s.transitions } + 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) + new_state = State( + tuple(closure), any(s.final for s in closure), formatter + ) closures.append(closure) states.append(new_state) pending.append(new_state) @@ -88,7 +91,7 @@ def from_nfa(nfa, get_states=False): for (origin, symbol), destinations in nfa.map.items(): origin = states[origin] - origin[symbol] = [ states[d] for d in destinations ] + origin[symbol] = [states[d] for d in destinations] if get_states: return states[nfa.start], states @@ -96,11 +99,13 @@ def from_nfa(nfa, get_states=False): @staticmethod def move_by_state(symbol, *states): - return { s for state in states if state.has_transition(symbol) for s in state[symbol]} + 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 } + closure = {state for state in states} l = 0 while l != len(closure): @@ -108,7 +113,7 @@ def epsilon_closure_by_state(*states): tmp = [s for s in closure] for s in tmp: for epsilon_state in s.epsilon_transitions: - closure.add(epsilon_state) + closure.add(epsilon_state) return closure @property @@ -125,7 +130,7 @@ def get(self, symbol): return target[0] def __getitem__(self, symbol): - if symbol == '': + if symbol == "": return self.epsilon_transitions try: return self.transitions[symbol] @@ -133,7 +138,7 @@ def __getitem__(self, symbol): return None def __setitem__(self, symbol, value): - if symbol == '': + if symbol == "": self.epsilon_transitions = value else: self.transitions[symbol] = value @@ -166,52 +171,64 @@ def _visit(self, visited=None): 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)) + 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 '')) + 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)) + 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)) + G.add_edge(pydot.Edge(ids, id(end), label="ε", labeldistance=2)) visit(self) - G.add_edge(pydot.Edge('start', id(self), label='', style='dashed')) + 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') + return self.graph().create_svg().decode("utf8") except: pass def _repr_png_(self, name): try: - return self.graph().write_png(f'{name}.png') + return self.graph().write_png(f"{name}.png") 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) + return "\n".join(str(item) for item in state) + def lr0_formatter(state): try: - return '\n'.join(str(item)[:-4] for item in state) + return "\n".join(str(item)[:-4] for item in state) except TypeError: return str(state)[:-4] def empty_formatter(state): - return '' \ No newline at end of file + return "" diff --git a/src/compiler/cmp/evaluation.py b/src/compiler/cmp/evaluation.py index 65e6125ac..6e976b463 100644 --- a/src/compiler/cmp/evaluation.py +++ b/src/compiler/cmp/evaluation.py @@ -1,6 +1,7 @@ from .pycompiler import EOF from .tools import ShiftReduceParser + def evaluate_reverse_parse(right_parse, operations, tokens): if not right_parse or not operations or not tokens: return @@ -16,18 +17,20 @@ def evaluate_reverse_parse(right_parse, operations, tokens): 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.' + 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):] + synteticed = [None] + stack[-len(body) :] value = rule(None, synteticed) - stack[-len(body):] = [value] + stack[-len(body) :] = [value] else: stack.append(rule(None, None)) else: - raise Exception('Invalid action!!!') + raise Exception("Invalid action!!!") assert len(stack) == 1 assert isinstance(next(tokens).token_type, EOF) - return stack[0] \ No newline at end of file + return stack[0] diff --git a/src/compiler/cmp/grammar.py b/src/compiler/cmp/grammar.py index 269119f30..113701f05 100644 --- a/src/compiler/cmp/grammar.py +++ b/src/compiler/cmp/grammar.py @@ -6,131 +6,173 @@ # non-terminals -program = G.NonTerminal('', startSymbol=True) -class_list, def_class = G.NonTerminals(' ') -feature_list, def_attr, def_func = G.NonTerminals(' ') -param_list, param, expr_list = G.NonTerminals(' ') -expr, comp, arith, term, factor, atom = G.NonTerminals(' ') -s_comp, s_arith, s_term, s_factor = G.NonTerminals(' ') -func_call, arg_list, args = G.NonTerminals(' ') -case_def, block_def, loop_def, cond_def, let_def, assign_def = G.NonTerminals(' ') -branch_list, branch = G.NonTerminals(' ') -iden_list, iden = G.NonTerminals(' ') +program = G.NonTerminal("", startSymbol=True) +class_list, def_class = G.NonTerminals(" ") +feature_list, def_attr, def_func = G.NonTerminals( + " " +) +param_list, param, expr_list = G.NonTerminals(" ") +expr, comp, arith, term, factor, atom = G.NonTerminals( + " " +) +s_comp, s_arith, s_term, s_factor = G.NonTerminals( + " " +) +func_call, arg_list, args = G.NonTerminals(" ") +case_def, block_def, loop_def, cond_def, let_def, assign_def = G.NonTerminals( + " " +) +branch_list, branch = G.NonTerminals(" ") +iden_list, iden = G.NonTerminals(" ") # terminals -classx, inherits = G.Terminals('class inherits') -let, inx = G.Terminals('let in') -case, of, esac = G.Terminals('case of esac') -whilex, loop, pool = G.Terminals('while loop pool') -ifx, then, elsex, fi = G.Terminals('if then else fi') -isvoid, notx = G.Terminals('isvoid not') -semi, colon, comma, dot, opar, cpar, ocur, ccur, larrow, rarrow, at = G.Terminals('; : , . ( ) { } <- => @') -equal, plus, minus, star, div, less, leq, neg = G.Terminals('= + - * / < <= ~') -typeid, objectid, num, stringx, boolx, new = G.Terminals('typeid objectid int string bool new') +classx, inherits = G.Terminals("class inherits") +let, inx = G.Terminals("let in") +case, of, esac = G.Terminals("case of esac") +whilex, loop, pool = G.Terminals("while loop pool") +ifx, then, elsex, fi = G.Terminals("if then else fi") +isvoid, notx = G.Terminals("isvoid not") +semi, colon, comma, dot, opar, cpar, ocur, ccur, larrow, rarrow, at = G.Terminals( + "; : , . ( ) { } <- => @" +) +equal, plus, minus, star, div, less, leq, neg = G.Terminals("= + - * / < <= ~") +typeid, objectid, num, stringx, boolx, new = G.Terminals( + "typeid objectid int string bool new" +) # productions -program %= class_list, lambda h,s: ProgramNode(s[1]) - -class_list %= def_class, lambda h,s: [s[1]] -class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] - -def_class %= classx + typeid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[4]) -def_class %= classx + typeid + inherits + typeid + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2], s[6], s[4]) - -feature_list %= G.Epsilon, lambda h,s: [] -feature_list %= def_attr + feature_list, lambda h,s: [s[1]] + s[2] -feature_list %= def_func + feature_list, lambda h,s: [s[1]] + s[2] - -def_attr %= objectid + colon + typeid + semi, lambda h,s: AttrDeclarationNode(s[1], s[3]) -def_attr %= objectid + colon + typeid + larrow + expr + semi, lambda h,s: AttrDeclarationNode(s[1], s[3], s[5]) - -def_func %= objectid + opar + cpar + colon + typeid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], [], s[5], s[7]) -def_func %= objectid + opar + param_list + cpar + colon + typeid + ocur + expr + ccur + semi, lambda h,s: FuncDeclarationNode(s[1], s[3], s[6], s[8]) - -param_list %= param, lambda h,s: [s[1]] -param_list %= param + comma + param_list, lambda h,s: [s[1]] + s[3] -param %= objectid + colon + typeid, lambda h,s: (s[1], s[3]) - -expr %= comp, lambda h,s: s[1] -expr %= s_comp, lambda h,s: s[1] - -comp %= arith, lambda h,s: s[1] -comp %= arith + leq + arith, lambda h,s: LeqNode(s[1], s[3]) -comp %= arith + less + arith, lambda h,s: LessNode(s[1], s[3]) -comp %= arith + equal + arith, lambda h,s: EqualNode(s[1], s[3]) - -arith %= term, 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]) - -term %= factor, 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]) - -factor %= atom, lambda h,s: s[1] -factor %= opar + expr + cpar, lambda h,s: s[2] -factor %= isvoid + factor, lambda h,s: VoidNode(s[2]) -factor %= neg + factor, lambda h,s: NegNode(s[2]) -factor %= func_call, lambda h,s: s[1] -factor %= case_def, lambda h,s: s[1] -factor %= block_def, lambda h,s: s[1] -factor %= loop_def, lambda h,s: s[1] -factor %= cond_def, lambda h,s: s[1] - -atom %= num, lambda h,s: ConstantNumNode(s[1]) -atom %= stringx, lambda h,s: ConstantStringNode(s[1]) -atom %= boolx, lambda h,s: ConstantBoolNode(s[1]) -atom %= objectid, lambda h,s: VariableNode(s[1]) -atom %= new + typeid, lambda h,s: InstantiateNode(s[2]) - -func_call %= objectid + opar + arg_list + cpar, lambda h,s: CallNode(VariableNode('self'), s[1], s[3]) -func_call %= factor + dot + objectid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[3], s[5]) -func_call %= factor + at + typeid + dot + objectid + opar + arg_list + cpar, lambda h,s: CallNode(s[1], s[5], s[7], s[3]) - -arg_list %= G.Epsilon, lambda h,s: [] -arg_list %= args, lambda h,s: s[1] -args %= expr, lambda h,s: [s[1]] -args %= expr + comma + args , lambda h,s: [s[1]] + s[3] - -case_def %= case + expr + of + branch_list + esac, lambda h,s: CaseNode(s[2], s[4]) -branch_list %= branch, lambda h,s: [s[1]] -branch_list %= branch + branch_list, lambda h,s: [s[1]] + s[2] -branch %= objectid + colon + typeid + rarrow + expr + semi, lambda h,s: (s[1], s[3], s[5]) - -block_def %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2]) -expr_list %= expr + semi, lambda h,s: [s[1]] -expr_list %= expr + semi + expr_list, lambda h,s: [s[1]] + s[3] - -loop_def %= whilex + expr + loop + expr + pool, lambda h,s: LoopNode(s[2], s[4]) - -cond_def %= ifx + expr + then + expr + elsex + expr + fi, lambda h,s: ConditionalNode(s[2], s[4], s[6]) - -s_comp %= s_arith, lambda h,s: s[1] -s_comp %= arith + leq + s_arith, lambda h,s: LeqNode(s[1], s[3]) -s_comp %= arith + less + s_arith, lambda h,s: LessNode(s[1], s[3]) -s_comp %= arith + equal + s_arith, lambda h,s: EqualNode(s[1], s[3]) - -s_arith %= s_term, lambda h,s: s[1] -s_arith %= arith + plus + s_term, lambda h,s: PlusNode(s[1], s[3]) -s_arith %= arith + minus + s_term, lambda h,s: MinusNode(s[1], s[3]) - -s_term %= s_factor, lambda h,s: s[1] -s_term %= term + star + s_factor, lambda h,s: StarNode(s[1], s[3]) -s_term %= term + div + s_factor, lambda h,s: DivNode(s[1], s[3]) - -s_factor %= notx + expr, lambda h,s: NotNode(s[2]) -s_factor %= let_def, lambda h,s: s[1] -s_factor %= assign_def, lambda h,s: s[1] -s_factor %= isvoid + s_factor, lambda h,s: VoidNode(s[2]) -s_factor %= neg + s_factor, lambda h,s: NegNode(s[2]) - -let_def %= let + iden_list + inx + expr, lambda h,s: LetNode(s[2], s[4]) -iden_list %= iden, lambda h,s: [s[1]] -iden_list %= iden + comma + iden_list, lambda h,s: [s[1]] + s[3] -iden %= objectid + colon + typeid, lambda h,s: (s[1], s[3], None) -iden %= objectid + colon + typeid + larrow + expr, lambda h,s: (s[1], s[3], s[5]) - -assign_def %= objectid + larrow + expr, lambda h,s: AssignNode(s[1], s[3]) \ No newline at end of file +program %= class_list, lambda h, s: ProgramNode(s[1]) + +class_list %= def_class, lambda h, s: [s[1]] +class_list %= def_class + class_list, lambda h, s: [s[1]] + s[2] + +def_class %= ( + classx + typeid + ocur + feature_list + ccur + semi, + lambda h, s: ClassDeclarationNode(s[2], s[4]), +) +def_class %= ( + classx + typeid + inherits + typeid + ocur + feature_list + ccur + semi, + lambda h, s: ClassDeclarationNode(s[2], s[6], s[4]), +) + +feature_list %= G.Epsilon, lambda h, s: [] +feature_list %= def_attr + feature_list, lambda h, s: [s[1]] + s[2] +feature_list %= def_func + feature_list, lambda h, s: [s[1]] + s[2] + +def_attr %= objectid + colon + typeid + semi, lambda h, s: AttrDeclarationNode( + s[1], s[3] +) +def_attr %= ( + objectid + colon + typeid + larrow + expr + semi, + lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]), +) + +def_func %= ( + objectid + opar + cpar + colon + typeid + ocur + expr + ccur + semi, + lambda h, s: FuncDeclarationNode(s[1], [], s[5], s[7]), +) +def_func %= ( + objectid + opar + param_list + cpar + colon + typeid + ocur + expr + ccur + semi, + lambda h, s: FuncDeclarationNode(s[1], s[3], s[6], s[8]), +) + +param_list %= param, lambda h, s: [s[1]] +param_list %= param + comma + param_list, lambda h, s: [s[1]] + s[3] +param %= objectid + colon + typeid, lambda h, s: (s[1], s[3]) + +expr %= comp, lambda h, s: s[1] +expr %= s_comp, lambda h, s: s[1] + +comp %= arith, lambda h, s: s[1] +comp %= arith + leq + arith, lambda h, s: LeqNode(s[1], s[3]) +comp %= arith + less + arith, lambda h, s: LessNode(s[1], s[3]) +comp %= arith + equal + arith, lambda h, s: EqualNode(s[1], s[3]) + +arith %= term, 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]) + +term %= factor, 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]) + +factor %= atom, lambda h, s: s[1] +factor %= opar + expr + cpar, lambda h, s: s[2] +factor %= isvoid + factor, lambda h, s: VoidNode(s[2]) +factor %= neg + factor, lambda h, s: NegNode(s[2]) +factor %= func_call, lambda h, s: s[1] +factor %= case_def, lambda h, s: s[1] +factor %= block_def, lambda h, s: s[1] +factor %= loop_def, lambda h, s: s[1] +factor %= cond_def, lambda h, s: s[1] + +atom %= num, lambda h, s: ConstantNumNode(s[1]) +atom %= stringx, lambda h, s: ConstantStringNode(s[1]) +atom %= boolx, lambda h, s: ConstantBoolNode(s[1]) +atom %= objectid, lambda h, s: VariableNode(s[1]) +atom %= new + typeid, lambda h, s: InstantiateNode(s[2]) + +func_call %= objectid + opar + arg_list + cpar, lambda h, s: CallNode( + VariableNode("self"), s[1], s[3] +) +func_call %= factor + dot + objectid + opar + arg_list + cpar, lambda h, s: CallNode( + s[1], s[3], s[5] +) +func_call %= ( + factor + at + typeid + dot + objectid + opar + arg_list + cpar, + lambda h, s: CallNode(s[1], s[5], s[7], s[3]), +) + +arg_list %= G.Epsilon, lambda h, s: [] +arg_list %= args, lambda h, s: s[1] +args %= expr, lambda h, s: [s[1]] +args %= expr + comma + args, lambda h, s: [s[1]] + s[3] + +case_def %= case + expr + of + branch_list + esac, lambda h, s: CaseNode(s[2], s[4]) +branch_list %= branch, lambda h, s: [s[1]] +branch_list %= branch + branch_list, lambda h, s: [s[1]] + s[2] +branch %= objectid + colon + typeid + rarrow + expr + semi, lambda h, s: ( + s[1], + s[3], + s[5], +) + +block_def %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2]) +expr_list %= expr + semi, lambda h, s: [s[1]] +expr_list %= expr + semi + expr_list, lambda h, s: [s[1]] + s[3] + +loop_def %= whilex + expr + loop + expr + pool, lambda h, s: LoopNode(s[2], s[4]) + +cond_def %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: ConditionalNode( + s[2], s[4], s[6] +) + +s_comp %= s_arith, lambda h, s: s[1] +s_comp %= arith + leq + s_arith, lambda h, s: LeqNode(s[1], s[3]) +s_comp %= arith + less + s_arith, lambda h, s: LessNode(s[1], s[3]) +s_comp %= arith + equal + s_arith, lambda h, s: EqualNode(s[1], s[3]) + +s_arith %= s_term, lambda h, s: s[1] +s_arith %= arith + plus + s_term, lambda h, s: PlusNode(s[1], s[3]) +s_arith %= arith + minus + s_term, lambda h, s: MinusNode(s[1], s[3]) + +s_term %= s_factor, lambda h, s: s[1] +s_term %= term + star + s_factor, lambda h, s: StarNode(s[1], s[3]) +s_term %= term + div + s_factor, lambda h, s: DivNode(s[1], s[3]) + +s_factor %= notx + expr, lambda h, s: NotNode(s[2]) +s_factor %= let_def, lambda h, s: s[1] +s_factor %= assign_def, lambda h, s: s[1] +s_factor %= isvoid + s_factor, lambda h, s: VoidNode(s[2]) +s_factor %= neg + s_factor, lambda h, s: NegNode(s[2]) + +let_def %= let + iden_list + inx + expr, lambda h, s: LetNode(s[2], s[4]) +iden_list %= iden, lambda h, s: [s[1]] +iden_list %= iden + comma + iden_list, lambda h, s: [s[1]] + s[3] +iden %= objectid + colon + typeid, lambda h, s: (s[1], s[3], None) +iden %= objectid + colon + typeid + larrow + expr, lambda h, s: (s[1], s[3], s[5]) + +assign_def %= objectid + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) diff --git a/src/compiler/cmp/nfa_dfa.py b/src/compiler/cmp/nfa_dfa.py index 645aa2732..b3953cce6 100644 --- a/src/compiler/cmp/nfa_dfa.py +++ b/src/compiler/cmp/nfa_dfa.py @@ -1,5 +1,6 @@ import pydot + class NFA: def __init__(self, states, finals, transitions, start=0): self.states = states @@ -7,39 +8,47 @@ def __init__(self, states, finals, transitions, start=0): self.finals = set(finals) self.map = transitions self.vocabulary = set() - self.transitions = { state: {} for state in range(states) } + self.transitions = {state: {} for state in range(states)} for (origin, symbol), destinations in transitions.items(): - assert hasattr(destinations, '__iter__'), 'Invalid collection of states' + assert hasattr(destinations, "__iter__"), "Invalid collection of states" self.transitions[origin][symbol] = destinations self.vocabulary.add(symbol) - self.vocabulary.discard('') + self.vocabulary.discard("") def epsilon_transitions(self, state): - assert state in self.transitions, 'Invalid state' + assert state in self.transitions, "Invalid state" try: - return self.transitions[state][''] + return self.transitions[state][""] except KeyError: return () def graph(self): - G = pydot.Dot(rankdir='LR', margin=0.1) - G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + G = pydot.Dot(rankdir="LR", margin=0.1) + G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) for (start, tran), destinations in self.map.items(): - tran = 'ε' if tran == '' else tran - G.add_node(pydot.Node(start, shape='circle', style='bold' if start in self.finals else '')) + tran = "ε" if tran == "" else tran + G.add_node( + pydot.Node( + start, shape="circle", style="bold" if start in self.finals else "" + ) + ) for end in destinations: - G.add_node(pydot.Node(end, shape='circle', style='bold' if end in self.finals else '')) + G.add_node( + pydot.Node( + end, shape="circle", style="bold" if end in self.finals else "" + ) + ) G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) - G.add_edge(pydot.Edge('start', self.start, label='', style='dashed')) + G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) return G def _repr_svg_(self): try: - return self.graph().create_svg().decode('utf8') + return self.graph().create_svg().decode("utf8") except: pass @@ -50,28 +59,32 @@ def _repr_png_(self): pass - - - class DFA(NFA): - def graph(self): - G = pydot.Dot(rankdir='LR', margin=0.1) - G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + G = pydot.Dot(rankdir="LR", margin=0.1) + G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) for (start, tran), destinations in self.map.items(): - tran = 'ε' if tran == '' else tran - G.add_node(pydot.Node(start, shape='circle', style='bold' if start in self.finals else '')) + tran = "ε" if tran == "" else tran + G.add_node( + pydot.Node( + start, shape="circle", style="bold" if start in self.finals else "" + ) + ) for end in destinations: - G.add_node(pydot.Node(end, shape='circle', style='bold' if end in self.finals else '')) + G.add_node( + pydot.Node( + end, shape="circle", style="bold" if end in self.finals else "" + ) + ) G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) - G.add_edge(pydot.Edge('start', self.start, label='', style='dashed')) + G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) return G def _repr_svg_(self): try: - return self.graph().create_svg().decode('utf8') + return self.graph().create_svg().decode("utf8") except: pass @@ -85,7 +98,7 @@ def __init__(self, states, finals, transitions, start=0): assert all(isinstance(value, int) for value in transitions.values()) assert all(len(symbol) > 0 for origin, symbol in transitions) - transitions = { key: [value] for key, value in transitions.items() } + transitions = {key: [value] for key, value in transitions.items()} NFA.__init__(self, states, finals, transitions, start) self.current = start @@ -104,4 +117,4 @@ def recognize(self, string): for symbol in string: if not self._move(symbol): return False - return self.current in self.finals \ No newline at end of file + return self.current in self.finals diff --git a/src/compiler/cmp/pycompiler.py b/src/compiler/cmp/pycompiler.py index 3ef02deeb..9d1100913 100644 --- a/src/compiler/cmp/pycompiler.py +++ b/src/compiler/cmp/pycompiler.py @@ -1,7 +1,7 @@ import json -class Symbol(object): +class Symbol(object): def __init__(self, name, grammar): self.Name = name self.Grammar = grammar @@ -32,14 +32,12 @@ def IsEpsilon(self): def __len__(self): return 1 -class NonTerminal(Symbol): - +class NonTerminal(Symbol): def __init__(self, name, grammar): super().__init__(name, grammar) self.productions = [] - def __imod__(self, other): if isinstance(other, (Sentence)): @@ -53,7 +51,9 @@ def __imod__(self, other): 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) == 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): @@ -91,8 +91,8 @@ def IsNonTerminal(self): def IsEpsilon(self): return False -class Terminal(Symbol): +class Terminal(Symbol): def __init__(self, name, grammar): super().__init__(name, grammar) @@ -108,13 +108,13 @@ def IsNonTerminal(self): def IsEpsilon(self): return False -class EOF(Terminal): +class EOF(Terminal): def __init__(self, Grammar): - super().__init__('$', Grammar) + super().__init__("$", Grammar) -class Sentence(object): +class Sentence(object): def __init__(self, *args): self._symbols = tuple(x for x in args if not x.IsEpsilon) self.hash = hash(self._symbols) @@ -162,8 +162,8 @@ def __hash__(self): def IsEpsilon(self): return False -class SentenceList(object): +class SentenceList(object): def __init__(self, *args): self._sentences = list(args) @@ -186,16 +186,14 @@ def __or__(self, other): class Epsilon(Terminal, Sentence): - def __init__(self, grammar): - super().__init__('epsilon', grammar) - + super().__init__("epsilon", grammar) def __str__(self): return "e" def __repr__(self): - return 'epsilon' + return "epsilon" def __iter__(self): yield from () @@ -216,8 +214,8 @@ def __hash__(self): def IsEpsilon(self): return True -class Production(object): +class Production(object): def __init__(self, nonTerminal, sentence): self.Left = nonTerminal @@ -225,17 +223,21 @@ def __init__(self, nonTerminal, sentence): def __str__(self): - return '%s := %s' % (self.Left, self.Right) + return "%s := %s" % (self.Left, self.Right) def __repr__(self): - return '%s -> %s' % (self.Left, self.Right) + 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 + return ( + isinstance(other, Production) + and self.Left == other.Left + and self.Right == other.Right + ) def __hash__(self): return hash((self.Left, self.Right)) @@ -244,8 +246,8 @@ def __hash__(self): def IsEpsilon(self): return self.Right.IsEpsilon -class AttributeProduction(Production): +class AttributeProduction(Production): def __init__(self, nonTerminal, sentence, attributes): if not isinstance(sentence, Sentence) and isinstance(sentence, Symbol): sentence = Sentence(sentence) @@ -254,16 +256,15 @@ def __init__(self, nonTerminal, sentence, attributes): self.attributes = attributes def __str__(self): - return '%s := %s' % (self.Left, self.Right) + return "%s := %s" % (self.Left, self.Right) def __repr__(self): - return '%s -> %s' % (self.Left, self.Right) + return "%s -> %s" % (self.Left, self.Right) def __iter__(self): yield self.Left yield self.Right - @property def IsEpsilon(self): return self.Right.IsEpsilon @@ -272,8 +273,8 @@ def IsEpsilon(self): def syntetice(self): pass -class Grammar(): +class Grammar: def __init__(self): self.Productions = [] @@ -285,15 +286,15 @@ def __init__(self): self.Epsilon = Epsilon(self) self.EOF = EOF(self) - self.symbDict = { '$': self.EOF } + self.symbDict = {"$": self.EOF} - def NonTerminal(self, name, startSymbol = False): + def NonTerminal(self, name, startSymbol=False): name = name.strip() if not name: raise Exception("Empty name") - term = NonTerminal(name,self) + term = NonTerminal(name, self) if startSymbol: @@ -312,7 +313,6 @@ def NonTerminals(self, names): return ans - def Add_Production(self, production): if len(self.Productions) == 0: @@ -323,7 +323,6 @@ def Add_Production(self, production): production.Left.productions.append(production) self.Productions.append(production) - def Terminal(self, name): name = name.strip() @@ -341,24 +340,23 @@ def Terminals(self, names): return ans - def __str__(self): - mul = '%s, ' + mul = "%s, " - ans = 'Non-Terminals:\n\t' + ans = "Non-Terminals:\n\t" - nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' + nonterminals = mul * (len(self.nonTerminals) - 1) + "%s\n" ans += nonterminals % tuple(self.nonTerminals) - ans += 'Terminals:\n\t' + ans += "Terminals:\n\t" - terminals = mul * (len(self.terminals)-1) + '%s\n' + terminals = mul * (len(self.terminals) - 1) + "%s\n" ans += terminals % tuple(self.terminals) - ans += 'Productions:\n\t' + ans += "Productions:\n\t" ans += str(self.Productions) @@ -383,12 +381,15 @@ def to_json(self): for s in p.Right: body.append(s.Name) - productions.append({'Head':head, 'Body':body}) + 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} + 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] + # [{'Head':p.Left.Name, "Body": [s.Name for s in p.Right]} for p in self.Productions] return json.dumps(d) @staticmethod @@ -396,17 +397,17 @@ def from_json(data): data = json.loads(data) G = Grammar() - dic = {'epsilon':G.Epsilon} + dic = {"epsilon": G.Epsilon} - for term in data['Terminals']: + for term in data["Terminals"]: dic[term] = G.Terminal(term) - for noTerm in data['NonTerminals']: + 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']]) + for p in data["Productions"]: + head = p["Head"] + dic[head] %= Sentence(*[dic[term] for term in p["Body"]]) return G @@ -441,19 +442,20 @@ def AugmentedGrammar(self, force=False): # S, self.startSymbol, SS = self.startSymbol, None, self.NonTerminal('S\'', True) S = G.startSymbol G.startSymbol = None - SS = G.NonTerminal('S\'', True) + SS = G.NonTerminal("S'", True) if G.pType is AttributeProduction: - SS %= S + G.Epsilon, lambda x : x + SS %= S + G.Epsilon, lambda x: x else: SS %= S + G.Epsilon return G else: return self.copy() - #endchange -class Item: + # endchange + +class Item: def __init__(self, production, pos, lookaheads=[]): self.production = production self.pos = pos @@ -462,7 +464,7 @@ def __init__(self, production, pos, lookaheads=[]): def __str__(self): s = str(self.production.Left) + " -> " if len(self.production.Right) > 0: - for i,c in enumerate(self.production.Right): + for i, c in enumerate(self.production.Right): if i == self.pos: s += "." s += str(self.production.Right[i]) @@ -476,16 +478,15 @@ def __str__(self): 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)) + (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)) + return hash((self.production, self.pos, self.lookaheads)) @property def IsReduceItem(self): @@ -500,13 +501,13 @@ def NextSymbol(self): def NextItem(self): if self.pos < len(self.production.Right): - return Item(self.production,self.pos+1,self.lookaheads) + 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 ] + 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 + return Item(self.production, self.pos) diff --git a/src/compiler/cmp/regex.py b/src/compiler/cmp/regex.py index 82e490c3e..d711462aa 100644 --- a/src/compiler/cmp/regex.py +++ b/src/compiler/cmp/regex.py @@ -3,12 +3,28 @@ from .tools import evaluate_parse from .tools import metodo_predictivo_no_recursivo from .tools import nfa_to_dfa -from .tools import automata_union, automata_concatenation, automata_closure, automata_minimization +from .tools import ( + automata_union, + automata_concatenation, + automata_closure, + automata_minimization, +) from .tools import get_printer -from .tools import Node, AtomicNode, UnaryNode, BinaryNode, EpsilonNode, SymbolNode, ClosureNode, UnionNode, ConcatNode +from .tools import ( + Node, + AtomicNode, + UnaryNode, + BinaryNode, + EpsilonNode, + SymbolNode, + ClosureNode, + UnionNode, + ConcatNode, +) from pprint import pprint as pp import pydot + class Regex: def __init__(self, regular_exp): @@ -25,14 +41,14 @@ def __call__(self, string): return self.mini.recognize(string) def __repr__(self): - return 'Regex: ' + self.regular_exp + return "Regex: " + self.regular_exp G = Grammar() -E = G.NonTerminal('E', True) -T, F, A, X, Y, Z = G.NonTerminals('T F A X Y Z') -pipe, star, opar, cpar, symbol, epsilon = G.Terminals('| * ( ) symbol ε') +E = G.NonTerminal("E", True) +T, F, A, X, Y, Z = G.NonTerminals("T F A X Y Z") +pipe, star, opar, cpar, symbol, epsilon = G.Terminals("| * ( ) symbol ε") # > PRODUCTIONS E %= T + X, lambda h, s: s[2], None, lambda h, s: s[1] @@ -55,7 +71,6 @@ def __repr__(self): A %= epsilon, lambda h, s: EpsilonNode(s[1]) - def regex_tokenizer(text, G, skip_whitespaces=True): tokens = [] jump = False @@ -65,23 +80,23 @@ def regex_tokenizer(text, G, skip_whitespaces=True): for char in text: if skip_whitespaces and char.isspace(): continue - elif(char == '\\' and not jump): + elif char == "\\" and not jump: jump = True - elif(char == '*' and not jump): - tokens.append(Token('*', star)) - elif(char == '(' and not jump): - tokens.append(Token('(', opar)) - elif(char == ')' and not jump): - tokens.append(Token(')', cpar)) - elif(char == '|' and not jump): - tokens.append(Token('|', pipe)) - elif(char == 'ε' and not jump): - tokens.append(Token('ε', epsilon)) + elif char == "*" and not jump: + tokens.append(Token("*", star)) + elif char == "(" and not jump: + tokens.append(Token("(", opar)) + elif char == ")" and not jump: + tokens.append(Token(")", cpar)) + elif char == "|" and not jump: + tokens.append(Token("|", pipe)) + elif char == "ε" and not jump: + tokens.append(Token("ε", epsilon)) else: tokens.append(Token(char, symbol)) jump = False - tokens.append(Token('$', G.EOF)) + tokens.append(Token("$", G.EOF)) return tokens diff --git a/src/compiler/cmp/semantic.py b/src/compiler/cmp/semantic.py index e4c9eaa45..cf9002e9f 100644 --- a/src/compiler/cmp/semantic.py +++ b/src/compiler/cmp/semantic.py @@ -7,6 +7,7 @@ class SemanticError(Exception): def text(self): return self.args[0] + class Attribute: def __init__(self, name, typex, idx=None): self.name = name @@ -14,13 +15,16 @@ def __init__(self, name, typex, idx=None): self.idx = idx def __str__(self): - return f'[attrib] {self.name} : {self.type.name};' + return f"[attrib] {self.name} : {self.type.name};" def __repr__(self): return str(self) + class Method: - def __init__(self, name, param_names, param_types, return_type, param_idx, ridx=None): + def __init__( + self, name, param_names, param_types, return_type, param_idx, ridx=None + ): self.name = name self.param_names = param_names self.param_types = param_types @@ -29,16 +33,21 @@ def __init__(self, name, param_names, param_types, return_type, param_idx, ridx= self.ridx = ridx 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};' + 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 + 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): + def __init__(self, name: str): self.name = name self.attributes = [] self.methods = [] @@ -46,21 +55,25 @@ def __init__(self, name:str): def set_parent(self, parent): if self.parent is not None: - raise SemanticError(f'Parent type is already set for {self.name}.') + raise SemanticError(f"Parent type is already set for {self.name}.") self.parent = parent - def get_attribute(self, name:str): + 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}.') + 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}.') + raise SemanticError( + f'Attribute "{name}" is not defined in {self.name}.' + ) - def define_attribute(self, name:str, typex, idx=None): + def define_attribute(self, name: str, typex, idx=None): try: self.get_attribute(name) except SemanticError: @@ -68,9 +81,11 @@ def define_attribute(self, name:str, typex, idx=None): self.attributes.append(attribute) return attribute else: - raise SemanticError(f'Attribute "{name}" is already defined in {self.name}.') + raise SemanticError( + f'Attribute "{name}" is already defined in {self.name}.' + ) - def get_method(self, name:str): + def get_method(self, name: str): try: return next(method for method in self.methods if method.name == name) except StopIteration: @@ -81,7 +96,15 @@ def get_method(self, name:str): 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, param_idx:list, ridx=None): + def define_method( + self, + name: str, + param_names: list, + param_types: list, + return_type, + param_idx: list, + ridx=None, + ): if name in (method.name for method in self.methods): raise SemanticError(f'Method "{name}" already defined in {self.name}') @@ -90,7 +113,9 @@ def define_method(self, name:str, param_names:list, param_types:list, return_typ return method def all_attributes(self, clean=True): - plain = OrderedDict() if self.parent is None else self.parent.all_attributes(False) + 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 @@ -106,7 +131,7 @@ def update_attr(self, attr_name, attr_type): if item.name == attr_name: self.attributes[i] = Attribute(attr_name, attr_type) break - + def update_method_rtype(self, method_name, rtype): for i, item in enumerate(self.methods): if item.name == method_name: @@ -120,25 +145,30 @@ def update_method_param(self, method_name, param_type, param_idx): break def conforms_to(self, other): - return other.bypass() or self.name == other.name or self.parent is not None and self.parent.conforms_to(other) + return ( + other.bypass() + or self.name == other.name + or self.parent is not None + and self.parent.conforms_to(other) + ) def bypass(self): return False - + def can_be_inherited(self): return True def __str__(self): - output = f'type {self.name}' - parent = '' if self.parent is None else f' : {self.parent.name}' + 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) - output += '\n' if self.methods else '' - output += '}\n' + 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) + output += "\n" if self.methods else "" + output += "}\n" return output def __repr__(self): @@ -147,9 +177,10 @@ def __repr__(self): def __eq__(self, other): return self.conforms_to(other) and other.conforms_to(self) + class ErrorType(Type): def __init__(self): - Type.__init__(self, '') + Type.__init__(self, "") def conforms_to(self, other): return True @@ -163,21 +194,23 @@ def __eq__(self, other): class ObjectType(Type): def __init__(self): - Type.__init__(self, 'Object') + Type.__init__(self, "Object") def __eq__(self, other): - return other.name == self.name or isinstance(other, ObjectType) + return other.name == self.name or isinstance(other, ObjectType) + class IOType(Type): def __init__(self): - Type.__init__(self, 'IO') + Type.__init__(self, "IO") def __eq__(self, other): return other.name == self.name or isinstance(other, IOType) + class StringType(Type): def __init__(self): - Type.__init__(self, 'String') + Type.__init__(self, "String") def __eq__(self, other): return other.name == self.name or isinstance(other, StringType) @@ -185,9 +218,10 @@ def __eq__(self, other): def can_be_inherited(self): return False + class BoolType(Type): def __init__(self): - Type.__init__(self, 'Bool') + Type.__init__(self, "Bool") def __eq__(self, other): return other.name == self.name or isinstance(other, BoolType) @@ -195,27 +229,30 @@ def __eq__(self, other): def can_be_inherited(self): return False + class IntType(Type): def __init__(self): - Type.__init__(self, 'Int') + Type.__init__(self, "Int") def __eq__(self, other): return other.name == self.name or isinstance(other, IntType) - + def can_be_inherited(self): return False + class SelfType(Type): def __init__(self, fixed=None): - Type.__init__(self, 'SELF_TYPE') + Type.__init__(self, "SELF_TYPE") self.fixed_type = fixed - + def can_be_inherited(self): return False + class AutoType(Type): def __init__(self): - Type.__init__(self, 'AUTO_TYPE') + Type.__init__(self, "AUTO_TYPE") def can_be_inherited(self): return False @@ -225,36 +262,42 @@ def conforms_to(self, other): def bypass(self): return True - + class Context: def __init__(self): self.types = {} - def create_type(self, name:str): + def create_type(self, name: str): if name in self.types: - raise SemanticError(f'Type with the same name ({name}) already in context.') + 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): + 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}' + 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, idx): self.name = name self.type = vtype self.idx = idx + class Scope: def __init__(self, parent=None): self.locals = [] @@ -280,7 +323,11 @@ 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 is not None 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 @@ -294,7 +341,11 @@ def update_variable(self, vname, vtype, index=None): if item.name == vname: self.locals[i] = VariableInfo(vname, vtype, item.idx) return True - return self.parent.update_variable(vname, vtype, self.index) if self.parent is not None else False + return ( + self.parent.update_variable(vname, vtype, self.index) + if self.parent is not None + else False + ) class InferencerManager: @@ -330,7 +381,7 @@ def auto_to_type(self, idx, typex): try: assert not isinstance(typex, ErrorType) assert not any(item.name == typex.name for item in self.conforms_to[idx]) - + self.conforms_to[idx].append(typex) except AssertionError: pass @@ -341,7 +392,7 @@ def type_to_auto(self, idx, typex): try: assert not isinstance(typex, ErrorType) assert not any(item.name == typex.name for item in self.conformed_by[idx]) - + self.conformed_by[idx].append(typex) except AssertionError: pass @@ -370,7 +421,7 @@ def infer_all(self): change = False for i in range(self.count): change |= self.infer(i) - + return change def infer_object(self, obj_type): @@ -387,7 +438,6 @@ def find(typex): return i return len(path) - for item in types: current = [] while item is not None: @@ -396,7 +446,7 @@ def find(typex): current.append(item) item = item.parent continue - + assert idx == len(path) - 1 or len(current) == 0 break current.reverse() @@ -405,7 +455,6 @@ def find(typex): return path[-1] - def LCA(types): if len(types) == 0: return None @@ -436,4 +485,4 @@ def LCA(types): current = current.parent # This part of the code is supposed to be unreachable - return None \ No newline at end of file + return None diff --git a/src/compiler/cmp/tools.py b/src/compiler/cmp/tools.py index b4b5f7186..702e138db 100644 --- a/src/compiler/cmp/tools.py +++ b/src/compiler/cmp/tools.py @@ -6,15 +6,16 @@ from itertools import islice - 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 @@ -27,6 +28,7 @@ def evaluate(self): def operate(value): raise NotImplementedError() + class BinaryNode(Node): def __init__(self, left, right): self.left = left @@ -41,32 +43,42 @@ def evaluate(self): def operate(lvalue, rvalue): raise NotImplementedError() + class EpsilonNode(AtomicNode): def evaluate(self): return NFA(1, [0], {}) + class SymbolNode(AtomicNode): def evaluate(self): s = self.lex - return NFA(2, [1], {(0, s) : [1],}) + return NFA( + 2, + [1], + { + (0, s): [1], + }, + ) + class ClosureNode(UnaryNode): @staticmethod def operate(value): return automata_closure(value) + class UnionNode(BinaryNode): @staticmethod def operate(lvalue, rvalue): return automata_union(lvalue, rvalue) + class ConcatNode(BinaryNode): @staticmethod def operate(lvalue, rvalue): return automata_concatenation(lvalue, rvalue) - def upd_table(table, head, symbol, production): if not head in table: table[head] = {} @@ -74,7 +86,8 @@ def upd_table(table, head, symbol, production): table[head][symbol] = [] if production not in table[head][symbol]: table[head][symbol].append(production) - return (len(table[head][symbol]) <= 1) + return len(table[head][symbol]) <= 1 + def compute_local_first(firsts, alpha): first_alpha = ContainerSet() @@ -97,6 +110,7 @@ def compute_local_first(firsts, alpha): return first_alpha + def compute_firsts(G): firsts = {} change = True @@ -128,8 +142,9 @@ def compute_firsts(G): return firsts + def compute_follows(G, firsts): - follows = { } + follows = {} change = True local_firsts = {} @@ -152,13 +167,16 @@ def compute_follows(G, firsts): try: beta_f = local_firsts[alpha, i] except KeyError: - beta_f = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None)) + beta_f = local_firsts[alpha, i] = compute_local_first( + firsts, islice(alpha, i + 1, None) + ) change |= follows[Y].update(beta_f) if beta_f.contains_epsilon: change |= follows[Y].update(follow_X) return follows + def build_parsing_table(G, firsts, follows): M = {} ok = True @@ -176,6 +194,7 @@ def build_parsing_table(G, firsts, follows): return M, ok + def deprecated_metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): if M is None: @@ -187,7 +206,7 @@ def deprecated_metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=No def parser(w): - stack = [G.EOF, G.startSymbol] + stack = [G.EOF, G.startSymbol] cursor = 0 output = [] @@ -212,12 +231,14 @@ def parser(w): return parser -def metodo_predictivo_no_recursivo(G, M = None): + +def metodo_predictivo_no_recursivo(G, M=None): parser = deprecated_metodo_predictivo_no_recursivo(G, M) + def updated(tokens): return parser([t.token_type for t in tokens]) - return updated + return updated def evaluate_parse(left_parse, tokens): @@ -231,6 +252,7 @@ def evaluate_parse(left_parse, tokens): assert isinstance(next(tokens).token_type, EOF) return result + def evaluate(production, left_parse, tokens, inherited_value=None): _, body = production attributes = production.attributes @@ -257,7 +279,6 @@ def evaluate(production, left_parse, tokens, inherited_value=None): return attr(inherited, synteticed) - def expand(item, firsts): next_symbol = item.NextSymbol if next_symbol is None or not next_symbol.IsNonTerminal: @@ -270,6 +291,7 @@ def expand(item, firsts): assert not lookaheads.contains_epsilon return [Item(prod, 0, lookaheads) for prod in next_symbol.productions] + def compress(items): centers = {} @@ -281,7 +303,10 @@ def compress(items): centers[center] = lookaheads = set() lookaheads.update(item.lookaheads) - return {Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items()} + return { + Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items() + } + def closure_lr1(items, firsts): closure = ContainerSet(*items) @@ -298,13 +323,17 @@ def closure_lr1(items, firsts): 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`' + 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' + assert len(G.startSymbol.productions) == 1, "Grammar must be augmented" firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) @@ -340,6 +369,7 @@ def build_LR1_automaton(G): automaton.set_formatter(lambda x: "") return automaton + class LR1Parser(ShiftReduceParser): def _build_parsing_table(self): self.ok = True @@ -347,9 +377,10 @@ def _build_parsing_table(self): 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') + 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}' + node.tag = f"I{i}" for node in automaton: idx = node.idx @@ -357,17 +388,30 @@ def _build_parsing_table(self): if item.IsReduceItem: prod = item.production if prod.Left == G.startSymbol: - self.ok &= upd_table(self.action, idx, G.EOF, (ShiftReduceParser.OK, '')) + 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)) + 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)) + 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) - + self.ok &= upd_table( + self.goto, idx, next_symbol, node[next_symbol.Name][0].idx + ) def move(automaton, states, symbol): @@ -380,16 +424,17 @@ def move(automaton, states, symbol): pass return moves + def epsilon_closure(automaton, states): - pending = [ s for s in states ] # equivalente a list(states) pero me gusta así :p - closure = { s for s in states } # equivalente a set(states) pero me gusta así :p + pending = [s for s in states] # equivalente a list(states) pero me gusta así :p + closure = {s for s in states} # equivalente a set(states) pero me gusta así :p while pending: state = pending.pop() - l = move(automaton, [state], '') + l = move(automaton, [state], "") for i in l: - if(i in closure): + if i in closure: pass else: closure.add(i) @@ -397,15 +442,16 @@ def epsilon_closure(automaton, states): return ContainerSet(*closure) + def nfa_to_dfa(automaton): transitions = {} start = epsilon_closure(automaton, [automaton.start]) start.id = 0 start.is_final = any(s in automaton.finals for s in start) - states = [ start ] + states = [start] - pending = [ start ] + pending = [start] while pending: state = pending.pop() @@ -417,7 +463,7 @@ def nfa_to_dfa(automaton): continue for s in states: - if(s == new_state): + if s == new_state: new_state = s break else: @@ -428,16 +474,15 @@ def nfa_to_dfa(automaton): try: transitions[state.id, symbol] - assert False, 'Invalid DFA!!!' + assert False, "Invalid DFA!!!" except KeyError: transitions[state.id, symbol] = new_state.id - finals = [ state.id for state in states if state.is_final ] + finals = [state.id for state in states if state.is_final] dfa = DFA(len(states), finals, transitions) return dfa - def distinguish_states(group, automaton, partition): split = {} vocabulary = tuple(automaton.vocabulary) @@ -465,15 +510,17 @@ def distinguish_states(group, automaton, partition): else: split[member.value] = [member.value] + return [group for group in split.values()] - return [ group for group in split.values()] def state_minimization(automaton): partition = DisjointSet(*range(automaton.states)) ## partition = { NON-FINALS | FINALS } finals = list(automaton.finals) - non_finals = [state for state in range(automaton.states) if not state in automaton.finals] + non_finals = [ + state for state in range(automaton.states) if not state in automaton.finals + ] partition.merge(finals) partition.merge(non_finals) @@ -493,6 +540,7 @@ def state_minimization(automaton): return partition + def automata_minimization(automaton): partition = state_minimization(automaton) @@ -505,7 +553,7 @@ def automata_minimization(automaton): new_dest = states.index(partition[destinations[0]].representative) try: - transitions[i,symbol] + transitions[i, symbol] assert False except KeyError: transitions[i, symbol] = new_dest @@ -516,6 +564,7 @@ def automata_minimization(automaton): return DFA(len(states), finals, transitions, start) + def automata_union(a1, a2): transitions = {} @@ -532,24 +581,24 @@ def automata_union(a1, a2): other = [q + d2 for q in destinations] transitions[origin + d2, symbol] = other - - transitions[start, ''] = [a1.start + d1, a2.start + d2] + transitions[start, ""] = [a1.start + d1, a2.start + d2] for i in a1.finals: try: - transitions[i + d1, ''].add(final) + transitions[i + d1, ""].add(final) except KeyError: - transitions[i + d1, ''] = [final] + transitions[i + d1, ""] = [final] for i in a2.finals: try: - transitions[i + d2, ''].add(final) + transitions[i + d2, ""].add(final) except KeyError: - transitions[i + d2, ''] = [final] + transitions[i + d2, ""] = [final] states = a1.states + a2.states + 2 - finals = { final } + finals = {final} return NFA(states, finals, transitions, start) + def automata_concatenation(a1, a2): transitions = {} @@ -562,27 +611,27 @@ def automata_concatenation(a1, a2): other = [q + d1 for q in destinations] transitions[origin + d1, symbol] = other - for (origin, symbol), destinations in a2.map.items(): other = [q + d2 for q in destinations] transitions[origin + d2, symbol] = other for i in a1.finals: try: - transitions[i + d1, ''].add(a2.start + d2) + transitions[i + d1, ""].add(a2.start + d2) except KeyError: - transitions[i + d1, ''] = [a2.start + d2] + transitions[i + d1, ""] = [a2.start + d2] for i in a2.finals: try: - transitions[i + d2, ''].append(final) + transitions[i + d2, ""].append(final) except KeyError: - transitions[i + d2, ''] = [final] + transitions[i + d2, ""] = [final] states = a1.states + a2.states + 1 - finals = { final } + finals = {final} return NFA(states, finals, transitions, start) + def automata_closure(a1): transitions = {} @@ -594,48 +643,51 @@ def automata_closure(a1): other = [q + d1 for q in destinations] transitions[origin + d1, symbol] = other - transitions[start, ''] = [final, a1.start + d1] + transitions[start, ""] = [final, a1.start + d1] for i in a1.finals: try: - transitions[i + d1, ''].add(final) + transitions[i + d1, ""].add(final) except KeyError: - transitions[i + d1, ''] = [final] + transitions[i + d1, ""] = [final] try: - transitions[final, ''].add(start) + transitions[final, ""].add(start) except: - transitions[final,''] = [start] + transitions[final, ""] = [start] - states = a1.states + 2 - finals = { final } + states = a1.states + 2 + finals = {final} return NFA(states, finals, transitions, start) - -def get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode, ): +def get_printer( + AtomicNode=AtomicNode, + UnaryNode=UnaryNode, + BinaryNode=BinaryNode, +): class PrintVisitor(object): - @visitor.on('node') + @visitor.on("node") def visit(self, node, tabs=0): pass @visitor.when(UnaryNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__}' + ans = "\t" * tabs + f"\\__ {node.__class__.__name__}" child = self.visit(node.node, tabs + 1) - return f'{ans}\n{child}' + return f"{ans}\n{child}" @visitor.when(BinaryNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + 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}' + 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}' + return "\t" * tabs + f"\\__ {node.__class__.__name__}: {node.lex}" printer = PrintVisitor() - return (lambda ast: printer.visit(ast)) + return lambda ast: printer.visit(ast) diff --git a/src/compiler/cmp/utils.py b/src/compiler/cmp/utils.py index 329502679..489efb039 100644 --- a/src/compiler/cmp/utils.py +++ b/src/compiler/cmp/utils.py @@ -1,5 +1,6 @@ from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon + class ContainerSet: def __init__(self, *values, contains_epsilon=False): self.set = set(values) @@ -42,7 +43,7 @@ def __len__(self): return len(self.set) + int(self.contains_epsilon) def __str__(self): - return '%s-%s' % (str(self.set), self.contains_epsilon) + return "%s-%s" % (str(self.set), self.contains_epsilon) def __repr__(self): return str(self) @@ -56,37 +57,49 @@ def __nonzero__(self): 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 + return ( + isinstance(other, ContainerSet) + and self.set == other.set + and self.contains_epsilon == other.contains_epsilon + ) -def inspect(item, grammar_name='G', mapper=None): +def inspect(item, grammar_name="G", mapper=None): try: return mapper[item] - except (TypeError, KeyError ): + 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}}' + 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})' + 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' + return f"{grammar_name}.EOF" elif isinstance(item, Epsilon): - return f'{grammar_name}.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})' + 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})' + return f"Production({left}, {right})" elif isinstance(item, tuple) or isinstance(item, list): - ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') + 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}') + raise ValueError(f"Invalid: {item}") + def pprint(item, header=""): if header: @@ -94,15 +107,16 @@ def pprint(item, header=""): if isinstance(item, dict): for key, value in item.items(): - print(f'{key} ---> {value}') + print(f"{key} ---> {value}") elif isinstance(item, list): - print('[') + print("[") for x in item: - print(f' {repr(x)}') - print(']') + print(f" {repr(x)}") + print("]") else: print(item) + class Token: """ Basic token class. @@ -113,14 +127,17 @@ class Token: Token's lexeme. token_type : Enum Token's type. + pos : (int, int) + Token's starting position (row, column) """ - def __init__(self, lex, token_type): + def __init__(self, lex, token_type, pos): self.lex = lex self.token_type = token_type + self.pos = pos def __str__(self): - return f'{self.token_type}: {self.lex}' + return f"{self.token_type}: {self.lex}" def __repr__(self): return str(self) @@ -129,20 +146,22 @@ def __repr__(self): def is_valid(self): return True + class UnknownToken(Token): - def __init__(self, lex): - Token.__init__(self, lex, None) + def __init__(self, lex, pos): + Token.__init__(self, lex, None, pos) def transform_to(self, token_type): - return Token(self.lex, token_type) + return Token(self.lex, token_type, self.pos) @property def is_valid(self): return False - + + class DisjointSet: def __init__(self, *items): - self.nodes = { x: DisjointNode(x) for x in items } + self.nodes = {x: DisjointNode(x) for x in items} def merge(self, items): items = (self.nodes[x] for x in items) @@ -155,11 +174,14 @@ def merge(self, items): @property def representatives(self): - return { n.representative for n in self.nodes.values() } + 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] + return [ + [n for n in self.nodes.values() if n.representative == r] + for r in self.representatives + ] def __len__(self): return len(self.representatives) @@ -173,6 +195,7 @@ def __str__(self): def __repr__(self): return str(self) + class DisjointNode: def __init__(self, value): self.value = value @@ -193,10 +216,11 @@ def __str__(self): def __repr__(self): return str(self) + class ShiftReduceParser: - SHIFT = 'SHIFT' - REDUCE = 'REDUCE' - OK = 'OK' + SHIFT = "SHIFT" + REDUCE = "REDUCE" + OK = "OK" def __init__(self, G, verbose=False): self.G = G @@ -217,7 +241,8 @@ def __call__(self, w, get_shift_reduce=False): while True: state = stack[-1] lookahead = w[cursor] - if self.verbose: print(stack, w[cursor:]) + if self.verbose: + print(stack, w[cursor:]) try: if state not in self.action or lookahead not in self.action[state]: @@ -236,12 +261,10 @@ def __call__(self, w, get_shift_reduce=False): elif action is self.REDUCE: operations.append(self.REDUCE) if len(tag.Right): - stack = stack[:-len(tag.Right)] + stack = stack[: -len(tag.Right)] stack.append(list(self.goto[stack[-1]][tag.Left])[0]) output.append(tag) elif action is ShiftReduceParser.OK: return output if not get_shift_reduce else (output, operations) else: raise ValueError - - diff --git a/src/compiler/visitors/formatter.py b/src/compiler/visitors/formatter.py index 7e29ca89c..b9cfd702f 100644 --- a/src/compiler/visitors/formatter.py +++ b/src/compiler/visitors/formatter.py @@ -1,116 +1,135 @@ import compiler.visitors.visitor as visitor from ..cmp.ast import ProgramNode, ClassDeclarationNode, FuncDeclarationNode from ..cmp.ast import AttrDeclarationNode, AssignNode, CallNode -from ..cmp.ast import CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode, InstantiateNode +from ..cmp.ast import ( + CaseNode, + BlockNode, + LoopNode, + ConditionalNode, + LetNode, + InstantiateNode, +) from ..cmp.ast import UnaryNode, BinaryNode, AtomicNode + class FormatVisitor(object): - @visitor.on('node') + @visitor.on("node") def visit(self, node, tabs=0): 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}' - + 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}' - + 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(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} {{ }}' + 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}' - + return f"{ans}\n{body}" + @visitor.when(AttrDeclarationNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {node.type}' + ans = "\t" * tabs + f"\\__AttrDeclarationNode: {node.id} : {node.type}" if node.expr is not None: expr = self.visit(node.expr, tabs + 1) - ans = f'{ans} <- \n{expr}' - return f'{ans}' - + ans = f"{ans} <- \n{expr}" + return f"{ans}" + @visitor.when(AssignNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__AssignNode: {node.id} <- ' + ans = "\t" * tabs + f"\\__AssignNode: {node.id} <- " expr = self.visit(node.expr, tabs + 1) - return f'{ans}\n{expr}' - + return f"{ans}\n{expr}" + @visitor.when(CallNode) def visit(self, node, tabs=0): obj = self.visit(node.obj, tabs + 1) - cast = '' if node.type is None else f'@{node.type}' - ans = '\t' * tabs + f'\\__CallNode: {cast}.{node.id}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{obj}\n{args}' + cast = "" if node.type is None else f"@{node.type}" + ans = "\t" * tabs + f"\\__CallNode: {cast}.{node.id}(, ..., )" + args = "\n".join(self.visit(arg, tabs + 1) for arg in node.args) + return f"{ans}\n{obj}\n{args}" @visitor.when(CaseNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__CaseNode: case of [ , ..., ]' + ans = "\t" * tabs + f"\\__CaseNode: case of [ , ..., ]" expr = self.visit(node.expr, tabs + 1) branches = [] for branch in node.branch_list: - branches.append('\t' * (tabs + 1) + f'{branch[0]} : {branch[1]} => \n{self.visit(branch[2], tabs + 2)}') - branches = '\n'.join(branches) - return f'{ans}\n{expr}\n{branches}' + branches.append( + "\t" * (tabs + 1) + + f"{branch[0]} : {branch[1]} => \n{self.visit(branch[2], tabs + 2)}" + ) + branches = "\n".join(branches) + return f"{ans}\n{expr}\n{branches}" @visitor.when(BlockNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__BlockNode: {{ ; ... ; ; }}' - exprs = '\n'.join(self.visit(expr, tabs + 1) for expr in node.expr_list) - return f'{ans}\n{exprs}' + ans = "\t" * tabs + f"\\__BlockNode: {{ ; ... ; ; }}" + exprs = "\n".join(self.visit(expr, tabs + 1) for expr in node.expr_list) + return f"{ans}\n{exprs}" @visitor.when(LoopNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__LoopNode: while loop pool' + ans = "\t" * tabs + f"\\__LoopNode: while loop pool" cond = self.visit(node.condition, tabs + 1) body = self.visit(node.body, tabs + 1) - return f'{ans}\n{cond}\n{body}' + return f"{ans}\n{cond}\n{body}" @visitor.when(ConditionalNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ConditionalNode: if then else fi' + ans = "\t" * tabs + f"\\__ConditionalNode: if then else fi" cond = self.visit(node.condition, tabs + 1) then_body = self.visit(node.then_body, tabs + 1) else_body = self.visit(node.else_body, tabs + 1) - return f'{ans}\n{cond}\n{then_body}\n{else_body}' + return f"{ans}\n{cond}\n{then_body}\n{else_body}" @visitor.when(LetNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__LetNode: let in ' + ans = "\t" * tabs + f"\\__LetNode: let in " expr = self.visit(node.body, tabs + 1) iden_list = [] for item in node.id_list: - iden = '\t' * (tabs + 1) + f'{item[0]} : {item[1]}' + iden = "\t" * (tabs + 1) + f"{item[0]} : {item[1]}" if item[2] is not None: - iden = f'{iden} <- \n{self.visit(item[2], tabs + 2)}' + iden = f"{iden} <- \n{self.visit(item[2], tabs + 2)}" iden_list.append(iden) - iden_list = '\n'.join(iden_list) - return f'{ans}\n{iden_list}\n{expr}' + iden_list = "\n".join(iden_list) + return f"{ans}\n{iden_list}\n{expr}" @visitor.when(BinaryNode) def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + 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}' + 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}' + return "\t" * tabs + f"\\__ {node.__class__.__name__}: {node.lex}" @visitor.when(UnaryNode) def visit(self, node, tabs=0): expr = self.visit(node.expr, tabs + 1) - return '\t' * tabs + f'\\__ {node.__class__.__name__} \n{expr}' - + return "\t" * tabs + f"\\__ {node.__class__.__name__} \n{expr}" + @visitor.when(InstantiateNode) def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ InstantiateNode: new {node.lex}' \ No newline at end of file + return "\t" * tabs + f"\\__ InstantiateNode: new {node.lex}" diff --git a/src/compiler/visitors/type_builder.py b/src/compiler/visitors/type_builder.py index c6b08ef1f..539148270 100644 --- a/src/compiler/visitors/type_builder.py +++ b/src/compiler/visitors/type_builder.py @@ -1,6 +1,17 @@ import compiler.visitors.visitor as visitor -from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from ..cmp.semantic import SemanticError, ErrorType, InferencerManager, AutoType, SelfType +from ..cmp.ast import ( + ProgramNode, + ClassDeclarationNode, + AttrDeclarationNode, + FuncDeclarationNode, +) +from ..cmp.semantic import ( + SemanticError, + ErrorType, + InferencerManager, + AutoType, + SelfType, +) SELF_IS_READONLY = 'Variable "self" is read-only.' @@ -12,28 +23,26 @@ def __init__(self, context, errors=[]): self.current_type = None self.errors = errors self.manager = InferencerManager() - - self.obj_type = self.context.get_type('Object') - - @visitor.on('node') + self.obj_type = self.context.get_type("Object") + + @visitor.on("node") def visit(self, node): pass - - + @visitor.when(ProgramNode) def visit(self, node): for dec in node.declarations: self.visit(dec) self.check_main_class() - + @visitor.when(ClassDeclarationNode) def visit(self, node): self.current_type = self.context.get_type(node.id) for feat in node.features: self.visit(feat) - + @visitor.when(FuncDeclarationNode) def visit(self, node): ## Building param-names and param-types of the method @@ -50,11 +59,11 @@ def visit(self, node): while True: if n in param_names: - n = f'1{n}' + n = f"1{n}" else: param_names.append(n) break - + try: t = self.context.get_type(t) if isinstance(t, SelfType): @@ -65,7 +74,7 @@ def visit(self, node): self.errors.append(ex.text) t = ErrorType() param_types.append(t) - + # Checking return type try: rtype = self.context.get_type(node.type) @@ -74,15 +83,21 @@ def visit(self, node): except SemanticError as ex: self.errors.append(ex.text) rtype = ErrorType() - - node.idx = self.manager.assign_id(self.obj_type) if isinstance(rtype, AutoType) else None - + + node.idx = ( + self.manager.assign_id(self.obj_type) + if isinstance(rtype, AutoType) + else None + ) + # Defining the method in the current type. There can not be another method with the same name. try: - self.current_type.define_method(node.id, param_names, param_types, rtype, node.index, node.idx) + self.current_type.define_method( + node.id, param_names, param_types, rtype, node.index, node.idx + ) except SemanticError as ex: self.errors.append(ex.text) - + @visitor.when(AttrDeclarationNode) def visit(self, node): # Checking attribute type @@ -94,12 +109,16 @@ def visit(self, node): self.errors.append(ex.text) attr_type = ErrorType() - node.idx = self.manager.assign_id(self.obj_type) if isinstance(attr_type, AutoType) else None + node.idx = ( + self.manager.assign_id(self.obj_type) + if isinstance(attr_type, AutoType) + else None + ) - #Checking attribute can't be named self + # Checking attribute can't be named self if node.id == "self": self.errors.append(SELF_IS_READONLY) - + # Checking attribute name. No other attribute can have the same name flag = False try: @@ -107,20 +126,19 @@ def visit(self, node): flag = True except SemanticError as ex: self.errors.append(ex.text) - + while not flag: - node.id = f'1{node.id}' + node.id = f"1{node.id}" try: self.current_type.define_attribute(node.id, attr_type, node.idx) flag = True except SemanticError: pass - def check_main_class(self): try: - typex = self.context.get_type('Main') - if not any(method.name == 'main' for method in typex.methods): - self.errors.append('Class Main must contain a method main') + typex = self.context.get_type("Main") + if not any(method.name == "main" for method in typex.methods): + self.errors.append("Class Main must contain a method main") except SemanticError: - self.errors.append('Program must contain a class Main') \ No newline at end of file + self.errors.append("Program must contain a class Main") diff --git a/src/compiler/visitors/type_checker.py b/src/compiler/visitors/type_checker.py index adfa1c351..6d114e7a4 100644 --- a/src/compiler/visitors/type_checker.py +++ b/src/compiler/visitors/type_checker.py @@ -1,10 +1,38 @@ import compiler.visitors.visitor as visitor -from ..cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA -from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from ..cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from ..cmp.semantic import ( + Scope, + SemanticError, + ErrorType, + IntType, + BoolType, + SelfType, + AutoType, + LCA, +) +from ..cmp.ast import ( + ProgramNode, + ClassDeclarationNode, + AttrDeclarationNode, + FuncDeclarationNode, +) +from ..cmp.ast import ( + AssignNode, + CallNode, + CaseNode, + BlockNode, + LoopNode, + ConditionalNode, + LetNode, +) from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode from ..cmp.ast import VoidNode, NotNode, NegNode -from ..cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode +from ..cmp.ast import ( + ConstantNumNode, + ConstantStringNode, + ConstantBoolNode, + VariableNode, + InstantiateNode, +) WRONG_SIGNATURE = 'Method "%s" already defined in "%s" with a different signature.' @@ -13,7 +41,8 @@ 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".' -INVALID_TYPE = 'SELF_TYPE is not valid' +INVALID_TYPE = "SELF_TYPE is not valid" + class TypeChecker: def __init__(self, context, manager, errors=[]): @@ -24,12 +53,12 @@ def __init__(self, context, manager, errors=[]): self.manager = manager # built-in types - self.obj_type = self.context.get_type('Object') - self.int_type = self.context.get_type('Int') - self.bool_type = self.context.get_type('Bool') - self.string_type = self.context.get_type('String') + self.obj_type = self.context.get_type("Object") + self.int_type = self.context.get_type("Int") + self.bool_type = self.context.get_type("Bool") + self.string_type = self.context.get_type("String") - @visitor.on('node') + @visitor.on("node") def visit(self, node, scope=None): pass @@ -43,15 +72,15 @@ def visit(self, node, scope=None): @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)) + scope.define_variable("self", SelfType(self.current_type)) attributes = self.current_type.all_attributes() for values in attributes: attr, _ = values scope.define_variable(attr.name, attr.type, attr.idx) - + for feature in node.features: self.visit(feature, scope.create_child()) - + @visitor.when(AttrDeclarationNode) def visit(self, node, scope): var = scope.find_variable(node.id) @@ -60,79 +89,96 @@ def visit(self, node, scope): if node.expr is not None: computed_type = self.visit(node.expr, scope) if not self.check_conformance(computed_type, attr_type): - self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, attr_type.name)) + self.errors.append( + INCOMPATIBLE_TYPES % (computed_type.name, attr_type.name) + ) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): self.current_method = self.current_type.get_method(node.id) - + # checking overwriting try: method = self.current_type.parent.get_method(node.id) if not len(self.current_method.param_types) == len(method.param_types): - self.errors.append(WRONG_SIGNATURE %(node.id, self.current_type.name)) + self.errors.append(WRONG_SIGNATURE % (node.id, self.current_type.name)) else: for i, t in enumerate(self.current_method.param_types): if not method.param_types[i] == t: - self.errors.append(WRONG_SIGNATURE %(node.id, self.current_type.name)) + self.errors.append( + WRONG_SIGNATURE % (node.id, self.current_type.name) + ) break else: if not self.current_method.return_type == method.return_type: - self.errors.append(WRONG_SIGNATURE %(node.id, self.current_type.name)) + self.errors.append( + WRONG_SIGNATURE % (node.id, self.current_type.name) + ) except SemanticError: pass - + # defining variables in new scope for i, var in enumerate(self.current_method.param_names): if scope.is_local(var): - self.errors.append(LOCAL_ALREADY_DEFINED %(var, self.current_method.name)) + self.errors.append( + LOCAL_ALREADY_DEFINED % (var, self.current_method.name) + ) else: - scope.define_variable(var, self.current_method.param_types[i], self.current_method.param_idx[i]) - + scope.define_variable( + var, + self.current_method.param_types[i], + self.current_method.param_idx[i], + ) + computed_type = self.visit(node.body, scope) - + # checking return type rtype = self.current_method.return_type if not self.check_conformance(computed_type, rtype): - self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, self.current_method.return_type.name)) - + self.errors.append( + INCOMPATIBLE_TYPES + % (computed_type.name, self.current_method.return_type.name) + ) + @visitor.when(AssignNode) def visit(self, node, scope): if node.id == "self": self.errors.append(SELF_IS_READONLY) - + # checking variable is defined var = scope.find_variable(node.id) if var is None: - self.errors.append(VARIABLE_NOT_DEFINED %(node.id, self.current_type.name)) + self.errors.append(VARIABLE_NOT_DEFINED % (node.id, self.current_type.name)) var = scope.define_variable(node.id, ErrorType()) - + computed_type = self.visit(node.expr, scope.create_child()) - + if not self.check_conformance(computed_type, var.type): - self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, var.type.name)) - + self.errors.append(INCOMPATIBLE_TYPES % (computed_type.name, var.type.name)) + return computed_type - + @visitor.when(CallNode) def visit(self, node, scope): # Evaluate object obj_type = self.visit(node.obj, scope) - + # Check object type conforms to cast type cast_type = obj_type if node.type is not None: try: cast_type = self.context.get_type(node.type) if isinstance(cast_type, AutoType): - raise SemanticError('AUTO_TYPE can\'t be the type on this type of dispatch') + raise SemanticError( + "AUTO_TYPE can't be the type on this type of dispatch" + ) if isinstance(cast_type, SelfType): cast_type = SelfType(self.current_type) except SemanticError as ex: cast_type = ErrorType() self.errors.append(ex.text) if not self.check_conformance(obj_type, cast_type): - self.errors.append(INCOMPATIBLE_TYPES %(obj_type.name, cast_type.name)) + self.errors.append(INCOMPATIBLE_TYPES % (obj_type.name, cast_type.name)) # if the obj that is calling the function is autotype, let it pass if isinstance(cast_type, AutoType): @@ -140,18 +186,21 @@ def visit(self, node, scope): if isinstance(cast_type, SelfType): cast_type = self.current_type - + # Check this function is defined for cast_type try: method = cast_type.get_method(node.id) if not len(node.args) == len(method.param_types): - self.errors.append(INVALID_OPERATION %(method.name, cast_type.name)) + self.errors.append(INVALID_OPERATION % (method.name, cast_type.name)) return ErrorType() for i, arg in enumerate(node.args): computed_type = self.visit(arg, scope) if not self.check_conformance(computed_type, method.param_types[i]): - self.errors.append(INCOMPATIBLE_TYPES %(computed_type.name, method.param_types[i].name)) - + self.errors.append( + INCOMPATIBLE_TYPES + % (computed_type.name, method.param_types[i].name) + ) + # check self_type rtype = method.return_type if isinstance(rtype, SelfType): @@ -162,7 +211,7 @@ def visit(self, node, scope): except SemanticError as ex: self.errors.append(ex.text) return ErrorType() - + @visitor.when(CaseNode) def visit(self, node, scope): # check expression @@ -174,12 +223,12 @@ def visit(self, node, scope): types = [] node.branch_idx = [] for branch in node.branch_list: - idx, typex, expr = branch - + idx, typex, expr = branch + node.branch_idx.append(None) - + # check idx is not self - if idx == 'self': + if idx == "self": self.errors.append(SELF_IS_READONLY) try: @@ -189,7 +238,7 @@ def visit(self, node, scope): except SemanticError as ex: self.errors.append(ex.text) var_type = ErrorType() - + # check type is autotype and assign an id in the manager if isinstance(var_type, AutoType): node.branch_idx[-1] = self.manager.assign_id(self.obj_type) @@ -201,7 +250,7 @@ def visit(self, node, scope): types.append(computed_type) return LCA(types) - + @visitor.when(BlockNode) def visit(self, node, scope): nscope = scope.create_child() @@ -221,7 +270,9 @@ def visit(self, node, scope): # checking condition: it must conform to bool cond_type = self.visit(node.condition, nscope) if not cond_type.conforms_to(self.bool_type): - self.errors.append(INCOMPATIBLE_TYPES %(cond_type.name, self.bool_type.name)) + self.errors.append( + INCOMPATIBLE_TYPES % (cond_type.name, self.bool_type.name) + ) # checking body self.visit(node.body, nscope) @@ -234,7 +285,9 @@ def visit(self, node, scope): # check condition conforms to bool cond_type = self.visit(node.condition, scope) if not cond_type.conforms_to(self.bool_type): - self.errors.append(INCOMPATIBLE_TYPES %(cond_type.name, self.bool_type.name)) + self.errors.append( + INCOMPATIBLE_TYPES % (cond_type.name, self.bool_type.name) + ) then_type = self.visit(node.then_body, scope.create_child()) else_type = self.visit(node.else_body, scope.create_child()) @@ -250,12 +303,12 @@ def visit(self, node, scope): idx, typex, expr = item # create a new_scope for every variable defined new_scope = nscope.create_child() - - if idx == 'self': + + if idx == "self": self.errors.append(SELF_IS_READONLY) - idx = f'1{idx}' + idx = f"1{idx}" node.id_list[i] = (idx, typex, expr) - + try: typex = self.context.get_type(typex) if isinstance(typex, SelfType): @@ -270,7 +323,9 @@ def visit(self, node, scope): if expr is not None: expr_type = self.visit(expr, new_scope) if not self.check_conformance(expr_type, typex): - self.errors.append(INCOMPATIBLE_TYPES %(expr_type.name, typex.name)) + self.errors.append( + INCOMPATIBLE_TYPES % (expr_type.name, typex.name) + ) new_scope.define_variable(idx, typex, node.idx_list[i]) nscope = new_scope @@ -293,40 +348,41 @@ def visit(self, node, scope): right = self.visit(node.right, scope) types = [self.int_type, self.bool_type, self.string_type] + def check_equal(typex, other): for t in types: if typex.conforms_to(t): if not other.conforms_to(t): - self.errors.append(INCOMPATIBLE_TYPES %(other.name, t.name)) + self.errors.append(INCOMPATIBLE_TYPES % (other.name, t.name)) return True return False ok = check_equal(left, right) if not ok: check_equal(right, left) - + return self.bool_type @visitor.when(VoidNode) def visit(self, node, scope): self.visit(node.expr, scope) - + return self.bool_type @visitor.when(NotNode) def visit(self, node, scope): typex = self.visit(node.expr, scope) if not typex.conforms_to(self.bool_type): - self.errors.append(INCOMPATIBLE_TYPES %(typex.name, self.bool_type.name)) - + self.errors.append(INCOMPATIBLE_TYPES % (typex.name, self.bool_type.name)) + return self.bool_type @visitor.when(NegNode) def visit(self, node, scope): typex = self.visit(node.expr, scope) if not typex.conforms_to(self.int_type): - self.errors.append(INCOMPATIBLE_TYPES %(typex.name, self.int_type.name)) - + self.errors.append(INCOMPATIBLE_TYPES % (typex.name, self.int_type.name)) + return self.int_type @visitor.when(ConstantNumNode) @@ -345,7 +401,9 @@ def visit(self, node, scope): def visit(self, node, scope): var = scope.find_variable(node.lex) if var is None: - self.errors.append(VARIABLE_NOT_DEFINED %(node.lex, self.current_type.name)) + self.errors.append( + VARIABLE_NOT_DEFINED % (node.lex, self.current_type.name) + ) var = scope.define_variable(node.lex, ErrorType()) return var.type @@ -355,29 +413,28 @@ def visit(self, node, scope): try: typex = self.context.get_type(node.lex) if isinstance(typex, AutoType): - raise SemanticError('AUTO_TYPE can\'t be instanciate with new') + raise SemanticError("AUTO_TYPE can't be instanciate with new") if isinstance(typex, SelfType): typex = SelfType(self.current_type) except SemanticError as ex: self.errors.append(ex.text) typex = ErrorType() - - return typex + return typex def check_expr(self, node, scope): # checking left expr left = self.visit(node.left, scope) if not left.conforms_to(self.int_type): - self.errors.append(INCOMPATIBLE_TYPES %(left.name, self.int_type.name)) + self.errors.append(INCOMPATIBLE_TYPES % (left.name, self.int_type.name)) # checking right expr right = self.visit(node.right, scope) if not right.conforms_to(self.int_type): - self.errors.append(INCOMPATIBLE_TYPES %(right.name, self.int_type.name)) + self.errors.append(INCOMPATIBLE_TYPES % (right.name, self.int_type.name)) def check_conformance(self, computed_type, attr_type): - return ( - computed_type.conforms_to(attr_type) - or (isinstance(computed_type, SelfType) and self.current_type.conforms_to(attr_type)) - ) \ No newline at end of file + return computed_type.conforms_to(attr_type) or ( + isinstance(computed_type, SelfType) + and self.current_type.conforms_to(attr_type) + ) diff --git a/src/compiler/visitors/type_collector.py b/src/compiler/visitors/type_collector.py index eff4d24de..d6cf75d9a 100644 --- a/src/compiler/visitors/type_collector.py +++ b/src/compiler/visitors/type_collector.py @@ -1,5 +1,16 @@ import compiler.visitors.visitor as visitor -from ..cmp.semantic import SemanticError, Type, Context, ObjectType, IOType, StringType, IntType, BoolType, SelfType, AutoType +from ..cmp.semantic import ( + SemanticError, + Type, + Context, + ObjectType, + IOType, + StringType, + IntType, + BoolType, + SelfType, + AutoType, +) from ..cmp.ast import ProgramNode, ClassDeclarationNode built_in_types = [] @@ -10,16 +21,16 @@ def __init__(self, errors=[]): self.context = None self.errors = errors self.parent = {} - - @visitor.on('node') + + @visitor.on("node") def visit(self, node): pass - + @visitor.when(ProgramNode) def visit(self, node): self.context = Context() self.define_built_in_types() - + # Adding built-in types to context for typex in built_in_types: self.context.types[typex.name] = typex @@ -32,14 +43,14 @@ def visit(self, node): # Order class declarations according to their depth in the inheritance tree node.declarations = self.order_types(node) - + @visitor.when(ClassDeclarationNode) def visit(self, node): # flag will be True if the class is succesfully added to the context - flag = False + flag = False try: - if node.id == 'AUTO_TYPE': - raise SemanticError('Name of class can\'t be autotype') + if node.id == "AUTO_TYPE": + raise SemanticError("Name of class can't be autotype") self.context.create_type(node.id) flag = True self.parent[node.id] = node.parent @@ -48,14 +59,13 @@ def visit(self, node): # changing class id so it can be added to context while not flag: - node.id = f'1{node.id}' + node.id = f"1{node.id}" try: self.context.create_type(node.id) flag = True self.parent[node.id] = node.parent except SemanticError: pass - def define_built_in_types(self): objectx = ObjectType() @@ -67,21 +77,21 @@ def define_built_in_types(self): autotype = AutoType() # Object Methods - objectx.define_method('abort', [], [], objectx, []) - objectx.define_method('type_name', [], [], stringx, []) - objectx.define_method('copy', [], [], self_type, []) + objectx.define_method("abort", [], [], objectx, []) + objectx.define_method("type_name", [], [], stringx, []) + objectx.define_method("copy", [], [], self_type, []) # IO Methods - iox.define_method('out_string', ['x'], [stringx], self_type, [None]) - iox.define_method('out_int', ['x'], [intx], self_type, [None]) - iox.define_method('in_string', [], [], stringx, []) - iox.define_method('in_int', [], [], intx, []) + iox.define_method("out_string", ["x"], [stringx], self_type, [None]) + iox.define_method("out_int", ["x"], [intx], self_type, [None]) + iox.define_method("in_string", [], [], stringx, []) + iox.define_method("in_int", [], [], intx, []) # String Methods - stringx.define_method('length', [], [], intx, []) - stringx.define_method('concat', ['s'], [stringx], stringx, [None]) - stringx.define_method('substr', ['i', 'l'], [intx, intx], stringx, [None]) - + stringx.define_method("length", [], [], intx, []) + stringx.define_method("concat", ["s"], [stringx], stringx, [None]) + stringx.define_method("substr", ["i", "l"], [intx, intx], stringx, [None]) + # Setting Object as parent iox.set_parent(objectx) stringx.set_parent(objectx) @@ -89,7 +99,7 @@ def define_built_in_types(self): boolx.set_parent(objectx) built_in_types.extend([objectx, iox, stringx, intx, boolx, self_type, autotype]) - + def check_parents(self): for item in self.parent.keys(): item_type = self.context.get_type(item) @@ -100,16 +110,18 @@ def check_parents(self): try: typex = self.context.get_type(self.parent[item]) if not typex.can_be_inherited(): - self.errors.append(f'Class {item} can not inherit from {typex.name}') + self.errors.append( + f"Class {item} can not inherit from {typex.name}" + ) typex = built_in_types[0] item_type.set_parent(typex) except SemanticError as ex: self.errors.append(ex.text) item_type.set_parent(built_in_types[0]) - + def check_cyclic_inheritance(self): flag = [] - + def find(item): for i, typex in enumerate(flag): if typex.name == item.name: @@ -125,7 +137,9 @@ def check_path(idx, item): pos = find(parent) if pos < len(flag): if pos >= idx: - self.errors.append(f'Class {item.name} can not inherit from {parent.name}') + self.errors.append( + f"Class {item.name} can not inherit from {parent.name}" + ) item.parent = built_in_types[0] break item = parent @@ -134,11 +148,11 @@ def check_path(idx, item): idx = find(item) if idx == len(flag): check_path(idx, item) - + def order_types(self, node): sorted_declarations = [] flag = [False] * len(node.declarations) - + change = True while change: change = False @@ -147,11 +161,13 @@ def order_types(self, node): for i, dec in enumerate(node.declarations): if not flag[i]: typex = self.context.get_type(dec.id) - if typex.parent.name in [item.id for item in sorted_declarations] or any(typex.parent.name == bit.name for bit in built_in_types): + if typex.parent.name in [ + item.id for item in sorted_declarations + ] or any(typex.parent.name == bit.name for bit in built_in_types): current.append(dec) flag[i] = True change = True - + sorted_declarations.extend(current) - return sorted_declarations \ No newline at end of file + return sorted_declarations diff --git a/src/compiler/visitors/type_inferencer.py b/src/compiler/visitors/type_inferencer.py index 2246a2385..2df2c4c11 100644 --- a/src/compiler/visitors/type_inferencer.py +++ b/src/compiler/visitors/type_inferencer.py @@ -1,31 +1,51 @@ import compiler.visitors.visitor as visitor from ..cmp.semantic import SemanticError, ErrorType, SelfType, AutoType, LCA -from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from ..cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from ..cmp.ast import ( + ProgramNode, + ClassDeclarationNode, + AttrDeclarationNode, + FuncDeclarationNode, +) +from ..cmp.ast import ( + AssignNode, + CallNode, + CaseNode, + BlockNode, + LoopNode, + ConditionalNode, + LetNode, +) from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode from ..cmp.ast import VoidNode, NotNode, NegNode -from ..cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode +from ..cmp.ast import ( + ConstantNumNode, + ConstantStringNode, + ConstantBoolNode, + VariableNode, + InstantiateNode, +) -AUTOTYPE_ERROR = 'Incorrect use of AUTO_TYPE' +AUTOTYPE_ERROR = "Incorrect use of AUTO_TYPE" + class TypeInferencer: def __init__(self, context, manager, errors=[]): self.context = context self.errors = errors self.manager = manager - + self.current_type = None self.current_method = None self.scope_children_id = 0 # built-in types - self.obj_type = self.context.get_type('Object') - self.int_type = self.context.get_type('Int') - self.bool_type = self.context.get_type('Bool') - self.string_type = self.context.get_type('String') + self.obj_type = self.context.get_type("Object") + self.int_type = self.context.get_type("Int") + self.bool_type = self.context.get_type("Bool") + self.string_type = self.context.get_type("String") - @visitor.on('node') + @visitor.on("node") def visit(self, node, scope, types=None): pass @@ -46,25 +66,25 @@ def visit(self, node, scope, types=None): self.manager.infer_object(self.obj_type) for declaration, child_scope in zip(node.declarations, scope.children): - self.scope_children_id = 0 - self.visit(declaration, child_scope, types) - + self.scope_children_id = 0 + self.visit(declaration, child_scope, types) + @visitor.when(ClassDeclarationNode) def visit(self, node, scope, types): self.current_type = self.context.get_type(node.id) - + for feature, child_scope in zip(node.features, scope.children): self.scope_children_id = 0 self.visit(feature, child_scope, types) - + @visitor.when(AttrDeclarationNode) def visit(self, node, scope, types): var_attr = scope.find_variable(node.id) attr_type = var_attr.type idx = var_attr.idx - + if isinstance(attr_type, AutoType): - inf_type = self.manager.infered_type[idx] + inf_type = self.manager.infered_type[idx] if inf_type is not None: if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) @@ -84,7 +104,7 @@ def visit(self, node, scope, types): _, computed_types = self.visit(node.expr, scope, conforms_to_types) if isinstance(attr_type, AutoType): self.manager.upd_conformed_by(node.idx, computed_types) - + @visitor.when(FuncDeclarationNode) def visit(self, node, scope, types): self.current_method = self.current_type.get_method(node.id) @@ -94,7 +114,7 @@ def visit(self, node, scope, types): idx = self.current_method.param_idx[i] name = self.current_method.param_names[i] if isinstance(t, AutoType): - inf_type = self.manager.infered_type[idx] + inf_type = self.manager.infered_type[idx] if inf_type is not None: if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) @@ -108,30 +128,40 @@ def visit(self, node, scope, types): rtype = self.current_method.return_type idx = self.current_method.ridx if isinstance(rtype, AutoType): - inf_type = self.manager.infered_type[idx] + inf_type = self.manager.infered_type[idx] if inf_type is not None: if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) else: node.type = inf_type.name - self.current_type.update_method_rtype(self.current_method.name, inf_type) + self.current_type.update_method_rtype( + self.current_method.name, inf_type + ) rtype = inf_type # checking overwriting try: - method = self.current_type.parent.get_method(node.id) - - for i, t in enumerate(method_params): - if not isinstance(method.param_types[i], AutoType) and isinstance(t, AutoType): - self.manager.auto_to_type(self.current_method.param_idx[i], method.param_types[i]) - self.manager.type_to_auto(self.current_method.param_idx[i], method.param_types[i]) + method = self.current_type.parent.get_method(node.id) - if isinstance(rtype, AutoType) and not isinstance(method.return_type, AutoType): + for i, t in enumerate(method_params): + if not isinstance(method.param_types[i], AutoType) and isinstance( + t, AutoType + ): + self.manager.auto_to_type( + self.current_method.param_idx[i], method.param_types[i] + ) + self.manager.type_to_auto( + self.current_method.param_idx[i], method.param_types[i] + ) + + if isinstance(rtype, AutoType) and not isinstance( + method.return_type, AutoType + ): self.manager.auto_to_type(idx, method.return_type) self.manager.type_to_auto(idx, method.return_type) except SemanticError: pass - + # checking return type in computed types of the expression conforms_to_types = [] if isinstance(rtype, AutoType): @@ -141,17 +171,17 @@ def visit(self, node, scope, types): _, computed_types = self.visit(node.body, scope, conforms_to_types) if isinstance(rtype, AutoType): self.manager.upd_conformed_by(self.current_method.ridx, computed_types) - + @visitor.when(AssignNode) def visit(self, node, scope, types): var = scope.find_variable(node.id) # obtaining defined variable if isinstance(var.type, AutoType): - inf_type = self.manager.infered_type[var.idx] - if inf_type is not None: + inf_type = self.manager.infered_type[var.idx] + if inf_type is not None: scope.update_variable(var.name, inf_type) var.type = inf_type - + conforms_to_types = [] if isinstance(var.type, AutoType): conforms_to_types.extend(self.manager.conforms_to[var.idx]) @@ -161,14 +191,16 @@ def visit(self, node, scope, types): scope_index = self.scope_children_id self.scope_children_id = 0 - - typex, computed_types = self.visit(node.expr, scope.children[scope_index], conforms_to_types) + + typex, computed_types = self.visit( + node.expr, scope.children[scope_index], conforms_to_types + ) if isinstance(var.type, AutoType): self.manager.upd_conformed_by(var.idx, computed_types) self.scope_children_id = scope_index + 1 return typex, computed_types - + @visitor.when(CallNode) def visit(self, node, scope, types): # Check cast type @@ -182,7 +214,7 @@ def visit(self, node, scope, types): cast_type = self.current_type except SemanticError: pass - + # Check object conforms_to_types = [] if cast_type is None else [cast_type] obj_type, computed_types = self.visit(node.obj, scope, conforms_to_types) @@ -195,7 +227,7 @@ def visit(self, node, scope, types): # if the obj that is calling the function is autotype, let it pass if isinstance(cast_type, AutoType): return cast_type, [] - + # Check this function is defined for cast_type try: method = cast_type.get_method(node.id) @@ -204,25 +236,27 @@ def visit(self, node, scope, types): for i, arg in enumerate(node.args): arg_type = method.param_types[i] if isinstance(arg_type, AutoType): - inf_type = self.manager.infered_type[method.param_idx[i]] + inf_type = self.manager.infered_type[method.param_idx[i]] if inf_type is not None: arg_type = inf_type conforms_to_types = [] if isinstance(arg_type, AutoType): - conforms_to_types.extend(self.manager.conforms_to[method.param_idx[i]]) + conforms_to_types.extend( + self.manager.conforms_to[method.param_idx[i]] + ) else: conforms_to_types.append(arg_type) _, computed_types = self.visit(arg, scope, conforms_to_types) if isinstance(arg_type, AutoType): self.manager.upd_conformed_by(method.param_idx[i], computed_types) - + # check return_type computed_types = [] rtype = method.return_type if isinstance(rtype, SelfType): rtype = obj_type - + if isinstance(rtype, AutoType): self.manager.upd_conforms_to(method.ridx, types) computed_types.extend(self.manager.conformed_by[method.ridx]) @@ -233,7 +267,7 @@ def visit(self, node, scope, types): except SemanticError: return ErrorType(), [] - + @visitor.when(CaseNode) def visit(self, node, scope, types): # check expression @@ -244,12 +278,14 @@ def visit(self, node, scope, types): # check branches expr_types = [] - for i, (branch, child_scope) in enumerate(zip(node.branch_list, nscope.children)): - branch_name, branch_type, expr = branch + for i, (branch, child_scope) in enumerate( + zip(node.branch_list, nscope.children) + ): + branch_name, branch_type, expr = branch var = child_scope.find_variable(branch_name) branch_type = var.type if isinstance(branch_type, AutoType): - inf_type = self.manager.infered_type[node.branch_idx[i]] + inf_type = self.manager.infered_type[node.branch_idx[i]] if inf_type is not None: if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) @@ -264,7 +300,7 @@ def visit(self, node, scope, types): self.scope_children_id = scope_index + 1 return LCA(expr_types), expr_types - + @visitor.when(BlockNode) def visit(self, node, scope, types): scope_index = self.scope_children_id @@ -302,7 +338,7 @@ def visit(self, node, scope, types): def visit(self, node, scope, types): # check condition conforms to bool self.visit(node.condition, scope, [self.bool_type]) - + branch_types = [] scope_index = self.scope_children_id @@ -329,11 +365,11 @@ def visit(self, node, scope, types): new_scope = nscope.children[temp_scope_index] self.scope_children_id = 0 - var_name, _, expr = item + var_name, _, expr = item var = new_scope.find_variable(var_name) if isinstance(var.type, AutoType): - inf_type = self.manager.infered_type[node.idx_list[i]] + inf_type = self.manager.infered_type[node.idx_list[i]] if inf_type is not None: if isinstance(inf_type, ErrorType): self.errors.append(AUTOTYPE_ERROR) @@ -341,13 +377,13 @@ def visit(self, node, scope, types): node.id_list[i] = (var_name, inf_type.name, expr) new_scope.update_variable(var_name, inf_type) var.type = inf_type - + conforms_to_types = [] if isinstance(var.type, AutoType): conforms_to_types.extend(self.manager.conforms_to[node.idx_list[i]]) else: conforms_to_types.append(var.type) - + if expr is not None: _, computed_types = self.visit(expr, new_scope, conforms_to_types) if isinstance(var.type, AutoType): @@ -375,6 +411,7 @@ def visit(self, node, scope, types): right, _ = self.visit(node.right, scope, []) fixed_types = [self.int_type, self.bool_type, self.string_type] + def check_equal(typex): if not isinstance(typex, AutoType) and not isinstance(typex, ErrorType): for t in fixed_types: @@ -386,7 +423,7 @@ def check_equal(typex): self.visit(node.right, scope, [left]) elif check_equal(right): self.visit(node.left, scope, [right]) - + return self.bool_type, [self.bool_type] @visitor.when(VoidNode) @@ -420,11 +457,11 @@ def visit(self, node, scope, types): def visit(self, node, scope, types): var = scope.find_variable(node.lex) if isinstance(var.type, AutoType): - inf_type = self.manager.infered_type[var.idx] - if inf_type is not None: + inf_type = self.manager.infered_type[var.idx] + if inf_type is not None: scope.update_variable(var.name, inf_type) var.type = inf_type - + conformed_by = [] if isinstance(var.type, AutoType): self.manager.upd_conforms_to(var.idx, types) @@ -433,7 +470,7 @@ def visit(self, node, scope, types): conformed_by.append(var.type) return var.type, conformed_by - + @visitor.when(InstantiateNode) def visit(self, node, scope, types): try: @@ -442,10 +479,9 @@ def visit(self, node, scope, types): typex = SelfType(self.current_type) except SemanticError: typex = ErrorType() - + return typex, [typex] - def check_expr(self, node, scope): self.visit(node.left, scope, [self.int_type]) self.visit(node.right, scope, [self.int_type]) diff --git a/src/compiler/visitors/visitor.py b/src/compiler/visitors/visitor.py index 964842836..0bcba712e 100644 --- a/src/compiler/visitors/visitor.py +++ b/src/compiler/visitors/visitor.py @@ -22,59 +22,64 @@ import inspect -__all__ = ['on', 'when'] +__all__ = ["on", "when"] + def on(param_name): - def f(fn): - dispatcher = Dispatcher(param_name, fn) - return dispatcher - return f + 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 + 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 __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 __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 + 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) + @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) From 48512d0e6ea0484329f19d864d2d857ea7e46d62 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 03:05:33 -0500 Subject: [PATCH 089/138] Modify parser to include token positions --- src/compiler/cmp/utils.py | 11 ++++++----- src/compiler/lexer/lex.py | 12 ++++++++---- src/main.py | 14 ++++++++++---- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/compiler/cmp/utils.py b/src/compiler/cmp/utils.py index 489efb039..268a0c050 100644 --- a/src/compiler/cmp/utils.py +++ b/src/compiler/cmp/utils.py @@ -1,3 +1,4 @@ +from typing import List from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon @@ -232,7 +233,7 @@ def __init__(self, G, verbose=False): def _build_parsing_table(self): raise NotImplementedError() - def __call__(self, w, get_shift_reduce=False): + def __call__(self, w : List[Token], get_shift_reduce=False): stack = [0] cursor = 0 output = [] @@ -240,18 +241,18 @@ 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:]) try: if state not in self.action or lookahead not in self.action[state]: - return None + return None, (True, w[cursor]) except: print(state) print(self.action) print(lookahead) - return None + return None, (True, w[cursor]) action, tag = list(self.action[state][lookahead])[0] if action is self.SHIFT: @@ -265,6 +266,6 @@ def __call__(self, w, get_shift_reduce=False): stack.append(list(self.goto[stack[-1]][tag.Left])[0]) output.append(tag) 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) else: raise ValueError diff --git a/src/compiler/lexer/lex.py b/src/compiler/lexer/lex.py index 2787ca4c7..fc3e65fc1 100644 --- a/src/compiler/lexer/lex.py +++ b/src/compiler/lexer/lex.py @@ -1,4 +1,5 @@ from distutils.log import debug +from compiler.cmp.pycompiler import EOF import ply.lex as lex from ..cmp.grammar import * @@ -308,13 +309,13 @@ def t_DIV(self, t): self.add_column(t) return t - def t_LESS(self, t): - r"<" + def t_LEQ(self, t): + r"<=" self.add_column(t) return t - def t_LEQ(self, t): - r"<=" + def t_LESS(self, t): + r"<" self.add_column(t) return t @@ -363,6 +364,9 @@ def tokenize(self, data): token_list.append( Token(tok.value, self.token_type[tok.type], (tok.lineno, tok.col)) ) + if not token_list: + errors.append("(0, 0) - SyntacticError: Unexpected token EOF") + token_list.append(Token("$", G.EOF, self.lexer.eof)) return token_list, errors diff --git a/src/main.py b/src/main.py index 4aa2c8ce3..4cf792fe6 100644 --- a/src/main.py +++ b/src/main.py @@ -6,7 +6,7 @@ from compiler.lexer.lex import CoolLexer from sys import exit -# from compiler.cmp.tools import LR1Parser +from compiler.cmp.tools import LR1Parser # from compiler.cmp.evaluation import evaluate_reverse_parse # from compiler.visitors.formatter import FormatVisitor # from compiler.visitors.type_collector import TypeCollector @@ -30,10 +30,16 @@ def main(args): if errors: exit(1) - # print('=================== PARSE =====================') - # parser = LR1Parser(G) - # parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) + + parser = LR1Parser(G) + parseResult, (failed, token) = parser(tokens, get_shift_reduce=True) + if failed: + print(f"{token.pos} - SyntacticError: ERROR at or near {token.lex}") + exit(1) + # print('\n'.join(repr(x) for x in parse)) + # print("---------------OPERATIONS---------------") + # print(operations) # print('==================== AST ======================') # ast = evaluate_reverse_parse(parse, operations, tokens) # formatter = FormatVisitor() From 0fcba1254ef48a8731e49f0b9a9a28771167fa4d Mon Sep 17 00:00:00 2001 From: kikexd Date: Thu, 24 Feb 2022 11:10:39 -0500 Subject: [PATCH 090/138] first try cool2cil --- src/compiler/cmp/ast.py | 40 +- src/compiler/cmp/cil_ast.py | 99 +++- src/compiler/visitors/cil_formatter.py | 112 +++- src/compiler/visitors/cool2cil.py | 688 +++++++++++++++++++++---- 4 files changed, 801 insertions(+), 138 deletions(-) diff --git a/src/compiler/cmp/ast.py b/src/compiler/cmp/ast.py index c0e950a91..419c5f0e3 100644 --- a/src/compiler/cmp/ast.py +++ b/src/compiler/cmp/ast.py @@ -1,21 +1,27 @@ class Node: pass + class ProgramNode(Node): def __init__(self, declarations): self.declarations = declarations + class DeclarationNode(Node): pass + + class ExpressionNode(Node): pass + class ClassDeclarationNode(DeclarationNode): def __init__(self, idx, features, parent=None): self.id = idx self.parent = parent self.features = features + class FuncDeclarationNode(DeclarationNode): def __init__(self, idx, params, return_type, body): self.id = idx @@ -24,17 +30,20 @@ def __init__(self, idx, params, return_type, body): self.type = return_type self.body = body + class AttrDeclarationNode(DeclarationNode): def __init__(self, idx, typex, expr=None): self.id = idx self.type = typex self.expr = expr + class AssignNode(ExpressionNode): def __init__(self, idx, expr): self.id = idx self.expr = expr + class CallNode(ExpressionNode): def __init__(self, obj, idx, args, cast_type=None): self.obj = obj @@ -42,48 +51,59 @@ def __init__(self, obj, idx, args, cast_type=None): self.args = args self.type = cast_type + class CaseNode(ExpressionNode): def __init__(self, expr, branch_list): self.expr = expr - #[(id, type, expr), ...] + # [(id, type, expr), ...] self.branch_list = branch_list + class BlockNode(ExpressionNode): def __init__(self, expr_list): self.expr_list = expr_list + class LoopNode(ExpressionNode): def __init__(self, cond, body): self.condition = cond self.body = body + class ConditionalNode(ExpressionNode): def __init__(self, cond, then_body, else_body): self.condition = cond self.then_body = then_body self.else_body = else_body + class LetNode(ExpressionNode): def __init__(self, id_list, body): - #[(id, type, expr), ...] + # [(id, type, expr), ...] self.id_list = id_list self.body = body + class AtomicNode(ExpressionNode): def __init__(self, lex): self.lex = lex + class UnaryNode(ExpressionNode): def __init__(self, expr): self.expr = expr + class BinaryNode(ExpressionNode): def __init__(self, left, right): self.left = left self.right = right + class ArithmeticNode(BinaryNode): pass + + class ComparisonNode(BinaryNode): pass @@ -91,44 +111,58 @@ class ComparisonNode(BinaryNode): class ConstantNumNode(AtomicNode): pass + class ConstantStringNode(AtomicNode): pass + class ConstantBoolNode(AtomicNode): pass + class VariableNode(AtomicNode): pass + class InstantiateNode(AtomicNode): pass + class PlusNode(ArithmeticNode): pass + class MinusNode(ArithmeticNode): pass + class StarNode(ArithmeticNode): pass + class DivNode(ArithmeticNode): pass + class LeqNode(ComparisonNode): pass + class LessNode(ComparisonNode): pass + class EqualNode(BinaryNode): pass + class VoidNode(UnaryNode): pass + class NotNode(UnaryNode): pass + class NegNode(UnaryNode): - pass \ No newline at end of file + pass diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py index 73397bae2..8974c2600 100644 --- a/src/compiler/cmp/cil_ast.py +++ b/src/compiler/cmp/cil_ast.py @@ -1,145 +1,230 @@ 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): + def __init__(self, fname, params, localvars, instructions, local_labels=0): self.name = fname self.params = params self.localvars = localvars self.instructions = instructions + self.local_labels = local_labels + 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): + +class LeqNode(ArithmeticNode): pass -class SetAttribNode(InstructionNode): + +class LessNode(ArithmeticNode): pass + +class EqualNode(ArithmeticNode): + pass + + +class EqualStrNode(ArithmeticNode): + pass + + +class VoidNode(ArithmeticNode): + pass + + +class NotNode(ArithmeticNode): + pass + + +class ComplementNode(InstructionNode): + def __init__(self, dest, obj): + self.dest = dest + self.obj = obj + + +class GetAttribNode(InstructionNode): + def __init__(self, dest, obj, attr): + self.dest = dest + self.obj = obj + self.attr = attr + + +class SetAttribNode(InstructionNode): + def __init__(self, obj, attr, value): + self.obj = obj + self.attr = attr + self.value = value + + 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): + def __init__(self, dest, obj): + self.dest = dest self.obj = obj + + +class NameNode(InstructionNode): + def __init__(self, dest, name): self.dest = dest + self.name = name + 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): 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 +class ErrorNode(InstructionNode): + def __init__(self, data_node): + self.data_node = data_node diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py index 5c486c604..5dcccca99 100644 --- a/src/compiler/visitors/cil_formatter.py +++ b/src/compiler/visitors/cil_formatter.py @@ -1,85 +1,147 @@ import compiler.visitors.visitor as visitor -from ..cmp.cil_ast import * +from ..cmp.cil_ast import * class PrintCILVisitor(object): - @visitor.on('node') + @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) + 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}' + 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) + 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}}' + 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) + 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}}' + 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}' + return f"PARAM {node.name}" @visitor.when(LocalNode) def visit(self, node): - return f'LOCAL {node.name}' + return f"LOCAL {node.name}" @visitor.when(AssignNode) def visit(self, node): - return f'{node.dest} = {node.source}' + return f"{node.dest} = {node.source}" @visitor.when(PlusNode) def visit(self, node): - return f'{node.dest} = {node.left} + {node.right}' + return f"{node.dest} = {node.left} + {node.right}" @visitor.when(MinusNode) def visit(self, node): - return f'{node.dest} = {node.left} - {node.right}' + return f"{node.dest} = {node.left} - {node.right}" @visitor.when(StarNode) def visit(self, node): - return f'{node.dest} = {node.left} * {node.right}' + return f"{node.dest} = {node.left} * {node.right}" @visitor.when(DivNode) def visit(self, node): - return f'{node.dest} = {node.left} / {node.right}' + return f"{node.dest} = {node.left} / {node.right}" + + @visitor.when(LeqNode) + 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(NotNode) + def visit(self, node): + return f"{node.dest} = NOT {node.obj}" + + @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(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}' + return f"{node.dest} = ALLOCATE {node.type}" @visitor.when(TypeOfNode) def visit(self, node): - return f'{node.dest} = TYPEOF {node.obj}' + 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}' + return f"{node.dest} = CALL {node.function}" @visitor.when(DynamicCallNode) def visit(self, node): - return f'{node.dest} = VCALL {node.type} {node.method}' + return f"{node.dest} = VCALL {node.type} {node.method}" @visitor.when(ArgNode) def visit(self, node): - return f'ARG {node.name}' + 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(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(ReadNode) + def visit(self, node): + return f"{node.dest} = READ" + + @visitor.when(PrintNode) + def visit(self, node): + return f"PRINT {node.value}" diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 8f6f2e394..429ecf83f 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -1,13 +1,45 @@ import compiler.visitors.visitor as visitor from ..cmp import cil_ast as cil -from ..cmp.semantic import Scope, SemanticError, ErrorType, IntType, BoolType, SelfType, AutoType, LCA, VariableInfo -from ..cmp.ast import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from ..cmp.ast import AssignNode, CallNode, CaseNode, BlockNode, LoopNode, ConditionalNode, LetNode +from ..cmp.semantic import ( + Scope, + SemanticError, + ErrorType, + IntType, + BoolType, + SelfType, + AutoType, + LCA, + VariableInfo, +) +from ..cmp.ast import ( + LeqNode, + LessNode, + ProgramNode, + ClassDeclarationNode, + AttrDeclarationNode, + FuncDeclarationNode, +) +from ..cmp.ast import ( + AssignNode, + CallNode, + CaseNode, + BlockNode, + LoopNode, + ConditionalNode, + LetNode, +) from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode from ..cmp.ast import VoidNode, NotNode, NegNode -from ..cmp.ast import ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode +from ..cmp.ast import ( + ConstantNumNode, + ConstantStringNode, + ConstantBoolNode, + VariableNode, + InstantiateNode, +) from ..cmp.ast import PlusNode, MinusNode, StarNode, DivNode + class BaseCOOLToCILVisitor: def __init__(self, context): self.dottypes = [] @@ -17,81 +49,102 @@ def __init__(self, context): 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_local(self, vinfo): - vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + 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, None) + vinfo = VariableInfo("internal", None, 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}' - + 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)}' + 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) + + 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 = self.register_data(msg) + self.register_instruction(cil.ErrorNode(data_node)) + self.register_instruction(continue_node) + + def init_name(self, type_name, attr=False): + if attr: + return f"init_attr_at_{type_name}" + return f"init_at_{type_name}" class COOLToCILVisitor(BaseCOOLToCILVisitor): - @visitor.on('node') + @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') + + self.current_function = self.register_function("entry") instance = self.define_internal_local() result = self.define_internal_local() - main_method_name = self.to_function_name('main', 'Main') - self.register_instruction(cil.AllocateNode('Main', instance)) + main_method_name = self.to_function_name("main", "Main") + self.register_instruction(cil.AllocateNode("Main", instance)) self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(main_method_name, 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): #################################################################### @@ -99,32 +152,56 @@ def visit(self, node, scope): # node.parent -> str # node.features -> [ FuncDeclarationNode/AttrDeclarationNode ... ] #################################################################### - + self.current_type = self.context.get_type(node.id) - + # Your code here!!! (Handle all the .TYPE section) type_node = self.register_type(self.current_type.name) - + visited_func = [] current = self.current_type while current is not None: attributes = [attr.name for attr in current.attributes] - methods = [func.name for func in current.methods if func.name not in visited_func] + methods = [ + func.name for func in current.methods if func.name not in visited_func + ] visited_func.extend(methods) type_node.attributes.extend(attributes[::-1]) - type_node.methods.extend([(item, self.to_function_name(item, current.name)) for item in methods[::-1]]) + type_node.methods.extend( + [ + (item, self.to_function_name(item, current.name)) + for item in methods[::-1] + ] + ) current = current.parent - + type_node.attributes.reverse() type_node.methods.reverse() - - - func_declarations = (f for f in node.features if isinstance(f, FuncDeclarationNode)) + + 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 + ############################### + expr = self.visit(node.expr, scope) + if node.expr: + self.register_instruction(cil.SetAttribNode(node.type, node.id, expr)) + + elif node.type in self.value_types: + value = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.type, value)) + self.register_instruction(cil.SetAttribNode(self.type, node.id, value)) + @visitor.when(FuncDeclarationNode) def visit(self, node, scope): ############################### @@ -133,21 +210,23 @@ def visit(self, node, scope): # node.type -> str # node.body -> [ ExpressionNode ... ] ############################### - + self.current_method = self.current_type.get_method(node.id) - + # Your code here!!! (Handle PARAMS) - self.current_function = self.register_function(self.to_function_name(self.current_method.name, self.current_type.name)) + self.current_function = self.register_function( + self.to_function_name(self.current_method.name, self.current_type.name) + ) self.current_vars = {} - self.params.append(cil.ParamNode('self')) - self.params.extend([cil.ParamNode(p) for p in self.current_method.param_names]) + self.params.append(cil.ParamNode("self")) + self.params.extend([cil.ParamNode(p) for p in self.current_method.param_names]) + + value = self.visit(node.body, scope) - value = self.visit(node.body,scope) - # Your code here!!! (Handle RETURN) self.register_instruction(cil.ReturnNode(value)) self.current_function = None - + self.current_method = None @visitor.when(AssignNode) @@ -156,10 +235,10 @@ def visit(self, node, scope): # node.id -> str # node.expr -> ExpressionNode ############################### - + # Your code here!!! - value = self.visit(node.expr, scope) - self.register_instruction(cil.AssignNode(node.id, value)) + value = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(node.id, value)) @visitor.when(CallNode) def visit(self, node, scope): @@ -167,64 +246,325 @@ def visit(self, node, scope): # node.obj -> AtomicNode # node.id -> str # node.args -> [ ExpressionNode ... ] - # node.type -> str + # node.type -> str ############################### # Your code here!!! - values = [self.visit(node.obj,scope)] - values.extend([self.visit(arg, scope) for arg in node.args]) + values = [self.visit(node.obj, scope)] + values.extend([self.visit(arg, scope) for arg in node.args]) local_vars = [self.define_internal_local() for v in values] - for (var,val) in zip( local_vars,values): - self.register_instruction(cil.AssignNode(var,val)) + for (var, val) in zip(local_vars, values): + self.register_instruction(cil.AssignNode(var, val)) for var in local_vars: self.register_instruction(cil.ArgNode(var)) - return_var = self.define_internal_local() - if node.type: # Dynamic Call - self.register_instruction(cil.DynamicCallNode(node.type,node.id,return_var)) - elif values[0] == 'self':#Static call - function = self.to_function_name(node.id,self.current_type.name) - self.register_instruction(cil.StaticCallNode(function,return_var)) - else: #Dynamic Call with type of obj + return_var = self.register_local(VariableInfo("call_node_value", None)) + if node.type: # Dynamic Call + self.register_instruction( + cil.DynamicCallNode(node.type, node.id, return_var) + ) + elif values[0] == "self": # Static call + function = self.to_function_name(node.id, self.current_type.name) + self.register_instruction(cil.StaticCallNode(function, return_var)) + else: # Dynamic Call with type of obj type_var = self.define_internal_local() self.register_instruction(cil.TypeOfNode(local_vars[0], type_var)) - self.register_instruction(cil.DynamicCallNode(type_var,node.id,return_var)) + self.register_instruction( + cil.DynamicCallNode(type_var, node.id, return_var) + ) return return_var - + @visitor.when(ConditionalNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.if_body -> ExpressionNode + # node.else_body -> ExpressionNode + ################################## + + then_label_node = self.register_label("then_label") + else_label_node = self.register_label("else_label") + continue_label_node = self.register_label("continue_label") - @visitor.when(ConstantNumNode) + cond_ret = self.visit(node.condition, scope) + self.register_instruction(cil.GotoIfNode(cond_ret, then_label_node.label)) + self.register_instruction(cil.GotoNode(else_label_node.label)) + + ret = self.register_local(VariableInfo("if_then_else_value", None)) + + # Label then_label + self.register_instruction(then_label_node) + ret_then = self.visit(node.then_body, scope) + self.register_instruction(cil.AssignNode(ret, ret_then)) + self.register_instruction(cil.GotoNode(continue_label_node.label)) + + # Label else_label + self.register_instruction(else_label_node) + ret_else = self.visit(node.else_body, scope) + self.register_instruction(cil.AssignNode(ret, ret_else)) + + self.register_instruction(continue_label_node) + return ret + + @visitor.when(LoopNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.body -> ExpressionNode + ################################### + + while_label_node = self.register_label("while_label") + loop_label_node = self.register_label("loop_label") + pool_label_node = self.register_label("pool_label") + + condition = self.define_internal_local() + self.register_instruction(while_label_node) + + condition_ret = self.visit(node.condition, scope) + self.register_instruction(cil.AssignNode(condition, condition_ret)) + + self.register_instruction(cil.GotoIfNode(condition, loop_label_node.label)) + self.register_instruction(cil.GotoNode(pool_label_node.label)) + self.register_instruction(loop_label_node) + + self.visit(node.body, scope) + + self.register_instruction(cil.GotoNode(while_label_node.label)) + + self.register_instruction(pool_label_node) + + return cil.VoidNode() + + @visitor.when(BlockNode) + def visit(self, node, scope): + ####################################### + # node.exprs -> [ ExpressionNode ... ] + ####################################### + ret = self.register_local(VariableInfo("block_node_value", None)) + + for expr in node.exprs: + ret_value = self.visit(expr, scope) + + self.register_instruction(cil.AssignNode(ret, ret_value)) + + return ret + + @visitor.when(LetNode) + def visit(self, node, scope): + ############################################ + # node.id_list -> [(id, type, expr), ...] + # node.in_body -> ExpressionNode + ############################################ + ret = self.register_local(VariableInfo("let_in_value", None)) + + values = [self.visit(l.expr, scope) for l in node.id_list] + local_vars = [self.define_internal_local() for _ in values] + for (var, val) in zip(local_vars, values): + self.register_instruction(cil.AssignNode(var, val)) + + ret_val = self.visit(node.in_body, scope) + self.register_instruction(cil.AssignNode(ret, ret_val)) + + return ret + + # create case visitor + # @visitor.when(CaseNode) + # def visit(self, node, scope): + # ############################################## + # # node.expr -> ExpressionNode + # # node.branches -> [(id, type, expr), ... } + # ############################################## + # ret = self.register_local(VariableInfo("case_expr_value", None)) + # ret_type = self.register_local(VariableInfo("typeName_value", None)) + # vcond = self.register_local(VariableInfo("equal_value", None)) + # ret = self.register_local(VariableInfo("case_value", None)) + + # ret_val = self.visit(node.expr, scope) + + # self.register_instruction(cil.AssignNode(ret, ret_val)) + # self.register_instruction(cil.TypeOfNode(ret_type, ret_val)) + + # # 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, ret, void)) + + # self.register_runtime_error( + # equal_result, + # f"({node.position.row},{node.position.column}) - RuntimeError: Case on void\n", + # ) + + # end_label = self.register_label("end_label") + # labels = [] + # old = {} + + # # 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: + # self.register_instruction(cil.GotoNode(labels[-1].label)) + # break + # h.add(b.type) + # for s in old: + # h -= s + # 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.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(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)) + # self.register_instruction(cil.GotoNode(end_label.label)) + + # scope.ret_expr = vret + # self.register_instruction(end_label) + + @visitor.when(NotNode) def visit(self, node, scope): ############################### - # node.lex -> str + # node.expr -> ExpressionNode ############################### - - # Your code here!!! (Pretty easy ;-)) - return node.lex - + value = self.define_internal_local() + value_minus_1 = self.define_internal_local() + ret = self.define_internal_local() - @visitor.when(VariableNode) + ret_value = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(value, ret_value)) + self.register_instruction(cil.MinusNode(value_minus_1, 1, value)) + + self.register_instruction(cil.ArgNode(value_minus_1)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret + + @visitor.when(LeqNode) def visit(self, node, scope): ############################### - # node.lex -> str + # node.left -> ExpressionNode + # node.right -> ExpressionNode ############################### - - # Your code here!!! - return node.lex - + ret_value = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + ret = self.define_internal_local() - @visitor.when(InstantiateNode) + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + + self.register_instruction(cil.AssignNode(left_value, left)) + self.register_instruction(cil.AssignNode(right_value, right)) + self.register_instruction(cil.LessEqualNode(ret_value, left_value, right_value)) + + self.register_instruction(cil.ArgNode(ret_value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + + return ret + + @visitor.when(LessNode) def visit(self, node, scope): ############################### - # node.lex -> str + # node.left -> ExpressionNode + # node.right -> ExpressionNode ############################### - - # Your code here!!! - var = self.define_internal_local() - self.register_instruction(cil.AllocateNode(node.lex, var)) - return var + value = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + ret = self.define_internal_local() + + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + self.register_instruction(cil.AssignNode(left_value, left)) + self.register_instruction(cil.AssignNode(right_value, right)) + self.register_instruction(cil.LessNode(value, left_value, right_value)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret + + @visitor.when(EqualNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + return_vale = 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() + ret = self.define_internal_local() + + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + + self.register_instruction(cil.TypeOfNode(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") + 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_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)) + + self.register_instruction(int_node) + self.register_instruction(cil.AssignNode(left_value, left)) + self.register_instruction(cil.AssignNode(right_value, right)) + self.register_instruction(cil.EqualNode(return_vale, left_value, right_value)) + self.register_instruction(cil.GotoNode(continue_node.label)) + + self.register_instruction(string_node) + self.register_instruction(cil.AssignNode(left_value, left)) + self.register_instruction(cil.AssignNode(right_value, right)) + self.register_instruction( + cil.EqualStrNode(return_vale, left_value, right_value) + ) + self.register_instruction(cil.GotoNode(continue_node.label)) + + self.register_instruction(reference_node) + self.register_instruction(cil.EqualNode(return_vale, left, right)) + + self.register_instruction(continue_node) + self.register_instruction(cil.ArgNode(return_vale)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret @visitor.when(PlusNode) def visit(self, node, scope): @@ -232,15 +572,21 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - # Your code here!!! - var = self.define_internal_local() + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() left = self.visit(node.left, scope) + self.register_instruction(cil.AssignNode(value_left, left)) right = self.visit(node.right, scope) - self.register_instruction(cil.PlusNode(var, left, right)) - return var - - + self.register_instruction(cil.AssignNode(value_right, right)) + + self.register_instruction(cil.PlusNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret @visitor.when(MinusNode) def visit(self, node, scope): @@ -248,14 +594,21 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - # Your code here!!! - var = self.define_internal_local() + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() left = self.visit(node.left, scope) + self.register_instruction(cil.AssignNode(value_left, left)) right = self.visit(node.right, scope) - self.register_instruction(cil.MinusNode(var, left, right)) - return var - + self.register_instruction(cil.AssignNode(value_right, right)) + + self.register_instruction(cil.MinusNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret @visitor.when(StarNode) def visit(self, node, scope): @@ -263,14 +616,21 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - # Your code here!!! - var = self.define_internal_local() + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() left = self.visit(node.left, scope) + self.register_instruction(cil.AssignNode(value_left, left)) right = self.visit(node.right, scope) - self.register_instruction(cil.StarNode(var, left, right)) - return var - + self.register_instruction(cil.AssignNode(value_right, right)) + + self.register_instruction(cil.StarNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret @visitor.when(DivNode) def visit(self, node, scope): @@ -278,11 +638,133 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - - # Your code here!!! - var = self.define_internal_local() + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() left = self.visit(node.left, scope) + self.register_instruction(cil.AssignNode(value_left, left)) right = self.visit(node.right, scope) - self.register_instruction(cil.DivNode(var, left, right)) - return var - \ No newline at end of file + self.register_instruction(cil.AssignNode(value_right, right)) + + # Check division by 0 + equal_result = self.define_internal_local() + self.register_instruction(cil.EqualNode(equal_result, value_right, 0)) + self.register_runtime_error( + equal_result, + f"({node.position.row},{node.position.column}) - RuntimeError: Division by zero\n", + ) + + self.register_instruction(cil.DivNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(VoidNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + void = cil.VoidNode() + value = self.define_internal_local() + left = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(value, left)) + + ret = self.define_internal_local() + self.register_instruction(cil.EqualNode(ret, value, void)) + self.register_instruction(cil.ArgNode(ret)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret + + @visitor.when(NegNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + value = self.define_internal_local() + complement_value = self.define_internal_local() + ret = self.define_internal_local() + left = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(value, left)) + + self.register_instruction(cil.ComplementNode(complement_value, value)) + self.register_instruction(cil.ArgNode(complement_value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + ############################### + # node.type -> str + ############################### + ret = self.define_internal_local() + + if node.type == SelfType().name: + value = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(value, node.type)) + self.register_instruction(cil.AllocateNode(value, ret)) + 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.init_name(node.type), ret)) + return ret + + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + ret = self.define_internal_local() + self.register_instruction(cil.ArgNode(int(node.lex))) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(VariableNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + self.current_type.get_attribute(node.lex) + attr = self.register_local(VariableInfo(node.lex, None)) + self.register_instruction(cil.GetAttribNode(attr, node, node.lex)) + return attr + + @visitor.when(ConstantStringNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + try: + data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] + except IndexError: + data_node = self.register_data(node.lex) + + vmsg = self.register_local(VariableInfo("msg", None)) + ret = self.define_internal_local() + self.register_instruction(cil.LoadNode(vmsg, data_node)) + self.register_instruction(cil.ArgNode(vmsg)) + self.register_instruction(cil.StaticCallNode(self.init_name("String"), ret)) + return ret + + @visitor.when(ConstantBoolNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + if node.lex == "true": + v = 1 + else: + v = 0 + ret = self.define_internal_local() + self.register_instruction(cil.ArgNode(v)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret From 2f3b09d324051c437de219ff14ae61630592da38 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 11:35:06 -0500 Subject: [PATCH 091/138] added tokens to ast nodes --- .gitignore | 1 + src/code.cl | 34 ++++++++ src/compiler/cmp/ast.py | 55 ++++++++---- src/compiler/cmp/evaluation.py | 4 +- src/compiler/cmp/grammar.py | 61 ++++++------- src/compiler/cmp/nfa_dfa.py | 134 ++++++++++++++--------------- src/compiler/cmp/utils.py | 17 +++- src/compiler/visitors/formatter.py | 7 +- src/main.py | 24 +++--- 9 files changed, 203 insertions(+), 134 deletions(-) create mode 100644 src/code.cl diff --git a/.gitignore b/.gitignore index 4acafde18..621bae8f2 100644 --- a/.gitignore +++ b/.gitignore @@ -408,3 +408,4 @@ dmypy.json # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) +.vscode/ \ No newline at end of file diff --git a/src/code.cl b/src/code.cl new file mode 100644 index 000000000..5338472cd --- /dev/null +++ b/src/code.cl @@ -0,0 +1,34 @@ +class Main inherits IO { + number: Int <- 5; + + main () : Object { + testing_fibonacci(number) + }; + + testing_fibonacci(n: Int) : IO {{ + out_string("Iterative Fibonacci : "); + out_int(iterative_fibonacci(5)); + out_string("\\n"); + + out_string("Recursive Fibonacci : "); + out_int(recursive_fibonacci(5)); + out_string("\\n"); + }}; + + recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { + if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi + }; + + iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { + let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { + while i < n loop + let temp: Int <- n2 in { + n2 <- n2 + n1; + n1 <- temp; + i <- i + 1; + } + pool; + n2; + } + }; +}; \ No newline at end of file diff --git a/src/compiler/cmp/ast.py b/src/compiler/cmp/ast.py index 419c5f0e3..e04e5f9f8 100644 --- a/src/compiler/cmp/ast.py +++ b/src/compiler/cmp/ast.py @@ -1,9 +1,14 @@ +from .utils import emptyToken + + class Node: - pass + def __init__(self, token): + self.token = token class ProgramNode(Node): def __init__(self, declarations): + super().__init__(emptyToken) self.declarations = declarations @@ -16,88 +21,102 @@ class ExpressionNode(Node): class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent=None): + def __init__(self, idx, features, token, parent=None): self.id = idx + self.token = token self.parent = parent self.features = features class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx, params, return_type, body): - self.id = idx + def __init__(self, token, params, return_type, body): + self.id = token.lex # param = (name, type) self.params = params self.type = return_type self.body = body + self.token = token class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expr=None): + def __init__(self, idx, typex, expr=None, token=emptyToken): self.id = idx self.type = typex self.expr = expr + self.token = token class AssignNode(ExpressionNode): - def __init__(self, idx, expr): + def __init__(self, idx, expr, token): self.id = idx self.expr = expr + self.token = token class CallNode(ExpressionNode): - def __init__(self, obj, idx, args, cast_type=None): + def __init__(self, obj, token, args, cast_type=emptyToken): self.obj = obj - self.id = idx + self.id = token.lex self.args = args - self.type = cast_type + self.type = cast_type.lex + self.token = token + self.typeToken = cast_type class CaseNode(ExpressionNode): - def __init__(self, expr, branch_list): + def __init__(self, expr, branch_list, token): self.expr = expr # [(id, type, expr), ...] self.branch_list = branch_list + self.token = token class BlockNode(ExpressionNode): - def __init__(self, expr_list): + def __init__(self, expr_list, token): self.expr_list = expr_list + self.token = token class LoopNode(ExpressionNode): - def __init__(self, cond, body): + def __init__(self, cond, body, token): self.condition = cond self.body = body + self.token = token class ConditionalNode(ExpressionNode): - def __init__(self, cond, then_body, else_body): + def __init__(self, cond, then_body, else_body, token): self.condition = cond self.then_body = then_body self.else_body = else_body + self.token = token class LetNode(ExpressionNode): - def __init__(self, id_list, body): + def __init__(self, id_list, body, token): # [(id, type, expr), ...] self.id_list = id_list self.body = body + self.token = token class AtomicNode(ExpressionNode): - def __init__(self, lex): - self.lex = lex + def __init__(self, token): + self.lex = token.lex + self.token = token class UnaryNode(ExpressionNode): - def __init__(self, expr): + def __init__(self, expr, symbol): self.expr = expr + self.token = symbol class BinaryNode(ExpressionNode): - def __init__(self, left, right): + def __init__(self, left, right, symbol): self.left = left self.right = right + self.token = symbol class ArithmeticNode(BinaryNode): diff --git a/src/compiler/cmp/evaluation.py b/src/compiler/cmp/evaluation.py index 6e976b463..b1d8d7b99 100644 --- a/src/compiler/cmp/evaluation.py +++ b/src/compiler/cmp/evaluation.py @@ -12,10 +12,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:] diff --git a/src/compiler/cmp/grammar.py b/src/compiler/cmp/grammar.py index 113701f05..dad41d765 100644 --- a/src/compiler/cmp/grammar.py +++ b/src/compiler/cmp/grammar.py @@ -1,5 +1,6 @@ from .pycompiler import Grammar from .ast import * +from .utils import selfToken # grammar G = Grammar() @@ -51,11 +52,11 @@ def_class %= ( classx + typeid + ocur + feature_list + ccur + semi, - lambda h, s: ClassDeclarationNode(s[2], s[4]), + lambda h, s: ClassDeclarationNode(s[2], s[4], s[1]), ) def_class %= ( classx + typeid + inherits + typeid + ocur + feature_list + ccur + semi, - lambda h, s: ClassDeclarationNode(s[2], s[6], s[4]), + lambda h, s: ClassDeclarationNode(s[2], s[6], s[1], s[4]), ) feature_list %= G.Epsilon, lambda h, s: [] @@ -67,7 +68,7 @@ ) def_attr %= ( objectid + colon + typeid + larrow + expr + semi, - lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]), + lambda h, s: AttrDeclarationNode(s[1], s[3], s[5], s[4]), ) def_func %= ( @@ -87,22 +88,22 @@ expr %= s_comp, lambda h, s: s[1] comp %= arith, lambda h, s: s[1] -comp %= arith + leq + arith, lambda h, s: LeqNode(s[1], s[3]) -comp %= arith + less + arith, lambda h, s: LessNode(s[1], s[3]) -comp %= arith + equal + arith, lambda h, s: EqualNode(s[1], s[3]) +comp %= arith + leq + arith, lambda h, s: LeqNode(s[1], s[3], s[2]) +comp %= arith + less + arith, lambda h, s: LessNode(s[1], s[3], s[2]) +comp %= arith + equal + arith, lambda h, s: EqualNode(s[1], s[3], s[2]) arith %= term, 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]) term %= factor, 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]) factor %= atom, lambda h, s: s[1] factor %= opar + expr + cpar, lambda h, s: s[2] -factor %= isvoid + factor, lambda h, s: VoidNode(s[2]) -factor %= neg + factor, lambda h, s: NegNode(s[2]) +factor %= isvoid + factor, lambda h, s: VoidNode(s[2], s[1]) +factor %= neg + factor, lambda h, s: NegNode(s[2], s[1]) factor %= func_call, lambda h, s: s[1] factor %= case_def, lambda h, s: s[1] factor %= block_def, lambda h, s: s[1] @@ -116,7 +117,7 @@ atom %= new + typeid, lambda h, s: InstantiateNode(s[2]) func_call %= objectid + opar + arg_list + cpar, lambda h, s: CallNode( - VariableNode("self"), s[1], s[3] + VariableNode(selfToken), s[1], s[3] ) func_call %= factor + dot + objectid + opar + arg_list + cpar, lambda h, s: CallNode( s[1], s[3], s[5] @@ -131,7 +132,9 @@ args %= expr, lambda h, s: [s[1]] args %= expr + comma + args, lambda h, s: [s[1]] + s[3] -case_def %= case + expr + of + branch_list + esac, lambda h, s: CaseNode(s[2], s[4]) +case_def %= case + expr + of + branch_list + esac, lambda h, s: CaseNode( + s[2], s[4], s[1] +) branch_list %= branch, lambda h, s: [s[1]] branch_list %= branch + branch_list, lambda h, s: [s[1]] + s[2] branch %= objectid + colon + typeid + rarrow + expr + semi, lambda h, s: ( @@ -140,39 +143,39 @@ s[5], ) -block_def %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2]) +block_def %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2], s[1]) expr_list %= expr + semi, lambda h, s: [s[1]] expr_list %= expr + semi + expr_list, lambda h, s: [s[1]] + s[3] -loop_def %= whilex + expr + loop + expr + pool, lambda h, s: LoopNode(s[2], s[4]) +loop_def %= whilex + expr + loop + expr + pool, lambda h, s: LoopNode(s[2], s[4], s[1]) cond_def %= ifx + expr + then + expr + elsex + expr + fi, lambda h, s: ConditionalNode( - s[2], s[4], s[6] + s[2], s[4], s[6], s[1] ) s_comp %= s_arith, lambda h, s: s[1] -s_comp %= arith + leq + s_arith, lambda h, s: LeqNode(s[1], s[3]) -s_comp %= arith + less + s_arith, lambda h, s: LessNode(s[1], s[3]) -s_comp %= arith + equal + s_arith, lambda h, s: EqualNode(s[1], s[3]) +s_comp %= arith + leq + s_arith, lambda h, s: LeqNode(s[1], s[3], s[2]) +s_comp %= arith + less + s_arith, lambda h, s: LessNode(s[1], s[3], s[2]) +s_comp %= arith + equal + s_arith, lambda h, s: EqualNode(s[1], s[3], s[2]) s_arith %= s_term, lambda h, s: s[1] -s_arith %= arith + plus + s_term, lambda h, s: PlusNode(s[1], s[3]) -s_arith %= arith + minus + s_term, lambda h, s: MinusNode(s[1], s[3]) +s_arith %= arith + plus + s_term, lambda h, s: PlusNode(s[1], s[3], s[2]) +s_arith %= arith + minus + s_term, lambda h, s: MinusNode(s[1], s[3], s[2]) s_term %= s_factor, lambda h, s: s[1] -s_term %= term + star + s_factor, lambda h, s: StarNode(s[1], s[3]) -s_term %= term + div + s_factor, lambda h, s: DivNode(s[1], s[3]) +s_term %= term + star + s_factor, lambda h, s: StarNode(s[1], s[3], s[2]) +s_term %= term + div + s_factor, lambda h, s: DivNode(s[1], s[3], s[2]) -s_factor %= notx + expr, lambda h, s: NotNode(s[2]) +s_factor %= notx + expr, lambda h, s: NotNode(s[2], s[1]) s_factor %= let_def, lambda h, s: s[1] s_factor %= assign_def, lambda h, s: s[1] -s_factor %= isvoid + s_factor, lambda h, s: VoidNode(s[2]) -s_factor %= neg + s_factor, lambda h, s: NegNode(s[2]) +s_factor %= isvoid + s_factor, lambda h, s: VoidNode(s[2], s[1]) +s_factor %= neg + s_factor, lambda h, s: NegNode(s[2], s[1]) -let_def %= let + iden_list + inx + expr, lambda h, s: LetNode(s[2], s[4]) +let_def %= let + iden_list + inx + expr, lambda h, s: LetNode(s[2], s[4], s[1]) iden_list %= iden, lambda h, s: [s[1]] iden_list %= iden + comma + iden_list, lambda h, s: [s[1]] + s[3] iden %= objectid + colon + typeid, lambda h, s: (s[1], s[3], None) iden %= objectid + colon + typeid + larrow + expr, lambda h, s: (s[1], s[3], s[5]) -assign_def %= objectid + larrow + expr, lambda h, s: AssignNode(s[1], s[3]) +assign_def %= objectid + larrow + expr, lambda h, s: AssignNode(s[1], s[3], s[2]) diff --git a/src/compiler/cmp/nfa_dfa.py b/src/compiler/cmp/nfa_dfa.py index b3953cce6..84b81d7dc 100644 --- a/src/compiler/cmp/nfa_dfa.py +++ b/src/compiler/cmp/nfa_dfa.py @@ -1,4 +1,4 @@ -import pydot +# import pydot class NFA: @@ -24,75 +24,75 @@ def epsilon_transitions(self, state): except KeyError: return () - def graph(self): - G = pydot.Dot(rankdir="LR", margin=0.1) - G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) - - for (start, tran), destinations in self.map.items(): - tran = "ε" if tran == "" else tran - G.add_node( - pydot.Node( - start, shape="circle", style="bold" if start in self.finals else "" - ) - ) - for end in destinations: - G.add_node( - pydot.Node( - end, shape="circle", style="bold" if end in self.finals else "" - ) - ) - G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) - - G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) - return G - - def _repr_svg_(self): - try: - return self.graph().create_svg().decode("utf8") - except: - pass - - def _repr_png_(self): - try: - return self.graph() - except: - pass + # def graph(self): + # G = pydot.Dot(rankdir="LR", margin=0.1) + # G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) + + # for (start, tran), destinations in self.map.items(): + # tran = "ε" if tran == "" else tran + # G.add_node( + # pydot.Node( + # start, shape="circle", style="bold" if start in self.finals else "" + # ) + # ) + # for end in destinations: + # G.add_node( + # pydot.Node( + # end, shape="circle", style="bold" if end in self.finals else "" + # ) + # ) + # G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) + + # G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) + # return G + + # def _repr_svg_(self): + # try: + # return self.graph().create_svg().decode("utf8") + # except: + # pass + + # def _repr_png_(self): + # try: + # return self.graph() + # except: + # pass class DFA(NFA): - def graph(self): - G = pydot.Dot(rankdir="LR", margin=0.1) - G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) - - for (start, tran), destinations in self.map.items(): - tran = "ε" if tran == "" else tran - G.add_node( - pydot.Node( - start, shape="circle", style="bold" if start in self.finals else "" - ) - ) - for end in destinations: - G.add_node( - pydot.Node( - end, shape="circle", style="bold" if end in self.finals else "" - ) - ) - G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) - - G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) - return G - - def _repr_svg_(self): - try: - return self.graph().create_svg().decode("utf8") - except: - pass - - def _repr_png_(self): - try: - return self.graph() - except: - pass + # def graph(self): + # G = pydot.Dot(rankdir="LR", margin=0.1) + # G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) + + # for (start, tran), destinations in self.map.items(): + # tran = "ε" if tran == "" else tran + # G.add_node( + # pydot.Node( + # start, shape="circle", style="bold" if start in self.finals else "" + # ) + # ) + # for end in destinations: + # G.add_node( + # pydot.Node( + # end, shape="circle", style="bold" if end in self.finals else "" + # ) + # ) + # G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) + + # G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) + # return G + + # def _repr_svg_(self): + # try: + # return self.graph().create_svg().decode("utf8") + # except: + # pass + + # def _repr_png_(self): + # try: + # return self.graph() + # except: + # pass def __init__(self, states, finals, transitions, start=0): assert all(isinstance(value, int) for value in transitions.values()) diff --git a/src/compiler/cmp/utils.py b/src/compiler/cmp/utils.py index 268a0c050..94230cb87 100644 --- a/src/compiler/cmp/utils.py +++ b/src/compiler/cmp/utils.py @@ -1,4 +1,7 @@ from typing import List + +from black import err +from numpy import empty from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon @@ -233,7 +236,7 @@ def __init__(self, G, verbose=False): def _build_parsing_table(self): raise NotImplementedError() - def __call__(self, w : List[Token], get_shift_reduce=False): + def __call__(self, w: List[Token], get_shift_reduce=False): stack = [0] cursor = 0 output = [] @@ -247,12 +250,14 @@ def __call__(self, w : List[Token], get_shift_reduce=False): try: if state not in self.action or lookahead not in self.action[state]: - return None, (True, w[cursor]) + error = f"{w[cursor].pos} - SyntacticError: ERROR at or near {w[cursor].lex}" + return None, error except: print(state) print(self.action) print(lookahead) - return None, (True, w[cursor]) + error = f"{w[cursor].pos} - SyntacticError: ERROR at or near {w[cursor].lex}" + return None, error action, tag = list(self.action[state][lookahead])[0] if action is self.SHIFT: @@ -266,6 +271,10 @@ def __call__(self, w : List[Token], get_shift_reduce=False): stack.append(list(self.goto[stack[-1]][tag.Left])[0]) output.append(tag) elif action is ShiftReduceParser.OK: - return (output if not get_shift_reduce else (output, operations)), (False, None) + return (output if not get_shift_reduce else (output, operations)), None else: raise ValueError + + +emptyToken = Token("", "", (0, 0)) +selfToken = Token("self", "", (0, 0)) diff --git a/src/compiler/visitors/formatter.py b/src/compiler/visitors/formatter.py index b9cfd702f..fac1cf025 100644 --- a/src/compiler/visitors/formatter.py +++ b/src/compiler/visitors/formatter.py @@ -37,7 +37,8 @@ def visit(self, node, tabs=0): @visitor.when(FuncDeclarationNode) def visit(self, node, tabs=0): - params = ", ".join(" : ".join(param) for param in node.params) + paramStr = [(id.lex, typex.lex) for (id, typex) in node.params] + params = ", ".join(" : ".join(param) for param in paramStr) ans = ( "\t" * tabs + f"\\__FuncDeclarationNode: {node.id}({params}) : {node.type} {{ }}" @@ -123,7 +124,9 @@ def visit(self, node, tabs=0): @visitor.when(AtomicNode) def visit(self, node, tabs=0): - return "\t" * tabs + f"\\__ {node.__class__.__name__}: {node.lex}" + return ( + "\t" * tabs + f"\\__ {node.__class__.__name__}: {node.lex, node.token.pos}" + ) @visitor.when(UnaryNode) def visit(self, node, tabs=0): diff --git a/src/main.py b/src/main.py index 4cf792fe6..eabbe9baa 100644 --- a/src/main.py +++ b/src/main.py @@ -7,8 +7,9 @@ from sys import exit from compiler.cmp.tools import LR1Parser -# from compiler.cmp.evaluation import evaluate_reverse_parse -# from compiler.visitors.formatter import FormatVisitor +from compiler.cmp.evaluation import evaluate_reverse_parse +from compiler.visitors.formatter import FormatVisitor + # from compiler.visitors.type_collector import TypeCollector # from compiler.visitors.type_builder import TypeBuilder # from compiler.visitors.type_checker import TypeChecker @@ -23,25 +24,24 @@ def main(args): print(f"(0,0) - CompilerError: file {args.file} not found") exit(1) + # Lexer lexer = CoolLexer() tokens, errors = lexer.tokenize(code) for error in errors: print(error) - if errors: exit(1) - + + # Parser parser = LR1Parser(G) - parseResult, (failed, token) = parser(tokens, get_shift_reduce=True) - if failed: - print(f"{token.pos} - SyntacticError: ERROR at or near {token.lex}") + parseResult, error = parser(tokens, get_shift_reduce=True) + if error: + print(error) exit(1) - # print('\n'.join(repr(x) for x in parse)) - # print("---------------OPERATIONS---------------") - # print(operations) - # print('==================== AST ======================') - # ast = evaluate_reverse_parse(parse, operations, tokens) + # AST + parse, operations = parseResult + ast = evaluate_reverse_parse(parse, operations, tokens) # formatter = FormatVisitor() # tree = formatter.visit(ast) # print(tree) From 57a04858b91579f8539c1d669deb063582f5a54a Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 20:25:52 -0500 Subject: [PATCH 092/138] Modify ast and grammar to receive tokens Started adding annotations for better readability --- src/compiler/cmp/ast.py | 137 ++++++++++++++++++++++++++---------- src/compiler/cmp/grammar.py | 12 ++-- 2 files changed, 106 insertions(+), 43 deletions(-) diff --git a/src/compiler/cmp/ast.py b/src/compiler/cmp/ast.py index e04e5f9f8..79458956f 100644 --- a/src/compiler/cmp/ast.py +++ b/src/compiler/cmp/ast.py @@ -1,17 +1,12 @@ -from .utils import emptyToken +from typing import List, Optional, Tuple, Union +from .utils import Token, emptyToken class Node: - def __init__(self, token): + def __init__(self, token: Token): self.token = token -class ProgramNode(Node): - def __init__(self, declarations): - super().__init__(emptyToken) - self.declarations = declarations - - class DeclarationNode(Node): pass @@ -20,100 +15,168 @@ class ExpressionNode(Node): pass -class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, token, parent=None): - self.id = idx - self.token = token - self.parent = parent - self.features = features - - class FuncDeclarationNode(DeclarationNode): - def __init__(self, token, params, return_type, body): + def __init__( + self, + token: Token, + params: List[Tuple[Token, Token]], + return_type: Token, + body: ExpressionNode, + ): self.id = token.lex # param = (name, type) self.params = params - self.type = return_type + self.type = return_type.lex + self.typeToken = return_type self.body = body self.token = token class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expr=None, token=emptyToken): - self.id = idx - self.type = typex + def __init__( + self, + idx: Token, + typex: Token, + expr: Optional[ExpressionNode] = None, + token: Token = emptyToken, + ): + self.id = idx.lex + self.idToken = idx + self.type = typex.lex + self.typeToken = typex self.expr = expr self.token = token +class ClassDeclarationNode(DeclarationNode): + def __init__( + self, + idx: Token, + features: List[Union[FuncDeclarationNode, AttrDeclarationNode]], + token: Token, + parent: Optional[Token] = None, + ): + self.id = idx.lex + self.tokenId = idx + self.token = token + self.parent = parent + self.features = features + + +class ProgramNode(Node): + def __init__(self, declarations: List[ClassDeclarationNode]): + super().__init__(emptyToken) + self.declarations = declarations + + class AssignNode(ExpressionNode): - def __init__(self, idx, expr, token): - self.id = idx + def __init__(self, idx: Token, expr: ExpressionNode, token: Token): + self.id = idx.lex + self.idToken = idx self.expr = expr self.token = token class CallNode(ExpressionNode): - def __init__(self, obj, token, args, cast_type=emptyToken): - self.obj = obj - self.id = token.lex + def __init__( + self, + obj: Token, + idx: Token, + args: List[ExpressionNode], + cast_type: Token = emptyToken, + ): + self.obj = obj.lex + self.objToken = obj + self.id = idx.lex self.args = args self.type = cast_type.lex - self.token = token + self.token = idx self.typeToken = cast_type +class CaseBranchNode(Node): + def __init__(self, token: Token, idx: Token, typex: Token, expr: ExpressionNode): + self.token = token + self.id = idx.lex + self.idToken = idx + self.typex = typex.lex + self.typexToken = typex + self.expression = expr + + class CaseNode(ExpressionNode): - def __init__(self, expr, branch_list, token): + def __init__( + self, expr: ExpressionNode, branch_list: List[CaseBranchNode], token: Token + ): self.expr = expr - # [(id, type, expr), ...] self.branch_list = branch_list self.token = token class BlockNode(ExpressionNode): - def __init__(self, expr_list, token): + def __init__(self, expr_list: List[ExpressionNode], token: Token): self.expr_list = expr_list self.token = token class LoopNode(ExpressionNode): - def __init__(self, cond, body, token): + def __init__(self, cond: ExpressionNode, body: ExpressionNode, token: Token): self.condition = cond self.body = body self.token = token class ConditionalNode(ExpressionNode): - def __init__(self, cond, then_body, else_body, token): + def __init__( + self, + cond: ExpressionNode, + then_body: ExpressionNode, + else_body: ExpressionNode, + token: Token, + ): self.condition = cond self.then_body = then_body self.else_body = else_body self.token = token +class LetVarNode(Node): + def __init__( + self, + idx: Token, + typex: Token, + expr: Optional[ExpressionNode] = None, + token: Token = emptyToken, + ): + self.token = token + self.id = idx.lex + self.idToken = idx + self.typex = typex.lex + self.typexToken = typex + self.expression = expr + + class LetNode(ExpressionNode): - def __init__(self, id_list, body, token): - # [(id, type, expr), ...] + def __init__(self, id_list: List[LetVarNode], body: ExpressionNode, token: Token): self.id_list = id_list self.body = body self.token = token class AtomicNode(ExpressionNode): - def __init__(self, token): + def __init__(self, token: Token): self.lex = token.lex self.token = token class UnaryNode(ExpressionNode): - def __init__(self, expr, symbol): + def __init__(self, expr: ExpressionNode, symbol: Token): self.expr = expr self.token = symbol class BinaryNode(ExpressionNode): - def __init__(self, left, right, symbol): + def __init__(self, left: ExpressionNode, right: ExpressionNode, symbol: Token): self.left = left self.right = right self.token = symbol diff --git a/src/compiler/cmp/grammar.py b/src/compiler/cmp/grammar.py index dad41d765..da8b4a1e6 100644 --- a/src/compiler/cmp/grammar.py +++ b/src/compiler/cmp/grammar.py @@ -137,10 +137,8 @@ ) branch_list %= branch, lambda h, s: [s[1]] branch_list %= branch + branch_list, lambda h, s: [s[1]] + s[2] -branch %= objectid + colon + typeid + rarrow + expr + semi, lambda h, s: ( - s[1], - s[3], - s[5], +branch %= objectid + colon + typeid + rarrow + expr + semi, lambda h, s: CaseBranchNode( + s[4], s[1], s[3], s[5] ) block_def %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2], s[1]) @@ -175,7 +173,9 @@ let_def %= let + iden_list + inx + expr, lambda h, s: LetNode(s[2], s[4], s[1]) iden_list %= iden, lambda h, s: [s[1]] iden_list %= iden + comma + iden_list, lambda h, s: [s[1]] + s[3] -iden %= objectid + colon + typeid, lambda h, s: (s[1], s[3], None) -iden %= objectid + colon + typeid + larrow + expr, lambda h, s: (s[1], s[3], s[5]) +iden %= objectid + colon + typeid, lambda h, s: LetVarNode(s[1], s[3]) +iden %= objectid + colon + typeid + larrow + expr, lambda h, s: LetVarNode( + s[1], s[3], s[5], s[4] +) assign_def %= objectid + larrow + expr, lambda h, s: AssignNode(s[1], s[3], s[2]) From 1727333dd6d6123fd2129db074f51509bfbbe2c2 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 20:27:33 -0500 Subject: [PATCH 093/138] Fix collector visitor to pass tests Added tokens positions to type class --- src/compiler/cmp/semantic.py | 22 +++++----- src/compiler/visitors/type_collector.py | 56 ++++++++++++++++--------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/compiler/cmp/semantic.py b/src/compiler/cmp/semantic.py index cf9002e9f..864816ecd 100644 --- a/src/compiler/cmp/semantic.py +++ b/src/compiler/cmp/semantic.py @@ -1,5 +1,6 @@ import itertools as itt from collections import OrderedDict +from typing import Dict, List, Optional, Tuple class SemanticError(Exception): @@ -47,11 +48,12 @@ def __eq__(self, other): class Type: - def __init__(self, name: str): - self.name = name - self.attributes = [] - self.methods = [] - self.parent = None + def __init__(self, name: str, pos: Tuple[int, int] = (0, 0)): + self.name: str = name + self.attributes: List[Attribute] = [] + self.methods: List[Method] = [] + self.parent: Optional[Type] = None + self.pos: Tuple[int, int] = pos def set_parent(self, parent): if self.parent is not None: @@ -106,7 +108,7 @@ def define_method( ridx=None, ): if name in (method.name for method in self.methods): - raise SemanticError(f'Method "{name}" already defined in {self.name}') + raise SemanticError(f'Method "{name}" is multiply defined.') method = Method(name, param_names, param_types, return_type, param_idx, ridx) self.methods.append(method) @@ -266,12 +268,12 @@ def bypass(self): class Context: def __init__(self): - self.types = {} + self.types: Dict[str, Type] = {} - def create_type(self, name: str): + def create_type(self, name: str, pos: Tuple[int, int] = (0, 0)): if name in self.types: - raise SemanticError(f"Type with the same name ({name}) already in context.") - typex = self.types[name] = Type(name) + raise SemanticError("Classes may not be redefined") + typex = self.types[name] = Type(name, pos) return typex def get_type(self, name: str): diff --git a/src/compiler/visitors/type_collector.py b/src/compiler/visitors/type_collector.py index d6cf75d9a..682b7bdf4 100644 --- a/src/compiler/visitors/type_collector.py +++ b/src/compiler/visitors/type_collector.py @@ -1,3 +1,7 @@ +from typing import Dict, List, Optional, Tuple +from numpy import void +from zmq import TYPE +from compiler.cmp.utils import Token, pprint import compiler.visitors.visitor as visitor from ..cmp.semantic import ( SemanticError, @@ -17,17 +21,17 @@ class TypeCollector(object): - def __init__(self, errors=[]): - self.context = None - self.errors = errors - self.parent = {} + def __init__(self): + self.context: Optional[Context] = None + self.errors: List[Tuple[Exception, Tuple[int, int]]] = [] + self.parent: Dict[str, Optional[Token]] = {} @visitor.on("node") def visit(self, node): pass @visitor.when(ProgramNode) - def visit(self, node): + def visit(self, node: ProgramNode): self.context = Context() self.define_built_in_types() @@ -45,17 +49,17 @@ def visit(self, node): node.declarations = self.order_types(node) @visitor.when(ClassDeclarationNode) - def visit(self, node): - # flag will be True if the class is succesfully added to the context + def visit(self, node: ClassDeclarationNode) -> void: + # flag is set to True if the class is succesfully added to the context flag = False try: if node.id == "AUTO_TYPE": raise SemanticError("Name of class can't be autotype") - self.context.create_type(node.id) + self.context.create_type(node.id, node.tokenId.pos) flag = True self.parent[node.id] = node.parent except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex, node.token.pos)) # changing class id so it can be added to context while not flag: @@ -67,7 +71,7 @@ def visit(self, node): except SemanticError: pass - def define_built_in_types(self): + def define_built_in_types(self) -> void: objectx = ObjectType() iox = IOType() intx = IntType() @@ -100,7 +104,7 @@ def define_built_in_types(self): built_in_types.extend([objectx, iox, stringx, intx, boolx, self_type, autotype]) - def check_parents(self): + def check_parents(self) -> void: for item in self.parent.keys(): item_type = self.context.get_type(item) if self.parent[item] is None: @@ -108,27 +112,39 @@ def check_parents(self): item_type.set_parent(built_in_types[0]) else: try: - typex = self.context.get_type(self.parent[item]) + typex = self.context.get_type(self.parent[item].lex) if not typex.can_be_inherited(): self.errors.append( - f"Class {item} can not inherit from {typex.name}" + ( + SemanticError( + f"Class {item} can not inherit class {typex.name}" + ), + self.parent[item].pos, + ) ) typex = built_in_types[0] item_type.set_parent(typex) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append( + ( + TypeError( + f"Class {item_type.name} inherits from an undefined class {self.parent[item].lex}." + ), + self.parent[item].pos, + ) + ) item_type.set_parent(built_in_types[0]) - def check_cyclic_inheritance(self): + def check_cyclic_inheritance(self) -> void: flag = [] - def find(item): + def find(item: Type) -> int: for i, typex in enumerate(flag): if typex.name == item.name: return i return len(flag) - def check_path(idx, item): + def check_path(idx: int, item: Type) -> void: while True: flag.append(item) parent = item.parent @@ -138,7 +154,7 @@ def check_path(idx, item): if pos < len(flag): if pos >= idx: self.errors.append( - f"Class {item.name} can not inherit from {parent.name}" + (SemanticError("Cyclic heritage."), item.pos) ) item.parent = built_in_types[0] break @@ -149,8 +165,8 @@ def check_path(idx, item): if idx == len(flag): check_path(idx, item) - def order_types(self, node): - sorted_declarations = [] + def order_types(self, node: ProgramNode): + sorted_declarations: List[ClassDeclarationNode] = [] flag = [False] * len(node.declarations) change = True From 9162fe574f6ac4fde573868e4096a2b0758eeda8 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 24 Feb 2022 20:28:20 -0500 Subject: [PATCH 094/138] Fix builder visitor to pass tests --- src/compiler/visitors/type_builder.py | 102 +++++++++++++++++++------- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/src/compiler/visitors/type_builder.py b/src/compiler/visitors/type_builder.py index 539148270..16df0a7ad 100644 --- a/src/compiler/visitors/type_builder.py +++ b/src/compiler/visitors/type_builder.py @@ -1,3 +1,6 @@ +from typing import List, Optional, Tuple + +from numpy import void import compiler.visitors.visitor as visitor from ..cmp.ast import ( ProgramNode, @@ -6,22 +9,21 @@ FuncDeclarationNode, ) from ..cmp.semantic import ( + Context, SemanticError, ErrorType, InferencerManager, AutoType, SelfType, + Type, ) -SELF_IS_READONLY = 'Variable "self" is read-only.' - - class TypeBuilder: - def __init__(self, context, errors=[]): - self.context = context - self.current_type = None - self.errors = errors + def __init__(self, context): + self.context: Context = context + self.current_type: Optional[Type] = None + self.errors: List[Tuple[Exception, Tuple[int, int]]] = [] self.manager = InferencerManager() self.obj_type = self.context.get_type("Object") @@ -31,32 +33,47 @@ def visit(self, node): pass @visitor.when(ProgramNode) - def visit(self, node): + def visit(self, node: ProgramNode) -> void: for dec in node.declarations: self.visit(dec) self.check_main_class() @visitor.when(ClassDeclarationNode) - def visit(self, node): + def visit(self, node: ClassDeclarationNode): self.current_type = self.context.get_type(node.id) for feat in node.features: self.visit(feat) @visitor.when(FuncDeclarationNode) - def visit(self, node): + def visit(self, node: FuncDeclarationNode): ## Building param-names and param-types of the method param_names = [] param_types = [] node.index = [] - for param in node.params: - n, t = param + for namex, typex in node.params: + n, t = namex.lex, typex.lex node.index.append(None) # Checking param name can't be self if n == "self": - self.errors.append(SELF_IS_READONLY) - + self.errors.append( + ( + SemanticError( + "'self' cannot be the name of a formal parameter." + ), + namex.pos, + ) + ) + + # Generate valid parameter name + if n in param_names: + self.errors.append( + ( + SemanticError(f"Formal parameter {n} is multiply defined."), + namex.pos, + ) + ) while True: if n in param_names: n = f"1{n}" @@ -70,8 +87,13 @@ def visit(self, node): t = SelfType(self.current_type) elif isinstance(t, AutoType): node.index[-1] = self.manager.assign_id(self.obj_type) - except SemanticError as ex: - self.errors.append(ex.text) + except SemanticError: + self.errors.append( + ( + TypeError(f"Class {t} of formal parameter {n} is undefined."), + typex.pos, + ) + ) t = ErrorType() param_types.append(t) @@ -80,8 +102,15 @@ def visit(self, node): rtype = self.context.get_type(node.type) if isinstance(rtype, SelfType): rtype = SelfType(self.current_type) - except SemanticError as ex: - self.errors.append(ex.text) + except SemanticError: + self.errors.append( + ( + TypeError( + f"Undefined return type {node.type} in method {node.id}." + ), + node.typeToken.pos, + ) + ) rtype = ErrorType() node.idx = ( @@ -96,17 +125,24 @@ def visit(self, node): node.id, param_names, param_types, rtype, node.index, node.idx ) except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append((ex, node.token.pos)) @visitor.when(AttrDeclarationNode) - def visit(self, node): + def visit(self, node: AttrDeclarationNode): # Checking attribute type try: attr_type = self.context.get_type(node.type) if isinstance(attr_type, SelfType): attr_type = SelfType(self.current_type) - except SemanticError as ex: - self.errors.append(ex.text) + except SemanticError: + self.errors.append( + ( + TypeError( + f"Class {node.type} of attribute {node.id} is undefined." + ), + node.typeToken.pos, + ) + ) attr_type = ErrorType() node.idx = ( @@ -117,7 +153,12 @@ def visit(self, node): # Checking attribute can't be named self if node.id == "self": - self.errors.append(SELF_IS_READONLY) + self.errors.append( + ( + SemanticError("'self' cannot be the name of an attribute."), + node.idToken.pos, + ) + ) # Checking attribute name. No other attribute can have the same name flag = False @@ -125,7 +166,12 @@ def visit(self, node): self.current_type.define_attribute(node.id, attr_type, node.idx) flag = True except SemanticError as ex: - self.errors.append(ex.text) + self.errors.append( + ( + SemanticError(f"Attribute {node.id} is multiply defined in class."), + node.idToken.pos, + ) + ) while not flag: node.id = f"1{node.id}" @@ -139,6 +185,10 @@ def check_main_class(self): try: typex = self.context.get_type("Main") if not any(method.name == "main" for method in typex.methods): - self.errors.append("Class Main must contain a method main") + self.errors.append( + (SemanticError("Class Main must contain a method main"), (0, 0)) + ) except SemanticError: - self.errors.append("Program must contain a class Main") + self.errors.append( + (SemanticError("Program must contain a class Main"), (0, 0)) + ) From f756132c53726f1c91d1e87e33d0e8d266c070e0 Mon Sep 17 00:00:00 2001 From: kikexd Date: Fri, 25 Feb 2022 02:12:40 -0500 Subject: [PATCH 095/138] add built in functions to cil ast and many other things --- src/compiler/cmp/cil_ast.py | 62 ++- src/compiler/cmp/semantic.py | 2 +- src/compiler/visitors/cool2cil.py | 764 +++++++++++++++++++++++------- 3 files changed, 655 insertions(+), 173 deletions(-) diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py index 8974c2600..0473465d2 100644 --- a/src/compiler/cmp/cil_ast.py +++ b/src/compiler/cmp/cil_ast.py @@ -16,6 +16,16 @@ def __init__(self, name): self.methods = [] +class InstructionNode(Node): + pass + + +class TypeNameNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + + class DataNode(Node): def __init__(self, vname, value): self.name = vname @@ -41,10 +51,6 @@ def __init__(self, name): self.name = name -class InstructionNode(Node): - pass - - class AssignNode(InstructionNode): def __init__(self, dest, source): self.dest = dest @@ -105,17 +111,19 @@ def __init__(self, dest, obj): class GetAttribNode(InstructionNode): - def __init__(self, dest, obj, attr): + def __init__(self, dest, obj, attr, computed_type): self.dest = dest self.obj = obj self.attr = attr + self.computed_type = computed_type class SetAttribNode(InstructionNode): - def __init__(self, obj, attr, value): + def __init__(self, obj, attr, value, computed_type): self.obj = obj self.attr = attr self.value = value + self.computed_type = computed_type class GetIndexNode(InstructionNode): @@ -170,6 +178,12 @@ def __init__(self, function, dest): self.dest = dest +class CopyNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + + class DynamicCallNode(InstructionNode): def __init__(self, xtype, method, dest): self.type = xtype @@ -194,11 +208,17 @@ def __init__(self, dest, msg): class LengthNode(InstructionNode): - pass + def __init__(self, dest, source): + self.dest = dest + self.source = source class ConcatNode(InstructionNode): - pass + def __init__(self, dest, prefix, suffix, length): + self.dest = dest + self.prefix = prefix + self.suffix = suffix + self.length = length class PrefixNode(InstructionNode): @@ -206,7 +226,11 @@ class PrefixNode(InstructionNode): 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): @@ -220,11 +244,31 @@ def __init__(self, dest): self.dest = dest +class ReadStrNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + + +class ReadIntNode(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 ErrorNode(InstructionNode): def __init__(self, data_node): self.data_node = data_node diff --git a/src/compiler/cmp/semantic.py b/src/compiler/cmp/semantic.py index cf9002e9f..49cae4ed4 100644 --- a/src/compiler/cmp/semantic.py +++ b/src/compiler/cmp/semantic.py @@ -292,7 +292,7 @@ def __repr__(self): class VariableInfo: - def __init__(self, name, vtype, idx): + def __init__(self, name, vtype, idx=None): self.name = name self.type = vtype self.idx = idx diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 429ecf83f..0703fb306 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -49,6 +49,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): @@ -58,15 +59,23 @@ def params(self): def localvars(self): return self.current_function.localvars + @property + def ids(self): + return self.current_function.ids + @property def instructions(self): return self.current_function.instructions - def register_local(self, vinfo): + def register_local(self, vinfo, id=False): vinfo.name = ( f"local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}" ) local_node = cil.LocalNode(vinfo.name) + + if id: + self.ids[vinfo.name] = vinfo.name + self.localvars.append(local_node) return vinfo.name @@ -86,6 +95,11 @@ def register_function(self, function_name): self.dotcode.append(function_node) return function_node + def register_param(self, vinfo): + param_node = cil.ParamNode(vinfo.name) + self.params.append(param_node) + return vinfo.name + def register_type(self, name): type_node = cil.TypeNode(name) self.dottypes.append(type_node) @@ -118,6 +132,285 @@ def init_name(self, type_name, attr=False): return f"init_attr_at_{type_name}" return f"init_at_{type_name}" + def buildHierarchy(self, t: str): + if t == "Object": + return None + return { + x.name + for x in self.context.types.values() + if x.name != "AUTO_TYPE" and x.conforms_to(self.context.get_type(t)) + } + + def register_built_in(self): + # Object + type_node = self.register_type("Object") + + # 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)) + + # abort Object + 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 == "Abort called from class " + ][0] + self.register_instruction(cil.LoadNode(vname, data_node)) + self.register_instruction(cil.PrintStrNode(vname)) + self.register_instruction(cil.TypeNode(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()) + + # type_name Object + 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.TypeNameNode(result, self.vself.name)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction( + cil.StaticCallNode(self.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # copy Object + 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.CopyNode(result, self.vself.name)) + self.register_instruction(cil.ReturnNode(result)) + + # Object + 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") + + # 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)) + + # out_string 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.GetAttribNode(vname, "x", "value", "String")) + self.register_instruction(cil.PrintStrNode(vname)) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + # out_int IO + self.current_function = self.register_function( + self.to_function_name("out_int", "IO") + ) + self.register_param(self.vself) + self.register_param(VariableInfo("x", None)) + vname = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(vname, "x", "value", "Int")) + self.register_instruction(cil.PrintIntNode(vname)) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + # in_string IO + 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.ReadStrNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction( + cil.StaticCallNode(self.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # in_int IO + 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.ReadIntNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + self.register_instruction(cil.ReturnNode(instance)) + + # IO + 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 ["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"] + + # 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)) + 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.init_name("Int"), attr)) + self.register_instruction(cil.SetAttribNode(instance, "length", attr, "String")) + self.register_instruction(cil.ReturnNode(instance)) + + # length 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.GetAttribNode(result, self.vself.name, "length", "String") + ) + self.register_instruction(cil.ReturnNode(result)) + + # concat String + 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() + 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, length_1)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction( + cil.StaticCallNode(self.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # subst String + 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() + 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() + 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 + 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") + 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.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # String + 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 ["length", "concat", "substr"] + ] + type_node.methods += [("init", self.init_name("String"))] + + # Int + type_node = self.register_type("Int") + type_node.attributes = ["value"] + + # 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)) + self.register_instruction(cil.SetAttribNode(instance, "value", "val", "Int")) + self.register_instruction(cil.ReturnNode(instance)) + + # Int + type_node.methods = [ + (method, self.to_function_name(method, "Object")) for method in obj_methods + ] + type_node.methods += [("init", self.init_name("Int"))] + + # Bool + type_node = self.register_type("Bool") + type_node.attributes = ["value"] + + # 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)) + self.register_instruction(cil.SetAttribNode(instance, "value", "val", "Bool")) + self.register_instruction(cil.ReturnNode(instance)) + + # Bool + type_node.methods = [ + (method, self.to_function_name(method, "Object")) for method in obj_methods + ] + type_node.methods += [("init", self.init_name("Bool"))] + class COOLToCILVisitor(BaseCOOLToCILVisitor): @visitor.on("node") @@ -131,13 +424,16 @@ def visit(self, node, scope): ###################################################### self.current_function = self.register_function("entry") - instance = self.define_internal_local() + instance = self.register_local(VariableInfo("instance", None)) + self.register_instruction(cil.StaticCallNode(self.init_name("Main"), instance)) + self.register_instruction(cil.ArgNode(instance)) result = self.define_internal_local() main_method_name = self.to_function_name("main", "Main") - self.register_instruction(cil.AllocateNode("Main", instance)) - self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(main_method_name, result)) self.register_instruction(cil.ReturnNode(0)) + + self.register_data("Abort called from class ") + self.register_built_in() self.current_function = None for declaration, child_scope in zip(node.declarations, scope.children): @@ -184,6 +480,39 @@ def visit(self, node, scope): for feature, child_scope in zip(func_declarations, scope.children): self.visit(feature, child_scope) + # init + 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)) + + func = self.current_function + vtemp = self.define_internal_local() + + # init_attr + 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.init_name(node.parent, attr=True), vtemp) + ) + attr_declarations = ( + f for f in node.features if isinstance(f, 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.init_name(node.id, attr=True), vtemp) + ) + + self.register_instruction(cil.ReturnNode(instance)) + self.current_function = None self.current_type = None @visitor.when(AttrDeclarationNode) @@ -193,14 +522,18 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - expr = self.visit(node.expr, scope) if node.expr: - self.register_instruction(cil.SetAttribNode(node.type, node.id, expr)) + value = self.visit(node.expr, scope) + self.register_instruction( + cil.SetAttribNode(self.vself.name, node.id, value, self.current_type) + ) elif node.type in self.value_types: value = self.define_internal_local() self.register_instruction(cil.AllocateNode(node.type, value)) - self.register_instruction(cil.SetAttribNode(self.type, node.id, value)) + self.register_instruction( + cil.SetAttribNode(self.vself.name, node.id, value, self.current_type) + ) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -217,15 +550,19 @@ def visit(self, node, scope): self.current_function = self.register_function( self.to_function_name(self.current_method.name, self.current_type.name) ) - self.current_vars = {} - self.params.append(cil.ParamNode("self")) + + self.params.append(cil.ParamNode(self.vself.name)) self.params.extend([cil.ParamNode(p) for p in self.current_method.param_names]) value = self.visit(node.body, scope) # Your code here!!! (Handle RETURN) - self.register_instruction(cil.ReturnNode(value)) - self.current_function = None + if value is None: + self.register_instruction(cil.ReturnNode("")) + elif self.current_function.name == "entry": + self.register_instruction(cil.ReturnNode(0)) + else: + self.register_instruction(cil.ReturnNode(value)) self.current_method = None @@ -238,7 +575,28 @@ def visit(self, node, scope): # Your code here!!! value = self.visit(node.expr, scope) - self.register_instruction(cil.AssignNode(node.id, value)) + + try: + self.current_type.get_attribute(node.id) + self.register_instruction( + cil.SetAttribNode( + self.vself.name, node.id, value, self.current_type.name + ) + ) + except AttributeError: + 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, value)) @visitor.when(CallNode) def visit(self, node, scope): @@ -249,32 +607,54 @@ def visit(self, node, scope): # node.type -> str ############################### - # Your code here!!! - values = [self.visit(node.obj, scope)] - values.extend([self.visit(arg, scope) for arg in node.args]) - local_vars = [self.define_internal_local() for v in values] - for (var, val) in zip(local_vars, values): - self.register_instruction(cil.AssignNode(var, val)) + args = [] + for arg in node.args: + vname = self.register_local(VariableInfo(f"{node.id}_arg", None), id=True) + ret = self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, ret)) + args.append(cil.ArgNode(vname)) + result = self.register_local( + VariableInfo(f"return_value_of_{node.id}", None), id=True + ) - for var in local_vars: - self.register_instruction(cil.ArgNode(var)) + vobj = self.define_internal_local() + ret = self.visit(node.obj, scope) + self.register_instruction(cil.AssignNode(vobj, ret)) - return_var = self.register_local(VariableInfo("call_node_value", None)) - if node.type: # Dynamic Call + # 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, + f"({node.token.row},{node.token.column}) - RuntimeError: Dispatch on void\n", + ) + + # self + self.register_instruction(cil.ArgNode(vobj)) + for arg in args: + self.register_instruction(arg) + + if node.type: + # Call of type @.id(,...,) self.register_instruction( - cil.DynamicCallNode(node.type, node.id, return_var) + 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), id=True ) - elif values[0] == "self": # Static call - function = self.to_function_name(node.id, self.current_type.name) - self.register_instruction(cil.StaticCallNode(function, return_var)) - else: # Dynamic Call with type of obj - type_var = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(local_vars[0], type_var)) + self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) + computed_type = node.obj.computed_type + if computed_type.name == "SELF_TYPE": + computed_type = computed_type.fixed self.register_instruction( - cil.DynamicCallNode(type_var, node.id, return_var) + cil.DynamicCallNode(type_of_node, node.id, result, computed_type.name) ) - return return_var + return result @visitor.when(ConditionalNode) def visit(self, node, scope): @@ -289,24 +669,30 @@ def visit(self, node, scope): continue_label_node = self.register_label("continue_label") cond_ret = self.visit(node.condition, scope) - self.register_instruction(cil.GotoIfNode(cond_ret, then_label_node.label)) + + cond_ret_value = self.define_internal_local() + + self.register_instruction( + cil.GetAttribNode(cond_ret_value, cond_ret, "value", "Bool") + ) + self.register_instruction(cil.GotoIfNode(cond_ret_value, then_label_node.label)) self.register_instruction(cil.GotoNode(else_label_node.label)) - ret = self.register_local(VariableInfo("if_then_else_value", None)) + value = self.register_local(VariableInfo("if_then_else_value", None)) # Label then_label self.register_instruction(then_label_node) ret_then = self.visit(node.then_body, scope) - self.register_instruction(cil.AssignNode(ret, ret_then)) + self.register_instruction(cil.AssignNode(cond_ret_value, ret_then)) self.register_instruction(cil.GotoNode(continue_label_node.label)) # Label else_label self.register_instruction(else_label_node) ret_else = self.visit(node.else_body, scope) - self.register_instruction(cil.AssignNode(ret, ret_else)) + self.register_instruction(cil.AssignNode(cond_ret_value, ret_else)) self.register_instruction(continue_label_node) - return ret + return cond_ret_value @visitor.when(LoopNode) def visit(self, node, scope): @@ -322,8 +708,11 @@ def visit(self, node, scope): condition = self.define_internal_local() self.register_instruction(while_label_node) - condition_ret = self.visit(node.condition, scope) - self.register_instruction(cil.AssignNode(condition, condition_ret)) + condition_value = self.visit(node.condition, scope) + + self.register_instruction( + cil.GetAttribNode(condition, condition_value, "value", "Bool") + ) self.register_instruction(cil.GotoIfNode(condition, loop_label_node.label)) self.register_instruction(cil.GotoNode(pool_label_node.label)) @@ -357,112 +746,140 @@ def visit(self, node, scope): # node.id_list -> [(id, type, expr), ...] # node.in_body -> ExpressionNode ############################################ - ret = self.register_local(VariableInfo("let_in_value", None)) + value = self.register_local(VariableInfo("let_in_value", None)) - values = [self.visit(l.expr, scope) for l in node.id_list] - local_vars = [self.define_internal_local() for _ in values] - for (var, val) in zip(local_vars, values): - self.register_instruction(cil.AssignNode(var, val)) + for let_var in node.id_list: + self.visit(let_var, scope) ret_val = self.visit(node.in_body, scope) + self.register_instruction(cil.AssignNode(value, ret_val)) + + return value + + @visitor.when(LetAttributeNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + 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: + ret_value = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(vname, ret_value)) + elif node.type in self.value_types: + self.register_instruction(cil.AllocateNode(node.type, vname)) + + @visitor.when(CaseNode) + def visit(self, node, scope): + ############################################## + # node.expr -> ExpressionNode + # node.branches -> [(id, type, expr), ... } + ############################################## + ret = self.register_local(VariableInfo("case_expr_value", None)) + ret_type = self.register_local(VariableInfo("typeName_value", None)) + vcond = self.register_local(VariableInfo("equal_value", None)) + value = self.register_local(VariableInfo("case_value", None)) + + ret_val = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(ret, ret_val)) + self.register_instruction(cil.TypeOfNode(ret_type, ret_val)) - return ret + # 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, ret, void)) + + self.register_runtime_error( + equal_result, + f"({node.position.row},{node.position.column}) - RuntimeError: Case on void\n", + ) + + # 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]) + + labels = [] + old = {} + for idx, (_, b) in enumerate(order): + 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 + h.add(b.type) + for s in old: + h -= s + 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, ret_type, 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"({node.position.row + 1 + len(node.branches)},{node.position.column - 5}) - RuntimeError: Execution of a case statement without a matching branch\n" + ) + self.register_instruction(cil.ErrorNode(data_node)) + + end_label = self.register_label("end_label") + for idx, l in enumerate(labels): + self.register_instruction(l) + vid = self.register_local(VariableInfo(order[idx][1].id, None), id=True) + self.register_instruction(cil.AssignNode(vid, ret)) + ret_2 = self.visit(order[idx][1], scope) + self.register_instruction(cil.AssignNode(value, ret_2)) + self.register_instruction(cil.GotoNode(end_label.label)) - # create case visitor - # @visitor.when(CaseNode) - # def visit(self, node, scope): - # ############################################## - # # node.expr -> ExpressionNode - # # node.branches -> [(id, type, expr), ... } - # ############################################## - # ret = self.register_local(VariableInfo("case_expr_value", None)) - # ret_type = self.register_local(VariableInfo("typeName_value", None)) - # vcond = self.register_local(VariableInfo("equal_value", None)) - # ret = self.register_local(VariableInfo("case_value", None)) - - # ret_val = self.visit(node.expr, scope) - - # self.register_instruction(cil.AssignNode(ret, ret_val)) - # self.register_instruction(cil.TypeOfNode(ret_type, ret_val)) - - # # 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, ret, void)) - - # self.register_runtime_error( - # equal_result, - # f"({node.position.row},{node.position.column}) - RuntimeError: Case on void\n", - # ) - - # end_label = self.register_label("end_label") - # labels = [] - # old = {} - - # # 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: - # self.register_instruction(cil.GotoNode(labels[-1].label)) - # break - # h.add(b.type) - # for s in old: - # h -= s - # 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.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(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)) - # self.register_instruction(cil.GotoNode(end_label.label)) - - # scope.ret_expr = vret - # self.register_instruction(end_label) + self.register_instruction(end_label) + return value + + @visitor.when(CaseExpressionNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + ret = self.register_local(VariableInfo("block_node_value", None)) + + ret_value = self.visit(node.expr, scope) + + self.register_instruction(cil.AssignNode(ret, ret_value)) + + return ret @visitor.when(NotNode) def visit(self, node, scope): ############################### # node.expr -> ExpressionNode ############################### - value = self.define_internal_local() - value_minus_1 = self.define_internal_local() ret = self.define_internal_local() + ret_minus_1 = self.define_internal_local() + value = self.define_internal_local() ret_value = self.visit(node.expr, scope) - self.register_instruction(cil.AssignNode(value, ret_value)) - self.register_instruction(cil.MinusNode(value_minus_1, 1, value)) + self.register_instruction(cil.GetAttribNode(ret, ret_value, "value", "Bool")) + self.register_instruction(cil.MinusNode(ret_minus_1, 1, ret)) - self.register_instruction(cil.ArgNode(value_minus_1)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) - return ret + self.register_instruction(cil.ArgNode(ret_minus_1)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) + return value @visitor.when(LeqNode) def visit(self, node, scope): @@ -473,19 +890,21 @@ def visit(self, node, scope): ret_value = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() - ret = self.define_internal_local() + value = self.define_internal_local() left = self.visit(node.left, scope) right = self.visit(node.right, scope) - self.register_instruction(cil.AssignNode(left_value, left)) - self.register_instruction(cil.AssignNode(right_value, right)) - self.register_instruction(cil.LessEqualNode(ret_value, left_value, right_value)) + self.register_instruction(cil.GetAttribNode(left_value, left, "value", "Bool")) + self.register_instruction( + cil.GetAttribNode(right_value, right, "value", "Bool") + ) + self.register_instruction(cil.LeqNode(ret_value, left_value, right_value)) self.register_instruction(cil.ArgNode(ret_value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) - return ret + return value @visitor.when(LessNode) def visit(self, node, scope): @@ -496,17 +915,19 @@ def visit(self, node, scope): value = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() - ret = self.define_internal_local() + value = self.define_internal_local() left = self.visit(node.left, scope) right = self.visit(node.right, scope) - self.register_instruction(cil.AssignNode(left_value, left)) - self.register_instruction(cil.AssignNode(right_value, right)) + 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(value, left_value, right_value)) self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) - return ret + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) + return value @visitor.when(EqualNode) def visit(self, node, scope): @@ -545,14 +966,18 @@ def visit(self, node, scope): self.register_instruction(cil.GotoNode(reference_node.label)) self.register_instruction(int_node) - self.register_instruction(cil.AssignNode(left_value, left)) - self.register_instruction(cil.AssignNode(right_value, 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.EqualNode(return_vale, left_value, right_value)) self.register_instruction(cil.GotoNode(continue_node.label)) self.register_instruction(string_node) - self.register_instruction(cil.AssignNode(left_value, left)) - self.register_instruction(cil.AssignNode(right_value, right)) + self.register_instruction( + cil.GetAttribNode(left_value, left, "value", "String") + ) + self.register_instruction( + cil.GetAttribNode(right_value, right, "value", "String") + ) self.register_instruction( cil.EqualStrNode(return_vale, left_value, right_value) ) @@ -576,9 +1001,9 @@ def visit(self, node, scope): value_left = self.define_internal_local() value_right = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.AssignNode(value_left, left)) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.AssignNode(value_right, right)) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) self.register_instruction(cil.PlusNode(value, value_left, value_right)) @@ -598,11 +1023,11 @@ def visit(self, node, scope): value_left = self.define_internal_local() value_right = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.AssignNode(value_left, left)) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.AssignNode(value_right, right)) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) - self.register_instruction(cil.MinusNode(value, value_left, value_right)) + self.register_instruction(cil.LessNode(value, value_left, value_right)) ret = self.define_internal_local() @@ -620,9 +1045,9 @@ def visit(self, node, scope): value_left = self.define_internal_local() value_right = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.AssignNode(value_left, left)) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.AssignNode(value_right, right)) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) self.register_instruction(cil.StarNode(value, value_left, value_right)) @@ -642,9 +1067,9 @@ def visit(self, node, scope): value_left = self.define_internal_local() value_right = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.AssignNode(value_left, left)) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.AssignNode(value_right, right)) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) # Check division by 0 equal_result = self.define_internal_local() @@ -687,7 +1112,7 @@ def visit(self, node, scope): complement_value = self.define_internal_local() ret = self.define_internal_local() left = self.visit(node.expr, scope) - self.register_instruction(cil.AssignNode(value, left)) + self.register_instruction(cil.GetAttribNode(value, left, "value", "Int")) self.register_instruction(cil.ComplementNode(complement_value, value)) self.register_instruction(cil.ArgNode(complement_value)) @@ -716,27 +1141,40 @@ def visit(self, node, scope): self.register_instruction(cil.StaticCallNode(self.init_name(node.type), ret)) return ret - @visitor.when(ConstantNumNode) + @visitor.when(VariableNode) def visit(self, node, scope): ############################### # node.lex -> str ############################### - ret = self.define_internal_local() - self.register_instruction(cil.ArgNode(int(node.lex))) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) - return ret + self.current_type.get_attribute(node.lex) + attr = self.register_local(VariableInfo(node.lex, None)) + self.register_instruction(cil.GetAttribNode(attr, node, node.lex)) + return attr - @visitor.when(VariableNode) + @visitor.when(ConstantNumNode) def visit(self, node, scope): ############################### # node.lex -> str ############################### - self.current_type.get_attribute(node.lex) - attr = self.register_local(VariableInfo(node.lex, None)) - self.register_instruction(cil.GetAttribNode(attr, node, node.lex)) - return attr + try: + self.current_type.get_attribute(node.lex) + 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 + ) + ) + return attr + except AttributeError: + 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 == n: + return n + else: + return self.ids[node.lex] @visitor.when(ConstantStringNode) def visit(self, node, scope): From 14805437f2d34b52fa5ecb3ebfe06fc4091c821c Mon Sep 17 00:00:00 2001 From: dgd Date: Fri, 25 Feb 2022 09:14:16 -0500 Subject: [PATCH 096/138] buildging cil2mips visitor --- src/compiler/cmp/mips_ast.py | 223 +++++++++++++++ src/compiler/visitors/cil2mips.py | 441 ++++++++++++++++++++++++++++++ 2 files changed, 664 insertions(+) create mode 100644 src/compiler/cmp/mips_ast.py create mode 100644 src/compiler/visitors/cil2mips.py diff --git a/src/compiler/cmp/mips_ast.py b/src/compiler/cmp/mips_ast.py new file mode 100644 index 000000000..5d6ed833e --- /dev/null +++ b/src/compiler/cmp/mips_ast.py @@ -0,0 +1,223 @@ +# ***********************Registers*********************** +class Register: + def __init__(self, name): + self.name = name + + +FP = Register("fp") +SP = Register("sp") +RA = Register("ra") +V0 = Register("v0") +RA = Register("ra") +A0 = Register("a0") +A1 = Register("a1") +A2 = Register("a2") +A3 = Register("a3") +ZERO = Register("zero") +T0 = Register("t0") +T1 = Register("t1") +T2 = Register("t2") + +# ***********************Registers*********************** + +# ***********************Utils*********************** + +MAIN_FUNCTION_NAME = "function_main_at_Main" + + +def push_to_stack(register: Register): + update_sp = AddInmediateNode(SP, SP, -4) + offset = RegisterRelativeLocation(SP, 0) + store_word = StoreWordNode(register, offset) + return [update_sp, store_word] + + +def pop_from_stack(register: Register): + load_word = LoadWordNode(register, RegisterRelativeLocation(SP, 0)) + update_sp = AddInmediateNode(SP, SP, 4) + return [load_word, update_sp] + + +def exit_program(): + instructions = [] + instructions.append(LoadInmediateNode(V0, 10)) + instructions.append(SyscallNode()) + return instructions + + +# ***********************Utils*********************** + + +# ***********************AST*********************** + + +class Node: + pass + + +class ProgramNode(Node): + def __init__(self, data, text): + self.data = data + self.text = text + + +class DataNode(Node): + def __init__(self, label, data): + self.label = label + self.data = data + + +class FunctionNode(Node): + def __init__(self, label, params, localvars): + self.label = label + self.params = params + self.localvars = localvars + self.instrucctions = [] + + +class TypeNode(Node): + def __init__(self, label): + self.label = label + self.methods = [] + + +class InstructionNode(Node): + pass + + +class LabelNode(InstructionNode): + def __init__(self, label): + self.label = label + + +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 JumpRegisterAndLinkNode(InstructionNode): + def __init__(self, reg): + self.reg = reg + + +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.constant_number = value + + +class AddInmediateUnsignedNode(InstructionNode): + def __init__(self, dest, src, value): + self.dest = dest + 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 + 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 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 + + +class SubNode(InstructionNode): + 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 + + +class DivideNode(InstructionNode): + 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 + + +class RegisterRelativeLocation: + def __init__(self, register, offset): + self.register = register + self.offset = offset diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py new file mode 100644 index 000000000..b17e2217b --- /dev/null +++ b/src/compiler/visitors/cil2mips.py @@ -0,0 +1,441 @@ +import compiler.visitors.visitor as visitor +from ..cmp.cil_ast import * +from ..cmp import mips_ast as mips + + +def flatten(iterable): + for item in iterable: + try: + yield from flatten(item) + except TypeError: + yield item + + +class BaseCILToMIPSVisitor: + def __init__(self): + self.data = {} + self.text = {} + self.types = {} + self.current_function: mips.FunctionNode = None + self.pushed_args = 0 + self.label_count = 0 + + def register_type(self, type_name): + type_node = mips.TypeNode(self.to_type_label_address(type_name)) + self.types[type_name] = type_node + return type_node + + def register_data(self, data_name, data): + data_node = mips.DataNode(self.to_data_label_address(data_name), data) + self.data[data_name] = data_node + return data_node + + def register_function(self, function_name, params, locals_var): + function_node = mips.FunctionNode( + self.to_function_label_address(function_name), params, locals_var + ) + self.text[function_name] = function_node + return function_node + + def to_label_count_address(self, label_name): + self.label_count +=1 + return f'{label_name}_{self.label_count}' + + def to_type_label_address(self, type_name): + return f"type_{type_name}" + + def to_data_label_address(self, data_name): + return f"data_{data_name}" + + def to_function_label_address(self, function_name): + return f"{function_name}" + + def make_callee_init_instructions(self, function_node: mips.FunctionNode): + push_fp = mips.push_to_stack(mips.FP) + set_fp = mips.AddInmediateNode(mips.FP, mips.SP, 4) + local_vars_frame_size = len(function_node.localvars) * 4 + set_sp = mips.AddInmediateNode(mips.SP, mips.SP, -local_vars_frame_size) + return list(flatten([push_fp, set_fp, set_sp])) + + def make_callee_final_instructions(self, function_node: mips.FunctionNode): + local_vars_frame_size = len(function_node.localvars) * 4 + set_sp = mips.AddInmediateNode(mips.SP, mips.SP, local_vars_frame_size) + pop_FP = mips.pop_from_stack(mips.FP) + final = None + if function_node.name == mips.MAIN_FUNCTION_NAME: + final = mips.exit_program() + else: + final = mips.JumpRegister(mips.RA) + + return list(flatten([set_sp, pop_FP, final])) + + def get_param_var_index(self, name): + index = self.current_function.params.index(name) + offset = ((len(self._params) - 1) - index) * 4 + return mips.RegisterRelativeLocation(mips.FP, offset) + + def get_local_var_index(self, name): + index = self.current_function.localvars.index(name) + offset = (index + 2) * - 4 + return mips.RegisterRelativeLocation(mips.FP offset) + + def get_var_location(self, name): + try: + return self.get_param_var_index()(name) + except ValueError: + return self.get_local_var_index()(name) + + +class BaseCILToMIPSVisitor(BaseCILToMIPSVisitor): + @visitor.on(Node) + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node: ProgramNode) -> mips.ProgramNode: + for dd in node.dotdata: + self.visit(dd) + + for dt in node.dottypes: + self.visit(dt) + + for dc in node.dotcode: + self.visit(dc) + + return mips.ProgramNode(self.data, self.text) + + @visitor.when(TypeNode) + def visit(self, node: TypeNode): + type_node = self.register_type(node.name) + type_node.methods = [method[1] for method in node.methods] + + @visitor.when(DataNode) + def visit(self, node: DataNode): + self.register_data(node.name, node.value) + + @visitor.when(FunctionNode) + def visit(self, node: FunctionNode): + params = [p.name for p in node.params] + local_vars = [lv.name for lv in node.localvars] + function_node = self.register_function(node.name, params, local_vars) + + init_callee = self.make_callee_init_instructions(function_node) + self.current_function = function_node + body = [self.visit(instruction) for instruction in node.instructions] + final_callee = self.make_callee_final_instructions(function_node) + + total_instructions = list(flatten(init_callee + body + final_callee)) + function_node.instrucctions = total_instructions + self.current_function = None + + # @visitor.when(ParamNode) + # def visit(self, node): + # pass + + # @visitor.when(LocalNode) + # def visit(self, node): + # pass + + @visitor.when(AssignNode) + def visit(self, node): + instructions = [] + + if type(node.source) == VoidNode: + instructions.append(mips.StoreWordNode(mips.ZERO, self.get_var_location(node.dest))) + return instructions + + if node.source.isnumeric(): + instructions.append(mips.LoadInmediateNode(mips.A0, int(node.source))) + else: + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.source))) + + instructions.append(mips.StoreWordNode(mips.A0, self.get_var_location(node.dest))) + + return instructions + + # @visitor.when(ArithmeticNode) + # def visit(self, node): + # pass + + @visitor.when(PlusNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + + instructions.append(mips.AddNode(mips.T2, mips.T0, mips.T1)) + instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(MinusNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + + instructions.append(mips.SubNode(mips.T2, mips.T0, mips.T1)) + instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(StarNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + + instructions.append(mips.MultiplyNode(mips.T2, mips.T0, mips.T1)) + instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(DivNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + + instructions.append(mips.DivideNode(mips.T0,mips.T1)) + instructions.append(mips.MoveFromLowNode(mips.MoveFromLowNode(mips.T2))) + instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(LeqNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + else: + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + else: + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) + + instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("less_equal"))) + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(LessNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + else: + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + else: + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) + + instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("less"))) + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(EqualNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + elif type(node.left) == VoidNode: + instructions.append(mips.LoadInmediateNode(mips.A0, 0)) + else: + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + elif type(node.right) == VoidNode: + instructions.append(mips.LoadInmediateNode(mips.A1, 0)) + else: + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) + + instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("equals"))) + + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(EqualStrNode) + def visit(self, node): + instructions = [] + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) + instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("equal_str"))) + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + return instructions + + # @visitor.when(VoidNode) + # def visit(self, node): + # pass + + @visitor.when(NotNode) + def visit(self, node): + pass + + @visitor.when(ComplementNode) + def visit(self, node): + instructions = [] + + if type(node.obj) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.obj)) + else: + instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.obj))) + + instructions.append(mips.ComplementNode(mips.T1, mips.T0)) + instructions.append(mips.AddInmediateNode(mips.T1, mips.T1, 1)) + instructions.append(mips.StoreWordNode(mips.T1, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(GetAttribNode) + def visit(self, node): + pass + + @visitor.when(SetAttribNode) + def visit(self, node): + pass + + @visitor.when(GetIndexNode) + def visit(self, node): + pass + + @visitor.when(SetIndexNode) + def visit(self, node): + pass + + @visitor.when(AllocateNode) + def visit(self, node): + pass + + @visitor.when(ArrayNode) + def visit(self, node): + pass + + @visitor.when(TypeOfNode) + def visit(self, node): + pass + + @visitor.when(NameNode) + def visit(self, node): + pass + + @visitor.when(LabelNode) + def visit(self, node:LabelNode): + pass + + @visitor.when(GotoNode) + def visit(self, node): + pass + + @visitor.when(GotoIfNode) + def visit(self, node): + pass + + @visitor.when(StaticCallNode) + def visit(self, node:StaticCallNode): + instructions = [] + instructions.append(mips.JumpAndLinkNode(self.to_function_label_address(node.function))) + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + if self.pushed_args > 0: + instructions.append(mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4)) + self.pushed_args = 0 + return instructions + + @visitor.when(DynamicCallNode) + def visit(self, node): + pass + + @visitor.when(ArgNode) + def visit(self, node): + self.pushed_args +=1 + instructions = [] + if type(node.name) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.name)) + instructions.extend(mips.push_to_stack(mips.A0)) + else: + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.name))) + instructions.extend(mips.push_to_stack(mips.A0)) + + return instructions + + @visitor.when(ReturnNode) + def visit(self, node): + pass + + @visitor.when(LoadNode) + def visit(self, node): + pass + + @visitor.when(LengthNode) + def visit(self, node): + pass + + @visitor.when(ConcatNode) + def visit(self, node): + pass + + @visitor.when(PrefixNode) + def visit(self, node): + pass + + @visitor.when(SubstringNode) + def visit(self, node): + pass + + @visitor.when(ToStrNode) + def visit(self, node): + pass + + @visitor.when(ReadNode) + def visit(self, node): + pass + + @visitor.when(PrintNode) + def visit(self, node): + pass + + @visitor.when(ErrorNode) + def visit(self, node): + pass From 8bfcd950da03701bb95737455a11a11f60350f8b Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Fri, 25 Feb 2022 12:07:40 -0500 Subject: [PATCH 097/138] Fix bugs and type checker --- src/compiler/cmp/ast.py | 5 +- src/compiler/cmp/grammar.py | 5 +- src/compiler/cmp/semantic.py | 6 +- src/compiler/visitors/formatter.py | 8 +- src/compiler/visitors/type_builder.py | 33 +-- src/compiler/visitors/type_checker.py | 314 +++++++++++++++++------ src/compiler/visitors/type_collector.py | 11 +- src/compiler/visitors/type_inferencer.py | 8 +- src/main.py | 81 +++--- 9 files changed, 310 insertions(+), 161 deletions(-) diff --git a/src/compiler/cmp/ast.py b/src/compiler/cmp/ast.py index 79458956f..e0c839f66 100644 --- a/src/compiler/cmp/ast.py +++ b/src/compiler/cmp/ast.py @@ -80,13 +80,12 @@ def __init__(self, idx: Token, expr: ExpressionNode, token: Token): class CallNode(ExpressionNode): def __init__( self, - obj: Token, + obj: ExpressionNode, idx: Token, args: List[ExpressionNode], cast_type: Token = emptyToken, ): - self.obj = obj.lex - self.objToken = obj + self.obj = obj self.id = idx.lex self.args = args self.type = cast_type.lex diff --git a/src/compiler/cmp/grammar.py b/src/compiler/cmp/grammar.py index da8b4a1e6..ce13a58ba 100644 --- a/src/compiler/cmp/grammar.py +++ b/src/compiler/cmp/grammar.py @@ -102,8 +102,8 @@ factor %= atom, lambda h, s: s[1] factor %= opar + expr + cpar, lambda h, s: s[2] -factor %= isvoid + factor, lambda h, s: VoidNode(s[2], s[1]) -factor %= neg + factor, lambda h, s: NegNode(s[2], s[1]) +# factor %= isvoid + factor, lambda h, s: VoidNode(s[2], s[1]) +# factor %= neg + factor, lambda h, s: NegNode(s[2], s[1]) factor %= func_call, lambda h, s: s[1] factor %= case_def, lambda h, s: s[1] factor %= block_def, lambda h, s: s[1] @@ -169,6 +169,7 @@ s_factor %= assign_def, lambda h, s: s[1] s_factor %= isvoid + s_factor, lambda h, s: VoidNode(s[2], s[1]) s_factor %= neg + s_factor, lambda h, s: NegNode(s[2], s[1]) +s_factor %= factor, lambda h, s: s[1] let_def %= let + iden_list + inx + expr, lambda h, s: LetNode(s[2], s[4], s[1]) iden_list %= iden, lambda h, s: [s[1]] diff --git a/src/compiler/cmp/semantic.py b/src/compiler/cmp/semantic.py index 864816ecd..01f13a0c2 100644 --- a/src/compiler/cmp/semantic.py +++ b/src/compiler/cmp/semantic.py @@ -75,7 +75,7 @@ def get_attribute(self, name: str): f'Attribute "{name}" is not defined in {self.name}.' ) - def define_attribute(self, name: str, typex, idx=None): + def define_attribute(self, name: str, typex, idx=None) -> Attribute: try: self.get_attribute(name) except SemanticError: @@ -87,7 +87,7 @@ def define_attribute(self, name: str, typex, idx=None): f'Attribute "{name}" is already defined in {self.name}.' ) - def get_method(self, name: str): + def get_method(self, name: str) -> Method: try: return next(method for method in self.methods if method.name == name) except StopIteration: @@ -280,7 +280,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 ( diff --git a/src/compiler/visitors/formatter.py b/src/compiler/visitors/formatter.py index fac1cf025..8fb18764b 100644 --- a/src/compiler/visitors/formatter.py +++ b/src/compiler/visitors/formatter.py @@ -103,14 +103,14 @@ def visit(self, node, tabs=0): return f"{ans}\n{cond}\n{then_body}\n{else_body}" @visitor.when(LetNode) - def visit(self, node, tabs=0): + def visit(self, node: LetNode, tabs=0): ans = "\t" * tabs + f"\\__LetNode: let in " expr = self.visit(node.body, tabs + 1) iden_list = [] for item in node.id_list: - iden = "\t" * (tabs + 1) + f"{item[0]} : {item[1]}" - if item[2] is not None: - iden = f"{iden} <- \n{self.visit(item[2], tabs + 2)}" + iden = "\t" * (tabs + 1) + f"{item.id} : {item.typex}" + if item.expression is not None: + iden = f"{iden} <- \n{self.visit(item.expression, tabs + 2)}" iden_list.append(iden) iden_list = "\n".join(iden_list) return f"{ans}\n{iden_list}\n{expr}" diff --git a/src/compiler/visitors/type_builder.py b/src/compiler/visitors/type_builder.py index 16df0a7ad..b1051f8af 100644 --- a/src/compiler/visitors/type_builder.py +++ b/src/compiler/visitors/type_builder.py @@ -87,13 +87,8 @@ def visit(self, node: FuncDeclarationNode): t = SelfType(self.current_type) elif isinstance(t, AutoType): node.index[-1] = self.manager.assign_id(self.obj_type) - except SemanticError: - self.errors.append( - ( - TypeError(f"Class {t} of formal parameter {n} is undefined."), - typex.pos, - ) - ) + except TypeError as ex: + self.errors.append((ex, typex.pos)) t = ErrorType() param_types.append(t) @@ -102,15 +97,8 @@ def visit(self, node: FuncDeclarationNode): rtype = self.context.get_type(node.type) if isinstance(rtype, SelfType): rtype = SelfType(self.current_type) - except SemanticError: - self.errors.append( - ( - TypeError( - f"Undefined return type {node.type} in method {node.id}." - ), - node.typeToken.pos, - ) - ) + except TypeError as ex: + self.errors.append((ex, node.typeToken.pos)) rtype = ErrorType() node.idx = ( @@ -134,15 +122,8 @@ def visit(self, node: AttrDeclarationNode): attr_type = self.context.get_type(node.type) if isinstance(attr_type, SelfType): attr_type = SelfType(self.current_type) - except SemanticError: - self.errors.append( - ( - TypeError( - f"Class {node.type} of attribute {node.id} is undefined." - ), - node.typeToken.pos, - ) - ) + except TypeError as ex: + self.errors.append((ex, node.typeToken.pos)) attr_type = ErrorType() node.idx = ( @@ -188,7 +169,7 @@ def check_main_class(self): self.errors.append( (SemanticError("Class Main must contain a method main"), (0, 0)) ) - except SemanticError: + except TypeError: self.errors.append( (SemanticError("Program must contain a class Main"), (0, 0)) ) diff --git a/src/compiler/visitors/type_checker.py b/src/compiler/visitors/type_checker.py index 6d114e7a4..83eb73076 100644 --- a/src/compiler/visitors/type_checker.py +++ b/src/compiler/visitors/type_checker.py @@ -1,5 +1,12 @@ +from ast import Call +from typing import List, Optional, Tuple + +from numpy import void import compiler.visitors.visitor as visitor from ..cmp.semantic import ( + Context, + InferencerManager, + Method, Scope, SemanticError, ErrorType, @@ -8,8 +15,10 @@ SelfType, AutoType, LCA, + Type, ) from ..cmp.ast import ( + BinaryNode, ProgramNode, ClassDeclarationNode, AttrDeclarationNode, @@ -45,12 +54,12 @@ class TypeChecker: - def __init__(self, context, manager, errors=[]): - self.context = context - self.current_type = None - self.current_method = None - self.errors = errors - self.manager = manager + def __init__(self, context, manager): + self.context: Context = context + self.current_type: Optional[Type] = None + self.current_method: Optional[Method] = None + self.errors: List[Tuple[Exception, Tuple[int, int]]] = [] + self.manager: InferencerManager = manager # built-in types self.obj_type = self.context.get_type("Object") @@ -63,14 +72,16 @@ def visit(self, node, scope=None): pass @visitor.when(ProgramNode) - def visit(self, node, scope=None): + def visit(self, node: ProgramNode, scope: Optional[Scope] = None) -> Scope: + # print("Hola program node") scope = Scope() for declaration in node.declarations: self.visit(declaration, scope.create_child()) return scope @visitor.when(ClassDeclarationNode) - def visit(self, node, scope): + def visit(self, node: ClassDeclarationNode, scope: Scope): + # print(f"Hola class {node.id}") self.current_type = self.context.get_type(node.id) scope.define_variable("self", SelfType(self.current_type)) attributes = self.current_type.all_attributes() @@ -82,37 +93,62 @@ def visit(self, node, scope): self.visit(feature, scope.create_child()) @visitor.when(AttrDeclarationNode) - def visit(self, node, scope): + def visit(self, node: AttrDeclarationNode, scope: Scope): + # print(f"Hola attribute {node.id}") var = scope.find_variable(node.id) attr_type = var.type if node.expr is not None: + # print("Hola adentro") computed_type = self.visit(node.expr, scope) if not self.check_conformance(computed_type, attr_type): self.errors.append( - INCOMPATIBLE_TYPES % (computed_type.name, attr_type.name) + ( + TypeError( + f"Inferred type {computed_type.name} of initialization of attribute {node.id} does not conform to declared type {node.type}." + ), + node.token.pos, + ) ) @visitor.when(FuncDeclarationNode) - def visit(self, node, scope): + def visit(self, node: FuncDeclarationNode, scope: Scope): + # print(f"Hola func {node.id}") self.current_method = self.current_type.get_method(node.id) # checking overwriting try: method = self.current_type.parent.get_method(node.id) if not len(self.current_method.param_types) == len(method.param_types): - self.errors.append(WRONG_SIGNATURE % (node.id, self.current_type.name)) + self.errors.append( + ( + SemanticError( + f"Incompatible number of formal parameters in redefined method {node.id}." + ), + node.token.pos, + ) + ) else: for i, t in enumerate(self.current_method.param_types): if not method.param_types[i] == t: self.errors.append( - WRONG_SIGNATURE % (node.id, self.current_type.name) + ( + SemanticError( + f"In redefined method {node.id}, parameter type {t.name} is different from original type {method.param_types[i].name}." + ), + node.token.pos, + ) ) break else: if not self.current_method.return_type == method.return_type: self.errors.append( - WRONG_SIGNATURE % (node.id, self.current_type.name) + ( + SemanticError( + f"In redefined method {node.id}, return type {self.current_method.return_type.name} is different from original return type {method.return_type.name}." + ), + node.typeToken.pos, + ) ) except SemanticError: pass @@ -121,7 +157,12 @@ def visit(self, node, scope): for i, var in enumerate(self.current_method.param_names): if scope.is_local(var): self.errors.append( - LOCAL_ALREADY_DEFINED % (var, self.current_method.name) + ( + SemanticError( + LOCAL_ALREADY_DEFINED % (var, self.current_method.name) + ), + node.token.pos, + ) ) else: scope.define_variable( @@ -136,84 +177,140 @@ def visit(self, node, scope): rtype = self.current_method.return_type if not self.check_conformance(computed_type, rtype): self.errors.append( - INCOMPATIBLE_TYPES - % (computed_type.name, self.current_method.return_type.name) + ( + TypeError( + f"Inferred return type {computed_type.name} of method {node.id} does not conform to declared return type {rtype.name}." + ), + node.typeToken.pos, + ) ) @visitor.when(AssignNode) - def visit(self, node, scope): + def visit(self, node: AssignNode, scope: Scope) -> Type: + # print(f"Hola assignment {node.id}") if node.id == "self": - self.errors.append(SELF_IS_READONLY) + self.errors.append( + (SemanticError("Cannot assign to 'self'."), node.idToken.pos) + ) # checking variable is defined var = scope.find_variable(node.id) if var is None: - self.errors.append(VARIABLE_NOT_DEFINED % (node.id, self.current_type.name)) + self.errors.append( + (NameError(f"Undeclared identifier {node.id}."), node.idToken.pos) + ) var = scope.define_variable(node.id, ErrorType()) computed_type = self.visit(node.expr, scope.create_child()) if not self.check_conformance(computed_type, var.type): - self.errors.append(INCOMPATIBLE_TYPES % (computed_type.name, var.type.name)) + self.errors.append( + ( + TypeError(INCOMPATIBLE_TYPES % (computed_type.name, var.type.name)), + node.token.pos, + ) + ) return computed_type @visitor.when(CallNode) - def visit(self, node, scope): + def visit(self, node: CallNode, scope: Scope): + # print(f"Hola callNode {node.id}") # Evaluate object obj_type = self.visit(node.obj, scope) + # print("step 1") # Check object type conforms to cast type cast_type = obj_type - if node.type is not None: + if not node.type == "": + # print("step 2") try: cast_type = self.context.get_type(node.type) + # print("step 3") if isinstance(cast_type, AutoType): + # print("step 4") raise SemanticError( "AUTO_TYPE can't be the type on this type of dispatch" ) if isinstance(cast_type, SelfType): + # print("step 5") cast_type = SelfType(self.current_type) - except SemanticError as ex: + except (SemanticError, TypeError) as ex: + # print("step 6") cast_type = ErrorType() - self.errors.append(ex.text) + self.errors.append((ex, node.typeToken.pos)) + # print(obj_type, cast_type) if not self.check_conformance(obj_type, cast_type): - self.errors.append(INCOMPATIBLE_TYPES % (obj_type.name, cast_type.name)) + # print("step 7") + self.errors.append( + ( + TypeError(INCOMPATIBLE_TYPES % (obj_type.name, cast_type.name)), + node.typeToken.pos, + ) + ) + # print("aqui estoy") # if the obj that is calling the function is autotype, let it pass if isinstance(cast_type, AutoType): + # print("step 8") return cast_type if isinstance(cast_type, SelfType): + # print("step 9") cast_type = self.current_type # Check this function is defined for cast_type try: method = cast_type.get_method(node.id) + # print("step 10") + # Check equal number of parameters if not len(node.args) == len(method.param_types): - self.errors.append(INVALID_OPERATION % (method.name, cast_type.name)) + # print("step 11") + self.errors.append( + ( + SemanticError( + f"Method {method.name} called with wrong number of arguments. " + ), + node.token.pos, + ) + ) return ErrorType() + + # Check conformance to parameter types for i, arg in enumerate(node.args): computed_type = self.visit(arg, scope) + # print("step 12 - ", i) if not self.check_conformance(computed_type, method.param_types[i]): + # print("step 13 - ", i) self.errors.append( - INCOMPATIBLE_TYPES - % (computed_type.name, method.param_types[i].name) + ( + TypeError( + INCOMPATIBLE_TYPES + % (computed_type.name, method.param_types[i].name) + ), + node.token.pos, + ) ) # check self_type rtype = method.return_type if isinstance(rtype, SelfType): rtype = obj_type - + # print("step 14") return rtype - except SemanticError as ex: - self.errors.append(ex.text) + except SemanticError: + # print("step 15") + self.errors.append( + ( + AttributeError(f"Dispatch to undefined method {node.id}."), + node.token.pos, + ) + ) return ErrorType() @visitor.when(CaseNode) - def visit(self, node, scope): + def visit(self, node: CaseNode, scope: Scope): # check expression self.visit(node.expr, scope) @@ -222,21 +319,36 @@ def visit(self, node, scope): # check branches types = [] node.branch_idx = [] + decTypes = set() + size = 0 for branch in node.branch_list: - idx, typex, expr = branch - node.branch_idx.append(None) # check idx is not self - if idx == "self": - self.errors.append(SELF_IS_READONLY) + if branch.id == "self": + self.errors.append( + (SemanticError(SELF_IS_READONLY), branch.idToken.pos) + ) + + # check no branch repeats type + decTypes.add(branch.typex) + if size == len(decTypes): + self.errors.append( + ( + SemanticError( + f"Duplicate branch {branch.typex} in case statement." + ), + branch.typexToken.pos, + ) + ) + size += 1 try: - var_type = self.context.get_type(typex) + var_type = self.context.get_type(branch.typex) if isinstance(var_type, SelfType): var_type = SelfType(self.current_type) - except SemanticError as ex: - self.errors.append(ex.text) + except TypeError as ex: + self.errors.append((ex, branch.typexToken.pos)) var_type = ErrorType() # check type is autotype and assign an id in the manager @@ -244,15 +356,16 @@ def visit(self, node, scope): node.branch_idx[-1] = self.manager.assign_id(self.obj_type) new_scope = nscope.create_child() - new_scope.define_variable(idx, var_type, node.branch_idx[-1]) + new_scope.define_variable(branch.id, var_type, node.branch_idx[-1]) - computed_type = self.visit(expr, new_scope) + computed_type = self.visit(branch.expression, new_scope) types.append(computed_type) return LCA(types) @visitor.when(BlockNode) - def visit(self, node, scope): + def visit(self, node: BlockNode, scope: Scope): + # print(f"Hola block {node.token.pos}") nscope = scope.create_child() # Check expressions @@ -264,14 +377,19 @@ def visit(self, node, scope): return computed_type @visitor.when(LoopNode) - def visit(self, node, scope): + def visit(self, node: LoopNode, scope: Scope): nscope = scope.create_child() # checking condition: it must conform to bool cond_type = self.visit(node.condition, nscope) if not cond_type.conforms_to(self.bool_type): self.errors.append( - INCOMPATIBLE_TYPES % (cond_type.name, self.bool_type.name) + ( + TypeError( + INCOMPATIBLE_TYPES % (cond_type.name, self.bool_type.name) + ), + node.token.pos, + ) ) # checking body @@ -280,13 +398,18 @@ def visit(self, node, scope): return self.obj_type @visitor.when(ConditionalNode) - def visit(self, node, scope): + def visit(self, node: ConditionalNode, scope: Scope): # check condition conforms to bool cond_type = self.visit(node.condition, scope) if not cond_type.conforms_to(self.bool_type): self.errors.append( - INCOMPATIBLE_TYPES % (cond_type.name, self.bool_type.name) + ( + TypeError( + INCOMPATIBLE_TYPES % (cond_type.name, self.bool_type.name) + ), + node.token.pos, + ) ) then_type = self.visit(node.then_body, scope.create_child()) @@ -295,45 +418,50 @@ def visit(self, node, scope): return LCA([then_type, else_type]) @visitor.when(LetNode) - def visit(self, node, scope): + def visit(self, node: LetNode, scope: Scope): nscope = scope.create_child() node.idx_list = [None] * len(node.id_list) for i, item in enumerate(node.id_list): - idx, typex, expr = item # create a new_scope for every variable defined new_scope = nscope.create_child() - if idx == "self": - self.errors.append(SELF_IS_READONLY) - idx = f"1{idx}" - node.id_list[i] = (idx, typex, expr) + # check id in let can not be self + if item.id == "self": + self.errors.append((SemanticError(SELF_IS_READONLY), item.idToken.pos)) + item.id = f"1{item.id}" + node.id_list[i] = (item.id, item.typex, item.expression) try: - typex = self.context.get_type(typex) + typex = self.context.get_type(item.typex) if isinstance(typex, SelfType): typex = SelfType(self.current_type) - except SemanticError as ex: - self.errors.append(ex.text) + except TypeError as ex: + self.errors.append((ex, item.typexToken.pos)) typex = ErrorType() if isinstance(typex, AutoType): node.idx_list[i] = self.manager.assign_id(self.obj_type) - if expr is not None: - expr_type = self.visit(expr, new_scope) + if item.expression is not None: + expr_type = self.visit(item.expression, new_scope) if not self.check_conformance(expr_type, typex): self.errors.append( - INCOMPATIBLE_TYPES % (expr_type.name, typex.name) + ( + TypeError( + INCOMPATIBLE_TYPES % (expr_type.name, typex.name) + ), + item.token.pos, + ) ) - new_scope.define_variable(idx, typex, node.idx_list[i]) + new_scope.define_variable(item.id, typex, node.idx_list[i]) nscope = new_scope return self.visit(node.body, nscope) @visitor.when(ArithmeticNode) - def visit(self, node, scope): + def visit(self, node: ArithmeticNode, scope: Scope): self.check_expr(node, scope) return self.int_type @@ -343,7 +471,7 @@ def visit(self, node, scope): return self.bool_type @visitor.when(EqualNode) - def visit(self, node, scope): + def visit(self, node: EqualNode, scope: Scope): left = self.visit(node.left, scope) right = self.visit(node.right, scope) @@ -353,7 +481,12 @@ def check_equal(typex, other): for t in types: if typex.conforms_to(t): if not other.conforms_to(t): - self.errors.append(INCOMPATIBLE_TYPES % (other.name, t.name)) + self.errors.append( + ( + TypeError(INCOMPATIBLE_TYPES % (other.name, t.name)), + node.token.pos, + ) + ) return True return False @@ -364,24 +497,38 @@ def check_equal(typex, other): return self.bool_type @visitor.when(VoidNode) - def visit(self, node, scope): + def visit(self, node: VoidNode, scope: Scope): self.visit(node.expr, scope) return self.bool_type @visitor.when(NotNode) - def visit(self, node, scope): + def visit(self, node: NotNode, scope: Scope): typex = self.visit(node.expr, scope) if not typex.conforms_to(self.bool_type): - self.errors.append(INCOMPATIBLE_TYPES % (typex.name, self.bool_type.name)) + self.errors.append( + ( + TypeError(INCOMPATIBLE_TYPES % (typex.name, self.bool_type.name)), + node.token.pos, + ) + ) return self.bool_type @visitor.when(NegNode) - def visit(self, node, scope): + def visit(self, node: NegNode, scope: Scope): + # if isinstance(node.expr, InstantiateNode): + # print(node.expr.token) + typex = self.visit(node.expr, scope) + # print(typex) if not typex.conforms_to(self.int_type): - self.errors.append(INCOMPATIBLE_TYPES % (typex.name, self.int_type.name)) + self.errors.append( + ( + TypeError(INCOMPATIBLE_TYPES % (typex.name, self.int_type.name)), + node.token.pos, + ) + ) return self.int_type @@ -398,40 +545,55 @@ def visit(self, node, scope): return self.string_type @visitor.when(VariableNode) - def visit(self, node, scope): + def visit(self, node: VariableNode, scope: Scope): var = scope.find_variable(node.lex) if var is None: self.errors.append( - VARIABLE_NOT_DEFINED % (node.lex, self.current_type.name) + ( + NameError( + VARIABLE_NOT_DEFINED % (node.lex, self.current_type.name) + ), + node.token.pos, + ) ) var = scope.define_variable(node.lex, ErrorType()) return var.type @visitor.when(InstantiateNode) - def visit(self, node, scope): + def visit(self, node: InstantiateNode, scope: Scope): try: typex = self.context.get_type(node.lex) if isinstance(typex, AutoType): raise SemanticError("AUTO_TYPE can't be instanciate with new") if isinstance(typex, SelfType): typex = SelfType(self.current_type) - except SemanticError as ex: - self.errors.append(ex.text) + except (SemanticError, TypeError) as ex: + self.errors.append((ex, node.token.pos)) typex = ErrorType() return typex - def check_expr(self, node, scope): + def check_expr(self, node: BinaryNode, scope: Scope): # checking left expr left = self.visit(node.left, scope) if not left.conforms_to(self.int_type): - self.errors.append(INCOMPATIBLE_TYPES % (left.name, self.int_type.name)) + self.errors( + ( + TypeError(INCOMPATIBLE_TYPES % (left.name, self.int_type.name)), + node.token.pos, + ) + ) # checking right expr right = self.visit(node.right, scope) if not right.conforms_to(self.int_type): - self.errors.append(INCOMPATIBLE_TYPES % (right.name, self.int_type.name)) + self.errors.append( + ( + TypeError(INCOMPATIBLE_TYPES % (right.name, self.int_type.name)), + node.token.pos, + ) + ) def check_conformance(self, computed_type, attr_type): return computed_type.conforms_to(attr_type) or ( diff --git a/src/compiler/visitors/type_collector.py b/src/compiler/visitors/type_collector.py index 682b7bdf4..82dc6672d 100644 --- a/src/compiler/visitors/type_collector.py +++ b/src/compiler/visitors/type_collector.py @@ -124,15 +124,8 @@ def check_parents(self) -> void: ) typex = built_in_types[0] item_type.set_parent(typex) - except SemanticError as ex: - self.errors.append( - ( - TypeError( - f"Class {item_type.name} inherits from an undefined class {self.parent[item].lex}." - ), - self.parent[item].pos, - ) - ) + except TypeError as ex: + self.errors.append((ex, self.parent[item].pos)) item_type.set_parent(built_in_types[0]) def check_cyclic_inheritance(self) -> void: diff --git a/src/compiler/visitors/type_inferencer.py b/src/compiler/visitors/type_inferencer.py index 2df2c4c11..d6d74b97f 100644 --- a/src/compiler/visitors/type_inferencer.py +++ b/src/compiler/visitors/type_inferencer.py @@ -1,5 +1,5 @@ import compiler.visitors.visitor as visitor -from ..cmp.semantic import SemanticError, ErrorType, SelfType, AutoType, LCA +from ..cmp.semantic import Context, SemanticError, ErrorType, SelfType, AutoType, LCA from ..cmp.ast import ( ProgramNode, ClassDeclarationNode, @@ -30,9 +30,9 @@ class TypeInferencer: - def __init__(self, context, manager, errors=[]): - self.context = context - self.errors = errors + def __init__(self, context, manager): + self.context: Context = context + self.errors: Li = [] self.manager = manager self.current_type = None diff --git a/src/main.py b/src/main.py index eabbe9baa..fc47eba89 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,6 @@ # import streamlit as st +from black import err from compiler.cmp.grammar import G # from compiler.lexer.lexer import tokenize_text, pprint_tokens @@ -10,10 +11,10 @@ from compiler.cmp.evaluation import evaluate_reverse_parse from compiler.visitors.formatter import FormatVisitor -# from compiler.visitors.type_collector import TypeCollector -# from compiler.visitors.type_builder import TypeBuilder -# from compiler.visitors.type_checker import TypeChecker -# from compiler.visitors.type_inferencer import TypeInferencer +from compiler.visitors.type_collector import TypeCollector +from compiler.visitors.type_builder import TypeBuilder +from compiler.visitors.type_checker import TypeChecker +from compiler.visitors.type_inferencer import TypeInferencer def main(args): @@ -39,38 +40,50 @@ def main(args): print(error) exit(1) - # AST parse, operations = parseResult ast = evaluate_reverse_parse(parse, operations, tokens) - # formatter = FormatVisitor() - # tree = formatter.visit(ast) - # print(tree) - # print('============== COLLECTING TYPES ===============') - # errors = [] - # collector = TypeCollector(errors) - # collector.visit(ast) - # context = collector.context - # print('Errors:', errors) - # print('Context:') - # print(context) - # print('=============== BUILDING TYPES ================') - # builder = TypeBuilder(context, errors) - # builder.visit(ast) - # manager = builder.manager - # print('Errors: [') - # for error in errors: - # print('\t', error) - # print(']') - # print('Context:')import argparse - # print('Errors: [') - # for error in errors: - # print('\t', error) - # print(']') - # formatter = FormatVisitor() - # tree = formatter.visit(ast) - # print(tree) - - # return ast + + # Collecting types + collector = TypeCollector() + collector.visit(ast) + context = collector.context + for (e, pos) in collector.errors: + print(f"{pos} - {type(e).__name__}: {str(e)}") + if collector.errors: + exit(1) + + # Building types + builder = TypeBuilder(context) + builder.visit(ast) + manager = builder.manager + for (e, pos) in builder.errors: + print(f"{pos} - {type(e).__name__}: {str(e)}") + if builder.errors: + exit(1) + + # Type checking + checker = TypeChecker(context, manager) + scope = checker.visit(ast) + for (e, pos) in checker.errors: + print(f"{pos} - {type(e).__name__}: {str(e)}") + if checker.errors: + exit(1) + + # Inferencing Autotype + inferencer = TypeInferencer(context, manager) + inferencer.visit(ast, scope) + for e in inferencer.errors: + print(f"{pos} - {type(e).__name__}: {str(e)}") + if inferencer.errors: + exit(1) + + # Last check without autotypes + checker = TypeChecker(context, manager) + checker.visit(ast) + for (e, pos) in checker.errors: + print(f"{pos} - {type(e).__name__}: {str(e)}") + if checker.errors: + exit(1) text = """ From 718543ce8cee31f01e536ef2457845a7bac77896 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Fri, 25 Feb 2022 13:09:07 -0500 Subject: [PATCH 098/138] Add computed type to expression nodes --- src/compiler/cmp/ast.py | 28 ++++++++------ src/compiler/visitors/type_checker.py | 54 ++++++++++++++++++--------- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/compiler/cmp/ast.py b/src/compiler/cmp/ast.py index e0c839f66..3945b6ad0 100644 --- a/src/compiler/cmp/ast.py +++ b/src/compiler/cmp/ast.py @@ -1,4 +1,6 @@ from typing import List, Optional, Tuple, Union + +from compiler.cmp.semantic import Type from .utils import Token, emptyToken @@ -12,7 +14,9 @@ class DeclarationNode(Node): class ExpressionNode(Node): - pass + def __init__(self, token: Token, computed_type: Optional[Type] = None): + super().__init__(token) + self.computed_type = computed_type class FuncDeclarationNode(DeclarationNode): @@ -24,7 +28,7 @@ def __init__( body: ExpressionNode, ): self.id = token.lex - # param = (name, type) + # `param` is (nameToken, typeToken) self.params = params self.type = return_type.lex self.typeToken = return_type @@ -71,10 +75,10 @@ def __init__(self, declarations: List[ClassDeclarationNode]): class AssignNode(ExpressionNode): def __init__(self, idx: Token, expr: ExpressionNode, token: Token): + super().__init__(token) self.id = idx.lex self.idToken = idx self.expr = expr - self.token = token class CallNode(ExpressionNode): @@ -85,11 +89,11 @@ def __init__( args: List[ExpressionNode], cast_type: Token = emptyToken, ): + super().__init__(idx) self.obj = obj self.id = idx.lex self.args = args self.type = cast_type.lex - self.token = idx self.typeToken = cast_type @@ -107,22 +111,22 @@ class CaseNode(ExpressionNode): def __init__( self, expr: ExpressionNode, branch_list: List[CaseBranchNode], token: Token ): + super().__init__(token) self.expr = expr self.branch_list = branch_list - self.token = token class BlockNode(ExpressionNode): def __init__(self, expr_list: List[ExpressionNode], token: Token): + super().__init__(token) self.expr_list = expr_list - self.token = token class LoopNode(ExpressionNode): def __init__(self, cond: ExpressionNode, body: ExpressionNode, token: Token): + super().__init__(token) self.condition = cond self.body = body - self.token = token class ConditionalNode(ExpressionNode): @@ -133,10 +137,10 @@ def __init__( else_body: ExpressionNode, token: Token, ): + super().__init__(token) self.condition = cond self.then_body = then_body self.else_body = else_body - self.token = token class LetVarNode(Node): @@ -157,28 +161,28 @@ def __init__( class LetNode(ExpressionNode): def __init__(self, id_list: List[LetVarNode], body: ExpressionNode, token: Token): + super().__init__(token) self.id_list = id_list self.body = body - self.token = token class AtomicNode(ExpressionNode): def __init__(self, token: Token): + super().__init__(token) self.lex = token.lex - self.token = token class UnaryNode(ExpressionNode): def __init__(self, expr: ExpressionNode, symbol: Token): + super().__init__(symbol) self.expr = expr - self.token = symbol class BinaryNode(ExpressionNode): def __init__(self, left: ExpressionNode, right: ExpressionNode, symbol: Token): + super().__init__(symbol) self.left = left self.right = right - self.token = symbol class ArithmeticNode(BinaryNode): diff --git a/src/compiler/visitors/type_checker.py b/src/compiler/visitors/type_checker.py index 83eb73076..ac9f52f1a 100644 --- a/src/compiler/visitors/type_checker.py +++ b/src/compiler/visitors/type_checker.py @@ -210,7 +210,7 @@ def visit(self, node: AssignNode, scope: Scope) -> Type: node.token.pos, ) ) - + node.computed_type = computed_type return computed_type @visitor.when(CallNode) @@ -253,6 +253,7 @@ def visit(self, node: CallNode, scope: Scope): # if the obj that is calling the function is autotype, let it pass if isinstance(cast_type, AutoType): # print("step 8") + node.computed_type = cast_type return cast_type if isinstance(cast_type, SelfType): @@ -274,7 +275,8 @@ def visit(self, node: CallNode, scope: Scope): node.token.pos, ) ) - return ErrorType() + node.computed_type = ErrorType() + return node.computed_type # Check conformance to parameter types for i, arg in enumerate(node.args): @@ -297,6 +299,7 @@ def visit(self, node: CallNode, scope: Scope): if isinstance(rtype, SelfType): rtype = obj_type # print("step 14") + node.computed_type = rtype return rtype except SemanticError: @@ -307,7 +310,8 @@ def visit(self, node: CallNode, scope: Scope): node.token.pos, ) ) - return ErrorType() + node.computed_type = ErrorType() + return node.computed_type @visitor.when(CaseNode) def visit(self, node: CaseNode, scope: Scope): @@ -361,7 +365,8 @@ def visit(self, node: CaseNode, scope: Scope): computed_type = self.visit(branch.expression, new_scope) types.append(computed_type) - return LCA(types) + node.computed_type = LCA(types) + return node.computed_type @visitor.when(BlockNode) def visit(self, node: BlockNode, scope: Scope): @@ -374,6 +379,7 @@ def visit(self, node: BlockNode, scope: Scope): computed_type = self.visit(expr, nscope) # return the type of the last expression of the list + node.computed_type = computed_type return computed_type @visitor.when(LoopNode) @@ -395,7 +401,8 @@ def visit(self, node: LoopNode, scope: Scope): # checking body self.visit(node.body, nscope) - return self.obj_type + node.computed_type = self.obj_type + return node.computed_type @visitor.when(ConditionalNode) def visit(self, node: ConditionalNode, scope: Scope): @@ -415,7 +422,8 @@ def visit(self, node: ConditionalNode, scope: Scope): then_type = self.visit(node.then_body, scope.create_child()) else_type = self.visit(node.else_body, scope.create_child()) - return LCA([then_type, else_type]) + node.computed_type = LCA([then_type, else_type]) + return node.computed_type @visitor.when(LetNode) def visit(self, node: LetNode, scope: Scope): @@ -458,17 +466,20 @@ def visit(self, node: LetNode, scope: Scope): new_scope.define_variable(item.id, typex, node.idx_list[i]) nscope = new_scope - return self.visit(node.body, nscope) + node.computed_type = self.visit(node.body, nscope) + return node.computed_type @visitor.when(ArithmeticNode) def visit(self, node: ArithmeticNode, scope: Scope): self.check_expr(node, scope) - return self.int_type + node.computed_type = self.int_type + return node.computed_type @visitor.when(ComparisonNode) - def visit(self, node, scope): + def visit(self, node: ComparisonNode, scope: Scope): self.check_expr(node, scope) - return self.bool_type + node.computed_type = self.bool_type + return node.computed_type @visitor.when(EqualNode) def visit(self, node: EqualNode, scope: Scope): @@ -494,13 +505,15 @@ def check_equal(typex, other): if not ok: check_equal(right, left) - return self.bool_type + node.computed_type = self.bool_type + return node.computed_type @visitor.when(VoidNode) def visit(self, node: VoidNode, scope: Scope): self.visit(node.expr, scope) - return self.bool_type + node.computed_type = self.bool_type + return node.computed_type @visitor.when(NotNode) def visit(self, node: NotNode, scope: Scope): @@ -513,7 +526,8 @@ def visit(self, node: NotNode, scope: Scope): ) ) - return self.bool_type + node.computed_type = self.bool_type + return node.computed_type @visitor.when(NegNode) def visit(self, node: NegNode, scope: Scope): @@ -530,19 +544,23 @@ def visit(self, node: NegNode, scope: Scope): ) ) - return self.int_type + node.computed_type = self.int_type + return node.computed_type @visitor.when(ConstantNumNode) def visit(self, node, scope): - return self.int_type + node.computed_type = self.int_type + return node.computed_type @visitor.when(ConstantBoolNode) def visit(self, node, scope): - return self.bool_type + node.computed_type = self.bool_type + return node.computed_type @visitor.when(ConstantStringNode) def visit(self, node, scope): - return self.string_type + node.computed_type = self.string_type + return node.computed_type @visitor.when(VariableNode) def visit(self, node: VariableNode, scope: Scope): @@ -558,6 +576,7 @@ def visit(self, node: VariableNode, scope: Scope): ) var = scope.define_variable(node.lex, ErrorType()) + node.computed_type = var.type return var.type @visitor.when(InstantiateNode) @@ -572,6 +591,7 @@ def visit(self, node: InstantiateNode, scope: Scope): self.errors.append((ex, node.token.pos)) typex = ErrorType() + node.computed_type = typex return typex def check_expr(self, node: BinaryNode, scope: Scope): From dd16568f6af4fe08c06e4a97e01f46eade0cb64a Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Fri, 25 Feb 2022 13:40:15 -0500 Subject: [PATCH 099/138] Modify Inferencer visitor errors --- src/compiler/visitors/type_inferencer.py | 58 ++++++++++++++++-------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/compiler/visitors/type_inferencer.py b/src/compiler/visitors/type_inferencer.py index d6d74b97f..ce38f712e 100644 --- a/src/compiler/visitors/type_inferencer.py +++ b/src/compiler/visitors/type_inferencer.py @@ -1,5 +1,17 @@ +from logging import Manager +from typing import List, Tuple import compiler.visitors.visitor as visitor -from ..cmp.semantic import Context, SemanticError, ErrorType, SelfType, AutoType, LCA +from ..cmp.semantic import ( + Context, + Method, + Scope, + SemanticError, + ErrorType, + SelfType, + AutoType, + LCA, + Type, +) from ..cmp.ast import ( ProgramNode, ClassDeclarationNode, @@ -32,11 +44,11 @@ class TypeInferencer: def __init__(self, context, manager): self.context: Context = context - self.errors: Li = [] - self.manager = manager + self.errors: List[Tuple[Exception, Tuple[int, int]]] = [] + self.manager: Manager = manager - self.current_type = None - self.current_method = None + self.current_type: Type = None + self.current_method: Method = None self.scope_children_id = 0 # built-in types @@ -50,7 +62,7 @@ def visit(self, node, scope, types=None): pass @visitor.when(ProgramNode) - def visit(self, node, scope, types=None): + def visit(self, node: ProgramNode, scope: Scope, types=None): if types is None: types = [] @@ -78,7 +90,7 @@ def visit(self, node, scope, types): self.visit(feature, child_scope, types) @visitor.when(AttrDeclarationNode) - def visit(self, node, scope, types): + def visit(self, node: AttrDeclarationNode, scope, types): var_attr = scope.find_variable(node.id) attr_type = var_attr.type idx = var_attr.idx @@ -87,7 +99,9 @@ def visit(self, node, scope, types): inf_type = self.manager.infered_type[idx] if inf_type is not None: if isinstance(inf_type, ErrorType): - self.errors.append(AUTOTYPE_ERROR) + self.errors.append( + (SemanticError(AUTOTYPE_ERROR), node.typeToken.pos) + ) else: node.type = inf_type.name self.current_type.update_attr(node.id, inf_type) @@ -106,7 +120,7 @@ def visit(self, node, scope, types): self.manager.upd_conformed_by(node.idx, computed_types) @visitor.when(FuncDeclarationNode) - def visit(self, node, scope, types): + def visit(self, node: FuncDeclarationNode, scope: Scope, types): self.current_method = self.current_type.get_method(node.id) method_params = [] @@ -117,7 +131,9 @@ def visit(self, node, scope, types): inf_type = self.manager.infered_type[idx] if inf_type is not None: if isinstance(inf_type, ErrorType): - self.errors.append(AUTOTYPE_ERROR) + self.errors.append( + (SemanticError(AUTOTYPE_ERROR), node.params[i][1].pos) + ) else: node.params[i] = (node.params[i][0], inf_type.name) self.current_type.update_method_param(name, inf_type, i) @@ -131,7 +147,9 @@ def visit(self, node, scope, types): inf_type = self.manager.infered_type[idx] if inf_type is not None: if isinstance(inf_type, ErrorType): - self.errors.append(AUTOTYPE_ERROR) + self.errors.append( + (SemanticError(AUTOTYPE_ERROR), node.typeToken.pos) + ) else: node.type = inf_type.name self.current_type.update_method_rtype( @@ -212,7 +230,7 @@ def visit(self, node, scope, types): cast_type = None elif isinstance(cast_type, SelfType): cast_type = self.current_type - except SemanticError: + except TypeError: pass # Check object @@ -269,7 +287,7 @@ def visit(self, node, scope, types): return ErrorType(), [] @visitor.when(CaseNode) - def visit(self, node, scope, types): + def visit(self, node: CaseNode, scope: Scope, types): # check expression self.visit(node.expr, scope, set()) @@ -281,14 +299,16 @@ def visit(self, node, scope, types): for i, (branch, child_scope) in enumerate( zip(node.branch_list, nscope.children) ): - branch_name, branch_type, expr = branch + branch_name, branch_type, expr = branch.id, branch.typex, branch.expression var = child_scope.find_variable(branch_name) branch_type = var.type if isinstance(branch_type, AutoType): inf_type = self.manager.infered_type[node.branch_idx[i]] if inf_type is not None: if isinstance(inf_type, ErrorType): - self.errors.append(AUTOTYPE_ERROR) + self.errors.append( + (SemanticError(AUTOTYPE_ERROR), branch.token.pos) + ) else: node.branch_list[i] = (branch_name, inf_type.name, expr) child_scope.update_variable(branch_name, inf_type) @@ -355,7 +375,7 @@ def visit(self, node, scope, types): return LCA(branch_types), branch_types @visitor.when(LetNode) - def visit(self, node, scope, types): + def visit(self, node: LetNode, scope: Scope, types): scope_index = self.scope_children_id nscope = scope.children[scope_index] self.scope_children_id = 0 @@ -365,14 +385,16 @@ def visit(self, node, scope, types): new_scope = nscope.children[temp_scope_index] self.scope_children_id = 0 - var_name, _, expr = item + var_name, _, expr = item.id, item.typex, item.expression var = new_scope.find_variable(var_name) if isinstance(var.type, AutoType): inf_type = self.manager.infered_type[node.idx_list[i]] if inf_type is not None: if isinstance(inf_type, ErrorType): - self.errors.append(AUTOTYPE_ERROR) + self.errors.append( + (SemanticError(AUTOTYPE_ERROR), item.typexToken.pos) + ) else: node.id_list[i] = (var_name, inf_type.name, expr) new_scope.update_variable(var_name, inf_type) From 2fb87b37afccd8eae5376436c631800d398ff356 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Fri, 25 Feb 2022 13:43:24 -0500 Subject: [PATCH 100/138] format files --- tests/codegen_test.py | 12 +++-- tests/conftest.py | 3 +- tests/lexer_test.py | 9 ++-- tests/parser_test.py | 9 ++-- tests/semantic_test.py | 13 ++++-- tests/utils/__init__.py | 2 +- tests/utils/utils.py | 100 ++++++++++++++++++++++++++++------------ 7 files changed, 103 insertions(+), 45 deletions(-) diff --git a/tests/codegen_test.py b/tests/codegen_test.py index 48df768ff..393d7907a 100644 --- a/tests/codegen_test.py +++ b/tests/codegen_test.py @@ -2,8 +2,8 @@ import os from utils import compare_outputs -tests_dir = __file__.rpartition('/')[0] + '/codegen/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/codegen/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] # @pytest.mark.lexer # @pytest.mark.parser @@ -13,5 +13,9 @@ @pytest.mark.run(order=4) @pytest.mark.parametrize("cool_file", tests) def test_codegen(compiler_path, cool_file): - compare_outputs(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_input.txt',\ - tests_dir + cool_file[:-3] + '_output.txt') \ No newline at end of file + compare_outputs( + compiler_path, + tests_dir + cool_file, + tests_dir + cool_file[:-3] + "_input.txt", + tests_dir + cool_file[:-3] + "_output.txt", + ) diff --git a/tests/conftest.py b/tests/conftest.py index 1f44eeb72..0969ec84d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ import pytest import os + @pytest.fixture def compiler_path(): - return os.path.abspath('./coolc.sh') \ No newline at end of file + return os.path.abspath("./coolc.sh") diff --git a/tests/lexer_test.py b/tests/lexer_test.py index 2a27223d3..1e2a36a0d 100644 --- a/tests/lexer_test.py +++ b/tests/lexer_test.py @@ -2,12 +2,15 @@ import os from utils import compare_errors -tests_dir = __file__.rpartition('/')[0] + '/lexer/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/lexer/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] + @pytest.mark.lexer @pytest.mark.error @pytest.mark.run(order=1) @pytest.mark.parametrize("cool_file", tests) def test_lexer_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file + compare_errors( + compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + "_error.txt" + ) diff --git a/tests/parser_test.py b/tests/parser_test.py index 129c0f20a..a1ecc8f9e 100644 --- a/tests/parser_test.py +++ b/tests/parser_test.py @@ -2,12 +2,15 @@ import os from utils import compare_errors -tests_dir = __file__.rpartition('/')[0] + '/parser/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/parser/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] + @pytest.mark.parser @pytest.mark.error @pytest.mark.run(order=2) @pytest.mark.parametrize("cool_file", tests) def test_parser_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file + compare_errors( + compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + "_error.txt" + ) diff --git a/tests/semantic_test.py b/tests/semantic_test.py index cac9cd78b..c80b3552d 100644 --- a/tests/semantic_test.py +++ b/tests/semantic_test.py @@ -2,13 +2,18 @@ import os from utils import compare_errors, first_error_only_line -tests_dir = __file__.rpartition('/')[0] + '/semantic/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/semantic/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] + @pytest.mark.semantic @pytest.mark.error @pytest.mark.run(order=3) @pytest.mark.parametrize("cool_file", tests) def test_semantic_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt', \ - cmp=first_error_only_line) \ No newline at end of file + compare_errors( + compiler_path, + tests_dir + cool_file, + tests_dir + cool_file[:-3] + "_error.txt", + cmp=first_error_only_line, + ) diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 90f60fdd8..16281fe0b 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -1 +1 @@ -from .utils import * \ No newline at end of file +from .utils import * diff --git a/tests/utils/utils.py b/tests/utils/utils.py index 961cf7cbc..21712c8f9 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -2,16 +2,17 @@ import re -COMPILER_TIMEOUT = 'El compilador tarda mucho en responder.' -SPIM_TIMEOUT = 'El spim tarda mucho en responder.' -TEST_MUST_FAIL = 'El test %s debe fallar al compilar' -TEST_MUST_COMPILE = 'El test %s debe compilar' -BAD_ERROR_FORMAT = '''El error no esta en formato: (,) - : - o no se encuentra en la 3ra linea\n\n%s''' -UNEXPECTED_ERROR = 'Se esperaba un %s en (%d, %d). Su error fue un %s en (%d, %d)' -UNEXPECTED_OUTPUT = 'La salida de %s no es la esperada:\n%s\nEsperada:\n%s' +COMPILER_TIMEOUT = "El compilador tarda mucho en responder." +SPIM_TIMEOUT = "El spim tarda mucho en responder." +TEST_MUST_FAIL = "El test %s debe fallar al compilar" +TEST_MUST_COMPILE = "El test %s debe compilar" +BAD_ERROR_FORMAT = """El error no esta en formato: (,) - : + o no se encuentra en la 3ra linea\n\n%s""" +UNEXPECTED_ERROR = "Se esperaba un %s en (%d, %d). Su error fue un %s en (%d, %d)" +UNEXPECTED_OUTPUT = "La salida de %s no es la esperada:\n%s\nEsperada:\n%s" + +ERROR_FORMAT = r"^\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*-\s*(\w+)\s*:(.*)$" -ERROR_FORMAT = r'^\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*-\s*(\w+)\s*:(.*)$' def parse_error(error: str): merror = re.fullmatch(ERROR_FORMAT, error) @@ -25,67 +26,108 @@ def first_error(compiler_output: list, errors: list): oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) - assert line == oline and column == ocolumn and error_type == oerror_type,\ - UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + assert ( + line == oline and column == ocolumn and error_type == oerror_type + ), UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + def first_error_only_line(compiler_output: list, errors: list): line, column, error_type, _ = parse_error(errors[0]) oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) - assert line == oline and error_type == oerror_type,\ - UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + assert line == oline and error_type == oerror_type, UNEXPECTED_ERROR % ( + error_type, + line, + column, + oerror_type, + oline, + ocolumn, + ) def get_file_name(path: str): try: - return path[path.rindex('/') + 1:] + return path[path.rindex("/") + 1 :] except ValueError: return path -def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str, cmp=first_error, timeout=100): + +def compare_errors( + compiler_path: str, + cool_file_path: str, + error_file_path: str, + cmp=first_error, + timeout=100, +): try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + sp = subprocess.run( + ["bash", compiler_path, cool_file_path], + capture_output=True, + timeout=timeout, + ) return_code, output = sp.returncode, sp.stdout.decode() except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT assert return_code == 1, TEST_MUST_FAIL % get_file_name(cool_file_path) - fd = open(error_file_path, 'r') - errors = fd.read().split('\n') + fd = open(error_file_path, "r") + errors = fd.read().split("\n") fd.close() # checking the errors of compiler - compiler_output = output.split('\n') + compiler_output = output.split("\n") cmp(compiler_output[2:], errors) -SPIM_HEADER = r'''^SPIM Version .+ of .+ + +SPIM_HEADER = r"""^SPIM Version .+ of .+ Copyright .+\, James R\. Larus\. All Rights Reserved\. See the file README for a full copyright notice\. -(?:Loaded: .+\n)*''' -def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: str, output_file_path: str, timeout=100): +(?:Loaded: .+\n)*""" + + +def compare_outputs( + compiler_path: str, + cool_file_path: str, + input_file_path: str, + output_file_path: str, + timeout=100, +): try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + sp = subprocess.run( + ["bash", compiler_path, cool_file_path], + capture_output=True, + timeout=timeout, + ) assert sp.returncode == 0, TEST_MUST_COMPILE % get_file_name(cool_file_path) except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT - spim_file = cool_file_path[:-2] + 'mips' + spim_file = cool_file_path[:-2] + "mips" try: - fd = open(input_file_path, 'rb') - sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), capture_output=True, timeout=timeout) + fd = open(input_file_path, "rb") + sp = subprocess.run( + ["spim", "-file", spim_file], + input=fd.read(), + capture_output=True, + timeout=timeout, + ) fd.close() mo = re.match(SPIM_HEADER, sp.stdout.decode()) if mo: - output = mo.string[mo.end():] + output = mo.string[mo.end() :] except subprocess.TimeoutExpired: assert False, SPIM_TIMEOUT - fd = open(output_file_path, 'r') + fd = open(output_file_path, "r") eoutput = fd.read() fd.close() - assert output == eoutput, UNEXPECTED_OUTPUT % (spim_file, repr(output), repr(eoutput)) + assert output == eoutput, UNEXPECTED_OUTPUT % ( + spim_file, + repr(output), + repr(eoutput), + ) From 21e35eaa162279ce4da967e44ec1ad065ac097f5 Mon Sep 17 00:00:00 2001 From: kikexd Date: Fri, 25 Feb 2022 14:25:26 -0500 Subject: [PATCH 101/138] add cil formatter fixes --- src/compiler/cmp/cil_ast.py | 14 +++-- src/compiler/visitors/cil_formatter.py | 44 ++++++++++++++++ src/compiler/visitors/cool2cil.py | 72 ++++++++++++++------------ src/main.py | 39 ++++++++------ 4 files changed, 116 insertions(+), 53 deletions(-) diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py index 0473465d2..34bae8219 100644 --- a/src/compiler/cmp/cil_ast.py +++ b/src/compiler/cmp/cil_ast.py @@ -26,6 +26,10 @@ def __init__(self, dest, source): self.source = source +class ExitNode(InstructionNode): + pass + + class DataNode(Node): def __init__(self, vname, value): self.name = vname @@ -33,12 +37,13 @@ def __init__(self, vname, value): class FunctionNode(Node): - def __init__(self, fname, params, localvars, instructions, local_labels=0): + def __init__(self, fname, params, localvars, instructions): self.name = fname self.params = params self.localvars = localvars self.instructions = instructions - self.local_labels = local_labels + self.ids = dict() + self.labels_count = 0 class ParamNode(Node): @@ -96,7 +101,7 @@ class EqualStrNode(ArithmeticNode): pass -class VoidNode(ArithmeticNode): +class VoidNode(InstructionNode): pass @@ -185,10 +190,11 @@ def __init__(self, dest, source): 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): diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py index 5dcccca99..ca9cfc44c 100644 --- a/src/compiler/visitors/cil_formatter.py +++ b/src/compiler/visitors/cil_formatter.py @@ -22,6 +22,14 @@ def visit(self, node): return f"type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}" + @visitor.when(CopyNode) + def visit(self, node): + return f"{node.dest} = COPY {node.source}" + + @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) @@ -34,6 +42,10 @@ def visit(self, node): def visit(self, node): return f"PARAM {node.name}" + @visitor.when(LoadNode) + def visit(self, node): + return f"{node.dest} = Load {node.msg}" + @visitor.when(LocalNode) def visit(self, node): return f"LOCAL {node.name}" @@ -145,3 +157,35 @@ def visit(self, node): @visitor.when(PrintNode) def visit(self, node): return f"PRINT {node.value}" + + @visitor.when(ExitNode) + def visit(self, node): + return f"EXIT" + + @visitor.when(LoadNode) + def visit(self, node): + return f"{node.dest} = Load {node.msg}" + + @visitor.when(TypeNameNode) + def visit(self, node): + return f"{node.dest} = TYPENAME {node.source}" + + @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(ErrorNode) + def visit(self, node): + return f"ERROR {node.data_node}" diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 0703fb306..8354d19e6 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -1,3 +1,4 @@ +from pprint import pprint import compiler.visitors.visitor as visitor from ..cmp import cil_ast as cil from ..cmp.semantic import ( @@ -12,8 +13,10 @@ VariableInfo, ) from ..cmp.ast import ( + CaseBranchNode, LeqNode, LessNode, + LetVarNode, ProgramNode, ClassDeclarationNode, AttrDeclarationNode, @@ -50,6 +53,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): @@ -68,16 +72,16 @@ def instructions(self): return self.current_function.instructions def register_local(self, vinfo, id=False): - vinfo.name = ( + new_vinfo = VariableInfo("", None) + new_vinfo.name = ( f"local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}" ) - local_node = cil.LocalNode(vinfo.name) + local_node = cil.LocalNode(new_vinfo.name) if id: - self.ids[vinfo.name] = vinfo.name - + 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, None) @@ -162,7 +166,7 @@ def register_built_in(self): ][0] self.register_instruction(cil.LoadNode(vname, data_node)) self.register_instruction(cil.PrintStrNode(vname)) - self.register_instruction(cil.TypeNode(vname, self.vself.name)) + 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)) @@ -583,7 +587,7 @@ def visit(self, node, scope): self.vself.name, node.id, value, self.current_type.name ) ) - except AttributeError: + except SemanticError: vname = None param_names = [pn.name for pn in self.current_function.params] if node.id in param_names: @@ -628,7 +632,7 @@ def visit(self, node, scope): self.register_runtime_error( equal_result, - f"({node.token.row},{node.token.column}) - RuntimeError: Dispatch on void\n", + f"{node.token.pos} - RuntimeError: Dispatch on void\n", ) # self @@ -649,7 +653,7 @@ def visit(self, node, scope): self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) computed_type = node.obj.computed_type if computed_type.name == "SELF_TYPE": - computed_type = computed_type.fixed + computed_type = computed_type.fixed_type self.register_instruction( cil.DynamicCallNode(type_of_node, node.id, result, computed_type.name) ) @@ -729,11 +733,11 @@ def visit(self, node, scope): @visitor.when(BlockNode) def visit(self, node, scope): ####################################### - # node.exprs -> [ ExpressionNode ... ] + # node.expr_list -> [ ExpressionNode ... ] ####################################### ret = self.register_local(VariableInfo("block_node_value", None)) - for expr in node.exprs: + for expr in node.expr_list: ret_value = self.visit(expr, scope) self.register_instruction(cil.AssignNode(ret, ret_value)) @@ -751,12 +755,12 @@ def visit(self, node, scope): for let_var in node.id_list: self.visit(let_var, scope) - ret_val = self.visit(node.in_body, scope) + ret_val = self.visit(node.body, scope) self.register_instruction(cil.AssignNode(value, ret_val)) return value - @visitor.when(LetAttributeNode) + @visitor.when(LetVarNode) def visit(self, node, scope): ############################### # node.id -> str @@ -766,12 +770,12 @@ def visit(self, node, scope): 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: - ret_value = self.visit(node.expr, scope) + vname = self.register_local(VariableInfo(node.id, node.typex), id=True) + if node.expression: + ret_value = self.visit(node.expression, scope) self.register_instruction(cil.AssignNode(vname, ret_value)) - elif node.type in self.value_types: - self.register_instruction(cil.AllocateNode(node.type, vname)) + elif node.typex in self.value_types: + self.register_instruction(cil.AllocateNode(node.typex, vname)) @visitor.when(CaseNode) def visit(self, node, scope): @@ -796,7 +800,7 @@ def visit(self, node, scope): self.register_runtime_error( equal_result, - f"({node.position.row},{node.position.column}) - RuntimeError: Case on void\n", + f"{node.token.pos} - RuntimeError: Case on void\n", ) # sorting the branches @@ -833,7 +837,7 @@ def visit(self, node, scope): # Raise runtime error if no Goto was executed data_node = self.register_data( - f"({node.position.row + 1 + len(node.branches)},{node.position.column - 5}) - RuntimeError: Execution of a case statement without a matching branch\n" + f"({node.token.pos[0] + 1 + len(node.branches)},{node.token.pos[1] - 5}) - RuntimeError: Execution of a case statement without a matching branch\n" ) self.register_instruction(cil.ErrorNode(data_node)) @@ -849,7 +853,7 @@ def visit(self, node, scope): self.register_instruction(end_label) return value - @visitor.when(CaseExpressionNode) + @visitor.when(CaseBranchNode) def visit(self, node, scope): ############################### # node.id -> str @@ -1076,7 +1080,7 @@ def visit(self, node, scope): self.register_instruction(cil.EqualNode(equal_result, value_right, 0)) self.register_runtime_error( equal_result, - f"({node.position.row},{node.position.column}) - RuntimeError: Division by zero\n", + f"{node.token.pos} - RuntimeError: Division by zero\n", ) self.register_instruction(cil.DivNode(value, value_left, value_right)) @@ -1147,17 +1151,6 @@ def visit(self, node, scope): # node.lex -> str ############################### - self.current_type.get_attribute(node.lex) - attr = self.register_local(VariableInfo(node.lex, None)) - self.register_instruction(cil.GetAttribNode(attr, node, node.lex)) - return attr - - @visitor.when(ConstantNumNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - try: self.current_type.get_attribute(node.lex) attr = self.register_local(VariableInfo(node.lex, None), id=True) @@ -1167,7 +1160,7 @@ def visit(self, node, scope): ) ) return attr - except AttributeError: + except SemanticError: param_names = [pn.name for pn in self.current_function.params] if node.lex in param_names: for n in param_names: @@ -1176,6 +1169,17 @@ def visit(self, node, scope): else: return self.ids[node.lex] + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(int(node.lex))) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + scope.ret_expr = instance + @visitor.when(ConstantStringNode) def visit(self, node, scope): ############################### diff --git a/src/main.py b/src/main.py index fc47eba89..f6ff96b8e 100644 --- a/src/main.py +++ b/src/main.py @@ -15,6 +15,8 @@ from compiler.visitors.type_builder import TypeBuilder from compiler.visitors.type_checker import TypeChecker from compiler.visitors.type_inferencer import TypeInferencer +from compiler.visitors.cool2cil import COOLToCILVisitor +from compiler.visitors.cil_formatter import PrintCILVisitor def main(args): @@ -69,21 +71,28 @@ def main(args): if checker.errors: exit(1) - # Inferencing Autotype - inferencer = TypeInferencer(context, manager) - inferencer.visit(ast, scope) - for e in inferencer.errors: - print(f"{pos} - {type(e).__name__}: {str(e)}") - if inferencer.errors: - exit(1) - - # Last check without autotypes - checker = TypeChecker(context, manager) - checker.visit(ast) - for (e, pos) in checker.errors: - print(f"{pos} - {type(e).__name__}: {str(e)}") - if checker.errors: - exit(1) + # # Inferencing Autotype + # inferencer = TypeInferencer(context, manager) + # inferencer.visit(ast, scope) + # for e in inferencer.errors: + # print(f"{pos} - {type(e).__name__}: {str(e)}") + # if inferencer.errors: + # exit(1) + + # # Last check without autotypes + # checker = TypeChecker(context, manager) + # checker.visit(ast) + # for (e, pos) in checker.errors: + # print(f"{pos} - {type(e).__name__}: {str(e)}") + # if checker.errors: + # exit(1) + + # mips to cil + cil_visitor = COOLToCILVisitor(context) + cil_ast = cil_visitor.visit(ast, scope) + + cil_formatter = PrintCILVisitor() + print(cil_formatter.visit(cil_ast)) text = """ From 534ab61fbe84a99c422dd5a8aa9e648cd3a33077 Mon Sep 17 00:00:00 2001 From: kikexd Date: Fri, 25 Feb 2022 15:07:00 -0500 Subject: [PATCH 102/138] fix bugs --- src/compiler/visitors/cil_formatter.py | 8 ++++++++ src/compiler/visitors/cool2cil.py | 28 ++++++++++++++------------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py index ca9cfc44c..c474bdec4 100644 --- a/src/compiler/visitors/cil_formatter.py +++ b/src/compiler/visitors/cil_formatter.py @@ -166,6 +166,10 @@ def visit(self, node): def visit(self, node): return f"{node.dest} = Load {node.msg}" + @visitor.when(NameNode) + def visit(self, node): + return f"{node.dest} = NAME {node.name}" + @visitor.when(TypeNameNode) def visit(self, node): return f"{node.dest} = TYPENAME {node.source}" @@ -189,3 +193,7 @@ def visit(self, node): @visitor.when(ErrorNode) def visit(self, node): return f"ERROR {node.data_node}" + + @visitor.when(EqualStrNode) + def visit(self, node): + return f"{node.dest} = {node.left} == {node.right}" diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 8354d19e6..1f0e1be29 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -805,11 +805,11 @@ def visit(self, node, scope): # sorting the branches order = [] - for b in node.branches: + for b in node.branch_list: count = 0 - t1 = self.context.get_type(b.type) - for other in node.branches: - t2 = self.context.get_type(other.type) + t1 = self.context.get_type(b.typex) + for other in node.branch_list: + t2 = self.context.get_type(other.typex) count += t2.conforms_to(t1) order.append((count, b)) order.sort(key=lambda x: x[0]) @@ -818,11 +818,11 @@ def visit(self, node, scope): old = {} for idx, (_, b) in enumerate(order): labels.append(self.register_label(f"{idx}_label")) - h = self.buildHierarchy(b.type) + h = self.buildHierarchy(b.typex) if not h: self.register_instruction(cil.GotoNode(labels[-1].label)) break - h.add(b.type) + h.add(b.typex) for s in old: h -= s for t in h: @@ -837,7 +837,7 @@ def visit(self, node, scope): # Raise runtime error if no Goto was executed data_node = self.register_data( - f"({node.token.pos[0] + 1 + len(node.branches)},{node.token.pos[1] - 5}) - RuntimeError: Execution of a case statement without a matching branch\n" + f"({node.token.pos[0] + 1 + len(node.branch_list)},{node.token.pos[1] - 5}) - RuntimeError: Execution of a case statement without a matching branch\n" ) self.register_instruction(cil.ErrorNode(data_node)) @@ -862,7 +862,7 @@ def visit(self, node, scope): ############################### ret = self.register_local(VariableInfo("block_node_value", None)) - ret_value = self.visit(node.expr, scope) + ret_value = self.visit(node.expression, scope) self.register_instruction(cil.AssignNode(ret, ret_value)) @@ -1130,19 +1130,21 @@ def visit(self, node, scope): ############################### ret = self.define_internal_local() - if node.type == SelfType().name: + if node.computed_type.name == SelfType().name: value = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(value, node.type)) + self.register_instruction(cil.TypeOfNode(value, node.computed_type.name)) self.register_instruction(cil.AllocateNode(value, ret)) - elif node.type == "Int" or node.type == "Bool": + elif node.computed_type.name == "Int" or node.computed_type.name == "Bool": self.register_instruction(cil.ArgNode(0)) - elif node.type == "String": + elif node.computed_type.name == "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.init_name(node.type), ret)) + self.register_instruction( + cil.StaticCallNode(self.init_name(node.computed_type.name), ret) + ) return ret @visitor.when(VariableNode) From c9acd8efe681f84561e7ebf56403ea2a11e6cbe2 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Fri, 25 Feb 2022 16:30:11 -0500 Subject: [PATCH 103/138] fix imports and clean code --- src/compiler/lexer/lex.py | 30 -------- src/compiler/visitors/type_builder.py | 32 ++++----- src/compiler/visitors/type_checker.py | 91 ++++++------------------ src/compiler/visitors/type_collector.py | 17 +++-- src/compiler/visitors/type_inferencer.py | 20 +++--- src/compiler/visitors/utils.py | 14 ++++ src/main.py | 41 ----------- 7 files changed, 67 insertions(+), 178 deletions(-) create mode 100644 src/compiler/visitors/utils.py diff --git a/src/compiler/lexer/lex.py b/src/compiler/lexer/lex.py index fc3e65fc1..ddc208530 100644 --- a/src/compiler/lexer/lex.py +++ b/src/compiler/lexer/lex.py @@ -1,5 +1,3 @@ -from distutils.log import debug -from compiler.cmp.pycompiler import EOF import ply.lex as lex from ..cmp.grammar import * @@ -338,7 +336,6 @@ def t_error(self, t): # Build the lexer def build(self, **kwargs): - # self.lexer = lex.lex(module=self, **kwargs) self.lexer = lex.lex( module=self, errorlog=lex.NullLogger(), debug=False, **kwargs ) @@ -368,30 +365,3 @@ def tokenize(self, data): errors.append("(0, 0) - SyntacticError: Unexpected token EOF") token_list.append(Token("$", G.EOF, self.lexer.eof)) return token_list, errors - - -def pprint_tokens(tokens, get=False): - indent = 0 - pending = [] - result = "" - for token in tokens: - pending.append(token) - if token.token_type in {ocur, ccur, semi}: - if token.token_type == ccur: - indent -= 1 - if get: - result += ( - " " * indent - + " ".join(str(t.token_type) for t in pending) - + "\n" - ) - else: - print(" " * indent + " ".join(str(t.token_type) for t in pending)) - pending.clear() - if token.token_type == ocur: - indent += 1 - if get: - result += " ".join([str(t.token_type) for t in pending]) + "\n" - return result - else: - print(" ".join([str(t.token_type) for t in pending])) diff --git a/src/compiler/visitors/type_builder.py b/src/compiler/visitors/type_builder.py index b1051f8af..7b5fca136 100644 --- a/src/compiler/visitors/type_builder.py +++ b/src/compiler/visitors/type_builder.py @@ -1,6 +1,5 @@ from typing import List, Optional, Tuple -from numpy import void import compiler.visitors.visitor as visitor from ..cmp.ast import ( ProgramNode, @@ -17,6 +16,12 @@ SelfType, Type, ) +from .utils import ( + ALREADY_DEFINED, + MAIN_CLASS_ERROR, + MAIN_PROGRAM_ERROR, + SELF_ERROR, +) class TypeBuilder: @@ -33,7 +38,7 @@ def visit(self, node): pass @visitor.when(ProgramNode) - def visit(self, node: ProgramNode) -> void: + def visit(self, node: ProgramNode): for dec in node.declarations: self.visit(dec) @@ -57,20 +62,13 @@ def visit(self, node: FuncDeclarationNode): # Checking param name can't be self if n == "self": - self.errors.append( - ( - SemanticError( - "'self' cannot be the name of a formal parameter." - ), - namex.pos, - ) - ) + self.errors.append((SemanticError(SELF_ERROR), namex.pos)) # Generate valid parameter name if n in param_names: self.errors.append( ( - SemanticError(f"Formal parameter {n} is multiply defined."), + SemanticError(ALREADY_DEFINED % (n)), namex.pos, ) ) @@ -136,7 +134,7 @@ def visit(self, node: AttrDeclarationNode): if node.id == "self": self.errors.append( ( - SemanticError("'self' cannot be the name of an attribute."), + SemanticError(SELF_ERROR), node.idToken.pos, ) ) @@ -149,7 +147,7 @@ def visit(self, node: AttrDeclarationNode): except SemanticError as ex: self.errors.append( ( - SemanticError(f"Attribute {node.id} is multiply defined in class."), + SemanticError(ALREADY_DEFINED % (node.id)), node.idToken.pos, ) ) @@ -166,10 +164,6 @@ def check_main_class(self): try: typex = self.context.get_type("Main") if not any(method.name == "main" for method in typex.methods): - self.errors.append( - (SemanticError("Class Main must contain a method main"), (0, 0)) - ) + self.errors.append((SemanticError(MAIN_CLASS_ERROR), (0, 0))) except TypeError: - self.errors.append( - (SemanticError("Program must contain a class Main"), (0, 0)) - ) + self.errors.append((SemanticError(MAIN_PROGRAM_ERROR), (0, 0))) diff --git a/src/compiler/visitors/type_checker.py b/src/compiler/visitors/type_checker.py index ac9f52f1a..0be411444 100644 --- a/src/compiler/visitors/type_checker.py +++ b/src/compiler/visitors/type_checker.py @@ -1,7 +1,5 @@ -from ast import Call from typing import List, Optional, Tuple -from numpy import void import compiler.visitors.visitor as visitor from ..cmp.semantic import ( Context, @@ -10,8 +8,6 @@ Scope, SemanticError, ErrorType, - IntType, - BoolType, SelfType, AutoType, LCA, @@ -23,8 +19,6 @@ ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode, -) -from ..cmp.ast import ( AssignNode, CallNode, CaseNode, @@ -32,25 +26,19 @@ LoopNode, ConditionalNode, LetNode, -) -from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode -from ..cmp.ast import VoidNode, NotNode, NegNode -from ..cmp.ast import ( + ArithmeticNode, + ComparisonNode, + EqualNode, + VoidNode, + NotNode, + NegNode, ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode, ) - - -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".' -INVALID_TYPE = "SELF_TYPE is not valid" +from .utils import * class TypeChecker: @@ -73,7 +61,6 @@ def visit(self, node, scope=None): @visitor.when(ProgramNode) def visit(self, node: ProgramNode, scope: Optional[Scope] = None) -> Scope: - # print("Hola program node") scope = Scope() for declaration in node.declarations: self.visit(declaration, scope.create_child()) @@ -81,7 +68,6 @@ def visit(self, node: ProgramNode, scope: Optional[Scope] = None) -> Scope: @visitor.when(ClassDeclarationNode) def visit(self, node: ClassDeclarationNode, scope: Scope): - # print(f"Hola class {node.id}") self.current_type = self.context.get_type(node.id) scope.define_variable("self", SelfType(self.current_type)) attributes = self.current_type.all_attributes() @@ -94,26 +80,21 @@ def visit(self, node: ClassDeclarationNode, scope: Scope): @visitor.when(AttrDeclarationNode) def visit(self, node: AttrDeclarationNode, scope: Scope): - # print(f"Hola attribute {node.id}") var = scope.find_variable(node.id) attr_type = var.type if node.expr is not None: - # print("Hola adentro") computed_type = self.visit(node.expr, scope) if not self.check_conformance(computed_type, attr_type): self.errors.append( ( - TypeError( - f"Inferred type {computed_type.name} of initialization of attribute {node.id} does not conform to declared type {node.type}." - ), + TypeError(INCOMPATIBLE_TYPES % (computed_type.name, node.type)), node.token.pos, ) ) @visitor.when(FuncDeclarationNode) def visit(self, node: FuncDeclarationNode, scope: Scope): - # print(f"Hola func {node.id}") self.current_method = self.current_type.get_method(node.id) # checking overwriting @@ -123,7 +104,7 @@ def visit(self, node: FuncDeclarationNode, scope: Scope): self.errors.append( ( SemanticError( - f"Incompatible number of formal parameters in redefined method {node.id}." + WRONG_SIGNATURE % (node.id, self.current_type.name) ), node.token.pos, ) @@ -134,7 +115,7 @@ def visit(self, node: FuncDeclarationNode, scope: Scope): self.errors.append( ( SemanticError( - f"In redefined method {node.id}, parameter type {t.name} is different from original type {method.param_types[i].name}." + WRONG_SIGNATURE % (node.id, self.current_type.name) ), node.token.pos, ) @@ -145,7 +126,7 @@ def visit(self, node: FuncDeclarationNode, scope: Scope): self.errors.append( ( SemanticError( - f"In redefined method {node.id}, return type {self.current_method.return_type.name} is different from original return type {method.return_type.name}." + WRONG_SIGNATURE % (node.id, self.current_type.name) ), node.typeToken.pos, ) @@ -179,7 +160,8 @@ def visit(self, node: FuncDeclarationNode, scope: Scope): self.errors.append( ( TypeError( - f"Inferred return type {computed_type.name} of method {node.id} does not conform to declared return type {rtype.name}." + INCOMPATIBLE_TYPES + % (computed_type.name, self.current_method.return_type.name) ), node.typeToken.pos, ) @@ -187,17 +169,17 @@ def visit(self, node: FuncDeclarationNode, scope: Scope): @visitor.when(AssignNode) def visit(self, node: AssignNode, scope: Scope) -> Type: - # print(f"Hola assignment {node.id}") if node.id == "self": - self.errors.append( - (SemanticError("Cannot assign to 'self'."), node.idToken.pos) - ) + self.errors.append((SemanticError(SELF_IS_READONLY), node.idToken.pos)) # checking variable is defined var = scope.find_variable(node.id) if var is None: self.errors.append( - (NameError(f"Undeclared identifier {node.id}."), node.idToken.pos) + ( + NameError(VARIABLE_NOT_DEFINED % (node.id, self.current_type.name)), + node.idToken.pos, + ) ) var = scope.define_variable(node.id, ErrorType()) @@ -215,62 +197,46 @@ def visit(self, node: AssignNode, scope: Scope) -> Type: @visitor.when(CallNode) def visit(self, node: CallNode, scope: Scope): - # print(f"Hola callNode {node.id}") # Evaluate object obj_type = self.visit(node.obj, scope) - # print("step 1") # Check object type conforms to cast type cast_type = obj_type if not node.type == "": - # print("step 2") try: cast_type = self.context.get_type(node.type) - # print("step 3") if isinstance(cast_type, AutoType): - # print("step 4") - raise SemanticError( - "AUTO_TYPE can't be the type on this type of dispatch" - ) + raise SemanticError(AUTOTYPE_ERROR) if isinstance(cast_type, SelfType): - # print("step 5") cast_type = SelfType(self.current_type) except (SemanticError, TypeError) as ex: - # print("step 6") cast_type = ErrorType() self.errors.append((ex, node.typeToken.pos)) - # print(obj_type, cast_type) if not self.check_conformance(obj_type, cast_type): - # print("step 7") self.errors.append( ( TypeError(INCOMPATIBLE_TYPES % (obj_type.name, cast_type.name)), node.typeToken.pos, ) ) - # print("aqui estoy") # if the obj that is calling the function is autotype, let it pass if isinstance(cast_type, AutoType): - # print("step 8") node.computed_type = cast_type return cast_type if isinstance(cast_type, SelfType): - # print("step 9") cast_type = self.current_type # Check this function is defined for cast_type try: method = cast_type.get_method(node.id) - # print("step 10") # Check equal number of parameters if not len(node.args) == len(method.param_types): - # print("step 11") self.errors.append( ( SemanticError( - f"Method {method.name} called with wrong number of arguments. " + INVALID_OPERATION % (method.name, cast_type.name) ), node.token.pos, ) @@ -281,9 +247,7 @@ def visit(self, node: CallNode, scope: Scope): # Check conformance to parameter types for i, arg in enumerate(node.args): computed_type = self.visit(arg, scope) - # print("step 12 - ", i) if not self.check_conformance(computed_type, method.param_types[i]): - # print("step 13 - ", i) self.errors.append( ( TypeError( @@ -298,15 +262,13 @@ def visit(self, node: CallNode, scope: Scope): rtype = method.return_type if isinstance(rtype, SelfType): rtype = obj_type - # print("step 14") node.computed_type = rtype return rtype except SemanticError: - # print("step 15") self.errors.append( ( - AttributeError(f"Dispatch to undefined method {node.id}."), + AttributeError(METHOD_NOT_DEFINED % (node.id)), node.token.pos, ) ) @@ -339,9 +301,7 @@ def visit(self, node: CaseNode, scope: Scope): if size == len(decTypes): self.errors.append( ( - SemanticError( - f"Duplicate branch {branch.typex} in case statement." - ), + SemanticError(CASE_BRANCH_ERROR % (branch.typex)), branch.typexToken.pos, ) ) @@ -370,7 +330,6 @@ def visit(self, node: CaseNode, scope: Scope): @visitor.when(BlockNode) def visit(self, node: BlockNode, scope: Scope): - # print(f"Hola block {node.token.pos}") nscope = scope.create_child() # Check expressions @@ -531,11 +490,7 @@ def visit(self, node: NotNode, scope: Scope): @visitor.when(NegNode) def visit(self, node: NegNode, scope: Scope): - # if isinstance(node.expr, InstantiateNode): - # print(node.expr.token) - typex = self.visit(node.expr, scope) - # print(typex) if not typex.conforms_to(self.int_type): self.errors.append( ( @@ -584,7 +539,7 @@ def visit(self, node: InstantiateNode, scope: Scope): try: typex = self.context.get_type(node.lex) if isinstance(typex, AutoType): - raise SemanticError("AUTO_TYPE can't be instanciate with new") + raise SemanticError(AUTOTYPE_ERROR) if isinstance(typex, SelfType): typex = SelfType(self.current_type) except (SemanticError, TypeError) as ex: diff --git a/src/compiler/visitors/type_collector.py b/src/compiler/visitors/type_collector.py index 82dc6672d..b471817c1 100644 --- a/src/compiler/visitors/type_collector.py +++ b/src/compiler/visitors/type_collector.py @@ -1,7 +1,6 @@ from typing import Dict, List, Optional, Tuple -from numpy import void -from zmq import TYPE -from compiler.cmp.utils import Token, pprint +from compiler.cmp.utils import Token +from compiler.visitors.utils import AUTOTYPE_ERROR import compiler.visitors.visitor as visitor from ..cmp.semantic import ( SemanticError, @@ -49,12 +48,12 @@ def visit(self, node: ProgramNode): node.declarations = self.order_types(node) @visitor.when(ClassDeclarationNode) - def visit(self, node: ClassDeclarationNode) -> void: + def visit(self, node: ClassDeclarationNode): # flag is set to True if the class is succesfully added to the context flag = False try: if node.id == "AUTO_TYPE": - raise SemanticError("Name of class can't be autotype") + raise SemanticError(AUTOTYPE_ERROR) self.context.create_type(node.id, node.tokenId.pos) flag = True self.parent[node.id] = node.parent @@ -71,7 +70,7 @@ def visit(self, node: ClassDeclarationNode) -> void: except SemanticError: pass - def define_built_in_types(self) -> void: + def define_built_in_types(self): objectx = ObjectType() iox = IOType() intx = IntType() @@ -104,7 +103,7 @@ def define_built_in_types(self) -> void: built_in_types.extend([objectx, iox, stringx, intx, boolx, self_type, autotype]) - def check_parents(self) -> void: + def check_parents(self): for item in self.parent.keys(): item_type = self.context.get_type(item) if self.parent[item] is None: @@ -128,7 +127,7 @@ def check_parents(self) -> void: self.errors.append((ex, self.parent[item].pos)) item_type.set_parent(built_in_types[0]) - def check_cyclic_inheritance(self) -> void: + def check_cyclic_inheritance(self): flag = [] def find(item: Type) -> int: @@ -137,7 +136,7 @@ def find(item: Type) -> int: return i return len(flag) - def check_path(idx: int, item: Type) -> void: + def check_path(idx: int, item: Type): while True: flag.append(item) parent = item.parent diff --git a/src/compiler/visitors/type_inferencer.py b/src/compiler/visitors/type_inferencer.py index ce38f712e..9f913699e 100644 --- a/src/compiler/visitors/type_inferencer.py +++ b/src/compiler/visitors/type_inferencer.py @@ -1,8 +1,8 @@ -from logging import Manager from typing import List, Tuple import compiler.visitors.visitor as visitor from ..cmp.semantic import ( Context, + InferencerManager, Method, Scope, SemanticError, @@ -17,8 +17,6 @@ ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode, -) -from ..cmp.ast import ( AssignNode, CallNode, CaseNode, @@ -26,26 +24,26 @@ LoopNode, ConditionalNode, LetNode, -) -from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode -from ..cmp.ast import VoidNode, NotNode, NegNode -from ..cmp.ast import ( + ArithmeticNode, + ComparisonNode, + EqualNode, + VoidNode, + NotNode, + NegNode, ConstantNumNode, ConstantStringNode, ConstantBoolNode, VariableNode, InstantiateNode, ) - - -AUTOTYPE_ERROR = "Incorrect use of AUTO_TYPE" +from .utils import AUTOTYPE_ERROR class TypeInferencer: def __init__(self, context, manager): self.context: Context = context self.errors: List[Tuple[Exception, Tuple[int, int]]] = [] - self.manager: Manager = manager + self.manager: InferencerManager = manager self.current_type: Type = None self.current_method: Method = None diff --git a/src/compiler/visitors/utils.py b/src/compiler/visitors/utils.py new file mode 100644 index 000000000..a109af42d --- /dev/null +++ b/src/compiler/visitors/utils.py @@ -0,0 +1,14 @@ +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".' +INVALID_TYPE = "SELF_TYPE is not valid" +AUTOTYPE_ERROR = "Incorrect use of AUTO_TYPE" +METHOD_NOT_DEFINED = 'Dispatch to undefined method "%s".' +CASE_BRANCH_ERROR = 'Duplicate branch "%s" in case statement.' +SELF_ERROR = 'Cannot use "self" as identifier' +ALREADY_DEFINED = '"%s" is already defined' +MAIN_CLASS_ERROR = "Class Main must contain a method main" +MAIN_PROGRAM_ERROR = "Program must contain a class Main" diff --git a/src/main.py b/src/main.py index fc47eba89..9ebff26b8 100644 --- a/src/main.py +++ b/src/main.py @@ -1,9 +1,5 @@ -# import streamlit as st - -from black import err from compiler.cmp.grammar import G -# from compiler.lexer.lexer import tokenize_text, pprint_tokens from compiler.lexer.lex import CoolLexer from sys import exit @@ -86,43 +82,6 @@ def main(args): exit(1) -text = """ -class Main inherits IO { - number: Int <- 5; - - main () : Object { - testing_fibonacci(number) - }; - - testing_fibonacci(n: Int) : IO {{ - out_string("Iterative Fibonacci : "); - out_int(iterative_fibonacci(5)); - out_string("\\n"); - - out_string("Recursive Fibonacci : "); - out_int(recursive_fibonacci(5)); - out_string("\\n"); - }}; - - recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { - if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi - }; - - iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { - let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { - while i < n loop - let temp: Int <- n2 in { - n2 <- n2 + n1; - n1 <- temp; - i <- i + 1; - } - pool; - n2; - } - }; -}; -""" - if __name__ == "__main__": import argparse From 6441d71fce7a28da18b4a0ac0bbefe2e623d0b83 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Fri, 25 Feb 2022 16:41:19 -0500 Subject: [PATCH 104/138] add ply to requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 9eb0cad1a..cba16ee2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pytest pytest-ordering +ply From 29667e85942fd00837e6b50bedfe3098be4af5dc Mon Sep 17 00:00:00 2001 From: dgd Date: Fri, 25 Feb 2022 23:39:06 -0500 Subject: [PATCH 105/138] mips visitor --- src/compiler/cmp/mips_ast.py | 10 + src/compiler/visitors/cil2mips.py | 330 ++++++++++++++++++++++---- src/compiler/visitors/mips_printer.py | 167 +++++++++++++ src/tests.py | 62 +++-- tests/codegen_test.py | 12 +- tests/conftest.py | 3 +- tests/lexer_test.py | 9 +- tests/parser_test.py | 9 +- tests/semantic_test.py | 13 +- tests/utils/__init__.py | 2 +- tests/utils/utils.py | 100 +++++--- 11 files changed, 592 insertions(+), 125 deletions(-) create mode 100644 src/compiler/visitors/mips_printer.py diff --git a/src/compiler/cmp/mips_ast.py b/src/compiler/cmp/mips_ast.py index 5d6ed833e..72cb05921 100644 --- a/src/compiler/cmp/mips_ast.py +++ b/src/compiler/cmp/mips_ast.py @@ -23,6 +23,8 @@ def __init__(self, name): # ***********************Utils*********************** MAIN_FUNCTION_NAME = "function_main_at_Main" +VIRTUAL_TABLE = "virtual_table" +TYPE_LIST = "type_list" def push_to_stack(register: Register): @@ -79,6 +81,8 @@ class TypeNode(Node): def __init__(self, label): self.label = label self.methods = [] + self.attributes = [] + self.pos = -1 class InstructionNode(Node): @@ -221,3 +225,9 @@ class RegisterRelativeLocation: def __init__(self, register, offset): self.register = register self.offset = offset + + +class LabelRelativeLocation: + def __init__(self, label, offset): + self._label = label + self._offset = offset diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index b17e2217b..169adb947 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -19,6 +19,7 @@ def __init__(self): self.current_function: mips.FunctionNode = None self.pushed_args = 0 self.label_count = 0 + self.type_count = 0 def register_type(self, type_name): type_node = mips.TypeNode(self.to_type_label_address(type_name)) @@ -46,9 +47,12 @@ def to_type_label_address(self, type_name): def to_data_label_address(self, data_name): return f"data_{data_name}" + + def to_label_local_address(self, label_name): + return f"{self.current_function.label}_{label_name}" - def to_function_label_address(self, function_name): - return f"{function_name}" + def to_function_label_address(self,type_name, function_name): + return f"{function_name}_at_{type_name}" def make_callee_init_instructions(self, function_node: mips.FunctionNode): push_fp = mips.push_to_stack(mips.FP) @@ -85,6 +89,8 @@ def get_var_location(self, name): except ValueError: return self.get_local_var_index()(name) + def get_type_size(self, type_name): + return len(self.types[type_name].attributes) * 4 class BaseCILToMIPSVisitor(BaseCILToMIPSVisitor): @visitor.on(Node) @@ -107,7 +113,10 @@ def visit(self, node: ProgramNode) -> mips.ProgramNode: @visitor.when(TypeNode) def visit(self, node: TypeNode): type_node = self.register_type(node.name) - type_node.methods = [method[1] for method in node.methods] + type_node.methods = node.methods + type_node.attributes = node.attributes + type_node.pos = self.type_count + self.type_count += 1 @visitor.when(DataNode) def visit(self, node: DataNode): @@ -128,13 +137,6 @@ def visit(self, node: FunctionNode): function_node.instrucctions = total_instructions self.current_function = None - # @visitor.when(ParamNode) - # def visit(self, node): - # pass - - # @visitor.when(LocalNode) - # def visit(self, node): - # pass @visitor.when(AssignNode) def visit(self, node): @@ -153,9 +155,6 @@ def visit(self, node): return instructions - # @visitor.when(ArithmeticNode) - # def visit(self, node): - # pass @visitor.when(PlusNode) def visit(self, node): @@ -248,7 +247,7 @@ def visit(self, node): else: instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) - instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("less_equal"))) + instructions.append(mips.JumpAndLinkNode("less_equal")) instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) return instructions @@ -267,7 +266,7 @@ def visit(self, node): else: instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) - instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("less"))) + instructions.append(mips.JumpAndLinkNode("less")) instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) return instructions @@ -290,7 +289,7 @@ def visit(self, node): else: instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) - instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("equals"))) + instructions.append(mips.JumpAndLinkNode("equals")) instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) @@ -302,13 +301,10 @@ def visit(self, node): instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) - instructions.append(mips.JumpAndLinkNode(self.to_function_label_address("equal_str"))) + instructions.append(mips.JumpAndLinkNode("equal_str")) instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) return instructions - # @visitor.when(VoidNode) - # def visit(self, node): - # pass @visitor.when(NotNode) def visit(self, node): @@ -346,32 +342,53 @@ def visit(self, node): pass @visitor.when(AllocateNode) - def visit(self, node): - pass + def visit(self, node:AllocateNode): + instructions = [] + type_size = self.get_type_size(node.type) + instructions.append(mips.LoadInmediateNode(mips.A0, type_size)) + instructions.append(mips.LoadInmediateNode(mips.V0, 9)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + return instructions + + + @visitor.when(ArrayNode) def visit(self, node): pass @visitor.when(TypeOfNode) - def visit(self, node): - pass + def visit(self, node:TypeOfNode): + instructions = [] + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.obj))) + + instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, 0))) + instructions.append(mips.StoreWordNode(mips.A1, self.get_var_location(node.dest))) + + return instructions + @visitor.when(NameNode) def visit(self, node): - pass + instructions = [] + + instructions.append(mips.LoadAddressNode(mips.A0, mips.TYPE_LIST)) + + tp_number = self.types[node.name].index + instructions.append(mips.AddInmediateUnsignedNode(mips.a0, mips.a0, tp_number*4)) + instructions.append(mips.LoadWordNode(mips.a0, mips.RegisterRelativeLocation(mips.a0, 0))) + + instructions.append(mips.StoreWordNode(mips.a0, self.get_var_location(node.dest))) + + return instructions @visitor.when(LabelNode) def visit(self, node:LabelNode): - pass + return [mips.LabelNode(self.to_label_local_address(node.label))] + - @visitor.when(GotoNode) - def visit(self, node): - pass - - @visitor.when(GotoIfNode) - def visit(self, node): - pass @visitor.when(StaticCallNode) def visit(self, node:StaticCallNode): @@ -384,8 +401,29 @@ def visit(self, node:StaticCallNode): return instructions @visitor.when(DynamicCallNode) - def visit(self, node): - pass + def visit(self, node:DynamicCallNode): + instructions = [] + caller_type = self.types[node.computed_type] + index = list(caller_type.methods).index(node.method) + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.type))) + + instructions.append(mips.LoadAddressNode(mips.A1, mips.VIRTUAL_TABLE)) + instructions.append(mips.ShiftLeftLogicalNode(mips.A2, mips.A1, 2)) + instructions.append(mips.AddUnsignedNode(mips.A1, mips.A1, mips.A2)) + instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0))) + instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 8))) + instructions.append(mips.AddInmediateUnsignedNode(mips.A1, mips.A1, index * 4)) + instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0))) + instructions.append(mips.JumpRegisterAndLinkNode(mips.A1)) + + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + if self._pushed_args > 0: + instructions.append(mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4)) + self.pushed_args = 0 + + return instructions + @visitor.when(ArgNode) def visit(self, node): @@ -402,19 +440,58 @@ def visit(self, node): @visitor.when(ReturnNode) def visit(self, node): - pass + instructions = [] + if isinstance(node.value, int): + instructions.append(mips.LoadInmediateNode(mips.V0, node.value)) + elif isinstance(node.value, VoidNode): + instructions.append(mips.LoadInmediateNode(mips.V0, 0)) + else: + instructions.append(mips.LoadWordNode(mips.V0, self.get_var_location(node.value))) + + return instructions @visitor.when(LoadNode) - def visit(self, node): - pass + def visit(self, node:LoadNode): + instructions = [] + + location = mips.LabelRelativeLocation(self.data[node.msg.name].label, 0) + instructions.append(mips.LoadAddressNode(mips.A0,location )) + instructions.append(mips.StoreWordNode(mips.A0, self.get_var_location(node.dest))) + + return instructions + @visitor.when(LengthNode) def visit(self, node): - pass + instructions = [] + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.source))) + + instructions.append(mips.JumpAndLinkNode("len")) + + 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 @visitor.when(ConcatNode) def visit(self, node): - pass + instructions = [] + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.prefix))) + + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.suffix))) + + instructions.append(mips.LoadWordNode(mips.A2, self.get_var_location(node.lenght))) + + instructions.append(mips.JumpAndLinkNode("concat")) + + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + return instructions @visitor.when(PrefixNode) def visit(self, node): @@ -422,20 +499,177 @@ def visit(self, node): @visitor.when(SubstringNode) def visit(self, node): - pass + instructions = [] + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.str_value))) + + if type(node.index) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.index)) + else: + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.index))) + + if type(node.length) == int: + instructions.append(mips.LoadInmediateNode(mips.A2, node.length)) + else: + instructions.append(mips.LoadWordNode(mips.A2, self.get_var_location(node.length))) + + instructions.append(mips.JumpAndLinkNode("substr")) + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + return instructions + - @visitor.when(ToStrNode) + @visitor.when(ReadStrNode) def visit(self, node): - pass + instructions = [] + instructions.append(mips.JumpAndLinkNode("read_str")) + + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + return instructions - @visitor.when(ReadNode) + + @visitor.when(PrintIntNode) def visit(self, node): - pass + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0, 1)) + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.value))) + instructions.append(mips.SyscallNode()) - @visitor.when(PrintNode) + return instructions + + @visitor.when(PrintStrNode) def visit(self, node): - pass + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0, 4)) + instructions.append(mips.LoadWordNode(mips.A0. self.get_var_location(node.value))) + instructions.append(mips.SyscallNode()) + return instructions @visitor.when(ErrorNode) def visit(self, node): - pass + 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, 10)) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(TypeNameNode) + def visit(self, node:TypeNameNode): + instructions = [] + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.source))) + instructions.append(mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0,0) )) + + instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + instructions.append(mips.LoadAddressNode(mips.A1, mips.TYPE_LIST)) + instructions.append(mips.AddUnsignedNode(mips.A0, mips.A0, mips.A1)) + instructions.append(mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0))) + + instructions.append(mips.StoreWordNode(mips.A0, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(ExitNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0, 10)) + instructions.append(mips.SyscallNode()) + + return instructions + + + @visitor.when(GetAttribNode) + def visit(self, node:GetAttribNode): + + instructions = [] + + 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 + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) + + tp = self._types[comp_type] + offset = 12 + tp.attributes.index(node.attr) * 4 + instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset))) + + instructions.append(mips.StoreWordNode(mips.A1, self.get_var_location(dest))) + + return instructions + + @visitor.when(SetAttribNode) + def visit(self, node:SetAttribNode): + + instructions = [] + + 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 + + tp = self._types[comp_type] + offset = 12 + tp.attributes.index(node.attr) * 4 + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) + + if type(node.value) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.value)) + else: + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.value))) + + instructions.append(mips.StoreWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset))) + + return instructions + + + @visitor.when(CopyNode) + def visit(self, node): + instructions = [] + + instructions.extend(mips.push_register(mips.A1)) + + instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.source))) + + instructions.append(mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A1, 4))) + instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + instructions.append(mips.JumpAndLinkNode("malloc")) + instructions.append(mips.MoveNode(mips.A2, mips.A0)) + instructions.append(mips.MoveNode(mips.A0, mips.A1)) + instructions.append(mips.MoveNode(mips.A1, mips.V0)) + instructions.append(mips.JumpAndLinkNode("copy")) + + instructions.extend(mips.pop_register(mips.A1)) + + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + return instructions + + @visitor.when(GotoIfNode) + def visit(self, node): + instructions = [] + + local_label = self.to_label_local_address(node.label) + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.condition))) + + instructions.append(mips.BranchOnNotEqualNode(mips.A0, mips.ZERO, local_label)) + + return instructions + + @visitor.when(GotoNode) + def visit(self, node): + local_label = self.to_label_local_address(node.label) + return [mips.JumpNode(local_label)] + + @visitor.when(ReadIntNode) + def visit(self, node): + instructions = [] + + instructions.append(mips.LoadInmediateNode(mips.V0, 5)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + + return instructions \ No newline at end of file diff --git a/src/compiler/visitors/mips_printer.py b/src/compiler/visitors/mips_printer.py new file mode 100644 index 000000000..e8d89f3db --- /dev/null +++ b/src/compiler/visitors/mips_printer.py @@ -0,0 +1,167 @@ +from ..cmp.mips_ast import * +import compiler.visitors.visitor as 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"{TYPE_LIST}:\n" + "\n".join( + [f"\t.word\t{tp.string_name_label}" for tp in node.types] + ) + proto_table = f"{VIRTUAL_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\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{-1}" + 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)}" diff --git a/src/tests.py b/src/tests.py index 751e62e33..4c65c6355 100644 --- a/src/tests.py +++ b/src/tests.py @@ -12,70 +12,70 @@ def run_pipeline(G, text): - print('=================== TEXT ======================') + print("=================== TEXT ======================") print(text) - print('================== TOKENS =====================') + print("================== TOKENS =====================") tokens = list(tokenize_text(text)) pprint_tokens(tokens) - print('=================== PARSE =====================') + print("=================== PARSE =====================") parser = LR1Parser(G) parse, operations = parser([t.token_type for t in tokens], get_shift_reduce=True) - print('\n'.join(repr(x) for x in parse)) - print('==================== AST ======================') + print("\n".join(repr(x) for x in parse)) + print("==================== AST ======================") ast = evaluate_reverse_parse(parse, operations, tokens) formatter = FormatVisitor() tree = formatter.visit(ast) print(tree) - print('============== COLLECTING TYPES ===============') + print("============== COLLECTING TYPES ===============") errors = [] collector = TypeCollector(errors) collector.visit(ast) context = collector.context - print('Errors:', errors) - print('Context:') + print("Errors:", errors) + print("Context:") print(context) - print('=============== BUILDING TYPES ================') + print("=============== BUILDING TYPES ================") builder = TypeBuilder(context, errors) builder.visit(ast) manager = builder.manager - print('Errors: [') + print("Errors: [") for error in errors: - print('\t', error) - print(']') - print('Context:') + print("\t", error) + print("]") + print("Context:") print(context) - print('=============== CHECKING TYPES ================') + print("=============== CHECKING TYPES ================") checker = TypeChecker(context, manager, []) scope = checker.visit(ast) - print('=============== INFERING TYPES ================') + print("=============== INFERING TYPES ================") temp_errors = [] inferencer = TypeInferencer(context, manager, temp_errors) inferencer.visit(ast, scope) - print('Errors: [') + print("Errors: [") for error in temp_errors: - print('\t', error) - print(']') - print('=============== LAST CHECK ================') + print("\t", error) + print("]") + print("=============== LAST CHECK ================") errors.extend(temp_errors) checker = TypeChecker(context, manager, errors) checker.visit(ast) - print('Errors: [') + print("Errors: [") for error in errors: - print('\t', error) - print(']') + print("\t", error) + print("]") formatter = FormatVisitor() tree = formatter.visit(ast) - #print(tree) + # print(tree) - print('=============== CIL CODE ================') - cil_visitor = COOLToCILVisitor(context) - cil_ast = cil_visitor.visit(ast,scope) - cil_formatter = PrintCILVisitor() + print("=============== CIL CODE ================") + cil_visitor = COOLToCILVisitor(context) + cil_ast = cil_visitor.visit(ast, scope) + cil_formatter = PrintCILVisitor() print(cil_formatter.visit(cil_ast)) return ast -text = ''' +text = """ class Main inherits IO { number: Int <- 5; @@ -89,9 +89,7 @@ class Main inherits IO { }; -''' +""" - - -ast = run_pipeline(G, text) \ No newline at end of file +ast = run_pipeline(G, text) diff --git a/tests/codegen_test.py b/tests/codegen_test.py index 48df768ff..393d7907a 100644 --- a/tests/codegen_test.py +++ b/tests/codegen_test.py @@ -2,8 +2,8 @@ import os from utils import compare_outputs -tests_dir = __file__.rpartition('/')[0] + '/codegen/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/codegen/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] # @pytest.mark.lexer # @pytest.mark.parser @@ -13,5 +13,9 @@ @pytest.mark.run(order=4) @pytest.mark.parametrize("cool_file", tests) def test_codegen(compiler_path, cool_file): - compare_outputs(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_input.txt',\ - tests_dir + cool_file[:-3] + '_output.txt') \ No newline at end of file + compare_outputs( + compiler_path, + tests_dir + cool_file, + tests_dir + cool_file[:-3] + "_input.txt", + tests_dir + cool_file[:-3] + "_output.txt", + ) diff --git a/tests/conftest.py b/tests/conftest.py index 1f44eeb72..0969ec84d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ import pytest import os + @pytest.fixture def compiler_path(): - return os.path.abspath('./coolc.sh') \ No newline at end of file + return os.path.abspath("./coolc.sh") diff --git a/tests/lexer_test.py b/tests/lexer_test.py index 2a27223d3..1e2a36a0d 100644 --- a/tests/lexer_test.py +++ b/tests/lexer_test.py @@ -2,12 +2,15 @@ import os from utils import compare_errors -tests_dir = __file__.rpartition('/')[0] + '/lexer/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/lexer/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] + @pytest.mark.lexer @pytest.mark.error @pytest.mark.run(order=1) @pytest.mark.parametrize("cool_file", tests) def test_lexer_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file + compare_errors( + compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + "_error.txt" + ) diff --git a/tests/parser_test.py b/tests/parser_test.py index 129c0f20a..a1ecc8f9e 100644 --- a/tests/parser_test.py +++ b/tests/parser_test.py @@ -2,12 +2,15 @@ import os from utils import compare_errors -tests_dir = __file__.rpartition('/')[0] + '/parser/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/parser/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] + @pytest.mark.parser @pytest.mark.error @pytest.mark.run(order=2) @pytest.mark.parametrize("cool_file", tests) def test_parser_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file + compare_errors( + compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + "_error.txt" + ) diff --git a/tests/semantic_test.py b/tests/semantic_test.py index cac9cd78b..c80b3552d 100644 --- a/tests/semantic_test.py +++ b/tests/semantic_test.py @@ -2,13 +2,18 @@ import os from utils import compare_errors, first_error_only_line -tests_dir = __file__.rpartition('/')[0] + '/semantic/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] +tests_dir = __file__.rpartition("/")[0] + "/semantic/" +tests = [(file) for file in os.listdir(tests_dir) if file.endswith(".cl")] + @pytest.mark.semantic @pytest.mark.error @pytest.mark.run(order=3) @pytest.mark.parametrize("cool_file", tests) def test_semantic_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt', \ - cmp=first_error_only_line) \ No newline at end of file + compare_errors( + compiler_path, + tests_dir + cool_file, + tests_dir + cool_file[:-3] + "_error.txt", + cmp=first_error_only_line, + ) diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 90f60fdd8..16281fe0b 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -1 +1 @@ -from .utils import * \ No newline at end of file +from .utils import * diff --git a/tests/utils/utils.py b/tests/utils/utils.py index 961cf7cbc..21712c8f9 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -2,16 +2,17 @@ import re -COMPILER_TIMEOUT = 'El compilador tarda mucho en responder.' -SPIM_TIMEOUT = 'El spim tarda mucho en responder.' -TEST_MUST_FAIL = 'El test %s debe fallar al compilar' -TEST_MUST_COMPILE = 'El test %s debe compilar' -BAD_ERROR_FORMAT = '''El error no esta en formato: (,) - : - o no se encuentra en la 3ra linea\n\n%s''' -UNEXPECTED_ERROR = 'Se esperaba un %s en (%d, %d). Su error fue un %s en (%d, %d)' -UNEXPECTED_OUTPUT = 'La salida de %s no es la esperada:\n%s\nEsperada:\n%s' +COMPILER_TIMEOUT = "El compilador tarda mucho en responder." +SPIM_TIMEOUT = "El spim tarda mucho en responder." +TEST_MUST_FAIL = "El test %s debe fallar al compilar" +TEST_MUST_COMPILE = "El test %s debe compilar" +BAD_ERROR_FORMAT = """El error no esta en formato: (,) - : + o no se encuentra en la 3ra linea\n\n%s""" +UNEXPECTED_ERROR = "Se esperaba un %s en (%d, %d). Su error fue un %s en (%d, %d)" +UNEXPECTED_OUTPUT = "La salida de %s no es la esperada:\n%s\nEsperada:\n%s" + +ERROR_FORMAT = r"^\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*-\s*(\w+)\s*:(.*)$" -ERROR_FORMAT = r'^\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*-\s*(\w+)\s*:(.*)$' def parse_error(error: str): merror = re.fullmatch(ERROR_FORMAT, error) @@ -25,67 +26,108 @@ def first_error(compiler_output: list, errors: list): oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) - assert line == oline and column == ocolumn and error_type == oerror_type,\ - UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + assert ( + line == oline and column == ocolumn and error_type == oerror_type + ), UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + def first_error_only_line(compiler_output: list, errors: list): line, column, error_type, _ = parse_error(errors[0]) oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) - assert line == oline and error_type == oerror_type,\ - UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + assert line == oline and error_type == oerror_type, UNEXPECTED_ERROR % ( + error_type, + line, + column, + oerror_type, + oline, + ocolumn, + ) def get_file_name(path: str): try: - return path[path.rindex('/') + 1:] + return path[path.rindex("/") + 1 :] except ValueError: return path -def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str, cmp=first_error, timeout=100): + +def compare_errors( + compiler_path: str, + cool_file_path: str, + error_file_path: str, + cmp=first_error, + timeout=100, +): try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + sp = subprocess.run( + ["bash", compiler_path, cool_file_path], + capture_output=True, + timeout=timeout, + ) return_code, output = sp.returncode, sp.stdout.decode() except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT assert return_code == 1, TEST_MUST_FAIL % get_file_name(cool_file_path) - fd = open(error_file_path, 'r') - errors = fd.read().split('\n') + fd = open(error_file_path, "r") + errors = fd.read().split("\n") fd.close() # checking the errors of compiler - compiler_output = output.split('\n') + compiler_output = output.split("\n") cmp(compiler_output[2:], errors) -SPIM_HEADER = r'''^SPIM Version .+ of .+ + +SPIM_HEADER = r"""^SPIM Version .+ of .+ Copyright .+\, James R\. Larus\. All Rights Reserved\. See the file README for a full copyright notice\. -(?:Loaded: .+\n)*''' -def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: str, output_file_path: str, timeout=100): +(?:Loaded: .+\n)*""" + + +def compare_outputs( + compiler_path: str, + cool_file_path: str, + input_file_path: str, + output_file_path: str, + timeout=100, +): try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + sp = subprocess.run( + ["bash", compiler_path, cool_file_path], + capture_output=True, + timeout=timeout, + ) assert sp.returncode == 0, TEST_MUST_COMPILE % get_file_name(cool_file_path) except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT - spim_file = cool_file_path[:-2] + 'mips' + spim_file = cool_file_path[:-2] + "mips" try: - fd = open(input_file_path, 'rb') - sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), capture_output=True, timeout=timeout) + fd = open(input_file_path, "rb") + sp = subprocess.run( + ["spim", "-file", spim_file], + input=fd.read(), + capture_output=True, + timeout=timeout, + ) fd.close() mo = re.match(SPIM_HEADER, sp.stdout.decode()) if mo: - output = mo.string[mo.end():] + output = mo.string[mo.end() :] except subprocess.TimeoutExpired: assert False, SPIM_TIMEOUT - fd = open(output_file_path, 'r') + fd = open(output_file_path, "r") eoutput = fd.read() fd.close() - assert output == eoutput, UNEXPECTED_OUTPUT % (spim_file, repr(output), repr(eoutput)) + assert output == eoutput, UNEXPECTED_OUTPUT % ( + spim_file, + repr(output), + repr(eoutput), + ) From 8bad1272e73f7618789cda72f571043771e8d7b2 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Sat, 26 Feb 2022 00:45:26 -0500 Subject: [PATCH 106/138] create package parser and remove unused functions fix imports --- doc/report.md | 105 ++++ src/compiler/cmp/ast.py | 5 +- src/compiler/cmp/automata.py | 69 --- src/compiler/cmp/evaluation.py | 36 -- src/compiler/cmp/nfa_dfa.py | 120 ---- src/compiler/cmp/regex.py | 105 ---- src/compiler/cmp/tools.py | 693 ----------------------- src/compiler/cmp/utils.py | 75 --- src/compiler/lexer/lex.py | 4 +- src/compiler/parser/__init__.py | 0 src/compiler/parser/parser.py | 200 +++++++ src/compiler/parser/utils.py | 95 ++++ src/compiler/visitors/type_builder.py | 5 +- src/compiler/visitors/type_checker.py | 30 +- src/compiler/visitors/type_inferencer.py | 28 +- src/main.py | 8 +- src/makefile | 17 +- 17 files changed, 450 insertions(+), 1145 deletions(-) create mode 100644 doc/report.md delete mode 100644 src/compiler/cmp/evaluation.py delete mode 100644 src/compiler/cmp/nfa_dfa.py delete mode 100644 src/compiler/cmp/regex.py delete mode 100644 src/compiler/cmp/tools.py create mode 100644 src/compiler/parser/__init__.py create mode 100644 src/compiler/parser/parser.py create mode 100644 src/compiler/parser/utils.py diff --git a/doc/report.md b/doc/report.md new file mode 100644 index 000000000..f23934f1d --- /dev/null +++ b/doc/report.md @@ -0,0 +1,105 @@ +# Cool Compiler + +## Autores ✒️ + +- **Carmen Irene Cabrera Rodríguez** - [cicr99](https://github.com/cicr99) +- **David Guaty Domínguez** - [Gu4ty](https://github.com/Gu4ty) +- **Enrique Martínez González** - [kikeXD](https://github.com/kikeXD) + + +## Requerimientos 📋 + +Para la ejecución de este proyecto necesita tener istalado: + +- Python3.7 o superior +- Las dependencias que se encuentran listadas en el archivo [requirements.txt](../requirements.txt) +- Spim para ejecutar programas MIPS32 + +Si lo desea, usted puede instalar todas las dependencias necesarias ejecutando en su terminal el siguiente comando, desde el directorio `/src`: + +```bash +make install +``` + +## Modo de uso + +Para compilar y ejecutar un archivo en COOL deberá ejecutar el siguiente comando en la terminal desde el directorio `/src`: + +```bash +make main .cl +``` + +Si usted no proporciona ningún archivo, se tomará por defecto el archivo `code.cl` presente en dicho directorio. El comando anterior es equivalente a: + +```bash +./coolc.sh .cl +spim -file .mips +``` + +## Arquitectura del compilador + +### Fases + +Las fases en que se divide el proceso de compilación se muestran a continuación y serán explicadas con más detalle en las secciones siguientes: +1. Lexer +2. Parsing +3. Recolección de tipos +4. Construcción de tipos +5. Chequeo / Inferencia de tipos +6. Traducción de COOL a CIL +7. Traducción de CIL a MIPS + +#### Lexer + + + +#### Recolección de tipos + +Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes pasos: + +- Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : Int, String, Bool, IO, Object; incluyendo la definición de sus métodos. Además se añaden como tipos SELF_TYPE, AUTO_TYPE. +- Recorrido por las declaraciones hechas en el programa recolectando los tipos creados. +- Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna Object como padre. +- Chequeo de herencia cíclica. En caso de detectar algún ciclo en la jerarquía de tipos, se reporta el error, y a la clase por la cual hubo problema se le asigna Object como padre, para continuar el análisis. +- Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. + +#### Construcción de tipos + +La construcción de tipos se desarrolla empleando la clase Type Builder. Esta se encarga de visitar los *features* de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. + +Además se encarga de chequear la existencia del tipo Main con su método main correspondiente, como es requerido en COOL. + +En esta clase también se hace uso de la clase Inferencer Manager que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO_TYPE se le asigna un *id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. + +#### Chequeo e Inferencia de tipos + +En primer lugar se utiliza la clase Type Checker para validar el correcto uso de los tipos definidos. Toma la instancia de clase Inferencer Manager utilizada en el Type Builder para continuar la asignación de id a otros elementos en el código que también pueden estar definidos como AUTO_TYPE, como es el caso de las variables definidas en la expresión Let. Las variables definidas en el Scope se encargarán de guardar el id asignado; en caso de que no se les haya asignado ninguno, el id será *None*. + +La instancia de Scope creada en el Type Checker, así como la de Inferencer Manager se pasarán al Type Inferencer para realizar la inferencia de tipos. + +Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El id asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. + +Sea una variable con id = i, que está definida como AUTO_TYPE y sea A el tipo estático que se ha de inferir: + +- `conforms_to[i]` guarda una lista con los tipos a los que debe conformarse A; note que esta lista contiene al menos al tipo Object. El hecho de que A se conforme a estos tipos, implica que todos ellos deben encontrarse en el camino de él a Object en el árbol de jerarquía de tipos. En caso contrario se puede decir que hubo algún error en la utilización del AUTO_TYPE para esta variable. Sea B el tipo más lejano a Object de los que aparecen en la lista. +- `conformed_by[i]` almacena una lista con los tipos que deben conformarse a A. Luego el menor ancestro común (*LCA - Lowest Common Ancestor*) de dichos tipos deberá conformarse a A. Note que este siempre existirá, pues en caso peor será Object, que es la raíz del árbol de tipos. Sea C el LCA de los tipos guardados. Note que si la lista está vacía, (que puede suceder) C será *None*. +- Como C se conforma a A y A se conforma B, tiene que ocurrir que C se conforma a B. En caso contrario, se reporta un uso incorrecto de AUTO_TYPE para esa variable. Todos los tipos en el camino entre B y C son válidos para inferir A; pues cumplen con todas las restricciones que impone el programa. En nuestro caso se elige C, que es el tipo más restringido, para la inferencia. En caso de que C sea *None* se toma B como tipo de inferencia. +- `infered_type[i]` guardará el tipo inferido una vez realizado el procedimiento anterior; mientra tanto su valor es *None*. + +La clase Inferencer Manager además, está equipada con métodos para actualizar las listas dado un id, y para realizar la inferencia dados los tipos almacenados. + +El Type Inferencer por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: + +1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un *ExpressionNode*, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el *manager*. +2. Infiere todos los tipos que pueda con la información recogida. +3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. + - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último rerrido asignando tipo Object a todos los AUTO_TYPES que no pudieron ser inferidos. + +> Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista *conforms_to* contiene a otro tipo distinto de Object o su lista *conformed_by* contiene al menos un tipo. + +Por último se realiza un nuevo recorrido del AST con el Type Checker para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los AUTO_TYPES sustituidos por el tipo inferido. + + +## Licencia + +Este proyecto se encuentra bajo la Licencia (MIT License) - ver el archivo [LICENSE.md](LICENSE.md) para más detalles. \ No newline at end of file diff --git a/src/compiler/cmp/ast.py b/src/compiler/cmp/ast.py index 3945b6ad0..14f22b5a1 100644 --- a/src/compiler/cmp/ast.py +++ b/src/compiler/cmp/ast.py @@ -1,7 +1,6 @@ -from typing import List, Optional, Tuple, Union - -from compiler.cmp.semantic import Type +from .semantic import Type from .utils import Token, emptyToken +from typing import List, Optional, Tuple, Union class Node: diff --git a/src/compiler/cmp/automata.py b/src/compiler/cmp/automata.py index 796ede39e..da7311f90 100644 --- a/src/compiler/cmp/automata.py +++ b/src/compiler/cmp/automata.py @@ -1,9 +1,3 @@ -try: - import pydot -except: - pass - - class State: def __init__(self, state, final=False, formatter=lambda x: str(x), shape="circle"): self.state = state @@ -169,66 +163,3 @@ def _visit(self, visited=None): 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 _repr_png_(self, name): - try: - return self.graph().write_png(f"{name}.png") - 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] - - -def empty_formatter(state): - return "" diff --git a/src/compiler/cmp/evaluation.py b/src/compiler/cmp/evaluation.py deleted file mode 100644 index b1d8d7b99..000000000 --- a/src/compiler/cmp/evaluation.py +++ /dev/null @@ -1,36 +0,0 @@ -from .pycompiler import EOF -from .tools 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) - elif operation == ShiftReduceParser.REDUCE: - production = next(right_parse) - _, 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] diff --git a/src/compiler/cmp/nfa_dfa.py b/src/compiler/cmp/nfa_dfa.py deleted file mode 100644 index 84b81d7dc..000000000 --- a/src/compiler/cmp/nfa_dfa.py +++ /dev/null @@ -1,120 +0,0 @@ -# import pydot - - -class NFA: - def __init__(self, states, finals, transitions, start=0): - self.states = states - self.start = start - self.finals = set(finals) - self.map = transitions - self.vocabulary = set() - self.transitions = {state: {} for state in range(states)} - - for (origin, symbol), destinations in transitions.items(): - assert hasattr(destinations, "__iter__"), "Invalid collection of states" - self.transitions[origin][symbol] = destinations - self.vocabulary.add(symbol) - - self.vocabulary.discard("") - - def epsilon_transitions(self, state): - assert state in self.transitions, "Invalid state" - try: - return self.transitions[state][""] - except KeyError: - return () - - # def graph(self): - # G = pydot.Dot(rankdir="LR", margin=0.1) - # G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) - - # for (start, tran), destinations in self.map.items(): - # tran = "ε" if tran == "" else tran - # G.add_node( - # pydot.Node( - # start, shape="circle", style="bold" if start in self.finals else "" - # ) - # ) - # for end in destinations: - # G.add_node( - # pydot.Node( - # end, shape="circle", style="bold" if end in self.finals else "" - # ) - # ) - # G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) - - # G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) - # return G - - # def _repr_svg_(self): - # try: - # return self.graph().create_svg().decode("utf8") - # except: - # pass - - # def _repr_png_(self): - # try: - # return self.graph() - # except: - # pass - - -class DFA(NFA): - # def graph(self): - # G = pydot.Dot(rankdir="LR", margin=0.1) - # G.add_node(pydot.Node("start", shape="plaintext", label="", width=0, height=0)) - - # for (start, tran), destinations in self.map.items(): - # tran = "ε" if tran == "" else tran - # G.add_node( - # pydot.Node( - # start, shape="circle", style="bold" if start in self.finals else "" - # ) - # ) - # for end in destinations: - # G.add_node( - # pydot.Node( - # end, shape="circle", style="bold" if end in self.finals else "" - # ) - # ) - # G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) - - # G.add_edge(pydot.Edge("start", self.start, label="", style="dashed")) - # return G - - # def _repr_svg_(self): - # try: - # return self.graph().create_svg().decode("utf8") - # except: - # pass - - # def _repr_png_(self): - # try: - # return self.graph() - # except: - # pass - - def __init__(self, states, finals, transitions, start=0): - assert all(isinstance(value, int) for value in transitions.values()) - assert all(len(symbol) > 0 for origin, symbol in transitions) - - transitions = {key: [value] for key, value in transitions.items()} - NFA.__init__(self, states, finals, transitions, start) - self.current = start - - def _move(self, symbol): - try: - self.current = self.transitions[self.current][symbol][0] - return True - except KeyError: - return False - - def _reset(self): - self.current = self.start - - def recognize(self, string): - self._reset() - for symbol in string: - if not self._move(symbol): - return False - return self.current in self.finals diff --git a/src/compiler/cmp/regex.py b/src/compiler/cmp/regex.py deleted file mode 100644 index d711462aa..000000000 --- a/src/compiler/cmp/regex.py +++ /dev/null @@ -1,105 +0,0 @@ -from .pycompiler import Grammar -from .utils import Token -from .tools import evaluate_parse -from .tools import metodo_predictivo_no_recursivo -from .tools import nfa_to_dfa -from .tools import ( - automata_union, - automata_concatenation, - automata_closure, - automata_minimization, -) -from .tools import get_printer -from .tools import ( - Node, - AtomicNode, - UnaryNode, - BinaryNode, - EpsilonNode, - SymbolNode, - ClosureNode, - UnionNode, - ConcatNode, -) -from pprint import pprint as pp -import pydot - - -class Regex: - def __init__(self, regular_exp): - - self.regular_exp = regular_exp - self.tokens = regex_tokenizer(regular_exp, G, False) - self.left_parse = parser(self.tokens) - self.ast = evaluate_parse(self.left_parse, self.tokens) - self.nfa = self.ast.evaluate() - self.dfa = nfa_to_dfa(self.nfa) - self.mini = automata_minimization(self.dfa) - self.automaton = self.mini - - def __call__(self, string): - return self.mini.recognize(string) - - def __repr__(self): - return "Regex: " + self.regular_exp - - -G = Grammar() - -E = G.NonTerminal("E", True) -T, F, A, X, Y, Z = G.NonTerminals("T F A X Y Z") -pipe, star, opar, cpar, symbol, epsilon = G.Terminals("| * ( ) symbol ε") - -# > PRODUCTIONS -E %= T + X, lambda h, s: s[2], None, lambda h, s: s[1] - -X %= pipe + T + X, lambda h, s: s[3], None, None, lambda h, s: UnionNode(h[0], s[2]) -X %= G.Epsilon, lambda h, s: h[0] - -T %= F + Y, lambda h, s: s[2], None, lambda h, s: s[1] - -Y %= F + Y, lambda h, s: s[2], None, lambda h, s: ConcatNode(h[0], s[1]) -Y %= G.Epsilon, lambda h, s: h[0] - -F %= A + Z, lambda h, s: s[2], None, lambda h, s: s[1] - -Z %= star + Z, lambda h, s: s[2], None, lambda h, s: ClosureNode(h[0]) -Z %= G.Epsilon, lambda h, s: h[0] - -A %= symbol, lambda h, s: SymbolNode(s[1]) -A %= opar + E + cpar, lambda h, s: s[2], None, None, None -A %= epsilon, lambda h, s: EpsilonNode(s[1]) - - -def regex_tokenizer(text, G, skip_whitespaces=True): - tokens = [] - jump = False - - # print(text) - - for char in text: - if skip_whitespaces and char.isspace(): - continue - elif char == "\\" and not jump: - jump = True - elif char == "*" and not jump: - tokens.append(Token("*", star)) - elif char == "(" and not jump: - tokens.append(Token("(", opar)) - elif char == ")" and not jump: - tokens.append(Token(")", cpar)) - elif char == "|" and not jump: - tokens.append(Token("|", pipe)) - elif char == "ε" and not jump: - tokens.append(Token("ε", epsilon)) - else: - tokens.append(Token(char, symbol)) - jump = False - - tokens.append(Token("$", G.EOF)) - return tokens - - -parser = metodo_predictivo_no_recursivo(G) - -printer = get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode) diff --git a/src/compiler/cmp/tools.py b/src/compiler/cmp/tools.py deleted file mode 100644 index 702e138db..000000000 --- a/src/compiler/cmp/tools.py +++ /dev/null @@ -1,693 +0,0 @@ -from .utils import ShiftReduceParser, ContainerSet, DisjointSet -from .pycompiler import Item, EOF -from .automata import State -from .nfa_dfa import DFA, NFA -import compiler.visitors.visitor as visitor -from itertools import islice - - -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() - - -class EpsilonNode(AtomicNode): - def evaluate(self): - return NFA(1, [0], {}) - - -class SymbolNode(AtomicNode): - def evaluate(self): - s = self.lex - return NFA( - 2, - [1], - { - (0, s): [1], - }, - ) - - -class ClosureNode(UnaryNode): - @staticmethod - def operate(value): - return automata_closure(value) - - -class UnionNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - return automata_union(lvalue, rvalue) - - -class ConcatNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - return automata_concatenation(lvalue, rvalue) - - -def upd_table(table, head, symbol, production): - if not head in table: - table[head] = {} - if not symbol in table[head]: - table[head][symbol] = [] - if production not in table[head][symbol]: - table[head][symbol].append(production) - return len(table[head][symbol]) <= 1 - - -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 KeyError: - 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.IsNonTerminal: - try: - beta_f = local_firsts[alpha, i] - except KeyError: - beta_f = local_firsts[alpha, i] = compute_local_first( - firsts, islice(alpha, i + 1, None) - ) - change |= follows[Y].update(beta_f) - if beta_f.contains_epsilon: - change |= follows[Y].update(follow_X) - - return follows - - -def build_parsing_table(G, firsts, follows): - M = {} - ok = True - - for production in G.Productions: - X = production.Left - alpha = production.Right - - for t in firsts[alpha]: - ok &= upd_table(M, X, t, production) - - if firsts[alpha].contains_epsilon: - for t in follows[X]: - ok &= upd_table(M, X, t, production) - - return M, ok - - -def deprecated_metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): - - if M is None: - if firsts is None: - firsts = compute_firsts(G) - if follows is None: - follows = compute_follows(G, firsts) - M, _ = build_parsing_table(G, firsts, follows) - - def parser(w): - - stack = [G.EOF, G.startSymbol] - cursor = 0 - output = [] - - while True: - top = stack.pop() - a = w[cursor] - - if top.IsEpsilon: - pass - elif top.IsTerminal: - assert top == a - if top == G.EOF: - break - cursor += 1 - else: - production = M[top][a][0] - output.append(production) - production = list(production.Right) - stack.extend(production[::-1]) - - return output - - return parser - - -def metodo_predictivo_no_recursivo(G, M=None): - parser = deprecated_metodo_predictivo_no_recursivo(G, M) - - def updated(tokens): - return parser([t.token_type for t in tokens]) - - return updated - - -def evaluate_parse(left_parse, tokens): - if not left_parse or not tokens: - return - - left_parse = iter(left_parse) - tokens = iter(tokens) - result = evaluate(next(left_parse), left_parse, tokens) - - assert isinstance(next(tokens).token_type, EOF) - return result - - -def evaluate(production, left_parse, tokens, inherited_value=None): - _, body = production - attributes = production.attributes - - synteticed = [None] * (len(body) + 1) - inherited = [None] * (len(body) + 1) - inherited[0] = inherited_value - - for i, symbol in enumerate(body, 1): - if symbol.IsTerminal: - assert inherited[i] is None - synteticed[i] = next(tokens).lex - else: - next_production = next(left_parse) - assert symbol == next_production.Left - attr = attributes[i] - if attr is not None: - inherited[i] = attr(inherited, synteticed) - synteticed[i] = evaluate(next_production, left_parse, tokens, inherited[i]) - - attr = attributes[0] - if attr is None: - return None - return attr(inherited, synteticed) - - -def expand(item, firsts): - next_symbol = item.NextSymbol - if next_symbol is None or not next_symbol.IsNonTerminal: - return [] - - lookaheads = ContainerSet() - for preview in item.Preview(): - lookaheads.hard_update(compute_local_first(firsts, preview)) - - assert not lookaheads.contains_epsilon - 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: - 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 - ) - - -def move(automaton, states, symbol): - moves = set() - for state in states: - try: - for item in automaton.transitions[state][symbol]: - moves.add(item) - except KeyError: - pass - return moves - - -def epsilon_closure(automaton, states): - pending = [s for s in states] # equivalente a list(states) pero me gusta así :p - closure = {s for s in states} # equivalente a set(states) pero me gusta así :p - - while pending: - state = pending.pop() - - l = move(automaton, [state], "") - for i in l: - if i in closure: - pass - else: - closure.add(i) - pending.append(i) - - return ContainerSet(*closure) - - -def nfa_to_dfa(automaton): - transitions = {} - - start = epsilon_closure(automaton, [automaton.start]) - start.id = 0 - start.is_final = any(s in automaton.finals for s in start) - states = [start] - - pending = [start] - while pending: - state = pending.pop() - - for symbol in automaton.vocabulary: - new_state = move(automaton, state, symbol) - new_state = epsilon_closure(automaton, new_state) - - if not new_state: - continue - - for s in states: - if s == new_state: - new_state = s - break - else: - new_state.id = len(states) - new_state.is_final = any(s in automaton.finals for s in new_state) - pending.append(new_state) - states.append(new_state) - - try: - transitions[state.id, symbol] - assert False, "Invalid DFA!!!" - except KeyError: - transitions[state.id, symbol] = new_state.id - - finals = [state.id for state in states if state.is_final] - dfa = DFA(len(states), finals, transitions) - return dfa - - -def distinguish_states(group, automaton, partition): - split = {} - vocabulary = tuple(automaton.vocabulary) - - transition = automaton.transitions - - for member in group: - for item in split.keys(): - for symbol in vocabulary: - q1 = None - q2 = None - try: - q1 = partition[transition[item][symbol][0]].representative - except KeyError: - q1 = None - try: - q2 = partition[transition[member.value][symbol][0]].representative - except KeyError: - q2 = None - if q1 != q2: - break - else: - split[item].append(member.value) - break - else: - split[member.value] = [member.value] - - return [group for group in split.values()] - - -def state_minimization(automaton): - partition = DisjointSet(*range(automaton.states)) - - ## partition = { NON-FINALS | FINALS } - finals = list(automaton.finals) - non_finals = [ - state for state in range(automaton.states) if not state in automaton.finals - ] - partition.merge(finals) - partition.merge(non_finals) - - while True: - new_partition = DisjointSet(*range(automaton.states)) - - ## Split each group if needed (use distinguish_states(group, automaton, partition)) - for group in partition.groups: - new_groups = distinguish_states(group, automaton, partition) - for new_group in new_groups: - new_partition.merge(new_group) - - if len(new_partition) == len(partition): - break - - partition = new_partition - - return partition - - -def automata_minimization(automaton): - partition = state_minimization(automaton) - - states = [s for s in partition.representatives] - - transitions = {} - for i, state in enumerate(states): - origin = state.value - for symbol, destinations in automaton.transitions[origin].items(): - new_dest = states.index(partition[destinations[0]].representative) - - try: - transitions[i, symbol] - assert False - except KeyError: - transitions[i, symbol] = new_dest - pass - - start = states.index(partition[automaton.start].representative) - finals = set([i for i in range(len(states)) if states[i].value in automaton.finals]) - - return DFA(len(states), finals, transitions, start) - - -def automata_union(a1, a2): - transitions = {} - - start = 0 - d1 = 1 - d2 = a1.states + d1 - final = a2.states + d2 - - for (origin, symbol), destinations in a1.map.items(): - other = [q + d1 for q in destinations] - transitions[origin + d1, symbol] = other - - for (origin, symbol), destinations in a2.map.items(): - other = [q + d2 for q in destinations] - transitions[origin + d2, symbol] = other - - transitions[start, ""] = [a1.start + d1, a2.start + d2] - for i in a1.finals: - try: - transitions[i + d1, ""].add(final) - except KeyError: - transitions[i + d1, ""] = [final] - for i in a2.finals: - try: - transitions[i + d2, ""].add(final) - except KeyError: - transitions[i + d2, ""] = [final] - - states = a1.states + a2.states + 2 - finals = {final} - - return NFA(states, finals, transitions, start) - - -def automata_concatenation(a1, a2): - transitions = {} - - start = 0 - d1 = 0 - d2 = a1.states + d1 - final = a2.states + d2 - - for (origin, symbol), destinations in a1.map.items(): - other = [q + d1 for q in destinations] - transitions[origin + d1, symbol] = other - - for (origin, symbol), destinations in a2.map.items(): - other = [q + d2 for q in destinations] - transitions[origin + d2, symbol] = other - - for i in a1.finals: - try: - transitions[i + d1, ""].add(a2.start + d2) - except KeyError: - transitions[i + d1, ""] = [a2.start + d2] - for i in a2.finals: - try: - transitions[i + d2, ""].append(final) - except KeyError: - transitions[i + d2, ""] = [final] - - states = a1.states + a2.states + 1 - finals = {final} - - return NFA(states, finals, transitions, start) - - -def automata_closure(a1): - transitions = {} - - start = 0 - d1 = 1 - final = a1.states + d1 - - for (origin, symbol), destinations in a1.map.items(): - other = [q + d1 for q in destinations] - transitions[origin + d1, symbol] = other - - transitions[start, ""] = [final, a1.start + d1] - - for i in a1.finals: - try: - transitions[i + d1, ""].add(final) - except KeyError: - transitions[i + d1, ""] = [final] - - try: - transitions[final, ""].add(start) - except: - transitions[final, ""] = [start] - - states = a1.states + 2 - finals = {final} - - return NFA(states, finals, transitions, start) - - -def get_printer( - AtomicNode=AtomicNode, - UnaryNode=UnaryNode, - BinaryNode=BinaryNode, -): - class PrintVisitor(object): - @visitor.on("node") - def visit(self, node, tabs=0): - 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) diff --git a/src/compiler/cmp/utils.py b/src/compiler/cmp/utils.py index 94230cb87..a2139016b 100644 --- a/src/compiler/cmp/utils.py +++ b/src/compiler/cmp/utils.py @@ -1,7 +1,3 @@ -from typing import List - -from black import err -from numpy import empty from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon @@ -105,22 +101,6 @@ def inspect(item, grammar_name="G", mapper=None): 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. @@ -221,60 +201,5 @@ 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: List[Token], 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:]) - - try: - if state not in self.action or lookahead not in self.action[state]: - error = f"{w[cursor].pos} - SyntacticError: ERROR at or near {w[cursor].lex}" - return None, error - except: - print(state) - print(self.action) - print(lookahead) - error = f"{w[cursor].pos} - SyntacticError: ERROR at or near {w[cursor].lex}" - return None, error - - action, tag = list(self.action[state][lookahead])[0] - if action is self.SHIFT: - operations.append(self.SHIFT) - stack.append(tag) - cursor += 1 - elif action is self.REDUCE: - operations.append(self.REDUCE) - if len(tag.Right): - stack = stack[: -len(tag.Right)] - stack.append(list(self.goto[stack[-1]][tag.Left])[0]) - output.append(tag) - elif action is ShiftReduceParser.OK: - return (output if not get_shift_reduce else (output, operations)), None - else: - raise ValueError - - emptyToken = Token("", "", (0, 0)) selfToken = Token("self", "", (0, 0)) diff --git a/src/compiler/lexer/lex.py b/src/compiler/lexer/lex.py index ddc208530..1c2fec965 100644 --- a/src/compiler/lexer/lex.py +++ b/src/compiler/lexer/lex.py @@ -1,8 +1,8 @@ -import ply.lex as lex - from ..cmp.grammar import * from ..cmp.utils import Token +import ply.lex as lex + class CoolLexer(object): def __init__(self): diff --git a/src/compiler/parser/__init__.py b/src/compiler/parser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/compiler/parser/parser.py b/src/compiler/parser/parser.py new file mode 100644 index 000000000..3008eb6c6 --- /dev/null +++ b/src/compiler/parser/parser.py @@ -0,0 +1,200 @@ +from typing import List +from ..cmp.automata import State +from ..cmp.pycompiler import EOF, Item +from ..cmp.utils import Token, ContainerSet +from .utils import upd_table, compute_firsts, expand, compress + + +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: List[Token], 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:]) + + try: + if state not in self.action or lookahead not in self.action[state]: + error = f"{w[cursor].pos} - SyntacticError: ERROR at or near {w[cursor].lex}" + return None, error + except: + print(state) + print(self.action) + print(lookahead) + error = f"{w[cursor].pos} - SyntacticError: ERROR at or near {w[cursor].lex}" + return None, error + + action, tag = list(self.action[state][lookahead])[0] + if action is self.SHIFT: + operations.append(self.SHIFT) + stack.append(tag) + cursor += 1 + elif action is self.REDUCE: + operations.append(self.REDUCE) + if len(tag.Right): + stack = stack[: -len(tag.Right)] + stack.append(list(self.goto[stack[-1]][tag.Left])[0]) + output.append(tag) + elif action is ShiftReduceParser.OK: + return (output if not get_shift_reduce else (output, operations)), None + else: + raise ValueError + + +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 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: + 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 + + +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 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) + elif operation == ShiftReduceParser.REDUCE: + production = next(right_parse) + _, 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] diff --git a/src/compiler/parser/utils.py b/src/compiler/parser/utils.py new file mode 100644 index 000000000..8ff715dcd --- /dev/null +++ b/src/compiler/parser/utils.py @@ -0,0 +1,95 @@ +from ..cmp.pycompiler import Item +from ..cmp.utils import ContainerSet + + +def upd_table(table, head, symbol, production): + if not head in table: + table[head] = {} + if not symbol in table[head]: + table[head][symbol] = [] + if production not in table[head][symbol]: + table[head][symbol].append(production) + return len(table[head][symbol]) <= 1 + + +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 KeyError: + 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 expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() + for preview in item.Preview(): + lookaheads.hard_update(compute_local_first(firsts, preview)) + + assert not lookaheads.contains_epsilon + 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 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 diff --git a/src/compiler/visitors/type_builder.py b/src/compiler/visitors/type_builder.py index 7b5fca136..0d4e2ac7f 100644 --- a/src/compiler/visitors/type_builder.py +++ b/src/compiler/visitors/type_builder.py @@ -1,6 +1,3 @@ -from typing import List, Optional, Tuple - -import compiler.visitors.visitor as visitor from ..cmp.ast import ( ProgramNode, ClassDeclarationNode, @@ -22,6 +19,8 @@ MAIN_PROGRAM_ERROR, SELF_ERROR, ) +from typing import List, Optional, Tuple +import compiler.visitors.visitor as visitor class TypeBuilder: diff --git a/src/compiler/visitors/type_checker.py b/src/compiler/visitors/type_checker.py index 0be411444..9422179f2 100644 --- a/src/compiler/visitors/type_checker.py +++ b/src/compiler/visitors/type_checker.py @@ -1,18 +1,3 @@ -from typing import List, Optional, Tuple - -import compiler.visitors.visitor as visitor -from ..cmp.semantic import ( - Context, - InferencerManager, - Method, - Scope, - SemanticError, - ErrorType, - SelfType, - AutoType, - LCA, - Type, -) from ..cmp.ast import ( BinaryNode, ProgramNode, @@ -38,7 +23,22 @@ VariableNode, InstantiateNode, ) +from ..cmp.semantic import ( + Context, + InferencerManager, + Method, + Scope, + SemanticError, + ErrorType, + SelfType, + AutoType, + LCA, + Type, +) from .utils import * +from typing import List, Optional, Tuple + +import compiler.visitors.visitor as visitor class TypeChecker: diff --git a/src/compiler/visitors/type_inferencer.py b/src/compiler/visitors/type_inferencer.py index 9f913699e..3f6e2e929 100644 --- a/src/compiler/visitors/type_inferencer.py +++ b/src/compiler/visitors/type_inferencer.py @@ -1,17 +1,3 @@ -from typing import List, Tuple -import compiler.visitors.visitor as visitor -from ..cmp.semantic import ( - Context, - InferencerManager, - Method, - Scope, - SemanticError, - ErrorType, - SelfType, - AutoType, - LCA, - Type, -) from ..cmp.ast import ( ProgramNode, ClassDeclarationNode, @@ -36,7 +22,21 @@ VariableNode, InstantiateNode, ) +from ..cmp.semantic import ( + Context, + InferencerManager, + Method, + Scope, + SemanticError, + ErrorType, + SelfType, + AutoType, + LCA, + Type, +) from .utils import AUTOTYPE_ERROR +from typing import List, Tuple +import compiler.visitors.visitor as visitor class TypeInferencer: diff --git a/src/main.py b/src/main.py index 9ebff26b8..c4f4f986c 100644 --- a/src/main.py +++ b/src/main.py @@ -1,16 +1,12 @@ from compiler.cmp.grammar import G - from compiler.lexer.lex import CoolLexer -from sys import exit - -from compiler.cmp.tools import LR1Parser -from compiler.cmp.evaluation import evaluate_reverse_parse +from compiler.parser.parser import LR1Parser, evaluate_reverse_parse from compiler.visitors.formatter import FormatVisitor - from compiler.visitors.type_collector import TypeCollector from compiler.visitors.type_builder import TypeBuilder from compiler.visitors.type_checker import TypeChecker from compiler.visitors.type_inferencer import TypeInferencer +from sys import exit def main(args): diff --git a/src/makefile b/src/makefile index 30df993f5..d6c870144 100644 --- a/src/makefile +++ b/src/makefile @@ -1,12 +1,21 @@ .PHONY: clean +CODE := code.cl +FILE_NAME := $(shell echo $(CODE) | cut -d '.' -f 1) +ASM := $(FILE_NAME).mips + main: - # Compiling the compiler :) + @./coolc.sh $(CODE) + @spim -file $(ASM) clean: - rm -rf build/* - rm -rf ../tests/*/*.mips + @rm -rf build/* + @rm -rf ../tests/*/*.mips test: - pytest ../tests -v --tb=short -m=${TAG} + @pytest ../tests -v --tb=short -m=${TAG} + +install: + @python -m pip install -r ../requirements.txt + @sudo apt-get install spim From 2b8cccd7fa13a3fa6537f308e1cfd379f2d7331a Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Sat, 26 Feb 2022 00:46:52 -0500 Subject: [PATCH 107/138] create report, code generation missing --- doc/report.md | 55 +++++++++++++++++++++++++++++++++++---------------- doc/team.yml | 18 ++++++++--------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/doc/report.md b/doc/report.md index f23934f1d..42672e5e5 100644 --- a/doc/report.md +++ b/doc/report.md @@ -38,6 +38,8 @@ spim -file .mips ## Arquitectura del compilador +Para la implementación de este proyecto se utilizaron como base los contenidos y proyectos desarrollados en 3er año; añadiendo las funcionalidades faltantes y realizando modificaciones y mejoras sobre el código ya existente. + ### Fases Las fases en que se divide el proceso de compilación se muestran a continuación y serán explicadas con más detalle en las secciones siguientes: @@ -51,53 +53,72 @@ Las fases en que se divide el proceso de compilación se muestran a continuació #### Lexer +Para el análisis léxico se utilizó el módulo `lex.py` del paquete PLY de Python, que permite separar el texto de entrada (código COOL) en una colección de *tokens* dado un conjunto de reglas de expresiones regulares. + +Para la obtención de los tokens de *string* y los comentarios multilíneas se definieron en el lexer, además del *INITIAL*, que es el estado que usa el lexer por defecto, dos estados exclusivos: + +```python + states = ( + ("string", "exclusive"), + ("comment", "exclusive"), + ) +``` + +Esto permitió tener en cuenta: el uso de caracteres inválidos en el primer caso, y los comentarios anidados en el segundo. + +Además se llevaron a cabo cálculos auxiliares para obtener el valor de la columna de cada token, puesto que el lexer solo cuenta con el número de fila y el index. + +#### Parsing + +Se utilizó una modificación de la implementación previa del parser LR1 para llevar a cabo la fase de *parsing*; esta se realizó para poder almacenar el token, en lugar de solo su lexema; puesto que el token también guarda la posición *(fila, columna)*. +La gramática utilizada es S-atributada. Podrá encontrar la implementación de la misma en [grammar.py](https://github.com/codersUP/cool-compiler-2021/blob/master/src/compiler/cmp/grammar.py) #### Recolección de tipos Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes pasos: -- Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : Int, String, Bool, IO, Object; incluyendo la definición de sus métodos. Además se añaden como tipos SELF_TYPE, AUTO_TYPE. +- Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : *Int*, *String*, *Bool*, *IO*, *Object*; incluyendo la definición de sus métodos. Además se añaden como tipos *SELF_TYPE*, *AUTO_TYPE*. - Recorrido por las declaraciones hechas en el programa recolectando los tipos creados. -- Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna Object como padre. +- Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna *Object* como padre. - Chequeo de herencia cíclica. En caso de detectar algún ciclo en la jerarquía de tipos, se reporta el error, y a la clase por la cual hubo problema se le asigna Object como padre, para continuar el análisis. - Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. #### Construcción de tipos -La construcción de tipos se desarrolla empleando la clase Type Builder. Esta se encarga de visitar los *features* de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. +La construcción de tipos se desarrolla empleando la clase *Type Builder*. Esta se encarga de visitar los *features* de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. -Además se encarga de chequear la existencia del tipo Main con su método main correspondiente, como es requerido en COOL. +Además se encarga de chequear la existencia del tipo **Main** con su método **main** correspondiente, como es requerido en COOL. -En esta clase también se hace uso de la clase Inferencer Manager que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO_TYPE se le asigna un *id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. +En esta clase también se hace uso de la clase *Inferencer Manager* que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO_TYPE se le asigna un *id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. #### Chequeo e Inferencia de tipos -En primer lugar se utiliza la clase Type Checker para validar el correcto uso de los tipos definidos. Toma la instancia de clase Inferencer Manager utilizada en el Type Builder para continuar la asignación de id a otros elementos en el código que también pueden estar definidos como AUTO_TYPE, como es el caso de las variables definidas en la expresión Let. Las variables definidas en el Scope se encargarán de guardar el id asignado; en caso de que no se les haya asignado ninguno, el id será *None*. +En primer lugar se utiliza la clase *Type Checker* para validar el correcto uso de los tipos definidos. Toma la instancia de clase *Inferencer Manager* utilizada en el *Type Builder* para continuar la asignación de *id* a otros elementos en el código que también pueden estar definidos como *AUTO_TYPE*, como es el caso de las variables definidas en la expresión *Let*. Las variables definidas en el *Scope* se encargarán de guardar el *id* asignado; en caso de que no se les haya asignado ninguno, el id será *None*. -La instancia de Scope creada en el Type Checker, así como la de Inferencer Manager se pasarán al Type Inferencer para realizar la inferencia de tipos. +La instancia de *Scope* creada en el *Type Checker*, así como la de *Inferencer Manager* se pasarán al *Type Inferencer* para realizar la inferencia de tipos. -Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El id asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. +Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El *id* asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. -Sea una variable con id = i, que está definida como AUTO_TYPE y sea A el tipo estático que se ha de inferir: +Sea una variable con *id = i*, que está definida como *AUTO_TYPE* y sea *A* el tipo estático que se ha de inferir: -- `conforms_to[i]` guarda una lista con los tipos a los que debe conformarse A; note que esta lista contiene al menos al tipo Object. El hecho de que A se conforme a estos tipos, implica que todos ellos deben encontrarse en el camino de él a Object en el árbol de jerarquía de tipos. En caso contrario se puede decir que hubo algún error en la utilización del AUTO_TYPE para esta variable. Sea B el tipo más lejano a Object de los que aparecen en la lista. -- `conformed_by[i]` almacena una lista con los tipos que deben conformarse a A. Luego el menor ancestro común (*LCA - Lowest Common Ancestor*) de dichos tipos deberá conformarse a A. Note que este siempre existirá, pues en caso peor será Object, que es la raíz del árbol de tipos. Sea C el LCA de los tipos guardados. Note que si la lista está vacía, (que puede suceder) C será *None*. -- Como C se conforma a A y A se conforma B, tiene que ocurrir que C se conforma a B. En caso contrario, se reporta un uso incorrecto de AUTO_TYPE para esa variable. Todos los tipos en el camino entre B y C son válidos para inferir A; pues cumplen con todas las restricciones que impone el programa. En nuestro caso se elige C, que es el tipo más restringido, para la inferencia. En caso de que C sea *None* se toma B como tipo de inferencia. -- `infered_type[i]` guardará el tipo inferido una vez realizado el procedimiento anterior; mientra tanto su valor es *None*. +- `conforms_to[i]` guarda una lista con los tipos a los que debe conformarse *A*; note que esta lista contiene al menos al tipo *Object*. El hecho de que *A* se conforme a estos tipos, implica que todos ellos deben encontrarse en el camino de él a Object en el árbol de jerarquía de tipos. En caso contrario se puede decir que hubo algún error en la utilización del *AUTO_TYPE* para esta variable. Sea *B* el tipo más lejano a *Object* de los que aparecen en la lista. +- `conformed_by[i]` almacena una lista con los tipos que deben conformarse a *A*. Luego el menor ancestro común (*LCA - Lowest Common Ancestor*) de dichos tipos deberá conformarse a A. Note que este siempre existirá, pues en caso peor será *Object*, que es la raíz del árbol de tipos. Sea *C* el *LCA* de los tipos guardados. Note que si la lista está vacía, (que puede suceder) *C* será *None*. +- Como *C* se conforma a *A* y *A* se conforma a *B*, tiene que ocurrir que *C* se conforma a *B*. En caso contrario, se reporta un uso incorrecto de *AUTO_TYPE* para esa variable. Todos los tipos en el camino entre *B* y *C* son válidos para inferir *A*; pues cumplen con todas las restricciones que impone el programa. En nuestro caso se elige *C*, que es el tipo más restringido, para la inferencia. En caso de que *C* sea *None* se toma *B* como tipo de inferencia. +- `infered_type[i]` guardará el tipo inferido una vez realizado el procedimiento anterior; mientras tanto su valor es *None*. -La clase Inferencer Manager además, está equipada con métodos para actualizar las listas dado un id, y para realizar la inferencia dados los tipos almacenados. +La clase *Inferencer Manager* además, está equipada con métodos para actualizar las listas dado un *id*, y para realizar la inferencia dados los tipos almacenados. -El Type Inferencer por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: +El *Type Inferencer* por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: 1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un *ExpressionNode*, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el *manager*. 2. Infiere todos los tipos que pueda con la información recogida. 3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. - - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último rerrido asignando tipo Object a todos los AUTO_TYPES que no pudieron ser inferidos. + - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último recorrido asignando tipo *Object* a todos los AUTO_TYPES que no pudieron ser inferidos. > Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista *conforms_to* contiene a otro tipo distinto de Object o su lista *conformed_by* contiene al menos un tipo. -Por último se realiza un nuevo recorrido del AST con el Type Checker para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los AUTO_TYPES sustituidos por el tipo inferido. +Por último se realiza un nuevo recorrido del *AST* con el *Type Checker* para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los *AUTO_TYPES* sustituidos por el tipo inferido. ## Licencia diff --git a/doc/team.yml b/doc/team.yml index c16162532..40f244493 100644 --- a/doc/team.yml +++ b/doc/team.yml @@ -1,10 +1,10 @@ members: - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX + - name: Carmen Irene Cabrera Rodríguez + github: cicr99 + group: C412 + - name: David Guaty Domínguez + github: Gu4ty + group: C412 + - name: Enrique Martínez González + github: kikeXD + group: C412 From 351c666e34a730153974cea7367ff226fa4cba11 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 01:09:49 -0500 Subject: [PATCH 108/138] add cil formatter --- src/compiler/visitors/cil_formatter.py | 199 +++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 src/compiler/visitors/cil_formatter.py diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py new file mode 100644 index 000000000..c474bdec4 --- /dev/null +++ b/src/compiler/visitors/cil_formatter.py @@ -0,0 +1,199 @@ +import compiler.visitors.visitor as visitor +from ..cmp.cil_ast import * + + +class PrintCILVisitor(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(CopyNode) + def visit(self, node): + return f"{node.dest} = COPY {node.source}" + + @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) + + 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(LoadNode) + def visit(self, node): + return f"{node.dest} = Load {node.msg}" + + @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(LeqNode) + 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(NotNode) + def visit(self, node): + return f"{node.dest} = NOT {node.obj}" + + @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(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(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(ReadNode) + def visit(self, node): + return f"{node.dest} = READ" + + @visitor.when(PrintNode) + def visit(self, node): + return f"PRINT {node.value}" + + @visitor.when(ExitNode) + def visit(self, node): + return f"EXIT" + + @visitor.when(LoadNode) + def visit(self, node): + return f"{node.dest} = Load {node.msg}" + + @visitor.when(NameNode) + def visit(self, node): + return f"{node.dest} = NAME {node.name}" + + @visitor.when(TypeNameNode) + def visit(self, node): + return f"{node.dest} = TYPENAME {node.source}" + + @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(ErrorNode) + def visit(self, node): + return f"ERROR {node.data_node}" + + @visitor.when(EqualStrNode) + def visit(self, node): + return f"{node.dest} = {node.left} == {node.right}" From ba886d73850be9f794cebead28a5c945af527375 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 01:10:29 -0500 Subject: [PATCH 109/138] add cool2cil --- src/compiler/visitors/cool2cil.py | 1214 +++++++++++++++++++++++++++++ 1 file changed, 1214 insertions(+) create mode 100644 src/compiler/visitors/cool2cil.py diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py new file mode 100644 index 000000000..5366390f5 --- /dev/null +++ b/src/compiler/visitors/cool2cil.py @@ -0,0 +1,1214 @@ +from pprint import pprint +import compiler.visitors.visitor as visitor +from ..cmp import cil_ast as cil +from ..cmp.semantic import ( + Scope, + SemanticError, + ErrorType, + IntType, + BoolType, + SelfType, + AutoType, + LCA, + VariableInfo, +) +from ..cmp.ast import ( + CaseBranchNode, + LeqNode, + LessNode, + LetVarNode, + ProgramNode, + ClassDeclarationNode, + AttrDeclarationNode, + FuncDeclarationNode, +) +from ..cmp.ast import ( + AssignNode, + CallNode, + CaseNode, + BlockNode, + LoopNode, + ConditionalNode, + LetNode, +) +from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode +from ..cmp.ast import VoidNode, NotNode, NegNode +from ..cmp.ast import ( + ConstantNumNode, + ConstantStringNode, + ConstantBoolNode, + VariableNode, + InstantiateNode, +) +from ..cmp.ast import PlusNode, MinusNode, StarNode, DivNode + + +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 + self.vself = VariableInfo("self", None) + self.value_types = ["String", "Int", "Bool"] + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def ids(self): + return self.current_function.ids + + @property + def instructions(self): + return self.current_function.instructions + + def register_local(self, vinfo, id=False): + new_vinfo = VariableInfo("", None) + new_vinfo.name = ( + f"local_{self.current_function.name[9:]}_{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 new_vinfo.name + + def define_internal_local(self): + vinfo = VariableInfo("internal", None, 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_param(self, vinfo): + param_node = cil.ParamNode(vinfo.name) + self.params.append(param_node) + return vinfo.name + + 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) + + 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 = self.register_data(msg) + self.register_instruction(cil.ErrorNode(data_node)) + + self.register_instruction(continue_node) + + def init_name(self, type_name, attr=False): + if attr: + return f"init_attr_at_{type_name}" + return f"init_at_{type_name}" + + def buildHierarchy(self, t: str): + if t == "Object": + return None + return { + x.name + for x in self.context.types.values() + if x.name != "AUTO_TYPE" and x.conforms_to(self.context.get_type(t)) + } + + def register_built_in(self): + # Object + type_node = self.register_type("Object") + + # 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)) + + # abort Object + 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 == "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)) + 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()) + + # type_name Object + 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.TypeNameNode(result, self.vself.name)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction( + cil.StaticCallNode(self.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # copy Object + 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.CopyNode(result, self.vself.name)) + self.register_instruction(cil.ReturnNode(result)) + + # Object + 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") + + # 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)) + + # out_string 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.GetAttribNode(vname, "x", "value", "String")) + self.register_instruction(cil.PrintStrNode(vname)) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + # out_int IO + self.current_function = self.register_function( + self.to_function_name("out_int", "IO") + ) + self.register_param(self.vself) + self.register_param(VariableInfo("x", None)) + vname = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(vname, "x", "value", "Int")) + self.register_instruction(cil.PrintIntNode(vname)) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + # in_string IO + 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.ReadStrNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction( + cil.StaticCallNode(self.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # in_int IO + 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.ReadIntNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + self.register_instruction(cil.ReturnNode(instance)) + + # IO + 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 ["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"] + + # 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)) + 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.init_name("Int"), attr)) + self.register_instruction(cil.SetAttribNode(instance, "length", attr, "String")) + self.register_instruction(cil.ReturnNode(instance)) + + # length 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.GetAttribNode(result, self.vself.name, "length", "String") + ) + self.register_instruction(cil.ReturnNode(result)) + + # concat String + 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() + 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, length_1)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction( + cil.StaticCallNode(self.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # subst String + 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() + 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() + 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 + 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") + 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.init_name("String"), instance) + ) + self.register_instruction(cil.ReturnNode(instance)) + + # String + 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 ["length", "concat", "substr"] + ] + type_node.methods += [("init", self.init_name("String"))] + + # Int + type_node = self.register_type("Int") + type_node.attributes = ["value"] + + # 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)) + self.register_instruction(cil.SetAttribNode(instance, "value", "val", "Int")) + self.register_instruction(cil.ReturnNode(instance)) + + # Int + type_node.methods = [ + (method, self.to_function_name(method, "Object")) for method in obj_methods + ] + type_node.methods += [("init", self.init_name("Int"))] + + # Bool + type_node = self.register_type("Bool") + type_node.attributes = ["value"] + + # 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)) + self.register_instruction(cil.SetAttribNode(instance, "value", "val", "Bool")) + self.register_instruction(cil.ReturnNode(instance)) + + # Bool + type_node.methods = [ + (method, self.to_function_name(method, "Object")) for method in obj_methods + ] + type_node.methods += [("init", self.init_name("Bool"))] + + +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.register_local(VariableInfo("instance", None)) + self.register_instruction(cil.StaticCallNode(self.init_name("Main"), instance)) + self.register_instruction(cil.ArgNode(instance)) + result = self.define_internal_local() + main_method_name = self.to_function_name("main", "Main") + self.register_instruction(cil.StaticCallNode(main_method_name, result)) + self.register_instruction(cil.ReturnNode(0)) + + self.register_data("Abort called from class ") + self.register_built_in() + 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) + + # Your code here!!! (Handle all the .TYPE section) + type_node = self.register_type(self.current_type.name) + + visited_func = [] + current = self.current_type + while current is not None: + attributes = [attr.name for attr in current.attributes] + methods = [ + func.name for func in current.methods if func.name not in visited_func + ] + visited_func.extend(methods) + type_node.attributes.extend(attributes[::-1]) + type_node.methods.extend( + [ + (item, self.to_function_name(item, current.name)) + for item in methods[::-1] + ] + ) + current = current.parent + + type_node.attributes.reverse() + type_node.methods.reverse() + + 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) + + # init + 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)) + + func = self.current_function + vtemp = self.define_internal_local() + + # init_attr + self.current_function = self.register_function( + self.init_name(node.id, attr=True) + ) + self.register_param(self.vself) + if node.parent.lex != "Object" and node.parent.lex != "IO": + self.register_instruction(cil.ArgNode(self.vself.name)) + self.register_instruction( + cil.StaticCallNode(self.init_name(node.parent, attr=True), vtemp) + ) + attr_declarations = ( + f for f in node.features if isinstance(f, 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.init_name(node.id, attr=True), vtemp) + ) + + self.register_instruction(cil.ReturnNode(instance)) + self.current_function = None + self.current_type = None + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + if node.expr: + value = self.visit(node.expr, scope) + self.register_instruction( + cil.SetAttribNode(self.vself.name, node.id, value, self.current_type) + ) + + elif node.type in self.value_types: + value = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.type, value)) + self.register_instruction( + cil.SetAttribNode(self.vself.name, node.id, value, self.current_type) + ) + + @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) + + # Your code here!!! (Handle PARAMS) + self.current_function = self.register_function( + self.to_function_name(self.current_method.name, self.current_type.name) + ) + + self.params.append(cil.ParamNode(self.vself.name)) + self.params.extend([cil.ParamNode(p) for p in self.current_method.param_names]) + + value = self.visit(node.body, scope) + + # Your code here!!! (Handle RETURN) + if value is None: + self.register_instruction(cil.ReturnNode("")) + elif self.current_function.name == "entry": + self.register_instruction(cil.ReturnNode(0)) + else: + self.register_instruction(cil.ReturnNode(value)) + + self.current_method = None + + @visitor.when(AssignNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + + # Your code here!!! + value = self.visit(node.expr, scope) + + try: + self.current_type.get_attribute(node.id) + self.register_instruction( + cil.SetAttribNode( + self.vself.name, node.id, value, self.current_type.name + ) + ) + except SemanticError: + 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, value)) + + @visitor.when(CallNode) + def visit(self, node, scope): + ############################### + # node.obj -> AtomicNode + # node.id -> str + # node.args -> [ ExpressionNode ... ] + # node.type -> str + ############################### + + args = [] + for arg in node.args: + vname = self.register_local(VariableInfo(f"{node.id}_arg", None), id=True) + ret = self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, ret)) + args.append(cil.ArgNode(vname)) + result = self.register_local( + VariableInfo(f"return_value_of_{node.id}", None), id=True + ) + + vobj = self.define_internal_local() + ret = self.visit(node.obj, scope) + self.register_instruction(cil.AssignNode(vobj, ret)) + + # 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, + f"{node.token.pos} - RuntimeError: Dispatch on void\n", + ) + + # self + self.register_instruction(cil.ArgNode(vobj)) + for arg in args: + self.register_instruction(arg) + + if node.type: + # Call of type @.id(,...,) + 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), id=True + ) + self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) + computed_type = node.obj.computed_type + if computed_type.name == "SELF_TYPE": + computed_type = computed_type.fixed_type + self.register_instruction( + cil.DynamicCallNode(type_of_node, node.id, result, computed_type.name) + ) + + return result + + @visitor.when(ConditionalNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.if_body -> ExpressionNode + # node.else_body -> ExpressionNode + ################################## + + then_label_node = self.register_label("then_label") + else_label_node = self.register_label("else_label") + continue_label_node = self.register_label("continue_label") + + cond_ret = self.visit(node.condition, scope) + + cond_ret_value = self.define_internal_local() + + self.register_instruction( + cil.GetAttribNode(cond_ret_value, cond_ret, "value", "Bool") + ) + self.register_instruction(cil.GotoIfNode(cond_ret_value, then_label_node.label)) + self.register_instruction(cil.GotoNode(else_label_node.label)) + + value = self.register_local(VariableInfo("if_then_else_value", None)) + + # Label then_label + self.register_instruction(then_label_node) + ret_then = self.visit(node.then_body, scope) + self.register_instruction(cil.AssignNode(cond_ret_value, ret_then)) + self.register_instruction(cil.GotoNode(continue_label_node.label)) + + # Label else_label + self.register_instruction(else_label_node) + ret_else = self.visit(node.else_body, scope) + self.register_instruction(cil.AssignNode(cond_ret_value, ret_else)) + + self.register_instruction(continue_label_node) + return cond_ret_value + + @visitor.when(LoopNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # node.body -> ExpressionNode + ################################### + + while_label_node = self.register_label("while_label") + loop_label_node = self.register_label("loop_label") + pool_label_node = self.register_label("pool_label") + + condition = self.define_internal_local() + self.register_instruction(while_label_node) + + condition_value = self.visit(node.condition, scope) + + self.register_instruction( + cil.GetAttribNode(condition, condition_value, "value", "Bool") + ) + + self.register_instruction(cil.GotoIfNode(condition, loop_label_node.label)) + self.register_instruction(cil.GotoNode(pool_label_node.label)) + self.register_instruction(loop_label_node) + + self.visit(node.body, scope) + + self.register_instruction(cil.GotoNode(while_label_node.label)) + + self.register_instruction(pool_label_node) + + return cil.VoidNode() + + @visitor.when(BlockNode) + def visit(self, node, scope): + ####################################### + # node.expr_list -> [ ExpressionNode ... ] + ####################################### + ret = self.register_local(VariableInfo("block_node_value", None)) + + for expr in node.expr_list: + ret_value = self.visit(expr, scope) + + self.register_instruction(cil.AssignNode(ret, ret_value)) + + return ret + + @visitor.when(LetNode) + def visit(self, node, scope): + ############################################ + # node.id_list -> [(id, type, expr), ...] + # node.in_body -> ExpressionNode + ############################################ + value = self.register_local(VariableInfo("let_in_value", None)) + + for let_var in node.id_list: + self.visit(let_var, scope) + + ret_val = self.visit(node.body, scope) + self.register_instruction(cil.AssignNode(value, ret_val)) + + return value + + @visitor.when(LetVarNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + if node.id in self.ids: + vname = self.ids[node.id] + else: + vname = self.register_local(VariableInfo(node.id, node.typex), id=True) + if node.expression: + ret_value = self.visit(node.expression, scope) + self.register_instruction(cil.AssignNode(vname, ret_value)) + elif node.typex in self.value_types: + self.register_instruction(cil.AllocateNode(node.typex, vname)) + + @visitor.when(CaseNode) + def visit(self, node, scope): + ############################################## + # node.expr -> ExpressionNode + # node.branches -> [(id, type, expr), ... } + ############################################## + ret = self.register_local(VariableInfo("case_expr_value", None)) + ret_type = self.register_local(VariableInfo("typeName_value", None)) + vcond = self.register_local(VariableInfo("equal_value", None)) + value = self.register_local(VariableInfo("case_value", None)) + + ret_val = self.visit(node.expr, scope) + + self.register_instruction(cil.AssignNode(ret, ret_val)) + self.register_instruction(cil.TypeOfNode(ret_type, ret_val)) + + # 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, ret, void)) + + self.register_runtime_error( + equal_result, + f"{node.token.pos} - RuntimeError: Case on void\n", + ) + + # sorting the branches + order = [] + for b in node.branch_list: + count = 0 + t1 = self.context.get_type(b.typex) + for other in node.branch_list: + t2 = self.context.get_type(other.typex) + count += t2.conforms_to(t1) + order.append((count, b)) + order.sort(key=lambda x: x[0]) + + labels = [] + old = {} + for idx, (_, b) in enumerate(order): + labels.append(self.register_label(f"{idx}_label")) + h = self.buildHierarchy(b.typex) + if not h: + self.register_instruction(cil.GotoNode(labels[-1].label)) + break + h.add(b.typex) + for s in old: + h -= s + 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, ret_type, 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"({node.token.pos[0] + 1 + len(node.branch_list)},{node.token.pos[1] - 5}) - RuntimeError: Execution of a case statement without a matching branch\n" + ) + self.register_instruction(cil.ErrorNode(data_node)) + + end_label = self.register_label("end_label") + for idx, l in enumerate(labels): + self.register_instruction(l) + vid = self.register_local(VariableInfo(order[idx][1].id, None), id=True) + self.register_instruction(cil.AssignNode(vid, ret)) + ret_2 = self.visit(order[idx][1], scope) + self.register_instruction(cil.AssignNode(value, ret_2)) + self.register_instruction(cil.GotoNode(end_label.label)) + + self.register_instruction(end_label) + return value + + @visitor.when(CaseBranchNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + ret = self.register_local(VariableInfo("block_node_value", None)) + + ret_value = self.visit(node.expression, scope) + + self.register_instruction(cil.AssignNode(ret, ret_value)) + + return ret + + @visitor.when(NotNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + ret = self.define_internal_local() + ret_minus_1 = self.define_internal_local() + value = self.define_internal_local() + + ret_value = self.visit(node.expr, scope) + self.register_instruction(cil.GetAttribNode(ret, ret_value, "value", "Bool")) + self.register_instruction(cil.MinusNode(ret_minus_1, 1, ret)) + + self.register_instruction(cil.ArgNode(ret_minus_1)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) + return value + + @visitor.when(LeqNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + ret_value = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + value = self.define_internal_local() + + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + + self.register_instruction(cil.GetAttribNode(left_value, left, "value", "Bool")) + self.register_instruction( + cil.GetAttribNode(right_value, right, "value", "Bool") + ) + self.register_instruction(cil.LeqNode(ret_value, left_value, right_value)) + + self.register_instruction(cil.ArgNode(ret_value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) + + return value + + @visitor.when(LessNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + value = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + value = self.define_internal_local() + + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + 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(value, left_value, right_value)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) + return value + + @visitor.when(EqualNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + return_vale = 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() + ret = self.define_internal_local() + + left = self.visit(node.left, scope) + right = self.visit(node.right, scope) + + self.register_instruction(cil.TypeOfNode(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") + 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_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)) + + 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(return_vale, 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.EqualStrNode(return_vale, left_value, right_value) + ) + self.register_instruction(cil.GotoNode(continue_node.label)) + + self.register_instruction(reference_node) + self.register_instruction(cil.EqualNode(return_vale, left, right)) + + self.register_instruction(continue_node) + self.register_instruction(cil.ArgNode(return_vale)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret + + @visitor.when(PlusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() + left = self.visit(node.left, scope) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + right = self.visit(node.right, scope) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) + + self.register_instruction(cil.PlusNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(MinusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() + left = self.visit(node.left, scope) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + right = self.visit(node.right, scope) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) + + self.register_instruction(cil.LessNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(StarNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() + left = self.visit(node.left, scope) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + right = self.visit(node.right, scope) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) + + self.register_instruction(cil.StarNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(DivNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + value = self.define_internal_local() + value_left = self.define_internal_local() + value_right = self.define_internal_local() + left = self.visit(node.left, scope) + self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + right = self.visit(node.right, scope) + self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) + + # Check division by 0 + equal_result = self.define_internal_local() + self.register_instruction(cil.EqualNode(equal_result, value_right, 0)) + self.register_runtime_error( + equal_result, + f"{node.token.pos} - RuntimeError: Division by zero\n", + ) + + self.register_instruction(cil.DivNode(value, value_left, value_right)) + + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(VoidNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + void = cil.VoidNode() + value = self.define_internal_local() + left = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(value, left)) + + ret = self.define_internal_local() + self.register_instruction(cil.EqualNode(ret, value, void)) + self.register_instruction(cil.ArgNode(ret)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret + + @visitor.when(NegNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + value = self.define_internal_local() + complement_value = self.define_internal_local() + ret = self.define_internal_local() + left = self.visit(node.expr, scope) + self.register_instruction(cil.GetAttribNode(value, left, "value", "Int")) + + self.register_instruction(cil.ComplementNode(complement_value, value)) + self.register_instruction(cil.ArgNode(complement_value)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) + return ret + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + ############################### + # node.type -> str + ############################### + ret = self.define_internal_local() + + if node.computed_type.name == SelfType().name: + value = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(value, node.computed_type.name)) + self.register_instruction(cil.AllocateNode(value, ret)) + elif node.computed_type.name == "Int" or node.computed_type.name == "Bool": + self.register_instruction(cil.ArgNode(0)) + elif node.computed_type.name == "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.init_name(node.computed_type.name), ret) + ) + return ret + + @visitor.when(VariableNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + try: + self.current_type.get_attribute(node.lex) + 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 + ) + ) + return 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 == n: + return n + else: + return self.ids[node.lex] + + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(int(node.lex))) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + scope.ret_expr = instance + + @visitor.when(ConstantStringNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + try: + data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] + except IndexError: + data_node = self.register_data(node.lex) + + vmsg = self.register_local(VariableInfo("msg", None)) + ret = self.define_internal_local() + self.register_instruction(cil.LoadNode(vmsg, data_node)) + self.register_instruction(cil.ArgNode(vmsg)) + self.register_instruction(cil.StaticCallNode(self.init_name("String"), ret)) + return ret + + @visitor.when(ConstantBoolNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + if node.lex == "true": + v = 1 + else: + v = 0 + ret = self.define_internal_local() + self.register_instruction(cil.ArgNode(v)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) + return ret From 1aa8aef87941a0af1daa22748421bb92f91fb095 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 01:11:13 -0500 Subject: [PATCH 110/138] add cil ast --- src/compiler/cmp/cil_ast.py | 280 ++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 src/compiler/cmp/cil_ast.py diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py new file mode 100644 index 000000000..34bae8219 --- /dev/null +++ b/src/compiler/cmp/cil_ast.py @@ -0,0 +1,280 @@ +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 InstructionNode(Node): + pass + + +class TypeNameNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + + +class ExitNode(InstructionNode): + pass + + +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 + self.ids = dict() + self.labels_count = 0 + + +class ParamNode(Node): + def __init__(self, name): + self.name = name + + +class LocalNode(Node): + def __init__(self, name): + self.name = name + + +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 LeqNode(ArithmeticNode): + pass + + +class LessNode(ArithmeticNode): + pass + + +class EqualNode(ArithmeticNode): + pass + + +class EqualStrNode(ArithmeticNode): + pass + + +class VoidNode(InstructionNode): + pass + + +class NotNode(ArithmeticNode): + pass + + +class ComplementNode(InstructionNode): + def __init__(self, dest, obj): + self.dest = dest + self.obj = obj + + +class GetAttribNode(InstructionNode): + def __init__(self, dest, obj, attr, computed_type): + self.dest = dest + self.obj = obj + self.attr = attr + self.computed_type = computed_type + + +class SetAttribNode(InstructionNode): + def __init__(self, obj, attr, value, computed_type): + self.obj = obj + self.attr = attr + self.value = value + self.computed_type = computed_type + + +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, dest, obj): + self.dest = dest + self.obj = obj + + +class NameNode(InstructionNode): + def __init__(self, dest, name): + self.dest = dest + self.name = name + + +class LabelNode(InstructionNode): + def __init__(self, label): + self.label = label + + +class GotoNode(InstructionNode): + def __init__(self, label): + self.label = label + + +class GotoIfNode(InstructionNode): + def __init__(self, condition, label): + self.condition = condition + self.label = label + + +class StaticCallNode(InstructionNode): + def __init__(self, function, dest): + self.function = function + self.dest = dest + + +class CopyNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + + +class DynamicCallNode(InstructionNode): + 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): + 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): + def __init__(self, dest, source): + self.dest = dest + self.source = source + + +class ConcatNode(InstructionNode): + def __init__(self, dest, prefix, suffix, length): + self.dest = dest + self.prefix = prefix + self.suffix = suffix + self.length = length + + +class PrefixNode(InstructionNode): + pass + + +class SubstringNode(InstructionNode): + 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): + self.dest = dest + self.ivalue = ivalue + + +class ReadNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + + +class ReadStrNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + + +class ReadIntNode(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 ErrorNode(InstructionNode): + def __init__(self, data_node): + self.data_node = data_node From 420491b265a0fd77dc24894c393f383cf39308c3 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 01:15:38 -0500 Subject: [PATCH 111/138] fix bug --- src/compiler/cmp/semantic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/cmp/semantic.py b/src/compiler/cmp/semantic.py index 01f13a0c2..fa62a3023 100644 --- a/src/compiler/cmp/semantic.py +++ b/src/compiler/cmp/semantic.py @@ -294,7 +294,7 @@ def __repr__(self): class VariableInfo: - def __init__(self, name, vtype, idx): + def __init__(self, name, vtype, idx=None): self.name = name self.type = vtype self.idx = idx From 65830561be2a6abf98541e3376311018b965707a Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 01:15:53 -0500 Subject: [PATCH 112/138] add cool2cil to main --- src/main.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main.py b/src/main.py index c4f4f986c..a93f34dcd 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,8 @@ from compiler.cmp.grammar import G from compiler.lexer.lex import CoolLexer from compiler.parser.parser import LR1Parser, evaluate_reverse_parse +from compiler.visitors.cil_formatter import PrintCILVisitor +from compiler.visitors.cool2cil import COOLToCILVisitor from compiler.visitors.formatter import FormatVisitor from compiler.visitors.type_collector import TypeCollector from compiler.visitors.type_builder import TypeBuilder @@ -77,6 +79,13 @@ def main(args): if checker.errors: exit(1) + # COOL to CIL + cil_visitor = COOLToCILVisitor(context) + cil_ast = cil_visitor.visit(ast, scope) + + cil_formatter = PrintCILVisitor() + print(cil_formatter.visit(cil_ast)) + if __name__ == "__main__": import argparse From 022bcd98837eddb37c28ad8b5f4efe7bd930a2e2 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 02:28:03 -0500 Subject: [PATCH 113/138] add cil to report --- doc/report.md | 67 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/doc/report.md b/doc/report.md index 42672e5e5..ee9f95d66 100644 --- a/doc/report.md +++ b/doc/report.md @@ -6,7 +6,6 @@ - **David Guaty Domínguez** - [Gu4ty](https://github.com/Gu4ty) - **Enrique Martínez González** - [kikeXD](https://github.com/kikeXD) - ## Requerimientos 📋 Para la ejecución de este proyecto necesita tener istalado: @@ -14,7 +13,7 @@ Para la ejecución de este proyecto necesita tener istalado: - Python3.7 o superior - Las dependencias que se encuentran listadas en el archivo [requirements.txt](../requirements.txt) - Spim para ejecutar programas MIPS32 - + Si lo desea, usted puede instalar todas las dependencias necesarias ejecutando en su terminal el siguiente comando, desde el directorio `/src`: ```bash @@ -43,6 +42,7 @@ Para la implementación de este proyecto se utilizaron como base los contenidos ### Fases Las fases en que se divide el proceso de compilación se muestran a continuación y serán explicadas con más detalle en las secciones siguientes: + 1. Lexer 2. Parsing 3. Recolección de tipos @@ -53,9 +53,9 @@ Las fases en que se divide el proceso de compilación se muestran a continuació #### Lexer -Para el análisis léxico se utilizó el módulo `lex.py` del paquete PLY de Python, que permite separar el texto de entrada (código COOL) en una colección de *tokens* dado un conjunto de reglas de expresiones regulares. +Para el análisis léxico se utilizó el módulo `lex.py` del paquete PLY de Python, que permite separar el texto de entrada (código COOL) en una colección de _tokens_ dado un conjunto de reglas de expresiones regulares. -Para la obtención de los tokens de *string* y los comentarios multilíneas se definieron en el lexer, además del *INITIAL*, que es el estado que usa el lexer por defecto, dos estados exclusivos: +Para la obtención de los tokens de _string_ y los comentarios multilíneas se definieron en el lexer, además del _INITIAL_, que es el estado que usa el lexer por defecto, dos estados exclusivos: ```python states = ( @@ -70,57 +70,72 @@ Además se llevaron a cabo cálculos auxiliares para obtener el valor de la colu #### Parsing -Se utilizó una modificación de la implementación previa del parser LR1 para llevar a cabo la fase de *parsing*; esta se realizó para poder almacenar el token, en lugar de solo su lexema; puesto que el token también guarda la posición *(fila, columna)*. +Se utilizó una modificación de la implementación previa del parser LR1 para llevar a cabo la fase de _parsing_; esta se realizó para poder almacenar el token, en lugar de solo su lexema; puesto que el token también guarda la posición _(fila, columna)_. La gramática utilizada es S-atributada. Podrá encontrar la implementación de la misma en [grammar.py](https://github.com/codersUP/cool-compiler-2021/blob/master/src/compiler/cmp/grammar.py) #### Recolección de tipos -Esta fase se realiza mediante la clase *Type Collector* que sigue los siguientes pasos: +Esta fase se realiza mediante la clase _Type Collector_ que sigue los siguientes pasos: -- Definición de los *built-in types*, o sea, los tipos que son inherentes al lenguaje Cool : *Int*, *String*, *Bool*, *IO*, *Object*; incluyendo la definición de sus métodos. Además se añaden como tipos *SELF_TYPE*, *AUTO_TYPE*. +- Definición de los _built-in types_, o sea, los tipos que son inherentes al lenguaje Cool : _Int_, _String_, _Bool_, _IO_, _Object_; incluyendo la definición de sus métodos. Además se añaden como tipos _SELF_TYPE_, _AUTO_TYPE_. - Recorrido por las declaraciones hechas en el programa recolectando los tipos creados. -- Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna *Object* como padre. +- Chequeo de los padres que están asignados a cada tipo. Como las clases pueden definirse de modo desordenado, el chequeo de la asignación correcta de padres para cada clase debe hacerse después de recolectar los tipos. De esta forma es posible capturar errores como que un tipo intente heredar de otro que no existe. Aquellas clases que no tengan un padre explícito se les asigna _Object_ como padre. - Chequeo de herencia cíclica. En caso de detectar algún ciclo en la jerarquía de tipos, se reporta el error, y a la clase por la cual hubo problema se le asigna Object como padre, para continuar el análisis. - Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. #### Construcción de tipos -La construcción de tipos se desarrolla empleando la clase *Type Builder*. Esta se encarga de visitar los *features* de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. +La construcción de tipos se desarrolla empleando la clase _Type Builder_. Esta se encarga de visitar los _features_ de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. Además se encarga de chequear la existencia del tipo **Main** con su método **main** correspondiente, como es requerido en COOL. -En esta clase también se hace uso de la clase *Inferencer Manager* que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO_TYPE se le asigna un *id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. +En esta clase también se hace uso de la clase _Inferencer Manager_ que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO*TYPE se le asigna un \_id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. #### Chequeo e Inferencia de tipos -En primer lugar se utiliza la clase *Type Checker* para validar el correcto uso de los tipos definidos. Toma la instancia de clase *Inferencer Manager* utilizada en el *Type Builder* para continuar la asignación de *id* a otros elementos en el código que también pueden estar definidos como *AUTO_TYPE*, como es el caso de las variables definidas en la expresión *Let*. Las variables definidas en el *Scope* se encargarán de guardar el *id* asignado; en caso de que no se les haya asignado ninguno, el id será *None*. +En primer lugar se utiliza la clase _Type Checker_ para validar el correcto uso de los tipos definidos. Toma la instancia de clase _Inferencer Manager_ utilizada en el _Type Builder_ para continuar la asignación de _id_ a otros elementos en el código que también pueden estar definidos como _AUTO_TYPE_, como es el caso de las variables definidas en la expresión _Let_. Las variables definidas en el _Scope_ se encargarán de guardar el _id_ asignado; en caso de que no se les haya asignado ninguno, el id será _None_. -La instancia de *Scope* creada en el *Type Checker*, así como la de *Inferencer Manager* se pasarán al *Type Inferencer* para realizar la inferencia de tipos. +La instancia de _Scope_ creada en el _Type Checker_, así como la de _Inferencer Manager_ se pasarán al _Type Inferencer_ para realizar la inferencia de tipos. -Ahora bien, la clase Inferencer Manager guarda las listas *conforms_to*, *conformed_by*, *infered_type*. El *id* asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. +Ahora bien, la clase Inferencer Manager guarda las listas _conforms_to_, _conformed_by_, _infered_type_. El _id_ asignado a una variable representa la posición donde se encuentra la información relacionada a la misma en las listas. -Sea una variable con *id = i*, que está definida como *AUTO_TYPE* y sea *A* el tipo estático que se ha de inferir: +Sea una variable con _id = i_, que está definida como _AUTO_TYPE_ y sea _A_ el tipo estático que se ha de inferir: -- `conforms_to[i]` guarda una lista con los tipos a los que debe conformarse *A*; note que esta lista contiene al menos al tipo *Object*. El hecho de que *A* se conforme a estos tipos, implica que todos ellos deben encontrarse en el camino de él a Object en el árbol de jerarquía de tipos. En caso contrario se puede decir que hubo algún error en la utilización del *AUTO_TYPE* para esta variable. Sea *B* el tipo más lejano a *Object* de los que aparecen en la lista. -- `conformed_by[i]` almacena una lista con los tipos que deben conformarse a *A*. Luego el menor ancestro común (*LCA - Lowest Common Ancestor*) de dichos tipos deberá conformarse a A. Note que este siempre existirá, pues en caso peor será *Object*, que es la raíz del árbol de tipos. Sea *C* el *LCA* de los tipos guardados. Note que si la lista está vacía, (que puede suceder) *C* será *None*. -- Como *C* se conforma a *A* y *A* se conforma a *B*, tiene que ocurrir que *C* se conforma a *B*. En caso contrario, se reporta un uso incorrecto de *AUTO_TYPE* para esa variable. Todos los tipos en el camino entre *B* y *C* son válidos para inferir *A*; pues cumplen con todas las restricciones que impone el programa. En nuestro caso se elige *C*, que es el tipo más restringido, para la inferencia. En caso de que *C* sea *None* se toma *B* como tipo de inferencia. -- `infered_type[i]` guardará el tipo inferido una vez realizado el procedimiento anterior; mientras tanto su valor es *None*. +- `conforms_to[i]` guarda una lista con los tipos a los que debe conformarse _A_; note que esta lista contiene al menos al tipo _Object_. El hecho de que _A_ se conforme a estos tipos, implica que todos ellos deben encontrarse en el camino de él a Object en el árbol de jerarquía de tipos. En caso contrario se puede decir que hubo algún error en la utilización del _AUTO_TYPE_ para esta variable. Sea _B_ el tipo más lejano a _Object_ de los que aparecen en la lista. +- `conformed_by[i]` almacena una lista con los tipos que deben conformarse a _A_. Luego el menor ancestro común (_LCA - Lowest Common Ancestor_) de dichos tipos deberá conformarse a A. Note que este siempre existirá, pues en caso peor será _Object_, que es la raíz del árbol de tipos. Sea _C_ el _LCA_ de los tipos guardados. Note que si la lista está vacía, (que puede suceder) _C_ será _None_. +- Como _C_ se conforma a _A_ y _A_ se conforma a _B_, tiene que ocurrir que _C_ se conforma a _B_. En caso contrario, se reporta un uso incorrecto de _AUTO_TYPE_ para esa variable. Todos los tipos en el camino entre _B_ y _C_ son válidos para inferir _A_; pues cumplen con todas las restricciones que impone el programa. En nuestro caso se elige _C_, que es el tipo más restringido, para la inferencia. En caso de que _C_ sea _None_ se toma _B_ como tipo de inferencia. +- `infered_type[i]` guardará el tipo inferido una vez realizado el procedimiento anterior; mientras tanto su valor es _None_. -La clase *Inferencer Manager* además, está equipada con métodos para actualizar las listas dado un *id*, y para realizar la inferencia dados los tipos almacenados. +La clase _Inferencer Manager_ además, está equipada con métodos para actualizar las listas dado un _id_, y para realizar la inferencia dados los tipos almacenados. -El *Type Inferencer* por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: +El _Type Inferencer_ por su parte, realizará un algoritmo de punto fijo para llevar a cabo la inferencia: -1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un *ExpressionNode*, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el *manager*. +1. Realiza un recorrido del AST (Árbol de Sintaxis Abstracta) actualizando los conjuntos ya mencionados. Cuando se visita un nodo, específicamente un _ExpressionNode_, este recibe como parámetro un conjunto de tipos a los que debe conformarse la expresión; a su vez retorna el tipo estático computado y el conjunto de tipos que se conforman a él. Esto es lo que permite actualizar las listas que están almacenadas en el _manager_. 2. Infiere todos los tipos que pueda con la información recogida. -3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. - - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último recorrido asignando tipo *Object* a todos los AUTO_TYPES que no pudieron ser inferidos. +3. - Si pudo inferir al menos uno nuevo, regresa al punto 1; puesto que este tipo puede influir en la inferencia de otros. + - Si no pudo inferir ninguno, significa que ya no hay más información que se pueda inferir, por tanto se realiza un último recorrido asignando tipo _Object_ a todos los AUTO_TYPES que no pudieron ser inferidos. + +> Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista _conforms_to_ contiene a otro tipo distinto de Object o su lista _conformed_by_ contiene al menos un tipo. + +Por último se realiza un nuevo recorrido del _AST_ con el _Type Checker_ para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los _AUTO_TYPES_ sustituidos por el tipo inferido. + +#### Traducción de COOL a CIL + +Se definió un _visitor_ en el que se recorre todo el _ast_ generado en etapas anteriores y que recibe el contexto, que también fue creado previamente, para tener la información relacionada a los tipos que se encuentren en el código. El objetivo fundamental de este recorrido es generar otro _ast_ que posee estructuras pertenecientes a CIL y que hará más fácil la generación de código MIPS posteriormente. Además, se generan chequeos que permitirán lanzar errores en tiempo de ejecución. + +Primero que todo, se generan todos los tipos pertenecientes a COOL por defecto. Para ello, por cada tipo se crea un nodo que contenga sus atributos y funciones, lo que permite luego generarlos en MIPS. Después de este paso, comienza en sí el recorrido al _ast_ de COOL. + +En este recorrido se generan las 3 principales estructuras que posee el código de CIL: -> Se considera que un tipo puede ser inferido, si no ha sido inferido anteriormente, y si su lista *conforms_to* contiene a otro tipo distinto de Object o su lista *conformed_by* contiene al menos un tipo. +- los **tipos**, donde se guarda un resumen de los _features_ de cada uno de los tipos declarados en el código, +- los **datos**, sección en la que se encuentran todas las "macros" que serán utilizadas durante la ejecución, +- el **código**, donde son colocadas todas las funciones generadas a partir del recorrido. -Por último se realiza un nuevo recorrido del *AST* con el *Type Checker* para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los *AUTO_TYPES* sustituidos por el tipo inferido. +Dentro de los errores chequeados en el ast de CIL se encuentra la comprobación de que no se realicen divisiones por 0. Aunque es cierto que el valor se tiene mayormente en tiempo de ejecución, agregar esta comprobación en el ast de CIL hace mucho más sencillo el proceso de recorrido de este ast posteriormente. También se chequea el llamado a una función dinámica de una variable de tipo _void_ y los índices en _strings_ fuera de rango. +En el caso del _case_ se chequea que la expresión principal no sea de tipo _void_ y además, que se conforme a alguna rama en la ejecución de este. El algoritmo empleado para reconocer por cuál de las ramas continuará la ejecución del código comienza por: tomar el tipo de todas las ramas del _case_, llámese a este conjunto $A$; por cada elemento del conjunto $A$ se toma la cantidad de tipos dentro del propio conjunto que se conforman a $a_i, i \in [1, |A|]$ ,de modo que se obtienen los pares $$. Se define $|\{a_j \leq a_i, \forall j, j\in[1, |A|]\}|$ como $a_{i_c}$. Tomando los elementos $a_i$ por el que menor $a_{i_c}$ tenga, se estará tomando los nodos más abajos en el árbol de tipos dentro de cada posible rama de este. Si se ordenan las ramas del _case_ por el que menor $a_{i_c}$ se obtendrá una lista. Luego se recorre esta generando por cada elemento el subconjunto $B_i$ donde $b_{i_i} \in B_i$ si $b_{i_i} <= a_i$. Se chequea si el tipo de la expresión principal del _case_ aparece en este subconjunto. En el caso de que aparezca, el case se resuelve yendo por la rama que posee el tipo $a_i$. ## Licencia -Este proyecto se encuentra bajo la Licencia (MIT License) - ver el archivo [LICENSE.md](LICENSE.md) para más detalles. \ No newline at end of file +Este proyecto se encuentra bajo la Licencia (MIT License) - ver el archivo [LICENSE.md](LICENSE.md) para más detalles. From e8535e865dd146e4b50d511cd362732f7af6aeb3 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 02:30:31 -0500 Subject: [PATCH 114/138] comment cil tree print --- src/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index a93f34dcd..7a94dc174 100644 --- a/src/main.py +++ b/src/main.py @@ -83,8 +83,8 @@ def main(args): cil_visitor = COOLToCILVisitor(context) cil_ast = cil_visitor.visit(ast, scope) - cil_formatter = PrintCILVisitor() - print(cil_formatter.visit(cil_ast)) + # cil_formatter = PrintCILVisitor() + # print(cil_formatter.visit(cil_ast)) if __name__ == "__main__": From 0964fbe738764e19da55174051e13ce47c5925cc Mon Sep 17 00:00:00 2001 From: dgd Date: Sat, 26 Feb 2022 04:10:15 -0500 Subject: [PATCH 115/138] fixing bugs --- mips_test.asm | 0 src/compiler/cmp/mips_ast.py | 10 +- src/compiler/visitors/cil2mips.py | 476 +++++++++++++++++--------- src/compiler/visitors/cool2cil.py | 2 +- src/compiler/visitors/mips_printer.py | 38 +- src/hello_world.cl | 5 + src/main.py | 9 +- src/mips_test.asm | 149 ++++++++ 8 files changed, 490 insertions(+), 199 deletions(-) create mode 100644 mips_test.asm create mode 100644 src/hello_world.cl create mode 100644 src/mips_test.asm diff --git a/mips_test.asm b/mips_test.asm new file mode 100644 index 000000000..e69de29bb diff --git a/src/compiler/cmp/mips_ast.py b/src/compiler/cmp/mips_ast.py index 72cb05921..424f6c23b 100644 --- a/src/compiler/cmp/mips_ast.py +++ b/src/compiler/cmp/mips_ast.py @@ -58,7 +58,8 @@ class Node: class ProgramNode(Node): - def __init__(self, data, text): + def __init__(self, types, data, text): + self.types = types self.data = data self.text = text @@ -74,7 +75,7 @@ def __init__(self, label, params, localvars): self.label = label self.params = params self.localvars = localvars - self.instrucctions = [] + self.instructions = [] class TypeNode(Node): @@ -82,6 +83,7 @@ def __init__(self, label): self.label = label self.methods = [] self.attributes = [] + self.label_name = None self.pos = -1 @@ -229,5 +231,5 @@ def __init__(self, register, offset): class LabelRelativeLocation: def __init__(self, label, offset): - self._label = label - self._offset = offset + self.label = label + self.offset = offset diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index 169adb947..fc50831f6 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -37,22 +37,22 @@ def register_function(self, function_name, params, locals_var): ) self.text[function_name] = function_node return function_node - + def to_label_count_address(self, label_name): - self.label_count +=1 - return f'{label_name}_{self.label_count}' + self.label_count += 1 + return f"{label_name}_{self.label_count}" def to_type_label_address(self, type_name): - return f"type_{type_name}" + return f"{type_name}" def to_data_label_address(self, data_name): - return f"data_{data_name}" - + return f"{data_name}" + def to_label_local_address(self, label_name): return f"{self.current_function.label}_{label_name}" - def to_function_label_address(self,type_name, function_name): - return f"{function_name}_at_{type_name}" + def to_function_label_address(self, function_name): + return f"{function_name}" def make_callee_init_instructions(self, function_node: mips.FunctionNode): push_fp = mips.push_to_stack(mips.FP) @@ -66,7 +66,7 @@ def make_callee_final_instructions(self, function_node: mips.FunctionNode): set_sp = mips.AddInmediateNode(mips.SP, mips.SP, local_vars_frame_size) pop_FP = mips.pop_from_stack(mips.FP) final = None - if function_node.name == mips.MAIN_FUNCTION_NAME: + if function_node.label == mips.MAIN_FUNCTION_NAME: final = mips.exit_program() else: final = mips.JumpRegister(mips.RA) @@ -75,25 +75,26 @@ def make_callee_final_instructions(self, function_node: mips.FunctionNode): def get_param_var_index(self, name): index = self.current_function.params.index(name) - offset = ((len(self._params) - 1) - index) * 4 + offset = ((len(self.current_function.params) - 1) - index) * 4 return mips.RegisterRelativeLocation(mips.FP, offset) def get_local_var_index(self, name): index = self.current_function.localvars.index(name) - offset = (index + 2) * - 4 - return mips.RegisterRelativeLocation(mips.FP offset) - + offset = (index + 2) * -4 + return mips.RegisterRelativeLocation(mips.FP, offset) + def get_var_location(self, name): try: - return self.get_param_var_index()(name) + return self.get_param_var_index(name) except ValueError: - return self.get_local_var_index()(name) + return self.get_local_var_index(name) def get_type_size(self, type_name): return len(self.types[type_name].attributes) * 4 -class BaseCILToMIPSVisitor(BaseCILToMIPSVisitor): - @visitor.on(Node) + +class CILToMIPSVisitor(BaseCILToMIPSVisitor): + @visitor.on("node") def visit(self, node): pass @@ -108,7 +109,7 @@ def visit(self, node: ProgramNode) -> mips.ProgramNode: for dc in node.dotcode: self.visit(dc) - return mips.ProgramNode(self.data, self.text) + return mips.ProgramNode(self.types, self.data, self.text) @visitor.when(TypeNode) def visit(self, node: TypeNode): @@ -137,25 +138,29 @@ def visit(self, node: FunctionNode): function_node.instrucctions = total_instructions self.current_function = None - @visitor.when(AssignNode) def visit(self, node): instructions = [] if type(node.source) == VoidNode: - instructions.append(mips.StoreWordNode(mips.ZERO, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.ZERO, self.get_var_location(node.dest)) + ) return instructions - + if node.source.isnumeric(): instructions.append(mips.LoadInmediateNode(mips.A0, int(node.source))) else: - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.source))) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) + ) - instructions.append(mips.StoreWordNode(mips.A0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) + ) return instructions - @visitor.when(PlusNode) def visit(self, node): instructions = [] @@ -163,15 +168,21 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) else: - instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) else: - instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) instructions.append(mips.AddNode(mips.T2, mips.T0, mips.T1)) - instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) return instructions @@ -182,15 +193,21 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) else: - instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) else: - instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) instructions.append(mips.SubNode(mips.T2, mips.T0, mips.T1)) - instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) return instructions @@ -201,15 +218,21 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) else: - instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) else: - instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) instructions.append(mips.MultiplyNode(mips.T2, mips.T0, mips.T1)) - instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) return instructions @@ -220,16 +243,22 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) else: - instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.left))) + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) else: - instructions.append(mips.LoadWordNode(mips.T1, self.get_var_location(node.right))) + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) - instructions.append(mips.DivideNode(mips.T0,mips.T1)) + instructions.append(mips.DivideNode(mips.T0, mips.T1)) instructions.append(mips.MoveFromLowNode(mips.MoveFromLowNode(mips.T2))) - instructions.append(mips.StoreWordNode(mips.T2, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) return instructions @@ -240,15 +269,21 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) else: - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) - + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) else: - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) instructions.append(mips.JumpAndLinkNode("less_equal")) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) return instructions @@ -259,15 +294,21 @@ def visit(self, node): if type(node.left) == int: instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) else: - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) - + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) else: - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) - + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) + instructions.append(mips.JumpAndLinkNode("less")) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) return instructions @@ -280,31 +321,42 @@ def visit(self, node): elif type(node.left) == VoidNode: instructions.append(mips.LoadInmediateNode(mips.A0, 0)) else: - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) - + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) elif type(node.right) == VoidNode: instructions.append(mips.LoadInmediateNode(mips.A1, 0)) else: - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) instructions.append(mips.JumpAndLinkNode("equals")) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) - + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + return instructions @visitor.when(EqualStrNode) def visit(self, node): instructions = [] - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.left))) - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.right))) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) instructions.append(mips.JumpAndLinkNode("equal_str")) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) - return instructions - + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + return instructions @visitor.when(NotNode) def visit(self, node): @@ -317,11 +369,15 @@ def visit(self, node): if type(node.obj) == int: instructions.append(mips.LoadInmediateNode(mips.T0, node.obj)) else: - instructions.append(mips.LoadWordNode(mips.T0, self.get_var_location(node.obj))) + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.obj)) + ) instructions.append(mips.ComplementNode(mips.T1, mips.T0)) instructions.append(mips.AddInmediateNode(mips.T1, mips.T1, 1)) - instructions.append(mips.StoreWordNode(mips.T1, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.T1, self.get_var_location(node.dest)) + ) return instructions @@ -342,33 +398,35 @@ def visit(self, node): pass @visitor.when(AllocateNode) - def visit(self, node:AllocateNode): + def visit(self, node: AllocateNode): instructions = [] type_size = self.get_type_size(node.type) instructions.append(mips.LoadInmediateNode(mips.A0, type_size)) instructions.append(mips.LoadInmediateNode(mips.V0, 9)) instructions.append(mips.SyscallNode()) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) return instructions - - - @visitor.when(ArrayNode) def visit(self, node): pass @visitor.when(TypeOfNode) - def visit(self, node:TypeOfNode): + def visit(self, node: TypeOfNode): instructions = [] instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.obj))) - - instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, 0))) - instructions.append(mips.StoreWordNode(mips.A1, self.get_var_location(node.dest))) - return instructions + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, 0)) + ) + instructions.append( + mips.StoreWordNode(mips.A1, self.get_var_location(node.dest)) + ) + return instructions @visitor.when(NameNode) def visit(self, node): @@ -377,63 +435,86 @@ def visit(self, node): instructions.append(mips.LoadAddressNode(mips.A0, mips.TYPE_LIST)) tp_number = self.types[node.name].index - instructions.append(mips.AddInmediateUnsignedNode(mips.a0, mips.a0, tp_number*4)) - instructions.append(mips.LoadWordNode(mips.a0, mips.RegisterRelativeLocation(mips.a0, 0))) + instructions.append( + mips.AddInmediateUnsignedNode(mips.a0, mips.a0, tp_number * 4) + ) + instructions.append( + mips.LoadWordNode(mips.a0, mips.RegisterRelativeLocation(mips.a0, 0)) + ) - instructions.append(mips.StoreWordNode(mips.a0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.a0, self.get_var_location(node.dest)) + ) - return instructions + return instructions @visitor.when(LabelNode) - def visit(self, node:LabelNode): + def visit(self, node: LabelNode): return [mips.LabelNode(self.to_label_local_address(node.label))] - - @visitor.when(StaticCallNode) - def visit(self, node:StaticCallNode): + def visit(self, node: StaticCallNode): instructions = [] - instructions.append(mips.JumpAndLinkNode(self.to_function_label_address(node.function))) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + instructions.append( + mips.JumpAndLinkNode(self.to_function_label_address(node.function)) + ) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) if self.pushed_args > 0: - instructions.append(mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4)) + instructions.append( + mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4) + ) self.pushed_args = 0 return instructions @visitor.when(DynamicCallNode) - def visit(self, node:DynamicCallNode): + def visit(self, node: DynamicCallNode): instructions = [] - caller_type = self.types[node.computed_type] - index = list(caller_type.methods).index(node.method) - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.type))) + caller_type = self.types[node.computed_type] + index = [m[0] for m in caller_type.methods].index(node.method) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.type)) + ) instructions.append(mips.LoadAddressNode(mips.A1, mips.VIRTUAL_TABLE)) instructions.append(mips.ShiftLeftLogicalNode(mips.A2, mips.A1, 2)) instructions.append(mips.AddUnsignedNode(mips.A1, mips.A1, mips.A2)) - instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0))) - instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 8))) + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) + ) + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 8)) + ) instructions.append(mips.AddInmediateUnsignedNode(mips.A1, mips.A1, index * 4)) - instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0))) + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) + ) instructions.append(mips.JumpRegisterAndLinkNode(mips.A1)) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) - - if self._pushed_args > 0: - instructions.append(mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4)) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + if self.pushed_args > 0: + instructions.append( + mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4) + ) self.pushed_args = 0 return instructions - @visitor.when(ArgNode) def visit(self, node): - self.pushed_args +=1 + self.pushed_args += 1 instructions = [] if type(node.name) == int: instructions.append(mips.LoadInmediateNode(mips.A0, node.name)) instructions.extend(mips.push_to_stack(mips.A0)) - else: - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.name))) + else: + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.name)) + ) instructions.extend(mips.push_to_stack(mips.A0)) return instructions @@ -446,34 +527,37 @@ def visit(self, node): elif isinstance(node.value, VoidNode): instructions.append(mips.LoadInmediateNode(mips.V0, 0)) else: - instructions.append(mips.LoadWordNode(mips.V0, self.get_var_location(node.value))) - + instructions.append( + mips.LoadWordNode(mips.V0, self.get_var_location(node.value)) + ) + return instructions @visitor.when(LoadNode) - def visit(self, node:LoadNode): + def visit(self, node: LoadNode): instructions = [] location = mips.LabelRelativeLocation(self.data[node.msg.name].label, 0) - instructions.append(mips.LoadAddressNode(mips.A0,location )) - instructions.append(mips.StoreWordNode(mips.A0, self.get_var_location(node.dest))) + instructions.append(mips.LoadAddressNode(mips.A0, location)) + instructions.append( + mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) + ) return instructions - @visitor.when(LengthNode) def visit(self, node): instructions = [] - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.source))) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) + ) instructions.append(mips.JumpAndLinkNode("len")) - 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.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) return instructions @@ -481,17 +565,25 @@ def visit(self, node): def visit(self, node): instructions = [] - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.prefix))) - - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.suffix))) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.prefix)) + ) + + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.suffix)) + ) - instructions.append(mips.LoadWordNode(mips.A2, self.get_var_location(node.lenght))) + instructions.append( + mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) + ) instructions.append(mips.JumpAndLinkNode("concat")) - - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) - return instructions + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions @visitor.when(PrefixNode) def visit(self, node): @@ -501,47 +593,59 @@ def visit(self, node): def visit(self, node): instructions = [] - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.str_value))) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.str_value)) + ) if type(node.index) == int: instructions.append(mips.LoadInmediateNode(mips.A1, node.index)) else: - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.index))) - + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.index)) + ) + if type(node.length) == int: instructions.append(mips.LoadInmediateNode(mips.A2, node.length)) else: - instructions.append(mips.LoadWordNode(mips.A2, self.get_var_location(node.length))) - + instructions.append( + mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) + ) + instructions.append(mips.JumpAndLinkNode("substr")) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) return instructions - @visitor.when(ReadStrNode) def visit(self, node): instructions = [] instructions.append(mips.JumpAndLinkNode("read_str")) - - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) - return instructions + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + return instructions @visitor.when(PrintIntNode) def visit(self, node): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0, 1)) - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.value))) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) + ) instructions.append(mips.SyscallNode()) return instructions - + @visitor.when(PrintStrNode) def visit(self, node): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0, 4)) - instructions.append(mips.LoadWordNode(mips.A0. self.get_var_location(node.value))) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) + ) instructions.append(mips.SyscallNode()) return instructions @@ -549,29 +653,37 @@ def visit(self, node): def visit(self, node): instructions = [] - mips_label = self._data_section[node.data_node.name].label + mips_label = self.data[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.LoadInmediateNode(mips.V0, 4)) + instructions.append(mips.LoadAddressNode(mips.A0, mips_label)) instructions.append(mips.SyscallNode()) - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.LoadInmediateNode(mips.V0, 10)) instructions.append(mips.SyscallNode()) return instructions @visitor.when(TypeNameNode) - def visit(self, node:TypeNameNode): + def visit(self, node: TypeNameNode): instructions = [] - - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.source))) - instructions.append(mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0,0) )) + + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) + ) + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) + ) instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) instructions.append(mips.LoadAddressNode(mips.A1, mips.TYPE_LIST)) instructions.append(mips.AddUnsignedNode(mips.A0, mips.A0, mips.A1)) - instructions.append(mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0))) - - instructions.append(mips.StoreWordNode(mips.A0, self.get_var_location(node.dest))) + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) + ) + + instructions.append( + mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) + ) return instructions @@ -583,93 +695,115 @@ def visit(self, node): return instructions - @visitor.when(GetAttribNode) - def visit(self, node:GetAttribNode): - + def visit(self, node: GetAttribNode): + instructions = [] 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 + comp_type = ( + node.computed_type + if type(node.computed_type) == str + else node.computed_type.name + ) instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) - - tp = self._types[comp_type] - offset = 12 + tp.attributes.index(node.attr) * 4 - instructions.append(mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset))) + + tp = self.types[comp_type] + offset = 12 + tp.attributes.index(node.attr) * 4 + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) + ) instructions.append(mips.StoreWordNode(mips.A1, self.get_var_location(dest))) return instructions @visitor.when(SetAttribNode) - def visit(self, node:SetAttribNode): - + def visit(self, node: SetAttribNode): + instructions = [] 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 + comp_type = ( + node.computed_type + if type(node.computed_type) == str + else node.computed_type.name + ) - tp = self._types[comp_type] - offset = 12 + tp.attributes.index(node.attr) * 4 + tp = self.types[comp_type] + offset = 12 + tp.attributes.index(node.attr) * 4 instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) if type(node.value) == int: instructions.append(mips.LoadInmediateNode(mips.A1, node.value)) else: - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.value))) + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.value)) + ) + + instructions.append( + mips.StoreWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) + ) - instructions.append(mips.StoreWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset))) - return instructions - - + @visitor.when(CopyNode) def visit(self, node): instructions = [] - instructions.extend(mips.push_register(mips.A1)) + instructions.extend(mips.push_to_stack(mips.A1)) - instructions.append(mips.LoadWordNode(mips.A1, self.get_var_location(node.source))) - - instructions.append(mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A1, 4))) + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.source)) + ) + + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A1, 4)) + ) instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) instructions.append(mips.JumpAndLinkNode("malloc")) instructions.append(mips.MoveNode(mips.A2, mips.A0)) instructions.append(mips.MoveNode(mips.A0, mips.A1)) instructions.append(mips.MoveNode(mips.A1, mips.V0)) - instructions.append(mips.JumpAndLinkNode("copy")) + instructions.append(mips.JumpAndLinkNode("copy")) - instructions.extend(mips.pop_register(mips.A1)) + instructions.extend(mips.pop_from_stack(mips.A1)) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) return instructions - + @visitor.when(GotoIfNode) def visit(self, node): instructions = [] local_label = self.to_label_local_address(node.label) - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.condition))) - + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.condition)) + ) + instructions.append(mips.BranchOnNotEqualNode(mips.A0, mips.ZERO, local_label)) - - return instructions - + + return instructions + @visitor.when(GotoNode) def visit(self, node): local_label = self.to_label_local_address(node.label) return [mips.JumpNode(local_label)] - + @visitor.when(ReadIntNode) def visit(self, node): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0, 5)) instructions.append(mips.SyscallNode()) - instructions.append(mips.StoreWordNode(mips.V0, self.get_var_location(node.dest))) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) - return instructions \ No newline at end of file + return instructions diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 1f0e1be29..5366390f5 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -498,7 +498,7 @@ def visit(self, node, scope): self.init_name(node.id, attr=True) ) self.register_param(self.vself) - if node.parent != "Object" and node.parent != "IO": + if node.parent.lex != "Object" and node.parent.lex != "IO": self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction( cil.StaticCallNode(self.init_name(node.parent, attr=True), vtemp) diff --git a/src/compiler/visitors/mips_printer.py b/src/compiler/visitors/mips_printer.py index e8d89f3db..225c08e94 100644 --- a/src/compiler/visitors/mips_printer.py +++ b/src/compiler/visitors/mips_printer.py @@ -2,7 +2,7 @@ import compiler.visitors.visitor as visitor -class PrintVisitor: +class MIPSPrintVisitor: @visitor.on("node") def visit(self, node): pass @@ -22,37 +22,31 @@ 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.data] - ) + static_strings = "\n".join([self.visit(node.data[d]) for d in node.data]) names_table = f"{TYPE_LIST}:\n" + "\n".join( - [f"\t.word\t{tp.string_name_label}" for tp in node.types] + [f"\t.word\t{node.types[tp].label}" for tp in node.types] ) proto_table = f"{VIRTUAL_TABLE}:\n" + "\n".join( - [f"\t.word\t{tp.label}_proto" for tp in node.types] + [f"\t.word\t{node.types[tp].label}_proto" for tp in node.types] ) - types = "\n\n".join([self.visit(tp) for tp in node.types]) + types = "\n\n".join([self.visit(node.types[tp]) for tp in node.types]) - code = "\n".join([self.visit(func) for func in node.functions]) + code = "\n".join([self.visit(node.text[func]) for func in node.text]) 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) + @visitor.when(DataNode) def visit(self, node): - return f'{node.label}: .asciiz "{node.string}"' + new_data = node.data.replace("\n", "") + return f'{node.label}: .asciiz "{new_data}"' - @visitor.when(MIPSType) - def visit(self, node): - methods = "\n".join([f"\t.word\t {node.methods[k]}" for k in node.methods]) + @visitor.when(TypeNode) + def visit(self, node: TypeNode): + methods = "\n".join([f"\t.word\t {m[1]}" for m 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_begin = f"{node.label}_proto:\n\t.word\t{node.pos}\n\t.word\t{len(node.attributes)*4}\n\t.word\t{node.label}_dispatch" + proto_attr = "\n".join([f"\t.word\t{0}" for attr in node.attributes]) proto_end = f"\t.word\t{-1}" proto = ( f"{proto_begin}\n{proto_attr}\n{proto_end}" @@ -84,7 +78,7 @@ def visit(self, node): @visitor.when(AddInmediateNode) def visit(self, node): - return f"addi {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.value)}" + return f"addi {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.constant_number)}" @visitor.when(StoreWordNode) def visit(self, node): @@ -132,7 +126,7 @@ def visit(self, node): @visitor.when(LabelNode) def visit(self, node): - return f"{node.name}:" + return f"{node.label}:" @visitor.when(BranchOnNotEqualNode) def visit(self, node): diff --git a/src/hello_world.cl b/src/hello_world.cl new file mode 100644 index 000000000..0c818f908 --- /dev/null +++ b/src/hello_world.cl @@ -0,0 +1,5 @@ +class Main inherits IO { + main(): IO { + out_string("Hello, World.\n") + }; +}; diff --git a/src/main.py b/src/main.py index f6ff96b8e..5cc77df54 100644 --- a/src/main.py +++ b/src/main.py @@ -17,6 +17,8 @@ from compiler.visitors.type_inferencer import TypeInferencer from compiler.visitors.cool2cil import COOLToCILVisitor from compiler.visitors.cil_formatter import PrintCILVisitor +from compiler.visitors.cil2mips import CILToMIPSVisitor +from compiler.visitors.mips_printer import MIPSPrintVisitor def main(args): @@ -92,7 +94,12 @@ def main(args): cil_ast = cil_visitor.visit(ast, scope) cil_formatter = PrintCILVisitor() - print(cil_formatter.visit(cil_ast)) + # print(cil_formatter.visit(cil_ast)) + + mips_visitor = CILToMIPSVisitor() + mips_ast = mips_visitor.visit(cil_ast) + mips_formatter = MIPSPrintVisitor() + print(mips_formatter.visit(mips_ast)) text = """ diff --git a/src/mips_test.asm b/src/mips_test.asm new file mode 100644 index 000000000..27853e5f8 --- /dev/null +++ b/src/mips_test.asm @@ -0,0 +1,149 @@ + .data +data_0: .asciiz "Abort called from class " +data_1: .asciiz "" +data_2: .asciiz "Substring out of range" +data_3: .asciiz "Hello, World." +data_4: .asciiz "(3, 2) - RuntimeError: Dispatch on void" + +type_list: + .word Object + .word IO + .word String + .word Int + .word Bool + .word Main + +virtual_table: + .word Object_proto + .word IO_proto + .word String_proto + .word Int_proto + .word Bool_proto + .word Main_proto + +Object_dispatch: + .word function_abort_at_Object + .word function_type_name_at_Object + .word function_copy_at_Object + .word init_at_Object + +Object_proto: + .word 0 + .word 0 + .word Object_dispatch + .word -1 + +IO_dispatch: + .word function_abort_at_Object + .word function_type_name_at_Object + .word function_copy_at_Object + .word function_out_string_at_IO + .word function_out_int_at_IO + .word function_in_string_at_IO + .word function_in_int_at_IO + .word init_at_IO + +IO_proto: + .word 1 + .word 0 + .word IO_dispatch + .word -1 + +String_dispatch: + .word function_abort_at_Object + .word function_type_name_at_Object + .word function_copy_at_Object + .word function_length_at_String + .word function_concat_at_String + .word function_substr_at_String + .word init_at_String + +String_proto: + .word 2 + .word 8 + .word String_dispatch + .word 0 + .word 0 + .word -1 + +Int_dispatch: + .word function_abort_at_Object + .word function_type_name_at_Object + .word function_copy_at_Object + .word init_at_Int + +Int_proto: + .word 3 + .word 4 + .word Int_dispatch + .word 0 + .word -1 + +Bool_dispatch: + .word function_abort_at_Object + .word function_type_name_at_Object + .word function_copy_at_Object + .word init_at_Bool + +Bool_proto: + .word 4 + .word 4 + .word Bool_dispatch + .word 0 + .word -1 + +Main_dispatch: + .word function_abort_at_Object + .word function_type_name_at_Object + .word function_copy_at_Object + .word function_out_string_at_IO + .word function_out_int_at_IO + .word function_in_string_at_IO + .word function_in_int_at_IO + .word function_main_at_Main + +Main_proto: + .word 5 + .word 0 + .word Main_dispatch + .word -1 + .text + .globl main +entry: + +init_at_Object: + +function_abort_at_Object: + +function_type_name_at_Object: + +function_copy_at_Object: + +init_at_IO: + +function_out_string_at_IO: + +function_out_int_at_IO: + +function_in_string_at_IO: + +function_in_int_at_IO: + +init_at_String: + +function_length_at_String: + +function_concat_at_String: + +function_substr_at_String: + +init_at_Int: + +init_at_Bool: + +function_main_at_Main: + +init_at_Main: + +init_attr_at_Main: + From 23d0019dd7335ec2bdb71a187a11fcef745b9f18 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 06:45:28 -0500 Subject: [PATCH 116/138] fix bug --- src/compiler/visitors/cool2cil.py | 51 +++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 5366390f5..b98db2d56 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -73,9 +73,13 @@ def instructions(self): def register_local(self, vinfo, id=False): new_vinfo = VariableInfo("", None) - new_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" + ): + new_vinfo.name = f"local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}" + else: + new_vinfo.name = f"local_{self.current_function.name[5:]}_{vinfo.name}_{len(self.localvars)}" local_node = cil.LocalNode(new_vinfo.name) if id: @@ -428,10 +432,10 @@ 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.init_name("Main"), instance)) self.register_instruction(cil.ArgNode(instance)) - result = self.define_internal_local() main_method_name = self.to_function_name("main", "Main") self.register_instruction(cil.StaticCallNode(main_method_name, result)) self.register_instruction(cil.ReturnNode(0)) @@ -555,12 +559,12 @@ def visit(self, node, scope): self.to_function_name(self.current_method.name, self.current_type.name) ) - self.params.append(cil.ParamNode(self.vself.name)) - self.params.extend([cil.ParamNode(p) for p in self.current_method.param_names]) + self.register_param(self.vself) + for param_name, _ in node.params: + self.register_param(VariableInfo(param_name, None)) value = self.visit(node.body, scope) - - # Your code here!!! (Handle RETURN) + # (Handle RETURN) if value is None: self.register_instruction(cil.ReturnNode("")) elif self.current_function.name == "entry": @@ -611,6 +615,35 @@ def visit(self, node, scope): # node.type -> str ############################### + # static call node + if node.obj.lex == self.vself.name: + args = [] + for arg in node.args: + vname = self.register_local( + VariableInfo(f"{node.id}_arg", None), id=True + ) + ret = self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, ret)) + args.append(cil.ArgNode(vname)) + 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) + + type_of_node = self.register_local( + VariableInfo(f"{self.vself.name}_type", None) + ) + self.register_instruction(cil.TypeOfNode(type_of_node, self.vself.name)) + self.register_instruction( + cil.DynamicCallNode( + type_of_node, node.id, result, self.current_type.name + ) + ) + return result + args = [] for arg in node.args: vname = self.register_local(VariableInfo(f"{node.id}_arg", None), id=True) @@ -650,7 +683,7 @@ def visit(self, node, scope): type_of_node = self.register_local( VariableInfo(f"{node.id}_type", None), id=True ) - self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) + self.register_instruction(cil.TypeOfNode(type_of_node, vobj)) computed_type = node.obj.computed_type if computed_type.name == "SELF_TYPE": computed_type = computed_type.fixed_type From d1307bab23dbcfb1739e300fd8b53e6cdd53c08b Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 07:55:03 -0500 Subject: [PATCH 117/138] fix bugs --- src/compiler/visitors/cool2cil.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index b98db2d56..0ee769b63 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -502,7 +502,11 @@ def visit(self, node, scope): self.init_name(node.id, attr=True) ) self.register_param(self.vself) - if node.parent.lex != "Object" and node.parent.lex != "IO": + if ( + node.parent is not None + and node.parent.lex != "Object" + and node.parent.lex != "IO" + ): self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction( cil.StaticCallNode(self.init_name(node.parent, attr=True), vtemp) @@ -561,7 +565,7 @@ def visit(self, node, scope): self.register_param(self.vself) for param_name, _ in node.params: - self.register_param(VariableInfo(param_name, None)) + self.register_param(VariableInfo(param_name.lex, None)) value = self.visit(node.body, scope) # (Handle RETURN) @@ -616,7 +620,7 @@ def visit(self, node, scope): ############################### # static call node - if node.obj.lex == self.vself.name: + if isinstance(node.obj, VariableNode) and node.obj.lex == self.vself.name: args = [] for arg in node.args: vname = self.register_local( @@ -768,14 +772,11 @@ def visit(self, node, scope): ####################################### # node.expr_list -> [ ExpressionNode ... ] ####################################### - ret = self.register_local(VariableInfo("block_node_value", None)) for expr in node.expr_list: ret_value = self.visit(expr, scope) - self.register_instruction(cil.AssignNode(ret, ret_value)) - - return ret + return ret_value @visitor.when(LetNode) def visit(self, node, scope): @@ -824,7 +825,7 @@ def visit(self, node, scope): ret_val = self.visit(node.expr, scope) self.register_instruction(cil.AssignNode(ret, ret_val)) - self.register_instruction(cil.TypeOfNode(ret_type, ret_val)) + self.register_instruction(cil.TypeNameNode(ret_type, ret_val)) # Check if node.expr is void and raise proper error if vexpr value is void void = cil.VoidNode() @@ -985,7 +986,7 @@ def visit(self, node, scope): left = self.visit(node.left, scope) right = self.visit(node.right, scope) - self.register_instruction(cil.TypeOfNode(type_left, left)) + 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")) @@ -1213,7 +1214,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.init_name("Int"), instance)) - scope.ret_expr = instance + return instance @visitor.when(ConstantStringNode) def visit(self, node, scope): From fe182fe02fc2be91222a6880d127fa90ed956718 Mon Sep 17 00:00:00 2001 From: dgd Date: Sat, 26 Feb 2022 08:13:31 -0500 Subject: [PATCH 118/138] allocate with malloc --- src/compiler/visitors/cil2mips.py | 42 +- src/main.py | 9 +- src/mips_lib.asm | 1152 +++++++++++++++++++++++++++++ 3 files changed, 1194 insertions(+), 9 deletions(-) create mode 100644 src/mips_lib.asm diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index fc50831f6..6f1d9c5e1 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -22,9 +22,11 @@ def __init__(self): self.type_count = 0 def register_type(self, type_name): - type_node = mips.TypeNode(self.to_type_label_address(type_name)) + label_name = self.to_type_label_address(type_name) + type_node = mips.TypeNode(label_name) self.types[type_name] = type_node - return type_node + self.register_data(label_name, type_name) + return (type_node, label_name) def register_data(self, data_name, data): data_node = mips.DataNode(self.to_data_label_address(data_name), data) @@ -113,10 +115,11 @@ def visit(self, node: ProgramNode) -> mips.ProgramNode: @visitor.when(TypeNode) def visit(self, node: TypeNode): - type_node = self.register_type(node.name) + type_node, label_name = self.register_type(node.name) type_node.methods = node.methods type_node.attributes = node.attributes type_node.pos = self.type_count + type_node.label_name = label_name self.type_count += 1 @visitor.when(DataNode) @@ -127,6 +130,8 @@ def visit(self, node: DataNode): def visit(self, node: FunctionNode): params = [p.name for p in node.params] local_vars = [lv.name for lv in node.localvars] + if node.name == "entry": + node.name = "main" function_node = self.register_function(node.name, params, local_vars) init_callee = self.make_callee_init_instructions(function_node) @@ -135,7 +140,7 @@ def visit(self, node: FunctionNode): final_callee = self.make_callee_final_instructions(function_node) total_instructions = list(flatten(init_callee + body + final_callee)) - function_node.instrucctions = total_instructions + function_node.instructions = total_instructions self.current_function = None @visitor.when(AssignNode) @@ -400,10 +405,31 @@ def visit(self, node): @visitor.when(AllocateNode) def visit(self, node: AllocateNode): instructions = [] - type_size = self.get_type_size(node.type) - instructions.append(mips.LoadInmediateNode(mips.A0, type_size)) - instructions.append(mips.LoadInmediateNode(mips.V0, 9)) - instructions.append(mips.SyscallNode()) + + tp = 0 + if node.type.isnumeric(): + tp = node.type + else: + tp = self.types[node.type].pos + + instructions.append(mips.LoadInmediateNode(mips.A0, tp)) + + instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + instructions.append(mips.LoadAddressNode(mips.A1, mips.VIRTUAL_TABLE)) + instructions.append(mips.AddUnsignedNode(mips.A1, mips.A1, mips.A0)) + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) + ) + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A1, 4)) + ) + instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + instructions.append(mips.JumpAndLinkNode("malloc")) + instructions.append(mips.MoveNode(mips.A2, mips.A0)) + instructions.append(mips.MoveNode(mips.A0, mips.A1)) + instructions.append(mips.MoveNode(mips.A1, mips.V0)) + instructions.append(mips.JumpAndLinkNode("copy")) + instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) diff --git a/src/main.py b/src/main.py index 5cc77df54..dcf52c1a7 100644 --- a/src/main.py +++ b/src/main.py @@ -99,7 +99,14 @@ def main(args): mips_visitor = CILToMIPSVisitor() mips_ast = mips_visitor.visit(cil_ast) mips_formatter = MIPSPrintVisitor() - print(mips_formatter.visit(mips_ast)) + mips_code = mips_formatter.visit(mips_ast) + + out_file = args.file[:-2] + "mips" + + with open(out_file, "w") as f: + f.write(mips_code) + with open("mips_lib.asm") as f2: + f.write("".join(f2.readlines())) text = """ diff --git a/src/mips_lib.asm b/src/mips_lib.asm new file mode 100644 index 000000000..608534319 --- /dev/null +++ b/src/mips_lib.asm @@ -0,0 +1,1152 @@ + + +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 +new_line = 10 +str_size_treshold = 1024 +int_type = 0 +string_type = 0 +type_number = 0 + + + +##################################################################################################### +# 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 + + +equals: + beq $a0 $a1 equals_equal + li $v0 0 + j equals_end + +equals_equal: + li $v0 1 + +equals_end: + jr $ra + + + +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 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 + 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 $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) + 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 + addiu $t3 $a2 1 + div $t3 $t1 + + mfhi $t2 + bne $t2 $zero substr_allign_size + move $t1 $t3 + j substr_new_block + +substr_allign_size: + sub $t1 $t1 $t2 + add $t1 $t1 $t3 + +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 + + +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 + + jr $ra + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8f7ad42a691645fd7690da03cf29db9497e5ee59 Mon Sep 17 00:00:00 2001 From: dgd Date: Sat, 26 Feb 2022 09:00:34 -0500 Subject: [PATCH 119/138] testing shit --- mips_test.asm | 0 src/compiler/cmp/cil.py | 304 +++++++ src/compiler/visitors/cil2mips.py | 33 +- src/compiler/visitors/cool2cil.py | 2 +- src/compiler/visitors/cool_to_cil.py | 1183 ++++++++++++++++++++++++++ src/compiler/visitors/utils.py | 5 + src/main.py | 19 +- src/mips_test.asm | 149 ---- 8 files changed, 1534 insertions(+), 161 deletions(-) delete mode 100644 mips_test.asm create mode 100644 src/compiler/cmp/cil.py create mode 100644 src/compiler/visitors/cool_to_cil.py create mode 100644 src/compiler/visitors/utils.py delete mode 100644 src/mips_test.asm diff --git a/mips_test.asm b/mips_test.asm deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/compiler/cmp/cil.py b/src/compiler/cmp/cil.py new file mode 100644 index 000000000..e274c6109 --- /dev/null +++ b/src/compiler/cmp/cil.py @@ -0,0 +1,304 @@ +# 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 + self.ids = dict() + self.labels_count = 0 + + +class ParamNode(Node): + def __init__(self, name): + self.name = name + + +class LocalNode(Node): + def __init__(self, name): + self.name = name + + +class InstructionNode(Node): + def __init__(self): + self.leader = False + + +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): + 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 LessEqualNode(ArithmeticNode): + pass + + +class LessNode(ArithmeticNode): + pass + + +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 + 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): + self.obj = obj + self.attr = attr + self.value = value + self.computed_type = computed_type + + +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 + + 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): + 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): + self.type = xtype + 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): + self.dest = dest + self.msg = msg + + def __repr__(self): + return f"{self.dest} LOAD {self.msg}" + + +class ExitNode(InstructionNode): + pass + + +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): + self.dest = dest + self.source = source + + +class LengthNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + + +class ConcatNode(InstructionNode): + 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): + self.dest = dest + self.str_value = str_value + self.index = index + self.length = length + + +class ReadStrNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + + +class ReadIntNode(InstructionNode): + def __init__(self, dest): + self.dest = 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): + self.value = value + + +class ComplementNode(InstructionNode): + def __init__(self, dest, obj): + self.dest = dest + self.obj = obj + + +class VoidNode(InstructionNode): + pass + + +class ErrorNode(InstructionNode): + def __init__(self, data_node): + self.data_node = data_node + + def __repr__(self): + return f"ERROR {self.data_node}" diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index fc50831f6..336fb6b47 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -1,5 +1,5 @@ import compiler.visitors.visitor as visitor -from ..cmp.cil_ast import * +from ..cmp.cil import * from ..cmp import mips_ast as mips @@ -262,7 +262,32 @@ def visit(self, node): return instructions - @visitor.when(LeqNode) + # @visitor.when(LeqNode) + # def visit(self, node): + # instructions = [] + + # if type(node.left) == int: + # instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + # else: + # instructions.append( + # mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + # ) + + # if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + # else: + # instructions.append( + # mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + # ) + + # instructions.append(mips.JumpAndLinkNode("less_equal")) + # instructions.append( + # mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + # ) + + # return instructions + + @visitor.when(LessEqualNode) def visit(self, node): instructions = [] @@ -298,8 +323,8 @@ def visit(self, node): mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) ) - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + if type(node.rigth) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.rigth)) else: instructions.append( mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 5366390f5..2fb8a6c2b 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -1,6 +1,6 @@ from pprint import pprint import compiler.visitors.visitor as visitor -from ..cmp import cil_ast as cil +from ..cmp import cil as cil from ..cmp.semantic import ( Scope, SemanticError, diff --git a/src/compiler/visitors/cool_to_cil.py b/src/compiler/visitors/cool_to_cil.py new file mode 100644 index 000000000..4480ab2e7 --- /dev/null +++ b/src/compiler/visitors/cool_to_cil.py @@ -0,0 +1,1183 @@ +from ..cmp.cil import cil +from .utils import get_token +import compiler.visitors.visitor as visitor +from ...cmp import cool_ast as cool, 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 + self.vself = VariableInfo("self", None) + self.value_types = ["String", "Int", "Bool"] + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def ids(self): + return self.current_function.ids + + @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, id=False): + new_vinfo = VariableInfo("", None) + if ( + len(self.current_function.name) >= 8 + and self.current_function.name[:8] == "function" + ): + new_vinfo.name = f"local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}" + else: + 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 new_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 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, [], [], []) + 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) + + def register_built_in(self): + # Object + type_node = self.register_type("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)) + + 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 == "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)) + 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?? + + 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.TypeNameNode(result, self.vself.name)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + 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") + ) + self.register_param(self.vself) + result = self.define_internal_local() + 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 += [("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.init_name("IO")) + instance = self.define_internal_local() + 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") + ) + self.register_param(self.vself) + self.register_param(VariableInfo("x", None)) + vname = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(vname, "x", "value", "String")) + 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") + ) + self.register_param(self.vself) + self.register_param(VariableInfo("x", None)) + vname = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(vname, "x", "value", "Int")) + 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") + ) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ReadStrNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + 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") + ) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ReadIntNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + 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 ["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.init_name("String")) + self.register_param(VariableInfo("val", None)) + 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.init_name("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.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") + ) + self.register_param(self.vself) + 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, length_1)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + 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") + ) + self.register_param(self.vself) + self.register_param(VariableInfo("i", None)) + self.register_param(VariableInfo("l", None)) + result = self.define_internal_local() + 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() + 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 + 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") + 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.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 ["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.init_name("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", "Int")) + 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.init_name("Int"))] + + # Bool + type_node = self.register_type("Bool") + type_node.attributes = ["value"] + + 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)) + 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.init_name("Bool"))] + + 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 = self.register_data(msg) + self.register_instruction(cil.ErrorNode(data_node)) + + self.register_instruction(continue_node) + + +class COOLToCILVisitor(BaseCOOLToCILVisitor): + def __init__(self, context): + super().__init__(context) + + def buildHierarchy(self, t: str): + if t == "Object": + return None + 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): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node, scope): + ###################################################### + # node.declarations -> [ ClassDeclarationNode ... ] + ###################################################### + + 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.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)) + # Error message raised by Object:abort() + self.register_data("Abort called from class ") + self.register_built_in() + 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) + + # init + 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)) + + func = self.current_function + vtemp = self.define_internal_local() + + # init_attr + 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.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.init_name(node.id, attr=True), vtemp) + ) + + self.register_instruction(cil.ReturnNode(instance)) + self.current_function = None + + self.current_type = None + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + if node.expr: + self.visit(node.expr, scope) + 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(self.vself.name, node.id, vtemp, self.current_type) + ) + + @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 + + self.current_function = self.register_function( + self.to_function_name(self.current_method.name, type_name) + ) + + # (Handle PARAMS) + self.register_param(self.vself) + for param_name, _ in node.params: + 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: + self.register_instruction(cil.ReturnNode("")) + elif self.current_function.name == "entry": + self.register_instruction(cil.ReturnNode(0)) + else: + self.register_instruction(cil.ReturnNode(scope.ret_expr)) + + 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)) + vcondition = self.define_internal_local() + + then_label_node = self.register_label("then_label") + else_label_node = self.register_label("else_label") + continue_label_node = self.register_label("continue_label") + + # If condition GOTO then_label + self.visit(node.condition, scope) + 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 + 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(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(continue_label_node) + scope.ret_expr = vret + + @visitor.when(cool.WhileLoopNode) + def visit(self, node, scope): + ################################### + # node.condition -> ExpressionNode + # 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") + # Label while + self.register_instruction(while_label_node) + # If condition GOTO loop + self.visit(node.condition, scope) + 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)) + # Label loop + self.register_instruction(loop_label_node) + self.visit(node.body, scope) + # GOTO while + self.register_instruction(cil.GotoNode(while_label_node.label)) + # Label pool + self.register_instruction(pool_label_node) + + # The result of a while loop is void + scope.ret_expr = cil.VoidNode() + + @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 + ############################################ + vret = self.register_local(VariableInfo("let_in_value", None)) + + 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 + + @visitor.when(cool.CaseOfNode) + 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)) + self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(vexpr, scope.ret_expr)) + self.register_instruction(cil.TypeNameNode(vtype, scope.ret_expr)) + + # 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)) + + 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 = [] + old = {} + + # 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: + self.register_instruction(cil.GotoNode(labels[-1].label)) + break + h.add(b.type) + for s in old: + h -= s + 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.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(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)) + self.register_instruction(cil.GotoNode(end_label.label)) + + scope.ret_expr = vret + self.register_instruction(end_label) + + @visitor.when(cool.CaseExpressionNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + self.visit(node.expr, scope) + + @visitor.when(cool.LetAttributeNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + 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)) + elif node.type in self.value_types: + self.register_instruction(cil.AllocateNode(node.type, vname)) + + @visitor.when(cool.AssignNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + + self.visit(node.expr, scope) + + try: + self.current_type.get_attribute(node.id) + 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] + 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) + 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.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.init_name("Bool"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.LessEqualNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + 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", "Bool")) + self.register_instruction( + cil.GetAttribNode(right_value, right, "value", "Bool") + ) + self.register_instruction(cil.LessEqualNode(vname, left_value, right_value)) + + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.LessNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + 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", "Bool")) + self.register_instruction( + cil.GetAttribNode(right_value, right, "value", "Bool") + ) + self.register_instruction(cil.LessNode(vname, left_value, right_value)) + + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.EqualNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + 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 + 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_bool, "Bool")) + 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_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)) + + 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.EqualStrNode(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) + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.PlusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + vname = self.define_internal_local() + 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", "Int") + ) + self.visit(node.right, scope) + 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)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.MinusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + vname = self.define_internal_local() + 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", "Int") + ) + self.visit(node.right, scope) + 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)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.StarNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + vname = self.define_internal_local() + 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", "Int") + ) + self.visit(node.right, scope) + 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)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.DivNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + vname = self.define_internal_local() + 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", "Int") + ) + self.visit(node.right, scope) + self.register_instruction( + cil.GetAttribNode(vright, scope.ret_expr, "value", "Int") + ) + + # Check division by 0 + equal_result = self.define_internal_local() + self.register_instruction(cil.EqualNode(equal_result, vright, 0)) + 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() + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.IsVoidNode) + def visit(self, node, scope): + ############################### + # node.expr -> ExpressionNode + ############################### + 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)) + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), result)) + scope.ret_expr = result + + @visitor.when(cool.ComplementNode) + 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.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.init_name("Int"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.FunctionCallNode) + def visit(self, node, scope): + ###################################### + # node.obj -> ExpressionNode + # node.id -> str + # node.args -> [ ExpressionNode ... ] + # node.type -> str + ##################################### + + args = [] + for arg in node.args: + 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), id=True + ) + + 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)) + + 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)) + for arg in args: + self.register_instruction(arg) + + if node.type: + # Call of type @.id(,...,) + 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), id=True + ) + self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) + 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 + + @visitor.when(cool.MemberCallNode) + 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] + + args = [] + for arg in node.args: + 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), id=True + ) + + self.register_instruction(cil.ArgNode(self.vself.name)) + for arg in args: + self.register_instruction(arg) + + 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) + 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.name, 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.init_name(node.type), instance) + ) + scope.ret_expr = instance + + @visitor.when(cool.IntegerNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(int(node.lex))) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + scope.ret_expr = instance + + @visitor.when(cool.IdNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + try: + self.current_type.get_attribute(node.lex) + 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: + 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 == n: + scope.ret_expr = n + break + else: + scope.ret_expr = self.ids[node.lex] + + @visitor.when(cool.StringNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + try: + data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] + except IndexError: + data_node = self.register_data(node.lex) + vmsg = self.register_local(VariableInfo("msg", None)) + 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.init_name("String"), instance) + ) + scope.ret_expr = instance + + @visitor.when(cool.BoolNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + if node.lex == "true": + scope.ret_expr = 1 + else: + scope.ret_expr = 0 + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(scope.ret_expr)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + scope.ret_expr = instance diff --git a/src/compiler/visitors/utils.py b/src/compiler/visitors/utils.py new file mode 100644 index 000000000..09208171f --- /dev/null +++ b/src/compiler/visitors/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/main.py b/src/main.py index 5cc77df54..fcae16281 100644 --- a/src/main.py +++ b/src/main.py @@ -15,6 +15,8 @@ from compiler.visitors.type_builder import TypeBuilder from compiler.visitors.type_checker import TypeChecker from compiler.visitors.type_inferencer import TypeInferencer + +# from compiler.visitors.cool2cil import COOLToCILVisitor from compiler.visitors.cool2cil import COOLToCILVisitor from compiler.visitors.cil_formatter import PrintCILVisitor from compiler.visitors.cil2mips import CILToMIPSVisitor @@ -90,16 +92,19 @@ def main(args): # exit(1) # mips to cil - cil_visitor = COOLToCILVisitor(context) - cil_ast = cil_visitor.visit(ast, scope) + # cil_visitor = COOLToCILVisitor(context) + # cil_ast = cil_visitor.visit(ast, scope) - cil_formatter = PrintCILVisitor() + # cil_formatter = PrintCILVisitor() # print(cil_formatter.visit(cil_ast)) - mips_visitor = CILToMIPSVisitor() - mips_ast = mips_visitor.visit(cil_ast) - mips_formatter = MIPSPrintVisitor() - print(mips_formatter.visit(mips_ast)) + # mips_visitor = CILToMIPSVisitor() + # mips_ast = mips_visitor.visit(cil_ast) + # mips_formatter = MIPSPrintVisitor() + # print(mips_formatter.visit(mips_ast)) + + cil_visitor = COOLToCILVisitor(context) + cil_ast = cil_visitor.visit(ast, scope) text = """ diff --git a/src/mips_test.asm b/src/mips_test.asm deleted file mode 100644 index 27853e5f8..000000000 --- a/src/mips_test.asm +++ /dev/null @@ -1,149 +0,0 @@ - .data -data_0: .asciiz "Abort called from class " -data_1: .asciiz "" -data_2: .asciiz "Substring out of range" -data_3: .asciiz "Hello, World." -data_4: .asciiz "(3, 2) - RuntimeError: Dispatch on void" - -type_list: - .word Object - .word IO - .word String - .word Int - .word Bool - .word Main - -virtual_table: - .word Object_proto - .word IO_proto - .word String_proto - .word Int_proto - .word Bool_proto - .word Main_proto - -Object_dispatch: - .word function_abort_at_Object - .word function_type_name_at_Object - .word function_copy_at_Object - .word init_at_Object - -Object_proto: - .word 0 - .word 0 - .word Object_dispatch - .word -1 - -IO_dispatch: - .word function_abort_at_Object - .word function_type_name_at_Object - .word function_copy_at_Object - .word function_out_string_at_IO - .word function_out_int_at_IO - .word function_in_string_at_IO - .word function_in_int_at_IO - .word init_at_IO - -IO_proto: - .word 1 - .word 0 - .word IO_dispatch - .word -1 - -String_dispatch: - .word function_abort_at_Object - .word function_type_name_at_Object - .word function_copy_at_Object - .word function_length_at_String - .word function_concat_at_String - .word function_substr_at_String - .word init_at_String - -String_proto: - .word 2 - .word 8 - .word String_dispatch - .word 0 - .word 0 - .word -1 - -Int_dispatch: - .word function_abort_at_Object - .word function_type_name_at_Object - .word function_copy_at_Object - .word init_at_Int - -Int_proto: - .word 3 - .word 4 - .word Int_dispatch - .word 0 - .word -1 - -Bool_dispatch: - .word function_abort_at_Object - .word function_type_name_at_Object - .word function_copy_at_Object - .word init_at_Bool - -Bool_proto: - .word 4 - .word 4 - .word Bool_dispatch - .word 0 - .word -1 - -Main_dispatch: - .word function_abort_at_Object - .word function_type_name_at_Object - .word function_copy_at_Object - .word function_out_string_at_IO - .word function_out_int_at_IO - .word function_in_string_at_IO - .word function_in_int_at_IO - .word function_main_at_Main - -Main_proto: - .word 5 - .word 0 - .word Main_dispatch - .word -1 - .text - .globl main -entry: - -init_at_Object: - -function_abort_at_Object: - -function_type_name_at_Object: - -function_copy_at_Object: - -init_at_IO: - -function_out_string_at_IO: - -function_out_int_at_IO: - -function_in_string_at_IO: - -function_in_int_at_IO: - -init_at_String: - -function_length_at_String: - -function_concat_at_String: - -function_substr_at_String: - -init_at_Int: - -init_at_Bool: - -function_main_at_Main: - -init_at_Main: - -init_attr_at_Main: - From 84776743a02391cb663e55a7dfbf83ad22c7301f Mon Sep 17 00:00:00 2001 From: dgd Date: Sat, 26 Feb 2022 09:08:01 -0500 Subject: [PATCH 120/138] fix pass getAttr --- src/compiler/visitors/cil2mips.py | 48 +++++++++++++------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index 6f1d9c5e1..e503ddf79 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -363,10 +363,6 @@ def visit(self, node): ) return instructions - @visitor.when(NotNode) - def visit(self, node): - pass - @visitor.when(ComplementNode) def visit(self, node): instructions = [] @@ -386,22 +382,6 @@ def visit(self, node): return instructions - @visitor.when(GetAttribNode) - def visit(self, node): - pass - - @visitor.when(SetAttribNode) - def visit(self, node): - pass - - @visitor.when(GetIndexNode) - def visit(self, node): - pass - - @visitor.when(SetIndexNode) - def visit(self, node): - pass - @visitor.when(AllocateNode) def visit(self, node: AllocateNode): instructions = [] @@ -436,10 +416,6 @@ def visit(self, node: AllocateNode): return instructions - @visitor.when(ArrayNode) - def visit(self, node): - pass - @visitor.when(TypeOfNode) def visit(self, node: TypeOfNode): instructions = [] @@ -611,10 +587,6 @@ def visit(self, node): return instructions - @visitor.when(PrefixNode) - def visit(self, node): - pass - @visitor.when(SubstringNode) def visit(self, node): instructions = [] @@ -833,3 +805,23 @@ def visit(self, node): ) return instructions + + # @visitor.when(NotNode) + # def visit(self, node): + # pass + + # @visitor.when(GetIndexNode) + # def visit(self, node): + # pass + + # @visitor.when(SetIndexNode) + # def visit(self, node): + # pass + + # @visitor.when(ArrayNode) + # def visit(self, node): + # pass + + # @visitor.when(PrefixNode) + # def visit(self, node): + # pass From 3703fc13d88e93f9153385433937c9b8186933eb Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 19:10:01 -0500 Subject: [PATCH 121/138] fix bugs --- src/compiler/cmp/cil_ast.py | 146 +++--- src/compiler/visitors/cil_formatter.py | 12 - src/compiler/visitors/cool2cil.py | 616 ++++++++++--------------- 3 files changed, 336 insertions(+), 438 deletions(-) diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py index 34bae8219..4edf9eddb 100644 --- a/src/compiler/cmp/cil_ast.py +++ b/src/compiler/cmp/cil_ast.py @@ -1,3 +1,4 @@ +# AST class Node: pass @@ -16,20 +17,6 @@ def __init__(self, name): self.methods = [] -class InstructionNode(Node): - pass - - -class TypeNameNode(InstructionNode): - def __init__(self, dest, source): - self.dest = dest - self.source = source - - -class ExitNode(InstructionNode): - pass - - class DataNode(Node): def __init__(self, vname, value): self.name = vname @@ -56,11 +43,19 @@ def __init__(self, name): self.name = name +class InstructionNode(Node): + def __init__(self): + self.leader = False + + 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): @@ -94,27 +89,14 @@ class LessNode(ArithmeticNode): class EqualNode(ArithmeticNode): - pass + def __repr__(self): + return f"{self.dest} = {self.left} == {self.right}" class EqualStrNode(ArithmeticNode): pass -class VoidNode(InstructionNode): - pass - - -class NotNode(ArithmeticNode): - pass - - -class ComplementNode(InstructionNode): - def __init__(self, dest, obj): - self.dest = dest - self.obj = obj - - class GetAttribNode(InstructionNode): def __init__(self, dest, obj, attr, computed_type): self.dest = dest @@ -122,6 +104,9 @@ def __init__(self, dest, obj, attr, computed_type): 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): @@ -150,43 +135,46 @@ class ArrayNode(InstructionNode): class TypeOfNode(InstructionNode): - def __init__(self, dest, obj): - self.dest = dest + def __init__(self, obj, dest): self.obj = obj - - -class NameNode(InstructionNode): - def __init__(self, dest, name): self.dest = dest - self.name = name + + 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): self.function = function self.dest = dest - -class CopyNode(InstructionNode): - def __init__(self, dest, source): - self.dest = dest - self.source = source + def __repr__(self): + return f"{self.dest} = CALL {self.function}" class DynamicCallNode(InstructionNode): @@ -196,22 +184,62 @@ def __init__(self, xtype, method, dest, computed_type): 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): self.dest = dest self.msg = msg + def __repr__(self): + return f"{self.dest} LOAD {self.msg}" + + +class ExitNode(InstructionNode): + pass + + +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): + self.dest = dest + self.source = source + class LengthNode(InstructionNode): def __init__(self, dest, source): @@ -227,10 +255,6 @@ def __init__(self, dest, prefix, suffix, length): self.length = length -class PrefixNode(InstructionNode): - pass - - class SubstringNode(InstructionNode): def __init__(self, dest, str_value, index, length): self.dest = dest @@ -239,17 +263,6 @@ def __init__(self, dest, str_value, index, length): self.length = length -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 ReadStrNode(InstructionNode): def __init__(self, dest): self.dest = dest @@ -260,21 +273,32 @@ 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 + def __repr__(self): + return f"PRINTSTR {self.value}" + class PrintIntNode(InstructionNode): def __init__(self, value): self.value = value +class ComplementNode(InstructionNode): + def __init__(self, dest, obj): + self.dest = dest + self.obj = obj + + +class VoidNode(InstructionNode): + pass + + class ErrorNode(InstructionNode): def __init__(self, data_node): self.data_node = data_node + + def __repr__(self): + return f"ERROR {self.data_node}" diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cil_formatter.py index c474bdec4..a4cdc4089 100644 --- a/src/compiler/visitors/cil_formatter.py +++ b/src/compiler/visitors/cil_formatter.py @@ -82,10 +82,6 @@ def visit(self, node): def visit(self, node): return f"{node.dest} = {node.left} == {node.right}" - @visitor.when(NotNode) - def visit(self, node): - return f"{node.dest} = NOT {node.obj}" - @visitor.when(ComplementNode) def visit(self, node): return f"{node.dest} = COMPL {node.obj}" @@ -150,14 +146,6 @@ def visit(self, node): def visit(self, node): return f"{node.dest} = SUBSTRING {node.index} {node.length}" - @visitor.when(ReadNode) - def visit(self, node): - return f"{node.dest} = READ" - - @visitor.when(PrintNode) - def visit(self, node): - return f"PRINT {node.value}" - @visitor.when(ExitNode) def visit(self, node): return f"EXIT" diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 0ee769b63..e4d478df8 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -1,56 +1,55 @@ -from pprint import pprint -import compiler.visitors.visitor as visitor from ..cmp import cil_ast as cil -from ..cmp.semantic import ( - Scope, - SemanticError, - ErrorType, - IntType, - BoolType, - SelfType, - AutoType, - LCA, - VariableInfo, -) -from ..cmp.ast import ( - CaseBranchNode, - LeqNode, - LessNode, - LetVarNode, - ProgramNode, - ClassDeclarationNode, - AttrDeclarationNode, - FuncDeclarationNode, -) from ..cmp.ast import ( AssignNode, + AttrDeclarationNode, + BlockNode, CallNode, + CaseBranchNode, CaseNode, - BlockNode, - LoopNode, + ClassDeclarationNode, ConditionalNode, - LetNode, -) -from ..cmp.ast import ArithmeticNode, ComparisonNode, EqualNode -from ..cmp.ast import VoidNode, NotNode, NegNode -from ..cmp.ast import ( + ConstantBoolNode, ConstantNumNode, ConstantStringNode, - ConstantBoolNode, - VariableNode, + DivNode, + EqualNode, + FuncDeclarationNode, InstantiateNode, + LeqNode, + LessNode, + LetNode, + LetVarNode, + LoopNode, + MinusNode, + NegNode, + NotNode, + PlusNode, + ProgramNode, + StarNode, + VariableNode, + VoidNode, +) +from ..cmp.semantic import ( + Context, + Method, + Scope, + SemanticError, + SelfType, + Type, + VariableInfo, ) -from ..cmp.ast import PlusNode, MinusNode, StarNode, DivNode +from typing import List, Optional +import compiler.visitors.visitor as visitor class BaseCOOLToCILVisitor: - def __init__(self, context): - self.dottypes = [] - self.dotdata = [] - self.dotcode = [] - self.current_type = None - self.current_method = None - self.current_function = None + def __init__(self, context: Context): + self.dottypes: List[cil.TypeNode] = [] + self.dotdata: List[cil.DataNode] = [] + self.dotcode: List[cil.FunctionNode] = [] + self.current_type: Optional[Type] = None + self.current_method: Optional[Method] = None + self.current_function: Optional[cil.FunctionNode] = None self.context = context self.vself = VariableInfo("self", None) self.value_types = ["String", "Int", "Bool"] @@ -88,7 +87,7 @@ def register_local(self, vinfo, id=False): return new_vinfo.name def define_internal_local(self): - vinfo = VariableInfo("internal", None, None) + vinfo = VariableInfo("internal", None) return self.register_local(vinfo) def register_instruction(self, instruction): @@ -208,6 +207,8 @@ def register_built_in(self): type_node.methods += [("init", self.init_name("Object"))] obj_methods = ["abort", "type_name", "copy"] + ############################################## + # IO type_node = self.register_type("IO") @@ -275,6 +276,8 @@ def register_built_in(self): ] type_node.methods += [("init", self.init_name("IO"))] + ############################################## + # String type_node = self.register_type("String") type_node.attributes = ["value", "length"] @@ -383,6 +386,8 @@ def register_built_in(self): ] type_node.methods += [("init", self.init_name("String"))] + ############################################## + # Int type_node = self.register_type("Int") type_node.attributes = ["value"] @@ -426,11 +431,7 @@ def visit(self, node): pass @visitor.when(ProgramNode) - def visit(self, node, scope): - ###################################################### - # node.declarations -> [ ClassDeclarationNode ... ] - ###################################################### - + def visit(self, node: ProgramNode, scope: Scope): self.current_function = self.register_function("entry") result = self.define_internal_local() instance = self.register_local(VariableInfo("instance", None)) @@ -450,43 +451,45 @@ def visit(self, node, 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 ... ] - #################################################################### + def visit(self, node: ClassDeclarationNode, scope: Scope): + self.current_type: Type = self.context.get_type(node.id) - self.current_type = self.context.get_type(node.id) - - # Your code here!!! (Handle all the .TYPE section) + # Handle all the .TYPE section type_node = self.register_type(self.current_type.name) - visited_func = [] - current = self.current_type - while current is not None: - attributes = [attr.name for attr in current.attributes] - methods = [ - func.name for func in current.methods if func.name not in visited_func + # TODO Check change next loop to use all_attributes and all_methods functions + type_node.attributes.extend( + [attr.name for attr, _ in self.current_type.all_attributes()] + ) + type_node.methods.extend( + [ + (method.name, self.to_function_name(method.name, typex.name)) + for method, typex in self.current_type.all_methods() ] - visited_func.extend(methods) - type_node.attributes.extend(attributes[::-1]) - type_node.methods.extend( - [ - (item, self.to_function_name(item, current.name)) - for item in methods[::-1] - ] - ) - current = current.parent - - type_node.attributes.reverse() - type_node.methods.reverse() - - 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) + # visited_func = [] + # current = self.current_type + # while current is not None: + # attributes = [attr.name for attr in current.attributes] + # methods = [ + # func.name for func in current.methods if func.name not in visited_func + # ] + # visited_func.extend(methods) + # type_node.attributes.extend(attributes[::-1]) + # type_node.methods.extend( + # [ + # (item, self.to_function_name(item, current.name)) + # for item in methods[::-1] + # ] + # ) + # current = current.parent + + # type_node.attributes.reverse() + # type_node.methods.reverse() + + for feature, child_scope in zip(node.features, scope.children): + if isinstance(feature, FuncDeclarationNode): + self.visit(feature, child_scope) # init self.current_function = self.register_function(self.init_name(node.id)) @@ -502,20 +505,16 @@ def visit(self, node, scope): self.init_name(node.id, attr=True) ) self.register_param(self.vself) - if ( - node.parent is not None - and node.parent.lex != "Object" - and node.parent.lex != "IO" - ): + parent_type = self.context.get_type(node.id).parent + if parent_type.name != "Object" and parent_type.name != "IO": self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction( - cil.StaticCallNode(self.init_name(node.parent, attr=True), vtemp) + cil.StaticCallNode(self.init_name(parent_type.name, attr=True), vtemp) ) - attr_declarations = ( - f for f in node.features if isinstance(f, AttrDeclarationNode) - ) - for feature in attr_declarations: - self.visit(feature, scope) + + for feature, child_scope in zip(node.features, scope.children): + if isinstance(feature, AttrDeclarationNode): + self.visit(feature, child_scope) self.current_function = func self.register_instruction(cil.ArgNode(instance)) @@ -528,17 +527,13 @@ def visit(self, node, scope): self.current_type = None @visitor.when(AttrDeclarationNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### + def visit(self, node: AttrDeclarationNode, scope: Scope): if node.expr: value = self.visit(node.expr, scope) self.register_instruction( cil.SetAttribNode(self.vself.name, node.id, value, self.current_type) ) + return value elif node.type in self.value_types: value = self.define_internal_local() @@ -546,19 +541,13 @@ def visit(self, node, scope): self.register_instruction( cil.SetAttribNode(self.vself.name, node.id, value, self.current_type) ) + return value @visitor.when(FuncDeclarationNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.params -> [ (str, str) ... ] - # node.type -> str - # node.body -> [ ExpressionNode ... ] - ############################### - + def visit(self, node: FuncDeclarationNode, scope: Scope): self.current_method = self.current_type.get_method(node.id) - # Your code here!!! (Handle PARAMS) + # Handle PARAMS self.current_function = self.register_function( self.to_function_name(self.current_method.name, self.current_type.name) ) @@ -567,8 +556,8 @@ def visit(self, node, scope): for param_name, _ in node.params: self.register_param(VariableInfo(param_name.lex, None)) + # Handle RETURN value = self.visit(node.body, scope) - # (Handle RETURN) if value is None: self.register_instruction(cil.ReturnNode("")) elif self.current_function.name == "entry": @@ -579,13 +568,7 @@ def visit(self, node, scope): self.current_method = None @visitor.when(AssignNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.expr -> ExpressionNode - ############################### - - # Your code here!!! + def visit(self, node: AssignNode, scope: Scope): value = self.visit(node.expr, scope) try: @@ -597,42 +580,35 @@ def visit(self, node, scope): ) except SemanticError: vname = None + # TODO - Splits by _ won't work 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 + vname = node.id else: - for n in [lv.name for lv in self.current_function.localvars]: - if node.id in n.split("_"): - vname = n - break + vname = self.ids[node.id] + # return self.data + # 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, value)) + return value + @visitor.when(CallNode) - def visit(self, node, scope): - ############################### - # node.obj -> AtomicNode - # node.id -> str - # node.args -> [ ExpressionNode ... ] - # node.type -> str - ############################### + def visit(self, node: CallNode, scope: Scope): + args = [] + for arg in node.args: + vname = self.register_local(VariableInfo(f"{node.id}_arg", None), id=True) + ret = self.visit(arg, scope) + self.register_instruction(cil.AssignNode(vname, ret)) + args.append(cil.ArgNode(vname)) + result = self.register_local( + VariableInfo(f"return_value_of_{node.id}", None), id=True + ) # static call node if isinstance(node.obj, VariableNode) and node.obj.lex == self.vself.name: - args = [] - for arg in node.args: - vname = self.register_local( - VariableInfo(f"{node.id}_arg", None), id=True - ) - ret = self.visit(arg, scope) - self.register_instruction(cil.AssignNode(vname, ret)) - args.append(cil.ArgNode(vname)) - 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) @@ -640,7 +616,7 @@ def visit(self, node, scope): type_of_node = self.register_local( VariableInfo(f"{self.vself.name}_type", None) ) - self.register_instruction(cil.TypeOfNode(type_of_node, self.vself.name)) + 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 @@ -648,16 +624,6 @@ def visit(self, node, scope): ) return result - args = [] - for arg in node.args: - vname = self.register_local(VariableInfo(f"{node.id}_arg", None), id=True) - ret = self.visit(arg, scope) - self.register_instruction(cil.AssignNode(vname, ret)) - args.append(cil.ArgNode(vname)) - result = self.register_local( - VariableInfo(f"return_value_of_{node.id}", None), id=True - ) - vobj = self.define_internal_local() ret = self.visit(node.obj, scope) self.register_instruction(cil.AssignNode(vobj, ret)) @@ -687,9 +653,9 @@ def visit(self, node, scope): type_of_node = self.register_local( VariableInfo(f"{node.id}_type", None), id=True ) - self.register_instruction(cil.TypeOfNode(type_of_node, vobj)) + self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) computed_type = node.obj.computed_type - if computed_type.name == "SELF_TYPE": + if isinstance(computed_type, SelfType): computed_type = computed_type.fixed_type self.register_instruction( cil.DynamicCallNode(type_of_node, node.id, result, computed_type.name) @@ -698,113 +664,83 @@ def visit(self, node, scope): return result @visitor.when(ConditionalNode) - def visit(self, node, scope): - ################################### - # node.condition -> ExpressionNode - # node.if_body -> ExpressionNode - # node.else_body -> ExpressionNode - ################################## + def visit(self, node: ConditionalNode, scope: Scope): + 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") continue_label_node = self.register_label("continue_label") - cond_ret = self.visit(node.condition, scope) - - cond_ret_value = self.define_internal_local() - - self.register_instruction( - cil.GetAttribNode(cond_ret_value, cond_ret, "value", "Bool") - ) - self.register_instruction(cil.GotoIfNode(cond_ret_value, then_label_node.label)) + # If condition GOTO then_label + ret = self.visit(node.condition, scope) + self.register_instruction(cil.GetAttribNode(vcondition, ret, "value", "Bool")) + self.register_instruction(cil.GotoIfNode(vcondition, then_label_node.label)) + # GOTO else_label self.register_instruction(cil.GotoNode(else_label_node.label)) - - value = self.register_local(VariableInfo("if_then_else_value", None)) - # Label then_label self.register_instruction(then_label_node) - ret_then = self.visit(node.then_body, scope) - self.register_instruction(cil.AssignNode(cond_ret_value, ret_then)) + retif = self.visit(node.then_body, scope) + self.register_instruction(cil.AssignNode(vret, retif)) self.register_instruction(cil.GotoNode(continue_label_node.label)) - # Label else_label self.register_instruction(else_label_node) - ret_else = self.visit(node.else_body, scope) - self.register_instruction(cil.AssignNode(cond_ret_value, ret_else)) + retelse = self.visit(node.else_body, scope) + self.register_instruction(cil.AssignNode(vret, retelse)) self.register_instruction(continue_label_node) - return cond_ret_value + return vret @visitor.when(LoopNode) - def visit(self, node, scope): - ################################### - # node.condition -> ExpressionNode - # node.body -> ExpressionNode - ################################### - + def visit(self, node: LoopNode, scope: Scope): + 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") - - condition = self.define_internal_local() + # Label while self.register_instruction(while_label_node) - - condition_value = self.visit(node.condition, scope) - - self.register_instruction( - cil.GetAttribNode(condition, condition_value, "value", "Bool") - ) - - self.register_instruction(cil.GotoIfNode(condition, loop_label_node.label)) + # If condition GOTO loop + ret = self.visit(node.condition, scope) + self.register_instruction(cil.GetAttribNode(vcondition, ret, "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 self.register_instruction(loop_label_node) - - self.visit(node.body, scope) - + ret = self.visit(node.body, scope) + # GOTO while self.register_instruction(cil.GotoNode(while_label_node.label)) - + # Label pool self.register_instruction(pool_label_node) + # The result of a while loop is void return cil.VoidNode() @visitor.when(BlockNode) - def visit(self, node, scope): - ####################################### - # node.expr_list -> [ ExpressionNode ... ] - ####################################### - + def visit(self, node: BlockNode, scope: Scope): + ret_value = None for expr in node.expr_list: ret_value = self.visit(expr, scope) return ret_value @visitor.when(LetNode) - def visit(self, node, scope): - ############################################ - # node.id_list -> [(id, type, expr), ...] - # node.in_body -> ExpressionNode - ############################################ - value = self.register_local(VariableInfo("let_in_value", None)) - - for let_var in node.id_list: - self.visit(let_var, scope) - - ret_val = self.visit(node.body, scope) - self.register_instruction(cil.AssignNode(value, ret_val)) + def visit(self, node: LetNode, scope: Scope): + vret = self.register_local(VariableInfo("let_in_value", None)) - return value + for let_var_node in node.id_list: + self.visit(let_var_node, scope) + ret = self.visit(node.body, scope) + self.register_instruction(cil.AssignNode(vret, ret)) + return vret @visitor.when(LetVarNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### - if node.id in self.ids: + def visit(self, node: LetVarNode, scope: Scope): + try: vname = self.ids[node.id] - else: + except KeyError: vname = self.register_local(VariableInfo(node.id, node.typex), id=True) + if node.expression: ret_value = self.visit(node.expression, scope) self.register_instruction(cil.AssignNode(vname, ret_value)) @@ -812,11 +748,7 @@ def visit(self, node, scope): self.register_instruction(cil.AllocateNode(node.typex, vname)) @visitor.when(CaseNode) - def visit(self, node, scope): - ############################################## - # node.expr -> ExpressionNode - # node.branches -> [(id, type, expr), ... } - ############################################## + def visit(self, node: CaseNode, scope: Scope): ret = self.register_local(VariableInfo("case_expr_value", None)) ret_type = self.register_local(VariableInfo("typeName_value", None)) vcond = self.register_local(VariableInfo("equal_value", None)) @@ -856,6 +788,7 @@ def visit(self, node, scope): if not h: self.register_instruction(cil.GotoNode(labels[-1].label)) break + # TODO - Esto no hace falta h.add(b.typex) for s in old: h -= s @@ -894,30 +827,24 @@ def visit(self, node, scope): # node.type -> str # node.expr -> ExpressionNode ############################### - ret = self.register_local(VariableInfo("block_node_value", None)) - - ret_value = self.visit(node.expression, scope) - - self.register_instruction(cil.AssignNode(ret, ret_value)) - - return ret + return self.visit(node.expression, scope) @visitor.when(NotNode) def visit(self, node, scope): ############################### # node.expr -> ExpressionNode ############################### - ret = self.define_internal_local() - ret_minus_1 = self.define_internal_local() + vname = self.define_internal_local() value = self.define_internal_local() + instance = self.define_internal_local() - ret_value = self.visit(node.expr, scope) - self.register_instruction(cil.GetAttribNode(ret, ret_value, "value", "Bool")) - self.register_instruction(cil.MinusNode(ret_minus_1, 1, ret)) + ret = self.visit(node.expr, scope) + self.register_instruction(cil.GetAttribNode(value, ret, "value", "Bool")) + self.register_instruction(cil.MinusNode(vname, 1, value)) - self.register_instruction(cil.ArgNode(ret_minus_1)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) - return value + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + return instance @visitor.when(LeqNode) def visit(self, node, scope): @@ -925,24 +852,22 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - ret_value = self.define_internal_local() + vname = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() - value = self.define_internal_local() + instance = self.define_internal_local() left = self.visit(node.left, scope) right = self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(left_value, left, "value", "Bool")) self.register_instruction( cil.GetAttribNode(right_value, right, "value", "Bool") ) - self.register_instruction(cil.LeqNode(ret_value, left_value, right_value)) - - self.register_instruction(cil.ArgNode(ret_value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) + self.register_instruction(cil.LeqNode(vname, left_value, right_value)) - return value + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + return instance @visitor.when(LessNode) def visit(self, node, scope): @@ -950,10 +875,10 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - value = self.define_internal_local() + vname = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() - value = self.define_internal_local() + instance = self.define_internal_local() left = self.visit(node.left, scope) right = self.visit(node.right, scope) @@ -961,11 +886,11 @@ def visit(self, node, scope): self.register_instruction( cil.GetAttribNode(right_value, right, "value", "Bool") ) - self.register_instruction(cil.LessNode(value, left_value, right_value)) + self.register_instruction(cil.LessNode(vname, left_value, right_value)) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), value)) - return value + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + return instance @visitor.when(EqualNode) def visit(self, node, scope): @@ -973,7 +898,7 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - return_vale = self.define_internal_local() + vname = self.define_internal_local() type_left = self.define_internal_local() type_int = self.define_internal_local() type_bool = self.define_internal_local() @@ -981,7 +906,7 @@ def visit(self, node, scope): equal_result = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() - ret = self.define_internal_local() + instance = self.define_internal_local() left = self.visit(node.left, scope) right = self.visit(node.right, scope) @@ -1006,7 +931,7 @@ def visit(self, node, scope): 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(return_vale, left_value, right_value)) + self.register_instruction(cil.EqualNode(vname, left_value, right_value)) self.register_instruction(cil.GotoNode(continue_node.label)) self.register_instruction(string_node) @@ -1016,18 +941,16 @@ def visit(self, node, scope): self.register_instruction( cil.GetAttribNode(right_value, right, "value", "String") ) - self.register_instruction( - cil.EqualStrNode(return_vale, 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) - self.register_instruction(cil.EqualNode(return_vale, left, right)) + self.register_instruction(cil.EqualNode(vname, left, right)) self.register_instruction(continue_node) - self.register_instruction(cil.ArgNode(return_vale)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) - return ret + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) + return instance @visitor.when(PlusNode) def visit(self, node, scope): @@ -1035,21 +958,18 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - value = self.define_internal_local() - value_left = self.define_internal_local() - value_right = self.define_internal_local() + vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + self.register_instruction(cil.GetAttribNode(vleft, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) - - self.register_instruction(cil.PlusNode(value, value_left, value_right)) - - ret = self.define_internal_local() - - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) - return ret + self.register_instruction(cil.GetAttribNode(vright, right, "value", "Int")) + 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.init_name("Int"), instance)) + return instance @visitor.when(MinusNode) def visit(self, node, scope): @@ -1057,21 +977,18 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - value = self.define_internal_local() - value_left = self.define_internal_local() - value_right = self.define_internal_local() + vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + self.register_instruction(cil.GetAttribNode(vleft, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) - - self.register_instruction(cil.LessNode(value, value_left, value_right)) - - ret = self.define_internal_local() - - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) - return ret + self.register_instruction(cil.GetAttribNode(vright, right, "value", "Int")) + 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.init_name("Int"), instance)) + return instance @visitor.when(StarNode) def visit(self, node, scope): @@ -1079,21 +996,18 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - value = self.define_internal_local() - value_left = self.define_internal_local() - value_right = self.define_internal_local() + vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + self.register_instruction(cil.GetAttribNode(vleft, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) - - self.register_instruction(cil.StarNode(value, value_left, value_right)) - - ret = self.define_internal_local() - - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) - return ret + self.register_instruction(cil.GetAttribNode(vright, right, "value", "Int")) + 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.init_name("Int"), instance)) + return instance @visitor.when(DivNode) def visit(self, node, scope): @@ -1101,73 +1015,60 @@ def visit(self, node, scope): # node.left -> ExpressionNode # node.right -> ExpressionNode ############################### - value = self.define_internal_local() - value_left = self.define_internal_local() - value_right = self.define_internal_local() + vname = self.define_internal_local() + vleft = self.define_internal_local() + vright = self.define_internal_local() left = self.visit(node.left, scope) - self.register_instruction(cil.GetAttribNode(value_left, left, "value", "Int")) + self.register_instruction(cil.GetAttribNode(vleft, left, "value", "Int")) right = self.visit(node.right, scope) - self.register_instruction(cil.GetAttribNode(value_right, right, "value", "Int")) + self.register_instruction(cil.GetAttribNode(vright, right, "value", "Int")) # Check division by 0 equal_result = self.define_internal_local() - self.register_instruction(cil.EqualNode(equal_result, value_right, 0)) + self.register_instruction(cil.EqualNode(equal_result, vright, 0)) self.register_runtime_error( equal_result, f"{node.token.pos} - RuntimeError: Division by zero\n", ) - self.register_instruction(cil.DivNode(value, value_left, value_right)) - - ret = self.define_internal_local() - - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) - return ret + 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.init_name("Int"), instance)) + return instance @visitor.when(VoidNode) def visit(self, node, scope): - ############################### - # node.expr -> ExpressionNode - ############################### void = cil.VoidNode() value = self.define_internal_local() - left = self.visit(node.expr, scope) - self.register_instruction(cil.AssignNode(value, left)) - - ret = self.define_internal_local() - self.register_instruction(cil.EqualNode(ret, value, void)) - self.register_instruction(cil.ArgNode(ret)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), ret)) - return ret + ret = self.visit(node.expr, scope) + self.register_instruction(cil.AssignNode(value, ret)) + 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)) + return result @visitor.when(NegNode) def visit(self, node, scope): - ############################### - # node.expr -> ExpressionNode - ############################### + vname = self.define_internal_local() value = self.define_internal_local() - complement_value = self.define_internal_local() - ret = self.define_internal_local() - left = self.visit(node.expr, scope) - self.register_instruction(cil.GetAttribNode(value, left, "value", "Int")) - - self.register_instruction(cil.ComplementNode(complement_value, value)) - self.register_instruction(cil.ArgNode(complement_value)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), ret)) - return ret + instance = self.define_internal_local() + ret = self.visit(node.expr, scope) + self.register_instruction(cil.GetAttribNode(value, ret, "value", "Int")) + self.register_instruction(cil.ComplementNode(vname, value)) + self.register_instruction(cil.ArgNode(vname)) + self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) + return instance @visitor.when(InstantiateNode) def visit(self, node, scope): - ############################### - # node.type -> str - ############################### - ret = self.define_internal_local() + instance = self.define_internal_local() if node.computed_type.name == SelfType().name: - value = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(value, node.computed_type.name)) - self.register_instruction(cil.AllocateNode(value, ret)) + vtype = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(self.vself.name, vtype)) + self.register_instruction(cil.AllocateNode(vtype, instance)) elif node.computed_type.name == "Int" or node.computed_type.name == "Bool": self.register_instruction(cil.ArgNode(0)) elif node.computed_type.name == "String": @@ -1177,15 +1078,12 @@ def visit(self, node, scope): self.register_instruction(cil.ArgNode(vmsg)) self.register_instruction( - cil.StaticCallNode(self.init_name(node.computed_type.name), ret) + cil.StaticCallNode(self.init_name(node.computed_type.name), instance) ) - return ret + return instance @visitor.when(VariableNode) def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### try: self.current_type.get_attribute(node.lex) @@ -1199,18 +1097,12 @@ def visit(self, node, scope): 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 == n: - return n + return node.lex else: return self.ids[node.lex] @visitor.when(ConstantNumNode) def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - instance = self.define_internal_local() self.register_instruction(cil.ArgNode(int(node.lex))) self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) @@ -1218,9 +1110,6 @@ def visit(self, node, scope): @visitor.when(ConstantStringNode) def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### try: data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] except IndexError: @@ -1235,9 +1124,6 @@ def visit(self, node, scope): @visitor.when(ConstantBoolNode) def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### if node.lex == "true": v = 1 else: From f39ab20b5103c4987c796d97a9bdb881d934f027 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 19:54:10 -0500 Subject: [PATCH 122/138] fxix bug --- src/compiler/visitors/cool2cil.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index e4d478df8..484e6fd95 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -608,7 +608,11 @@ def visit(self, node: CallNode, scope: Scope): ) # static call node - if isinstance(node.obj, VariableNode) and node.obj.lex == self.vself.name: + if ( + isinstance(node.obj, VariableNode) + and node.obj.lex == self.vself.name + and not node.type + ): self.register_instruction(cil.ArgNode(self.vself.name)) for arg in args: self.register_instruction(arg) From 67747bc23ea864f0987ee3dd4efb6755fb288a31 Mon Sep 17 00:00:00 2001 From: dgd Date: Sat, 26 Feb 2022 20:15:14 -0500 Subject: [PATCH 123/138] mips temp folder --- mips/cil2mips.py | 911 ++++++++++++++++++ mips/mips_ast.py | 236 +++++ mips/mips_printer.py | 164 ++++ .../{mips_printer.py => mips_printer1.py} | 0 4 files changed, 1311 insertions(+) create mode 100644 mips/cil2mips.py create mode 100644 mips/mips_ast.py create mode 100644 mips/mips_printer.py rename src/compiler/visitors/{mips_printer.py => mips_printer1.py} (100%) diff --git a/mips/cil2mips.py b/mips/cil2mips.py new file mode 100644 index 000000000..245e30478 --- /dev/null +++ b/mips/cil2mips.py @@ -0,0 +1,911 @@ +from ...visitors import visitor +from ..cil.cil import * +import core.visitors.mips.mips_ast as mips + + +def flatten(iterable): + for item in iterable: + try: + yield from flatten(item) + except TypeError: + yield item + + +class FunctionCollectorVisitor: + def __init__(self): + self.function_count = 0 + self.functions = {} + + def generate_function_name(self): + self.function_count += 1 + return f"F_{self.function_count}" + + @visitor.on("node") + def collect(self, node): + pass + + @visitor.when(ProgramNode) + def collect(self, node): + for func in node.dotcode: + self.collect(func) + + @visitor.when(FunctionNode) + def collect(self, node): + if node.name == "entry": + self.functions[node.name] = "main" + else: + self.functions[node.name] = self.generate_function_name() + + +class BaseCILToMIPSVisitor: + def __init__(self): + self.data = {} + self.text = {} + self.types = {} + self.current_function: mips.FunctionNode = None + self.pushed_args = 0 + self.label_count = 0 + self.type_count = 0 + self.function_labels = {} + self.function_collector = FunctionCollectorVisitor() + self.function_label_count = 0 + + def make_data_label(self): + self.label_count += 1 + return f"data_{self.label_count}" + + def make_function_label(self): + self.function_label_count += 1 + return f"Label_{self.function_label_count}" + + def make_type_label(self): + self.type_count += 1 + return f"type_{self.type_count}" + + def make_callee_init_instructions(self, function_node: mips.FunctionNode): + push_fp = mips.push_to_stack(mips.FP) + set_fp = mips.AddInmediateNode(mips.FP, mips.SP, 4) + local_vars_frame_size = len(function_node.localvars) * 4 + set_sp = mips.AddInmediateNode(mips.SP, mips.SP, -local_vars_frame_size) + return list(flatten([push_fp, set_fp, set_sp])) + + def make_callee_final_instructions(self, function_node: mips.FunctionNode): + local_vars_frame_size = len(function_node.localvars) * 4 + set_sp = mips.AddInmediateNode(mips.SP, mips.SP, local_vars_frame_size) + pop_FP = mips.pop_from_stack(mips.FP) + final = None + if function_node.label == mips.MAIN_FUNCTION_NAME: + final = mips.exit_program() + else: + final = mips.JumpRegister(mips.RA) + + return list(flatten([set_sp, pop_FP, final])) + + def register_function(self, name, function: FunctionNode): + self.text[name] = function + self.current_function = function + self.function_labels = {} + + def get_param_var_index(self, name): + index = self.current_function.params.index(name) # i + offset = (len(self.current_function.params) - index) * 4 + return mips.RegisterRelativeLocation(mips.FP, offset) + + def get_local_var_index(self, name): + index = self.current_function.localvars.index(name) + offset = (index + 1) * -4 + return mips.RegisterRelativeLocation(mips.FP, offset) + + def get_var_location(self, name): + try: + return self.get_param_var_index(name) + except ValueError: + return self.get_local_var_index(name) + + def get_type_size(self, type_name): + return len(self.types[type_name].attributes) * 4 + + +class CILToMIPSVisitor(BaseCILToMIPSVisitor): + @visitor.on("node") + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node: ProgramNode) -> mips.ProgramNode: + + self.function_collector.collect(node) + self.data["default_str"] = mips.StringConst("default_str", "") + + for dd in node.dotdata: + self.visit(dd) + + for dt in node.dottypes: + self.visit(dt) + + for dc in node.dotcode: + self.visit(dc) + + return mips.ProgramNode( + [t for t in self.types.values()], + [d for d in self.data.values()], + [f for f in self.text.values()], + ) + + @visitor.when(TypeNode) + def visit(self, node: TypeNode): + data_label = self.make_data_label() + self.data[data_label] = mips.StringConst(data_label, node.name) + + type_label = self.make_type_label() + methods = { + type_function: self.function_collector.functions[implemented_function] + for type_function, implemented_function in node.methods + } + defaults = {} + if node.name == "String": + defaults = {"value": "default_str", "length": "type_4_proto"} + else: + defaults = {att: "0" for att in node.attributes} + + self.types[node.name] = mips.TypeNode( + data_label, type_label, node.attributes, methods, self.type_count, defaults + ) + + @visitor.when(DataNode) + def visit(self, node: DataNode): + data_label = self.make_data_label() + self.data[node.name] = mips.StringConst(data_label, node.value) + + @visitor.when(FunctionNode) + def visit(self, node: FunctionNode): + # Init function + params = [p.name for p in node.params] + local_vars = [lv.name for lv in node.localvars] + function_name = self.function_collector.functions[node.name] + function_node = mips.FunctionNode(function_name, params, local_vars) + self.register_function(function_name, function_node) + for inst in node.instructions: + if isinstance(inst, LabelNode): + new_label = self.make_function_label() + self.function_labels[inst.label] = new_label + + # Conventions of Init intructions of the calle function + init_callee = self.make_callee_init_instructions(function_node) + + # Body instructions + self.current_function = function_node + body = [self.visit(instruction) for instruction in node.instructions] + + # Conventions of Final calle instrucctions + final_callee = self.make_callee_final_instructions(function_node) + + total_instructions = list(flatten(init_callee + body + final_callee)) + function_node.instructions = total_instructions + self.current_function = None + + @visitor.when(AssignNode) + def visit(self, node: AssignNode): + instructions = [] + + if type(node.source) == VoidNode: + instructions.append( + mips.StoreWordNode(mips.ZERO, self.get_var_location(node.dest)) + ) + return instructions + + if node.source.isnumeric(): + instructions.append(mips.LoadInmediateNode(mips.A0, int(node.source))) + else: + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) + ) + + instructions.append( + mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(PlusNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) + + instructions.append(mips.AddNode(mips.T2, mips.T0, mips.T1)) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(MinusNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) + + instructions.append(mips.SubNode(mips.T2, mips.T0, mips.T1)) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(StarNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) + + instructions.append(mips.MultiplyNode(mips.T2, mips.T0, mips.T1)) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(DivNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) + else: + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) + ) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) + else: + instructions.append( + mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) + ) + + instructions.append(mips.DivideNode(mips.T0, mips.T1)) + instructions.append(mips.MoveFromLowNode(mips.MoveFromLowNode(mips.T2))) + instructions.append( + mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) + ) + + return instructions + + # @visitor.when(LeqNode) + # def visit(self, node): + # instructions = [] + + # if type(node.left) == int: + # instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + # else: + # instructions.append( + # mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + # ) + + # if type(node.right) == int: + # instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + # else: + # instructions.append( + # mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + # ) + + # instructions.append(mips.JumpAndLinkNode("less_equal")) + # instructions.append( + # mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + # ) + + # return instructions + + @visitor.when(LessEqualNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + else: + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + else: + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) + + instructions.append(mips.JumpAndLinkNode("less_equal")) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(LessNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + else: + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.rigth)) + else: + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) + + instructions.append(mips.JumpAndLinkNode("less")) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(EqualNode) + def visit(self, node): + instructions = [] + + if type(node.left) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) + elif type(node.left) == VoidNode: + instructions.append(mips.LoadInmediateNode(mips.A0, 0)) + else: + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + + if type(node.right) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) + elif type(node.right) == VoidNode: + instructions.append(mips.LoadInmediateNode(mips.A1, 0)) + else: + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) + + instructions.append(mips.JumpAndLinkNode("equals")) + + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(EqualStrNode) + def visit(self, node): + instructions = [] + + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) + ) + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) + ) + instructions.append(mips.JumpAndLinkNode("equal_str")) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + return instructions + + @visitor.when(ComplementNode) + def visit(self, node): + instructions = [] + + if type(node.obj) == int: + instructions.append(mips.LoadInmediateNode(mips.T0, node.obj)) + else: + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.obj)) + ) + + instructions.append(mips.ComplementNode(mips.T1, mips.T0)) + instructions.append(mips.AddInmediateNode(mips.T1, mips.T1, 1)) + instructions.append( + mips.StoreWordNode(mips.T1, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(AllocateNode) + def visit(self, node: AllocateNode): + instructions = [] + + tp = 0 + if node.type.isnumeric(): + tp = node.type + else: + tp = self.types[node.type].pos + + # reg1 = t0 reg2 = t1 + instructions.extend(mips.push_to_stack(mips.T0)) + instructions.extend(mips.push_to_stack(mips.T1)) + instructions.append(mips.LoadInmediateNode(mips.T0, tp)) + + instructions.append(mips.ShiftLeftLogicalNode(mips.T0, mips.T0, 2)) + instructions.append(mips.LoadAddressNode(mips.T1, mips.VIRTUAL_TABLE)) + instructions.append(mips.AddUnsignedNode(mips.T1, mips.T1, mips.T0)) + instructions.append( + mips.LoadWordNode(mips.T1, mips.RegisterRelativeLocation(mips.T1, 0)) + ) + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T1, 4)) + ) + instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + instructions.append(mips.JumpAndLinkNode("malloc")) + instructions.append(mips.MoveNode(mips.A1, mips.A0)) + instructions.append(mips.MoveNode(mips.A0, mips.T1)) + instructions.append(mips.MoveNode(mips.A1, mips.V0)) + instructions.append(mips.JumpAndLinkNode("copy")) + + instructions.extend(mips.pop_from_stack(mips.T1)) + instructions.extend(mips.pop_from_stack(mips.T0)) + + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + # @visitor.when(AllocateNode) + # def visit(self, node: AllocateNode): + # instructions = [] + # instructions.append(mips.LoadInmediateNode(mips.V0, 9)) + # instructions.append( + # mips.LoadInmediateNode(mips.A0, self.get_type_size(node.type)) + # ) + # instructions.append(mips.SyscallNode()) + # return instructions + + @visitor.when(TypeOfNode) + def visit(self, node: TypeOfNode): + instructions = [] + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.obj))) + + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, 0)) + ) + instructions.append( + mips.StoreWordNode(mips.A1, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(NameNode) + def visit(self, node): + instructions = [] + + instructions.append(mips.LoadAddressNode(mips.A0, mips.TYPE_LIST)) + + tp_number = self.types[node.name].index + instructions.append( + mips.AddInmediateUnsignedNode(mips.a0, mips.a0, tp_number * 4) + ) + instructions.append( + mips.LoadWordNode(mips.a0, mips.RegisterRelativeLocation(mips.a0, 0)) + ) + + instructions.append( + mips.StoreWordNode(mips.a0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(LabelNode) + def visit(self, node: LabelNode): + return [mips.LabelNode(self.function_labels[node.label])] + + @visitor.when(StaticCallNode) + def visit(self, node: StaticCallNode): + instructions = [] + function_to_call = self.function_collector.functions[node.function] + instructions.append(mips.JumpAndLinkNode(function_to_call)) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + if self.pushed_args > 0: + instructions.append( + mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4) + ) + self.pushed_args = 0 + return instructions + + @visitor.when(DynamicCallNode) + def visit(self, node: DynamicCallNode): + instructions = [] + caller_type = self.types[node.computed_type] + print(caller_type.methods) + index = [m for m, m_label in caller_type.methods.items()].index(node.method) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.type)) + ) + + instructions.append(mips.LoadAddressNode(mips.A1, mips.VIRTUAL_TABLE)) + instructions.append(mips.ShiftLeftLogicalNode(mips.A2, mips.A1, 2)) + instructions.append(mips.AddUnsignedNode(mips.A1, mips.A1, mips.A2)) + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) + ) + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 8)) + ) + instructions.append(mips.AddInmediateUnsignedNode(mips.A1, mips.A1, index * 4)) + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) + ) + instructions.append(mips.JumpRegisterAndLinkNode(mips.A1)) + + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + if self.pushed_args > 0: + instructions.append( + mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4) + ) + self.pushed_args = 0 + + return instructions + + @visitor.when(ArgNode) + def visit(self, node: ArgNode): + self.pushed_args += 1 + instructions = [] + if type(node.name) == int: + instructions.append(mips.LoadInmediateNode(mips.A0, node.name)) + instructions.extend(mips.push_to_stack(mips.A0)) + else: + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.name)) + ) + instructions.extend(mips.push_to_stack(mips.A0)) + + return instructions + + @visitor.when(ReturnNode) + def visit(self, node): + instructions = [] + if node.value is None: + instructions.append(mips.LoadInmediateNode(mips.V0, 0)) + elif isinstance(node.value, int): + instructions.append(mips.LoadInmediateNode(mips.V0, node.value)) + elif isinstance(node.value, VoidNode): + instructions.append(mips.LoadInmediateNode(mips.V0, 0)) + else: + instructions.append( + mips.LoadWordNode(mips.V0, self.get_var_location(node.value)) + ) + + return instructions + + @visitor.when(LoadNode) + def visit(self, node: LoadNode): + instructions = [] + + location = mips.LabelRelativeLocation(self.data[node.msg.name].label, 0) + instructions.append(mips.LoadAddressNode(mips.A0, location)) + instructions.append( + mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(LengthNode) + def visit(self, node): + instructions = [] + + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) + ) + + instructions.append(mips.JumpAndLinkNode("len")) + + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(ConcatNode) + def visit(self, node): + instructions = [] + + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.prefix)) + ) + + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.suffix)) + ) + + instructions.append( + mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) + ) + + instructions.append(mips.JumpAndLinkNode("concat")) + + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(SubstringNode) + def visit(self, node): + instructions = [] + + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.str_value)) + ) + + if type(node.index) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.index)) + else: + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.index)) + ) + + if type(node.length) == int: + instructions.append(mips.LoadInmediateNode(mips.A2, node.length)) + else: + instructions.append( + mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) + ) + + instructions.append(mips.JumpAndLinkNode("substr")) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + return instructions + + @visitor.when(ReadStrNode) + def visit(self, node): + instructions = [] + instructions.append(mips.JumpAndLinkNode("read_str")) + + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(PrintIntNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0, 1)) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) + ) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(PrintStrNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0, 4)) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) + ) + instructions.append(mips.SyscallNode()) + return instructions + + @visitor.when(ErrorNode) + def visit(self, node): + instructions = [] + + mips_label = self.data[node.data_node.name].label + + instructions.append(mips.LoadInmediateNode(mips.V0, 4)) + instructions.append(mips.LoadAddressNode(mips.A0, mips_label)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.LoadInmediateNode(mips.V0, 10)) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(TypeNameNode) + def visit(self, node: TypeNameNode): + instructions = [] + + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) + ) + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) + ) + + instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + instructions.append(mips.LoadAddressNode(mips.A1, mips.TYPE_LIST)) + instructions.append(mips.AddUnsignedNode(mips.A0, mips.A0, mips.A1)) + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) + ) + + instructions.append( + mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) + ) + + return instructions + + @visitor.when(ExitNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0, 10)) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(GetAttribNode) + def visit(self, node: GetAttribNode): + + instructions = [] + + 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 + ) + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) + + tp = self.types[comp_type] + offset = 12 + tp.attributes.index(node.attr) * 4 + instructions.append( + mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) + ) + + instructions.append(mips.StoreWordNode(mips.A1, self.get_var_location(dest))) + + return instructions + + @visitor.when(SetAttribNode) + def visit(self, node: SetAttribNode): + + instructions = [] + + 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 + ) + + tp = self.types[comp_type] + offset = 12 + tp.attributes.index(node.attr) * 4 + + instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) + + if type(node.value) == int: + instructions.append(mips.LoadInmediateNode(mips.A1, node.value)) + else: + instructions.append( + mips.LoadWordNode(mips.A1, self.get_var_location(node.value)) + ) + + instructions.append( + mips.StoreWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) + ) + + return instructions + + @visitor.when(CopyNode) + def visit(self, node): + instructions = [] + # reg1 T0 reg2 A3 + instructions.extend(mips.push_to_stack(mips.T0)) + + instructions.append( + mips.LoadWordNode(mips.T0, self.get_var_location(node.source)) + ) + + instructions.append( + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T0, 4)) + ) + instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + instructions.append(mips.JumpAndLinkNode("malloc")) + instructions.append(mips.MoveNode(mips.A2, mips.A0)) + instructions.append(mips.MoveNode(mips.A0, mips.T0)) + instructions.append(mips.MoveNode(mips.A1, mips.V0)) + instructions.append(mips.JumpAndLinkNode("copy")) + + instructions.extend(mips.pop_from_stack(mips.T0)) + + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + return instructions + + @visitor.when(GotoIfNode) + def visit(self, node): + instructions = [] + + local_label = self.function_labels[node.label] + + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.condition)) + ) + + instructions.append(mips.BranchOnNotEqualNode(mips.A0, mips.ZERO, local_label)) + + return instructions + + @visitor.when(GotoNode) + def visit(self, node): + local_label = self.function_labels[node.label] + return [mips.JumpNode(local_label)] + + @visitor.when(ReadIntNode) + def visit(self, node): + instructions = [] + + instructions.append(mips.LoadInmediateNode(mips.V0, 5)) + instructions.append(mips.SyscallNode()) + instructions.append( + mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + ) + + return instructions + + # @visitor.when(NotNode) + # def visit(self, node): + # pass + + # @visitor.when(GetIndexNode) + # def visit(self, node): + # pass + + # @visitor.when(SetIndexNode) + # def visit(self, node): + # pass + + # @visitor.when(ArrayNode) + # def visit(self, node): + # pass + + # @visitor.when(PrefixNode) + # def visit(self, node): + # pass diff --git a/mips/mips_ast.py b/mips/mips_ast.py new file mode 100644 index 000000000..02cc0d240 --- /dev/null +++ b/mips/mips_ast.py @@ -0,0 +1,236 @@ +# ***********************Registers*********************** +class Register: + def __init__(self, name): + self.name = name + + +FP = Register("fp") +SP = Register("sp") +RA = Register("ra") +V0 = Register("v0") +RA = Register("ra") +A0 = Register("a0") +A1 = Register("a1") +A2 = Register("a2") +A3 = Register("a3") +ZERO = Register("zero") +T0 = Register("t0") +T1 = Register("t1") +T2 = Register("t2") + +# ***********************Registers*********************** + +# ***********************Utils*********************** + +MAIN_FUNCTION_NAME = "function_main_at_Main" +VIRTUAL_TABLE = "virtual_table" +TYPE_LIST = "type_list" + + +def push_to_stack(register: Register): + update_sp = AddInmediateNode(SP, SP, -4) + offset = RegisterRelativeLocation(SP, 0) + store_word = StoreWordNode(register, offset) + return [update_sp, store_word] + + +def pop_from_stack(register: Register): + load_word = LoadWordNode(register, RegisterRelativeLocation(SP, 0)) + update_sp = AddInmediateNode(SP, SP, 4) + return [load_word, update_sp] + + +def exit_program(): + instructions = [] + instructions.append(LoadInmediateNode(V0, 10)) + instructions.append(SyscallNode()) + return instructions + + +# ***********************Utils*********************** + + +# ***********************AST*********************** + + +class Node: + pass + + +class ProgramNode(Node): + def __init__(self, types, data, text): + self.types = types + self.data = data + self.text = text + + +class StringConst(Node): + def __init__(self, label, string): + self.label = label + self.string = string + + +class FunctionNode(Node): + def __init__(self, label, params, localvars): + self.label = label + self.params = params + self.localvars = localvars + self.instructions = [] + + +class TypeNode(Node): + def __init__(self, data_label, type_label, attributes, methods, pos, defaults): + self.data_label = data_label + self.type_label = type_label + self.attributes = attributes + self.methods = methods + self.pos = pos + self.defaults = defaults + + +class InstructionNode(Node): + pass + + +class LabelNode(InstructionNode): + def __init__(self, label): + self.label = label + + +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 JumpRegisterAndLinkNode(InstructionNode): + def __init__(self, reg): + self.reg = reg + + +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.constant_number = value + + +class AddInmediateUnsignedNode(InstructionNode): + def __init__(self, dest, src, value): + self.dest = dest + 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 + 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 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 + + +class SubNode(InstructionNode): + 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 + + +class DivideNode(InstructionNode): + 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 + + +class RegisterRelativeLocation: + def __init__(self, register, offset): + self.register = register + self.offset = offset + + +class LabelRelativeLocation: + def __init__(self, label, offset): + self.label = label + self.offset = offset diff --git a/mips/mips_printer.py b/mips/mips_printer.py new file mode 100644 index 000000000..c75953fcd --- /dev/null +++ b/mips/mips_printer.py @@ -0,0 +1,164 @@ +from .mips_ast import * +from ...visitors import visitor + + +class MIPSPrintVisitor: + @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(d) for d in node.data]) + + names_table = f"{TYPE_LIST}:\n\t .word " + ", ".join( + [f"{tp.data_label}" for tp in node.types] + ) + proto_table = f"{VIRTUAL_TABLE}:\n\t .word " + ", ".join( + [f"{tp.type_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.text]) + 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(TypeNode) + def visit(self, node: TypeNode): + print(node.data_label, ":") + print(node.attributes) + methods = ", ".join([f"{node.methods[m]}" for m in node.methods]) + dispatch_table = f"{node.type_label}_dispatch:\n\t .word {methods}" + proto_begin = f"{node.type_label}_proto:\n\t.word\t{node.pos}, {len(node.attributes)*4}, {node.type_label}_dispatch" + proto_attr = ", ".join( + [f'{node.defaults.get(attr,"0")}' for attr in node.attributes] + ) + proto_end = f"{-1}" + proto = ( + f"{proto_begin}, {proto_attr}, {proto_end}" + if proto_attr != "" + else f"{proto_begin}, {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.constant_number)}" + + @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.label}:" + + @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)}" diff --git a/src/compiler/visitors/mips_printer.py b/src/compiler/visitors/mips_printer1.py similarity index 100% rename from src/compiler/visitors/mips_printer.py rename to src/compiler/visitors/mips_printer1.py From 29bc0020cf95204577ef48c63074190f3bcad0f8 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sat, 26 Feb 2022 20:49:01 -0500 Subject: [PATCH 124/138] add cil to mips visitor --- mips/cil2mips.py | 911 ------------- mips/mips_ast.py | 236 ---- src/compiler/cmp/mips_ast.py | 19 +- src/compiler/visitors/cil2mips.py | 254 ++-- src/compiler/visitors/mips_lib.asm | 1152 +++++++++++++++++ .../compiler/visitors}/mips_printer.py | 4 +- src/compiler/visitors/mips_printer1.py | 161 --- src/main.py | 30 +- 8 files changed, 1313 insertions(+), 1454 deletions(-) delete mode 100644 mips/cil2mips.py delete mode 100644 mips/mips_ast.py create mode 100644 src/compiler/visitors/mips_lib.asm rename {mips => src/compiler/visitors}/mips_printer.py (98%) delete mode 100644 src/compiler/visitors/mips_printer1.py diff --git a/mips/cil2mips.py b/mips/cil2mips.py deleted file mode 100644 index 245e30478..000000000 --- a/mips/cil2mips.py +++ /dev/null @@ -1,911 +0,0 @@ -from ...visitors import visitor -from ..cil.cil import * -import core.visitors.mips.mips_ast as mips - - -def flatten(iterable): - for item in iterable: - try: - yield from flatten(item) - except TypeError: - yield item - - -class FunctionCollectorVisitor: - def __init__(self): - self.function_count = 0 - self.functions = {} - - def generate_function_name(self): - self.function_count += 1 - return f"F_{self.function_count}" - - @visitor.on("node") - def collect(self, node): - pass - - @visitor.when(ProgramNode) - def collect(self, node): - for func in node.dotcode: - self.collect(func) - - @visitor.when(FunctionNode) - def collect(self, node): - if node.name == "entry": - self.functions[node.name] = "main" - else: - self.functions[node.name] = self.generate_function_name() - - -class BaseCILToMIPSVisitor: - def __init__(self): - self.data = {} - self.text = {} - self.types = {} - self.current_function: mips.FunctionNode = None - self.pushed_args = 0 - self.label_count = 0 - self.type_count = 0 - self.function_labels = {} - self.function_collector = FunctionCollectorVisitor() - self.function_label_count = 0 - - def make_data_label(self): - self.label_count += 1 - return f"data_{self.label_count}" - - def make_function_label(self): - self.function_label_count += 1 - return f"Label_{self.function_label_count}" - - def make_type_label(self): - self.type_count += 1 - return f"type_{self.type_count}" - - def make_callee_init_instructions(self, function_node: mips.FunctionNode): - push_fp = mips.push_to_stack(mips.FP) - set_fp = mips.AddInmediateNode(mips.FP, mips.SP, 4) - local_vars_frame_size = len(function_node.localvars) * 4 - set_sp = mips.AddInmediateNode(mips.SP, mips.SP, -local_vars_frame_size) - return list(flatten([push_fp, set_fp, set_sp])) - - def make_callee_final_instructions(self, function_node: mips.FunctionNode): - local_vars_frame_size = len(function_node.localvars) * 4 - set_sp = mips.AddInmediateNode(mips.SP, mips.SP, local_vars_frame_size) - pop_FP = mips.pop_from_stack(mips.FP) - final = None - if function_node.label == mips.MAIN_FUNCTION_NAME: - final = mips.exit_program() - else: - final = mips.JumpRegister(mips.RA) - - return list(flatten([set_sp, pop_FP, final])) - - def register_function(self, name, function: FunctionNode): - self.text[name] = function - self.current_function = function - self.function_labels = {} - - def get_param_var_index(self, name): - index = self.current_function.params.index(name) # i - offset = (len(self.current_function.params) - index) * 4 - return mips.RegisterRelativeLocation(mips.FP, offset) - - def get_local_var_index(self, name): - index = self.current_function.localvars.index(name) - offset = (index + 1) * -4 - return mips.RegisterRelativeLocation(mips.FP, offset) - - def get_var_location(self, name): - try: - return self.get_param_var_index(name) - except ValueError: - return self.get_local_var_index(name) - - def get_type_size(self, type_name): - return len(self.types[type_name].attributes) * 4 - - -class CILToMIPSVisitor(BaseCILToMIPSVisitor): - @visitor.on("node") - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node: ProgramNode) -> mips.ProgramNode: - - self.function_collector.collect(node) - self.data["default_str"] = mips.StringConst("default_str", "") - - for dd in node.dotdata: - self.visit(dd) - - for dt in node.dottypes: - self.visit(dt) - - for dc in node.dotcode: - self.visit(dc) - - return mips.ProgramNode( - [t for t in self.types.values()], - [d for d in self.data.values()], - [f for f in self.text.values()], - ) - - @visitor.when(TypeNode) - def visit(self, node: TypeNode): - data_label = self.make_data_label() - self.data[data_label] = mips.StringConst(data_label, node.name) - - type_label = self.make_type_label() - methods = { - type_function: self.function_collector.functions[implemented_function] - for type_function, implemented_function in node.methods - } - defaults = {} - if node.name == "String": - defaults = {"value": "default_str", "length": "type_4_proto"} - else: - defaults = {att: "0" for att in node.attributes} - - self.types[node.name] = mips.TypeNode( - data_label, type_label, node.attributes, methods, self.type_count, defaults - ) - - @visitor.when(DataNode) - def visit(self, node: DataNode): - data_label = self.make_data_label() - self.data[node.name] = mips.StringConst(data_label, node.value) - - @visitor.when(FunctionNode) - def visit(self, node: FunctionNode): - # Init function - params = [p.name for p in node.params] - local_vars = [lv.name for lv in node.localvars] - function_name = self.function_collector.functions[node.name] - function_node = mips.FunctionNode(function_name, params, local_vars) - self.register_function(function_name, function_node) - for inst in node.instructions: - if isinstance(inst, LabelNode): - new_label = self.make_function_label() - self.function_labels[inst.label] = new_label - - # Conventions of Init intructions of the calle function - init_callee = self.make_callee_init_instructions(function_node) - - # Body instructions - self.current_function = function_node - body = [self.visit(instruction) for instruction in node.instructions] - - # Conventions of Final calle instrucctions - final_callee = self.make_callee_final_instructions(function_node) - - total_instructions = list(flatten(init_callee + body + final_callee)) - function_node.instructions = total_instructions - self.current_function = None - - @visitor.when(AssignNode) - def visit(self, node: AssignNode): - instructions = [] - - if type(node.source) == VoidNode: - instructions.append( - mips.StoreWordNode(mips.ZERO, self.get_var_location(node.dest)) - ) - return instructions - - if node.source.isnumeric(): - instructions.append(mips.LoadInmediateNode(mips.A0, int(node.source))) - else: - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) - ) - - instructions.append( - mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(PlusNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.AddNode(mips.T2, mips.T0, mips.T1)) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(MinusNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.SubNode(mips.T2, mips.T0, mips.T1)) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(StarNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.MultiplyNode(mips.T2, mips.T0, mips.T1)) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(DivNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.DivideNode(mips.T0, mips.T1)) - instructions.append(mips.MoveFromLowNode(mips.MoveFromLowNode(mips.T2))) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions - - # @visitor.when(LeqNode) - # def visit(self, node): - # instructions = [] - - # if type(node.left) == int: - # instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - # else: - # instructions.append( - # mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - # ) - - # if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) - # else: - # instructions.append( - # mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - # ) - - # instructions.append(mips.JumpAndLinkNode("less_equal")) - # instructions.append( - # mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - # ) - - # return instructions - - @visitor.when(LessEqualNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - ) - - instructions.append(mips.JumpAndLinkNode("less_equal")) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(LessNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.rigth)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - ) - - instructions.append(mips.JumpAndLinkNode("less")) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(EqualNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - elif type(node.left) == VoidNode: - instructions.append(mips.LoadInmediateNode(mips.A0, 0)) - else: - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) - elif type(node.right) == VoidNode: - instructions.append(mips.LoadInmediateNode(mips.A1, 0)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - ) - - instructions.append(mips.JumpAndLinkNode("equals")) - - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(EqualStrNode) - def visit(self, node): - instructions = [] - - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - ) - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - ) - instructions.append(mips.JumpAndLinkNode("equal_str")) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - return instructions - - @visitor.when(ComplementNode) - def visit(self, node): - instructions = [] - - if type(node.obj) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.obj)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.obj)) - ) - - instructions.append(mips.ComplementNode(mips.T1, mips.T0)) - instructions.append(mips.AddInmediateNode(mips.T1, mips.T1, 1)) - instructions.append( - mips.StoreWordNode(mips.T1, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(AllocateNode) - def visit(self, node: AllocateNode): - instructions = [] - - tp = 0 - if node.type.isnumeric(): - tp = node.type - else: - tp = self.types[node.type].pos - - # reg1 = t0 reg2 = t1 - instructions.extend(mips.push_to_stack(mips.T0)) - instructions.extend(mips.push_to_stack(mips.T1)) - instructions.append(mips.LoadInmediateNode(mips.T0, tp)) - - instructions.append(mips.ShiftLeftLogicalNode(mips.T0, mips.T0, 2)) - instructions.append(mips.LoadAddressNode(mips.T1, mips.VIRTUAL_TABLE)) - instructions.append(mips.AddUnsignedNode(mips.T1, mips.T1, mips.T0)) - instructions.append( - mips.LoadWordNode(mips.T1, mips.RegisterRelativeLocation(mips.T1, 0)) - ) - instructions.append( - mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T1, 4)) - ) - instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) - instructions.append(mips.JumpAndLinkNode("malloc")) - instructions.append(mips.MoveNode(mips.A1, mips.A0)) - instructions.append(mips.MoveNode(mips.A0, mips.T1)) - instructions.append(mips.MoveNode(mips.A1, mips.V0)) - instructions.append(mips.JumpAndLinkNode("copy")) - - instructions.extend(mips.pop_from_stack(mips.T1)) - instructions.extend(mips.pop_from_stack(mips.T0)) - - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - # @visitor.when(AllocateNode) - # def visit(self, node: AllocateNode): - # instructions = [] - # instructions.append(mips.LoadInmediateNode(mips.V0, 9)) - # instructions.append( - # mips.LoadInmediateNode(mips.A0, self.get_type_size(node.type)) - # ) - # instructions.append(mips.SyscallNode()) - # return instructions - - @visitor.when(TypeOfNode) - def visit(self, node: TypeOfNode): - instructions = [] - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(node.obj))) - - instructions.append( - mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, 0)) - ) - instructions.append( - mips.StoreWordNode(mips.A1, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(NameNode) - def visit(self, node): - instructions = [] - - instructions.append(mips.LoadAddressNode(mips.A0, mips.TYPE_LIST)) - - tp_number = self.types[node.name].index - instructions.append( - mips.AddInmediateUnsignedNode(mips.a0, mips.a0, tp_number * 4) - ) - instructions.append( - mips.LoadWordNode(mips.a0, mips.RegisterRelativeLocation(mips.a0, 0)) - ) - - instructions.append( - mips.StoreWordNode(mips.a0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(LabelNode) - def visit(self, node: LabelNode): - return [mips.LabelNode(self.function_labels[node.label])] - - @visitor.when(StaticCallNode) - def visit(self, node: StaticCallNode): - instructions = [] - function_to_call = self.function_collector.functions[node.function] - instructions.append(mips.JumpAndLinkNode(function_to_call)) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - if self.pushed_args > 0: - instructions.append( - mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4) - ) - self.pushed_args = 0 - return instructions - - @visitor.when(DynamicCallNode) - def visit(self, node: DynamicCallNode): - instructions = [] - caller_type = self.types[node.computed_type] - print(caller_type.methods) - index = [m for m, m_label in caller_type.methods.items()].index(node.method) - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.type)) - ) - - instructions.append(mips.LoadAddressNode(mips.A1, mips.VIRTUAL_TABLE)) - instructions.append(mips.ShiftLeftLogicalNode(mips.A2, mips.A1, 2)) - instructions.append(mips.AddUnsignedNode(mips.A1, mips.A1, mips.A2)) - instructions.append( - mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) - ) - instructions.append( - mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 8)) - ) - instructions.append(mips.AddInmediateUnsignedNode(mips.A1, mips.A1, index * 4)) - instructions.append( - mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) - ) - instructions.append(mips.JumpRegisterAndLinkNode(mips.A1)) - - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - if self.pushed_args > 0: - instructions.append( - mips.AddInmediateNode(mips.SP, mips.SP, self.pushed_args * 4) - ) - self.pushed_args = 0 - - return instructions - - @visitor.when(ArgNode) - def visit(self, node: ArgNode): - self.pushed_args += 1 - instructions = [] - if type(node.name) == int: - instructions.append(mips.LoadInmediateNode(mips.A0, node.name)) - instructions.extend(mips.push_to_stack(mips.A0)) - else: - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.name)) - ) - instructions.extend(mips.push_to_stack(mips.A0)) - - return instructions - - @visitor.when(ReturnNode) - def visit(self, node): - instructions = [] - if node.value is None: - instructions.append(mips.LoadInmediateNode(mips.V0, 0)) - elif isinstance(node.value, int): - instructions.append(mips.LoadInmediateNode(mips.V0, node.value)) - elif isinstance(node.value, VoidNode): - instructions.append(mips.LoadInmediateNode(mips.V0, 0)) - else: - instructions.append( - mips.LoadWordNode(mips.V0, self.get_var_location(node.value)) - ) - - return instructions - - @visitor.when(LoadNode) - def visit(self, node: LoadNode): - instructions = [] - - location = mips.LabelRelativeLocation(self.data[node.msg.name].label, 0) - instructions.append(mips.LoadAddressNode(mips.A0, location)) - instructions.append( - mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(LengthNode) - def visit(self, node): - instructions = [] - - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) - ) - - instructions.append(mips.JumpAndLinkNode("len")) - - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(ConcatNode) - def visit(self, node): - instructions = [] - - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.prefix)) - ) - - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.suffix)) - ) - - instructions.append( - mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) - ) - - instructions.append(mips.JumpAndLinkNode("concat")) - - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(SubstringNode) - def visit(self, node): - instructions = [] - - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.str_value)) - ) - - if type(node.index) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.index)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.index)) - ) - - if type(node.length) == int: - instructions.append(mips.LoadInmediateNode(mips.A2, node.length)) - else: - instructions.append( - mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) - ) - - instructions.append(mips.JumpAndLinkNode("substr")) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - return instructions - - @visitor.when(ReadStrNode) - def visit(self, node): - instructions = [] - instructions.append(mips.JumpAndLinkNode("read_str")) - - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(PrintIntNode) - def visit(self, node): - instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0, 1)) - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) - ) - instructions.append(mips.SyscallNode()) - - return instructions - - @visitor.when(PrintStrNode) - def visit(self, node): - instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0, 4)) - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) - ) - instructions.append(mips.SyscallNode()) - return instructions - - @visitor.when(ErrorNode) - def visit(self, node): - instructions = [] - - mips_label = self.data[node.data_node.name].label - - instructions.append(mips.LoadInmediateNode(mips.V0, 4)) - instructions.append(mips.LoadAddressNode(mips.A0, mips_label)) - instructions.append(mips.SyscallNode()) - instructions.append(mips.LoadInmediateNode(mips.V0, 10)) - instructions.append(mips.SyscallNode()) - - return instructions - - @visitor.when(TypeNameNode) - def visit(self, node: TypeNameNode): - instructions = [] - - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) - ) - instructions.append( - mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) - ) - - instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) - instructions.append(mips.LoadAddressNode(mips.A1, mips.TYPE_LIST)) - instructions.append(mips.AddUnsignedNode(mips.A0, mips.A0, mips.A1)) - instructions.append( - mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) - ) - - instructions.append( - mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) - ) - - return instructions - - @visitor.when(ExitNode) - def visit(self, node): - instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0, 10)) - instructions.append(mips.SyscallNode()) - - return instructions - - @visitor.when(GetAttribNode) - def visit(self, node: GetAttribNode): - - instructions = [] - - 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 - ) - - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) - - tp = self.types[comp_type] - offset = 12 + tp.attributes.index(node.attr) * 4 - instructions.append( - mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) - ) - - instructions.append(mips.StoreWordNode(mips.A1, self.get_var_location(dest))) - - return instructions - - @visitor.when(SetAttribNode) - def visit(self, node: SetAttribNode): - - instructions = [] - - 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 - ) - - tp = self.types[comp_type] - offset = 12 + tp.attributes.index(node.attr) * 4 - - instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) - - if type(node.value) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.value)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.value)) - ) - - instructions.append( - mips.StoreWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) - ) - - return instructions - - @visitor.when(CopyNode) - def visit(self, node): - instructions = [] - # reg1 T0 reg2 A3 - instructions.extend(mips.push_to_stack(mips.T0)) - - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.source)) - ) - - instructions.append( - mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T0, 4)) - ) - instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) - instructions.append(mips.JumpAndLinkNode("malloc")) - instructions.append(mips.MoveNode(mips.A2, mips.A0)) - instructions.append(mips.MoveNode(mips.A0, mips.T0)) - instructions.append(mips.MoveNode(mips.A1, mips.V0)) - instructions.append(mips.JumpAndLinkNode("copy")) - - instructions.extend(mips.pop_from_stack(mips.T0)) - - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - return instructions - - @visitor.when(GotoIfNode) - def visit(self, node): - instructions = [] - - local_label = self.function_labels[node.label] - - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.condition)) - ) - - instructions.append(mips.BranchOnNotEqualNode(mips.A0, mips.ZERO, local_label)) - - return instructions - - @visitor.when(GotoNode) - def visit(self, node): - local_label = self.function_labels[node.label] - return [mips.JumpNode(local_label)] - - @visitor.when(ReadIntNode) - def visit(self, node): - instructions = [] - - instructions.append(mips.LoadInmediateNode(mips.V0, 5)) - instructions.append(mips.SyscallNode()) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions - - # @visitor.when(NotNode) - # def visit(self, node): - # pass - - # @visitor.when(GetIndexNode) - # def visit(self, node): - # pass - - # @visitor.when(SetIndexNode) - # def visit(self, node): - # pass - - # @visitor.when(ArrayNode) - # def visit(self, node): - # pass - - # @visitor.when(PrefixNode) - # def visit(self, node): - # pass diff --git a/mips/mips_ast.py b/mips/mips_ast.py deleted file mode 100644 index 02cc0d240..000000000 --- a/mips/mips_ast.py +++ /dev/null @@ -1,236 +0,0 @@ -# ***********************Registers*********************** -class Register: - def __init__(self, name): - self.name = name - - -FP = Register("fp") -SP = Register("sp") -RA = Register("ra") -V0 = Register("v0") -RA = Register("ra") -A0 = Register("a0") -A1 = Register("a1") -A2 = Register("a2") -A3 = Register("a3") -ZERO = Register("zero") -T0 = Register("t0") -T1 = Register("t1") -T2 = Register("t2") - -# ***********************Registers*********************** - -# ***********************Utils*********************** - -MAIN_FUNCTION_NAME = "function_main_at_Main" -VIRTUAL_TABLE = "virtual_table" -TYPE_LIST = "type_list" - - -def push_to_stack(register: Register): - update_sp = AddInmediateNode(SP, SP, -4) - offset = RegisterRelativeLocation(SP, 0) - store_word = StoreWordNode(register, offset) - return [update_sp, store_word] - - -def pop_from_stack(register: Register): - load_word = LoadWordNode(register, RegisterRelativeLocation(SP, 0)) - update_sp = AddInmediateNode(SP, SP, 4) - return [load_word, update_sp] - - -def exit_program(): - instructions = [] - instructions.append(LoadInmediateNode(V0, 10)) - instructions.append(SyscallNode()) - return instructions - - -# ***********************Utils*********************** - - -# ***********************AST*********************** - - -class Node: - pass - - -class ProgramNode(Node): - def __init__(self, types, data, text): - self.types = types - self.data = data - self.text = text - - -class StringConst(Node): - def __init__(self, label, string): - self.label = label - self.string = string - - -class FunctionNode(Node): - def __init__(self, label, params, localvars): - self.label = label - self.params = params - self.localvars = localvars - self.instructions = [] - - -class TypeNode(Node): - def __init__(self, data_label, type_label, attributes, methods, pos, defaults): - self.data_label = data_label - self.type_label = type_label - self.attributes = attributes - self.methods = methods - self.pos = pos - self.defaults = defaults - - -class InstructionNode(Node): - pass - - -class LabelNode(InstructionNode): - def __init__(self, label): - self.label = label - - -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 JumpRegisterAndLinkNode(InstructionNode): - def __init__(self, reg): - self.reg = reg - - -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.constant_number = value - - -class AddInmediateUnsignedNode(InstructionNode): - def __init__(self, dest, src, value): - self.dest = dest - 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 - 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 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 - - -class SubNode(InstructionNode): - 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 - - -class DivideNode(InstructionNode): - 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 - - -class RegisterRelativeLocation: - def __init__(self, register, offset): - self.register = register - self.offset = offset - - -class LabelRelativeLocation: - def __init__(self, label, offset): - self.label = label - self.offset = offset diff --git a/src/compiler/cmp/mips_ast.py b/src/compiler/cmp/mips_ast.py index 424f6c23b..02cc0d240 100644 --- a/src/compiler/cmp/mips_ast.py +++ b/src/compiler/cmp/mips_ast.py @@ -64,10 +64,10 @@ def __init__(self, types, data, text): self.text = text -class DataNode(Node): - def __init__(self, label, data): +class StringConst(Node): + def __init__(self, label, string): self.label = label - self.data = data + self.string = string class FunctionNode(Node): @@ -79,12 +79,13 @@ def __init__(self, label, params, localvars): class TypeNode(Node): - def __init__(self, label): - self.label = label - self.methods = [] - self.attributes = [] - self.label_name = None - self.pos = -1 + def __init__(self, data_label, type_label, attributes, methods, pos, defaults): + self.data_label = data_label + self.type_label = type_label + self.attributes = attributes + self.methods = methods + self.pos = pos + self.defaults = defaults class InstructionNode(Node): diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index 3fe1bfa99..84c3f3f65 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -1,6 +1,6 @@ -import compiler.visitors.visitor as visitor -from ..cmp.cil import * -from ..cmp import mips_ast as mips +from compiler.cmp import mips_ast as mips +from compiler.cmp.cil_ast import * +from compiler.visitors import visitor def flatten(iterable): @@ -11,6 +11,32 @@ def flatten(iterable): yield item +class FunctionCollectorVisitor: + def __init__(self): + self.function_count = 0 + self.functions = {} + + def generate_function_name(self): + self.function_count += 1 + return f"F_{self.function_count}" + + @visitor.on("node") + def collect(self, node): + pass + + @visitor.when(ProgramNode) + def collect(self, node): + for func in node.dotcode: + self.collect(func) + + @visitor.when(FunctionNode) + def collect(self, node): + if node.name == "entry": + self.functions[node.name] = "main" + else: + self.functions[node.name] = self.generate_function_name() + + class BaseCILToMIPSVisitor: def __init__(self): self.data = {} @@ -20,41 +46,21 @@ def __init__(self): self.pushed_args = 0 self.label_count = 0 self.type_count = 0 + self.function_labels = {} + self.function_collector = FunctionCollectorVisitor() + self.function_label_count = 0 - def register_type(self, type_name): - label_name = self.to_type_label_address(type_name) - type_node = mips.TypeNode(label_name) - self.types[type_name] = type_node - self.register_data(label_name, type_name) - return (type_node, label_name) - - def register_data(self, data_name, data): - data_node = mips.DataNode(self.to_data_label_address(data_name), data) - self.data[data_name] = data_node - return data_node - - def register_function(self, function_name, params, locals_var): - function_node = mips.FunctionNode( - self.to_function_label_address(function_name), params, locals_var - ) - self.text[function_name] = function_node - return function_node - - def to_label_count_address(self, label_name): + def make_data_label(self): self.label_count += 1 - return f"{label_name}_{self.label_count}" - - def to_type_label_address(self, type_name): - return f"{type_name}" + return f"data_{self.label_count}" - def to_data_label_address(self, data_name): - return f"{data_name}" + def make_function_label(self): + self.function_label_count += 1 + return f"Label_{self.function_label_count}" - def to_label_local_address(self, label_name): - return f"{self.current_function.label}_{label_name}" - - def to_function_label_address(self, function_name): - return f"{function_name}" + def make_type_label(self): + self.type_count += 1 + return f"type_{self.type_count}" def make_callee_init_instructions(self, function_node: mips.FunctionNode): push_fp = mips.push_to_stack(mips.FP) @@ -75,14 +81,19 @@ def make_callee_final_instructions(self, function_node: mips.FunctionNode): return list(flatten([set_sp, pop_FP, final])) + def register_function(self, name, function: FunctionNode): + self.text[name] = function + self.current_function = function + self.function_labels = {} + def get_param_var_index(self, name): - index = self.current_function.params.index(name) - offset = ((len(self.current_function.params) - 1) - index) * 4 + index = self.current_function.params.index(name) # i + offset = (len(self.current_function.params) - index) * 4 return mips.RegisterRelativeLocation(mips.FP, offset) def get_local_var_index(self, name): index = self.current_function.localvars.index(name) - offset = (index + 2) * -4 + offset = (index + 1) * -4 return mips.RegisterRelativeLocation(mips.FP, offset) def get_var_location(self, name): @@ -102,6 +113,10 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node: ProgramNode) -> mips.ProgramNode: + + self.function_collector.collect(node) + self.data["default_str"] = mips.StringConst("default_str", "") + for dd in node.dotdata: self.visit(dd) @@ -111,32 +126,58 @@ def visit(self, node: ProgramNode) -> mips.ProgramNode: for dc in node.dotcode: self.visit(dc) - return mips.ProgramNode(self.types, self.data, self.text) + return mips.ProgramNode( + [t for t in self.types.values()], + [d for d in self.data.values()], + [f for f in self.text.values()], + ) @visitor.when(TypeNode) def visit(self, node: TypeNode): - type_node, label_name = self.register_type(node.name) - type_node.methods = node.methods - type_node.attributes = node.attributes - type_node.pos = self.type_count - type_node.label_name = label_name - self.type_count += 1 + data_label = self.make_data_label() + self.data[data_label] = mips.StringConst(data_label, node.name) + + type_label = self.make_type_label() + methods = { + type_function: self.function_collector.functions[implemented_function] + for type_function, implemented_function in node.methods + } + defaults = {} + if node.name == "String": + defaults = {"value": "default_str", "length": "type_4_proto"} + else: + defaults = {att: "0" for att in node.attributes} + + self.types[node.name] = mips.TypeNode( + data_label, type_label, node.attributes, methods, self.type_count, defaults + ) @visitor.when(DataNode) def visit(self, node: DataNode): - self.register_data(node.name, node.value) + data_label = self.make_data_label() + self.data[node.name] = mips.StringConst(data_label, node.value) @visitor.when(FunctionNode) def visit(self, node: FunctionNode): + # Init function params = [p.name for p in node.params] local_vars = [lv.name for lv in node.localvars] - if node.name == "entry": - node.name = "main" - function_node = self.register_function(node.name, params, local_vars) - + function_name = self.function_collector.functions[node.name] + function_node = mips.FunctionNode(function_name, params, local_vars) + self.register_function(function_name, function_node) + for inst in node.instructions: + if isinstance(inst, LabelNode): + new_label = self.make_function_label() + self.function_labels[inst.label] = new_label + + # Conventions of Init intructions of the calle function init_callee = self.make_callee_init_instructions(function_node) + + # Body instructions self.current_function = function_node body = [self.visit(instruction) for instruction in node.instructions] + + # Conventions of Final calle instrucctions final_callee = self.make_callee_final_instructions(function_node) total_instructions = list(flatten(init_callee + body + final_callee)) @@ -144,7 +185,7 @@ def visit(self, node: FunctionNode): self.current_function = None @visitor.when(AssignNode) - def visit(self, node): + def visit(self, node: AssignNode): instructions = [] if type(node.source) == VoidNode: @@ -267,32 +308,7 @@ def visit(self, node): return instructions - # @visitor.when(LeqNode) - # def visit(self, node): - # instructions = [] - - # if type(node.left) == int: - # instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - # else: - # instructions.append( - # mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - # ) - - # if type(node.right) == int: - # instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) - # else: - # instructions.append( - # mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - # ) - - # instructions.append(mips.JumpAndLinkNode("less_equal")) - # instructions.append( - # mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - # ) - - # return instructions - - @visitor.when(LessEqualNode) + @visitor.when(LeqNode) def visit(self, node): instructions = [] @@ -328,7 +344,7 @@ def visit(self, node): mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) ) - if type(node.rigth) == int: + if type(node.right) == int: instructions.append(mips.LoadInmediateNode(mips.A1, node.rigth)) else: instructions.append( @@ -417,30 +433,46 @@ def visit(self, node: AllocateNode): else: tp = self.types[node.type].pos - instructions.append(mips.LoadInmediateNode(mips.A0, tp)) + # reg1 = t0 reg2 = t1 + instructions.extend(mips.push_to_stack(mips.T0)) + instructions.extend(mips.push_to_stack(mips.T1)) + instructions.append(mips.LoadInmediateNode(mips.T0, tp)) - instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) - instructions.append(mips.LoadAddressNode(mips.A1, mips.VIRTUAL_TABLE)) - instructions.append(mips.AddUnsignedNode(mips.A1, mips.A1, mips.A0)) + instructions.append(mips.ShiftLeftLogicalNode(mips.T0, mips.T0, 2)) + instructions.append(mips.LoadAddressNode(mips.T1, mips.VIRTUAL_TABLE)) + instructions.append(mips.AddUnsignedNode(mips.T1, mips.T1, mips.T0)) instructions.append( - mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) + mips.LoadWordNode(mips.T1, mips.RegisterRelativeLocation(mips.T1, 0)) ) instructions.append( - mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A1, 4)) + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T1, 4)) ) instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) instructions.append(mips.JumpAndLinkNode("malloc")) - instructions.append(mips.MoveNode(mips.A2, mips.A0)) - instructions.append(mips.MoveNode(mips.A0, mips.A1)) + instructions.append(mips.MoveNode(mips.A1, mips.A0)) + instructions.append(mips.MoveNode(mips.A0, mips.T1)) instructions.append(mips.MoveNode(mips.A1, mips.V0)) instructions.append(mips.JumpAndLinkNode("copy")) + instructions.extend(mips.pop_from_stack(mips.T1)) + instructions.extend(mips.pop_from_stack(mips.T0)) + instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) return instructions + # @visitor.when(AllocateNode) + # def visit(self, node: AllocateNode): + # instructions = [] + # instructions.append(mips.LoadInmediateNode(mips.V0, 9)) + # instructions.append( + # mips.LoadInmediateNode(mips.A0, self.get_type_size(node.type)) + # ) + # instructions.append(mips.SyscallNode()) + # return instructions + @visitor.when(TypeOfNode) def visit(self, node: TypeOfNode): instructions = [] @@ -477,14 +509,13 @@ def visit(self, node): @visitor.when(LabelNode) def visit(self, node: LabelNode): - return [mips.LabelNode(self.to_label_local_address(node.label))] + return [mips.LabelNode(self.function_labels[node.label])] @visitor.when(StaticCallNode) def visit(self, node: StaticCallNode): instructions = [] - instructions.append( - mips.JumpAndLinkNode(self.to_function_label_address(node.function)) - ) + function_to_call = self.function_collector.functions[node.function] + instructions.append(mips.JumpAndLinkNode(function_to_call)) instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) @@ -499,7 +530,8 @@ def visit(self, node: StaticCallNode): def visit(self, node: DynamicCallNode): instructions = [] caller_type = self.types[node.computed_type] - index = [m[0] for m in caller_type.methods].index(node.method) + print(caller_type.methods) + index = [m for m, m_label in caller_type.methods.items()].index(node.method) instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.type)) ) @@ -532,7 +564,7 @@ def visit(self, node: DynamicCallNode): return instructions @visitor.when(ArgNode) - def visit(self, node): + def visit(self, node: ArgNode): self.pushed_args += 1 instructions = [] if type(node.name) == int: @@ -549,7 +581,9 @@ def visit(self, node): @visitor.when(ReturnNode) def visit(self, node): instructions = [] - if isinstance(node.value, int): + if node.value is None: + instructions.append(mips.LoadInmediateNode(mips.V0, 0)) + elif isinstance(node.value, int): instructions.append(mips.LoadInmediateNode(mips.V0, node.value)) elif isinstance(node.value, VoidNode): instructions.append(mips.LoadInmediateNode(mips.V0, 0)) @@ -776,24 +810,24 @@ def visit(self, node: SetAttribNode): @visitor.when(CopyNode) def visit(self, node): instructions = [] - - instructions.extend(mips.push_to_stack(mips.A1)) + # reg1 T0 reg2 A3 + instructions.extend(mips.push_to_stack(mips.T0)) instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.source)) + mips.LoadWordNode(mips.T0, self.get_var_location(node.source)) ) instructions.append( - mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A1, 4)) + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T0, 4)) ) instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) instructions.append(mips.JumpAndLinkNode("malloc")) instructions.append(mips.MoveNode(mips.A2, mips.A0)) - instructions.append(mips.MoveNode(mips.A0, mips.A1)) + instructions.append(mips.MoveNode(mips.A0, mips.T0)) instructions.append(mips.MoveNode(mips.A1, mips.V0)) instructions.append(mips.JumpAndLinkNode("copy")) - instructions.extend(mips.pop_from_stack(mips.A1)) + instructions.extend(mips.pop_from_stack(mips.T0)) instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) @@ -804,7 +838,7 @@ def visit(self, node): def visit(self, node): instructions = [] - local_label = self.to_label_local_address(node.label) + local_label = self.function_labels[node.label] instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.condition)) @@ -816,7 +850,7 @@ def visit(self, node): @visitor.when(GotoNode) def visit(self, node): - local_label = self.to_label_local_address(node.label) + local_label = self.function_labels[node.label] return [mips.JumpNode(local_label)] @visitor.when(ReadIntNode) @@ -830,23 +864,3 @@ def visit(self, node): ) return instructions - - # @visitor.when(NotNode) - # def visit(self, node): - # pass - - # @visitor.when(GetIndexNode) - # def visit(self, node): - # pass - - # @visitor.when(SetIndexNode) - # def visit(self, node): - # pass - - # @visitor.when(ArrayNode) - # def visit(self, node): - # pass - - # @visitor.when(PrefixNode) - # def visit(self, node): - # pass diff --git a/src/compiler/visitors/mips_lib.asm b/src/compiler/visitors/mips_lib.asm new file mode 100644 index 000000000..608534319 --- /dev/null +++ b/src/compiler/visitors/mips_lib.asm @@ -0,0 +1,1152 @@ + + +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 +new_line = 10 +str_size_treshold = 1024 +int_type = 0 +string_type = 0 +type_number = 0 + + + +##################################################################################################### +# 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 + + +equals: + beq $a0 $a1 equals_equal + li $v0 0 + j equals_end + +equals_equal: + li $v0 1 + +equals_end: + jr $ra + + + +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 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 + 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 $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) + 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 + addiu $t3 $a2 1 + div $t3 $t1 + + mfhi $t2 + bne $t2 $zero substr_allign_size + move $t1 $t3 + j substr_new_block + +substr_allign_size: + sub $t1 $t1 $t2 + add $t1 $t1 $t3 + +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 + + +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 + + jr $ra + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mips/mips_printer.py b/src/compiler/visitors/mips_printer.py similarity index 98% rename from mips/mips_printer.py rename to src/compiler/visitors/mips_printer.py index c75953fcd..42c00252a 100644 --- a/mips/mips_printer.py +++ b/src/compiler/visitors/mips_printer.py @@ -1,5 +1,5 @@ -from .mips_ast import * -from ...visitors import visitor +from compiler.cmp.mips_ast import * +from compiler.visitors import visitor class MIPSPrintVisitor: diff --git a/src/compiler/visitors/mips_printer1.py b/src/compiler/visitors/mips_printer1.py deleted file mode 100644 index 225c08e94..000000000 --- a/src/compiler/visitors/mips_printer1.py +++ /dev/null @@ -1,161 +0,0 @@ -from ..cmp.mips_ast import * -import compiler.visitors.visitor as visitor - - -class MIPSPrintVisitor: - @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(node.data[d]) for d in node.data]) - - names_table = f"{TYPE_LIST}:\n" + "\n".join( - [f"\t.word\t{node.types[tp].label}" for tp in node.types] - ) - proto_table = f"{VIRTUAL_TABLE}:\n" + "\n".join( - [f"\t.word\t{node.types[tp].label}_proto" for tp in node.types] - ) - - types = "\n\n".join([self.visit(node.types[tp]) for tp in node.types]) - - code = "\n".join([self.visit(node.text[func]) for func in node.text]) - 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(DataNode) - def visit(self, node): - new_data = node.data.replace("\n", "") - return f'{node.label}: .asciiz "{new_data}"' - - @visitor.when(TypeNode) - def visit(self, node: TypeNode): - methods = "\n".join([f"\t.word\t {m[1]}" for m in node.methods]) - dispatch_table = f"{node.label}_dispatch:\n{methods}" - proto_begin = f"{node.label}_proto:\n\t.word\t{node.pos}\n\t.word\t{len(node.attributes)*4}\n\t.word\t{node.label}_dispatch" - proto_attr = "\n".join([f"\t.word\t{0}" for attr in node.attributes]) - proto_end = f"\t.word\t{-1}" - 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.constant_number)}" - - @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.label}:" - - @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)}" diff --git a/src/main.py b/src/main.py index 44ee653a8..31132513c 100644 --- a/src/main.py +++ b/src/main.py @@ -85,21 +85,6 @@ def main(args): # if checker.errors: # exit(1) - # mips to cil - # cil_visitor = COOLToCILVisitor(context) - # cil_ast = cil_visitor.visit(ast, scope) - - # cil_formatter = PrintCILVisitor() - # print(cil_formatter.visit(cil_ast)) - - # mips_visitor = CILToMIPSVisitor() - # mips_ast = mips_visitor.visit(cil_ast) - # mips_formatter = MIPSPrintVisitor() - # print(mips_formatter.visit(mips_ast)) - - cil_visitor = COOLToCILVisitor(context) - cil_ast = cil_visitor.visit(ast, scope) - # COOL to CIL cil_visitor = COOLToCILVisitor(context) cil_ast = cil_visitor.visit(ast, scope) @@ -107,6 +92,21 @@ def main(args): # cil_formatter = PrintCILVisitor() # print(cil_formatter.visit(cil_ast)) + cil_to_mips = CILToMIPSVisitor() + mips_ast = cil_to_mips.visit(cil_ast) + printer = MIPSPrintVisitor() + mips_code = printer.visit(mips_ast) + # print(mips_code) + + 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("src/compiler/visitors/mips_lib.asm") as f2: + f.write("".join(f2.readlines())) + if __name__ == "__main__": import argparse From 9e4827cf8daf8467359c446b0da4e2951d47607f Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Sat, 26 Feb 2022 21:57:27 -0500 Subject: [PATCH 125/138] fix bug in type inferencer about nested scopes --- src/compiler/visitors/type_inferencer.py | 52 ++++++++++++------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/compiler/visitors/type_inferencer.py b/src/compiler/visitors/type_inferencer.py index 3f6e2e929..19fbad38e 100644 --- a/src/compiler/visitors/type_inferencer.py +++ b/src/compiler/visitors/type_inferencer.py @@ -1,37 +1,37 @@ from ..cmp.ast import ( - ProgramNode, - ClassDeclarationNode, - AttrDeclarationNode, - FuncDeclarationNode, + ArithmeticNode, AssignNode, + AttrDeclarationNode, + BlockNode, CallNode, CaseNode, - BlockNode, - LoopNode, - ConditionalNode, - LetNode, - ArithmeticNode, + ClassDeclarationNode, ComparisonNode, - EqualNode, - VoidNode, - NotNode, - NegNode, + ConditionalNode, + ConstantBoolNode, ConstantNumNode, ConstantStringNode, - ConstantBoolNode, - VariableNode, + EqualNode, + FuncDeclarationNode, InstantiateNode, + LetNode, + LoopNode, + NegNode, + NotNode, + ProgramNode, + VariableNode, + VoidNode, ) from ..cmp.semantic import ( + AutoType, Context, + ErrorType, InferencerManager, + LCA, Method, Scope, - SemanticError, - ErrorType, SelfType, - AutoType, - LCA, + SemanticError, Type, ) from .utils import AUTOTYPE_ERROR @@ -207,21 +207,21 @@ def visit(self, node, scope, types): scope_index = self.scope_children_id self.scope_children_id = 0 - typex, computed_types = self.visit( node.expr, scope.children[scope_index], conforms_to_types ) + self.scope_children_id = scope_index + 1 + if isinstance(var.type, AutoType): self.manager.upd_conformed_by(var.idx, computed_types) - self.scope_children_id = scope_index + 1 return typex, computed_types @visitor.when(CallNode) def visit(self, node, scope, types): # Check cast type cast_type = None - if node.type is not None: + if not node.type == "": try: cast_type = self.context.get_type(node.type) if isinstance(cast_type, AutoType): @@ -312,7 +312,6 @@ def visit(self, node: CaseNode, scope: Scope, types): child_scope.update_variable(branch_name, inf_type) self.scope_children_id = 0 - _, computed_types = self.visit(expr, child_scope, types) expr_types.extend(computed_types) @@ -326,15 +325,14 @@ def visit(self, node, scope, types): self.scope_children_id = 0 # Check expressions but last one - sz = len(node.expr_list) - 1 - for expr in node.expr_list[:sz]: + for expr in node.expr_list[:-1]: self.visit(expr, nscope, []) # Check last expression typex, computed_types = self.visit(node.expr_list[-1], nscope, types) # return the type of the last expression of the list - self.scope_children_id = scope_index + self.scope_children_id = scope_index + 1 return typex, computed_types @visitor.when(LoopNode) @@ -349,7 +347,7 @@ def visit(self, node, scope, types): # checking body self.visit(node.body, nscope, []) - self.scope_children_id = scope_index + self.scope_children_id = scope_index + 1 return self.obj_type, [self.obj_type] @visitor.when(ConditionalNode) From 3113b578c5121cfcc122f56e7cab19ad44a9bca9 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Sat, 26 Feb 2022 23:21:15 -0500 Subject: [PATCH 126/138] organize project in packages --- src/compiler/visitors/cool2cil/__init__.py | 0 .../visitors/{ => cool2cil}/cil_formatter.py | 2 +- .../visitors/{ => cool2cil}/cool2cil.py | 6 ++--- .../visitors/semantics_check/__init__.py | 0 .../{ => semantics_check}/formatter.py | 22 ++++++++++++------- .../{ => semantics_check}/type_builder.py | 6 ++--- .../{ => semantics_check}/type_checker.py | 6 ++--- .../{ => semantics_check}/type_collector.py | 12 +++++----- .../{ => semantics_check}/type_inferencer.py | 6 ++--- src/main.py | 14 ++++++------ 10 files changed, 40 insertions(+), 34 deletions(-) create mode 100644 src/compiler/visitors/cool2cil/__init__.py rename src/compiler/visitors/{ => cool2cil}/cil_formatter.py (99%) rename src/compiler/visitors/{ => cool2cil}/cool2cil.py (99%) create mode 100644 src/compiler/visitors/semantics_check/__init__.py rename src/compiler/visitors/{ => semantics_check}/formatter.py (95%) rename src/compiler/visitors/{ => semantics_check}/type_builder.py (98%) rename src/compiler/visitors/{ => semantics_check}/type_checker.py (99%) rename src/compiler/visitors/{ => semantics_check}/type_collector.py (97%) rename src/compiler/visitors/{ => semantics_check}/type_inferencer.py (99%) diff --git a/src/compiler/visitors/cool2cil/__init__.py b/src/compiler/visitors/cool2cil/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/compiler/visitors/cil_formatter.py b/src/compiler/visitors/cool2cil/cil_formatter.py similarity index 99% rename from src/compiler/visitors/cil_formatter.py rename to src/compiler/visitors/cool2cil/cil_formatter.py index a4cdc4089..a9e2ce4dc 100644 --- a/src/compiler/visitors/cil_formatter.py +++ b/src/compiler/visitors/cool2cil/cil_formatter.py @@ -1,5 +1,5 @@ +from ...cmp.cil_ast import * import compiler.visitors.visitor as visitor -from ..cmp.cil_ast import * class PrintCILVisitor(object): diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil/cool2cil.py similarity index 99% rename from src/compiler/visitors/cool2cil.py rename to src/compiler/visitors/cool2cil/cool2cil.py index 484e6fd95..ac6dcbf21 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil/cool2cil.py @@ -1,5 +1,5 @@ -from ..cmp import cil_ast as cil -from ..cmp.ast import ( +from ...cmp import cil_ast as cil +from ...cmp.ast import ( AssignNode, AttrDeclarationNode, BlockNode, @@ -29,7 +29,7 @@ VariableNode, VoidNode, ) -from ..cmp.semantic import ( +from ...cmp.semantic import ( Context, Method, Scope, diff --git a/src/compiler/visitors/semantics_check/__init__.py b/src/compiler/visitors/semantics_check/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/compiler/visitors/formatter.py b/src/compiler/visitors/semantics_check/formatter.py similarity index 95% rename from src/compiler/visitors/formatter.py rename to src/compiler/visitors/semantics_check/formatter.py index 8fb18764b..6a8f33575 100644 --- a/src/compiler/visitors/formatter.py +++ b/src/compiler/visitors/semantics_check/formatter.py @@ -1,15 +1,21 @@ -import compiler.visitors.visitor as visitor -from ..cmp.ast import ProgramNode, ClassDeclarationNode, FuncDeclarationNode -from ..cmp.ast import AttrDeclarationNode, AssignNode, CallNode -from ..cmp.ast import ( - CaseNode, +from ...cmp.ast import ( + AssignNode, + AtomicNode, + AttrDeclarationNode, + BinaryNode, BlockNode, - LoopNode, + CallNode, + CaseNode, + ClassDeclarationNode, ConditionalNode, - LetNode, + FuncDeclarationNode, InstantiateNode, + LetNode, + LoopNode, + ProgramNode, + UnaryNode, ) -from ..cmp.ast import UnaryNode, BinaryNode, AtomicNode +import compiler.visitors.visitor as visitor class FormatVisitor(object): diff --git a/src/compiler/visitors/type_builder.py b/src/compiler/visitors/semantics_check/type_builder.py similarity index 98% rename from src/compiler/visitors/type_builder.py rename to src/compiler/visitors/semantics_check/type_builder.py index 0d4e2ac7f..1d3c0580f 100644 --- a/src/compiler/visitors/type_builder.py +++ b/src/compiler/visitors/semantics_check/type_builder.py @@ -1,10 +1,10 @@ -from ..cmp.ast import ( +from ...cmp.ast import ( ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode, ) -from ..cmp.semantic import ( +from ...cmp.semantic import ( Context, SemanticError, ErrorType, @@ -13,7 +13,7 @@ SelfType, Type, ) -from .utils import ( +from ..utils import ( ALREADY_DEFINED, MAIN_CLASS_ERROR, MAIN_PROGRAM_ERROR, diff --git a/src/compiler/visitors/type_checker.py b/src/compiler/visitors/semantics_check/type_checker.py similarity index 99% rename from src/compiler/visitors/type_checker.py rename to src/compiler/visitors/semantics_check/type_checker.py index 9422179f2..0009c1b22 100644 --- a/src/compiler/visitors/type_checker.py +++ b/src/compiler/visitors/semantics_check/type_checker.py @@ -1,4 +1,4 @@ -from ..cmp.ast import ( +from ...cmp.ast import ( BinaryNode, ProgramNode, ClassDeclarationNode, @@ -23,7 +23,7 @@ VariableNode, InstantiateNode, ) -from ..cmp.semantic import ( +from ...cmp.semantic import ( Context, InferencerManager, Method, @@ -35,7 +35,7 @@ LCA, Type, ) -from .utils import * +from ..utils import * from typing import List, Optional, Tuple import compiler.visitors.visitor as visitor diff --git a/src/compiler/visitors/type_collector.py b/src/compiler/visitors/semantics_check/type_collector.py similarity index 97% rename from src/compiler/visitors/type_collector.py rename to src/compiler/visitors/semantics_check/type_collector.py index b471817c1..6db89e746 100644 --- a/src/compiler/visitors/type_collector.py +++ b/src/compiler/visitors/semantics_check/type_collector.py @@ -1,8 +1,5 @@ -from typing import Dict, List, Optional, Tuple -from compiler.cmp.utils import Token -from compiler.visitors.utils import AUTOTYPE_ERROR -import compiler.visitors.visitor as visitor -from ..cmp.semantic import ( +from ...cmp.utils import Token +from ...cmp.semantic import ( SemanticError, Type, Context, @@ -14,7 +11,10 @@ SelfType, AutoType, ) -from ..cmp.ast import ProgramNode, ClassDeclarationNode +from ...cmp.ast import ProgramNode, ClassDeclarationNode +from ..utils import AUTOTYPE_ERROR +from typing import Dict, List, Optional, Tuple +import compiler.visitors.visitor as visitor built_in_types = [] diff --git a/src/compiler/visitors/type_inferencer.py b/src/compiler/visitors/semantics_check/type_inferencer.py similarity index 99% rename from src/compiler/visitors/type_inferencer.py rename to src/compiler/visitors/semantics_check/type_inferencer.py index 19fbad38e..b67c343b7 100644 --- a/src/compiler/visitors/type_inferencer.py +++ b/src/compiler/visitors/semantics_check/type_inferencer.py @@ -1,4 +1,4 @@ -from ..cmp.ast import ( +from ...cmp.ast import ( ArithmeticNode, AssignNode, AttrDeclarationNode, @@ -22,7 +22,7 @@ VariableNode, VoidNode, ) -from ..cmp.semantic import ( +from ...cmp.semantic import ( AutoType, Context, ErrorType, @@ -34,7 +34,7 @@ SemanticError, Type, ) -from .utils import AUTOTYPE_ERROR +from ..utils import AUTOTYPE_ERROR from typing import List, Tuple import compiler.visitors.visitor as visitor diff --git a/src/main.py b/src/main.py index 7a94dc174..2e3f1897c 100644 --- a/src/main.py +++ b/src/main.py @@ -1,13 +1,13 @@ from compiler.cmp.grammar import G from compiler.lexer.lex import CoolLexer from compiler.parser.parser import LR1Parser, evaluate_reverse_parse -from compiler.visitors.cil_formatter import PrintCILVisitor -from compiler.visitors.cool2cil import COOLToCILVisitor -from compiler.visitors.formatter import FormatVisitor -from compiler.visitors.type_collector import TypeCollector -from compiler.visitors.type_builder import TypeBuilder -from compiler.visitors.type_checker import TypeChecker -from compiler.visitors.type_inferencer import TypeInferencer +from compiler.visitors.cool2cil.cil_formatter import PrintCILVisitor +from compiler.visitors.cool2cil.cool2cil import COOLToCILVisitor +from compiler.visitors.semantics_check.formatter import FormatVisitor +from compiler.visitors.semantics_check.type_builder import TypeBuilder +from compiler.visitors.semantics_check.type_checker import TypeChecker +from compiler.visitors.semantics_check.type_collector import TypeCollector +from compiler.visitors.semantics_check.type_inferencer import TypeInferencer from sys import exit From 32d7ce4e1179c25b34fa8954696f345f73c32464 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Sun, 27 Feb 2022 00:39:37 -0500 Subject: [PATCH 127/138] update report --- doc/report.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/doc/report.md b/doc/report.md index ee9f95d66..dccf0c443 100644 --- a/doc/report.md +++ b/doc/report.md @@ -25,14 +25,14 @@ make install Para compilar y ejecutar un archivo en COOL deberá ejecutar el siguiente comando en la terminal desde el directorio `/src`: ```bash -make main .cl +make main .cl ``` Si usted no proporciona ningún archivo, se tomará por defecto el archivo `code.cl` presente en dicho directorio. El comando anterior es equivalente a: ```bash -./coolc.sh .cl -spim -file .mips +./coolc.sh .cl +spim -file .mips ``` ## Arquitectura del compilador @@ -132,10 +132,52 @@ En este recorrido se generan las 3 principales estructuras que posee el código - los **datos**, sección en la que se encuentran todas las "macros" que serán utilizadas durante la ejecución, - el **código**, donde son colocadas todas las funciones generadas a partir del recorrido. -Dentro de los errores chequeados en el ast de CIL se encuentra la comprobación de que no se realicen divisiones por 0. Aunque es cierto que el valor se tiene mayormente en tiempo de ejecución, agregar esta comprobación en el ast de CIL hace mucho más sencillo el proceso de recorrido de este ast posteriormente. También se chequea el llamado a una función dinámica de una variable de tipo _void_ y los índices en _strings_ fuera de rango. +En este recorrido por el ast, se define la estructura necesaria para la detección de ciertos errores en tiempo de ejecución. Entre los errores que se chequean se encuentran: la comprobación de que no se realicen divisiones por 0, el llamado a una función dinámica de una variable de tipo _void_, los índices en _strings_ fuera de rango, entre otros. Agregar esta comprobación en el ast de CIL hace mucho más sencillo el proceso de recorrido de este *ast* posteriormente. En el caso del _case_ se chequea que la expresión principal no sea de tipo _void_ y además, que se conforme a alguna rama en la ejecución de este. El algoritmo empleado para reconocer por cuál de las ramas continuará la ejecución del código comienza por: tomar el tipo de todas las ramas del _case_, llámese a este conjunto $A$; por cada elemento del conjunto $A$ se toma la cantidad de tipos dentro del propio conjunto que se conforman a $a_i, i \in [1, |A|]$ ,de modo que se obtienen los pares $$. Se define $|\{a_j \leq a_i, \forall j, j\in[1, |A|]\}|$ como $a_{i_c}$. Tomando los elementos $a_i$ por el que menor $a_{i_c}$ tenga, se estará tomando los nodos más abajos en el árbol de tipos dentro de cada posible rama de este. Si se ordenan las ramas del _case_ por el que menor $a_{i_c}$ se obtendrá una lista. Luego se recorre esta generando por cada elemento el subconjunto $B_i$ donde $b_{i_i} \in B_i$ si $b_{i_i} <= a_i$. Se chequea si el tipo de la expresión principal del _case_ aparece en este subconjunto. En el caso de que aparezca, el case se resuelve yendo por la rama que posee el tipo $a_i$. +### Estructura del proyecto + +El *pipeline* que sigue el proceso de compilación se observa en el archivo [main.py](https://github.com/codersUP/cool-compiler-2021/blob/master/src/main.py). Se hace uso de las funcionalidades implementadas en el paquete `compiler`, que presenta la siguiente estructura: + +```bash +compiler/ +├── cmp +│ ├── ast.py +│ ├── automata.py +│ ├── cil_ast.py +│ ├── grammar.py +│ ├── __init__.py +│ ├── pycompiler.py +│ ├── semantic.py +│ └── utils.py +├── __init__.py +├── lexer +│ ├── __init__.py +│ └── lex.py +├── parser +│ ├── __init__.py +│ ├── parser.py +│ └── utils.py +└── visitors + ├── cool2cil + │ ├── cil_formatter.py + │ ├── cool2cil.py + │ └── __init__.py + ├── __init__.py + ├── semantics_check + │ ├── formatter.py + │ ├── __init__.py + │ ├── type_builder.py + │ ├── type_checker.py + │ ├── type_collector.py + │ └── type_inferencer.py + ├── utils.py + └── visitor.py +``` + +En su mayoría los módulos que posee el paquete `cmp` fueron tomados de los proyectos y contenidos vistos en 3er año. Los paquetes `lexer` y `parser` definen la lógica para la tokenización y posterior parsing del texto de entrada respectivamente. El paquete `visitors` contiene las funcionalidades para llevar a cabo los recorridos sobre los *ast*, que en este caso serían: los *visitors* para realizar el chequeo semántico, el *visitor* que permite traducir de COOL a CIL, y finalmente, el *visitor* que permite traducir de CIL a MIPS. + ## Licencia Este proyecto se encuentra bajo la Licencia (MIT License) - ver el archivo [LICENSE.md](LICENSE.md) para más detalles. From 6584999f57e3c1b2d13d1921c9088567a0de7084 Mon Sep 17 00:00:00 2001 From: kikexd Date: Sun, 27 Feb 2022 01:00:26 -0500 Subject: [PATCH 128/138] fix issue in alloc instruction store word after creating an instance --- src/compiler/cmp/mips_ast.py | 2 +- src/compiler/visitors/cil2mips.py | 94 +++++++++++++++++-------------- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/src/compiler/cmp/mips_ast.py b/src/compiler/cmp/mips_ast.py index 02cc0d240..48a5b3b30 100644 --- a/src/compiler/cmp/mips_ast.py +++ b/src/compiler/cmp/mips_ast.py @@ -22,7 +22,7 @@ def __init__(self, name): # ***********************Utils*********************** -MAIN_FUNCTION_NAME = "function_main_at_Main" +MAIN_FUNCTION_NAME = "main" VIRTUAL_TABLE = "virtual_table" TYPE_LIST = "type_list" diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index 84c3f3f65..ab808087e 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -103,7 +103,10 @@ def get_var_location(self, name): return self.get_local_var_index(name) def get_type_size(self, type_name): - return len(self.types[type_name].attributes) * 4 + print("---------------") + print(type_name, self.types[type_name].attributes) + print("---------------") + return (len(self.types[type_name].attributes) + 1) * 4 class CILToMIPSVisitor(BaseCILToMIPSVisitor): @@ -177,6 +180,11 @@ def visit(self, node: FunctionNode): self.current_function = function_node body = [self.visit(instruction) for instruction in node.instructions] + print( + f"*************Instructions at {self.function_collector.functions[node.name], node.name}*********************" + ) + [print(i) for i in node.instructions] + print("***********************************") # Conventions of Final calle instrucctions final_callee = self.make_callee_final_instructions(function_node) @@ -423,56 +431,60 @@ def visit(self, node): return instructions - @visitor.when(AllocateNode) - def visit(self, node: AllocateNode): - instructions = [] + # @visitor.when(AllocateNode) + # def visit(self, node: AllocateNode): + # instructions = [] - tp = 0 - if node.type.isnumeric(): - tp = node.type - else: - tp = self.types[node.type].pos + # tp = 0 + # if node.type.isnumeric(): + # tp = node.type + # else: + # tp = self.types[node.type].pos - # reg1 = t0 reg2 = t1 - instructions.extend(mips.push_to_stack(mips.T0)) - instructions.extend(mips.push_to_stack(mips.T1)) - instructions.append(mips.LoadInmediateNode(mips.T0, tp)) + # # reg1 = t0 reg2 = t1 + # instructions.extend(mips.push_to_stack(mips.T0)) + # instructions.extend(mips.push_to_stack(mips.T1)) + # instructions.append(mips.LoadInmediateNode(mips.T0, tp)) - instructions.append(mips.ShiftLeftLogicalNode(mips.T0, mips.T0, 2)) - instructions.append(mips.LoadAddressNode(mips.T1, mips.VIRTUAL_TABLE)) - instructions.append(mips.AddUnsignedNode(mips.T1, mips.T1, mips.T0)) - instructions.append( - mips.LoadWordNode(mips.T1, mips.RegisterRelativeLocation(mips.T1, 0)) - ) - instructions.append( - mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T1, 4)) - ) - instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) - instructions.append(mips.JumpAndLinkNode("malloc")) - instructions.append(mips.MoveNode(mips.A1, mips.A0)) - instructions.append(mips.MoveNode(mips.A0, mips.T1)) - instructions.append(mips.MoveNode(mips.A1, mips.V0)) - instructions.append(mips.JumpAndLinkNode("copy")) + # instructions.append(mips.ShiftLeftLogicalNode(mips.T0, mips.T0, 2)) + # instructions.append(mips.LoadAddressNode(mips.T1, mips.VIRTUAL_TABLE)) + # instructions.append(mips.AddUnsignedNode(mips.T1, mips.T1, mips.T0)) + # instructions.append( + # mips.LoadWordNode(mips.T1, mips.RegisterRelativeLocation(mips.T1, 0)) + # ) + # instructions.append( + # mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T1, 4)) + # ) + # instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) + # instructions.append(mips.JumpAndLinkNode("malloc")) + # instructions.append(mips.MoveNode(mips.A1, mips.A0)) + # instructions.append(mips.MoveNode(mips.A0, mips.T1)) + # instructions.append(mips.MoveNode(mips.A1, mips.V0)) + # instructions.append(mips.JumpAndLinkNode("copy")) - instructions.extend(mips.pop_from_stack(mips.T1)) - instructions.extend(mips.pop_from_stack(mips.T0)) + # instructions.extend(mips.pop_from_stack(mips.T1)) + # instructions.extend(mips.pop_from_stack(mips.T0)) + + # instructions.append( + # mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) + # ) + # return instructions + + @visitor.when(AllocateNode) + def visit(self, node: AllocateNode): + instructions = [] + instructions.append( + mips.LoadInmediateNode(mips.A0, self.get_type_size(node.type)) + ) + instructions.append(mips.LoadInmediateNode(mips.V0, 9)) + instructions.append(mips.SyscallNode()) instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) return instructions - # @visitor.when(AllocateNode) - # def visit(self, node: AllocateNode): - # instructions = [] - # instructions.append(mips.LoadInmediateNode(mips.V0, 9)) - # instructions.append( - # mips.LoadInmediateNode(mips.A0, self.get_type_size(node.type)) - # ) - # instructions.append(mips.SyscallNode()) - # return instructions - @visitor.when(TypeOfNode) def visit(self, node: TypeOfNode): instructions = [] @@ -537,7 +549,7 @@ def visit(self, node: DynamicCallNode): ) instructions.append(mips.LoadAddressNode(mips.A1, mips.VIRTUAL_TABLE)) - instructions.append(mips.ShiftLeftLogicalNode(mips.A2, mips.A1, 2)) + instructions.append(mips.ShiftLeftLogicalNode(mips.A2, mips.A0, 2)) instructions.append(mips.AddUnsignedNode(mips.A1, mips.A1, mips.A2)) instructions.append( mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) From bc4818f46c3f7332b7a1ce82018fa4b5ba1bfa98 Mon Sep 17 00:00:00 2001 From: dgd Date: Mon, 28 Feb 2022 20:19:40 -0500 Subject: [PATCH 129/138] fixing hello world --- src/compiler/visitors/cil2mips.py | 38 ++++++++++++++++++------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index ab808087e..1a61dcde7 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -63,23 +63,25 @@ def make_type_label(self): return f"type_{self.type_count}" def make_callee_init_instructions(self, function_node: mips.FunctionNode): + push_ra = mips.push_to_stack(mips.RA) push_fp = mips.push_to_stack(mips.FP) set_fp = mips.AddInmediateNode(mips.FP, mips.SP, 4) local_vars_frame_size = len(function_node.localvars) * 4 set_sp = mips.AddInmediateNode(mips.SP, mips.SP, -local_vars_frame_size) - return list(flatten([push_fp, set_fp, set_sp])) + return list(flatten([push_ra, push_fp, set_fp, set_sp])) def make_callee_final_instructions(self, function_node: mips.FunctionNode): local_vars_frame_size = len(function_node.localvars) * 4 set_sp = mips.AddInmediateNode(mips.SP, mips.SP, local_vars_frame_size) pop_FP = mips.pop_from_stack(mips.FP) + pop_RA = mips.pop_from_stack(mips.RA) final = None if function_node.label == mips.MAIN_FUNCTION_NAME: final = mips.exit_program() else: final = mips.JumpRegister(mips.RA) - return list(flatten([set_sp, pop_FP, final])) + return list(flatten([set_sp, pop_FP, pop_RA, final])) def register_function(self, name, function: FunctionNode): self.text[name] = function @@ -87,13 +89,13 @@ def register_function(self, name, function: FunctionNode): self.function_labels = {} def get_param_var_index(self, name): - index = self.current_function.params.index(name) # i + index = self.current_function.params.index(name) offset = (len(self.current_function.params) - index) * 4 return mips.RegisterRelativeLocation(mips.FP, offset) def get_local_var_index(self, name): index = self.current_function.localvars.index(name) - offset = (index + 1) * -4 + offset = (index + 2) * -4 return mips.RegisterRelativeLocation(mips.FP, offset) def get_var_location(self, name): @@ -145,14 +147,9 @@ def visit(self, node: TypeNode): type_function: self.function_collector.functions[implemented_function] for type_function, implemented_function in node.methods } - defaults = {} - if node.name == "String": - defaults = {"value": "default_str", "length": "type_4_proto"} - else: - defaults = {att: "0" for att in node.attributes} self.types[node.name] = mips.TypeNode( - data_label, type_label, node.attributes, methods, self.type_count, defaults + data_label, type_label, node.attributes, methods, self.type_count - 1 ) @visitor.when(DataNode) @@ -474,11 +471,22 @@ def visit(self, node): @visitor.when(AllocateNode) def visit(self, node: AllocateNode): instructions = [] + + tp = 0 + if node.type.isnumeric(): + tp = node.type + else: + tp = self.types[node.type].pos + instructions.append( mips.LoadInmediateNode(mips.A0, self.get_type_size(node.type)) ) instructions.append(mips.LoadInmediateNode(mips.V0, 9)) instructions.append(mips.SyscallNode()) + instructions.append(mips.LoadInmediateNode(mips.A0, tp)) + instructions.append( + mips.StoreWordNode(mips.A0, mips.RegisterRelativeLocation(mips.V0, 0)) + ) instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) @@ -542,8 +550,8 @@ def visit(self, node: StaticCallNode): def visit(self, node: DynamicCallNode): instructions = [] caller_type = self.types[node.computed_type] - print(caller_type.methods) index = [m for m, m_label in caller_type.methods.items()].index(node.method) + instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.type)) ) @@ -554,9 +562,6 @@ def visit(self, node: DynamicCallNode): instructions.append( mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) ) - instructions.append( - mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 8)) - ) instructions.append(mips.AddInmediateUnsignedNode(mips.A1, mips.A1, index * 4)) instructions.append( mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A1, 0)) @@ -780,7 +785,7 @@ def visit(self, node: GetAttribNode): instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) tp = self.types[comp_type] - offset = 12 + tp.attributes.index(node.attr) * 4 + offset = (tp.attributes.index(node.attr) + 1) * 4 instructions.append( mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) ) @@ -802,7 +807,8 @@ def visit(self, node: SetAttribNode): ) tp = self.types[comp_type] - offset = 12 + tp.attributes.index(node.attr) * 4 + # offset = 4 + tp.attributes.index(node.attr) * 4 + offset = (tp.attributes.index(node.attr) + 1) * 4 instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) From 1891c232e27d709c29becdd8aabf254af9a8358e Mon Sep 17 00:00:00 2001 From: dgd Date: Mon, 28 Feb 2022 21:26:09 -0500 Subject: [PATCH 130/138] tests in codegen not working --- hello_world.cl | 5 ++++ src/compiler/cmp/mips_ast.py | 3 +-- src/compiler/visitors/mips_printer.py | 33 ++++++++++++++------------- src/main.py | 9 ++++++-- 4 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 hello_world.cl diff --git a/hello_world.cl b/hello_world.cl new file mode 100644 index 000000000..0c818f908 --- /dev/null +++ b/hello_world.cl @@ -0,0 +1,5 @@ +class Main inherits IO { + main(): IO { + out_string("Hello, World.\n") + }; +}; diff --git a/src/compiler/cmp/mips_ast.py b/src/compiler/cmp/mips_ast.py index 48a5b3b30..a6e72f036 100644 --- a/src/compiler/cmp/mips_ast.py +++ b/src/compiler/cmp/mips_ast.py @@ -79,13 +79,12 @@ def __init__(self, label, params, localvars): class TypeNode(Node): - def __init__(self, data_label, type_label, attributes, methods, pos, defaults): + def __init__(self, data_label, type_label, attributes, methods, pos): self.data_label = data_label self.type_label = type_label self.attributes = attributes self.methods = methods self.pos = pos - self.defaults = defaults class InstructionNode(Node): diff --git a/src/compiler/visitors/mips_printer.py b/src/compiler/visitors/mips_printer.py index 42c00252a..54455d71f 100644 --- a/src/compiler/visitors/mips_printer.py +++ b/src/compiler/visitors/mips_printer.py @@ -27,14 +27,14 @@ def visit(self, node): names_table = f"{TYPE_LIST}:\n\t .word " + ", ".join( [f"{tp.data_label}" for tp in node.types] ) - proto_table = f"{VIRTUAL_TABLE}:\n\t .word " + ", ".join( - [f"{tp.type_label}_proto" for tp in node.types] + virtual_table = f"{VIRTUAL_TABLE}:\n\t .word " + ", ".join( + [f"{tp.type_label}_dispatch" 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.text]) - 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}" + return f"{data_section_header}\n{static_strings}\n\n{names_table}\n\n{virtual_table}\n\n{types}\n\n.text\n\t.globl main\n{code}" @visitor.when(StringConst) def visit(self, node): @@ -46,18 +46,19 @@ def visit(self, node: TypeNode): print(node.attributes) methods = ", ".join([f"{node.methods[m]}" for m in node.methods]) dispatch_table = f"{node.type_label}_dispatch:\n\t .word {methods}" - proto_begin = f"{node.type_label}_proto:\n\t.word\t{node.pos}, {len(node.attributes)*4}, {node.type_label}_dispatch" - proto_attr = ", ".join( - [f'{node.defaults.get(attr,"0")}' for attr in node.attributes] - ) - proto_end = f"{-1}" - proto = ( - f"{proto_begin}, {proto_attr}, {proto_end}" - if proto_attr != "" - else f"{proto_begin}, {proto_end}" - ) - - return f"{dispatch_table}\n\n{proto}" + # proto_begin = f"{node.type_label}_proto:\n\t.word\t{node.pos}, {len(node.attributes)*4}, {node.type_label}_dispatch" + # proto_attr = ", ".join( + # [f'{node.defaults.get(attr,"0")}' for attr in node.attributes] + # ) + # proto_end = f"{-1}" + # proto = ( + # f"{proto_begin}, {proto_attr}, {proto_end}" + # if proto_attr != "" + # else f"{proto_begin}, {proto_end}" + # ) + + # return f"{dispatch_table}\n\n{proto}" + return f"{dispatch_table}" @visitor.when(SyscallNode) def visit(self, node): @@ -101,7 +102,7 @@ def visit(self, node): @visitor.when(JumpRegisterAndLinkNode) def visit(self, node): - return f"jal {self.visit(node.reg)}" + return f"jalr {self.visit(node.reg)}" @visitor.when(LoadWordNode) def visit(self, node): diff --git a/src/main.py b/src/main.py index 31132513c..131db2937 100644 --- a/src/main.py +++ b/src/main.py @@ -89,8 +89,10 @@ def main(args): cil_visitor = COOLToCILVisitor(context) cil_ast = cil_visitor.visit(ast, scope) - # cil_formatter = PrintCILVisitor() - # print(cil_formatter.visit(cil_ast)) + print("*****************************CIL CODE **************************") + cil_formatter = PrintCILVisitor() + print(cil_formatter.visit(cil_ast)) + print("*****************************CIL CODE **************************") cil_to_mips = CILToMIPSVisitor() mips_ast = cil_to_mips.visit(cil_ast) @@ -101,12 +103,15 @@ def main(args): out_file = args.file.split(".") out_file[-1] = "mips" out_file = ".".join(out_file) + out_file = f"{args.file[:-3]}.mips" with open(out_file, "w") as f: f.write(mips_code) with open("src/compiler/visitors/mips_lib.asm") as f2: f.write("".join(f2.readlines())) + exit(0) + if __name__ == "__main__": import argparse From 692cf5248f49f6b4bdec8cf5e41bcc3aea341cf7 Mon Sep 17 00:00:00 2001 From: dgd Date: Tue, 1 Mar 2022 13:41:36 -0500 Subject: [PATCH 131/138] codegen tests fixed --- hello_world.cl | 5 - src/compiler/cmp/cil.py | 304 ------- src/compiler/cmp/cil_ast.py | 12 + src/compiler/visitors/cil2mips.py | 56 +- src/compiler/visitors/cool2cil.py | 3 +- src/compiler/visitors/cool_to_cil.py | 1183 ------------------------- src/compiler/visitors/mips_printer.py | 15 - src/hello_world.cl | 5 - src/main.py | 12 +- 9 files changed, 60 insertions(+), 1535 deletions(-) delete mode 100644 hello_world.cl delete mode 100644 src/compiler/cmp/cil.py delete mode 100644 src/compiler/visitors/cool_to_cil.py delete mode 100644 src/hello_world.cl diff --git a/hello_world.cl b/hello_world.cl deleted file mode 100644 index 0c818f908..000000000 --- a/hello_world.cl +++ /dev/null @@ -1,5 +0,0 @@ -class Main inherits IO { - main(): IO { - out_string("Hello, World.\n") - }; -}; diff --git a/src/compiler/cmp/cil.py b/src/compiler/cmp/cil.py deleted file mode 100644 index e274c6109..000000000 --- a/src/compiler/cmp/cil.py +++ /dev/null @@ -1,304 +0,0 @@ -# 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 - self.ids = dict() - self.labels_count = 0 - - -class ParamNode(Node): - def __init__(self, name): - self.name = name - - -class LocalNode(Node): - def __init__(self, name): - self.name = name - - -class InstructionNode(Node): - def __init__(self): - self.leader = False - - -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): - 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 LessEqualNode(ArithmeticNode): - pass - - -class LessNode(ArithmeticNode): - pass - - -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 - 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): - self.obj = obj - self.attr = attr - self.value = value - self.computed_type = computed_type - - -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 - - 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): - 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): - self.type = xtype - 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): - self.dest = dest - self.msg = msg - - def __repr__(self): - return f"{self.dest} LOAD {self.msg}" - - -class ExitNode(InstructionNode): - pass - - -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): - self.dest = dest - self.source = source - - -class LengthNode(InstructionNode): - def __init__(self, dest, source): - self.dest = dest - self.source = source - - -class ConcatNode(InstructionNode): - 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): - self.dest = dest - self.str_value = str_value - self.index = index - self.length = length - - -class ReadStrNode(InstructionNode): - def __init__(self, dest): - self.dest = dest - - -class ReadIntNode(InstructionNode): - def __init__(self, dest): - self.dest = 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): - self.value = value - - -class ComplementNode(InstructionNode): - def __init__(self, dest, obj): - self.dest = dest - self.obj = obj - - -class VoidNode(InstructionNode): - pass - - -class ErrorNode(InstructionNode): - def __init__(self, data_node): - self.data_node = data_node - - def __repr__(self): - return f"ERROR {self.data_node}" diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py index 4edf9eddb..eb9c67e1d 100644 --- a/src/compiler/cmp/cil_ast.py +++ b/src/compiler/cmp/cil_ast.py @@ -22,6 +22,9 @@ def __init__(self, vname, value): self.name = vname self.value = value + def __repr__(self): + return f"{self.value}" + class FunctionNode(Node): def __init__(self, fname, params, localvars, instructions): @@ -115,6 +118,9 @@ def __init__(self, obj, attr, value, computed_type): self.value = value self.computed_type = computed_type + def __repr__(self): + return f"SETATTR {self.obj} {self.attr} {self.value}" + class GetIndexNode(InstructionNode): pass @@ -129,6 +135,9 @@ def __init__(self, itype, dest): self.type = itype self.dest = dest + def __repr__(self): + return f"{self.dest} = ALLOCATE {self.type}" + class ArrayNode(InstructionNode): pass @@ -246,6 +255,9 @@ def __init__(self, dest, source): self.dest = dest self.source = source + def __repr__(self): + return f"{self.dest} = LENGTH {self.source}" + class ConcatNode(InstructionNode): def __init__(self, dest, prefix, suffix, length): diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index 1a61dcde7..d3c19dbd7 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -83,7 +83,7 @@ def make_callee_final_instructions(self, function_node: mips.FunctionNode): return list(flatten([set_sp, pop_FP, pop_RA, final])) - def register_function(self, name, function: FunctionNode): + def register_function(self, name, function: mips.FunctionNode): self.text[name] = function self.current_function = function self.function_labels = {} @@ -105,9 +105,6 @@ def get_var_location(self, name): return self.get_local_var_index(name) def get_type_size(self, type_name): - print("---------------") - print(type_name, self.types[type_name].attributes) - print("---------------") return (len(self.types[type_name].attributes) + 1) * 4 @@ -120,7 +117,7 @@ def visit(self, node): def visit(self, node: ProgramNode) -> mips.ProgramNode: self.function_collector.collect(node) - self.data["default_str"] = mips.StringConst("default_str", "") + self.data["null_str"] = mips.StringConst("null_str", "") for dd in node.dotdata: self.visit(dd) @@ -177,11 +174,6 @@ def visit(self, node: FunctionNode): self.current_function = function_node body = [self.visit(instruction) for instruction in node.instructions] - print( - f"*************Instructions at {self.function_collector.functions[node.name], node.name}*********************" - ) - [print(i) for i in node.instructions] - print("***********************************") # Conventions of Final calle instrucctions final_callee = self.make_callee_final_instructions(function_node) @@ -306,7 +298,7 @@ def visit(self, node): ) instructions.append(mips.DivideNode(mips.T0, mips.T1)) - instructions.append(mips.MoveFromLowNode(mips.MoveFromLowNode(mips.T2))) + instructions.append(mips.MoveFromLowNode(mips.T2)) instructions.append( mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) ) @@ -487,6 +479,37 @@ def visit(self, node: AllocateNode): instructions.append( mips.StoreWordNode(mips.A0, mips.RegisterRelativeLocation(mips.V0, 0)) ) + if self.types["Int"].pos == tp: + instructions.append(mips.LoadInmediateNode(mips.A0, 0)) + instructions.append( + mips.StoreWordNode(mips.A0, mips.RegisterRelativeLocation(mips.V0, 4)) + ) + if self.types["String"].pos == tp: + instructions.append(mips.LoadAddressNode(mips.A0, "null_str")) + instructions.append( + mips.StoreWordNode(mips.A0, mips.RegisterRelativeLocation(mips.V0, 4)) + ) + + instructions.append(mips.MoveNode(mips.A1, mips.V0)) + + int_type_index = self.types["Int"].pos + instructions.append(mips.LoadInmediateNode(mips.A0, 8)) + instructions.append(mips.LoadInmediateNode(mips.V0, 9)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.LoadInmediateNode(mips.A0, int_type_index)) + instructions.append( + mips.StoreWordNode(mips.A0, mips.RegisterRelativeLocation(mips.V0, 0)) + ) + instructions.append(mips.LoadInmediateNode(mips.A0, 0)) + instructions.append( + mips.StoreWordNode(mips.A0, mips.RegisterRelativeLocation(mips.V0, 4)) + ) + instructions.append( + mips.StoreWordNode(mips.V0, mips.RegisterRelativeLocation(mips.A1, 8)) + ) + + instructions.append(mips.MoveNode(mips.V0, mips.A1)) + instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) @@ -513,16 +536,16 @@ def visit(self, node): instructions.append(mips.LoadAddressNode(mips.A0, mips.TYPE_LIST)) - tp_number = self.types[node.name].index + tp_number = self.types[node.name].pos instructions.append( - mips.AddInmediateUnsignedNode(mips.a0, mips.a0, tp_number * 4) + mips.AddInmediateUnsignedNode(mips.A0, mips.A0, tp_number * 4) ) instructions.append( - mips.LoadWordNode(mips.a0, mips.RegisterRelativeLocation(mips.a0, 0)) + mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) ) instructions.append( - mips.StoreWordNode(mips.a0, self.get_var_location(node.dest)) + mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) ) return instructions @@ -536,6 +559,7 @@ def visit(self, node: StaticCallNode): instructions = [] function_to_call = self.function_collector.functions[node.function] instructions.append(mips.JumpAndLinkNode(function_to_call)) + instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) @@ -814,6 +838,8 @@ def visit(self, node: SetAttribNode): if type(node.value) == int: instructions.append(mips.LoadInmediateNode(mips.A1, node.value)) + elif isinstance(node.value, VoidNode): + instructions.append(mips.LoadInmediateNode(mips.A1, 0)) else: instructions.append( mips.LoadWordNode(mips.A1, self.get_var_location(node.value)) diff --git a/src/compiler/visitors/cool2cil.py b/src/compiler/visitors/cool2cil.py index 484e6fd95..c559be68f 100644 --- a/src/compiler/visitors/cool2cil.py +++ b/src/compiler/visitors/cool2cil.py @@ -507,9 +507,10 @@ def visit(self, node: ClassDeclarationNode, scope: Scope): self.register_param(self.vself) parent_type = self.context.get_type(node.id).parent if parent_type.name != "Object" and parent_type.name != "IO": + vtemp2 = self.define_internal_local() self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction( - cil.StaticCallNode(self.init_name(parent_type.name, attr=True), vtemp) + cil.StaticCallNode(self.init_name(parent_type.name, attr=True), vtemp2) ) for feature, child_scope in zip(node.features, scope.children): diff --git a/src/compiler/visitors/cool_to_cil.py b/src/compiler/visitors/cool_to_cil.py deleted file mode 100644 index 4480ab2e7..000000000 --- a/src/compiler/visitors/cool_to_cil.py +++ /dev/null @@ -1,1183 +0,0 @@ -from ..cmp.cil import cil -from .utils import get_token -import compiler.visitors.visitor as visitor -from ...cmp import cool_ast as cool, 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 - self.vself = VariableInfo("self", None) - self.value_types = ["String", "Int", "Bool"] - - @property - def params(self): - return self.current_function.params - - @property - def localvars(self): - return self.current_function.localvars - - @property - def ids(self): - return self.current_function.ids - - @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, id=False): - new_vinfo = VariableInfo("", None) - if ( - len(self.current_function.name) >= 8 - and self.current_function.name[:8] == "function" - ): - new_vinfo.name = f"local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}" - else: - 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 new_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 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, [], [], []) - 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) - - def register_built_in(self): - # Object - type_node = self.register_type("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)) - - 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 == "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)) - 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?? - - 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.TypeNameNode(result, self.vself.name)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - 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") - ) - self.register_param(self.vself) - result = self.define_internal_local() - 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 += [("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.init_name("IO")) - instance = self.define_internal_local() - 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") - ) - self.register_param(self.vself) - self.register_param(VariableInfo("x", None)) - vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, "x", "value", "String")) - 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") - ) - self.register_param(self.vself) - self.register_param(VariableInfo("x", None)) - vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, "x", "value", "Int")) - 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") - ) - self.register_param(self.vself) - result = self.define_internal_local() - self.register_instruction(cil.ReadStrNode(result)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - 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") - ) - self.register_param(self.vself) - result = self.define_internal_local() - self.register_instruction(cil.ReadIntNode(result)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - 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 ["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.init_name("String")) - self.register_param(VariableInfo("val", None)) - 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.init_name("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.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") - ) - self.register_param(self.vself) - 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, length_1)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - 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") - ) - self.register_param(self.vself) - self.register_param(VariableInfo("i", None)) - self.register_param(VariableInfo("l", None)) - result = self.define_internal_local() - 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() - 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 - 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") - 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.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 ["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.init_name("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", "Int")) - 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.init_name("Int"))] - - # Bool - type_node = self.register_type("Bool") - type_node.attributes = ["value"] - - 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)) - 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.init_name("Bool"))] - - 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 = self.register_data(msg) - self.register_instruction(cil.ErrorNode(data_node)) - - self.register_instruction(continue_node) - - -class COOLToCILVisitor(BaseCOOLToCILVisitor): - def __init__(self, context): - super().__init__(context) - - def buildHierarchy(self, t: str): - if t == "Object": - return None - 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): - pass - - @visitor.when(cool.ProgramNode) - def visit(self, node, scope): - ###################################################### - # node.declarations -> [ ClassDeclarationNode ... ] - ###################################################### - - 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.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)) - # Error message raised by Object:abort() - self.register_data("Abort called from class ") - self.register_built_in() - 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) - - # init - 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)) - - func = self.current_function - vtemp = self.define_internal_local() - - # init_attr - 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.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.init_name(node.id, attr=True), vtemp) - ) - - self.register_instruction(cil.ReturnNode(instance)) - self.current_function = None - - self.current_type = None - - @visitor.when(cool.AttrDeclarationNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### - if node.expr: - self.visit(node.expr, scope) - 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(self.vself.name, node.id, vtemp, self.current_type) - ) - - @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 - - self.current_function = self.register_function( - self.to_function_name(self.current_method.name, type_name) - ) - - # (Handle PARAMS) - self.register_param(self.vself) - for param_name, _ in node.params: - 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: - self.register_instruction(cil.ReturnNode("")) - elif self.current_function.name == "entry": - self.register_instruction(cil.ReturnNode(0)) - else: - self.register_instruction(cil.ReturnNode(scope.ret_expr)) - - 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)) - vcondition = self.define_internal_local() - - then_label_node = self.register_label("then_label") - else_label_node = self.register_label("else_label") - continue_label_node = self.register_label("continue_label") - - # If condition GOTO then_label - self.visit(node.condition, scope) - 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 - 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(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(continue_label_node) - scope.ret_expr = vret - - @visitor.when(cool.WhileLoopNode) - def visit(self, node, scope): - ################################### - # node.condition -> ExpressionNode - # 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") - # Label while - self.register_instruction(while_label_node) - # If condition GOTO loop - self.visit(node.condition, scope) - 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)) - # Label loop - self.register_instruction(loop_label_node) - self.visit(node.body, scope) - # GOTO while - self.register_instruction(cil.GotoNode(while_label_node.label)) - # Label pool - self.register_instruction(pool_label_node) - - # The result of a while loop is void - scope.ret_expr = cil.VoidNode() - - @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 - ############################################ - vret = self.register_local(VariableInfo("let_in_value", None)) - - 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 - - @visitor.when(cool.CaseOfNode) - 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)) - self.visit(node.expr, scope) - self.register_instruction(cil.AssignNode(vexpr, scope.ret_expr)) - self.register_instruction(cil.TypeNameNode(vtype, scope.ret_expr)) - - # 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)) - - 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 = [] - old = {} - - # 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: - self.register_instruction(cil.GotoNode(labels[-1].label)) - break - h.add(b.type) - for s in old: - h -= s - 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.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(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)) - self.register_instruction(cil.GotoNode(end_label.label)) - - scope.ret_expr = vret - self.register_instruction(end_label) - - @visitor.when(cool.CaseExpressionNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### - self.visit(node.expr, scope) - - @visitor.when(cool.LetAttributeNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### - 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)) - elif node.type in self.value_types: - self.register_instruction(cil.AllocateNode(node.type, vname)) - - @visitor.when(cool.AssignNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.expr -> ExpressionNode - ############################### - - self.visit(node.expr, scope) - - try: - self.current_type.get_attribute(node.id) - 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] - 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) - 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.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.init_name("Bool"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.LessEqualNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - 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", "Bool")) - self.register_instruction( - cil.GetAttribNode(right_value, right, "value", "Bool") - ) - self.register_instruction(cil.LessEqualNode(vname, left_value, right_value)) - - self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.LessNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - 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", "Bool")) - self.register_instruction( - cil.GetAttribNode(right_value, right, "value", "Bool") - ) - self.register_instruction(cil.LessNode(vname, left_value, right_value)) - - self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.EqualNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - 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 - 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_bool, "Bool")) - 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_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)) - - 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.EqualStrNode(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) - self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.PlusNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - vname = self.define_internal_local() - 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", "Int") - ) - self.visit(node.right, scope) - 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)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.MinusNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - vname = self.define_internal_local() - 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", "Int") - ) - self.visit(node.right, scope) - 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)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.StarNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - vname = self.define_internal_local() - 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", "Int") - ) - self.visit(node.right, scope) - 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)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.DivNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### - vname = self.define_internal_local() - 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", "Int") - ) - self.visit(node.right, scope) - self.register_instruction( - cil.GetAttribNode(vright, scope.ret_expr, "value", "Int") - ) - - # Check division by 0 - equal_result = self.define_internal_local() - self.register_instruction(cil.EqualNode(equal_result, vright, 0)) - 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() - self.register_instruction(cil.ArgNode(vname)) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.IsVoidNode) - def visit(self, node, scope): - ############################### - # node.expr -> ExpressionNode - ############################### - 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)) - self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), result)) - scope.ret_expr = result - - @visitor.when(cool.ComplementNode) - 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.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.init_name("Int"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.FunctionCallNode) - def visit(self, node, scope): - ###################################### - # node.obj -> ExpressionNode - # node.id -> str - # node.args -> [ ExpressionNode ... ] - # node.type -> str - ##################################### - - args = [] - for arg in node.args: - 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), id=True - ) - - 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)) - - 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)) - for arg in args: - self.register_instruction(arg) - - if node.type: - # Call of type @.id(,...,) - 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), id=True - ) - self.register_instruction(cil.TypeOfNode(vobj, type_of_node)) - 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 - - @visitor.when(cool.MemberCallNode) - 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] - - args = [] - for arg in node.args: - 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), id=True - ) - - self.register_instruction(cil.ArgNode(self.vself.name)) - for arg in args: - self.register_instruction(arg) - - 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) - 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.name, 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.init_name(node.type), instance) - ) - scope.ret_expr = instance - - @visitor.when(cool.IntegerNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(int(node.lex))) - self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) - scope.ret_expr = instance - - @visitor.when(cool.IdNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - try: - self.current_type.get_attribute(node.lex) - 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: - 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 == n: - scope.ret_expr = n - break - else: - scope.ret_expr = self.ids[node.lex] - - @visitor.when(cool.StringNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - try: - data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] - except IndexError: - data_node = self.register_data(node.lex) - vmsg = self.register_local(VariableInfo("msg", None)) - 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.init_name("String"), instance) - ) - scope.ret_expr = instance - - @visitor.when(cool.BoolNode) - def visit(self, node, scope): - ############################### - # node.lex -> str - ############################### - if node.lex == "true": - scope.ret_expr = 1 - else: - scope.ret_expr = 0 - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(scope.ret_expr)) - self.register_instruction(cil.StaticCallNode(self.init_name("Bool"), instance)) - scope.ret_expr = instance diff --git a/src/compiler/visitors/mips_printer.py b/src/compiler/visitors/mips_printer.py index 54455d71f..53f594e54 100644 --- a/src/compiler/visitors/mips_printer.py +++ b/src/compiler/visitors/mips_printer.py @@ -42,22 +42,8 @@ def visit(self, node): @visitor.when(TypeNode) def visit(self, node: TypeNode): - print(node.data_label, ":") - print(node.attributes) methods = ", ".join([f"{node.methods[m]}" for m in node.methods]) dispatch_table = f"{node.type_label}_dispatch:\n\t .word {methods}" - # proto_begin = f"{node.type_label}_proto:\n\t.word\t{node.pos}, {len(node.attributes)*4}, {node.type_label}_dispatch" - # proto_attr = ", ".join( - # [f'{node.defaults.get(attr,"0")}' for attr in node.attributes] - # ) - # proto_end = f"{-1}" - # proto = ( - # f"{proto_begin}, {proto_attr}, {proto_end}" - # if proto_attr != "" - # else f"{proto_begin}, {proto_end}" - # ) - - # return f"{dispatch_table}\n\n{proto}" return f"{dispatch_table}" @visitor.when(SyscallNode) @@ -75,7 +61,6 @@ def visit(self, node): @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}" diff --git a/src/hello_world.cl b/src/hello_world.cl deleted file mode 100644 index 0c818f908..000000000 --- a/src/hello_world.cl +++ /dev/null @@ -1,5 +0,0 @@ -class Main inherits IO { - main(): IO { - out_string("Hello, World.\n") - }; -}; diff --git a/src/main.py b/src/main.py index 131db2937..d38bea3ca 100644 --- a/src/main.py +++ b/src/main.py @@ -15,6 +15,7 @@ from compiler.visitors.cil_formatter import PrintCILVisitor from compiler.visitors.cil2mips import CILToMIPSVisitor from compiler.visitors.mips_printer import MIPSPrintVisitor +import os def main(args): @@ -89,25 +90,22 @@ def main(args): cil_visitor = COOLToCILVisitor(context) cil_ast = cil_visitor.visit(ast, scope) - print("*****************************CIL CODE **************************") - cil_formatter = PrintCILVisitor() - print(cil_formatter.visit(cil_ast)) - print("*****************************CIL CODE **************************") - cil_to_mips = CILToMIPSVisitor() mips_ast = cil_to_mips.visit(cil_ast) printer = MIPSPrintVisitor() mips_code = printer.visit(mips_ast) - # print(mips_code) out_file = args.file.split(".") out_file[-1] = "mips" out_file = ".".join(out_file) out_file = f"{args.file[:-3]}.mips" + lib_path = os.path.abspath( + os.path.join(__file__, "../compiler/visitors/mips_lib.asm") + ) with open(out_file, "w") as f: f.write(mips_code) - with open("src/compiler/visitors/mips_lib.asm") as f2: + with open(lib_path) as f2: f.write("".join(f2.readlines())) exit(0) From a0504b20325ec478cbd71a635edfd04becca05cf Mon Sep 17 00:00:00 2001 From: dgd Date: Tue, 1 Mar 2022 16:08:49 -0500 Subject: [PATCH 132/138] Deleting commented code --- src/compiler/visitors/cil2mips.py | 40 ------------------------------- 1 file changed, 40 deletions(-) diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips.py index d3c19dbd7..550f824d0 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips.py @@ -420,46 +420,6 @@ def visit(self, node): return instructions - # @visitor.when(AllocateNode) - # def visit(self, node: AllocateNode): - # instructions = [] - - # tp = 0 - # if node.type.isnumeric(): - # tp = node.type - # else: - # tp = self.types[node.type].pos - - # # reg1 = t0 reg2 = t1 - # instructions.extend(mips.push_to_stack(mips.T0)) - # instructions.extend(mips.push_to_stack(mips.T1)) - # instructions.append(mips.LoadInmediateNode(mips.T0, tp)) - - # instructions.append(mips.ShiftLeftLogicalNode(mips.T0, mips.T0, 2)) - # instructions.append(mips.LoadAddressNode(mips.T1, mips.VIRTUAL_TABLE)) - # instructions.append(mips.AddUnsignedNode(mips.T1, mips.T1, mips.T0)) - # instructions.append( - # mips.LoadWordNode(mips.T1, mips.RegisterRelativeLocation(mips.T1, 0)) - # ) - # instructions.append( - # mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T1, 4)) - # ) - # instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) - # instructions.append(mips.JumpAndLinkNode("malloc")) - # instructions.append(mips.MoveNode(mips.A1, mips.A0)) - # instructions.append(mips.MoveNode(mips.A0, mips.T1)) - # instructions.append(mips.MoveNode(mips.A1, mips.V0)) - # instructions.append(mips.JumpAndLinkNode("copy")) - - # instructions.extend(mips.pop_from_stack(mips.T1)) - # instructions.extend(mips.pop_from_stack(mips.T0)) - - # instructions.append( - # mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - # ) - - # return instructions - @visitor.when(AllocateNode) def visit(self, node: AllocateNode): instructions = [] From b5ba5ab27e6132318e923e97e6c3d3a16dbee2f8 Mon Sep 17 00:00:00 2001 From: dgd Date: Tue, 1 Mar 2022 17:08:34 -0500 Subject: [PATCH 133/138] mips section in report --- doc/report.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/doc/report.md b/doc/report.md index ee9f95d66..97cb63251 100644 --- a/doc/report.md +++ b/doc/report.md @@ -136,6 +136,56 @@ Dentro de los errores chequeados en el ast de CIL se encuentra la comprobación En el caso del _case_ se chequea que la expresión principal no sea de tipo _void_ y además, que se conforme a alguna rama en la ejecución de este. El algoritmo empleado para reconocer por cuál de las ramas continuará la ejecución del código comienza por: tomar el tipo de todas las ramas del _case_, llámese a este conjunto $A$; por cada elemento del conjunto $A$ se toma la cantidad de tipos dentro del propio conjunto que se conforman a $a_i, i \in [1, |A|]$ ,de modo que se obtienen los pares $$. Se define $|\{a_j \leq a_i, \forall j, j\in[1, |A|]\}|$ como $a_{i_c}$. Tomando los elementos $a_i$ por el que menor $a_{i_c}$ tenga, se estará tomando los nodos más abajos en el árbol de tipos dentro de cada posible rama de este. Si se ordenan las ramas del _case_ por el que menor $a_{i_c}$ se obtendrá una lista. Luego se recorre esta generando por cada elemento el subconjunto $B_i$ donde $b_{i_i} \in B_i$ si $b_{i_i} <= a_i$. Se chequea si el tipo de la expresión principal del _case_ aparece en este subconjunto. En el caso de que aparezca, el case se resuelve yendo por la rama que posee el tipo $a_i$. +#### Traducción de CIL a MIPS + +Para la generación de código MIPS se definió un _visitor_ sobre el _ast_ de CIL generado en una etapa anterior. Este _visitor_ produce un nuevo _ast_ que representan las secciones _.DATA_, _.TEXT_ y las instrucciones en el código MIPS. Otro _visitor_ definido esta vez sobre los nodos del _ast_ del código MIPS se encarga de producir el código de MIPS que será ejecutado por el emulador SPIM. + +**Representación de objetos en memoria** + +El principal desafío en esta etapa es decidir como representar las instancias de tipos en memoria. Los objetos en memoria se representan de la siguiente manera: + +| Dirección x | Dirección x + 4 | Dirección x + 8 | ... | Dirección x + a * 4 | +| ----------- | --------------- | --------------- | ---- | ------------------- | +| Tipo | Atributo 0 | Atributo 1 | ... | Atributo a-1 | + +Por lo que un objeto es una zona continua de memoria de tamaño $1 + 4 * a$, donde $a$ es la cantidad de atributos que posee el objeto. El tipo y cada atributo son de tamaño $1$ _palabra_. + +El campo _Tipo_ es un número entre $0$ y $n-1$, siendo $n$ la cantidad total de tipos definidos en el programa de COOL a compilar. Un atributo puede guardar un valor específico o dicho valor puede ser interpretado como la dirección en memoria de otro objeto. + +Para saber la cantidad de tipos y asignarles a cada uno un valor entre $0$ y $n$, en el _visitor_ sobre el _ast_ de CIL primero se recorren todos los tipos definidos por el código CIL, asignandoles valores disitntos de manera ordenada según se van descubriendo. Además, por cada tipo se guardan también los nombre de sus parámetros y métodos en el orden en que se definieron en el tipo. + +Para obtener o modificar un atributo específico de una instancia conociendo el nombre del atributo, se busca su índice en los atributos almacenados para el tipo en cuestión. Si el índice es $i$, entonces su valor estará en la dirección de memoria $(x+4) + (i * 4)$. + +**Inicialización** + +Cuando se crea una nueva instancia mediante la instrucción de CIL _ALLOCATE_ se conoce el tipo del objeto a crear. Esta información se aprovecha para inicializar con valores por defecto la instancia de acuerdo a su tipo. Los tipos primitivos de COOL se inicializan de forma específica. Para los demás tipos, el código CIL de la etapa anterior genera para cada tipo una función _init_ que se encarga de esta tarea, la cual es llamada en el código CIL y traducida a MIPS después. + +**LLamado de función dinámico** + +Para cada tipo, se guardan sus métodos en una lista llamada _dispatch_. Una lista _dispatch_ de $m$ métodos tiene la siguiente estructura + +| Dirección x | Dirección x + 4 | Dirección x + 8 | ... | Dirección x + (m-1) * 4 | +| ----------- | --------------- | ---------------- | ---- | ----------------------- | +| Método 0 | Método 1 | Método 2 | ... | Método m-1 | + +Se tendrán $n$ listas, una por cada tipo. Cada celda es de una palabra y contiene la dirección a la primera instrucción del método correspondiente, o lo que es lo mismo, la dirección de la etiqueta generada para el método. + +Los métodos en la lista se encuentran en el mismo orden en que fueron definidos en el tipo. + +Estando una lista _dispatch_ específica, se decide la ubicación del método buscado por un proceso análogo a los atributos en las instancias de los objetos explicado anteriormente. Si el índice del método dentro del tipo es $i$, entonces la dirección del método buscando estará en la dirección $x + 4 * i$. + +Ahora solo faltaría saber por cual de las listas _dispatch_ decidirse para buscar el método dado un tipo. + +Para eso se tiene otra lista llamada _virtual_. Su función es almacenar por cada tipo, la dirección a su lista _dispatch_ . La lista _virtual_ tiene la siguiente forma: + +| Dirección x | Dirección x + 4 | Dirección x + 8 | ... | Dirección x + (n-1) * 4 | +| ------------ | --------------- | ---------------- | ---- | ----------------------- | +| _dispatch_ 0 | _dispatch_ 1 | _dispatch_ 2 | ... | _dispatch_ n-1 | + +Recordar que $n$ es la cantidad de tipos. + +Dado una instancia en memoria, se puede ver su tipo en la primera de sus direcciones continuas. Luego se hace otro proceso análogo a como se buscaron los atributos y métodos. Se obtiene el índice del tipo de la instancia y se decide por cual _dispatch_ buscar el método que se quiere invocar. Si el índice del tipo es $i$, se buscará en la lista _dispatch_ en la posición $x + 4*i$. + ## Licencia Este proyecto se encuentra bajo la Licencia (MIT License) - ver el archivo [LICENSE.md](LICENSE.md) para más detalles. From 0a40a17d0ea4c5d8fe5de7e922c6b6cdc6441386 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Wed, 2 Mar 2022 14:12:40 -0500 Subject: [PATCH 134/138] refactoring code --- src/compiler/visitors/cil2mips/__init__.py | 0 .../visitors/{ => cil2mips}/cil2mips.py | 406 +++++++----------- .../visitors/{ => cil2mips}/mips_lib.asm | 0 .../visitors/{ => cil2mips}/mips_printer.py | 0 src/compiler/visitors/cil2mips/utils.py | 6 + src/main.py | 48 +-- 6 files changed, 181 insertions(+), 279 deletions(-) create mode 100644 src/compiler/visitors/cil2mips/__init__.py rename src/compiler/visitors/{ => cil2mips}/cil2mips.py (74%) rename src/compiler/visitors/{ => cil2mips}/mips_lib.asm (100%) rename src/compiler/visitors/{ => cil2mips}/mips_printer.py (100%) create mode 100644 src/compiler/visitors/cil2mips/utils.py diff --git a/src/compiler/visitors/cil2mips/__init__.py b/src/compiler/visitors/cil2mips/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/compiler/visitors/cil2mips.py b/src/compiler/visitors/cil2mips/cil2mips.py similarity index 74% rename from src/compiler/visitors/cil2mips.py rename to src/compiler/visitors/cil2mips/cil2mips.py index 550f824d0..ccf642204 100644 --- a/src/compiler/visitors/cil2mips.py +++ b/src/compiler/visitors/cil2mips/cil2mips.py @@ -1,14 +1,49 @@ -from compiler.cmp import mips_ast as mips -from compiler.cmp.cil_ast import * -from compiler.visitors import visitor - - -def flatten(iterable): - for item in iterable: - try: - yield from flatten(item) - except TypeError: - yield item +from ...cmp import mips_ast as mips +from ...cmp.cil_ast import ( + AllocateNode, + ArgNode, + ArithmeticNode, + AssignNode, + ComplementNode, + ConcatNode, + CopyNode, + DataNode, + DivNode, + DynamicCallNode, + EqualNode, + EqualStrNode, + ErrorNode, + ExitNode, + FunctionNode, + GetAttribNode, + GotoIfNode, + GotoNode, + LabelNode, + LengthNode, + LeqNode, + LessNode, + LoadNode, + MinusNode, + NameNode, + PlusNode, + PrintIntNode, + PrintStrNode, + ProgramNode, + ReadIntNode, + ReadStrNode, + ReturnNode, + SetAttribNode, + StarNode, + StaticCallNode, + SubstringNode, + TypeNameNode, + TypeNode, + TypeOfNode, + VoidNode, +) +from ...visitors import visitor +from .utils import flatten +from typing import Optional class FunctionCollectorVisitor: @@ -25,12 +60,12 @@ def collect(self, node): pass @visitor.when(ProgramNode) - def collect(self, node): + def collect(self, node: ProgramNode): for func in node.dotcode: self.collect(func) @visitor.when(FunctionNode) - def collect(self, node): + def collect(self, node: FunctionNode): if node.name == "entry": self.functions[node.name] = "main" else: @@ -42,7 +77,7 @@ def __init__(self): self.data = {} self.text = {} self.types = {} - self.current_function: mips.FunctionNode = None + self.current_function: Optional[mips.FunctionNode] = None self.pushed_args = 0 self.label_count = 0 self.type_count = 0 @@ -167,14 +202,14 @@ def visit(self, node: FunctionNode): new_label = self.make_function_label() self.function_labels[inst.label] = new_label - # Conventions of Init intructions of the calle function + # Conventions of Init intructions of the callee function init_callee = self.make_callee_init_instructions(function_node) # Body instructions self.current_function = function_node body = [self.visit(instruction) for instruction in node.instructions] - # Conventions of Final calle instrucctions + # Conventions of Final callee instructions final_callee = self.make_callee_final_instructions(function_node) total_instructions = list(flatten(init_callee + body + final_callee)) @@ -185,7 +220,7 @@ def visit(self, node: FunctionNode): def visit(self, node: AssignNode): instructions = [] - if type(node.source) == VoidNode: + if isinstance(node.source, VoidNode): instructions.append( mips.StoreWordNode(mips.ZERO, self.get_var_location(node.dest)) ) @@ -205,172 +240,52 @@ def visit(self, node: AssignNode): return instructions @visitor.when(PlusNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.AddNode(mips.T2, mips.T0, mips.T1)) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions + def visit(self, node: PlusNode): + instructions = [mips.AddNode(mips.T2, mips.T0, mips.T1)] + return self.numeric_operation(node, instructions) @visitor.when(MinusNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.SubNode(mips.T2, mips.T0, mips.T1)) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions + def visit(self, node: MinusNode): + instructions = [mips.SubNode(mips.T2, mips.T0, mips.T1)] + return self.numeric_operation(node, instructions) @visitor.when(StarNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.MultiplyNode(mips.T2, mips.T0, mips.T1)) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions + def visit(self, node: StarNode): + instructions = [mips.MultiplyNode(mips.T2, mips.T0, mips.T1)] + return self.numeric_operation(node, instructions) @visitor.when(DivNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.T0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.T0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.T1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.T1, self.get_var_location(node.right)) - ) - - instructions.append(mips.DivideNode(mips.T0, mips.T1)) - instructions.append(mips.MoveFromLowNode(mips.T2)) - instructions.append( - mips.StoreWordNode(mips.T2, self.get_var_location(node.dest)) - ) - - return instructions + def visit(self, node: DivNode): + instructions = [ + mips.DivideNode(mips.T0, mips.T1), + mips.MoveFromLowNode(mips.T2), + ] + return self.numeric_operation(node, instructions) @visitor.when(LeqNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - ) - - instructions.append(mips.JumpAndLinkNode("less_equal")) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions + def visit(self, node: LeqNode): + return self.boolean_operation(node, "less_equal") @visitor.when(LessNode) - def visit(self, node): - instructions = [] - - if type(node.left) == int: - instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - else: - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) - ) - - if type(node.right) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.rigth)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.right)) - ) - - instructions.append(mips.JumpAndLinkNode("less")) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) - ) - - return instructions + def visit(self, node: LessNode): + return self.boolean_operation(node, "less") @visitor.when(EqualNode) - def visit(self, node): + def visit(self, node: EqualNode): instructions = [] - if type(node.left) == int: + if isinstance(node.left, int): instructions.append(mips.LoadInmediateNode(mips.A0, node.left)) - elif type(node.left) == VoidNode: + elif isinstance(node.left, VoidNode): instructions.append(mips.LoadInmediateNode(mips.A0, 0)) else: instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.left)) ) - if type(node.right) == int: + if isinstance(node.right, int): instructions.append(mips.LoadInmediateNode(mips.A1, node.right)) - elif type(node.right) == VoidNode: + elif isinstance(node.right, VoidNode): instructions.append(mips.LoadInmediateNode(mips.A1, 0)) else: instructions.append( @@ -386,7 +301,7 @@ def visit(self, node): return instructions @visitor.when(EqualStrNode) - def visit(self, node): + def visit(self, node: EqualStrNode): instructions = [] instructions.append( @@ -402,10 +317,10 @@ def visit(self, node): return instructions @visitor.when(ComplementNode) - def visit(self, node): + def visit(self, node: ComplementNode): instructions = [] - if type(node.obj) == int: + if isinstance(node.obj, int): instructions.append(mips.LoadInmediateNode(mips.T0, node.obj)) else: instructions.append( @@ -491,7 +406,7 @@ def visit(self, node: TypeOfNode): return instructions @visitor.when(NameNode) - def visit(self, node): + def visit(self, node: NameNode): instructions = [] instructions.append(mips.LoadAddressNode(mips.A0, mips.TYPE_LIST)) @@ -568,7 +483,7 @@ def visit(self, node: DynamicCallNode): def visit(self, node: ArgNode): self.pushed_args += 1 instructions = [] - if type(node.name) == int: + if isinstance(node.name, int): instructions.append(mips.LoadInmediateNode(mips.A0, node.name)) instructions.extend(mips.push_to_stack(mips.A0)) else: @@ -580,7 +495,7 @@ def visit(self, node: ArgNode): return instructions @visitor.when(ReturnNode) - def visit(self, node): + def visit(self, node: ReturnNode): instructions = [] if node.value is None: instructions.append(mips.LoadInmediateNode(mips.V0, 0)) @@ -608,107 +523,66 @@ def visit(self, node: LoadNode): return instructions @visitor.when(LengthNode) - def visit(self, node): + def visit(self, node: LengthNode): instructions = [] - instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) ) - instructions.append(mips.JumpAndLinkNode("len")) - instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) - return instructions @visitor.when(ConcatNode) - def visit(self, node): + def visit(self, node: ConcatNode): instructions = [] - instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.prefix)) ) - instructions.append( mips.LoadWordNode(mips.A1, self.get_var_location(node.suffix)) ) - instructions.append( mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) ) - instructions.append(mips.JumpAndLinkNode("concat")) - instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) - return instructions @visitor.when(SubstringNode) - def visit(self, node): - instructions = [] - - instructions.append( + def visit(self, node: SubstringNode): + instructions = [ mips.LoadWordNode(mips.A0, self.get_var_location(node.str_value)) - ) - - if type(node.index) == int: - instructions.append(mips.LoadInmediateNode(mips.A1, node.index)) - else: - instructions.append( - mips.LoadWordNode(mips.A1, self.get_var_location(node.index)) + ] + instructions.extend( + self.jump_and_link_node_instructions( + node.index, node.length, node.dest, "substr", mips.A1, mips.A2, mips.V0 ) - - if type(node.length) == int: - instructions.append(mips.LoadInmediateNode(mips.A2, node.length)) - else: - instructions.append( - mips.LoadWordNode(mips.A2, self.get_var_location(node.length)) - ) - - instructions.append(mips.JumpAndLinkNode("substr")) - instructions.append( - mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) return instructions @visitor.when(ReadStrNode) - def visit(self, node): + def visit(self, node: ReadStrNode): instructions = [] instructions.append(mips.JumpAndLinkNode("read_str")) - instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) - return instructions @visitor.when(PrintIntNode) - def visit(self, node): - instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0, 1)) - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) - ) - instructions.append(mips.SyscallNode()) - - return instructions + def visit(self, node: PrintIntNode): + return self.print_instructions(node, 1) @visitor.when(PrintStrNode) - def visit(self, node): - instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0, 4)) - instructions.append( - mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) - ) - instructions.append(mips.SyscallNode()) - return instructions + def visit(self, node: PrintStrNode): + return self.print_instructions(node, 4) @visitor.when(ErrorNode) - def visit(self, node): + def visit(self, node: ErrorNode): instructions = [] mips_label = self.data[node.data_node.name].label @@ -724,45 +598,39 @@ def visit(self, node): @visitor.when(TypeNameNode) def visit(self, node: TypeNameNode): instructions = [] - instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.source)) ) instructions.append( mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) ) - instructions.append(mips.ShiftLeftLogicalNode(mips.A0, mips.A0, 2)) instructions.append(mips.LoadAddressNode(mips.A1, mips.TYPE_LIST)) instructions.append(mips.AddUnsignedNode(mips.A0, mips.A0, mips.A1)) instructions.append( mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.A0, 0)) ) - instructions.append( mips.StoreWordNode(mips.A0, self.get_var_location(node.dest)) ) - return instructions @visitor.when(ExitNode) - def visit(self, node): + def visit(self, node: ExitNode): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0, 10)) instructions.append(mips.SyscallNode()) - return instructions @visitor.when(GetAttribNode) def visit(self, node: GetAttribNode): - instructions = [] - dest = node.dest if type(node.dest) == str else node.dest.name - obj = node.obj if type(node.obj) == str else node.obj.name + dest = node.dest if isinstance(node.dest, str) else node.dest.name + obj = node.obj if isinstance(node.obj, str) else node.obj.name comp_type = ( node.computed_type - if type(node.computed_type) == str + if isinstance(node.computed_type, str) else node.computed_type.name ) @@ -770,33 +638,30 @@ def visit(self, node: GetAttribNode): tp = self.types[comp_type] offset = (tp.attributes.index(node.attr) + 1) * 4 + instructions.append( mips.LoadWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) ) - instructions.append(mips.StoreWordNode(mips.A1, self.get_var_location(dest))) - return instructions @visitor.when(SetAttribNode) def visit(self, node: SetAttribNode): - instructions = [] - obj = node.obj if type(node.obj) == str else node.obj.name + obj = node.obj if isinstance(node.obj, str) else node.obj.name comp_type = ( node.computed_type - if type(node.computed_type) == str + if isinstance(node.computed_type, str) else node.computed_type.name ) tp = self.types[comp_type] - # offset = 4 + tp.attributes.index(node.attr) * 4 offset = (tp.attributes.index(node.attr) + 1) * 4 instructions.append(mips.LoadWordNode(mips.A0, self.get_var_location(obj))) - if type(node.value) == int: + if isinstance(node.value, int): instructions.append(mips.LoadInmediateNode(mips.A1, node.value)) elif isinstance(node.value, VoidNode): instructions.append(mips.LoadInmediateNode(mips.A1, 0)) @@ -808,19 +673,16 @@ def visit(self, node: SetAttribNode): instructions.append( mips.StoreWordNode(mips.A1, mips.RegisterRelativeLocation(mips.A0, offset)) ) - return instructions @visitor.when(CopyNode) - def visit(self, node): + def visit(self, node: CopyNode): instructions = [] # reg1 T0 reg2 A3 instructions.extend(mips.push_to_stack(mips.T0)) - instructions.append( mips.LoadWordNode(mips.T0, self.get_var_location(node.source)) ) - instructions.append( mips.LoadWordNode(mips.A0, mips.RegisterRelativeLocation(mips.T0, 4)) ) @@ -830,16 +692,14 @@ def visit(self, node): instructions.append(mips.MoveNode(mips.A0, mips.T0)) instructions.append(mips.MoveNode(mips.A1, mips.V0)) instructions.append(mips.JumpAndLinkNode("copy")) - instructions.extend(mips.pop_from_stack(mips.T0)) - instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) return instructions @visitor.when(GotoIfNode) - def visit(self, node): + def visit(self, node: GotoIfNode): instructions = [] local_label = self.function_labels[node.label] @@ -847,24 +707,68 @@ def visit(self, node): instructions.append( mips.LoadWordNode(mips.A0, self.get_var_location(node.condition)) ) - instructions.append(mips.BranchOnNotEqualNode(mips.A0, mips.ZERO, local_label)) - return instructions @visitor.when(GotoNode) - def visit(self, node): + def visit(self, node: GotoNode): local_label = self.function_labels[node.label] return [mips.JumpNode(local_label)] @visitor.when(ReadIntNode) - def visit(self, node): + def visit(self, node: ReadIntNode): instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0, 5)) instructions.append(mips.SyscallNode()) instructions.append( mips.StoreWordNode(mips.V0, self.get_var_location(node.dest)) ) + return instructions + + def add_node_instruction(self, node, register: mips.Register): + if isinstance(node, int): + return mips.LoadInmediateNode(register, node) + else: + return mips.LoadWordNode(register, self.get_var_location(node)) + + def numeric_operation(self, node, instructions): + return self.generate_instructions( + node.left, node.right, node.dest, instructions, mips.T0, mips.T1, mips.T2 + ) + def boolean_operation(self, node, op: str): + return self.jump_and_link_node_instructions( + node.left, node.right, node.dest, op, mips.A0, mips.A1, mips.V0 + ) + + def jump_and_link_node_instructions(self, node0, node1, dest, op, reg0, reg1, reg2): + instructions = [mips.JumpAndLinkNode(op)] + return self.generate_instructions( + node0, node1, dest, instructions, reg0, reg1, reg2 + ) + + def generate_instructions( + self, + node0, + node1, + dest, + specific_instructions, + reg0: mips.Register, + reg1: mips.Register, + reg2: mips.Register, + ): + instructions = [] + instructions.append(self.add_node_instruction(node0, reg0)) + instructions.append(self.add_node_instruction(node1, reg1)) + instructions.extend(specific_instructions) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(dest))) + return instructions + + def print_instructions(self, node, n): + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0, n)) + instructions.append( + mips.LoadWordNode(mips.A0, self.get_var_location(node.value)) + ) + instructions.append(mips.SyscallNode()) return instructions diff --git a/src/compiler/visitors/mips_lib.asm b/src/compiler/visitors/cil2mips/mips_lib.asm similarity index 100% rename from src/compiler/visitors/mips_lib.asm rename to src/compiler/visitors/cil2mips/mips_lib.asm diff --git a/src/compiler/visitors/mips_printer.py b/src/compiler/visitors/cil2mips/mips_printer.py similarity index 100% rename from src/compiler/visitors/mips_printer.py rename to src/compiler/visitors/cil2mips/mips_printer.py diff --git a/src/compiler/visitors/cil2mips/utils.py b/src/compiler/visitors/cil2mips/utils.py new file mode 100644 index 000000000..6785766ad --- /dev/null +++ b/src/compiler/visitors/cil2mips/utils.py @@ -0,0 +1,6 @@ +def flatten(iterable): + for item in iterable: + try: + yield from flatten(item) + except TypeError: + yield item diff --git a/src/main.py b/src/main.py index 5d803c26f..1331ac819 100644 --- a/src/main.py +++ b/src/main.py @@ -1,20 +1,14 @@ from compiler.cmp.grammar import G from compiler.lexer.lex import CoolLexer from compiler.parser.parser import LR1Parser, evaluate_reverse_parse -from compiler.visitors.cool2cil.cil_formatter import PrintCILVisitor +from compiler.visitors.cil2mips.cil2mips import CILToMIPSVisitor +from compiler.visitors.cil2mips.mips_printer import MIPSPrintVisitor from compiler.visitors.cool2cil.cool2cil import COOLToCILVisitor -from compiler.visitors.semantics_check.formatter import FormatVisitor from compiler.visitors.semantics_check.type_builder import TypeBuilder from compiler.visitors.semantics_check.type_checker import TypeChecker from compiler.visitors.semantics_check.type_collector import TypeCollector from compiler.visitors.semantics_check.type_inferencer import TypeInferencer from sys import exit - -# from compiler.visitors.cool2cil import COOLToCILVisitor -from compiler.visitors.cool2cil import COOLToCILVisitor -from compiler.visitors.cil_formatter import PrintCILVisitor -from compiler.visitors.cil2mips import CILToMIPSVisitor -from compiler.visitors.mips_printer import MIPSPrintVisitor import os @@ -70,38 +64,36 @@ def main(args): if checker.errors: exit(1) - # # Inferencing Autotype - # inferencer = TypeInferencer(context, manager) - # inferencer.visit(ast, scope) - # for e in inferencer.errors: - # print(f"{pos} - {type(e).__name__}: {str(e)}") - # if inferencer.errors: - # exit(1) - - # # Last check without autotypes - # checker = TypeChecker(context, manager) - # checker.visit(ast) - # for (e, pos) in checker.errors: - # print(f"{pos} - {type(e).__name__}: {str(e)}") - # if checker.errors: - # exit(1) + # Inferencing Autotype + inferencer = TypeInferencer(context, manager) + inferencer.visit(ast, scope) + for e in inferencer.errors: + print(f"{pos} - {type(e).__name__}: {str(e)}") + if inferencer.errors: + exit(1) + + # Last check without autotypes + checker = TypeChecker(context, manager) + checker.visit(ast) + for (e, pos) in checker.errors: + print(f"{pos} - {type(e).__name__}: {str(e)}") + if checker.errors: + exit(1) # COOL to CIL cil_visitor = COOLToCILVisitor(context) cil_ast = cil_visitor.visit(ast, scope) + # CIL to MIPS cil_to_mips = CILToMIPSVisitor() mips_ast = cil_to_mips.visit(cil_ast) printer = MIPSPrintVisitor() mips_code = printer.visit(mips_ast) - out_file = args.file.split(".") - out_file[-1] = "mips" - out_file = ".".join(out_file) + # Output MIPS file out_file = f"{args.file[:-3]}.mips" - lib_path = os.path.abspath( - os.path.join(__file__, "../compiler/visitors/mips_lib.asm") + os.path.join(__file__, "../compiler/visitors/cil2mips/mips_lib.asm") ) with open(out_file, "w") as f: f.write(mips_code) From 1f73db167ede22417921dc52f864e2a6565b7b3e Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Wed, 2 Mar 2022 14:31:51 -0500 Subject: [PATCH 135/138] remove some unused code on cil section --- src/compiler/cmp/cil_ast.py | 66 +-------------- src/compiler/visitors/cool2cil/cool2cil.py | 98 ++++------------------ 2 files changed, 20 insertions(+), 144 deletions(-) diff --git a/src/compiler/cmp/cil_ast.py b/src/compiler/cmp/cil_ast.py index eb9c67e1d..9db2514cb 100644 --- a/src/compiler/cmp/cil_ast.py +++ b/src/compiler/cmp/cil_ast.py @@ -1,4 +1,3 @@ -# AST class Node: pass @@ -22,9 +21,6 @@ def __init__(self, vname, value): self.name = vname self.value = value - def __repr__(self): - return f"{self.value}" - class FunctionNode(Node): def __init__(self, fname, params, localvars, instructions): @@ -47,8 +43,7 @@ def __init__(self, name): class InstructionNode(Node): - def __init__(self): - self.leader = False + pass class AssignNode(InstructionNode): @@ -56,9 +51,6 @@ 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): @@ -92,11 +84,10 @@ class LessNode(ArithmeticNode): class EqualNode(ArithmeticNode): - def __repr__(self): - return f"{self.dest} = {self.left} == {self.right}" + pass -class EqualStrNode(ArithmeticNode): +class EqualStrNode(EqualNode): pass @@ -107,9 +98,6 @@ def __init__(self, dest, obj, attr, computed_type): 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): @@ -118,9 +106,6 @@ def __init__(self, obj, attr, value, computed_type): self.value = value self.computed_type = computed_type - def __repr__(self): - return f"SETATTR {self.obj} {self.attr} {self.value}" - class GetIndexNode(InstructionNode): pass @@ -135,9 +120,6 @@ def __init__(self, itype, dest): self.type = itype self.dest = dest - def __repr__(self): - return f"{self.dest} = ALLOCATE {self.type}" - class ArrayNode(InstructionNode): pass @@ -148,43 +130,28 @@ 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): 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): @@ -193,34 +160,22 @@ def __init__(self, xtype, method, dest, computed_type): 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): self.dest = dest self.msg = msg - def __repr__(self): - return f"{self.dest} LOAD {self.msg}" - class ExitNode(InstructionNode): pass @@ -231,18 +186,12 @@ 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): @@ -255,9 +204,6 @@ def __init__(self, dest, source): self.dest = dest self.source = source - def __repr__(self): - return f"{self.dest} = LENGTH {self.source}" - class ConcatNode(InstructionNode): def __init__(self, dest, prefix, suffix, length): @@ -289,9 +235,6 @@ 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): @@ -311,6 +254,3 @@ 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}" diff --git a/src/compiler/visitors/cool2cil/cool2cil.py b/src/compiler/visitors/cool2cil/cool2cil.py index 2525c1a03..9d2f5fd27 100644 --- a/src/compiler/visitors/cool2cil/cool2cil.py +++ b/src/compiler/visitors/cool2cil/cool2cil.py @@ -457,7 +457,6 @@ def visit(self, node: ClassDeclarationNode, scope: Scope): # Handle all the .TYPE section type_node = self.register_type(self.current_type.name) - # TODO Check change next loop to use all_attributes and all_methods functions type_node.attributes.extend( [attr.name for attr, _ in self.current_type.all_attributes()] ) @@ -467,26 +466,6 @@ def visit(self, node: ClassDeclarationNode, scope: Scope): for method, typex in self.current_type.all_methods() ] ) - # visited_func = [] - # current = self.current_type - # while current is not None: - # attributes = [attr.name for attr in current.attributes] - # methods = [ - # func.name for func in current.methods if func.name not in visited_func - # ] - # visited_func.extend(methods) - # type_node.attributes.extend(attributes[::-1]) - # type_node.methods.extend( - # [ - # (item, self.to_function_name(item, current.name)) - # for item in methods[::-1] - # ] - # ) - # current = current.parent - - # type_node.attributes.reverse() - # type_node.methods.reverse() - for feature, child_scope in zip(node.features, scope.children): if isinstance(feature, FuncDeclarationNode): self.visit(feature, child_scope) @@ -581,17 +560,12 @@ def visit(self, node: AssignNode, scope: Scope): ) except SemanticError: vname = None - # TODO - Splits by _ won't work param_names = [pn.name for pn in self.current_function.params] if node.id in param_names: vname = node.id else: vname = self.ids[node.id] - # return self.data - # 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, value)) return value @@ -793,8 +767,6 @@ def visit(self, node: CaseNode, scope: Scope): if not h: self.register_instruction(cil.GotoNode(labels[-1].label)) break - # TODO - Esto no hace falta - h.add(b.typex) for s in old: h -= s for t in h: @@ -826,19 +798,11 @@ def visit(self, node: CaseNode, scope: Scope): return value @visitor.when(CaseBranchNode) - def visit(self, node, scope): - ############################### - # node.id -> str - # node.type -> str - # node.expr -> ExpressionNode - ############################### + def visit(self, node: CaseBranchNode, scope: Scope): return self.visit(node.expression, scope) @visitor.when(NotNode) - def visit(self, node, scope): - ############################### - # node.expr -> ExpressionNode - ############################### + def visit(self, node: NotNode, scope: Scope): vname = self.define_internal_local() value = self.define_internal_local() instance = self.define_internal_local() @@ -852,11 +816,7 @@ def visit(self, node, scope): return instance @visitor.when(LeqNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### + def visit(self, node: LeqNode, scope: Scope): vname = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() @@ -875,11 +835,7 @@ def visit(self, node, scope): return instance @visitor.when(LessNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### + def visit(self, node: LessNode, scope: Scope): vname = self.define_internal_local() left_value = self.define_internal_local() right_value = self.define_internal_local() @@ -898,11 +854,7 @@ def visit(self, node, scope): return instance @visitor.when(EqualNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### + def visit(self, node: EqualNode, scope: Scope): vname = self.define_internal_local() type_left = self.define_internal_local() type_int = self.define_internal_local() @@ -958,11 +910,7 @@ def visit(self, node, scope): return instance @visitor.when(PlusNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### + def visit(self, node: PlusNode, scope: Scope): vname = self.define_internal_local() vleft = self.define_internal_local() vright = self.define_internal_local() @@ -977,11 +925,7 @@ def visit(self, node, scope): return instance @visitor.when(MinusNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### + def visit(self, node: MinusNode, scope: Scope): vname = self.define_internal_local() vleft = self.define_internal_local() vright = self.define_internal_local() @@ -996,11 +940,7 @@ def visit(self, node, scope): return instance @visitor.when(StarNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### + def visit(self, node: StarNode, scope: Scope): vname = self.define_internal_local() vleft = self.define_internal_local() vright = self.define_internal_local() @@ -1015,11 +955,7 @@ def visit(self, node, scope): return instance @visitor.when(DivNode) - def visit(self, node, scope): - ############################### - # node.left -> ExpressionNode - # node.right -> ExpressionNode - ############################### + def visit(self, node: DivNode, scope: Scope): vname = self.define_internal_local() vleft = self.define_internal_local() vright = self.define_internal_local() @@ -1043,7 +979,7 @@ def visit(self, node, scope): return instance @visitor.when(VoidNode) - def visit(self, node, scope): + def visit(self, node: VoidNode, scope: Scope): void = cil.VoidNode() value = self.define_internal_local() ret = self.visit(node.expr, scope) @@ -1055,7 +991,7 @@ def visit(self, node, scope): return result @visitor.when(NegNode) - def visit(self, node, scope): + def visit(self, node: NegNode, scope: Scope): vname = self.define_internal_local() value = self.define_internal_local() instance = self.define_internal_local() @@ -1067,7 +1003,7 @@ def visit(self, node, scope): return instance @visitor.when(InstantiateNode) - def visit(self, node, scope): + def visit(self, node: InstantiateNode, scope: Scope): instance = self.define_internal_local() if node.computed_type.name == SelfType().name: @@ -1088,7 +1024,7 @@ def visit(self, node, scope): return instance @visitor.when(VariableNode) - def visit(self, node, scope): + def visit(self, node: VariableNode, scope: Scope): try: self.current_type.get_attribute(node.lex) @@ -1107,14 +1043,14 @@ def visit(self, node, scope): return self.ids[node.lex] @visitor.when(ConstantNumNode) - def visit(self, node, scope): + def visit(self, node: ConstantNumNode, scope: Scope): instance = self.define_internal_local() self.register_instruction(cil.ArgNode(int(node.lex))) self.register_instruction(cil.StaticCallNode(self.init_name("Int"), instance)) return instance @visitor.when(ConstantStringNode) - def visit(self, node, scope): + def visit(self, node: ConstantStringNode, scope: Scope): try: data_node = [dn for dn in self.dotdata if dn.value == node.lex][0] except IndexError: @@ -1128,7 +1064,7 @@ def visit(self, node, scope): return ret @visitor.when(ConstantBoolNode) - def visit(self, node, scope): + def visit(self, node: ConstantBoolNode, scope: Scope): if node.lex == "true": v = 1 else: From 2384b04fa5e8f0dc0d741d6c0ad72d9328d4a4d0 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Wed, 2 Mar 2022 15:42:34 -0500 Subject: [PATCH 136/138] update report --- doc/report.md | 40 +- doc/report.pdf | Bin 0 -> 123049 bytes src/code.cl | 6 +- src/mips_lib.asm | 1152 ---------------------------------------------- src/tests.py | 95 ---- 5 files changed, 26 insertions(+), 1267 deletions(-) create mode 100644 doc/report.pdf delete mode 100644 src/mips_lib.asm delete mode 100644 src/tests.py diff --git a/doc/report.md b/doc/report.md index 3c7bbd0f9..526e60a6e 100644 --- a/doc/report.md +++ b/doc/report.md @@ -51,7 +51,7 @@ Las fases en que se divide el proceso de compilación se muestran a continuació 6. Traducción de COOL a CIL 7. Traducción de CIL a MIPS -#### Lexer +### Lexer Para el análisis léxico se utilizó el módulo `lex.py` del paquete PLY de Python, que permite separar el texto de entrada (código COOL) en una colección de _tokens_ dado un conjunto de reglas de expresiones regulares. @@ -68,13 +68,13 @@ Esto permitió tener en cuenta: el uso de caracteres inválidos en el primer cas Además se llevaron a cabo cálculos auxiliares para obtener el valor de la columna de cada token, puesto que el lexer solo cuenta con el número de fila y el index. -#### Parsing +### Parsing Se utilizó una modificación de la implementación previa del parser LR1 para llevar a cabo la fase de _parsing_; esta se realizó para poder almacenar el token, en lugar de solo su lexema; puesto que el token también guarda la posición _(fila, columna)_. La gramática utilizada es S-atributada. Podrá encontrar la implementación de la misma en [grammar.py](https://github.com/codersUP/cool-compiler-2021/blob/master/src/compiler/cmp/grammar.py) -#### Recolección de tipos +### Recolección de tipos Esta fase se realiza mediante la clase _Type Collector_ que sigue los siguientes pasos: @@ -84,7 +84,7 @@ Esta fase se realiza mediante la clase _Type Collector_ que sigue los siguientes - Chequeo de herencia cíclica. En caso de detectar algún ciclo en la jerarquía de tipos, se reporta el error, y a la clase por la cual hubo problema se le asigna Object como padre, para continuar el análisis. - Una vez chequeados los puntos anteriores, se reorganiza la lista de nodos de declaración de clases que está guardada en el nodo Program. La reorganización se realiza tal que para cada tipo A, si este hereda del tipo B (siendo B otra de las clases definidas en el programa) la posición de B en la lista es menor que la de A. De esta manera, cuando se visite un nodo de declaración de clase, todas las clases de las cuales él es descendiente, ya fueron visitadas previamente. -#### Construcción de tipos +### Construcción de tipos La construcción de tipos se desarrolla empleando la clase _Type Builder_. Esta se encarga de visitar los _features_ de las declaraciones de clase, dígase: funciones y atributos; tal que cada tipo contenga los atributos y métodos que lo caracterizan. @@ -92,7 +92,7 @@ Además se encarga de chequear la existencia del tipo **Main** con su método ** En esta clase también se hace uso de la clase _Inferencer Manager_ que permitirá luego realizar la inferencia de tipo. Por tanto, a todo atributo, parámetro de método o tipo de retorno de método, que esté definido como AUTO*TYPE se le asigna un \_id* que será manejado por el manager mencionado anteriormente. Este id será guardado en el nodo en cuestión para poder acceder a su información en el manager cuando sea necesario. -#### Chequeo e Inferencia de tipos +### Chequeo e Inferencia de tipos En primer lugar se utiliza la clase _Type Checker_ para validar el correcto uso de los tipos definidos. Toma la instancia de clase _Inferencer Manager_ utilizada en el _Type Builder_ para continuar la asignación de _id_ a otros elementos en el código que también pueden estar definidos como _AUTO_TYPE_, como es el caso de las variables definidas en la expresión _Let_. Las variables definidas en el _Scope_ se encargarán de guardar el _id_ asignado; en caso de que no se les haya asignado ninguno, el id será _None_. @@ -120,7 +120,7 @@ El _Type Inferencer_ por su parte, realizará un algoritmo de punto fijo para ll Por último se realiza un nuevo recorrido del _AST_ con el _Type Checker_ para detectar nuevamente los errores semánticos que puedan existir en el código, ahora con los _AUTO_TYPES_ sustituidos por el tipo inferido. -#### Traducción de COOL a CIL +### Traducción de COOL a CIL Se definió un _visitor_ en el que se recorre todo el _ast_ generado en etapas anteriores y que recibe el contexto, que también fue creado previamente, para tener la información relacionada a los tipos que se encuentren en el código. El objetivo fundamental de este recorrido es generar otro _ast_ que posee estructuras pertenecientes a CIL y que hará más fácil la generación de código MIPS posteriormente. Además, se generan chequeos que permitirán lanzar errores en tiempo de ejecución. @@ -136,9 +136,9 @@ En este recorrido por el ast, se define la estructura necesaria para la detecci En el caso del _case_ se chequea que la expresión principal no sea de tipo _void_ y además, que se conforme a alguna rama en la ejecución de este. El algoritmo empleado para reconocer por cuál de las ramas continuará la ejecución del código comienza por: tomar el tipo de todas las ramas del _case_, llámese a este conjunto $A$; por cada elemento del conjunto $A$ se toma la cantidad de tipos dentro del propio conjunto que se conforman a $a_i, i \in [1, |A|]$ ,de modo que se obtienen los pares $$. Se define $|\{a_j \leq a_i, \forall j, j\in[1, |A|]\}|$ como $a_{i_c}$. Tomando los elementos $a_i$ por el que menor $a_{i_c}$ tenga, se estará tomando los nodos más abajos en el árbol de tipos dentro de cada posible rama de este. Si se ordenan las ramas del _case_ por el que menor $a_{i_c}$ se obtendrá una lista. Luego se recorre esta generando por cada elemento el subconjunto $B_i$ donde $b_{i_i} \in B_i$ si $b_{i_i} <= a_i$. Se chequea si el tipo de la expresión principal del _case_ aparece en este subconjunto. En el caso de que aparezca, el case se resuelve yendo por la rama que posee el tipo $a_i$. -#### Traducción de CIL a MIPS +### Traducción de CIL a MIPS -Para la generación de código MIPS se definió un _visitor_ sobre el _ast_ de CIL generado en una etapa anterior. Este _visitor_ produce un nuevo _ast_ que representan las secciones _.DATA_, _.TEXT_ y las instrucciones en el código MIPS. Otro _visitor_ definido esta vez sobre los nodos del _ast_ del código MIPS se encarga de producir el código de MIPS que será ejecutado por el emulador SPIM. +Para la generación de código MIPS se definió un _visitor_ sobre el _ast_ de CIL generado en la etapa anterior. Este _visitor_ produce un nuevo _ast_ que representan las secciones: _.DATA_, _.TEXT_ y las instrucciones en el código MIPS. Otro _visitor_ definido esta vez sobre los nodos del _ast_ del código MIPS se encarga de producir el código de MIPS que será ejecutado por el emulador SPIM. **Representación de objetos en memoria** @@ -146,13 +146,13 @@ El principal desafío en esta etapa es decidir como representar las instancias d | Dirección x | Dirección x + 4 | Dirección x + 8 | ... | Dirección x + a * 4 | | ----------- | --------------- | --------------- | --- | ------------------- | -| Tipo | Atributo 0 | Atributo 1 | ... | Atributo a-1 | +| Tipo | Atributo $0$ | Atributo $1$ | ... | Atributo $a - 1$ | Por lo que un objeto es una zona continua de memoria de tamaño $1 + 4 * a$, donde $a$ es la cantidad de atributos que posee el objeto. El tipo y cada atributo son de tamaño $1$ _palabra_. El campo _Tipo_ es un número entre $0$ y $n-1$, siendo $n$ la cantidad total de tipos definidos en el programa de COOL a compilar. Un atributo puede guardar un valor específico o dicho valor puede ser interpretado como la dirección en memoria de otro objeto. -Para saber la cantidad de tipos y asignarles a cada uno un valor entre $0$ y $n$, en el _visitor_ sobre el _ast_ de CIL primero se recorren todos los tipos definidos por el código CIL, asignandoles valores disitntos de manera ordenada según se van descubriendo. Además, por cada tipo se guardan también los nombre de sus parámetros y métodos en el orden en que se definieron en el tipo. +Para saber la cantidad de tipos y asignarles a cada uno un valor entre $0$ y $n$, en el _visitor_ sobre el _ast_ de CIL primero se recorren todos los tipos definidos por el código CIL, asignándoles valores distintos de manera ordenada según se van descubriendo. Además, por cada tipo se guardan también los nombres de sus parámetros y métodos en el orden en que se definieron en el tipo. Para obtener o modificar un atributo específico de una instancia conociendo el nombre del atributo, se busca su índice en los atributos almacenados para el tipo en cuestión. Si el índice es $i$, entonces su valor estará en la dirección de memoria $(x+4) + (i * 4)$. @@ -172,15 +172,15 @@ Se tendrán $n$ listas, una por cada tipo. Cada celda es de una palabra y contie Los métodos en la lista se encuentran en el mismo orden en que fueron definidos en el tipo. -Estando una lista _dispatch_ específica, se decide la ubicación del método buscado por un proceso análogo a los atributos en las instancias de los objetos explicado anteriormente. Si el índice del método dentro del tipo es $i$, entonces la dirección del método buscando estará en la dirección $x + 4 * i$. +Estando una lista _dispatch_ específica, se decide la ubicación del método buscado por un proceso análogo a los atributos en las instancias de los objetos explicado anteriormente. Si el índice del método dentro del tipo es $i$, entonces la dirección del método buscado estará en la dirección $x + 4 * i$. -Ahora solo faltaría saber por cual de las listas _dispatch_ decidirse para buscar el método dado un tipo. +Ahora solo faltaría saber por cuál de las listas _dispatch_ decidirse para buscar el método dado un tipo. Para eso se tiene otra lista llamada _virtual_. Su función es almacenar por cada tipo, la dirección a su lista _dispatch_ . La lista _virtual_ tiene la siguiente forma: -| Dirección x | Dirección x + 4 | Dirección x + 8 | ... | Dirección x + (n-1) * 4 | -| ------------ | --------------- | ---------------- | --- | ----------------------- | -| _dispatch_ 0 | _dispatch_ 1 | _dispatch_ 2 | ... | _dispatch_ n-1 | +| Dirección $x$ | Dirección $x + 4$ | Dirección $x + 8$ | ... | Dirección $x + (n-1) * 4$ | +| -------------- | ----------------- | ----------------- | --- | ------------------------- | +| _dispatch_ $0$ | _dispatch_ $1$ | _dispatch_ $2$ | ... | _dispatch_ $n - 1$ | Recordar que $n$ es la cantidad de tipos. @@ -191,13 +191,13 @@ Dado una instancia en memoria, se puede ver su tipo en la primera de sus direcci El *pipeline* que sigue el proceso de compilación se observa en el archivo [main.py](https://github.com/codersUP/cool-compiler-2021/blob/master/src/main.py). Se hace uso de las funcionalidades implementadas en el paquete `compiler`, que presenta la siguiente estructura: ```bash -compiler/ ├── cmp │ ├── ast.py │ ├── automata.py │ ├── cil_ast.py │ ├── grammar.py │ ├── __init__.py +│ ├── mips_ast.py │ ├── pycompiler.py │ ├── semantic.py │ └── utils.py @@ -210,6 +210,12 @@ compiler/ │ ├── parser.py │ └── utils.py └── visitors + ├── cil2mips + │ ├── cil2mips.py + │ ├── __init__.py + │ ├── mips_lib.asm + │ ├── mips_printer.py + │ └── utils.py ├── cool2cil │ ├── cil_formatter.py │ ├── cool2cil.py @@ -226,7 +232,7 @@ compiler/ └── visitor.py ``` -En su mayoría los módulos que posee el paquete `cmp` fueron tomados de los proyectos y contenidos vistos en 3er año. Los paquetes `lexer` y `parser` definen la lógica para la tokenización y posterior parsing del texto de entrada respectivamente. El paquete `visitors` contiene las funcionalidades para llevar a cabo los recorridos sobre los *ast*, que en este caso serían: los *visitors* para realizar el chequeo semántico, el *visitor* que permite traducir de COOL a CIL, y finalmente, el *visitor* que permite traducir de CIL a MIPS. +En su mayoría, los módulos que posee el paquete `cmp` fueron tomados de los proyectos y contenidos vistos en 3er año. Los paquetes `lexer` y `parser` definen la lógica para la tokenización y posterior parsing del texto de entrada respectivamente. El paquete `visitors` contiene las funcionalidades para llevar a cabo los recorridos sobre los *ast*, que en este caso serían: los *visitors* para realizar el chequeo semántico, el *visitor* que permite traducir de COOL a CIL, y finalmente, el *visitor* que permite traducir de CIL a MIPS. ## Licencia diff --git a/doc/report.pdf b/doc/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dde73e2c90ce586a7084957efd1f9ee308952747 GIT binary patch literal 123049 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 Date: Thu, 3 Mar 2022 06:44:15 -0500 Subject: [PATCH 137/138] remove unused code in cool script --- src/coolc.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/coolc.sh b/src/coolc.sh index 7d7ae68f0..6f3c3db55 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,9 +1,7 @@ -# Incluya aquí las instrucciones necesarias para ejecutar su compilador - INPUT_FILE=$1 -OUTPUT_FILE=${INPUT_FILE:0: -2}mips echo "codersUP - COOLCompilerv0.0.1" echo "Copyright (c) 2022: Carmen Irene Cabrera Rodríguez, David Guaty Domínguez, Enrique Martínez González" -# Llamar al compilador -python3 main.py -f "$INPUT_FILE" + +# Run the compiler +python3 main.py -f $INPUT_FILE From 30d4570d6c5a68dcea2869ad647d54c82b6f4b07 Mon Sep 17 00:00:00 2001 From: Carmen Cabrera Date: Thu, 3 Mar 2022 07:55:39 -0500 Subject: [PATCH 138/138] changing default goal in makefile --- src/coolc.sh | 2 +- src/makefile | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coolc.sh b/src/coolc.sh index 6f3c3db55..62c220125 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -4,4 +4,4 @@ echo "codersUP - COOLCompilerv0.0.1" echo "Copyright (c) 2022: Carmen Irene Cabrera Rodríguez, David Guaty Domínguez, Enrique Martínez González" # Run the compiler -python3 main.py -f $INPUT_FILE +python3 main.py -f "$INPUT_FILE" diff --git a/src/makefile b/src/makefile index d6c870144..9be6ed365 100644 --- a/src/makefile +++ b/src/makefile @@ -1,4 +1,5 @@ -.PHONY: clean +.DEFAULT_GOAL := info +.PHONY: clean, info, main, test CODE := code.cl FILE_NAME := $(shell echo $(CODE) | cut -d '.' -f 1) @@ -19,3 +20,5 @@ install: @python -m pip install -r ../requirements.txt @sudo apt-get install spim +info: + @echo "Cool Compiler 2021 - CodersUP" \ No newline at end of file