diff --git a/Readme.md b/Readme.md index a47d48b9e..73af6506c 100644 --- a/Readme.md +++ b/Readme.md @@ -123,7 +123,7 @@ Verá la indicación **Some checks haven't completed yet**. ![](img/img8.png) -Es posible que tenga que actualizar los cambios que se hayan hecho en el repositorio original, por ejemplo, si se han agregado nuevos tests. En este caso obtendrá el siguiente mensaje: +Es posible que tenga que actualizar los cambios que se hayan hecho en el repositorio original, por ejemplo, si se han agregado nuevos tests. En este caso obtendrá el siguiente mensaje: > **This branch is out-of-date with base branch** diff --git a/doc/Report/files/SPIM_Manual.pdf b/doc/Report/files/SPIM_Manual.pdf new file mode 100644 index 000000000..b785613c8 Binary files /dev/null and b/doc/Report/files/SPIM_Manual.pdf differ diff --git a/doc/Report/files/compilers.pdf b/doc/Report/files/compilers.pdf new file mode 100644 index 000000000..4f7c80494 Binary files /dev/null and b/doc/Report/files/compilers.pdf differ diff --git a/doc/cool-manual.pdf b/doc/Report/files/cool-manual.pdf similarity index 100% rename from doc/cool-manual.pdf rename to doc/Report/files/cool-manual.pdf diff --git a/doc/github-git-cheat-sheet.pdf b/doc/Report/files/github-git-cheat-sheet.pdf similarity index 100% rename from doc/github-git-cheat-sheet.pdf rename to doc/Report/files/github-git-cheat-sheet.pdf diff --git a/doc/Report/report.pdf b/doc/Report/report.pdf new file mode 100644 index 000000000..ec0da7e3e Binary files /dev/null and b/doc/Report/report.pdf differ diff --git a/doc/Report/report.tex b/doc/Report/report.tex new file mode 100644 index 000000000..43af13d3e --- /dev/null +++ b/doc/Report/report.tex @@ -0,0 +1,215 @@ +\documentclass[twoside]{article} + +\usepackage{lipsum} +\usepackage[none]{hyphenat} + +\usepackage[sc]{mathpazo} +\usepackage{amsmath} +\usepackage[T1]{fontenc} +\linespread{1.05} +\usepackage{microtype} + +\usepackage[hmarginratio=1:1,top=32mm,columnsep=20pt]{geometry} +\usepackage{multicol} +\usepackage[hang, small,labelfont=bf,up,textfont=it,up]{caption} +\usepackage{booktabs} +\usepackage{float} +\usepackage[hidelinks]{hyperref} +\usepackage[usenames]{color} + +\usepackage{lettrine} +\usepackage{paralist} + +\usepackage{abstract} +\renewcommand{\abstractnamefont}{\normalfont\bfseries} +\renewcommand{\abstracttextfont}{\normalfont\small\itshape} + +\usepackage{titlesec} +\renewcommand\thesection{\Roman{section}} +\renewcommand\thesubsection{\Roman{subsection}} +\titleformat{\section}[block]{\large\scshape\centering}{\thesection.}{1em}{} +\titleformat{\subsection}[block]{\large}{\thesubsection.}{1em}{} + +\usepackage{fancyhdr} +\pagestyle{fancy} +\fancyhead{} +\fancyfoot{} + +\fancyhead[C]{ Compilaci\'on $\bullet$ COOL $\bullet$ C-411} +\fancyfoot[RO,LE]{\thepage} + +\title{\vspace{-15mm}\fontsize{20pt}{10pt}\selectfont\textbf{Proyecto de Complementos de Compilaci\'on}} + +\author{ +\large +\textbf{\large Compilador para el lenguaje COOL} \\[1.5cm] +\textsc{Alejandro Campos, Darian Dominguez}\\\\[2mm] +\normalsize Facultad de Matem\'atica y Computaci\'on \\ +\normalsize Universidad de la Habana \\ +\normalsize 2022 \\[2cm] +\vspace{-5mm} +} +\date{} + + +\usepackage{graphicx} +\begin{document} + +\maketitle + +\thispagestyle{fancy} + +\begin{center} +\textbf{Resumen} +\end{center} +\noindent \textit{Este reporte contiene las principales ideas seguidas en la implementaci\'on de un compilador completamente funcional para el lenguaje COOL, el cual es un peque\~no lenguaje que mantiene muchas de las caracter\'isticas de los lenguajes de programaci\'on modernos, incluyendo orientaci\'on a objetos, tipado est\'atico y manejo autom\'atico de memoria. A lo largo de este informe se explicar\'an, adem\'as, cada una de las etapas o fases seguidas en la implementaci\'on del compilador. Por \'ultimo, daremos, a groso modo, una peque\~na ojeada al c\'odigo con el objetivo de que el lector entienda el funcionamiento de los m\'etodos que consideramos fundamentales para el desarrollo del compilador.}\\[0.5cm] + +\section{Introducci\'on} +Un compilador es un programa que traduce c\'odigo escrito en un lenguaje de programaci\'on, llamado fuente, a otro lenguaje. En este tipo de traductor, el lenguaje fuente es generalmente un lenguaje de alto nivel, y el otro, un lenguaje de bajo nivel. Un compilador es uno de los pilares de la programaci\'on y de c\'omo entender la comunicaci\'on entre un lenguaje de alto nivel y una m\'aquina. Al poder conocer el funcionamiento de este paso intermedio nos permitir\'a desarrollar y programar de una forma m\'as precisa en los lenguajes de alto nivel. El proceso de compilaci\'on es aquel por el cual se traducen las instrucciones escritas en un determinado lenguaje de programaci\'on a lenguaje de m\'aquina, es decir, a algo que la computadora entiende. + +En nuestro caso, el lenguaje fuente es COOL. Un programa en COOL son conjuntos de clases. Una clase encapsula las variables y procedimientos de un tipo de datos. Las instancias de una clase son objetos. En COOL se identifican clases y tipos; es decir, cada clase define un tipo. Las clases permiten a los programadores definir nuevos tipos y procedimientos asociados (o m\'etodos) espec\'ificos para esos tipos. La herencia permite que los nuevos tipos ampl\'ien el comportamiento de los tipos existentes. COOL es un lenguaje de expresi\'on. La mayor\'ia de las construcciones son expresiones, y cada expresi\'on tiene un valor y un tipo. Este lenguaje es type safe, es decir, se garantiza que los procedimientos se aplicar\'an a los datos del tipo correcto. Si bien el tipeo est\'atico impone una fuerte disciplina en la programaci\'on, garantiza que no puedan surgir errores de tipo en la ejecuci\'on de los programas en COOL. + +Una vez se tiene una idea acerca del funcionamiento de un compilador y del lenguaje COOL, podemos decir que el objetivo de este informe es presentar la soluci\'on al proceso de compilaci\'on del lenguaje COOL a lenguaje ensamblador, implementado en el lenguaje de pogramaci\'on python, cuyo c\'odigo se encuentra en este \href{https://github.com/BeginnerCompilers/cool-compiler-2021}{\textcolor{red}{\underline{repositorio}}} de Github.\\\\ + +\section{Gram\'atica y AST de COOL} +La gram\'atica de este lenguaje responde al manual de COOL \cite{cool}. Se encuentra definida en \texttt{src/utils/COOL\_Grammar.py} y su estructura se presenta a continuaci\'on:\\ + +\begin{align*} +\textit{program} &\rightarrow \textit{class-list}\\ +\textit{class-list} &\rightarrow \textit{def-class ;}\\ + &\rightarrow \textit{def\_class ; class-list}\\ +\textit{def\_class} &\rightarrow \textit{class type \{ feature-list \} }\\ +&\rightarrow \textit{class type inherits type \{ feature-list \} }\\ +\textit{feature-list} &\rightarrow \textit{def-attr ; feature-list}\\ +&\rightarrow \textit{def-meth ; feature-list}\\ +&\rightarrow \epsilon\\ +\textit{def-attr} &\rightarrow \textit{id : type}\\ +&\rightarrow \textit{id : type <- expr}\\ +\textit{def-meth} &\rightarrow \textit{id ( param-list ) : type \{ expr \} }\\ +\textit{param-list} &\rightarrow \textit{non-empty-param-list}\\ +&\rightarrow \epsilon\\ +\textit{non-empty-param-list} &\rightarrow \textit{param}\\ +&\rightarrow \textit{param , non-empty-param-list}\\ +\textit{param} &\rightarrow \textit{id : type}\\ +\textit{expr} &\rightarrow \textit{id <- expr}\\ +&\rightarrow \textit{while expr loop expr pool}\\ +&\rightarrow \textit{ \{ expr-list \} }\\ +\textit{expr-list} &\rightarrow \textit{expr ;}\\ +&\rightarrow \textit{expr ; expr-list}\\ +\textit{expr} &\rightarrow \textit{let id-list in expr}\\ +\end{align*} +\begin{align*} +\textit{id-list} &\rightarrow \textit{id : type}\\ +&\rightarrow \textit{id : type <- expr}\\ +&\rightarrow \textit{id : type, id-list}\\ +&\rightarrow \textit{id : type <- expr , id-list}\\ +\textit{expr} &\rightarrow \textit{case expr of case-list esac}\\ +\textit{case-list} &\rightarrow \textit{id : type <- expr ;}\\ +&\rightarrow \textit{id : type <- expr ; case-list}\\ +\textit{expr} &\rightarrow \textit{not expr}\\ +&\rightarrow \textit{comp = expr}\\ +&\rightarrow \textit{comp}\\ +\textit{comp} &\rightarrow \textit{comp $<$ artih}\\ +&\rightarrow \textit{comp $\leq$ arith}\\ +&\rightarrow \textit{arith}\\ +\textit{arith} &\rightarrow \textit{arith + term}\\ +&\rightarrow \textit{arith - term}\\ +&\rightarrow \textit{term}\\ +\textit{term} &\rightarrow \textit{term * factor}\\ +&\rightarrow \textit{term / factor}\\ +&\rightarrow \textit{factor}\\ +\textit{factor} &\rightarrow \textit{isvoid factor}\\ +&\rightarrow \textit{$\sim$ factor}\\ +&\rightarrow \textit{atom}\\ +\textit{atom} &\rightarrow \textit{true}\\ +&\rightarrow \textit{false}\\ +&\rightarrow \textit{string}\\ +&\rightarrow \textit{num}\\ +&\rightarrow \textit{id}\\ +&\rightarrow \textit{new type}\\ +&\rightarrow \textit{if expr then expr else expr fi}\\ +&\rightarrow \textit{( expr )}\\ +&\rightarrow \textit{dispatch}\\ +\textit{dispatch} &\rightarrow \textit{atom . id ( arg-list )}\\ +&\rightarrow \textit{id ( arg-list )}\\ +&\rightarrow \textit{atom @ type . id ( arg-list )}\\ +\textit{arg-list} &\rightarrow \textit{non-empty-arg-list}\\ +&\rightarrow \epsilon\\ +\textit{non-empty-arg-list} &\rightarrow \textit{expr}\\ +&\rightarrow \textit{expr , non-empty-arg-list}\\ +\end{align*} + +El \'arbol de sint\'axis abstracta de COOL se encuentra definido en \texttt{src/utils/ast/AST\_Nodes.py}. Se defini\'o teniendo en cuenta la gram\'atica que mostramos anteriormente. Esta gram\'atica es atributada y cada producci\'on contiene como computar cada uno de los atributos que la conforman que, en la mayor\'ia de los casos, son argumentos que se pasan como par\'ametros para la creaci\'on de los nodos del ast de nuestro lenguaje.\\\\ + + +\section{An\'alisis lexicogr\'afico.} +El an\'alisis lexicogr\'afico es la primera fase del proceso de compilaci\'on, es donde transformamos la entrada del usuario en tokens v\'alidos del lenguaje. Esta fase se encuentra implementada en \texttt{src/utils/COOL\_Lexer.py}. + +El lexer en nuestro proyecto se apoya en el m\'odulo \textbf{re} de python para expresiones regulares. La clase \texttt{COOL\_Lexer} contiene las expresiones regulares que conforman el lenguaje COOL, esta clase hereda de \texttt{Lexer}, que implementa el m\'etodo \texttt{tokenize}, el cual recibe un texto como entrada y devuelve los tokens del lenguaje. Tambi\'en se detectan los errores lexicogr\'aficos que pueden existir y, en caso de que hayan, se reportan y se detiene la ejecuci\'on del programa. Los errores que se detectan en esta etapa son token inv\'alido, y los errores relacionados con los comentarios y la definici\'on de cadenas de texto, es decir, variables de tipo String. Estos \'ultimos se analizan de forma independiente al m\'odulo \textbf{re} de pyhton para clasificar cada uno de los errores que pueden surgir.\\\\ + + +\section{An\'alisis sint\'actico} +El proceso de parsing b\'asicamente de lo que se encarga es en convertir cadenas de texto escritas en el lenguaje origen a una estructura arb\'orea que captura la sem\'antica del programa. Nuestro parser consiste en analizar la secuencia de tokens calculada en el lexer, y produce un \'arbol de derivaci\'on. + +La implementaci\'on del parser se enuentra en \texttt{src/utils/parser}. + +Primeramente tenemos la clase \texttt{parser}, que contiene las caracter\'isticas comunes a todos los parsers, en esta clase es donde se calculan los conjuntos firsts y follows, y contiene los m\'etodos que deben implementar los parsers que heredan de esta. El parser utilizado es un parser ShiftReduce, la clase \texttt{ShiftReduceParser} devuelve el \'arbol de derivaci\'on de la cadena de texto analizada y las operaciones (shift o reduce) que, posteriormente, se utilizan para generar el ast correspondiente. Adem\'as esta clase es la encargada de devolver los errores sint\'acticos o de parser, en caso de que ocurra alg\'un conflicto. La clase \texttt{LR1\_Parser} implementa los m\'etodos que calculan la tabla de parsing y su respectivo aut\'omata. Finalmente, el parser utilizado fue el LALR 1, que es un parser LR 1, solo que reduce los estados del aut\'omata antes mencionado. + +En \texttt{COOL\_Parser} se encuentran los valores precalculados de las tablas action y goto, que se guardan para acelerar la ejecuci\'on del programa, ya que de otra forma demora un poco en calcularlas desde 0. De esta forma \texttt{ShiftReduceParser} ejecuta el proceso de parsing con estas tablas ya calculadas. Si se detecta alg\'un error se reporta y se detiene la ejecuci\'on del algoritmo.\\\\ + + +\section{An\'alisis Sem\'antico} +Llegados a esta fase, no existen errores lexicogr\'aficos ni de parsing, por lo que el texto est\'a sint\'acticamente correcto y podemos pasar al chequeo de tipos sin problemas. Podemos encontrar la implementaci\'on de esta fase en \texttt{src/utils/semantic\_check}. + +Para verificar la consistencia de tipos se realizan tres recorridos por el ast. El primero de estos recorridos, \texttt{TypeCollector} a\~nade al contexto todos los tipos declarados en el lenguaje, no sin antes a\~nadir los tipos predeterminados de COOL con sus respectivos m\'etodos. Verifica las redefiniciones de clases. + +Luego, antes de pasar al cuerpo de los m\'etodos y despu\'es de recolectar todos los tipos, se hace necesario otro recorrido que verifique declaraci\'on de atributos, par\'ametros, valores de retorno y otras referencias a tipos, y esto precisamnte es lo que hace \texttt{TypeBuilder}. + +El tercer y \'ultimo recorrido, \texttt{TypeChecker}, verifica que el programa haga un uso correcto de los tipos definidos, seg\'un las especificaciones del manual de COOL. En este recorrido tambi\'en se chequea la herencia circular, la redefinici\'on de m\'etodos y que el programa contenga una clase Main con un m\'etodo main sin par\'ametros. + +Al principio de cada recorrido aparecen los errores e incosistencias que se detectan en cada uno. Los recorridos del ast fueron implementados con ayuda del patr\'on visitor.\\\\ + + +\section{Generaci\'on de c\'odigo} +Para la generaci\'on de c\'odigo intermedio de COOL a MIPS vamos a dise\~nar un lenguaje de m\'aquina con capacidades orientadas a objetos. Este lenguaje nos va a permitir generar c\'odigo de MIPS de forma m\'as sencilla, ya que el salto directamente desde COOL a MIPS es demasiado complejo. Este lenguaje se denomina CIL, 3-address object-oriented. + +En esta fase no hay incosistencia de tipos, es decir el c\'odigo de COOL est\'a lexicogr\'afica, sint\'actica y sem\'anticamente correcto, listo para la ejecuci\'on. Esta fase se encuentra implementada en \texttt{src/utils/code\_generation} + +\subsection{CIL} +Como ya se dijo, primeramente, pasaremos de COOL para un lenguaje intermedio, CIL. El ast de este lenguaje se implement\'o seg\'un la definici\'on brindada en \cite{compilers} Cap\'itulo 7, donde, adem\'as de las funciones que ah\'i se describen, se a\~naden otras para realizar la ejecuci\'on de las funciones predefinidas en COOL, y que son necesarias para el retorno de valores de m\'etodos de este lenguaje. + +Con ayuda del patr\'on visitor se recorre el ast de COOL y, primeramente, se a\~naden todas las funciones y tipos built-in, es decir, que est\'an definidas por defecto en COOL. Posteriormente, por cada nodo del ast de COOL se crean las instrucciones correspondientes en CIL de acuerdo a su definici\'on. + +Contamos con una clase auxiliar, \texttt{BaseCOOLToCIL}, que contiene funciones \'utiles para el registro de par\'ametros, funciones, instrucciones, variables locales y otras declaraciones en CIL. Esta clase contiene, adem\'as, la definici\'on de los m\'etodos built-in de COOL.\\ + +\subsection{MIPS} +Despu\'es de tener el ast de CIL, el siguiente paso es, finalmente, generar el c\'odigo ensamblador correspondiente a la entrada de COOL. Para ello, primeramente definimos el ast de MIPS, con ayuda de \cite{mips}, en el cual cada nodo representa una instrucci\'on v\'alida en MIPS. Siguiendo la misma idea de generaci\'on de c\'odigo anterior, se recorre cada nodo del ast de CIL y se va creando el correspondiente ast v\'alido en MIPS. Una vez m\'as, los recorridos de cada ast se realizan con ayuda del patr\'on visitor, mediante el cual tambi\'en, se genera el c\'odigo ensamblador en la clase \texttt{PrintMIPS}, que tranforma cada nodo del ast en el c\'odigo de MIPS correspondiente. + +Los objetos en memoria est\'an ubicados de la siguiente forma: tipo, espacio que ocupa, direcci\'on de tabla de dispatch, atributos y un valor que indica que hay un objeto en esta zona de memoria. + +El valor que se encuentra en la secci\'on que guarda el tipo se interpreta, como su nombre lo indica, como el tipo del objeto. + +La segunda secci\'on de este segmento se interpreta como el tama\~no en palabras del objeto. + +La tabla de dispatch es una direcci\'on que indica el inicio del segmento de la memoria donde esta se encuentra. Las secciones de dicho segmento se interpretan como la direcci\'on a cada uno de los m\'etodos del objeto. + +Cada una de las subsecciones de la secci\'on de los atributos representa el valor de un atributo del objeto. + +La \'ultima secci\'on nos dice que esta zona de la memoria corresponde a un objeto. + +Los tipos est\'an representados en memoria mediante tres estructuras. Primeramente est\'a definida una direcci\'on a un segmento de memoria que representa el nombre del tipo. Luego se representa una estructura que es utilizada en la creaci\'on de los objetos, por lo tanto se representa como se dijo anteriormente. Este segmento de memoria es asignado al objeto una vez que se crea y contiene los valores por defecto de estos. Por \'ultimo, tambi\'en se encuentra una tabla de dispatch, y existe una tabla de nombres, donde se puede encontrar el nombre de un tipo espec\'ifico. + +\section{Ejecuci\'on} +Este proyecto no usa ninguna librer\'ia de python fuera de la librer\'ia est\'andar. Por lo que para ejecutarlo solo debe tener instalado python 3 o superior. Desde el directorio del proyecto ejecutar \texttt{python3 main.py file} donde file debe ser un archivo ubicado en el mismo directorio del proyecto. El compilador generar\'a como salida un archivo con extensi\'on \texttt{.mips} que puede ejecutar con cualquier simulador de spim. Sugerimos el uso de QtSpim.\\\\ + + +\begin{thebibliography}{99} + \bibitem{cool} Cool-manual \href{https://github.com/BeginnerCompilers/cool-compiler-2021/tree/master/doc/Report/files/cool-manual.pdf}{(\textcolor{red}{\underline{abrir}})} + + \bibitem{compilers} Compilers \href{https://github.com/BeginnerCompilers/cool-compiler-2021/tree/master/doc/Report/files/compilers.pdf}{(\textcolor{red}{\underline{abrir}})} + + \bibitem{mips} Spim Manual \href{https://github.com/BeginnerCompilers/cool-compiler-2021/tree/master/doc/Report/files/SPIM\_Manual.pdf}{(\textcolor{red}{\underline{abrir}})} +\end{thebibliography} + + +\end{document} \ No newline at end of file diff --git a/doc/team.yml b/doc/team.yml index c16162532..af017d6ba 100644 --- a/doc/team.yml +++ b/doc/team.yml @@ -1,10 +1,8 @@ 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: Alejandro Campos Matanzas + github: Alexx-4 + group: C-411 + + - name: Darian Dominguez Alayón + github: Darian10 + group: C-411 diff --git a/src/cmp/__init__.py b/src/cmp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cmp/ast.py b/src/cmp/ast.py new file mode 100644 index 000000000..ec162b83b --- /dev/null +++ b/src/cmp/ast.py @@ -0,0 +1,62 @@ +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() + +def get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode, ): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__}' + child = self.visit(node.node, tabs + 1) + return f'{ans}\n{child}' + + @visitor.when(BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + left = self.visit(node.left, tabs + 1) + right = self.visit(node.right, tabs + 1) + return f'{ans}\n{left}\n{right}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/cmp/automata.py b/src/cmp/automata.py new file mode 100644 index 000000000..f42b6f00c --- /dev/null +++ b/src/cmp/automata.py @@ -0,0 +1,207 @@ +try: + import pydot +except: + pass + +class State: + def __init__(self, state, final=False, formatter=lambda x: str(x), shape='circle'): + self.state = state + self.final = final + self.transitions = {} + self.epsilon_transitions = set() + self.tag = None + self.formatter = formatter + self.shape = shape + + # The method name is set this way from compatibility issues. + def set_formatter(self, value, attr='formatter', visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + self.__setattr__(attr, value) + for destinations in self.transitions.values(): + for node in destinations: + node.set_formatter(value, attr, visited) + for node in self.epsilon_transitions: + node.set_formatter(value, attr, visited) + return self + + def has_transition(self, symbol): + return symbol in self.transitions + + def add_transition(self, symbol, state): + try: + self.transitions[symbol].append(state) + except: + self.transitions[symbol] = [state] + return self + + def add_epsilon_transition(self, state): + self.epsilon_transitions.add(state) + return self + + def recognize(self, string): + states = self.epsilon_closure + for symbol in string: + states = self.move_by_state(symbol, *states) + states = self.epsilon_closure_by_state(*states) + return any(s.final for s in states) + + def to_deterministic(self, formatter=lambda x: str(x)): + closure = self.epsilon_closure + start = State(tuple(closure), any(s.final for s in closure), formatter) + + closures = [ closure ] + states = [ start ] + pending = [ start ] + + while pending: + state = pending.pop() + symbols = { symbol for s in state.state for symbol in s.transitions } + + for symbol in symbols: + move = self.move_by_state(symbol, *state.state) + closure = self.epsilon_closure_by_state(*move) + + if closure not in closures: + new_state = State(tuple(closure), any(s.final for s in closure), formatter) + closures.append(closure) + states.append(new_state) + pending.append(new_state) + else: + index = closures.index(closure) + new_state = states[index] + + state.add_transition(symbol, new_state) + + return start + + @staticmethod + def from_nfa(nfa, get_states=False): + states = [] + for n in range(nfa.states): + state = State(n, n in nfa.finals) + states.append(state) + + for (origin, symbol), destinations in nfa.map.items(): + origin = states[origin] + origin[symbol] = [ states[d] for d in destinations ] + + if get_states: + return states[nfa.start], states + return states[nfa.start] + + @staticmethod + def move_by_state(symbol, *states): + return { s for state in states if state.has_transition(symbol) for s in state[symbol]} + + @staticmethod + def epsilon_closure_by_state(*states): + closure = { state for state in states } + + l = 0 + while l != len(closure): + l = len(closure) + tmp = [s for s in closure] + for s in tmp: + for epsilon_state in s.epsilon_transitions: + closure.add(epsilon_state) + return closure + + @property + def epsilon_closure(self): + return self.epsilon_closure_by_state(self) + + @property + def name(self): + return self.formatter(self.state) + + def get(self, symbol): + target = self.transitions[symbol] + assert len(target) == 1 + return target[0] + + def __getitem__(self, symbol): + if symbol == '': + return self.epsilon_transitions + try: + return self.transitions[symbol] + except KeyError: + return None + + def __setitem__(self, symbol, value): + if symbol == '': + self.epsilon_transitions = value + else: + self.transitions[symbol] = value + + def __repr__(self): + return str(self) + + def __str__(self): + return str(self.state) + + def __hash__(self): + return hash(self.state) + + def __iter__(self): + yield from self._visit() + + def _visit(self, visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + yield self + + for destinations in self.transitions.values(): + for node in destinations: + yield from node._visit(visited) + for node in self.epsilon_transitions: + yield from node._visit(visited) + + def graph(self): + G = pydot.Dot(rankdir='LR', margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + visited = set() + def visit(start): + ids = id(start) + if ids not in visited: + visited.add(ids) + G.add_node(pydot.Node(ids, label=start.name, shape=self.shape, style='bold' if start.final else '')) + for tran, destinations in start.transitions.items(): + for end in destinations: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label=tran, labeldistance=2)) + for end in start.epsilon_transitions: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label='ε', labeldistance=2)) + + visit(self) + G.add_edge(pydot.Edge('start', id(self), label='', style='dashed')) + + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + + def write_to(self, fname): + return self.graph().write_svg(fname) + +def multiline_formatter(state): + return '\n'.join(str(item) for item in state) + +def lr0_formatter(state): + try: + return '\n'.join(str(item)[:-4] for item in state) + except TypeError: + return str(state)[:-4] \ No newline at end of file diff --git a/src/cmp/cil.py b/src/cmp/cil.py new file mode 100644 index 000000000..de6782c16 --- /dev/null +++ b/src/cmp/cil.py @@ -0,0 +1,231 @@ +import cmp.visitor as visitor + + +class Node: + pass + +class ProgramNode(Node): + def __init__(self, dottypes, dotdata, dotcode): + self.dottypes = dottypes + self.dotdata = dotdata + self.dotcode = dotcode + +class TypeNode(Node): + def __init__(self, name): + self.name = name + self.attributes = [] + self.methods = [] + +class DataNode(Node): + def __init__(self, vname, value): + self.name = vname + self.value = value + +class FunctionNode(Node): + def __init__(self, fname, params, localvars, instructions): + self.name = fname + self.params = params + self.localvars = localvars + self.instructions = instructions + +class ParamNode(Node): + def __init__(self, name): + self.name = name + +class LocalNode(Node): + def __init__(self, name): + self.name = name + +class InstructionNode(Node): + pass + +class AssignNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + +class ArithmeticNode(InstructionNode): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + +class PlusNode(ArithmeticNode): + pass + +class MinusNode(ArithmeticNode): + pass + +class StarNode(ArithmeticNode): + pass + +class DivNode(ArithmeticNode): + pass + +class GetAttribNode(InstructionNode): + pass + +class SetAttribNode(InstructionNode): + pass + +class GetIndexNode(InstructionNode): + pass + +class SetIndexNode(InstructionNode): + pass + +class AllocateNode(InstructionNode): + def __init__(self, itype, dest): + self.type = itype + self.dest = dest + +class ArrayNode(InstructionNode): + pass + +class TypeOfNode(InstructionNode): + def __init__(self, obj, dest): + self.obj = obj + self.dest = dest + +class LabelNode(InstructionNode): + pass + +class GotoNode(InstructionNode): + pass + +class GotoIfNode(InstructionNode): + pass + +class StaticCallNode(InstructionNode): + def __init__(self, function, dest): + self.function = function + self.dest = dest + +class DynamicCallNode(InstructionNode): + def __init__(self, xtype, method, dest): + self.type = xtype + self.method = method + self.dest = dest + +class ArgNode(InstructionNode): + def __init__(self, name): + self.name = name + +class ReturnNode(InstructionNode): + def __init__(self, value=None): + self.value = value + +class LoadNode(InstructionNode): + def __init__(self, dest, msg): + self.dest = dest + self.msg = msg + +class LengthNode(InstructionNode): + pass + +class ConcatNode(InstructionNode): + pass + +class PrefixNode(InstructionNode): + pass + +class SubstringNode(InstructionNode): + pass + +class ToStrNode(InstructionNode): + def __init__(self, dest, ivalue): + self.dest = dest + self.ivalue = ivalue + +class ReadNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + +class PrintNode(InstructionNode): + def __init__(self, str_addr): + self.str_addr = str_addr + +def get_formatter(): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + dottypes = '\n'.join(self.visit(t) for t in node.dottypes) + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(TypeNode) + def visit(self, node): + attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) + methods = '\n\t'.join(f'method {x}: {y}' for x,y in node.methods) + + return f'type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}' + + @visitor.when(FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions) + + return f'function {node.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(ParamNode) + def visit(self, node): + return f'PARAM {node.name}' + + @visitor.when(LocalNode) + def visit(self, node): + return f'LOCAL {node.name}' + + @visitor.when(AssignNode) + def visit(self, node): + return f'{node.dest} = {node.source}' + + @visitor.when(PlusNode) + def visit(self, node): + return f'{node.dest} = {node.left} + {node.right}' + + @visitor.when(MinusNode) + def visit(self, node): + return f'{node.dest} = {node.left} - {node.right}' + + @visitor.when(StarNode) + def visit(self, node): + return f'{node.dest} = {node.left} * {node.right}' + + @visitor.when(DivNode) + def visit(self, node): + return f'{node.dest} = {node.left} / {node.right}' + + @visitor.when(AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.type}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method}' + + @visitor.when(ArgNode) + def visit(self, node): + return f'ARG {node.name}' + + @visitor.when(ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/cmp/evaluation.py b/src/cmp/evaluation.py new file mode 100644 index 000000000..aa6e8f665 --- /dev/null +++ b/src/cmp/evaluation.py @@ -0,0 +1,34 @@ +from cmp.pycompiler import EOF +from cmp.tools.parsing import ShiftReduceParser + +def evaluate_reverse_parse(G, 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) + 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(G[next(tokens).token_type], EOF) + return stack[0] \ No newline at end of file diff --git a/src/cmp/languages.py b/src/cmp/languages.py new file mode 100644 index 000000000..e2c0c33da --- /dev/null +++ b/src/cmp/languages.py @@ -0,0 +1,228 @@ +from cmp.pycompiler import Sentence, Production +from cmp.utils import ContainerSet, Token, UnknownToken +from cmp.tools.parsing import build_parsing_table, metodo_predictivo_no_recursivo + +class BasicXCool: + def __init__(self, G): + self.G = G + self.fixed_tokens = { lex: Token(lex, G[lex]) for lex in '+ - * / ( )'.split() } + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G.EOF, G['*'], G['/'], G[')'], G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['num'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['+'], ): [ Production(G['X'], Sentence(G['+'], G['T'], G['X'])), ], + ( G['X'], G['-'], ): [ Production(G['X'], Sentence(G['-'], G['T'], G['X'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['num'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['*'], ): [ Production(G['Y'], Sentence(G['*'], G['F'], G['Y'])), ], + ( G['Y'], G['/'], ): [ Production(G['Y'], Sentence(G['/'], G['F'], G['Y'])), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['-'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['+'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['num'], ): [ Production(G['F'], Sentence(G['num'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def tokenizer(self): + G = self.G + fixed_tokens = self.fixed_tokens + + def tokenize_text(text): + tokens = [] + for item in text.split(): + try: + float(item) + token = Token(item, G['num']) + except ValueError: + try: + token = fixed_tokens[item] + except: + token = UnknownToken(item) + tokens.append(token) + eof = Token('$', G.EOF) + tokens.append(eof) + return tokens + + return tokenize_text + +class PowXCool: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['^']: ContainerSet(G['^'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + G['Z']: ContainerSet(G['^'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['^'], G['F']): ContainerSet(G['^'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['A']: ContainerSet(G['-'], G['*'], G['/'], G['^'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['Z']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False) + } + +class Regex: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['|']: ContainerSet(G['|'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['symbol']: ContainerSet(G['symbol'] , contains_epsilon=False), + G['ε']: ContainerSet(G['ε'] , contains_epsilon=False), + G['E']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['ε'], G['symbol'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['|'] , contains_epsilon=True), + G['Y']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=True), + G['Z']: ContainerSet(G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['|'], G['E']): ContainerSet(G['|'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['T']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['*']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['symbol']): ContainerSet(G['symbol'] , contains_epsilon=False), + Sentence(G['ε']): ContainerSet(G['ε'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), + G['F']: ContainerSet(G[')'], G.EOF, G['symbol'], G['|'], G['ε'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G.EOF, G['|'], G['*'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), + G['Z']: ContainerSet(G.EOF, G['|'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['symbol'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['ε'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['|'], ): [ Production(G['X'], Sentence(G['|'], G['E'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['symbol'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['ε'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['symbol'], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['ε'], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['('], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['|'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['symbol'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['F'], G['ε'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['Z'], G['*'], ): [ Production(G['Z'], Sentence(G['*'])), ], + ( G['Z'], G.EOF, ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['|'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['('], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G[')'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['symbol'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['ε'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['A'], G['symbol'], ): [ Production(G['A'], Sentence(G['symbol'])), ], + ( G['A'], G['ε'], ): [ Production(G['A'], Sentence(G['ε'])), ], + ( G['A'], G['('], ): [ Production(G['A'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def parser(self): + firsts = self.firsts + follows = self.follows + M = build_parsing_table(self.G, firsts, follows) + parser = metodo_predictivo_no_recursivo(self.G, M) + return parser \ No newline at end of file diff --git a/src/cmp/nbpackage.py b/src/cmp/nbpackage.py new file mode 100644 index 000000000..e89c62ad3 --- /dev/null +++ b/src/cmp/nbpackage.py @@ -0,0 +1,87 @@ +import io, os, sys, types + +from IPython import get_ipython +from nbformat import read +from IPython.core.interactiveshell import InteractiveShell + +def find_notebook(fullname, path=None): + """find a notebook, given its fully qualified name and an optional path + + This turns "foo.bar" into "foo/bar.ipynb" + and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar + does not exist. + """ + name = fullname.rsplit('.', 1)[-1] + if not path: + path = [''] + for d in path: + nb_path = os.path.join(d, name + ".ipynb") + if os.path.isfile(nb_path): + return nb_path + # let import Notebook_Name find "Notebook Name.ipynb" + nb_path = nb_path.replace("_", " ") + if os.path.isfile(nb_path): + return nb_path + +class NotebookLoader(object): + """Module Loader for Jupyter Notebooks""" + def __init__(self, path=None): + self.shell = InteractiveShell.instance() + self.path = path + + def load_module(self, fullname): + """import a notebook as a module""" + path = find_notebook(fullname, self.path) + + print ("importing Jupyter notebook from %s" % path) + + # load the notebook object + with io.open(path, 'r', encoding='utf-8') as f: + nb = read(f, 4) + + + # create the module and add it to sys.modules + # if name in sys.modules: + # return sys.modules[name] + mod = types.ModuleType(fullname) + mod.__file__ = path + mod.__loader__ = self + mod.__dict__['get_ipython'] = get_ipython + sys.modules[fullname] = mod + + # extra work to ensure that magics that would affect the user_ns + # actually affect the notebook module's ns + save_user_ns = self.shell.user_ns + self.shell.user_ns = mod.__dict__ + + try: + for cell in nb.cells: + if cell.cell_type == 'code': + # transform the input to executable Python + code = self.shell.input_transformer_manager.transform_cell(cell.source) + # run the code in themodule + exec(code, mod.__dict__) + finally: + self.shell.user_ns = save_user_ns + return mod + +class NotebookFinder(object): + """Module finder that locates Jupyter Notebooks""" + def __init__(self): + self.loaders = {} + + def find_module(self, fullname, path=None): + nb_path = find_notebook(fullname, path) + if not nb_path: + return + + key = path + if path: + # lists aren't hashable + key = os.path.sep.join(path) + + if key not in self.loaders: + self.loaders[key] = NotebookLoader(path) + return self.loaders[key] + +sys.meta_path.append(NotebookFinder()) \ No newline at end of file diff --git a/src/cmp/pycompiler.py b/src/cmp/pycompiler.py new file mode 100644 index 000000000..2815e9ea3 --- /dev/null +++ b/src/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 = { 'eof': self.EOF } + + def NonTerminal(self, name, startSymbol = False): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = NonTerminal(name,self) + + if startSymbol: + + if self.startSymbol is None: + self.startSymbol = term + else: + raise Exception("Cannot define more than one start symbol.") + + self.nonTerminals.append(term) + self.symbDict[name] = term + return term + + def NonTerminals(self, names): + + ans = tuple((self.NonTerminal(x) for x in names.strip().split())) + + return ans + + + def Add_Production(self, production): + + if len(self.Productions) == 0: + self.pType = type(production) + + assert type(production) == self.pType, "The Productions most be of only 1 type." + + production.Left.productions.append(production) + self.Productions.append(production) + + + def Terminal(self, name): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = Terminal(name, self) + self.terminals.append(term) + self.symbDict[name] = term + return term + + def Terminals(self, names): + + ans = tuple((self.Terminal(x) for x in names.strip().split())) + + return ans + + + def __str__(self): + + mul = '%s, ' + + ans = 'Non-Terminals:\n\t' + + nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' + + ans += nonterminals % tuple(self.nonTerminals) + + ans += 'Terminals:\n\t' + + terminals = mul * (len(self.terminals)-1) + '%s\n' + + ans += terminals % tuple(self.terminals) + + ans += 'Productions:\n\t' + + ans += str(self.Productions) + + return ans + + def __getitem__(self, name): + try: + return self.symbDict[name] + except KeyError: + return None + + @property + def to_json(self): + + productions = [] + + for p in self.Productions: + head = p.Left.Name + + body = [] + + for s in p.Right: + body.append(s.Name) + + productions.append({'Head':head, 'Body':body}) + + d={'NonTerminals':[symb.Name for symb in self.nonTerminals], 'Terminals': [symb.Name for symb in self.terminals],\ + 'Productions':productions} + + # [{'Head':p.Left.Name, "Body": [s.Name for s in p.Right]} for p in self.Productions] + return json.dumps(d) + + @staticmethod + def from_json(data): + data = json.loads(data) + + G = Grammar() + dic = {'epsilon':G.Epsilon} + + for term in data['Terminals']: + dic[term] = G.Terminal(term) + + for noTerm in data['NonTerminals']: + dic[noTerm] = G.NonTerminal(noTerm) + + for p in data['Productions']: + head = p['Head'] + dic[head] %= Sentence(*[dic[term] for term in p['Body']]) + + return G + + def copy(self): + G = Grammar() + G.Productions = self.Productions.copy() + G.nonTerminals = self.nonTerminals.copy() + G.terminals = self.terminals.copy() + G.pType = self.pType + G.startSymbol = self.startSymbol + G.Epsilon = self.Epsilon + G.EOF = self.EOF + G.symbDict = self.symbDict.copy() + + return G + + @property + def IsAugmentedGrammar(self): + augmented = 0 + for left, right in self.Productions: + if self.startSymbol == left: + augmented += 1 + if augmented <= 1: + return True + else: + return False + + def AugmentedGrammar(self, force=False): + if not self.IsAugmentedGrammar or force: + + G = self.copy() + # S, self.startSymbol, SS = self.startSymbol, None, self.NonTerminal('S\'', True) + S = G.startSymbol + G.startSymbol = None + SS = G.NonTerminal('S\'', True) + if G.pType is AttributeProduction: + SS %= S + G.Epsilon, lambda x : x + else: + SS %= S + G.Epsilon + + return G + else: + return self.copy() + #endchange + +class Item: + + def __init__(self, production, pos, lookaheads=[]): + self.production = production + self.pos = pos + self.lookaheads = frozenset(look for look in lookaheads) + + def __str__(self): + s = str(self.production.Left) + " -> " + if len(self.production.Right) > 0: + for i,c in enumerate(self.production.Right): + if i == self.pos: + s += "." + s += str(self.production.Right[i]) + if self.pos == len(self.production.Right): + s += "." + else: + s += "." + s += ", " + str(self.lookaheads)[10:-1] + return s + + def __repr__(self): + return str(self) + + + def __eq__(self, other): + return ( + (self.pos == other.pos) and + (self.production == other.production) and + (set(self.lookaheads) == set(other.lookaheads)) + ) + + def __hash__(self): + return hash((self.production,self.pos,self.lookaheads)) + + @property + def IsReduceItem(self): + return len(self.production.Right) == self.pos + + @property + def NextSymbol(self): + if self.pos < len(self.production.Right): + return self.production.Right[self.pos] + else: + return None + + def NextItem(self): + if self.pos < len(self.production.Right): + return Item(self.production,self.pos+1,self.lookaheads) + else: + return None + + def Preview(self, skip=1): + unseen = self.production.Right[self.pos+skip:] + return [ unseen + (lookahead,) for lookahead in self.lookaheads ] + + def Center(self): + return Item(self.production, self.pos) \ No newline at end of file diff --git a/src/cmp/semantic.py b/src/cmp/semantic.py new file mode 100644 index 000000000..41b90fa85 --- /dev/null +++ b/src/cmp/semantic.py @@ -0,0 +1,239 @@ +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 + self.expected_param_types = dict() + self.expected_return_type = None + + 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 + self.child = 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 + parent.child = self + + def get_attribute(self, name:str, typex: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: + if self.parent.name != typex: + return self.parent.get_attribute(name, typex) + raise SemanticError() + + 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, self.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 __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) + + def join(self, other): + if self is ErrorType(): return ErrorType() + + current_parent = other + while not self.conforms_to(current_parent): + current_parent = current_parent.parent + + return current_parent + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + return True + + def bypass(self): + return True + + def get_method(self, name:str): + raise SemanticError(None) + + def __eq__(self, other): + return isinstance(other, Type) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + raise Exception('Invalid type: void type.') + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, VoidType) + +class IntType(Type): + def __init__(self): + Type.__init__(self, 'int') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, IntType) + +class 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): + if name == '': + return ErrorType() + + 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) + + self._return = None + + 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 not 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) diff --git a/src/cmp/tools/.ipynb_checkpoints/__init__-checkpoint.py b/src/cmp/tools/.ipynb_checkpoints/__init__-checkpoint.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cmp/tools/.ipynb_checkpoints/automata-checkpoint.py b/src/cmp/tools/.ipynb_checkpoints/automata-checkpoint.py new file mode 100644 index 000000000..59643cc5b --- /dev/null +++ b/src/cmp/tools/.ipynb_checkpoints/automata-checkpoint.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJzNWN1um0gUvucp6BWwRSjx1SrSrBRt4iR20rRJW9VCCI1hbE+LgTBDGjvKY+1r7DPtmR9gjIm3q+5KexPDzPn9zjdnDuGIEW4xVOF8SawHtMIMc15ZZ2hKNudVVVTWNcJZZm0QZTRnHOcJsQiiObeWKCO5tUIfNyVRojka44wRa4o+VjWxPiOcbyyMtrS0OEckr9ekwhz2tUBZIl6XGbFYEwGvNieWTddlUXG73KQFt8hTQkpuX8k16QckSsyYtaiKtZ2sy6DmNGON1u9FzjHNSXUPmSUZCNrvxqegk5KFHcc0pzyOXUayhQ/pcML8Bc0hbJ9DCIxyWuRM7FQcHXmgZgvRQIki9WMsgpT82ywpW4i76sFr1tcYcu0cNMuPRYLndYarDai0woYget6ePL8sisre2jS3masi8F5AFlbdU3/u+bdiy1AKKCdr5srobQCAACwP7q3vQPqcVHHseL5zlT/ijKZ2UmQZSYSeXSxsZd4Riv1QwtMonEfott3rgg9wmrpzbz+rIKUswVXqOo6nKkBKRrMijw3DRjFkyDpiuSCT7gXSxS5FRLCKN7ZdEV5X+xqhFIxCx4lASjPqzNBwdXTLCpcrGY+M5AJJDgZnBXfB2reUVsi5vnP8Na6WNEdHwbFI+kLkH+dFSlwl/048OpIYjs9WuCTIKTNgJSdPsJLhOcmQ4/jfacpX6MhfEbpcCb55uqyZT3VZG/bs1JQi588/HJsu4Ans2AROnU3FznAoWRNEQqskIxAT32TwOi+yVJrJWleKt8oiFE0EJCKyx0LiVnp/xcn475yMDzrRVkm6bKyei8fMH2u8qPoFRskuhEZeh31fq8W+PaYd6DqsFLMVSZVzTZsL3SQqUlYxe1zGHROGCKbI4gVJRYBdQgFeUpLI4td88avgvGabVJZdS7Wks/GpC23J+4m+pI/Jtbtx4TTUxCeeKJR87jcEuQjs8Uy9JRzZ345kdaGN9FQ8mbTRh76RzUko7URCA179QVeap6JBQYLBP8qs7SBJXVUkb3urhGhdPBJtZbMGUkkQgFbqzc4LPtQsQtNeZNYw33N2UDVUfqLwKOoYM20ZA/dox5Z9s0YiFTBkmdNtkwyvaL7s1LQt3QnsRCYlZdTRX8hMlaiEJFE9wUzLZKkOo3f2LBGJVMc1L9aYF3lTnA7dG3UpdbePFBDeUtSq7SC2jYyzcovSBrR+171FMsGboC5TsOnewpsO+sYyr4kkK1hd7UcpwpuhkMngWBcceHpCz3vLwMbvK5oReybcb9EsKItShjBwHxm5Dd1W26Y0F8L+gERTp4uGk0+qQE/ymrxQ3W4W4LIkuXrXqZuji/vLkyeRyBc45kWcLnAHgsg+Qc+QVYZeRyrs8pD0i8BTFtAUHclfFksqoM+uBKoT1t25hTADvQkKRRVn6ucVLIXGfNdWNwpIDG5Qj3NbX44N9uWBPG48g/iXCssEoKI5zJBq57KBeqK2L0WaS3fiNW8/nOylUpk05bnslUu9i5tLeSrQJKB5Sp4ayUs0CQtB+PYc2Em4hXj8uVxt+m/ejTFwGbx580YOXTunpFNEIiHLvkdyoTuRE5H8tk0PHLxH4moRqfv3ftJx670kk84bx3UOTHXxsY9HJpmAGekxOoa/I4SP9dT7NoWFGuFR+z6yesMnyPZmlCQ8BT0R+vMYHowZ4mVPeTSoPGqUR7vKSZj5MMihUAVYceFHBSeeR5HqWOmTfy+tuyHsw7IvFPTNA+LqKZL+WjzvT3YK9wVBBd6CJTU49qpj7urx3f4iz3gtT0wHYAfdSBTxuX5p6wJ3pDsRpfIzb7dEQPEEdHLMD5Tq6H9dqhbXFnlzltL4HfsDY7m5p7FV0BqFNq+mtqADDkYHHIz2HPw7tWua2PHg6ap7FfupCg2ehzr6T9CvjUJ03nqIHcZp+Kv9jLKvBXwfiY92gaSY8mHkqSlbxcqse+d3V8JUwPJJwnqOytIdunA0PWqBwJ1Ia/bK0FKr8VigcI1cdxYyMeSJyVKoztRnyrsiV/O1XDwXOEyEZ3caplEgvhhgbIPrm9NHIppyKsSmgfhIYj0LcuvaM2rxKZxEzQWjo/H6NREyyIhVYRveySTvhMlP7ZgfSQwlbvEa5u813eouYk4QU2Sg7v7CmLszMIgpC6KYBmtSwSeVcUf2b89hqSF7ElR9VQ9YUXPFdCo5/0PRddlP4XusqEs1fYnVlVj9ER6Ji1aHv/LUN8USpmoEA4Ten8MX3jd4mKLE6kb/nRN/COaDhbDsq505dtojE5Nj17PuptRv/y3CuXslw3tUDlT1NSRj/3oXY5Pwj9HOfxPmaBpeA+P7NBZ7X9GVHm/UnNZejbOQ+uPdgWb/hlRC6Ktlf0AhHUyAap4G7cdknxfg5KGNYro31PaC7iYeNQddef4Hf+Y/eNZffhu9yA=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cmp/tools/.ipynb_checkpoints/evaluation-checkpoint.py b/src/cmp/tools/.ipynb_checkpoints/evaluation-checkpoint.py new file mode 100644 index 000000000..d49df0ce5 --- /dev/null +++ b/src/cmp/tools/.ipynb_checkpoints/evaluation-checkpoint.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJxdUMFuwjAMvecrfGy3qoLrJN86YGViCO2GECrF1SLSpErcqf37OUWwsZPtZ/v5+TXetVC3Xd6NtWs7bciDbjvnGV4/FmqJmsmrE1oaWFnUQdvAla1JFbhxltQaDVm1QLJ9S75iUmdqgL4r00tx7CofKGmyMn1RoBuwjqEB56ekFAw8ce+tggaXSZMqKCWWEge8kSQnaWSRQ0EVAok2K1iZ5uwuZI88dpSJWmlfyWB4EJF03p37mrWzkSXT9ou8/HU+xgHCImrbZAZ/5xSMf6q8Yvb61DMFBYz74vCUrBOTPs/l5OV/vZ8d8N8J+U5e1tkOtIVFYrJ5PBn92OVv4ZN8q21lInR78LLXBx2giBBLjtO/hgYByASaZld4miy7b+0QV/k7NRyxLY6yGDO5swVhi54X0+bEj9vkknF6P3H3azXZFEekGWlmh7u1150HxkmQSP0BhJm25Q=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cmp/tools/.ipynb_checkpoints/parsing-checkpoint.py b/src/cmp/tools/.ipynb_checkpoints/parsing-checkpoint.py new file mode 100644 index 000000000..02d672f4b --- /dev/null +++ b/src/cmp/tools/.ipynb_checkpoints/parsing-checkpoint.py @@ -0,0 +1,10 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJytVVtr2zAUfvevEH2yqWfa14AeypKGlXlNL5SCMUZO5FTMlhRZbpZu++87ujiOm3QwGJRU56pzvu8cuVKiQUxTpYWoW8QaKZRGrK3ZkgaVMS4bmXSaDcbPgmvCOFUPVAcrWqGlaGSnaVGLJamLiqlWh/a3jUktX0g0CVCFnSZAEltlgKb4MFMYBUirHbiiBZbJl3YmW1YLHiD6Y0mldoZrUrc0QKxCC6OYJi3VBXWeJgMFszFUQqE5YhxJI6EUV9k8N6dp0skV0TRMIyNCIi40SpOlK6Xtk9kwVCpKvsOpT3t8oaK6UxxNR0C4VsO5a/zn7wDN8KPqoHBTV2nqmieAecM49GPrzcp8DEcZOW/tvLngj+MAnR/ht31hNUUzY5/1UNkkty7JQolVt9RMcJsDPePb5CuttDlLON+z9YsVrgCvZ4uXpwQ6B5W0qoGPXntUCEI3+ORUxNJaZ7/wNHkhalV4Nm569dWR2iNcjREWdS22AHHssB6P2GaEObXSX7DcnMJyk82TVhOlH3ZNKep3DvNkdnv9PxG/xxuPuIlmcbszCSjvGqoMEjJygMPAtjvYjm9DD86A7iBDu8udsKcN8pWYZjJm3nLI3oHxA7rcQxCCx/llDHfSKHKRQNVdv0pV6ZVQXFV+sErjkPuB2I0ltuxYvSokUS3j60KTsqZ7cmPP9pjkCo5OH8B+9xSTk7g/Y9LDvoBjj7oJoCagyhb5ZDTuafYc0zwhUlK+Ckn0fvCdHWfEwGr6hgynOx8uMTvlogd6Tt0z5ujwJg9ZaiFrqBYrUUhFVwxafRUFF4Wiyw4wfBWAXooNYx5Ef3aIWcHCyQY8xYAnNJTCRwAZt4lvkB0qTODRa+cdxdhR4FNLa5xT/BHrUCc42CbDrZ38J5zZnYvHWwmWpsEX8O8NZ0ZyC2kW396+xk+JFNK9SQRvs6bJ/bu/hi0ar5BRYkwm+2EGyV7aT3D/OUAHXwRkKjjHl8E7rVSM6/BsppRQCboq4csJPSZJcuaXxXNpgApGocNwLHCarWOSZxd+ee3bYGZJEb6mYU15uHDTHH26jO1f1Ff11A+V98hY7m9+21tOjNs/1u2lt/1sNsEfuhaMXQ=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) + +deprecated_metodo_predictivo_no_recursivo = metodo_predictivo_no_recursivo +def metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): + parser = deprecated_metodo_predictivo_no_recursivo(G, M, firsts, follows) + def updated(tokens): + return parser([t.token_type for t in tokens]) + return updated \ No newline at end of file diff --git a/src/cmp/tools/__init__.py b/src/cmp/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cmp/tools/automata.py b/src/cmp/tools/automata.py new file mode 100644 index 000000000..59643cc5b --- /dev/null +++ b/src/cmp/tools/automata.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJzNWN1um0gUvucp6BWwRSjx1SrSrBRt4iR20rRJW9VCCI1hbE+LgTBDGjvKY+1r7DPtmR9gjIm3q+5KexPDzPn9zjdnDuGIEW4xVOF8SawHtMIMc15ZZ2hKNudVVVTWNcJZZm0QZTRnHOcJsQiiObeWKCO5tUIfNyVRojka44wRa4o+VjWxPiOcbyyMtrS0OEckr9ekwhz2tUBZIl6XGbFYEwGvNieWTddlUXG73KQFt8hTQkpuX8k16QckSsyYtaiKtZ2sy6DmNGON1u9FzjHNSXUPmSUZCNrvxqegk5KFHcc0pzyOXUayhQ/pcML8Bc0hbJ9DCIxyWuRM7FQcHXmgZgvRQIki9WMsgpT82ywpW4i76sFr1tcYcu0cNMuPRYLndYarDai0woYget6ePL8sisre2jS3masi8F5AFlbdU3/u+bdiy1AKKCdr5srobQCAACwP7q3vQPqcVHHseL5zlT/ijKZ2UmQZSYSeXSxsZd4Riv1QwtMonEfott3rgg9wmrpzbz+rIKUswVXqOo6nKkBKRrMijw3DRjFkyDpiuSCT7gXSxS5FRLCKN7ZdEV5X+xqhFIxCx4lASjPqzNBwdXTLCpcrGY+M5AJJDgZnBXfB2reUVsi5vnP8Na6WNEdHwbFI+kLkH+dFSlwl/048OpIYjs9WuCTIKTNgJSdPsJLhOcmQ4/jfacpX6MhfEbpcCb55uqyZT3VZG/bs1JQi588/HJsu4Ans2AROnU3FznAoWRNEQqskIxAT32TwOi+yVJrJWleKt8oiFE0EJCKyx0LiVnp/xcn475yMDzrRVkm6bKyei8fMH2u8qPoFRskuhEZeh31fq8W+PaYd6DqsFLMVSZVzTZsL3SQqUlYxe1zGHROGCKbI4gVJRYBdQgFeUpLI4td88avgvGabVJZdS7Wks/GpC23J+4m+pI/Jtbtx4TTUxCeeKJR87jcEuQjs8Uy9JRzZ345kdaGN9FQ8mbTRh76RzUko7URCA179QVeap6JBQYLBP8qs7SBJXVUkb3urhGhdPBJtZbMGUkkQgFbqzc4LPtQsQtNeZNYw33N2UDVUfqLwKOoYM20ZA/dox5Z9s0YiFTBkmdNtkwyvaL7s1LQt3QnsRCYlZdTRX8hMlaiEJFE9wUzLZKkOo3f2LBGJVMc1L9aYF3lTnA7dG3UpdbePFBDeUtSq7SC2jYyzcovSBrR+171FMsGboC5TsOnewpsO+sYyr4kkK1hd7UcpwpuhkMngWBcceHpCz3vLwMbvK5oReybcb9EsKItShjBwHxm5Dd1W26Y0F8L+gERTp4uGk0+qQE/ymrxQ3W4W4LIkuXrXqZuji/vLkyeRyBc45kWcLnAHgsg+Qc+QVYZeRyrs8pD0i8BTFtAUHclfFksqoM+uBKoT1t25hTADvQkKRRVn6ucVLIXGfNdWNwpIDG5Qj3NbX44N9uWBPG48g/iXCssEoKI5zJBq57KBeqK2L0WaS3fiNW8/nOylUpk05bnslUu9i5tLeSrQJKB5Sp4ayUs0CQtB+PYc2Em4hXj8uVxt+m/ejTFwGbx580YOXTunpFNEIiHLvkdyoTuRE5H8tk0PHLxH4moRqfv3ftJx670kk84bx3UOTHXxsY9HJpmAGekxOoa/I4SP9dT7NoWFGuFR+z6yesMnyPZmlCQ8BT0R+vMYHowZ4mVPeTSoPGqUR7vKSZj5MMihUAVYceFHBSeeR5HqWOmTfy+tuyHsw7IvFPTNA+LqKZL+WjzvT3YK9wVBBd6CJTU49qpj7urx3f4iz3gtT0wHYAfdSBTxuX5p6wJ3pDsRpfIzb7dEQPEEdHLMD5Tq6H9dqhbXFnlzltL4HfsDY7m5p7FV0BqFNq+mtqADDkYHHIz2HPw7tWua2PHg6ap7FfupCg2ehzr6T9CvjUJ03nqIHcZp+Kv9jLKvBXwfiY92gaSY8mHkqSlbxcqse+d3V8JUwPJJwnqOytIdunA0PWqBwJ1Ia/bK0FKr8VigcI1cdxYyMeSJyVKoztRnyrsiV/O1XDwXOEyEZ3caplEgvhhgbIPrm9NHIppyKsSmgfhIYj0LcuvaM2rxKZxEzQWjo/H6NREyyIhVYRveySTvhMlP7ZgfSQwlbvEa5u813eouYk4QU2Sg7v7CmLszMIgpC6KYBmtSwSeVcUf2b89hqSF7ElR9VQ9YUXPFdCo5/0PRddlP4XusqEs1fYnVlVj9ER6Ji1aHv/LUN8USpmoEA4Ten8MX3jd4mKLE6kb/nRN/COaDhbDsq505dtojE5Nj17PuptRv/y3CuXslw3tUDlT1NSRj/3oXY5Pwj9HOfxPmaBpeA+P7NBZ7X9GVHm/UnNZejbOQ+uPdgWb/hlRC6Ktlf0AhHUyAap4G7cdknxfg5KGNYro31PaC7iYeNQddef4Hf+Y/eNZffhu9yA=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cmp/tools/evaluation.py b/src/cmp/tools/evaluation.py new file mode 100644 index 000000000..d49df0ce5 --- /dev/null +++ b/src/cmp/tools/evaluation.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJxdUMFuwjAMvecrfGy3qoLrJN86YGViCO2GECrF1SLSpErcqf37OUWwsZPtZ/v5+TXetVC3Xd6NtWs7bciDbjvnGV4/FmqJmsmrE1oaWFnUQdvAla1JFbhxltQaDVm1QLJ9S75iUmdqgL4r00tx7CofKGmyMn1RoBuwjqEB56ekFAw8ce+tggaXSZMqKCWWEge8kSQnaWSRQ0EVAok2K1iZ5uwuZI88dpSJWmlfyWB4EJF03p37mrWzkSXT9ou8/HU+xgHCImrbZAZ/5xSMf6q8Yvb61DMFBYz74vCUrBOTPs/l5OV/vZ8d8N8J+U5e1tkOtIVFYrJ5PBn92OVv4ZN8q21lInR78LLXBx2giBBLjtO/hgYByASaZld4miy7b+0QV/k7NRyxLY6yGDO5swVhi54X0+bEj9vkknF6P3H3azXZFEekGWlmh7u1150HxkmQSP0BhJm25Q=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cmp/tools/parsing.py b/src/cmp/tools/parsing.py new file mode 100644 index 000000000..d0275cbcb --- /dev/null +++ b/src/cmp/tools/parsing.py @@ -0,0 +1,16 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJytVVtr2zAUfvevEH2yqWfa14AeypKGlXlNL5SCMUZO5FTMlhRZbpZu++87ujiOm3QwGJRU56pzvu8cuVKiQUxTpYWoW8QaKZRGrK3ZkgaVMS4bmXSaDcbPgmvCOFUPVAcrWqGlaGSnaVGLJamLiqlWh/a3jUktX0g0CVCFnSZAEltlgKb4MFMYBUirHbiiBZbJl3YmW1YLHiD6Y0mldoZrUrc0QKxCC6OYJi3VBXWeJgMFszFUQqE5YhxJI6EUV9k8N6dp0skV0TRMIyNCIi40SpOlK6Xtk9kwVCpKvsOpT3t8oaK6UxxNR0C4VsO5a/zn7wDN8KPqoHBTV2nqmieAecM49GPrzcp8DEcZOW/tvLngj+MAnR/ht31hNUUzY5/1UNkkty7JQolVt9RMcJsDPePb5CuttDlLON+z9YsVrgCvZ4uXpwQ6B5W0qoGPXntUCEI3+ORUxNJaZ7/wNHkhalV4Nm569dWR2iNcjREWdS22AHHssB6P2GaEObXSX7DcnMJyk82TVhOlH3ZNKep3DvNkdnv9PxG/xxuPuIlmcbszCSjvGqoMEjJygMPAtjvYjm9DD86A7iBDu8udsKcN8pWYZjJm3nLI3oHxA7rcQxCCx/llDHfSKHKRQNVdv0pV6ZVQXFV+sErjkPuB2I0ltuxYvSokUS3j60KTsqZ7cmPP9pjkCo5OH8B+9xSTk7g/Y9LDvoBjj7oJoCagyhb5ZDTuafYc0zwhUlK+Ckn0fvCdHWfEwGr6hgynOx8uMTvlogd6Tt0z5ujwJg9ZaiFrqBYrUUhFVwxafRUFF4Wiyw4wfBWAXooNYx5Ef3aIWcHCyQY8xYAnNJTCRwAZt4lvkB0qTODRa+cdxdhR4FNLa5xT/BHrUCc42CbDrZ38J5zZnYvHWwmWpsEX8O8NZ0ZyC2kW396+xk+JFNK9SQRvs6bJ/bu/hi0ar5BRYkwm+2EGyV7aT3D/OUAHXwRkKjjHl8E7rVSM6/BsppRQCboq4csJPSZJcuaXxXNpgApGocNwLHCarWOSZxd+ee3bYGZJEb6mYU15uHDTHH26jO1f1Ff11A+V98hY7m9+21tOjNs/1u2lt/1sNsEfuhaMXQ=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) + +deprecated_metodo_predictivo_no_recursivo = metodo_predictivo_no_recursivo +def metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): + parser = deprecated_metodo_predictivo_no_recursivo(G, M, firsts, follows) + def updated(tokens): + return parser([t.token_type for t in tokens]) + return updated + +exec(zlib.decompress(base64.b64decode('eJx9U8GO2jAQvfMVZi9JtCFarqiuVLUsRUilWranCEUmGcBaY0e2s3TV7b/X9iQhpaiXxJ4Zv3nzZqYUzBiyOfK9fYKqKeE70wb0bEQ2X5ePzzQKv2hEnuZffnye0wj/zrBe0Wi9cocK9qQouOS2KGIDYp8u0lfQO2WAPjJhIHFoxDuyBV10xy6i/XdmVlquJP31uzMclFWDa7FruKiK2rHk8lBYthMQJy2JWz7/KhDQjBsg35RdnmoBJ5AWqrnWSvfPi5IJ0dVwTg9gC+OFKXRQZliMZeULzR+27lw22ihNH9xRNbZuLM29WdWgma/F4P185ALIs27AA3gECzTg5JOpDyBCqRd2BFbRc46gwcz3fwk2qzWXNg4v0+jDZDJ5f3efj1HavZptE3wXhyRpj5tIZQmXQ6EDF4KQ/4QnA+ddkCojn3ZKW6dulmV36NdgGy2dsNI3kSBuatmBDvLkV9hdZW27MTSMGjK6qJexugZZxZcITBsEuKd5D+lTBti2I/d06m8grtPgBP83D4ZgImxq53ZJ0BxS7lT1Rp0pWPZKE/N22inhRdbgGmagin1MgtmQdFarOkYQ4pYPtB3aHcmA0RV5PSUkLES/Gq2wvaa9LoGfj9jeVmG9mo1uUbrJKOxu5mzabi7s2lABEsfRRU6HI4HK+Tb7wbteJ8fJQIwx6aUPCdI1uCbt1s5/llB7dxwt5SsTvGqLGY/HUTL6AyImjec='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) + +exec(zlib.decompress(base64.b64decode('eJyFVltP4zoQfu+v8D41OcfHWl6RLJ0KekEgqLg+dKtiGrf1ktiR7RS6iP9+ZuykSYE9ywOKLzPzzTffjLuypiDLomSVV7kjqiiN9eTEaC+UlvZG+t6queKNyR0rhXVKr5urS1OUlZeLlbLOO9osc7MUedxsHZQ7PFa5tI31mZdFL5MrIl9LobMkozo97pEdz9ilfPU3u+LJ5D2iVmRHlCOXRktiLNHGkx07c7C+lbZQWuRgRaz0ldWzeY/c824KSdojKzAbEqVJxqZWbpV8STASeeZfQE40HYINuWdVmQkvk2dYCeckQMbY92wZ3buFLJ3Kje41wTGjpLQmo9/pfYpRcYGBdwy/qqVXRrt5yBpDW+lcMkAsOX97j0DD/QHCWwETJ1J7aTEJ4u0OdyG/fLaCPIG3pSw9OZe7obXGhkM84vfcxcTbJDKWG/MsNlJkLm0AvwXArx1sFBbGUTR/TkMGr/QZAeVMwV2XpO8RfG5cZYE3e5QMYt0mh7T/NYAwV/zWVrJHXjZQeHKFCK/4SOQO9oj4VKc2/0lIRjDQgQRpdBSSBh+TJi+xT6YldJIGjGvjTQ1wSqNEOYqI/qycXzxLq2UewSD8usKdMxRbNEP5YemDdf8xbj6SAu6SJ4lF3qpMZijVx0/OH/s9MuDQB7+kRl6jugPzaVtvtO3qnvNpm1k47SKT4PdDDSKotG04UXlTCC+adrvxwBctqhyaHThfQGw4BnEFsp4qlWeLi+ujRW1ndDLu8JJLWDPnha0BdgWdcn5E+2MrikLYPS2iWheo3gwI0PxwVoBv2JyN2fBqND/UQdiD0zP+23iz7yB/wwOHZ9BrrbR5NKcoE98hfWbmKUq0y5kHNQHFPBCTtHcnKUXVwtmWzzxE2vA3f2zfGxlvUZsXfAudUgbV3vHN7GJey3eK5RwzX48m9/eY6XZSuaDrQxwXAQcha75X7AQU2xVSjYegDlCI6+CG4CBSGhusnQ7kBdCsEc2X8+FD7HUdu7b6Hy7gb8tEWWI7rsP6joksW3grtFNYlmTKLkUh6QuyysC6lVjyhexaeds/PDM3G7Xy1xKqL6dwAopd5iBLAmqN6+TTDVQuynoRdV07XHjxlMvkIQz/MX9gYzZoRFqrN2nStfzrlqjLrOgpFlrqqpAWOQshQ4Ee2FbaJ+PkcWmV9omi/R++D//0D0/67KdROnHeJq9xvqKbU1S6lyle6gdyT5nKXrmqo4VYsYCShyP83E+P2jwWOAySMxfZwBaJ26SE16TtobgHd0t2IVeeHzZbbQKpLKxcK4clfGAiPhGJpLFHKexdnVOcimlUSBhMjfG+G7pvT3P4W9fT4PZ6eHp3MqRl7bfjdvrh5wEJnXPK1qDWKMC04SfkNwUuur8T/hz7ZnI2uqXrr1I6NMR2rc2wI/7FIqhlIf3GZLX89rdHFIgKEqkH6nloZGBnhO/MaHY+5/yS9oOS/4nFw4P41WxAw69ytfTfvn2DoRqtLnv/ATLgLos='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cmp/tools/regex.py b/src/cmp/tools/regex.py new file mode 100644 index 000000000..85d74325a --- /dev/null +++ b/src/cmp/tools/regex.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJytVsFu2zgQvesreFhDVE0IdY8BCNTZtVRg2yyaOKgdwxAYiYrZSKRK0qnttp+1v7HftCQlWbLWTXPYgwzyzXDem9Fw5FyKEqRlFRKlASsrITWYalGy9EpkFF0yTuTeLW/blZe3Z6p9KsqKFVS2R2NJypLIzkULUaiQbE1IoknjBv+IpujKPC2epIVQW0l7gOAp0ZQTzQTv4JJxVrLDAN1yu+U5SbRIspwEQ376RIqtO9QKbRCaVEQqOvS3IOMPrXNJtchEUkmasVSzJ5FwkUiabo3Xk+gObzUrVHtoLh4p975gk6uXFkQpMKsUKwS3NYRdjYMLD2Q0PyqCiha5BYGkeis5+AKVNrjCE5Sb11EovHq9RloSrphNSeFvPwJP4l74hvBmX96L4sV8CttlWNDdGfI3R/LJgBy+Riq4mBgNKe4YGwm/1y/WaTh2kGV7a+Oy1JR2I7JakaiotIKsMNqvwLBNGg/vI+6FbwhvbS84uq53f8FXuHBI/pzXNdjAz7vGR642Wde0/zf5yVUYiogi3LF6NrCkD3RnLoLpPnagEmq60yhG6pFVydcNM++yIql5oXNZ823wau2BOMbfdheuZ+EOxavdOsiFBDvA+Mr/7iP/lXmgeQLz/PO3v/7hAetwMA7AUljlLAdDGkB4Bg4hU24LXYLAZKQZ31Kz1nLvoM84jlcHIwTQXUorDf6k+5mUQjbWWtnBKPOV6zF/HRiL68miDrEJSVVRnsHPxnLc1Af933wUh7O/osDYmgJvXLnut6zIkod6bjl9MY7bnQdmOA6vBJ9TWVoq6M985CrngTmK0BQt0BLdnTop6M9BBKZgAZbgzjeuFfqAbtAlIujWuPb8voNXAIIA1DkBU1jLOcLz8QIVpLzPCNggdaFWb9bIMNBTcGLqtRjhajzr49dwYweEPRM4u0m8Hg19L+tjchjhaLx8IdXS6OqjUdQyTSzT8lmmaISn47sXMt2N8Ic++tERBc7wDMd0hEkfTWEjzRhu+wbZM9yMZ+PLoa5jk8RejAct4r3Hz38QYBw0A+Ha3sVm3iaJ+XbpJHHzFrlb+t9LGZmuqKfAJzeM7SJ0vtj9un0zGQTHn8Ja2xGBzitoGVNzOVpGe0lPIzcp9gIaqlQ82LnhxkZwdnKdpXwulQ0ezqT6yJmhVB+yFxu/hxu7mOPTTzXMkcPf4Xl4/IRZYIG7PwDwnUUe8dn/DXARdMk/ev8C0IsPHg=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cmp/utils.py b/src/cmp/utils.py new file mode 100644 index 000000000..a839ff5ee --- /dev/null +++ b/src/cmp/utils.py @@ -0,0 +1,219 @@ +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) \ No newline at end of file diff --git a/src/cmp/visitor.py b/src/cmp/visitor.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/src/cmp/visitor.py @@ -0,0 +1,80 @@ +# The MIT License (MIT) +# +# Copyright (c) 2013 Curtis Schlak +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import inspect + +__all__ = ['on', 'when'] + +def on(param_name): + def f(fn): + dispatcher = Dispatcher(param_name, fn) + return dispatcher + return f + + +def when(param_type): + def f(fn): + frame = inspect.currentframe().f_back + func_name = fn.func_name if 'func_name' in dir(fn) else fn.__name__ + dispatcher = frame.f_locals[func_name] + if not isinstance(dispatcher, Dispatcher): + dispatcher = dispatcher.dispatcher + dispatcher.add_target(param_type, fn) + def ff(*args, **kw): + return dispatcher(*args, **kw) + ff.dispatcher = dispatcher + return ff + return f + + +class Dispatcher(object): + def __init__(self, param_name, fn): + frame = inspect.currentframe().f_back.f_back + top_level = frame.f_locals == frame.f_globals + self.param_index = self.__argspec(fn).args.index(param_name) + self.param_name = param_name + self.targets = {} + + def __call__(self, *args, **kw): + typ = args[self.param_index].__class__ + d = self.targets.get(typ) + if d is not None: + return d(*args, **kw) + else: + issub = issubclass + t = self.targets + ks = t.keys() + ans = [t[k](*args, **kw) for k in ks if issub(typ, k)] + if len(ans) == 1: + return ans.pop() + return ans + + def add_target(self, typ, target): + self.targets[typ] = target + + @staticmethod + def __argspec(fn): + # Support for Python 3 type hints requires inspect.getfullargspec + if hasattr(inspect, 'getfullargspec'): + return inspect.getfullargspec(fn) + else: + return inspect.getargspec(fn) diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f9..5a4697ff8 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,11 +1,13 @@ -# Incluya aquí las instrucciones necesarias para ejecutar su compilador +# Incluya aquí las instrucciones necesarias para ejecutar su compilador INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips # Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos +echo "Cool Compiler 2021 v1" # TODO: Recuerde cambiar estas +echo "Copyright (c) 2021: Alejandro Campos, Darian Dominguez" # TODO: líneas a los valores correctos + +FILE="main.py" # Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python ${FILE} $INPUT_FILE $OUTPUT_FILE diff --git a/src/main.py b/src/main.py new file mode 100644 index 000000000..b479553fe --- /dev/null +++ b/src/main.py @@ -0,0 +1,94 @@ +import os +import sys +from pathlib import Path +from utils.COOL_Grammar import build_COOL_Grammar +from utils.COOL_Lexer import COOL_Lexer +from utils.code_generation.mips.CIL_to_MIPS import CILToMIPS +from utils.code_generation.mips.print_MIPS_AST import PrintMIPS +from utils.parser.COOL_parser import COOL_Parser +from cmp.evaluation import evaluate_reverse_parse +from cmp.semantic import Context +from utils.semantic_check.type_collector import TypeCollector +from utils.semantic_check.type_builder import TypeBuilder +from utils.semantic_check.type_checker import TypeChecker +from utils.code_generation.cil.COOL_to_CIL import COOLtoCIL +from utils.code_generation.cil.print_CIL_AST import get_formatter + +if __name__ == "__main__": + add = "codegen/" + _file = "primes.cl" + + path: str = f"{Path.cwd()}/tests/{add}" if os.path.exists( + f"{Path.cwd()}/tests/{add}") else f"{Path.cwd()}/../tests/{add}{_file}" + + _in = sys.argv[1] if len(sys.argv) > 1 else path + + with open(_in) as file: + code = file.read() + + G = build_COOL_Grammar() + + # lexer + lexer = COOL_Lexer() + tokens = lexer(code) + + if lexer.errors: + for error in lexer.errors: + print(error) + raise Exception() + + # parser + parser = COOL_Parser(G) + + derivation, operations = parser(tokens) + + if parser.error: + print(parser.error) + raise Exception() + + # ast + ast = evaluate_reverse_parse( + G, derivation, operations, lexer.fixed_tokens(tokens)) + + # chequeo semantico + context = Context() + semantic_errors = [] + + collector = TypeCollector(context, semantic_errors) + collector.visit(ast) + + builder = TypeBuilder(context, semantic_errors) + builder.visit(ast) + + checker = TypeChecker(context, semantic_errors) + scope = checker.visit(ast) + + if semantic_errors: + for error in semantic_errors: + print(error) + raise Exception() + + # generacion de codigo + cil_visitor = COOLtoCIL(context) + cil_ast = cil_visitor.visit(ast, scope) + + # print(get_formatter()(cil_ast)) + + mips_visitor = CILToMIPS() + mips_ast = mips_visitor.visit(cil_ast) + + print_mips = PrintMIPS() + mips = print_mips.visit(mips_ast) + + # print(mips) + + _file = sys.argv[1] if len(sys.argv) > 1 else _file + + output = _file.split(".") + output[-1] = "mips" + output = ".".join(output) + + with open(output, 'w') as f: + f.write(mips) + + exit(0) diff --git a/src/utils/COOL_Grammar.py b/src/utils/COOL_Grammar.py new file mode 100644 index 000000000..0cae24564 --- /dev/null +++ b/src/utils/COOL_Grammar.py @@ -0,0 +1,112 @@ +from cmp.pycompiler import Grammar +from utils.ast.AST_Nodes import ast_nodes as node + +def build_COOL_Grammar(): + # gramatica + G = Grammar() + + # no terminales + program = G.NonTerminal('', startSymbol=True) + class_list, def_class, param_list, param = G.NonTerminals(' ') + feature_list, def_attr, def_meth, arg_list = G.NonTerminals(' ') + expr, expr_list, id_list, case_list, comp = G.NonTerminals(' ') + expr, arith, term, factor, atom, dispatch = G.NonTerminals(' ') + nonEmpty_argList, nonEmpty_paramList = G.NonTerminals(' ') + + #terminales + classx, let, inx = G.Terminals('class let in') + semi, colon, arrow, comma, dot, opar, cpar, ocur, ccur, darrow = G.Terminals('; : <- , . ( ) { } =>') + equal, plus, minus, star, div, less, lesse, tilde, at = G.Terminals('= + - * / < <= ~ @') + typex, inherits, idx, whilex, loop, pool, num, new = G.Terminals('type inherits id while loop pool int new') + ifx, then, elsex, fi, case, esac, of, notx, isvoid = G.Terminals('if then else fi case esac of not isvoid') + true, false, string = G.Terminals('true false string') + + + #produciones + program %= class_list, lambda h,s: node.ProgramNode(s[1]) + + class_list %= def_class + semi, lambda h,s: [s[1]] + class_list %= def_class + semi + class_list, lambda h,s: [s[1]] + s[3] + + def_class %= classx + typex + ocur + feature_list + ccur, lambda h,s: node.ClassDeclarationNode(s[2],s[4],s[1]) + def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur, lambda h,s: node.ClassDeclarationNode(s[2],s[6],s[1],s[4]) + + feature_list %= def_attr + semi + feature_list, lambda h,s: [s[1]] + s[3] + feature_list %= def_meth + semi + feature_list, lambda h,s: [s[1]] + s[3] + feature_list %= G.Epsilon, lambda h,s: [] + + def_attr %= idx + colon + typex, lambda h,s: node.AttrDeclarationNode(s[1],s[3]) + def_attr %= idx + colon + typex + arrow + expr, lambda h,s: node.AttrDeclarationNode(s[1],s[3],s[4],s[5]) + + def_meth %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur, lambda h,s: node.MethDeclarationNode(s[1],s[3],s[6],s[8],s[7]) + + param_list %= G.Epsilon, lambda h,s: [] + param_list %= nonEmpty_paramList, lambda h,s: s[1] + + nonEmpty_paramList %= param, lambda h,s: [s[1]] + nonEmpty_paramList %= param + comma + nonEmpty_paramList, lambda h,s: [s[1]] + s[3] + + param %= idx + colon + typex, lambda h,s: [s[1],s[3]] + + expr %= idx + arrow + expr, lambda h,s: node.AssignNode(s[1],s[3],s[2]) + expr %= whilex + expr + loop + expr + pool, lambda h,s: node.WhileNode(s[2],s[4],s[1]) + + expr %= ocur + expr_list + ccur, lambda h,s: node.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] + + expr %= let + id_list + inx + expr, lambda h,s: node.LetNode(s[2],s[4],s[1]) + + id_list %= idx + colon + typex, lambda h,s: [(s[1],s[3],None)] + id_list %= idx + colon + typex + arrow + expr, lambda h,s: [(s[1],s[3],s[5])] + id_list %= idx + colon + typex + comma + id_list, lambda h,s: [(s[1],s[3],None)] + s[5] + id_list %= idx + colon + typex + arrow + expr + comma + id_list, lambda h,s: [(s[1],s[3],s[5])] + s[7] + + expr %= case + expr + of + case_list + esac, lambda h,s: node.CaseNode(s[2],s[4],s[1]) + + case_list %= idx + colon + typex + darrow + expr + semi, lambda h,s: [(s[1],s[3],s[5])] + case_list %= idx + colon + typex + darrow + expr + semi + case_list, lambda h,s: [(s[1],s[3],s[5])] + s[7] + + expr %= notx + expr, lambda h,s: node.NotNode(s[2],s[1]) + expr %= comp + equal + expr, lambda h,s: node.EqualNode(s[1],s[3],s[2]) + + expr %= comp, lambda h,s: s[1] + + comp %= comp + less + arith, lambda h,s: node.LessThanNode(s[1],s[3],s[2]) + comp %= comp + lesse + arith, lambda h,s: node.LessEqualNode(s[1],s[3],s[2]) + comp %= arith, lambda h,s: s[1] + + arith %= arith + plus + term, lambda h,s: node.PlusNode(s[1],s[3],s[2]) + arith %= arith + minus + term, lambda h,s: node.MinusNode(s[1],s[3],s[2]) + arith %= term, lambda h,s:s[1] + + term %= term + star + factor, lambda h,s: node.StarNode(s[1],s[3],s[2]) + term %= term + div + factor, lambda h,s: node.DivNode(s[1],s[3],s[2]) + term %= factor, lambda h,s: s[1] + + factor %= isvoid + factor, lambda h,s: node.IsVoidNode(s[2]) + factor %= tilde + factor, lambda h,s: node.ComplementNode(s[2], s[1]) + factor %= atom, lambda h,s: s[1] + + atom %= true, lambda h,s: node.ConstantBoolNode(s[1]) + atom %= false, lambda h,s: node.ConstantBoolNode(s[1]) + atom %= string, lambda h,s: node.ConstantStringNode(s[1]) + atom %= num, lambda h,s: node.ConstantNumNode(s[1]) + atom %= idx, lambda h,s: node.VariableNode(s[1]) + atom %= new + typex, lambda h,s: node.InstantiateNode(s[2],s[1]) + atom %= ifx + expr + then + expr + elsex + expr + fi, lambda h,s: node.IfThenElseNode(s[2],s[4],s[6],s[1]) + atom %= opar + expr + cpar, lambda h,s: s[2] + atom %= dispatch, lambda h,s: s[1] + + dispatch %= atom + dot + idx + opar + arg_list + cpar, lambda h,s: node.CallNode(s[3],s[5],s[1]) + dispatch %= idx + opar + arg_list + cpar, lambda h,s: node.CallNode(s[1],s[3]) + dispatch %= atom + at + typex + dot + idx + opar + arg_list + cpar, lambda h,s: node.CallNode(s[5],s[7],s[1],s[3]) + + arg_list %= G.Epsilon, lambda h,s: [] + arg_list %= nonEmpty_argList, lambda h,s: s[1] + + nonEmpty_argList %= expr, lambda h,s: [s[1]] + nonEmpty_argList %= expr + comma + nonEmpty_argList, lambda h,s: [s[1]] + s[3] + + return G \ No newline at end of file diff --git a/src/utils/COOL_Lexer.py b/src/utils/COOL_Lexer.py new file mode 100644 index 000000000..8aabc5f11 --- /dev/null +++ b/src/utils/COOL_Lexer.py @@ -0,0 +1,233 @@ +import re + +''' +Recibe como parametros el lexema, tipo, linea y columna donde se encuentra +''' + + +class Token: + + def __init__(self, lex, ttype, line, column): + + self.lex = lex + self.token_type = ttype + self.line = line + self.column = column + self.next_token = None + + def __str__(self): + return f'({self.line}, {self.column}) - {self.token_type}: {self.lex}' + + +''' +El lexer es el resultado de la union de todas las expresiones regulares que forman +el lenguaje +''' + + +class Lexer: + + def __init__(self, table, keywords, ignored_tokens, tokens_toFix, eof): + self.line = 1 + self.column = 0 + self.table = table + self.keywords = keywords + self.ignored_tokens = ignored_tokens + self.tokens_toFix = tokens_toFix + self.regex = self._build_regex(table) + self.errors = [] + self.eof = eof + + def tokenize(self, text): + while len(text) > 0: + + match = self.regex.match(text) + error_token = '' + + while not match: + error_token += text[0] + text = text[1:] + if len(text) <= 0: + break + match = self.regex.match(text) + + if error_token: + self.errors.append( + f'({self.line}, {self.column + 1}) - LexicographicError: ERROR "{error_token}"') + self.column += len(error_token) + if len(text) <= 0: + continue + + lexeme = match.group() + self.column += len(lexeme) if lexeme != '\t' else 4 + + if lexeme == '\n': + self.line += 1 + self.column = 0 + + # COMMENTS + elif lexeme == '(*': + text = text[2:] + openx = 1 + + while len(text) > 0: + lexeme += text[0] + text = text[1:] + self.column += 1 + + if lexeme[-1] == '\n': + self.line += 1 + self.column = 0 + + elif lexeme[-2:] == '(*': + openx += 1 + + elif lexeme[-2:] == '*)': + openx -= 1 + if openx == 0: + break + + else: + self.errors.append( + f'({self.line}, {self.column + 1}) - LexicographicError: EOF in comment') + + # STRINGS + elif lexeme == '"': + text = text[1:] + while len(text) > 0: + c = text[0] + text = text[1:] + self.column += 1 + + if c == '\\': + if text[0] == '\n': + lexeme += '\n' + self.line += 1 + self.column = 0 + + elif text[0] == 'b': + lexeme += '\b' + + elif text[0] == 't': + lexeme += '\t' + + elif text[0] == 'n': + lexeme += '\n' + + elif text[0] == 'f': + lexeme += '\f' + + else: + lexeme += text[0] + + text = text[1:] + self.column += 1 + + elif c == '\n': + self.errors.append( + f'({self.line}, {self.column}) - LexicographicError: Unterminated string constant') + self.line += 1 + self.column = 0 + break + + elif c == '\0': + self.errors.append( + f'({self.line}, {self.column}) - LexicographicError: String contains null character') + + else: + lexeme += c + if c == '"': + break + + else: + self.errors.append( + f'({self.line}, {self.column}) - LexicographicError: EOF in string constant') + + token_type = match.lastgroup if lexeme.lower( + ) not in self.keywords and match.lastgroup is not None else match.group().lower() + + yield lexeme, token_type, self.line, self.column - len(lexeme) + 1 + + text = text[match.end( + ):] if lexeme[:2] != '(*' and lexeme[0] != '"' else text + + yield '$', self.eof, 0, 0 + + def _build_regex(sef, table): + return re.compile('|'.join([f'(?P<{name}>{regex})' if name != regex else f'({name})' for name, regex in table.items()])) + + def __call__(self, text): + return [Token(lex, ttype, line, column) for lex, ttype, line, column in self.tokenize(text) if ttype not in self.ignored_tokens] + + def fixed_tokens(self, tokens): + for i, token in enumerate(tokens[:(len(tokens) - 1)]): + token.next_token = tokens[i+1] + return tokens + + +''' +Esta clase guarda las propiedades sintacticas de COOL +''' + + +class COOL_Lexer(Lexer): + def __init__(self): + self.regexs = { + 'id': r'[a-z][a-zA-Z0-9_]*', + 'type': r'[A-Z][a-zA-Z0-9_]*', + 'string': r'\"', + 'int': r'\d+', + 'comment': r'(\(\*)|--.*', + 'newline': r'\n', + 'whitespace': r' +', + 'tabulation': r'\t', + 'inherits': r'inherits', + 'isvoid': r'isvoid', + 'class': r'class', + 'while': r'while', + 'false': r'false', + 'then': r'then', + 'else': r'else', + 'loop': r'loop', + 'pool': r'pool', + 'case': r'case', + 'esac': r'esac', + 'true': r'true', + '<\-': r'<\-', + 'let': r'let', + 'new': r'new', + 'not': r'not', + '\{': r'\{', + '\}': r'\}', + '\(': r'\(', + '\)': r'\)', + '\.': r'\.', + '=>': r'=>', + 'if': r'if', + 'fi': r'fi', + 'in': r'in', + 'of': r'of', + '\+': r'\+', + '\-': r'\-', + '\*': r'\*', + '<=': r'<=', + '\~': r'\~', + ',': r',', + ':': r':', + ';': r';', + '@': r'@', + '/': r'/', + '<': r'<', + '=': r'='} + + self.keywords = ['inherits', 'isvoid', 'class', 'while', 'false', 'then', 'else', 'loop', + 'pool', 'case', 'esac', 'true', 'let', 'new', 'not', 'if', 'fi', 'in', 'of'] + + self.ignored_tokens = ['newline', + 'whitespace', 'tabulation', 'comment'] + + self.tokens_toFix = ['inherits', 'isvoid', 'class', 'while', 'then', 'else', 'loop', 'case', + 'let', 'new', 'not', 'if', 'in', 'of', '<-'] + + Lexer.__init__(self, self.regexs, self.keywords, + self.ignored_tokens, self.tokens_toFix, 'eof') diff --git a/src/utils/ast/AST_Nodes.py b/src/utils/ast/AST_Nodes.py new file mode 100644 index 000000000..bdc6d1925 --- /dev/null +++ b/src/utils/ast/AST_Nodes.py @@ -0,0 +1,176 @@ +from utils.COOL_Lexer import Token + + +class ast_nodes: + + class Node: + line = 0 + column = 0 + + 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, token, parent=None): + self.id = idx.lex + self.line = idx.line + self.column = idx.column + self.classt_line = token.line + self.classt_column = token.column + 4 + self.parent = parent.lex if parent else parent + self.parent_column = parent.column if parent else None + self.features = features + + class MethDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body, ocurToken): + self.id = idx.lex + self.line = idx.line + self.column = idx.column + self.body_line = ocurToken.next_token.line + self.body_column = ocurToken.next_token.column + self.params = [[item.lex if isinstance( + item, Token) else item for item in _list] for _list in params] + self.type = return_type.lex + self.body = body + + class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, arrowToken=None, expr=None): + self.id = idx.lex + self.line = arrowToken.next_token.line if expr else typex.line + self.column = arrowToken.next_token.column if expr else typex.column + self.type = typex.lex + self.expr = expr + + class AssignNode(ExpressionNode): + def __init__(self, token, expr, token_pos): + self.id = token.lex + self.line = token_pos.line + self.column = token_pos.column + self.expr = expr + + class CallNode(ExpressionNode): + def __init__(self, token, args, obj=None, typex=None): + self.obj = obj.lex if isinstance(obj, Token) else obj + self.type = typex.lex if typex else typex + self.id = token.lex + self.line = obj.line if obj else token.line + self.column = obj.column if obj else token.column + self.args = args + + class IfThenElseNode(ExpressionNode): + def __init__(self, if_expr, then_expr, else_expr, token): + self.if_expr = if_expr + self.then_expr = then_expr + self.else_expr = else_expr + self.line = token.next_token.line + self.column = token.next_token.column + + class WhileNode(ExpressionNode): + def __init__(self, conditional_expr, loop_expr, token): + self.conditional_expr = conditional_expr + self.loop_expr = loop_expr + self.line = token.line + self.column = token.column + + class BlockNode(ExpressionNode): + def __init__(self, expr_list, token): + self.expr_list = expr_list + self.line = token.line + self.column = token.column + + class LetNode(ExpressionNode): + def __init__(self, identifiers, in_expr, token): + self.identifiers = [tuple([item.lex if isinstance( + item, Token) else item for item in _list]) for _list in identifiers] + self.in_expr = in_expr + self.line = token.line + self.column = token.column + + class CaseNode(ExpressionNode): + def __init__(self, predicate, branches, token): + self.predicate = predicate + self.branches = [] + self.branchesPos = [] + for branch in branches: + self.branches.append( + tuple([item.lex if isinstance(item, Token) else item for item in branch])) + _, typex, _ = branch + self.branchesPos.append((typex.line, typex.column)) + self.line = token.line + self.column = token.column + + class NotNode(ExpressionNode): + def __init__(self, expr, token): + self.expr = expr + self.line = token.next_token.line + self.column = token.next_token.column + + class AtomicNode(ExpressionNode): + def __init__(self, expr): + self.lex = expr.lex if isinstance(expr, Token) else expr + self.line = expr.line + self.column = expr.column + + class BinaryNode(ExpressionNode): + def __init__(self, left, right, token): + self.left = left + self.right = right + self.line = token.line + self.column = token.column + + class ConstantNumNode(AtomicNode): + pass + + class ConstantBoolNode(AtomicNode): + pass + + class ConstantStringNode(AtomicNode): + def __init__(self, expr): + expr.lex = expr.lex[1:-1] + super().__init__(expr) + + class VariableNode(AtomicNode): + pass + + class InstantiateNode(AtomicNode): + def __init__(self, typex, token): + self.lex = typex.lex + self.line = token.next_token.line + self.column = token.next_token.column + + class IsVoidNode(AtomicNode): + pass + + class ComplementNode(AtomicNode): + def __init__(self, expr, token): + self.lex = expr.lex if isinstance(expr, Token) else expr + self.line = token.next_token.line + self.column = token.next_token.column + + class PlusNode(BinaryNode): + pass + + class MinusNode(BinaryNode): + pass + + class StarNode(BinaryNode): + pass + + class DivNode(BinaryNode): + pass + + class LessThanNode(BinaryNode): + pass + + class LessEqualNode(BinaryNode): + pass + + class EqualNode(BinaryNode): + pass diff --git a/src/utils/code_generation/cil/AST_CIL.py b/src/utils/code_generation/cil/AST_CIL.py new file mode 100644 index 000000000..446efe0cb --- /dev/null +++ b/src/utils/code_generation/cil/AST_CIL.py @@ -0,0 +1,204 @@ +class cil_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, idx): + self.id = idx + self.attributes = [] + self.methods = [] + + class DataNode(Node): + def __init__(self, idx, value): + self.id = idx + self.value = value + + class FunctionNode(Node): + def __init__(self, idx, params, localvars, instructions): + self.id = idx + self.params = params + self.localvars = localvars + self.instructions = instructions + self.ids = dict() + self.labels_count = 0 + + class ParamNode(Node): + def __init__(self, idx): + self.id = idx + + class LocalNode(Node): + def __init__(self, idx): + self.id = idx + + class InstructionNode(Node): + def __init__(self): + self.leader = False + + class AssignNode(InstructionNode): + def __init__(self, left, right): + self.left = left + self.right = right + + class ArithmeticNode(InstructionNode): + def __init__(self, dest, op_l, op_r): + self.dest = dest + self.op_l = op_l + self.op_r = op_r + + class PlusNode(ArithmeticNode): + pass + + class MinusNode(ArithmeticNode): + pass + + class StarNode(ArithmeticNode): + pass + + class DivNode(ArithmeticNode): + pass + + class LessThanNode(ArithmeticNode): + pass + + class LessEqualNode(ArithmeticNode): + pass + + class EqualNode(ArithmeticNode): + pass + + class EqualStrNode(EqualNode): + pass + + class GetAttrNode(InstructionNode): + def __init__(self, dest, idx, attr, computed_type): + self.dest = dest + self.id = idx + self.attr = attr + self.computed_type = computed_type + + class SetAttrNode(InstructionNode): + def __init__(self, idx, attr, value, computed_type): + self.id = idx + self.attr = attr + self.value = value + self.computed_type = computed_type + + class AllocateNode(InstructionNode): + def __init__(self, typex, dest): + self.dest = dest + self.type = typex + + class TypeOfNode(InstructionNode): + def __init__(self, idx, dest): + self.id = idx + self.dest = dest + + class StaticCallNode(InstructionNode): + def __init__(self, function, dest): + self.function = function + self.dest = dest + + class DynamicCallNode(InstructionNode): + def __init__(self, typex, function, dest, computed_type): + self.type = typex + self.function = function + self.dest = dest + self.computed_type = computed_type + + class ArgNode(InstructionNode): + def __init__(self, idx): + self.id = idx + + class IfGotoNode(InstructionNode): + def __init__(self, if_cond, label): + self.if_cond = if_cond + self.label = label + + class LabelNode(InstructionNode): + def __init__(self, label): + self.label = label + + class GotoNode(InstructionNode): + def __init__(self, label): + self.label = label + + class ReturnNode(InstructionNode): + def __init__(self, idx=None): + self.id = idx + + class LoadNode(InstructionNode): + def __init__(self, dest, msg): + self.dest = dest + self.msg = msg + + class LengthNode(InstructionNode): + def __init__(self, dest, idx): + self.dest = dest + self.id = idx + + class ConcatNode(InstructionNode): + def __init__(self, dest, s1, s2, length): + self.dest = dest + self.s1 = s1 + self.s2 = s2 + self.length = length + + class SubstringNode(InstructionNode): + def __init__(self, dest, s, i, length): + self.dest = dest + self.s = s + self.i = i + self.length = length + + class ReadStrNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + + class PrintStrNode(InstructionNode): + def __init__(self, value): + self.value = value + + class ErrorNode(InstructionNode): + def __init__(self, data_node): + self.data_node = data_node + + class TypeNameNode(InstructionNode): + def __init__(self, dest, typex): + self.dest = dest + self.type = typex + + class NameNode(InstructionNode): + def __init__(self, dest, idx): + self.dest = dest + self.id = idx + + class AbortNode(InstructionNode): + pass + + class CopyNode(InstructionNode): + def __init__(self, dest, copy): + self.dest = dest + self.copy = copy + + class ReadIntNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + + class PrintIntNode(InstructionNode): + def __init__(self, value): + self.value = value + + class VoidNode(InstructionNode): + pass + + class ComplementNode(InstructionNode): + def __init__(self, dest, idx): + self.dest = dest + self.id = idx \ No newline at end of file diff --git a/src/utils/code_generation/cil/Base_COOL_to_CIL.py b/src/utils/code_generation/cil/Base_COOL_to_CIL.py new file mode 100644 index 000000000..164c42ca6 --- /dev/null +++ b/src/utils/code_generation/cil/Base_COOL_to_CIL.py @@ -0,0 +1,343 @@ +from utils.code_generation.cil.AST_CIL import cil_ast as nodes +from cmp.semantic import VariableInfo + + +class BaseCOOLToCIL: + 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) + + @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_node = nodes.ParamNode(vinfo.name) + self.params.append(param_node) + return vinfo.name + + def register_local(self, vinfo, id=False): + if len(self.current_function.id) >= 8 and self.current_function.id[:8] == 'function': + name = f'local_{self.current_function.id[9:]}_{vinfo.name}_{len(self.localvars)}' + else: + name = f'local_{self.current_function.id[5:]}_{vinfo.name}_{len(self.localvars)}' + + new_vinfo = VariableInfo(name, None) + local_node = nodes.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 = nodes.FunctionNode(function_name, [], [], []) + self.dotcode.append(function_node) + return function_node + + def register_type(self, name): + type_node = nodes.TypeNode(name) + self.dottypes.append(type_node) + return type_node + + def register_data(self, value): + vname = f'data_{len(self.dotdata)}' + data_node = nodes.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 nodes.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( + nodes.IfGotoNode(condition, error_node.label)) + self.register_instruction(nodes.GotoNode(continue_node.label)) + self.register_instruction(error_node) + data_node = self.register_data(msg) + self.register_instruction(nodes.ErrorNode(data_node)) + self.register_instruction(continue_node) + + def register_built_in(self): + self.__register_Object() + self.__register_IO() + self.__register_String() + self.__register_Int() + self.__register_Bool() + + # convirtiendo tipos y funciones integrados en COOL en CIL + + def __register_Object(self): + type_node = self.register_type('Object') + + self.current_function = self.register_function( + self.init_name('Object')) + instance = self.define_internal_local() + self.register_instruction(nodes.AllocateNode('Object', instance)) + self.register_instruction(nodes.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(nodes.LoadNode(vname, data_node)) + self.register_instruction(nodes.PrintStrNode(vname)) + self.register_instruction(nodes.TypeNameNode(vname, self.vself.name)) + self.register_instruction(nodes.PrintStrNode(vname)) + data_node = self.register_data('\n') + self.register_instruction(nodes.LoadNode(vname, data_node)) + self.register_instruction(nodes.PrintStrNode(vname)) + self.register_instruction(nodes.AbortNode()) + + 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(nodes.TypeNameNode(result, self.vself.name)) + instance = self.define_internal_local() + self.register_instruction(nodes.ArgNode(result)) + self.register_instruction(nodes.StaticCallNode( + self.init_name('String'), instance)) + self.register_instruction(nodes.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(nodes.CopyNode(result, self.vself.name)) + self.register_instruction(nodes.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'))] + + def __register_IO(self): + type_node = self.register_type('IO') + + self.current_function = self.register_function(self.init_name('IO')) + instance = self.define_internal_local() + self.register_instruction(nodes.AllocateNode('IO', instance)) + self.register_instruction(nodes.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( + nodes.GetAttrNode(vname, 'x', 'value', 'String')) + self.register_instruction(nodes.PrintStrNode(vname)) + self.register_instruction(nodes.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( + nodes.GetAttrNode(vname, 'x', 'value', 'Int')) + self.register_instruction(nodes.PrintIntNode(vname)) + self.register_instruction(nodes.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(nodes.ReadStrNode(result)) + instance = self.define_internal_local() + self.register_instruction(nodes.ArgNode(result)) + self.register_instruction(nodes.StaticCallNode( + self.init_name('String'), instance)) + self.register_instruction(nodes.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(nodes.ReadIntNode(result)) + instance = self.define_internal_local() + self.register_instruction(nodes.ArgNode(result)) + self.register_instruction(nodes.StaticCallNode( + self.init_name('Int'), instance)) + self.register_instruction(nodes.ReturnNode(instance)) + + type_node.methods = [(method, self.to_function_name( + method, 'Object')) for method in ['type_name', 'abort', 'copy']] + 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'))] + + def __register_Int(self): + 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(nodes.AllocateNode('Int', instance)) + self.register_instruction(nodes.SetAttrNode( + instance, 'value', 'val', 'Int')) + self.register_instruction(nodes.ReturnNode(instance)) + + type_node.methods = [(method, self.to_function_name( + method, 'Object')) for method in ['type_name', 'abort', 'copy']] + type_node.methods += [('init', self.init_name('Int'))] + + def __register_String(self): + 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(nodes.AllocateNode('String', instance)) + self.register_instruction(nodes.SetAttrNode( + instance, 'value', 'val', 'String')) + result = self.define_internal_local() + self.register_instruction(nodes.LengthNode(result, 'val')) + attr = self.define_internal_local() + self.register_instruction(nodes.ArgNode(result)) + self.register_instruction( + nodes.StaticCallNode(self.init_name('Int'), attr)) + self.register_instruction(nodes.SetAttrNode( + instance, 'length', attr, 'String')) + self.register_instruction(nodes.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(nodes.GetAttrNode( + result, self.vself.name, 'length', 'String')) + self.register_instruction(nodes.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(nodes.GetAttrNode( + str_1, self.vself.name, 'value', 'String')) + self.register_instruction( + nodes.GetAttrNode(str_2, 's', 'value', 'String')) + self.register_instruction(nodes.GetAttrNode( + length_1, self.vself.name, 'length', 'String')) + self.register_instruction(nodes.GetAttrNode( + length_2, 's', 'length', 'String')) + self.register_instruction(nodes.GetAttrNode( + length_1, length_1, 'value', 'Int')) + self.register_instruction(nodes.GetAttrNode( + length_2, length_2, 'value', 'Int')) + self.register_instruction(nodes.PlusNode(length_1, length_1, length_2)) + + result = self.define_internal_local() + self.register_instruction( + nodes.ConcatNode(result, str_1, str_2, length_1)) + instance = self.define_internal_local() + self.register_instruction(nodes.ArgNode(result)) + self.register_instruction(nodes.StaticCallNode( + self.init_name('String'), instance)) + self.register_instruction(nodes.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(nodes.GetAttrNode( + str_value, self.vself.name, 'value', 'String')) + self.register_instruction(nodes.GetAttrNode( + index_value, 'i', 'value', 'Int')) + self.register_instruction(nodes.GetAttrNode( + length_value, 'l', 'value', 'Int')) + + self.register_instruction(nodes.GetAttrNode( + length_attr, self.vself.name, 'length', 'String')) + self.register_instruction(nodes.PlusNode( + length_substr, length_value, index_value)) + self.register_instruction(nodes.LessThanNode( + less_value, length_attr, length_substr)) + self.register_runtime_error(less_value, 'Substring out of range') + self.register_instruction(nodes.SubstringNode( + result, str_value, index_value, length_value)) + instance = self.define_internal_local() + self.register_instruction(nodes.ArgNode(result)) + self.register_instruction(nodes.StaticCallNode( + self.init_name('String'), instance)) + self.register_instruction(nodes.ReturnNode(instance)) + + type_node.methods = [(method, self.to_function_name( + method, 'Object')) for method in ['type_name', 'abort', 'copy']] + type_node.methods += [(name, self.to_function_name(name, 'String')) + for name in ['length', 'concat', 'substr']] + type_node.methods += [('init', self.init_name('String'))] + + def __register_Bool(self): + 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(nodes.AllocateNode('Bool', instance)) + self.register_instruction(nodes.SetAttrNode( + instance, 'value', 'val', 'Bool')) + self.register_instruction(nodes.ReturnNode(instance)) + + type_node.methods = [(method, self.to_function_name( + method, 'Object')) for method in ['type_name', 'abort', 'copy']] + type_node.methods += [('init', self.init_name('Bool'))] diff --git a/src/utils/code_generation/cil/COOL_to_CIL.py b/src/utils/code_generation/cil/COOL_to_CIL.py new file mode 100644 index 000000000..c62a7630f --- /dev/null +++ b/src/utils/code_generation/cil/COOL_to_CIL.py @@ -0,0 +1,702 @@ +import cmp.visitor as visitor +from cmp.semantic import SemanticError, VariableInfo +from utils.ast.AST_Nodes import ast_nodes as nodes +from utils.code_generation.cil.AST_CIL import cil_ast as nodes_cil +from utils.code_generation.cil.Base_COOL_to_CIL import BaseCOOLToCIL + + +class COOLtoCIL(BaseCOOLToCIL): + def __init__(self, context): + BaseCOOLToCIL.__init__(self, context) + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(nodes.ProgramNode) + def visit(self, node, scope=None): + self.current_function = self.register_function('entry') + result = self.define_internal_local() + instance = self.register_local(VariableInfo('instance', None)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Main'), instance)) + self.register_instruction(nodes_cil.ArgNode(instance)) + self.register_instruction(nodes_cil.StaticCallNode( + self.to_function_name('main', 'Main'), result)) + self.register_instruction(nodes_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 nodes_cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) + + @visitor.when(nodes.ClassDeclarationNode) + def visit(self, node, scope): + self.current_type = self.context.get_type(node.id) + + 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, nodes.MethDeclarationNode)) + for feature, child_scope in zip(func_declarations, scope.children): + self.visit(feature, child_scope) + + self.current_function = self.register_function(self.init_name(node.id)) + + instance = self.register_local(VariableInfo('instance', None)) + self.register_instruction(nodes_cil.AllocateNode(node.id, instance)) + + temp_f = self.current_function + vtemp = self.define_internal_local() + + 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(nodes_cil.ArgNode(self.vself.name)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name(node.parent, attr=True), vtemp)) + attr_declarations = (f for f in node.features if isinstance( + f, nodes.AttrDeclarationNode)) + for feature in attr_declarations: + self.visit(feature, scope) + + self.current_function = temp_f + self.register_instruction(nodes_cil.ArgNode(instance)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name(node.id, attr=True), vtemp)) + self.register_instruction(nodes_cil.ReturnNode(instance)) + self.current_function = None + self.current_type = None + + @visitor.when(nodes.AttrDeclarationNode) + def visit(self, node, scope): + if node.expr: + self.visit(node.expr, scope) + self.register_instruction(nodes_cil.SetAttrNode( + self.vself.name, node.id, scope._return, self.current_type)) + elif node.type in ['String', 'Int', 'Bool']: + vtemp = self.define_internal_local() + self.register_instruction(nodes_cil.AllocateNode(node.type, vtemp)) + self.register_instruction(nodes_cil.SetAttrNode( + self.vself.name, node.id, vtemp, self.current_type)) + + @visitor.when(nodes.MethDeclarationNode) + def visit(self, node, scope): + self.current_method = self.current_type.get_method(node.id) + self.current_function = self.register_function( + self.to_function_name(self.current_method.name, self.current_type.name)) + + self.register_param(self.vself) + for param_name, _ in node.params: + self.register_param(VariableInfo(param_name, None)) + + scope._return = None + self.visit(node.body, scope) + + if scope._return is None: + self.register_instruction(nodes_cil.ReturnNode('')) + elif self.current_function.id == 'entry': + self.register_instruction(nodes_cil.ReturnNode(0)) + else: + self.register_instruction(nodes_cil.ReturnNode(scope._return)) + + self.current_method = None + + @visitor.when(nodes.AssignNode) + def visit(self, node, scope): + self.visit(node.expr, scope) + + try: + self.current_type.get_attribute(node.id, None) + self.register_instruction(nodes_cil.SetAttrNode( + self.vself.name, node.id, scope._return, self.current_type.name)) + + except SemanticError: + vname = None + param_names = [pn.id 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.id for lv in self.current_function.localvars]: + if node.id in n.split("_"): + vname = n + break + self.register_instruction( + nodes_cil.AssignNode(vname, scope._return)) + + @visitor.when(nodes.CallNode) + def visit(self, node, scope): + if node.obj == None and node.type == None: + 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( + nodes_cil.AssignNode(vname, scope._return)) + args.append(nodes_cil.ArgNode(vname)) + result = self.register_local(VariableInfo( + f'return_value_of_{node.id}', None), id=True) + + self.register_instruction(nodes_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( + nodes_cil.TypeOfNode(self.vself.name, type_of_node)) + self.register_instruction(nodes_cil.DynamicCallNode( + type_of_node, node.id, result, self.current_type.name)) + scope._return = result + + else: + 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( + nodes_cil.AssignNode(vname, scope._return)) + args.append(nodes_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( + nodes_cil.AssignNode(vobj, scope._return)) + + void = nodes_cil.VoidNode() + equal_result = self.define_internal_local() + self.register_instruction( + nodes_cil.EqualNode(equal_result, vobj, void)) + + self.register_runtime_error( + equal_result, f'({node.line},{node.column}) - RuntimeError: Dispatch on void\n') + + self.register_instruction(nodes_cil.ArgNode(vobj)) + for arg in args: + self.register_instruction(arg) + + if node.type: + self.register_instruction(nodes_cil.StaticCallNode( + self.to_function_name(node.id, node.type), result)) + else: + type_of_node = self.register_local( + VariableInfo(f'{node.id}_type', None), id=True) + self.register_instruction( + nodes_cil.TypeOfNode(vobj, type_of_node)) + typex = node.obj.computed_type + if typex.name == 'SELF_TYPE': + typex = self.current_type + self.register_instruction(nodes_cil.DynamicCallNode( + type_of_node, node.id, result, typex.name)) + + scope._return = result + + @visitor.when(nodes.IfThenElseNode) + def visit(self, node, scope): + vresult = 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') + + self.visit(node.if_expr, scope) + self.register_instruction(nodes_cil.GetAttrNode( + vcondition, scope._return, 'value', 'Bool')) + self.register_instruction(nodes_cil.IfGotoNode( + vcondition, then_label_node.label)) + + self.register_instruction(nodes_cil.GotoNode(else_label_node.label)) + + self.register_instruction(then_label_node) + self.visit(node.then_expr, scope) + self.register_instruction(nodes_cil.AssignNode(vresult, scope._return)) + self.register_instruction( + nodes_cil.GotoNode(continue_label_node.label)) + + self.register_instruction(else_label_node) + self.visit(node.else_expr, scope) + self.register_instruction(nodes_cil.AssignNode(vresult, scope._return)) + + self.register_instruction(continue_label_node) + scope._return = vresult + + @visitor.when(nodes.WhileNode) + def visit(self, node, 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') + + self.register_instruction(while_label_node) + self.visit(node.conditional_expr, scope) + self.register_instruction(nodes_cil.GetAttrNode( + vcondition, scope._return, 'value', 'Bool')) + self.register_instruction(nodes_cil.IfGotoNode( + vcondition, loop_label_node.label)) + + self.register_instruction(nodes_cil.GotoNode(pool_label_node.label)) + self.register_instruction(loop_label_node) + self.visit(node.loop_expr, scope) + + self.register_instruction(nodes_cil.GotoNode(while_label_node.label)) + self.register_instruction(pool_label_node) + + scope._return = nodes_cil.VoidNode() + + @visitor.when(nodes.BlockNode) + def visit(self, node, scope): + for expr in node.expr_list: + self.visit(expr, scope) + + @visitor.when(nodes.LetNode) + def visit(self, node, scope): + vresult = self.register_local(VariableInfo('let_in_value', None)) + + for idx, typex, id_expr in node.identifiers: + if idx in self.ids: + vname = self.ids[idx] + else: + vname = self.register_local(VariableInfo(idx, typex), id=True) + + if id_expr: + self.visit(id_expr, scope) + self.register_instruction( + nodes_cil.AssignNode(vname, scope._return)) + elif typex in ['String', 'Int', 'Bool']: + self.register_instruction(nodes_cil.AllocateNode(typex, vname)) + + self.visit(node.in_expr, scope) + self.register_instruction(nodes_cil.AssignNode(vresult, scope._return)) + scope._return = vresult + + @visitor.when(nodes.CaseNode) + def visit(self, node, scope): + _expr = self.register_local(VariableInfo('case_expr_value', None)) + _type = self.register_local(VariableInfo('typeName_value', None)) + _condition = self.register_local(VariableInfo('equal_value', None)) + + vresult = self.register_local(VariableInfo('case_value', None)) + + self.visit(node.predicate, scope) + self.register_instruction(nodes_cil.AssignNode(_expr, scope._return)) + self.register_instruction(nodes_cil.TypeNameNode(_type, scope._return)) + + void = nodes_cil.VoidNode() + equal_result = self.define_internal_local() + self.register_instruction( + nodes_cil.EqualNode(equal_result, _expr, void)) + + self.register_runtime_error( + equal_result, f'({node.line},{node.column}) - RuntimeError: Case on void\n') + + end_label = self.register_label('end_label') + labels = [] + + order = [] + for branch in node.branches: + (_, typex, _) = branch + count = 0 + t1 = self.context.get_type(typex) + + for other_branch in node.branches: + (_, other_typex, _) = other_branch + t2 = self.context.get_type(other_typex) + count += t2.conforms_to(t1) + + order.append((count, branch)) + order.sort(key=lambda x: x[0]) + + for i, (_, branch) in enumerate(order): + (_, typex, _) = branch + labels.append(self.register_label(f'{i}_label')) + h = {x.name for x in self.context.types.values() if x.conforms_to( + self.context.get_type(typex))} if typex != 'Object' else None + if not h: + self.register_instruction(nodes_cil.GotoNode(labels[-1].label)) + break + h.add(typex) + for t in h: + vbranch_type_name = self.register_local( + VariableInfo('branch_type_name', None)) + self.register_instruction( + nodes_cil.NameNode(vbranch_type_name, t)) + self.register_instruction(nodes_cil.EqualNode( + _condition, _type, vbranch_type_name)) + self.register_instruction( + nodes_cil.IfGotoNode(_condition, labels[-1].label)) + + (line, column) = node.branchesPos[i] + data_node = self.register_data( + f'({line},{column}) - RuntimeError: Execution of a case statement without a matching branch\n') + self.register_instruction(nodes_cil.ErrorNode(data_node)) + + for i, l in enumerate(labels): + self.register_instruction(l) + + (idx, typex, expr) = order[i][1] + vid = self.register_local(VariableInfo(idx, None), id=True) + self.register_instruction(nodes_cil.AssignNode(vid, _expr)) + + self.visit(expr, scope) + self.register_instruction( + nodes_cil.AssignNode(vresult, scope._return)) + self.register_instruction(nodes_cil.GotoNode(end_label.label)) + + scope._return = vresult + self.register_instruction(end_label) + + @visitor.when(nodes.NotNode) + def visit(self, node, scope): + vname = self.define_internal_local() + value = self.define_internal_local() + instance = self.define_internal_local() + + self.visit(node.expr, scope) + self.register_instruction(nodes_cil.GetAttrNode( + value, scope._return, 'value', 'Bool')) + self.register_instruction(nodes_cil.MinusNode(vname, 1, value)) + + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Bool'), instance)) + scope._return = instance + + @visitor.when(nodes.ConstantNumNode) + def visit(self, node, scope): + instance = self.define_internal_local() + self.register_instruction(nodes_cil.ArgNode(int(node.lex))) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Int'), instance)) + scope._return = instance + + @visitor.when(nodes.ConstantBoolNode) + def visit(self, node, scope): + if node.lex == 'true': + value = 1 + + else: + value = 0 + + instance = self.define_internal_local() + self.register_instruction(nodes_cil.ArgNode(value)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Bool'), instance)) + scope._return = instance + + @visitor.when(nodes.ConstantStringNode) + def visit(self, node, scope): + 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(nodes_cil.LoadNode(vmsg, data_node)) + self.register_instruction(nodes_cil.ArgNode(vmsg)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('String'), instance)) + scope._return = instance + + @visitor.when(nodes.VariableNode) + def visit(self, node, scope): + try: + self.current_type.get_attribute(node.lex, None) + attr = self.register_local(VariableInfo(node.lex, None), id=True) + self.register_instruction(nodes_cil.GetAttrNode( + attr, self.vself.name, node.lex, self.current_type.name)) + scope._return = attr + + except SemanticError: + param_names = [pn.id for pn in self.current_function.params] + if node.lex in param_names: + for n in param_names: + if node.lex == n: + scope._return = n + break + else: + scope._return = self.ids[node.lex] + + @visitor.when(nodes.InstantiateNode) + def visit(self, node, scope): + instance = self.define_internal_local() + + if node.lex == 'SELF_TYPE': + vtype = self.define_internal_local() + self.register_instruction( + nodes_cil.TypeOfNode(self.vself.name, vtype)) + self.register_instruction(nodes_cil.AllocateNode(vtype, instance)) + + elif node.lex == 'Int' or node.lex == 'Bool': + self.register_instruction(nodes_cil.ArgNode(0)) + + elif node.lex == 'String': + data_node = [dn for dn in self.dotdata if dn.value == ''][0] + vmsg = self.register_local(VariableInfo('msg', None)) + self.register_instruction(nodes_cil.LoadNode(vmsg, data_node)) + self.register_instruction(nodes_cil.ArgNode(vmsg)) + + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name(node.lex), instance)) + scope._return = instance + + @visitor.when(nodes.IsVoidNode) + def visit(self, node, scope): + void = nodes_cil.VoidNode() + value = self.define_internal_local() + + self.visit(node.lex, scope) + self.register_instruction(nodes_cil.AssignNode(value, scope._return)) + vresult = self.define_internal_local() + + self.register_instruction(nodes_cil.EqualNode(vresult, value, void)) + self.register_instruction(nodes_cil.ArgNode(vresult)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name("Bool"), vresult)) + scope._return = vresult + + @visitor.when(nodes.ComplementNode) + def visit(self, node, scope): + vname = self.define_internal_local() + value = self.define_internal_local() + instance = self.define_internal_local() + + self.visit(node.lex, scope) + self.register_instruction(nodes_cil.GetAttrNode( + value, scope._return, 'value', 'Int')) + self.register_instruction(nodes_cil.ComplementNode(vname, value)) + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Int'), instance)) + scope._return = instance + + @visitor.when(nodes.PlusNode) + def visit(self, node, scope): + vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + + self.visit(node.left, scope) + self.register_instruction(nodes_cil.GetAttrNode( + left_value, scope._return, 'value', 'Int')) + self.visit(node.right, scope) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, scope._return, 'value', 'Int')) + + self.register_instruction( + nodes_cil.PlusNode(vname, left_value, right_value)) + instance = self.define_internal_local() + + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Int'), instance)) + scope._return = instance + + @visitor.when(nodes.MinusNode) + def visit(self, node, scope): + vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + + self.visit(node.left, scope) + self.register_instruction(nodes_cil.GetAttrNode( + left_value, scope._return, 'value', 'Int')) + self.visit(node.right, scope) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, scope._return, 'value', 'Int')) + + self.register_instruction( + nodes_cil.MinusNode(vname, left_value, right_value)) + instance = self.define_internal_local() + + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Int'), instance)) + scope._return = instance + + @visitor.when(nodes.StarNode) + def visit(self, node, scope): + vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + + self.visit(node.left, scope) + self.register_instruction(nodes_cil.GetAttrNode( + left_value, scope._return, 'value', 'Int')) + self.visit(node.right, scope) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, scope._return, 'value', 'Int')) + + self.register_instruction( + nodes_cil.StarNode(vname, left_value, right_value)) + instance = self.define_internal_local() + + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Int'), instance)) + scope._return = instance + + @visitor.when(nodes.DivNode) + def visit(self, node, scope): + vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + + self.visit(node.left, scope) + self.register_instruction(nodes_cil.GetAttrNode( + left_value, scope._return, 'value', 'Int')) + self.visit(node.right, scope) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, scope._return, 'value', 'Int')) + + vresult = self.define_internal_local() + self.register_instruction(nodes_cil.EqualNode(vresult, right_value, 0)) + self.register_runtime_error( + vresult, f'({node.line},{node.column}) - RuntimeError: Division by zero\n') + + self.register_instruction( + nodes_cil.DivNode(vname, left_value, right_value)) + instance = self.define_internal_local() + + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Int'), instance)) + scope._return = instance + + @visitor.when(nodes.LessThanNode) + def visit(self, node, scope): + vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + instance = self.define_internal_local() + + self.visit(node.left, scope) + left = scope._return + + self.visit(node.right, scope) + right = scope._return + + self.register_instruction(nodes_cil.GetAttrNode( + left_value, left, 'value', 'Bool')) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, right, 'value', 'Bool')) + self.register_instruction( + nodes_cil.LessThanNode(vname, left_value, right_value)) + + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Bool'), instance)) + scope._return = instance + + @visitor.when(nodes.LessEqualNode) + def visit(self, node, scope): + vname = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + instance = self.define_internal_local() + + self.visit(node.left, scope) + left = scope._return + + self.visit(node.right, scope) + right = scope._return + + self.register_instruction(nodes_cil.GetAttrNode( + left_value, left, 'value', 'Bool')) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, right, 'value', 'Bool')) + self.register_instruction( + nodes_cil.LessEqualNode(vname, left_value, right_value)) + + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Bool'), instance)) + scope._return = instance + + @visitor.when(nodes.EqualNode) + def visit(self, node, scope): + vname = self.define_internal_local() + type_left = self.define_internal_local() + _bool = self.define_internal_local() + _string = self.define_internal_local() + _int = self.define_internal_local() + left_value = self.define_internal_local() + right_value = self.define_internal_local() + vresult = self.define_internal_local() + instance = self.define_internal_local() + + self.visit(node.left, scope) + left = scope._return + + self.visit(node.right, scope) + right = scope._return + + self.register_instruction(nodes_cil.TypeNameNode(type_left, left)) + self.register_instruction(nodes_cil.NameNode(_int, 'Int')) + self.register_instruction(nodes_cil.NameNode(_bool, 'Bool')) + self.register_instruction(nodes_cil.NameNode(_string, 'String')) + + string_node = self.register_label('string_label') + int_node = self.register_label('int_label') + reference_node = self.register_label('reference_label') + continue_node = self.register_label('continue_label') + + self.register_instruction( + nodes_cil.EqualNode(vresult, type_left, _int)) + self.register_instruction( + nodes_cil.IfGotoNode(vresult, int_node.label)) + self.register_instruction( + nodes_cil.EqualNode(vresult, type_left, _bool)) + self.register_instruction( + nodes_cil.IfGotoNode(vresult, int_node.label)) + self.register_instruction( + nodes_cil.EqualNode(vresult, type_left, _string)) + self.register_instruction( + nodes_cil.IfGotoNode(vresult, string_node.label)) + self.register_instruction(nodes_cil.GotoNode(reference_node.label)) + + self.register_instruction(int_node) + self.register_instruction(nodes_cil.GetAttrNode( + left_value, left, 'value', 'Int')) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, right, 'value', 'Int')) + self.register_instruction( + nodes_cil.EqualNode(vname, left_value, right_value)) + self.register_instruction(nodes_cil.GotoNode(continue_node.label)) + + self.register_instruction(string_node) + self.register_instruction(nodes_cil.GetAttrNode( + left_value, left, 'value', 'String')) + self.register_instruction(nodes_cil.GetAttrNode( + right_value, right, 'value', 'String')) + self.register_instruction( + nodes_cil.EqualStrNode(vname, left_value, right_value)) + self.register_instruction(nodes_cil.GotoNode(continue_node.label)) + + self.register_instruction(reference_node) + self.register_instruction(nodes_cil.EqualNode(vname, left, right)) + + self.register_instruction(continue_node) + self.register_instruction(nodes_cil.ArgNode(vname)) + self.register_instruction(nodes_cil.StaticCallNode( + self.init_name('Bool'), instance)) + scope._return = instance diff --git a/src/utils/code_generation/cil/print_CIL_AST.py b/src/utils/code_generation/cil/print_CIL_AST.py new file mode 100644 index 000000000..ac54779de --- /dev/null +++ b/src/utils/code_generation/cil/print_CIL_AST.py @@ -0,0 +1,185 @@ +from utils.code_generation.cil.AST_CIL import cil_ast as nodes +import cmp.visitor as visitor + +def get_formatter(): + + class PrintCIL(object): + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(nodes.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(nodes.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.id} {{\n\t{attributes}\n\n\t{methods}\n}}' + + @visitor.when(nodes.DataNode) + def visit(self, node): + return f'{node.id} = {node.value}' + + @visitor.when(nodes.FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions if self.visit(x) != []) + + return f'function {node.id} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(nodes.ParamNode) + def visit(self, node): + return f'PARAM {node.id}' + + @visitor.when(nodes.LocalNode) + def visit(self, node): + return f'LOCAL {node.id}' + + @visitor.when(nodes.AssignNode) + def visit(self, node): + return f'{node.left} = {node.right}' + + @visitor.when(nodes.PlusNode) + def visit(self, node): + return f'{node.dest} = {node.op_l} + {node.op_r}' + + @visitor.when(nodes.MinusNode) + def visit(self, node): + return f'{node.dest} = {node.op_l} - {node.op_r}' + + @visitor.when(nodes.StarNode) + def visit(self, node): + return f'{node.dest} = {node.op_l} * {node.op_r}' + + @visitor.when(nodes.DivNode) + def visit(self, node): + return f'{node.dest} = {node.op_l} / {node.op_r}' + + @visitor.when(nodes.LessEqualNode) + def visit(self, node): + return f'{node.dest} = {node.op_l} <= {node.op_r}' + + @visitor.when(nodes.LessThanNode) + def visit(self, node): + return f'{node.dest} = {node.op_l} < {node.op_r}' + + @visitor.when(nodes.EqualNode) + def visit(self, node): + return f'{node.dest} = {node.op_l} == {node.op_r}' + + @visitor.when(nodes.GetAttrNode) + def visit(self, node): + return f'{node.dest} = GETATTR {node.id} {node.attr}' + + @visitor.when(nodes.SetAttrNode) + def visit(self, node): + return f'SETATTR {node.id} {node.attr} {node.value}' + + @visitor.when(nodes.AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(nodes.TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.id}' + + @visitor.when(nodes.StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(nodes.DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.function}' + + @visitor.when(nodes.ArgNode) + def visit(self, node): + return f'ARG {node.id}' + + @visitor.when(nodes.IfGotoNode) + def visit(self, node): + return f'IF {node.if_cond} GOTO {node.label}' + + @visitor.when(nodes.LabelNode) + def visit(self, node): + return f'LABEL {node.label}' + + @visitor.when(nodes.GotoNode) + def visit(self, node): + return f'GOTO {node.label}' + + @visitor.when(nodes.ReturnNode) + def visit(self, node): + return f'RETURN {node.id if node.id is not None else ""}' + + @visitor.when(nodes.LoadNode) + def visit(self, node): + return f'{node.dest} = LOAD {node.msg}' + + @visitor.when(nodes.LengthNode) + def visit(self, node): + return f'{node.dest} = LENGTH {node.id}' + + @visitor.when(nodes.ConcatNode) + def visit(self, node): + return f'{node.dest} = CONCAT {node.s1} {node.s2}' + + @visitor.when(nodes.SubstringNode) + def visit(self, node): + return f'{node.dest} = SUBSTRING {node.s} {node.i} {node.length}' + + @visitor.when(nodes.ReadStrNode) + def visit(self, node): + return f'{node.dest} = READSTR' + + @visitor.when(nodes.PrintStrNode) + def visit(self, node): + return f'PRINT {node.value}' + + @visitor.when(nodes.ErrorNode) + def visit(self, node): + return f'ERROR {node.data_node}' + + @visitor.when(nodes.TypeNameNode) + def visit(self, node): + return f'{node.dest} = TYPENAME {node.type}' + + @visitor.when(nodes.NameNode) + def visit(self, node): + return f'{node.dest} = NAME {node.id}' + + @visitor.when(nodes.AbortNode) + def visit(self, node): + return f'ABORT' + + @visitor.when(nodes.CopyNode) + def visit(self, node): + return f'{node.dest} = COPY {node.copy}' + + @visitor.when(nodes.ReadIntNode) + def visit(self, node): + return f'{node.dest} = READINT' + + @visitor.when(nodes.PrintIntNode) + def visit(self, node): + return f'PRINT {node.value}' + + @visitor.when(nodes.VoidNode) + def visit(self, node): + return 'VOID' + + @visitor.when(nodes.ComplementNode) + def visit(self, node): + return f'{node.dest} = COMPLEMENT {node.id}' + + printer = PrintCIL() + return (lambda ast: printer.visit(ast)) + \ No newline at end of file diff --git a/src/utils/code_generation/mips/AST_MIPS.py b/src/utils/code_generation/mips/AST_MIPS.py new file mode 100644 index 000000000..02ec14ec1 --- /dev/null +++ b/src/utils/code_generation/mips/AST_MIPS.py @@ -0,0 +1,234 @@ + +class mips_ast: + class Node: + pass + + class ProgramNode(Node): + def __init__(self, data, types, functions): + self._data = data + self._types = types + self._functions = functions + + @property + def data(self): + return self._data + + @property + def types(self): + return self._types + + @property + def functions(self): + return self._functions + + class DataNode(Node): + def __init__(self, label): + self._label = label + + @property + def label(self): + return self._label + + class FunctionNode(Node): + def __init__(self, label, params, localvars): + self._label = label + self._instructions = [] + self._params = params + self._localvars = localvars + + @property + def label(self): + return self._label + + @property + def instructions(self): + return self._instructions + + def add_instructions(self, instructions): + self._instructions.extend(instructions) + + def get_param_stack_location(self, name): + index = self._params.index(name) + offset = ((len(self._params) - 1) - index) * 4 + return RegisterRelativeLocation(Register('fp'), offset) + + def get_local_stack_location(self, name): + index = self._localvars.index(name) + offset = (index + 2) * -4 + return RegisterRelativeLocation(Register('fp'), offset) + + def get_var_location(self, name): + try: + return self.get_param_stack_location(name) + except ValueError: + return self.get_local_stack_location(name) + + class InstructionNode(Node): + pass + + 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 LabelNode(InstructionNode): + def __init__(self, name): + self.name = name + + class ComplementNode(InstructionNode): + def __init__(self, reg1, reg2): + self.reg1 = reg1 + self.reg2 = reg2 + + class StringConst(DataNode): + def __init__(self, label, string): + super().__init__(label) + self._string = string + + @property + def string(self): + return self._string + + 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 JumpRegisterNode(InstructionNode): + def __init__(self, reg): + self.reg = reg + + class AddInmediateNode(InstructionNode): + def __init__(self, dest, src, value): + self.dest = dest + self.src = src + self.value = value + + 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 MoveFromLowNode(InstructionNode): + def __init__(self, reg): + self.reg = reg + + +class Register(): + def __init__(self, name): + self.name = name + + +ARG_REGISTERS = [Register(name) for name in ['a0', 'a1', 'a2', 'a3']] + + +SP_REG = Register('sp') +RA_REG = Register('ra') +V0_REG = Register('v0') +V1_REG = Register('v1') +ZERO_REG = Register('zero') +LOW_REG = Register('low') + + +class MemoryLocation: + pass + + +class RegisterRelativeLocation(MemoryLocation): + def __init__(self, register, offset): + self._register = register + self._offset = offset + + @property + def register(self): + return self._register + + @property + def offset(self): + return self._offset + + +class LabelRelativeLocation(MemoryLocation): + def __init__(self, label, offset): + self._label = label + self._offset = offset + + @property + def label(self): + return self._label + + @property + def offset(self): + return self._offset diff --git a/src/utils/code_generation/mips/CIL_to_MIPS.py b/src/utils/code_generation/mips/CIL_to_MIPS.py new file mode 100644 index 000000000..5019a305b --- /dev/null +++ b/src/utils/code_generation/mips/CIL_to_MIPS.py @@ -0,0 +1,1076 @@ +import itertools as itt +import cmp.visitor as visitor +from utils.code_generation.cil.AST_CIL import cil_ast as cil +from utils.code_generation.mips.AST_MIPS import mips_ast as mips +from utils.code_generation.mips.AST_MIPS import * +from utils.code_generation.mips.utils_mips import * + + +class CILToMIPS: + def __init__(self, label_generator=LabelGenerator()): + self._label_generator = label_generator + self.memory_manager = None + self._types = {} + self._data_section = {} + self._functions = {} + self._actual_function = None + self._name_func_map = {} + self._pushed_args = 0 + self._labels_map = {} + + def generate_type_label(self): + return self._label_generator.generate_type_label() + + def generate_data_label(self): + return self._label_generator.generate_data_label() + + def generate_code_label(self): + return self._label_generator.generate_code_label() + + def get_var_location(self, name): + return self._actual_function.get_var_location(name) + + def register_function(self, name, function): + self._functions[name] = function + + def init_function(self, function): + self._actual_function = function + self._labels_map = {} + + def finish_functions(self): + self._actual_function = None + + def push_arg(self): + self._pushed_args += 1 + + def clean_pushed_args(self): + self._pushed_args = 0 + + def get_free_reg(self): + return self._registers_manager.get_free_reg() + + def free_reg(self, reg): + self._registers_manager.free_reg(reg) + + def in_entry_function(self): + return self._actual_function.label == 'main' + + def register_label(self, cil_label, mips_label): + self._labels_map[cil_label] = mips_label + + def get_mips_label(self, label): + return self._labels_map[label] + + @visitor.on('node') + def collect_func_names(self, node): + pass + + @visitor.when(cil.ProgramNode) + def collect_func_names(self, node): + for func in node.dotcode: + self.collect_func_names(func) + + @visitor.when(cil.FunctionNode) + def collect_func_names(self, node): + if node.id == "entry": + self._name_func_map[node.id] = 'main' + else: + self._name_func_map[node.id] = self.generate_code_label() + + @visitor.on('node') + def collect_labels_in_func(self, node): + pass + + @visitor.when(cil.LabelNode) + def collect_labels_in_func(self, node): + mips_label = self.generate_code_label() + self.register_label(node.label, mips_label) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cil.ProgramNode) + def visit(self, node): + + self.collect_func_names(node) + + self._data_section["default_str"] = mips.StringConst("default_str", "") + for tp in node.dottypes: + self.visit(tp) + + for data in node.dotdata: + self.visit(data) + + for func in node.dotcode: + self.visit(func) + + return mips.ProgramNode([data for data in self._data_section.values()], [tp for tp in self._types.values()], [func for func in self._functions.values()]) + + @visitor.when(cil.TypeNode) + def visit(self, node): + name_label = self.generate_data_label() + self._data_section[node.id] = mips.StringConst(name_label, node.id) + + type_label = self.generate_type_label() + methods = {key: self._name_func_map[value] + for key, value in node.methods} + defaults = [] + if node.id == "String": + defaults = [('value', 'default_str'), ('length', 'type_4_proto')] + new_type = MIPSType(type_label, name_label, node.attributes, methods, len( + self._types), default=defaults) + + self._types[node.id] = new_type + + @visitor.when(cil.DataNode) + def visit(self, node): + label = self.generate_data_label() + self._data_section[node.id] = mips.StringConst(label, node.value) + + @visitor.when(cil.FunctionNode) + def visit(self, node): + used_regs_finder = UsedRegisterFinder() + + label = self._name_func_map[node.id] + params = [param.id for param in node.params] + localvars = [local.id for local in node.localvars] + size_for_locals = len(localvars) * 4 + + new_func = mips.FunctionNode(label, params, localvars) + self.register_function(node.id, new_func) + self.init_function(new_func) + + ra = RegistersAllocator() + + if len(node.instructions): + reg_for_var = ra.get_registers_for_variables( + node.instructions, node.params, 10) + self.memory_manager = MemoryManager( + [Register(name) for name in ['t0', 't1', 't2', + 't3', 't4', 't5', 't6', 't7', 't8', 't9']], lambda x: reg_for_var[x]) + + for instruction in node.instructions: + self.collect_labels_in_func(instruction) + + initial_instructions = [] + if self.in_entry_function(): + initial_instructions.append( + mips.JumpAndLinkNode("mem_manager_init")) + + initial_instructions.extend(push_register(Register('fp'))) + initial_instructions.append( + mips.AddInmediateNode(Register('fp'), SP_REG, 4)) + initial_instructions.append(mips.AddInmediateNode( + SP_REG, SP_REG, -size_for_locals)) + + code_instructions = [] + + code_instructions = list(itt.chain.from_iterable( + [self.visit(instruction) for instruction in node.instructions])) + + final_instructions = [] + + for param in params: + reg = self.memory_manager.get_reg_for_var(param) + if reg is not None: + code_instructions.insert(0, mips.LoadWordNode( + reg, self.get_var_location(param))) + + if not self.in_entry_function(): + used_regs = used_regs_finder.get_used_registers(code_instructions) + for reg in used_regs: + initial_instructions.extend(push_register(reg)) + + for reg in used_regs[::-1]: + final_instructions.extend(pop_register(reg)) + + final_instructions.append(mips.AddInmediateNode( + SP_REG, SP_REG, size_for_locals)) + final_instructions.extend(pop_register(Register('fp'))) + + if not self.in_entry_function(): + final_instructions.append(mips.JumpRegisterNode(RA_REG)) + else: + final_instructions.extend(exit_program()) + + func_instructions = list( + itt.chain(initial_instructions, code_instructions, final_instructions)) + new_func.add_instructions(func_instructions) + + self.finish_functions() + + @visitor.when(cil.InstructionNode) + def visit(self, node): + print(type(node)) + + @visitor.when(cil.AssignNode) + def visit(self, node): + instructions = [] + + reg1 = None + if type(node.right) == cil.VoidNode: + reg1 = ZERO_REG + elif node.right.isnumeric(): + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, int(node.right))) + else: + reg1 = self.memory_manager.get_reg_for_var(node.right) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.right))) + + reg2 = self.memory_manager.get_reg_for_var(node.left) + if reg2 is None: + instructions.append(mips.StoreWordNode( + reg1, self.get_var_location(node.left))) + else: + instructions.append(mips.MoveNode(reg2, reg1)) + + return instructions + + @visitor.when(cil.PlusNode) + def visit(self, node): + instructions = [] + + reg1, reg2 = None, None + if type(node.op_l) == int: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.op_l)) + else: + reg1 = self.memory_manager.get_reg_for_var(node.op_l) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.op_l))) + + if type(node.op_r) == int: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadInmediateNode(reg2, node.op_r)) + else: + reg2 = self.memory_manager.get_reg_for_var(node.op_r) + if reg2 is None: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode( + reg2, self.get_var_location(node.op_r))) + + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.AddNode( + ARG_REGISTERS[0], reg1, reg2)) + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.AddNode(reg3, reg1, reg2)) + + return instructions + + @visitor.when(cil.MinusNode) + def visit(self, node): + instructions = [] + + reg1, reg2 = None, None + if type(node.op_l) == int: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.op_l)) + else: + reg1 = self.memory_manager.get_reg_for_var(node.op_l) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.op_l))) + + if type(node.op_r) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.op_r)) + else: + reg2 = self.memory_manager.get_reg_for_var(node.op_r) + if reg2 is None: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode( + reg2, self.get_var_location(node.op_r))) + + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.SubNode( + ARG_REGISTERS[0], reg1, reg2)) + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.SubNode(reg3, reg1, reg2)) + + return instructions + + @visitor.when(cil.StarNode) + def visit(self, node): + instructions = [] + + reg1, reg2 = None, None + if type(node.op_l) == int: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.op_l)) + else: + reg1 = self.memory_manager.get_reg_for_var(node.op_l) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.op_l))) + + if type(node.op_r) == int: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadInmediateNode(reg2, node.op_r)) + else: + reg2 = self.memory_manager.get_reg_for_var(node.op_r) + if reg2 is None: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode( + reg2, self.get_var_location(node.op_r))) + + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.MultiplyNode( + ARG_REGISTERS[0], reg1, reg2)) + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.MultiplyNode(reg3, reg1, reg2)) + + return instructions + + @visitor.when(cil.DivNode) + def visit(self, node): + instructions = [] + + reg1, reg2 = None, None + if type(node.op_l) == int: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.op_l)) + else: + reg1 = self.memory_manager.get_reg_for_var(node.op_l) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.op_l))) + + if type(node.op_r) == int: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadInmediateNode(reg2, node.op_r)) + else: + reg2 = self.memory_manager.get_reg_for_var(node.op_r) + if reg2 is None: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode( + reg2, self.get_var_location(node.op_r))) + + instructions.append(mips.DivideNode(reg1, reg2)) + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.MoveFromLowNode(ARG_REGISTERS[0])) + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveFromLowNode(reg3)) + + return instructions + + @visitor.when(cil.LessThanNode) + def visit(self, node): + instructions = [] + + if type(node.op_l) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[0], node.op_l)) + else: + reg = self.memory_manager.get_reg_for_var(node.op_l) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.op_l))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + + if type(node.op_r) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[1], node.op_r)) + else: + reg = self.memory_manager.get_reg_for_var(node.op_r) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], self.get_var_location(node.op_r))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[1], reg)) + + instructions.append(mips.JumpAndLinkNode('less')) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.LessEqualNode) + def visit(self, node): + instructions = [] + + if type(node.op_l) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[0], node.op_l)) + else: + reg = self.memory_manager.get_reg_for_var(node.op_l) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.op_l))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + + if type(node.op_r) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[1], node.op_r)) + else: + reg = self.memory_manager.get_reg_for_var(node.op_r) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], self.get_var_location(node.op_r))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[1], reg)) + + instructions.append(mips.JumpAndLinkNode('less_eq')) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.EqualNode) + def visit(self, node): + instructions = [] + + if type(node.op_l) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[0], node.op_l)) + elif type(node.op_l) == cil.VoidNode: + instructions.append( + mips.LoadInmediateNode(ARG_REGISTERS[0], 0)) + else: + reg = self.memory_manager.get_reg_for_var(node.op_l) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.op_l))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + + if type(node.op_r) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[1], node.op_r)) + elif type(node.op_r) == cil.VoidNode: + instructions.append( + mips.LoadInmediateNode(ARG_REGISTERS[1], 0)) + else: + reg = self.memory_manager.get_reg_for_var(node.op_r) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], self.get_var_location(node.op_r))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[1], reg)) + + instructions.append(mips.JumpAndLinkNode("eqs")) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.EqualStrNode) + def visit(self, node): + instructions = [] + + reg = self.memory_manager.get_reg_for_var(node.op_l) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.op_l))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + + reg = self.memory_manager.get_reg_for_var(node.op_r) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], self.get_var_location(node.op_r))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[1], reg)) + + instructions.append(mips.JumpAndLinkNode("eq_string")) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.GetAttrNode) + def visit(self, node): + instructions = [] + + dest = node.dest if type(node.dest) == str else node.dest.id + idx = node.id if type(node.id) == str else node.id.id + comp_type = node.computed_type if type( + node.computed_type) == str else node.computed_type.id + + reg = self.memory_manager.get_reg_for_var(idx) + if reg is None: + reg = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg, self.get_var_location(idx))) + + tp = self._types[comp_type] + offset = (tp.attributes.index(node.attr) + 3) * 4 + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], RegisterRelativeLocation(reg, offset))) + + reg = self.memory_manager.get_reg_for_var(dest) + if reg is None: + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[1], self.get_var_location(dest))) + else: + instructions.append(mips.MoveNode(reg, ARG_REGISTERS[1])) + + return instructions + + @visitor.when(cil.SetAttrNode) + def visit(self, node): + instructions = [] + + idx = node.id if type(node.id) == str else node.id.id + comp_type = node.computed_type if type( + node.computed_type) == str else node.computed_type.name + + tp = self._types[comp_type] + offset = (tp.attributes.index(node.attr) + 3) * 4 + + reg1 = self.memory_manager.get_reg_for_var(idx) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(idx))) + + reg2 = None + if type(node.value) == int: + reg2 = instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[1], node.value)) + else: + reg2 = self.memory_manager.get_reg_for_var(node.value) + if reg2 is None: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.LoadWordNode( + reg2, self.get_var_location(node.value))) + + instructions.append(mips.StoreWordNode( + reg2, RegisterRelativeLocation(reg1, offset))) + + return instructions + + @visitor.when(cil.AllocateNode) + def visit(self, node): + instructions = [] + + tp = 0 + if node.type.isnumeric(): + tp = node.type + else: + tp = self._types[node.type].index + + reg1 = self.memory_manager.get_reg_unusued() + reg2 = self.memory_manager.get_reg_unusued([reg1]) + instructions.extend(push_register(reg1)) + instructions.extend(push_register(reg2)) + + instructions.append(mips.LoadInmediateNode(reg1, tp)) + + instructions.extend(create_object(reg1, reg2)) + + instructions.extend(pop_register(reg2)) + instructions.extend(pop_register(reg1)) + + reg3 = self.memory_manager.get_reg_for_var(node.dest) + if reg3 is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg3, V0_REG)) + + return instructions + + @visitor.when(cil.TypeOfNode) + def visit(self, node): + instructions = [] + + reg1 = self.memory_manager.get_reg_for_var(node.id) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.id))) + + reg2 = self.memory_manager.get_reg_for_var(node.dest) + if reg2 is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[1], self.get_var_location(node.dest))) + else: + instructions.append(mips.LoadWordNode( + reg2, RegisterRelativeLocation(reg1, 0))) + + return instructions + + @visitor.when(cil.StaticCallNode) + def visit(self, node): + instructions = [] + label = self._name_func_map[node.function] + instructions.append(mips.JumpAndLinkNode(label)) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + if self._pushed_args > 0: + instructions.append(mips.AddInmediateNode( + SP_REG, SP_REG, self._pushed_args * 4)) + self.clean_pushed_args() + return instructions + + @visitor.when(cil.DynamicCallNode) + def visit(self, node): + instructions = [] + + comp_tp = self._types[node.computed_type] + method_index = list(comp_tp.methods).index(node.function) + reg = self.memory_manager.get_reg_for_var(node.type) + if reg is None: + reg = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg, self.get_var_location(node.type))) + + instructions.append(mips.LoadAddressNode( + ARG_REGISTERS[1], "proto_table")) + instructions.append(mips.ShiftLeftLogicalNode( + ARG_REGISTERS[2], reg, 2)) + instructions.append(mips.AddUnsignedNode( + ARG_REGISTERS[1], ARG_REGISTERS[1], ARG_REGISTERS[2])) + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], RegisterRelativeLocation(ARG_REGISTERS[1], 0))) + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], RegisterRelativeLocation(ARG_REGISTERS[1], 8))) + instructions.append(mips.AddInmediateUnsignedNode( + ARG_REGISTERS[1], ARG_REGISTERS[1], method_index * 4)) + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], RegisterRelativeLocation(ARG_REGISTERS[1], 0))) + instructions.append( + mips.JumpRegisterAndLinkNode(ARG_REGISTERS[1])) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + if self._pushed_args > 0: + instructions.append(mips.AddInmediateNode( + SP_REG, SP_REG, self._pushed_args * 4)) + self.clean_pushed_args() + + return instructions + + @visitor.when(cil.ArgNode) + def visit(self, node): + self.push_arg() + instructions = [] + if type(node.id) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[0], node.id)) + instructions.extend(push_register(ARG_REGISTERS[0])) + else: + reg = self.memory_manager.get_reg_for_var(node.id) + if reg is None: + reg = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg, self.get_var_location(node.id))) + instructions.extend(push_register(reg)) + return instructions + + @visitor.when(cil.IfGotoNode) + def visit(self, node): + instructions = [] + + mips_label = self.get_mips_label(node.label) + + reg = self.memory_manager.get_reg_for_var(node.if_cond) + if reg is None: + reg = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.if_cond))) + + instructions.append(mips.BranchOnNotEqualNode( + reg, ZERO_REG, mips_label)) + + return instructions + + @visitor.when(cil.LabelNode) + def visit(self, node): + return [mips.LabelNode(self.get_mips_label(node.label))] + + @visitor.when(cil.GotoNode) + def visit(self, node): + mips_label = self.get_mips_label(node.label) + return [mips.JumpNode(mips_label)] + + @visitor.when(cil.ReturnNode) + def visit(self, node): + instructions = [] + + if node.id is None: + instructions.append(mips.LoadInmediateNode(V0_REG, 0)) + elif isinstance(node.id, int): + instructions.append( + mips.LoadInmediateNode(V0_REG, node.id)) + elif isinstance(node.id, cil.VoidNode): + instructions.append(mips.LoadInmediateNode(V0_REG, 0)) + else: + reg = self.memory_manager.get_reg_for_var(node.id) + if reg is None: + instructions.append(mips.LoadWordNode( + V0_REG, self.get_var_location(node.id))) + else: + instructions.append(mips.MoveNode(V0_REG, reg)) + return instructions + + @visitor.when(cil.LoadNode) + def visit(self, node): + instructions = [] + + string_location = LabelRelativeLocation( + self._data_section[node.msg.id].label, 0) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.LoadAddressNode( + ARG_REGISTERS[0], string_location)) + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.LoadAddressNode(reg, string_location)) + + return instructions + + @visitor.when(cil.LengthNode) + def visit(self, node): + instructions = [] + + reg = self.memory_manager.get_reg_for_var(node.id) + if reg is None: + reg = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg, self.get_var_location(node.id))) + + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + instructions.append(mips.JumpAndLinkNode("len")) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.ConcatNode) + def visit(self, node): + instructions = [] + + reg = self.memory_manager.get_reg_for_var(node.s1) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.s1))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + + reg = self.memory_manager.get_reg_for_var(node.s2) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], self.get_var_location(node.s2))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[1], reg)) + + reg = self.memory_manager.get_reg_for_var(node.length) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[2], self.get_var_location(node.lenght))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[2], reg)) + + instructions.append(mips.JumpAndLinkNode("concat")) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.SubstringNode) + def visit(self, node): + instructions = [] + + reg = self.memory_manager.get_reg_for_var(node.s) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.s))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + + if type(node.i) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[1], node.i)) + else: + reg = self.memory_manager.get_reg_for_var(node.i) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[1], self.get_var_location(node.i))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[1], reg)) + + if type(node.length) == int: + instructions.append(mips.LoadInmediateNode( + ARG_REGISTERS[2], node.length)) + else: + reg = self.memory_manager.get_reg_for_var(node.length) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[2], self.get_var_location(node.length))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[2], reg)) + + instructions.append(mips.JumpAndLinkNode("sub_string")) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + return instructions + + @visitor.when(cil.ReadStrNode) + def visit(self, node): + instructions = [] + instructions.append(mips.JumpAndLinkNode("get_string")) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.PrintStrNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(V0_REG, 4)) + + reg = self.memory_manager.get_reg_for_var(node.value) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0]. self.get_var_location(node.value))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(cil.ErrorNode) + def visit(self, node): + instructions = [] + + mips_label = self._data_section[node.data_node.id].label + + instructions.append(mips.LoadInmediateNode(V0_REG, 4)) + instructions.append(mips.LoadAddressNode( + ARG_REGISTERS[0], mips_label)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.LoadInmediateNode(V0_REG, 10)) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(cil.TypeNameNode) + def visit(self, node): + instructions = [] + + reg1 = self.memory_manager.get_reg_for_var(node.type) + pushed = False + if reg1 is None: + reg1 = self.memory_manager.get_reg_unusued() + instructions.extend(push_register(reg1)) + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.type))) + pushed = True + + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], RegisterRelativeLocation(reg1, 0))) + + if pushed: + instructions.extend(pop_register(reg1)) + + instructions.append(mips.ShiftLeftLogicalNode( + ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) + instructions.append(mips.LoadAddressNode( + ARG_REGISTERS[1], "type_name_table")) + instructions.append(mips.AddUnsignedNode( + ARG_REGISTERS[0], ARG_REGISTERS[0], ARG_REGISTERS[1])) + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], RegisterRelativeLocation(ARG_REGISTERS[0], 0))) + + reg2 = self.memory_manager.get_reg_for_var(node.dest) + if reg2 is None: + instructions.append(mips.StoreWordNode( + ARG_REGISTERS[0], self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg2, ARG_REGISTERS[0])) + + return instructions + + @visitor.when(cil.NameNode) + def visit(self, node): + instructions = [] + + save = False + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + reg = ARG_REGISTERS[0] + save = True + + instructions.append(mips.LoadAddressNode( + reg, "type_name_table")) + + tp_number = self._types[node.id].index + instructions.append( + mips.AddInmediateUnsignedNode(reg, reg, tp_number*4)) + instructions.append(mips.LoadWordNode( + reg, RegisterRelativeLocation(reg, 0))) + + if save: + instructions.append(mips.StoreWordNode( + reg, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(cil.AbortNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(V0_REG, 10)) + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(cil.CopyNode) + def visit(self, node): + instructions = [] + + pushed = False + reg = self.memory_manager.get_reg_for_var(node.copy) + if reg is None: + reg = self.memory_manager.get_reg_unusued() + instructions.extend(push_register(reg)) + instructions.append(mips.LoadWordNode( + reg, self.get_var_location(node.copy))) + pushed = True + + instructions.extend(copy_object(reg, ARG_REGISTERS[3])) + + if pushed: + instructions.extend(pop_register(reg)) + + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.ReadIntNode) + def visit(self, node): + instructions = [] + + instructions.append(mips.LoadInmediateNode(V0_REG, 5)) + instructions.append(mips.SyscallNode()) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode( + V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, V0_REG)) + + return instructions + + @visitor.when(cil.PrintIntNode) + def visit(self, node): + instructions = [] + instructions.append(mips.LoadInmediateNode(V0_REG, 1)) + + reg = self.memory_manager.get_reg_for_var(node.value) + if reg is None: + instructions.append(mips.LoadWordNode( + ARG_REGISTERS[0], self.get_var_location(node.value))) + else: + instructions.append(mips.MoveNode(ARG_REGISTERS[0], reg)) + + instructions.append(mips.SyscallNode()) + + return instructions + + @visitor.when(cil.ComplementNode) + def visit(self, node): + instructions = [] + + reg1 = None + + if type(node.id) == int: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, node.id)) + else: + reg1 = self.memory_manager.get_reg_for_var(node.id) + if reg1 is None: + reg1 = ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode( + reg1, self.get_var_location(node.id))) + + reg2 = self.memory_manager.get_reg_for_var(node.dest) + if reg2 is None: + reg2 = ARG_REGISTERS[1] + instructions.append(mips.ComplementNode(reg2, reg1)) + instructions.append(mips.AddInmediateNode(reg2, reg2, 1)) + instructions.append(mips.StoreWordNode( + reg2, self.get_var_location(node.dest))) + else: + instructions.append(mips.ComplementNode(reg2, reg1)) + instructions.append(mips.AddInmediateNode(reg2, reg2, 1)) + + return instructions diff --git a/src/utils/code_generation/mips/print_MIPS_AST.py b/src/utils/code_generation/mips/print_MIPS_AST.py new file mode 100644 index 000000000..4c807ad54 --- /dev/null +++ b/src/utils/code_generation/mips/print_MIPS_AST.py @@ -0,0 +1,1396 @@ +from utils.code_generation.mips.AST_MIPS import * +import cmp.visitor as visitor +from utils.code_generation.mips.AST_MIPS import mips_ast as nodes +from utils.code_generation.mips.utils_mips import * + + +class PrintMIPS: + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(nodes.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_name_table"}:\n' + \ + "\n".join( + [f"\t.word\t{tp.string_name_label}" for tp in node.types]) + proto_table = f'{"proto_table"}:\n' + \ + "\n".join([f"\t.word\t{tp.label}_proto" for tp in node.types]) + + types = "\n\n".join([self.visit(tp) for tp in node.types]) + + code = "\n".join([self.visit(func) for func in node.functions]) + return f'{data_section_header}\n{static_strings}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\t.text\n\t.globl main\n{code}\n\n{mips_static_code()}' + + @visitor.when(nodes.FunctionNode) + def visit(self, node): + instr = [self.visit(instruction) for instruction in node.instructions] + instr2 = [inst for inst in instr if type(inst) == str] + instructions = "\n\t".join(instr2) + return f'{node.label}:\n\t{instructions}' + + @visitor.when(nodes.AddNode) + def visit(self, node): + return f"add {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(nodes.SubNode) + def visit(self, node): + return f"sub {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(nodes.MultiplyNode) + def visit(self, node): + return f"mul {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.reg3)}" + + @visitor.when(nodes.DivideNode) + def visit(self, node): + return f"div {self.visit(node.reg1)} {self.visit(node.reg2)}" + + @visitor.when(nodes.LabelNode) + def visit(self, node): + return f"{node.name}:" + + @visitor.when(nodes.ComplementNode) + def visit(self, node): + return f"not {self.visit(node.reg1)} {self.visit(node.reg2)}" + + @visitor.when(nodes.StringConst) + def visit(self, node): + return f'{node.label}: .asciiz "{node.string}"' + + @visitor.when(nodes.MoveNode) + def visit(self, node): + return f'move {self.visit(node.reg1)} {self.visit(node.reg2 )}' + + @visitor.when(nodes.LoadInmediateNode) + def visit(self, node): + return f'li {self.visit(node.reg)}, {self.visit(node.value)}' + + @visitor.when(nodes.LoadWordNode) + def visit(self, node): + return f'lw {self.visit(node.reg)}, {self.visit(node.addr)}' + + @visitor.when(nodes.SyscallNode) + def visit(self, node): + return 'syscall' + + @visitor.when(nodes.LoadAddressNode) + def visit(self, node): + return f'la {self.visit(node.reg)}, {self.visit(node.label)}' + + @visitor.when(nodes.StoreWordNode) + def visit(self, node): + return f'sw {self.visit(node.reg)}, {self.visit(node.addr)}' + + @visitor.when(nodes.JumpAndLinkNode) + def visit(self, node): + return f'jal {node.label}' + + @visitor.when(nodes.JumpRegisterAndLinkNode) + def visit(self, node): + return f'jal {self.visit(node.reg)}' + + @visitor.when(nodes.JumpRegisterNode) + def visit(self, node): + return f'jr {self.visit(node.reg)}' + + @visitor.when(nodes.AddInmediateNode) + def visit(self, node): + return f'addi {self.visit(node.dest)}, {self.visit(node.src)}, {self.visit(node.value)}' + + @visitor.when(nodes.AddInmediateUnsignedNode) + def visit(self, node): + return f"addiu {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.value)}" + + @visitor.when(nodes.AddUnsignedNode) + def visit(self, node): + return f"addu {self.visit(node.dest)} {self.visit(node.sum1)} {self.visit(node.sum2)}" + + @visitor.when(nodes.ShiftLeftLogicalNode) + def visit(self, node): + return f"sll {self.visit(node.dest)} {self.visit(node.src)} {node.bits}" + + @visitor.when(nodes.BranchOnNotEqualNode) + def visit(self, node): + return f"bne {self.visit(node.reg1)} {self.visit(node.reg2)} {node.label}" + + @visitor.when(nodes.JumpNode) + def visit(self, node): + return f"j {node.label}" + + @visitor.when(nodes.MoveFromLowNode) + def visit(self, node): + return f"mflo {self.visit(node.reg)}" + + @visitor.when(Register) + def visit(self, node): + return f'${node.name}' + + @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(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_finish = f"\t.word\t{-1}" + proto = f"{proto_begin}\n{proto_attr}\n{proto_finish}" if proto_attr != "" else f"{proto_begin}\n{proto_finish}" + + return f'{dispatch_table}\n\n{proto}' + + @visitor.when(int) + def visit(self, node): + return str(node) + + @visitor.when(str) + def visit(self, node): + return node + + +def mips_static_code(): + return \ + local_vars() +\ + verify_obj() +\ + verify_obj_not_obj() +\ + verify_obj_is_obj() +\ + verify_obj_finish() +\ + concat() +\ + concat_size_alligned() +\ + concat_allign_size() +\ + concat_copy_first_cycle() +\ + concat_copy_second_cycle() +\ + concat_finish() +\ + copy() +\ + copy_cycle() +\ + copy_finish() +\ + eqs() +\ + eqs_eq() +\ + eqs_finish() +\ + eq_string() +\ + eq_string_cycle() +\ + eq_string_not_eq() +\ + eq_string_eq() +\ + eq_string_finish() +\ + extend_block() +\ + extend_block_extend() +\ + extend_block_finish() +\ + extend_heap() +\ + free_block() +\ + free_block_cycle_used_list() +\ + free_block_cycle_free_list() +\ + free_block_founded_prev() +\ + free_block_finish() +\ + get_gc() +\ + get_gc_cycle() +\ + get_gc_dfs() +\ + get_gc_outer_cycle() +\ + get_gc_extend() +\ + get_gc_free() +\ + get_gc_free_cycle() +\ + get_gc_free_cycle_free() +\ + get_gc_finish() +\ + get_gc_rec_extend() +\ + get_gc_rec_extend_attr_cycle() +\ + get_gc_rec_extend_string_obj() +\ + get_gc_rec_extend_finish() +\ + less_eq() +\ + less_eq_true() +\ + less_eq_finish() +\ + less() +\ + less_true() +\ + less_finish() +\ + lenx() +\ + len_cycle() +\ + len_finish() +\ + malloc() +\ + malloc_finish() +\ + malloc_cycle() +\ + malloc_search_finish() +\ + malloc_alloc_new_block() +\ + malloc_big_block() +\ + malloc_verify_valid_block() +\ + malloc_valid_block() +\ + malloc_first_valid_block() +\ + mem_manager_init() + \ + get_string() +\ + get_string_larger_block_cycle() +\ + get_string_reading() +\ + get_string_look_nl() +\ + get_string_zero_founded() +\ + get_string_nl_founded() +\ + get_string_nl_founded_alligned() +\ + get_string_no_nl() +\ + get_string_dup() +\ + get_string_extend_heap() +\ + get_string_last_block_cycle() +\ + get_string_last_block_founded() +\ + get_string_copy_prev() +\ + get_string_copy_cycle() +\ + get_string_copy_finish() +\ + get_string_finish() +\ + get_string_new_block() +\ + get_string_new_block_search_last() +\ + get_string_new_block_create() +\ + get_string_new_block_extended() +\ + divide_block() +\ + divide_block_same_size() +\ + divide_block_error_small() +\ + divide_block_finish() +\ + sub_string() +\ + sub_string_allign_size() +\ + sub_string_new_block() +\ + sub_string_copy_cycle() +\ + sub_string_finish() +\ + use_block() +\ + use_block_cycle() +\ + use_block_founded() +\ + use_block_finish() + + +# Mips labels + +def local_vars(): + return ''' + + alloc_size = 2048 + free_list = 0 + header_size = 12 + header_size_slot = 0 + header_next_slot = 4 + header_reachable_slot = 8 + init_alloc_size = 28 + int_type = 0 + meta_data_obj_size = 4 + neg_header_size = -12 + new_line = 10 + obj_mark = -1 + obj_extended = -2 + reachable = 1 + state_size = 4 + stack_base = -4 + string_size_treshold = 1024 + string_type = 0 + total_alloc_size = 2060 + num_type = 0 + used_list = header_size\n\n''' + + +def verify_obj(): + return '''verify_obj: + 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 + + + blt $t0 $gp verify_obj_not_obj + bgt $t0 $t1 verify_obj_not_obj + lw $t2 0($t0) + blt $t2 $zero verify_obj_not_obj + la $t3 num_type + lw $t3 0($t3) + bge $t2 $t3 verify_obj_not_obj + + addiu $t0 $t0 4 + blt $t0 $gp verify_obj_not_obj + bgt $t0 $t1 verify_obj_not_obj + lw $t2 0($t0) + + addiu $t0 $t0 8 + + + li $t3 meta_data_obj_size + sub $t2 $t2 $t3 + sll $t2 $t2 2 + addu $t0 $t0 $t2 + + + blt $t0 $gp verify_obj_not_obj + bgt $t0 $t1 verify_obj_not_obj + lw $t2 0($t0) + beq $t2 obj_mark verify_obj_is_obj + beq $t2 obj_extended verify_obj_is_obj\n\n''' + + +def verify_obj_not_obj(): + return '''verify_obj_not_obj: + li $v0 0 + j verify_obj_finish\n\n''' + + +def verify_obj_is_obj(): + return '''verify_obj_is_obj: + li $v0 1\n\n''' + + +def verify_obj_finish(): + return '''verify_obj_finish: + 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\n\n''' + + +def concat(): + return '''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\n\n''' + + +def concat_size_alligned(): + return '''concat_size_alligned: + jal malloc + move $t2 $v0 + j concat_copy_first_cycle\n\n''' + + +def concat_allign_size(): + return '''concat_allign_size: + sub $t2 $t2 $a0 + add $a0 $a2 $t2 + addiu $a0 $a0 1 + j concat_size_alligned\n\n''' + + +def concat_copy_first_cycle(): + return '''concat_copy_first_cycle: + lb $a0 0($t0) + beq $a0 $zero concat_copy_second_cycle + sb $a0 0($t2) + addiu $t0 $t0 1 + addiu $t2 $t2 1 + j concat_copy_first_cycle\n\n''' + + +def concat_copy_second_cycle(): + return '''concat_copy_second_cycle: + lb $a0 0($t1) + beq $a0 $zero concat_finish + sb $a0 0($t2) + addiu $t1 $t1 1 + addiu $t2 $t2 1 + j concat_copy_second_cycle\n\n''' + + +def concat_finish(): + return '''concat_finish: + 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\n\n''' + + +def copy(): + return '''copy: + addiu $sp $sp -16 + sw $a0 0($sp) + sw $a1 4($sp) + sw $a2 8($sp) + sw $t0 12($sp)\n\n''' + + +def copy_cycle(): + return '''copy_cycle: + beq $a2 $zero copy_finish + lw $t0 0($a0) + sw $t0 0($a1) + addiu $a0 $a0 4 + addiu $a1 $a1 4 + addi $a2 $a2 -4 + j copy_cycle\n\n''' + + +def copy_finish(): + return '''copy_finish: + lw $a0 0($sp) + lw $a1 4($sp) + lw $a2 8($sp) + lw $t0 12($sp) + addiu $sp $sp 16 + + jr $ra\n\n''' + + +def eqs(): + return '''eqs: + beq $a0 $a1 eqs_eq + li $v0 0 + j eqs_finish\n\n''' + + +def eqs_eq(): + return '''eqs_eq: + li $v0 1\n\n''' + + +def eqs_finish(): + return '''eqs_finish: + jr $ra\n\n''' + + +def eq_string(): + return '''eq_string: + 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\n\n''' + + +def eq_string_cycle(): + return '''eq_string_cycle: + lb $t2 0($t0) + lb $t3 0($t1) + bne $t2 $t3 eq_string_not_eq + beq $t2 $zero eq_string_eq + + addiu $t0 $t0 1 + addiu $t1 $t1 1 + j eq_string_cycle\n\n''' + + +def eq_string_not_eq(): + return '''eq_string_not_eq: + move $v0 $zero + j eq_string_finish\n\n''' + + +def eq_string_eq(): + return '''eq_string_eq: + li $v0 1\n\n''' + + +def eq_string_finish(): + return '''eq_string_finish: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + addiu $sp $sp 16 + + jr $ra\n\n''' + + +def extend_block(): + return '''extend_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 + + beq $t0 $a0 extend_block_finish + move $t0 $a0 + + 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 extend_block_extend + j extend_block_finish\n\n''' + + +def extend_block_extend(): + return '''extend_block_extend: + 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)\n\n''' + + +def extend_block_finish(): + return '''extend_block_finish: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + addiu $sp $sp 16 + + jr $ra\n\n''' + + +def extend_heap(): + return '''extend_heap: + addiu $sp $sp -12 + sw $a0 0($sp) + sw $a1 4($sp) + sw $t0 8($sp) + + + li $v0 9 + addiu $a0 $a1 header_size + syscall + + + move $t0 $a1 + sw $t0 header_size_slot($v0) + sw $zero header_next_slot($v0) + sw $zero header_reachable_slot($v0) + + + 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\n\n''' + + +def free_block(): + return '''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 + + addiu $t3 $gp used_list\n\n''' + + +def free_block_cycle_used_list(): + return '''free_block_cycle_used_list: + lw $t4 header_next_slot($t3) + beq $t4 $t0 free_block_cycle_free_list + move $t3 $t4 + j free_block_cycle_used_list\n\n''' + + +def free_block_cycle_free_list(): + return '''free_block_cycle_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_cycle_free_list\n\n''' + + +def free_block_founded_prev(): + return '''free_block_founded_prev: + lw $t4 header_next_slot($t0) + sw $t4 header_next_slot($t3) + + + sw $t2 header_next_slot($t0) + sw $t0 header_next_slot($t1)\n\n''' + + +def free_block_finish(): + return '''free_block_finish: + + move $a0 $t0 + jal extend_block + move $a0 $t1 + jal extend_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\n\n''' + + +def get_gc(): + return '''get_gc: + 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 + addiu $t0 $sp 20 + lw $t1 stack_base($gp) + + li $t2 1\n\n''' + + +def get_gc_cycle(): + return '''get_gc_cycle: + + addiu $t0 $t0 4 + beq $t0 $t1 get_gc_dfs + + lw $a0 0($t0) + jal verify_obj + + bne $v0 $t2 get_gc_cycle + + addiu $a0 $a0 neg_header_size + sw $t3 header_reachable_slot($a0) + + j get_gc_cycle\n\n''' + + +def get_gc_dfs(): + return '''get_gc_dfs: + addiu $t1 $gp used_list\n\n''' + + +def get_gc_outer_cycle(): + return '''get_gc_outer_cycle: + lw $t1 header_next_slot($t1) + beq $t1 $zero get_gc_free + lw $t2 header_reachable_slot($t1) + beq $t2 reachable get_gc_extend + j get_gc_outer_cycle\n\n''' + + +def get_gc_extend(): + return '''get_gc_extend: + addiu $a0 $t1 header_size + jal get_gc_rec_extend + j get_gc_outer_cycle\n\n''' + + +def get_gc_free(): + return '''get_gc_free: + addiu $t0 $gp used_list + lw $t0 header_next_slot($t0)\n\n''' + + +def get_gc_free_cycle(): + return '''get_gc_free_cycle: + beq $t0 $zero get_gc_finish + lw $t1 header_reachable_slot($t0) + bne $t1 reachable get_gc_free_cycle_free + sw $zero header_reachable_slot($t0) + move $a0 $t0 + jal verify_obj + beq $v0 $zero get_gc_free_cycle + li $t1 obj_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 get_gc_free_cycle\n\n''' + + +def get_gc_free_cycle_free(): + return '''get_gc_free_cycle_free: + move $a0 $t0 + lw $t0 header_next_slot($t0) + jal free_block + j get_gc_free_cycle\n\n''' + + +def get_gc_finish(): + return '''get_gc_finish: + 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\n\n''' + + +def get_gc_rec_extend(): + return '''get_gc_rec_extend: + addiu $sp $sp -16 + sw $a0 0($sp) + sw $t0 4($sp) + sw $t1 8($sp) + sw $ra 12($sp) + + jal verify_obj + beq $v0 $zero get_gc_rec_extend_finish + + lw $t0 4($a0) + sll $t0 $t0 2 + addiu $t0 $t0 -4 + addu $t0 $a0 $t0 + lw $t1 0($t0) + beq $t1 obj_extended get_gc_rec_extend_finish + + + li $t1 reachable + addiu $a0 $a0 neg_header_size + sw $t1 header_reachable_slot($a0) + addiu $a0 $a0 header_size + + + li $t1 obj_extended + sw $t1 0($t0) + + lw $t0 0($a0) + + + la $t1 int_type + lw $t1 0($t1) + beq $t0 $t1 get_gc_rec_extend_finish + + la $t1 string_type + lw $t1 0($t1) + beq $t0 $t1 get_gc_rec_extend_string_obj + + lw $t0 4($a0) + li $t1 meta_data_obj_size + sub $t0 $t0 $t1 + + addiu $t1 $a0 12\n\n''' + + +def get_gc_rec_extend_attr_cycle(): + return '''get_gc_rec_extend_attr_cycle: + beq $t0 $zero get_gc_rec_extend_finish + lw $a0 0($t1) + jal get_gc_rec_extend + addiu $t1 $t1 4 + sub $t0 $t0 1 + j get_gc_rec_extend_attr_cycle\n\n''' + + +def get_gc_rec_extend_string_obj(): + return '''get_gc_rec_extend_string_obj: + lw $t0 8($a0) + addiu $t0 $t0 neg_header_size + li $t1 reachable + sw $t1 header_reachable_slot($t0)\n\n''' + + +def get_gc_rec_extend_finish(): + return '''get_gc_rec_extend_finish: + lw $a0 0($sp) + lw $t0 4($sp) + lw $t1 8($sp) + lw $ra 12($sp) + addiu $sp $sp 16 + + jr $ra\n\n''' + + +def less_eq(): + return '''less_eq: + ble $a0 $a1 less_eq_true + li $v0 0 + j less_eq_finish\n\n''' + + +def less_eq_true(): + return '''less_eq_true: + li $v0 1\n\n''' + + +def less_eq_finish(): + return '''less_eq_finish: + jr $ra\n\n''' + + +def less(): + return '''less: + blt $a0 $a1 less_true + li $v0 0 + j less_finish\n\n''' + + +def less_true(): + return '''less_true: + li $v0 1\n\n''' + + +def less_finish(): + return '''less_finish: + jr $ra\n\n''' + + +def lenx(): + return '''len: + addiu $sp $sp -8 + sw $t0 0($sp) + sw $t1 4($sp) + + move $t0 $a0 + move $v0 $zero\n\n''' + + +def len_cycle(): + return '''len_cycle: + lb $t1 0($t0) + beq $t1 $zero len_finish + addi $v0 $v0 1 + addiu $t0 $t0 1 + j len_cycle\n\n''' + + +def len_finish(): + return '''len_finish: + lw $t0 0($sp) + lw $t1 4($sp) + addiu $sp $sp 8 + + jr $ra\n\n''' + + +def malloc(): + return '''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_cycle\n\n''' + + +def malloc_finish(): + return '''malloc_finish: + move $a0 $v0 + lw $a1 8($sp) + jal divide_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\n\n''' + + +def malloc_cycle(): + return '''malloc_cycle: + move $t2 $t0 + lw $t0 header_next_slot($t0) + beq $t0 $zero malloc_search_finish + j malloc_verify_valid_block\n\n''' + + +def malloc_search_finish(): + return '''malloc_search_finish: + beq $v0 $zero malloc_alloc_new_block + j malloc_finish\n\n''' + + +def malloc_alloc_new_block(): + return '''malloc_alloc_new_block: + li $t1 alloc_size + move $t3 $t2 + move $a1 $a0 + move $a0 $t2 + bge $a1 $t1 malloc_big_block + li $a1 alloc_size + jal extend_heap + + j malloc_finish\n\n''' + + +def malloc_big_block(): + return '''malloc_big_block: + jal extend_heap + j malloc_finish\n\n''' + + +def malloc_verify_valid_block(): + return '''malloc_verify_valid_block: + lw $t1 header_size_slot($t0) + bge $t1 $a0 malloc_valid_block + j malloc_cycle\n\n''' + + +def malloc_valid_block(): + return '''malloc_valid_block: + beq $v0 $zero malloc_first_valid_block + bge $t1 $v1 malloc_cycle + move $v0 $t0 + move $v1 $t1 + move $t3 $t2 + j malloc_cycle\n\n''' + + +def malloc_first_valid_block(): + return '''malloc_first_valid_block: + move $v0 $t0 + move $v1 $t1 + move $t3 $t2 + j malloc_cycle\n\n''' + + +def mem_manager_init(): + return '''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 + move $gp $v0 + addiu $gp $gp state_size + + sw $zero header_size_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) + 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\n\n''' + + +def get_string(): + return '''get_string: + 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\n\n''' + + +def get_string_larger_block_cycle(): + return '''get_string_larger_block_cycle: + lw $t0 header_next_slot($t0) + beq $t0 $zero get_string_reading + lw $t3 header_size_slot($t0) + bge $t1 $t3 get_string_larger_block_cycle + move $t1 $t3 + move $t2 $t0 + j get_string_larger_block_cycle\n\n''' + + +def get_string_reading(): + return '''get_string_reading: + beq $t1 $zero get_string_new_block + move $a1 $t1 + li $v0 8 + addiu $a0 $t2 header_size + syscall + move $t0 $a0 + move $t1 $zero\n\n''' + + +def get_string_look_nl(): + return '''get_string_look_nl: + lb $t2 0($t0) + beq $t2 new_line get_string_nl_founded + beq $t2 $zero get_string_zero_founded + addi $t1 $t1 1 + addi $t0 $t0 1 + j get_string_look_nl\n\n''' + + +def get_string_zero_founded(): + return '''get_string_zero_founded: + blt $t1 $t3 get_string_nl_founded + j get_string_no_nl\n\n''' + + +def get_string_nl_founded(): + return '''get_string_nl_founded: + sb $zero 0($t0) + addi $t1 $t1 1 + li $t2 4 + div $t1 $t2 + mfhi $t3 + beq $t3 $zero get_string_nl_founded_alligned + sub $t2 $t2 $t3 + add $t1 $t1 $t2\n\n''' + + +def get_string_nl_founded_alligned(): + return '''get_string_nl_founded_alligned: + move $a1 $t1 + addiu $a0 $a0 neg_header_size + jal divide_block + jal use_block + + addiu $v0 $a0 header_size + j get_string_finish\n\n''' + + +def get_string_no_nl(): + return '''get_string_no_nl: + addi $t1 $t1 1 + blt $t1 string_size_treshold get_string_dup + addi $t1 $t1 alloc_size + j get_string_extend_heap\n\n''' + + +def get_string_dup(): + return '''get_string_dup: + sll $t1 $t1 1\n\n''' + + +def get_string_extend_heap(): + return '''get_string_extend_heap: + move $a1 $t1 + move $t0 $a0 + addiu $a0 $gp free_list\n\n''' + + +def get_string_last_block_cycle(): + return '''get_string_last_block_cycle: + lw $t1 header_next_slot($a0) + beq $t1 $zero get_string_last_block_founded + lw $a0 header_next_slot($a0) + j get_string_last_block_cycle\n\n''' + + +def get_string_last_block_founded(): + return '''get_string_last_block_founded: + jal extend_heap + jal extend_block + lw $t1 header_next_slot($a0) + bne $t1 $zero get_string_copy_prev + move $t1 $a0\n\n''' + + +def get_string_copy_prev(): + return '''get_string_copy_prev: + lw $t3 header_size_slot($t1) + move $t2 $zero + move $t5 $t1 + addiu $t1 $t1 header_size\n\n''' + + +def get_string_copy_cycle(): + return '''get_string_copy_cycle: + lb $t4 0($t0) + beq $t4 $zero get_string_copy_finish + sb $t4 0($t1) + addi $t2 $t2 1 + addi $t0 $t0 1 + addi $t1 $t1 1 + j get_string_copy_cycle\n\n''' + + +def get_string_copy_finish(): + return '''get_string_copy_finish: + 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 get_string_look_nl\n\n''' + + +def get_string_finish(): + return '''get_string_finish: + 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\n\n''' + + +def get_string_new_block(): + return '''get_string_new_block: + addiu $t0 $gp free_list\n\n''' + + +def get_string_new_block_search_last(): + return '''get_string_new_block_search_last: + lw $t1 header_next_slot($t0) + beq $t1 $zero get_string_new_block_create + move $t0 $t1 + j get_string_new_block_search_last\n\n''' + + +def get_string_new_block_create(): + return '''get_string_new_block_create: + move $a0 $t0 + li $a1 alloc_size + jal extend_heap + jal extend_block + lw $t2 header_next_slot($a0) + beq $t2 $zero get_string_new_block_extended + lw $t1 header_size_slot($t2) + j get_string_reading\n\n''' + + +def get_string_new_block_extended(): + return '''get_string_new_block_extended: + move $t2 $a0 + lw $t1 header_size_slot($a0) + j get_string_reading\n\n''' + + +def divide_block(): + return '''divide_block: + addiu $sp $sp -16 + sw $t0 0($sp) + sw $t1 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + + + lw $t0 header_size_slot($a0) + bgt $a1 $t0 divide_block_error_small + + + sub $t0 $t0 $a1 + li $t1 header_size + ble $t0 $t1 divide_block_same_size + + + addu $t0 $a0 $a1 + addiu $t0 $t0 header_size + + + lw $t1 header_next_slot($a0) + sw $t1 header_next_slot($t0) + sw $t0 header_next_slot($a0) + + lw $t1 header_size_slot($a0) + 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 divide_block_finish\n\n''' + + +def divide_block_same_size(): + return '''divide_block_same_size: + move $v0 $a0 + j divide_block_finish\n\n''' + + +def divide_block_error_small(): + return '''divide_block_error_small: + j divide_block_finish\n\n''' + + +def divide_block_finish(): + return '''divide_block_finish: + lw $t0 0($sp) + lw $t1 4($sp) + lw $a0 8($sp) + lw $a1 12($sp) + addiu $sp $sp 16 + + jr $ra\n\n''' + + +def sub_string(): + return '''sub_string: + 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 sub_string_allign_size + move $t1 $t3 + j sub_string_new_block\n\n''' + + +def sub_string_allign_size(): + return '''sub_string_allign_size: + sub $t1 $t1 $t2 + add $t1 $t1 $t3\n\n''' + + +def sub_string_new_block(): + return '''sub_string_new_block: + move $a0 $t1 + jal malloc + move $t3 $v0 + move $t1 $zero + addu $t0 $t0 $a1\n\n''' + + +def sub_string_copy_cycle(): + return '''sub_string_copy_cycle: + beq $t1 $a2 sub_string_finish + lb $t2 0($t0) + sb $t2 0($t3) + addiu $t0 $t0 1 + addiu $t3 $t3 1 + addiu $t1 $t1 1 + j sub_string_copy_cycle\n\n''' + + +def sub_string_finish(): + return '''sub_string_finish: + 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\n\n''' + + +def use_block(): + return '''use_block: + addiu $sp $sp -12 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + + addiu $t0 $gp free_list\n\n''' + + +def use_block_cycle(): + return '''use_block_cycle: + move $t1 $t0 + lw $t0 header_next_slot($t0) + beq $t0 $zero use_block_finish + beq $t0 $a0 use_block_founded + j use_block_cycle\n\n''' + + +def use_block_founded(): + return '''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)\n\n''' + + +def use_block_finish(): + return '''use_block_finish: + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + addiu $sp $sp 12 + + jr $ra\n\n''' diff --git a/src/utils/code_generation/mips/utils_mips.py b/src/utils/code_generation/mips/utils_mips.py new file mode 100644 index 000000000..1f2106e59 --- /dev/null +++ b/src/utils/code_generation/mips/utils_mips.py @@ -0,0 +1,547 @@ +from collections import defaultdict +import cmp.visitor as visitor +from utils.code_generation.cil.AST_CIL import cil_ast as cil +from utils.code_generation.mips.AST_MIPS import mips_ast as mips +from utils.code_generation.mips.AST_MIPS import mips_ast as nodes +from utils.code_generation.mips.AST_MIPS import * +from random import choice + + +def push_register(reg): + move_stack = nodes.AddInmediateNode(SP_REG, SP_REG, -4) + save_location = RegisterRelativeLocation(SP_REG, 0) + save_register = nodes.StoreWordNode(reg, save_location) + return [move_stack, save_register] + + +def pop_register(reg): + load_value = nodes.LoadWordNode(reg, RegisterRelativeLocation(SP_REG, 0)) + move_stack = nodes.AddInmediateNode(SP_REG, SP_REG, 4) + return [load_value, move_stack] + + +def alloc_memory(size): + instructions = [] + instructions.append(nodes.LoadInmediateNode(V0_REG, 9)) + instructions.append(nodes.LoadInmediateNode(ARG_REGISTERS[0], size)) + instructions.append(nodes.SyscallNode()) + return instructions + + +def exit_program(): + instructions = [] + instructions.append(nodes.LoadInmediateNode(V0_REG, 10)) + instructions.append(nodes.SyscallNode()) + return instructions + + +def create_object(reg1, reg2): + instructions = [] + + instructions.append(nodes.ShiftLeftLogicalNode(reg1, reg1, 2)) + instructions.append(nodes.LoadAddressNode(reg2, "proto_table")) + instructions.append(nodes.AddUnsignedNode(reg2, reg2, reg1)) + instructions.append(nodes.LoadWordNode( + reg2, RegisterRelativeLocation(reg2, 0))) + instructions.append(nodes.LoadWordNode( + ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4))) + instructions.append(nodes.ShiftLeftLogicalNode( + ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) + instructions.append(nodes.JumpAndLinkNode("malloc")) + instructions.append(nodes.MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) + instructions.append(nodes.MoveNode(ARG_REGISTERS[0], reg2)) + instructions.append(nodes.MoveNode(ARG_REGISTERS[1], V0_REG)) + instructions.append(nodes.JumpAndLinkNode("copy")) + + return instructions + + +def copy_object(reg1, reg2): + instructions = [] + + instructions.append(nodes.LoadWordNode( + ARG_REGISTERS[0], RegisterRelativeLocation(reg1, 4))) + instructions.append(nodes.ShiftLeftLogicalNode( + ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) + instructions.append(nodes.JumpAndLinkNode("malloc")) + instructions.append(nodes.MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) + instructions.append(nodes.MoveNode(ARG_REGISTERS[0], reg1)) + instructions.append(nodes.MoveNode(ARG_REGISTERS[1], V0_REG)) + instructions.append(nodes.JumpAndLinkNode("copy")) + + return instructions + + +class MemoryManager: + def __init__(self, registers, function_for_assign): + self.registers = registers + self.func = function_for_assign + + def get_reg_for_var(self, var): + index = self.func(var) + if index == -1: + return None + return self.registers[index] + + def get_reg_unusued(self, used=[]): + possibles = list(set(self.registers).difference(set(used))) + return choice(possibles) + + +class LabelGenerator: + def __init__(self): + self.data_count = 0 + self.type_count = 0 + self.code_count = 0 + + def generate_type_label(self): + self.type_count += 1 + return f'type_{self.type_count}' + + def generate_data_label(self): + self.data_count += 1 + return f'data_{self.data_count}' + + def generate_code_label(self): + self.code_count += 1 + return f'L_{self.code_count}' + + +class UsedRegisterFinder: + def __init__(self): + self.used_registers = set() + + def get_used_registers(self, instructions): + self.used_registers = set() + + for inst in instructions: + self.visit(inst) + self.used_registers = set.difference( + self.used_registers, set([SP_REG, Register('fp'), V0_REG])) + return [reg for reg in self.used_registers] + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(mips.LoadInmediateNode) + def visit(self, node): + self.used_registers.add(node.reg) + + @visitor.when(mips.LoadAddressNode) + def visit(self, node): + self.used_registers.add(node.reg) + + @visitor.when(mips.AddInmediateNode) + def visit(self, node): + self.used_registers.add(node.dest) + + @visitor.when(mips.MoveNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.LoadWordNode) + def visit(self, node): + self.used_registers.add(node.reg) + + @visitor.when(mips.JumpAndLinkNode) + def visit(self, node): + self.used_registers.add(RA_REG) + + @visitor.when(mips.JumpRegisterAndLinkNode) + def visit(self, node): + self.used_registers.add(RA_REG) + + @visitor.when(mips.AddUnsignedNode) + def visit(self, node): + self.used_registers.add(node.dest) + + @visitor.when(mips.ShiftLeftLogicalNode) + def visit(self, node): + self.used_registers.add(node.dest) + + @visitor.when(mips.AddNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.SubNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.MultiplyNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.ComplementNode) + def visit(self, node): + self.used_registers.add(node.reg1) + + @visitor.when(mips.MoveFromLowNode) + def visit(self, node): + self.used_registers.add(node.reg) + + +class RegistersAllocator: + def __init__(self): + self.mark = False + + def get_registers_for_variables(self, instructions, params, n): + self.numbered_instructions(instructions) + basic_blocks = self.divide_basics_blocks(instructions) + flow_graph = RegistersAllocator.create_flow_graph(basic_blocks) + gk, io = self.liveness_analysis((basic_blocks, flow_graph), params) + interference = RegistersAllocator.interference_compute(gk, io) + return RegistersAllocator.assign_registers(interference, n) + + def divide_basics_blocks(self, instructions): + self.mark = True + for instruction in instructions: + self.mark_leaders(instruction) + + blocks = [] + + for instruction in instructions: + if instruction.leader: + blocks.append([instruction]) + else: + blocks[-1].append(instruction) + + return blocks + + def liveness_analysis(self, graph, params): + blocks, ady_list = graph + + instructions = [] + for block in blocks: + instructions.extend(block) + instructions_total = len(instructions) + + suc = [0 for _ in range(instructions_total)] + for block_index, block in enumerate(blocks): + for ins_index, instruction in enumerate(block): + if ins_index == len(block) - 1: + ady = [i for i in range(len(blocks)) + if ady_list[block_index][i] == 1] + suc[instruction.number] = [ + blocks[b][0].number for b in ady] + else: + suc[instruction.number] = [block[ins_index + 1].number] + + gk = [self.gen_kill(inst) for inst in instructions] + io = RegistersAllocator.out_in_compute(suc, gk) + gk = [([], [param.id for param in params])] + gk + io = [([], io[0][0])] + io + + return gk, io + + @staticmethod + def interference_compute(gk, in_out): + neigs = {} + for g, k in gk: + for v in g: + neigs[v] = set() + for v in k: + neigs[v] = set() + + for i, (_, k) in enumerate(gk): + for v in k: + neigs[v].update(in_out[i][1]) + + for k, v in neigs.items(): + for n in v: + neigs[n].add(k) + + for k, v in neigs.items(): + neigs[k] = list(v.difference([k])) + + return neigs + + @staticmethod + def assign_registers(interference_graph, n): + stack = [] + var_registers = defaultdict(lambda: -1) + nodes = set(interference_graph.keys()) + + def myLen(l): + count = 0 + for v in l: + if v in nodes: + count += 1 + return count + + while nodes: + to_remove = None + for node in nodes: + if myLen(interference_graph[node]) < n: + stack.append((node, interference_graph[node])) + to_remove = node + break + + if to_remove: + nodes.remove(to_remove) + else: + selection = choice(list(nodes)) + stack.append((selection, interference_graph[selection])) + nodes.remove(selection) + + while stack: + node, ady = stack.pop() + regs = set(range(n)) + for neig in ady: + reg = var_registers[neig] + if reg != -1: + try: + regs.remove(reg) + except: + pass + if regs: + var_registers[node] = min(regs) + else: + var_registers[node] = -1 + + return var_registers + + @staticmethod + def out_in_compute(suc, gk): + n_instructions = len(gk) + in_out = [[set(), set()] for _ in range(n_instructions)] + next_in_out = [[set(), set()] for _ in range(n_instructions)] + + def add(set1, set2): + return not set2.issubset(set1) + + changed = True + while changed: + changed = False + for i in range(n_instructions)[::-1]: + for i_suc in suc[i]: + if i_suc < i: + changed |= add(next_in_out[i][1], in_out[i_suc][0]) + next_in_out[i][1] = next_in_out[i][1].union( + in_out[i_suc][0]) + else: + changed |= add( + next_in_out[i][1], next_in_out[i_suc][0]) + next_in_out[i][1] = next_in_out[i][1].union( + next_in_out[i_suc][0]) + + g_i = set(gk[i][0]) + k_i = set(gk[i][1]) + new = g_i.union(next_in_out[i][1].difference(k_i)) + changed |= add(next_in_out[i][0], new) + next_in_out[i][0] = next_in_out[i][0].union(new) + + in_out = next_in_out + + return in_out + + @staticmethod + def create_flow_graph(blocks): + graph = [[-1 for _ in range(len(blocks))] for _ in range(len(blocks))] + labels = {b[0].label: i for i, b in enumerate( + blocks) if isinstance(b[0], cil.LabelNode)} + + for i, block in enumerate(blocks): + if isinstance(block[-1], cil.GotoNode): + graph[i][labels[block[-1].label]] = 1 + elif isinstance(block[-1], cil.IfGotoNode): + graph[i][labels[block[-1].label]] = 1 + graph[i][i + 1] = 1 if i + 1 < len(blocks) else -1 + elif i != len(blocks) - 1: + graph[i][i + 1] = 1 + + return graph + + @staticmethod + def numbered_instructions(instructions): + for i, instr in enumerate(instructions): + instr.number = i + + @visitor.on('instruction') + def gen_kill(self, instruction): + pass + + @visitor.when(cil.ArgNode) + def gen_kill(self, instruction): + if isinstance(instruction.id, int): + return ([], []) + return ([instruction.id], []) + + @visitor.when(cil.StaticCallNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.AssignNode) + def gen_kill(self, instruction): + gen = [] + if isinstance(instruction.right, str): + if not instruction.right.isnumeric(): + gen = [instruction.right] + return (gen, [instruction.left]) + + @visitor.when(cil.AllocateNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.ReturnNode) + def gen_kill(self, instruction): + gen = [instruction.id] if isinstance(instruction.id, str) else [] + return (gen, []) + + @visitor.when(cil.LoadNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.PrintIntNode) + def gen_kill(self, instruction): + return ([instruction.value], []) + + @visitor.when(cil.PrintStrNode) + def gen_kill(self, instruction): + return ([instruction.value], []) + + @visitor.when(cil.TypeNameNode) + def gen_kill(self, instruction): + return ([instruction.type], [instruction.dest]) + + @visitor.when(cil.AbortNode) + def gen_kill(self, instruction): + return ([], []) + + @visitor.when(cil.GetAttrNode) + def gen_kill(self, instruction): + return ([instruction.id], [instruction.dest]) + + @visitor.when(cil.SetAttrNode) + def gen_kill(self, instruction): + gen = [instruction.id] + if not isinstance(instruction.value, int): + gen.append(instruction.value) + return (gen, []) + + @visitor.when(cil.CopyNode) + def gen_kill(self, instruction): + return ([instruction.copy], [instruction.dest]) + + @visitor.when(cil.ArithmeticNode) + def gen_kill(self, instruction): + gen = [x for x in [instruction.op_l, + instruction.op_r] if isinstance(x, str)] + return (gen, [instruction.dest]) + + @visitor.when(cil.IfGotoNode) + def gen_kill(self, instruction): + return ([instruction.if_cond], []) + + @visitor.when(cil.GotoNode) + def gen_kill(self, instruction): + return ([], []) + + @visitor.when(cil.TypeOfNode) + def gen_kill(self, instruction): + return ([instruction.id], [instruction.dest]) + + @visitor.when(cil.DynamicCallNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.NameNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.ComplementNode) + def gen_kill(self, instruction): + gen = [instruction.id] if isinstance(instruction.id, str) else [] + return (gen, [instruction.dest]) + + @visitor.when(cil.ReadStrNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.LengthNode) + def gen_kill(self, instruction): + return ([instruction.id], [instruction.dest]) + + @visitor.when(cil.ReadIntNode) + def gen_kill(self, instruction): + return ([], [instruction.dest]) + + @visitor.when(cil.ConcatNode) + def gen_kill(self, instruction): + return ([instruction.s1, instruction.s2], [instruction.dest]) + + @visitor.when(cil.SubstringNode) + def gen_kill(self, instruction): + gen = [instruction.s] + if isinstance(instruction.i, str): + gen.append(instruction.i) + if isinstance(instruction.length, str): + gen.append(instruction.length) + + return (gen, [instruction.dest]) + + @visitor.when(cil.LabelNode) + def gen_kill(self, instruction): + return ([], []) + + @visitor.when(cil.ErrorNode) + def gen_kill(self, instruction): + return ([], []) + + @visitor.on('instruction') + def mark_leaders(self, instruction): + pass + + @visitor.when(cil.LabelNode) + def mark_leaders(self, instruction): + instruction.leader = True + self.mark = False + + @visitor.when(cil.GotoNode) + def mark_leaders(self, instruction): + instruction.leader = self.mark + self.mark = True + + @visitor.when(cil.IfGotoNode) + def mark_leaders(self, instruction): + instruction.leader = self.mark + self.mark = True + + @visitor.when(cil.InstructionNode) + def mark_leaders(self, instruction): + instruction.leader = self.mark + self.mark = False + + +class MIPSType: + def __init__(self, label, name_addr, attributes, methods, index, default=[]): + self._label = label + self._name = name_addr + self._attributes = attributes + self._default_attributes = dict(default) + self._methods = methods + self._index = index + + @property + def size(self): + return len(self.attributes) + 4 + + @property + def label(self): + return self._label + + @property + def string_name_label(self): + return self._name + + @property + def methods(self): + return self._methods + + @property + def attributes(self): + return self._attributes + + @property + def index(self): + return self._index \ No newline at end of file diff --git a/src/utils/parser/COOL_parser.py b/src/utils/parser/COOL_parser.py new file mode 100644 index 000000000..6f2958843 --- /dev/null +++ b/src/utils/parser/COOL_parser.py @@ -0,0 +1,1305 @@ +from utils.parser.shift_reduce_parser import ShiftReduceParser + +''' +Las tablas action y goto del parser lalr1 se precompilaron porque demora mucho su calculo +El algoritmo para copiarlas fue el siguiente: + +###code### +parser = LALR1_Parser(G) + +with open('action.txt','w') as action: + for key,value in parser.action.items(): + action.write(f'{key} : {value},\n') + +with open('goto.txt','w') as goto: + for key,value in parser.goto.items(): + goto.write(f'{key} : {value},\n') +#### ### ### ## +''' + +class COOL_Parser(ShiftReduceParser): + + def __init__(self, G): + self.action = { + (0, 'class') : ('SHIFT', 1), + (1, 'type') : ('SHIFT', 2), + (2, '{') : ('SHIFT', 3), + (2, 'inherits') : ('SHIFT', 133), + (3, 'id') : ('SHIFT', 4), + (3, '}') : ('REDUCE', 7), + (4, '(') : ('SHIFT', 110), + (4, ':') : ('SHIFT', 5), + (5, 'type') : ('SHIFT', 6), + (6, ';') : ('REDUCE', 8), + (6, '<-') : ('SHIFT', 7), + (7, 'id') : ('SHIFT', 18), + (7, 'isvoid') : ('SHIFT', 27), + (7, 'false') : ('SHIFT', 29), + (7, 'if') : ('SHIFT', 24), + (7, '~') : ('SHIFT', 15), + (7, 'string') : ('SHIFT', 30), + (7, '{') : ('SHIFT', 14), + (7, 'case') : ('SHIFT', 25), + (7, 'new') : ('SHIFT', 22), + (7, 'true') : ('SHIFT', 28), + (7, 'int') : ('SHIFT', 21), + (7, '(') : ('SHIFT', 13), + (7, 'not') : ('SHIFT', 26), + (7, 'let') : ('SHIFT', 8), + (7, 'while') : ('SHIFT', 20), + (8, 'id') : ('SHIFT', 9), + (9, ':') : ('SHIFT', 10), + (10, 'type') : ('SHIFT', 11), + (11, '<-') : ('SHIFT', 12), + (11, ',') : ('SHIFT', 104), + (11, 'in') : ('REDUCE', 22), + (12, 'id') : ('SHIFT', 18), + (12, '{') : ('SHIFT', 14), + (12, 'false') : ('SHIFT', 29), + (12, 'case') : ('SHIFT', 25), + (12, 'if') : ('SHIFT', 24), + (12, 'string') : ('SHIFT', 30), + (12, 'isvoid') : ('SHIFT', 27), + (12, 'new') : ('SHIFT', 22), + (12, 'not') : ('SHIFT', 26), + (12, 'let') : ('SHIFT', 8), + (12, '~') : ('SHIFT', 15), + (12, 'true') : ('SHIFT', 28), + (12, 'while') : ('SHIFT', 20), + (12, 'int') : ('SHIFT', 21), + (12, '(') : ('SHIFT', 13), + (13, 'id') : ('SHIFT', 18), + (13, 'let') : ('SHIFT', 8), + (13, 'not') : ('SHIFT', 26), + (13, 'false') : ('SHIFT', 29), + (13, 'isvoid') : ('SHIFT', 27), + (13, 'if') : ('SHIFT', 24), + (13, 'while') : ('SHIFT', 20), + (13, '~') : ('SHIFT', 15), + (13, 'string') : ('SHIFT', 30), + (13, 'new') : ('SHIFT', 22), + (13, 'true') : ('SHIFT', 28), + (13, 'int') : ('SHIFT', 21), + (13, '(') : ('SHIFT', 13), + (13, '{') : ('SHIFT', 14), + (13, 'case') : ('SHIFT', 25), + (14, 'id') : ('SHIFT', 18), + (14, 'isvoid') : ('SHIFT', 27), + (14, 'false') : ('SHIFT', 29), + (14, 'if') : ('SHIFT', 24), + (14, '~') : ('SHIFT', 15), + (14, 'string') : ('SHIFT', 30), + (14, '{') : ('SHIFT', 14), + (14, 'case') : ('SHIFT', 25), + (14, 'new') : ('SHIFT', 22), + (14, 'true') : ('SHIFT', 28), + (14, 'int') : ('SHIFT', 21), + (14, '(') : ('SHIFT', 13), + (14, 'not') : ('SHIFT', 26), + (14, 'let') : ('SHIFT', 8), + (14, 'while') : ('SHIFT', 20), + (15, 'string') : ('SHIFT', 30), + (15, 'id') : ('SHIFT', 16), + (15, 'new') : ('SHIFT', 22), + (15, 'isvoid') : ('SHIFT', 27), + (15, 'true') : ('SHIFT', 28), + (15, '~') : ('SHIFT', 15), + (15, 'int') : ('SHIFT', 21), + (15, 'false') : ('SHIFT', 29), + (15, '(') : ('SHIFT', 13), + (15, 'if') : ('SHIFT', 24), + (16, '(') : ('SHIFT', 17), + (16, 'in') : ('REDUCE', 48), + (16, ';') : ('REDUCE', 48), + (16, 'loop') : ('REDUCE', 48), + (16, 'pool') : ('REDUCE', 48), + (16, ',') : ('REDUCE', 48), + (16, '.') : ('REDUCE', 48), + (16, ')') : ('REDUCE', 48), + (16, 'then') : ('REDUCE', 48), + (16, 'else') : ('REDUCE', 48), + (16, '}') : ('REDUCE', 48), + (16, 'fi') : ('REDUCE', 48), + (16, '=') : ('REDUCE', 48), + (16, '+') : ('REDUCE', 48), + (16, 'of') : ('REDUCE', 48), + (16, '-') : ('REDUCE', 48), + (16, '*') : ('REDUCE', 48), + (16, '/') : ('REDUCE', 48), + (16, '<') : ('REDUCE', 48), + (16, '<=') : ('REDUCE', 48), + (16, '@') : ('REDUCE', 48), + (17, 'not') : ('SHIFT', 26), + (17, 'let') : ('SHIFT', 8), + (17, 'string') : ('SHIFT', 30), + (17, '~') : ('SHIFT', 15), + (17, 'while') : ('SHIFT', 20), + (17, ')') : ('REDUCE', 56), + (17, 'id') : ('SHIFT', 18), + (17, 'new') : ('SHIFT', 22), + (17, 'true') : ('SHIFT', 28), + (17, '(') : ('SHIFT', 13), + (17, 'int') : ('SHIFT', 21), + (17, '{') : ('SHIFT', 14), + (17, 'isvoid') : ('SHIFT', 27), + (17, 'case') : ('SHIFT', 25), + (17, 'false') : ('SHIFT', 29), + (17, 'if') : ('SHIFT', 24), + (18, '(') : ('SHIFT', 17), + (18, 'in') : ('REDUCE', 48), + (18, ';') : ('REDUCE', 48), + (18, 'loop') : ('REDUCE', 48), + (18, 'pool') : ('REDUCE', 48), + (18, ',') : ('REDUCE', 48), + (18, '.') : ('REDUCE', 48), + (18, ')') : ('REDUCE', 48), + (18, 'then') : ('REDUCE', 48), + (18, 'else') : ('REDUCE', 48), + (18, '}') : ('REDUCE', 48), + (18, 'fi') : ('REDUCE', 48), + (18, '=') : ('REDUCE', 48), + (18, '+') : ('REDUCE', 48), + (18, 'of') : ('REDUCE', 48), + (18, '-') : ('REDUCE', 48), + (18, '*') : ('REDUCE', 48), + (18, '/') : ('REDUCE', 48), + (18, '<') : ('REDUCE', 48), + (18, '<=') : ('REDUCE', 48), + (18, '@') : ('REDUCE', 48), + (18, '<-') : ('SHIFT', 19), + (19, 'string') : ('SHIFT', 30), + (19, 'not') : ('SHIFT', 26), + (19, 'let') : ('SHIFT', 8), + (19, 'while') : ('SHIFT', 20), + (19, 'new') : ('SHIFT', 22), + (19, 'id') : ('SHIFT', 18), + (19, 'true') : ('SHIFT', 28), + (19, 'int') : ('SHIFT', 21), + (19, '(') : ('SHIFT', 13), + (19, '{') : ('SHIFT', 14), + (19, 'isvoid') : ('SHIFT', 27), + (19, 'case') : ('SHIFT', 25), + (19, '~') : ('SHIFT', 15), + (19, 'false') : ('SHIFT', 29), + (19, 'if') : ('SHIFT', 24), + (20, 'int') : ('SHIFT', 21), + (20, '(') : ('SHIFT', 13), + (20, 'not') : ('SHIFT', 26), + (20, 'isvoid') : ('SHIFT', 27), + (20, 'let') : ('SHIFT', 8), + (20, 'while') : ('SHIFT', 20), + (20, '~') : ('SHIFT', 15), + (20, 'id') : ('SHIFT', 18), + (20, 'false') : ('SHIFT', 29), + (20, 'if') : ('SHIFT', 24), + (20, 'string') : ('SHIFT', 30), + (20, '{') : ('SHIFT', 14), + (20, 'new') : ('SHIFT', 22), + (20, 'case') : ('SHIFT', 25), + (20, 'true') : ('SHIFT', 28), + (21, 'in') : ('REDUCE', 47), + (21, ';') : ('REDUCE', 47), + (21, 'loop') : ('REDUCE', 47), + (21, 'pool') : ('REDUCE', 47), + (21, ',') : ('REDUCE', 47), + (21, '.') : ('REDUCE', 47), + (21, ')') : ('REDUCE', 47), + (21, 'then') : ('REDUCE', 47), + (21, 'else') : ('REDUCE', 47), + (21, '}') : ('REDUCE', 47), + (21, 'fi') : ('REDUCE', 47), + (21, '=') : ('REDUCE', 47), + (21, '+') : ('REDUCE', 47), + (21, 'of') : ('REDUCE', 47), + (21, '-') : ('REDUCE', 47), + (21, '*') : ('REDUCE', 47), + (21, '/') : ('REDUCE', 47), + (21, '<') : ('REDUCE', 47), + (21, '<=') : ('REDUCE', 47), + (21, '@') : ('REDUCE', 47), + (22, 'type') : ('SHIFT', 23), + (23, 'in') : ('REDUCE', 49), + (23, ';') : ('REDUCE', 49), + (23, 'loop') : ('REDUCE', 49), + (23, 'pool') : ('REDUCE', 49), + (23, ',') : ('REDUCE', 49), + (23, '.') : ('REDUCE', 49), + (23, ')') : ('REDUCE', 49), + (23, 'then') : ('REDUCE', 49), + (23, 'else') : ('REDUCE', 49), + (23, '}') : ('REDUCE', 49), + (23, 'fi') : ('REDUCE', 49), + (23, '=') : ('REDUCE', 49), + (23, '+') : ('REDUCE', 49), + (23, 'of') : ('REDUCE', 49), + (23, '-') : ('REDUCE', 49), + (23, '*') : ('REDUCE', 49), + (23, '/') : ('REDUCE', 49), + (23, '<') : ('REDUCE', 49), + (23, '<=') : ('REDUCE', 49), + (23, '@') : ('REDUCE', 49), + (24, 'true') : ('SHIFT', 28), + (24, '(') : ('SHIFT', 13), + (24, 'int') : ('SHIFT', 21), + (24, '{') : ('SHIFT', 14), + (24, 'id') : ('SHIFT', 18), + (24, 'case') : ('SHIFT', 25), + (24, 'false') : ('SHIFT', 29), + (24, 'if') : ('SHIFT', 24), + (24, 'string') : ('SHIFT', 30), + (24, 'not') : ('SHIFT', 26), + (24, 'let') : ('SHIFT', 8), + (24, 'isvoid') : ('SHIFT', 27), + (24, 'while') : ('SHIFT', 20), + (24, '~') : ('SHIFT', 15), + (24, 'new') : ('SHIFT', 22), + (25, '{') : ('SHIFT', 14), + (25, 'false') : ('SHIFT', 29), + (25, 'case') : ('SHIFT', 25), + (25, 'if') : ('SHIFT', 24), + (25, 'string') : ('SHIFT', 30), + (25, 'new') : ('SHIFT', 22), + (25, 'id') : ('SHIFT', 18), + (25, 'not') : ('SHIFT', 26), + (25, 'let') : ('SHIFT', 8), + (25, 'while') : ('SHIFT', 20), + (25, 'true') : ('SHIFT', 28), + (25, 'int') : ('SHIFT', 21), + (25, '(') : ('SHIFT', 13), + (25, 'isvoid') : ('SHIFT', 27), + (25, '~') : ('SHIFT', 15), + (26, 'string') : ('SHIFT', 30), + (26, 'not') : ('SHIFT', 26), + (26, 'let') : ('SHIFT', 8), + (26, 'while') : ('SHIFT', 20), + (26, 'new') : ('SHIFT', 22), + (26, 'id') : ('SHIFT', 18), + (26, 'true') : ('SHIFT', 28), + (26, 'int') : ('SHIFT', 21), + (26, '(') : ('SHIFT', 13), + (26, '{') : ('SHIFT', 14), + (26, 'isvoid') : ('SHIFT', 27), + (26, 'case') : ('SHIFT', 25), + (26, '~') : ('SHIFT', 15), + (26, 'false') : ('SHIFT', 29), + (26, 'if') : ('SHIFT', 24), + (27, 'string') : ('SHIFT', 30), + (27, 'id') : ('SHIFT', 16), + (27, 'new') : ('SHIFT', 22), + (27, 'isvoid') : ('SHIFT', 27), + (27, 'true') : ('SHIFT', 28), + (27, '~') : ('SHIFT', 15), + (27, 'int') : ('SHIFT', 21), + (27, 'false') : ('SHIFT', 29), + (27, '(') : ('SHIFT', 13), + (27, 'if') : ('SHIFT', 24), + (28, 'in') : ('REDUCE', 44), + (28, ';') : ('REDUCE', 44), + (28, 'loop') : ('REDUCE', 44), + (28, 'pool') : ('REDUCE', 44), + (28, ',') : ('REDUCE', 44), + (28, '.') : ('REDUCE', 44), + (28, ')') : ('REDUCE', 44), + (28, 'then') : ('REDUCE', 44), + (28, 'else') : ('REDUCE', 44), + (28, '}') : ('REDUCE', 44), + (28, 'fi') : ('REDUCE', 44), + (28, '=') : ('REDUCE', 44), + (28, '+') : ('REDUCE', 44), + (28, 'of') : ('REDUCE', 44), + (28, '-') : ('REDUCE', 44), + (28, '*') : ('REDUCE', 44), + (28, '/') : ('REDUCE', 44), + (28, '<') : ('REDUCE', 44), + (28, '<=') : ('REDUCE', 44), + (28, '@') : ('REDUCE', 44), + (29, 'in') : ('REDUCE', 45), + (29, ';') : ('REDUCE', 45), + (29, 'loop') : ('REDUCE', 45), + (29, 'pool') : ('REDUCE', 45), + (29, ',') : ('REDUCE', 45), + (29, '.') : ('REDUCE', 45), + (29, ')') : ('REDUCE', 45), + (29, 'then') : ('REDUCE', 45), + (29, 'else') : ('REDUCE', 45), + (29, '}') : ('REDUCE', 45), + (29, 'fi') : ('REDUCE', 45), + (29, '=') : ('REDUCE', 45), + (29, '+') : ('REDUCE', 45), + (29, 'of') : ('REDUCE', 45), + (29, '-') : ('REDUCE', 45), + (29, '*') : ('REDUCE', 45), + (29, '/') : ('REDUCE', 45), + (29, '<') : ('REDUCE', 45), + (29, '<=') : ('REDUCE', 45), + (29, '@') : ('REDUCE', 45), + (30, 'in') : ('REDUCE', 46), + (30, ';') : ('REDUCE', 46), + (30, 'loop') : ('REDUCE', 46), + (30, 'pool') : ('REDUCE', 46), + (30, ',') : ('REDUCE', 46), + (30, '.') : ('REDUCE', 46), + (30, ')') : ('REDUCE', 46), + (30, 'then') : ('REDUCE', 46), + (30, 'else') : ('REDUCE', 46), + (30, '}') : ('REDUCE', 46), + (30, 'fi') : ('REDUCE', 46), + (30, '=') : ('REDUCE', 46), + (30, '+') : ('REDUCE', 46), + (30, 'of') : ('REDUCE', 46), + (30, '-') : ('REDUCE', 46), + (30, '*') : ('REDUCE', 46), + (30, '/') : ('REDUCE', 46), + (30, '<') : ('REDUCE', 46), + (30, '<=') : ('REDUCE', 46), + (30, '@') : ('REDUCE', 46), + (31, 'fi') : ('REDUCE', 41), + (31, '/') : ('REDUCE', 41), + (31, '}') : ('REDUCE', 41), + (31, 'else') : ('REDUCE', 41), + (31, 'in') : ('REDUCE', 41), + (31, '<') : ('REDUCE', 41), + (31, '=') : ('REDUCE', 41), + (31, ';') : ('REDUCE', 41), + (31, '<=') : ('REDUCE', 41), + (31, '+') : ('REDUCE', 41), + (31, ')') : ('REDUCE', 41), + (31, 'of') : ('REDUCE', 41), + (31, '-') : ('REDUCE', 41), + (31, 'then') : ('REDUCE', 41), + (31, 'loop') : ('REDUCE', 41), + (31, '*') : ('REDUCE', 41), + (31, 'pool') : ('REDUCE', 41), + (31, ',') : ('REDUCE', 41), + (32, 'fi') : ('REDUCE', 43), + (32, '/') : ('REDUCE', 43), + (32, '}') : ('REDUCE', 43), + (32, 'else') : ('REDUCE', 43), + (32, 'in') : ('REDUCE', 43), + (32, '<') : ('REDUCE', 43), + (32, '=') : ('REDUCE', 43), + (32, ';') : ('REDUCE', 43), + (32, '<=') : ('REDUCE', 43), + (32, '+') : ('REDUCE', 43), + (32, ')') : ('REDUCE', 43), + (32, 'of') : ('REDUCE', 43), + (32, '-') : ('REDUCE', 43), + (32, 'then') : ('REDUCE', 43), + (32, 'loop') : ('REDUCE', 43), + (32, '*') : ('REDUCE', 43), + (32, 'pool') : ('REDUCE', 43), + (32, ',') : ('REDUCE', 43), + (32, '@') : ('SHIFT', 61), + (32, '.') : ('SHIFT', 33), + (33, 'id') : ('SHIFT', 34), + (34, '(') : ('SHIFT', 35), + (35, 'not') : ('SHIFT', 26), + (35, 'let') : ('SHIFT', 8), + (35, 'string') : ('SHIFT', 30), + (35, '~') : ('SHIFT', 15), + (35, 'while') : ('SHIFT', 20), + (35, ')') : ('REDUCE', 56), + (35, 'id') : ('SHIFT', 18), + (35, 'new') : ('SHIFT', 22), + (35, 'true') : ('SHIFT', 28), + (35, '(') : ('SHIFT', 13), + (35, 'int') : ('SHIFT', 21), + (35, '{') : ('SHIFT', 14), + (35, 'isvoid') : ('SHIFT', 27), + (35, 'case') : ('SHIFT', 25), + (35, 'false') : ('SHIFT', 29), + (35, 'if') : ('SHIFT', 24), + (36, ')') : ('SHIFT', 37), + (37, 'in') : ('REDUCE', 53), + (37, ';') : ('REDUCE', 53), + (37, 'loop') : ('REDUCE', 53), + (37, 'pool') : ('REDUCE', 53), + (37, ',') : ('REDUCE', 53), + (37, '.') : ('REDUCE', 53), + (37, ')') : ('REDUCE', 53), + (37, 'then') : ('REDUCE', 53), + (37, 'else') : ('REDUCE', 53), + (37, '}') : ('REDUCE', 53), + (37, 'fi') : ('REDUCE', 53), + (37, '=') : ('REDUCE', 53), + (37, '+') : ('REDUCE', 53), + (37, 'of') : ('REDUCE', 53), + (37, '-') : ('REDUCE', 53), + (37, '*') : ('REDUCE', 53), + (37, '/') : ('REDUCE', 53), + (37, '<') : ('REDUCE', 53), + (37, '<=') : ('REDUCE', 53), + (37, '@') : ('REDUCE', 53), + (38, '<=') : ('SHIFT', 55), + (38, 'fi') : ('REDUCE', 31), + (38, '}') : ('REDUCE', 31), + (38, 'in') : ('REDUCE', 31), + (38, ';') : ('REDUCE', 31), + (38, ')') : ('REDUCE', 31), + (38, 'of') : ('REDUCE', 31), + (38, 'then') : ('REDUCE', 31), + (38, 'loop') : ('REDUCE', 31), + (38, 'else') : ('REDUCE', 31), + (38, 'pool') : ('REDUCE', 31), + (38, ',') : ('REDUCE', 31), + (38, '=') : ('SHIFT', 39), + (38, '<') : ('SHIFT', 53), + (39, 'string') : ('SHIFT', 30), + (39, 'not') : ('SHIFT', 26), + (39, 'let') : ('SHIFT', 8), + (39, 'while') : ('SHIFT', 20), + (39, 'new') : ('SHIFT', 22), + (39, 'id') : ('SHIFT', 18), + (39, 'true') : ('SHIFT', 28), + (39, 'int') : ('SHIFT', 21), + (39, '(') : ('SHIFT', 13), + (39, '{') : ('SHIFT', 14), + (39, 'isvoid') : ('SHIFT', 27), + (39, 'case') : ('SHIFT', 25), + (39, '~') : ('SHIFT', 15), + (39, 'false') : ('SHIFT', 29), + (39, 'if') : ('SHIFT', 24), + (40, 'fi') : ('REDUCE', 30), + (40, '}') : ('REDUCE', 30), + (40, 'in') : ('REDUCE', 30), + (40, ';') : ('REDUCE', 30), + (40, ')') : ('REDUCE', 30), + (40, 'of') : ('REDUCE', 30), + (40, 'then') : ('REDUCE', 30), + (40, 'loop') : ('REDUCE', 30), + (40, 'else') : ('REDUCE', 30), + (40, 'pool') : ('REDUCE', 30), + (40, ',') : ('REDUCE', 30), + (41, '+') : ('SHIFT', 42), + (41, '-') : ('SHIFT', 50), + (41, 'fi') : ('REDUCE', 34), + (41, '}') : ('REDUCE', 34), + (41, 'in') : ('REDUCE', 34), + (41, '<') : ('REDUCE', 34), + (41, '=') : ('REDUCE', 34), + (41, ';') : ('REDUCE', 34), + (41, '<=') : ('REDUCE', 34), + (41, ')') : ('REDUCE', 34), + (41, 'of') : ('REDUCE', 34), + (41, 'then') : ('REDUCE', 34), + (41, 'loop') : ('REDUCE', 34), + (41, 'else') : ('REDUCE', 34), + (41, 'pool') : ('REDUCE', 34), + (41, ',') : ('REDUCE', 34), + (42, 'string') : ('SHIFT', 30), + (42, 'id') : ('SHIFT', 16), + (42, 'new') : ('SHIFT', 22), + (42, 'isvoid') : ('SHIFT', 27), + (42, 'true') : ('SHIFT', 28), + (42, '~') : ('SHIFT', 15), + (42, 'int') : ('SHIFT', 21), + (42, 'false') : ('SHIFT', 29), + (42, '(') : ('SHIFT', 13), + (42, 'if') : ('SHIFT', 24), + (43, '*') : ('SHIFT', 44), + (43, '/') : ('SHIFT', 47), + (43, 'fi') : ('REDUCE', 35), + (43, '}') : ('REDUCE', 35), + (43, 'in') : ('REDUCE', 35), + (43, '<') : ('REDUCE', 35), + (43, '=') : ('REDUCE', 35), + (43, ';') : ('REDUCE', 35), + (43, '<=') : ('REDUCE', 35), + (43, '+') : ('REDUCE', 35), + (43, ')') : ('REDUCE', 35), + (43, 'of') : ('REDUCE', 35), + (43, '-') : ('REDUCE', 35), + (43, 'then') : ('REDUCE', 35), + (43, 'loop') : ('REDUCE', 35), + (43, 'else') : ('REDUCE', 35), + (43, 'pool') : ('REDUCE', 35), + (43, ',') : ('REDUCE', 35), + (44, 'string') : ('SHIFT', 30), + (44, 'id') : ('SHIFT', 16), + (44, 'new') : ('SHIFT', 22), + (44, 'isvoid') : ('SHIFT', 27), + (44, 'true') : ('SHIFT', 28), + (44, '~') : ('SHIFT', 15), + (44, 'int') : ('SHIFT', 21), + (44, 'false') : ('SHIFT', 29), + (44, '(') : ('SHIFT', 13), + (44, 'if') : ('SHIFT', 24), + (45, 'fi') : ('REDUCE', 38), + (45, '/') : ('REDUCE', 38), + (45, '}') : ('REDUCE', 38), + (45, 'else') : ('REDUCE', 38), + (45, 'in') : ('REDUCE', 38), + (45, '<') : ('REDUCE', 38), + (45, '=') : ('REDUCE', 38), + (45, ';') : ('REDUCE', 38), + (45, '<=') : ('REDUCE', 38), + (45, '+') : ('REDUCE', 38), + (45, ')') : ('REDUCE', 38), + (45, 'of') : ('REDUCE', 38), + (45, '-') : ('REDUCE', 38), + (45, 'then') : ('REDUCE', 38), + (45, 'loop') : ('REDUCE', 38), + (45, '*') : ('REDUCE', 38), + (45, 'pool') : ('REDUCE', 38), + (45, ',') : ('REDUCE', 38), + (46, 'in') : ('REDUCE', 52), + (46, ';') : ('REDUCE', 52), + (46, 'loop') : ('REDUCE', 52), + (46, 'pool') : ('REDUCE', 52), + (46, ',') : ('REDUCE', 52), + (46, '.') : ('REDUCE', 52), + (46, ')') : ('REDUCE', 52), + (46, 'then') : ('REDUCE', 52), + (46, 'else') : ('REDUCE', 52), + (46, '}') : ('REDUCE', 52), + (46, 'fi') : ('REDUCE', 52), + (46, '=') : ('REDUCE', 52), + (46, '+') : ('REDUCE', 52), + (46, 'of') : ('REDUCE', 52), + (46, '-') : ('REDUCE', 52), + (46, '*') : ('REDUCE', 52), + (46, '/') : ('REDUCE', 52), + (46, '<') : ('REDUCE', 52), + (46, '<=') : ('REDUCE', 52), + (46, '@') : ('REDUCE', 52), + (47, 'string') : ('SHIFT', 30), + (47, 'id') : ('SHIFT', 16), + (47, 'new') : ('SHIFT', 22), + (47, 'isvoid') : ('SHIFT', 27), + (47, 'true') : ('SHIFT', 28), + (47, '~') : ('SHIFT', 15), + (47, 'int') : ('SHIFT', 21), + (47, 'false') : ('SHIFT', 29), + (47, '(') : ('SHIFT', 13), + (47, 'if') : ('SHIFT', 24), + (48, 'fi') : ('REDUCE', 39), + (48, '/') : ('REDUCE', 39), + (48, '}') : ('REDUCE', 39), + (48, 'else') : ('REDUCE', 39), + (48, 'in') : ('REDUCE', 39), + (48, '<') : ('REDUCE', 39), + (48, '=') : ('REDUCE', 39), + (48, ';') : ('REDUCE', 39), + (48, '<=') : ('REDUCE', 39), + (48, '+') : ('REDUCE', 39), + (48, ')') : ('REDUCE', 39), + (48, 'of') : ('REDUCE', 39), + (48, '-') : ('REDUCE', 39), + (48, 'then') : ('REDUCE', 39), + (48, 'loop') : ('REDUCE', 39), + (48, '*') : ('REDUCE', 39), + (48, 'pool') : ('REDUCE', 39), + (48, ',') : ('REDUCE', 39), + (49, 'fi') : ('REDUCE', 40), + (49, '/') : ('REDUCE', 40), + (49, '}') : ('REDUCE', 40), + (49, 'else') : ('REDUCE', 40), + (49, 'in') : ('REDUCE', 40), + (49, '<') : ('REDUCE', 40), + (49, '=') : ('REDUCE', 40), + (49, ';') : ('REDUCE', 40), + (49, '<=') : ('REDUCE', 40), + (49, '+') : ('REDUCE', 40), + (49, ')') : ('REDUCE', 40), + (49, 'of') : ('REDUCE', 40), + (49, '-') : ('REDUCE', 40), + (49, 'then') : ('REDUCE', 40), + (49, 'loop') : ('REDUCE', 40), + (49, '*') : ('REDUCE', 40), + (49, 'pool') : ('REDUCE', 40), + (49, ',') : ('REDUCE', 40), + (50, 'string') : ('SHIFT', 30), + (50, 'id') : ('SHIFT', 16), + (50, 'new') : ('SHIFT', 22), + (50, 'isvoid') : ('SHIFT', 27), + (50, 'true') : ('SHIFT', 28), + (50, '~') : ('SHIFT', 15), + (50, 'int') : ('SHIFT', 21), + (50, 'false') : ('SHIFT', 29), + (50, '(') : ('SHIFT', 13), + (50, 'if') : ('SHIFT', 24), + (51, '*') : ('SHIFT', 44), + (51, 'fi') : ('REDUCE', 36), + (51, '}') : ('REDUCE', 36), + (51, 'in') : ('REDUCE', 36), + (51, '<') : ('REDUCE', 36), + (51, '=') : ('REDUCE', 36), + (51, ';') : ('REDUCE', 36), + (51, '<=') : ('REDUCE', 36), + (51, '+') : ('REDUCE', 36), + (51, ')') : ('REDUCE', 36), + (51, 'of') : ('REDUCE', 36), + (51, '-') : ('REDUCE', 36), + (51, 'then') : ('REDUCE', 36), + (51, 'loop') : ('REDUCE', 36), + (51, 'else') : ('REDUCE', 36), + (51, 'pool') : ('REDUCE', 36), + (51, ',') : ('REDUCE', 36), + (51, '/') : ('SHIFT', 47), + (52, '*') : ('SHIFT', 44), + (52, 'fi') : ('REDUCE', 37), + (52, '}') : ('REDUCE', 37), + (52, 'in') : ('REDUCE', 37), + (52, '<') : ('REDUCE', 37), + (52, '=') : ('REDUCE', 37), + (52, ';') : ('REDUCE', 37), + (52, '<=') : ('REDUCE', 37), + (52, '+') : ('REDUCE', 37), + (52, ')') : ('REDUCE', 37), + (52, 'of') : ('REDUCE', 37), + (52, '-') : ('REDUCE', 37), + (52, 'then') : ('REDUCE', 37), + (52, 'loop') : ('REDUCE', 37), + (52, 'else') : ('REDUCE', 37), + (52, 'pool') : ('REDUCE', 37), + (52, ',') : ('REDUCE', 37), + (52, '/') : ('SHIFT', 47), + (53, 'string') : ('SHIFT', 30), + (53, 'id') : ('SHIFT', 16), + (53, 'new') : ('SHIFT', 22), + (53, 'isvoid') : ('SHIFT', 27), + (53, 'true') : ('SHIFT', 28), + (53, '~') : ('SHIFT', 15), + (53, 'int') : ('SHIFT', 21), + (53, 'false') : ('SHIFT', 29), + (53, '(') : ('SHIFT', 13), + (53, 'if') : ('SHIFT', 24), + (54, '+') : ('SHIFT', 42), + (54, '-') : ('SHIFT', 50), + (54, 'fi') : ('REDUCE', 32), + (54, '}') : ('REDUCE', 32), + (54, 'in') : ('REDUCE', 32), + (54, '<') : ('REDUCE', 32), + (54, '=') : ('REDUCE', 32), + (54, ';') : ('REDUCE', 32), + (54, '<=') : ('REDUCE', 32), + (54, ')') : ('REDUCE', 32), + (54, 'of') : ('REDUCE', 32), + (54, 'then') : ('REDUCE', 32), + (54, 'loop') : ('REDUCE', 32), + (54, 'else') : ('REDUCE', 32), + (54, 'pool') : ('REDUCE', 32), + (54, ',') : ('REDUCE', 32), + (55, 'string') : ('SHIFT', 30), + (55, 'id') : ('SHIFT', 16), + (55, 'new') : ('SHIFT', 22), + (55, 'isvoid') : ('SHIFT', 27), + (55, 'true') : ('SHIFT', 28), + (55, '~') : ('SHIFT', 15), + (55, 'int') : ('SHIFT', 21), + (55, 'false') : ('SHIFT', 29), + (55, '(') : ('SHIFT', 13), + (55, 'if') : ('SHIFT', 24), + (56, '+') : ('SHIFT', 42), + (56, '-') : ('SHIFT', 50), + (56, 'fi') : ('REDUCE', 33), + (56, '}') : ('REDUCE', 33), + (56, 'in') : ('REDUCE', 33), + (56, '<') : ('REDUCE', 33), + (56, '=') : ('REDUCE', 33), + (56, ';') : ('REDUCE', 33), + (56, '<=') : ('REDUCE', 33), + (56, ')') : ('REDUCE', 33), + (56, 'of') : ('REDUCE', 33), + (56, 'then') : ('REDUCE', 33), + (56, 'loop') : ('REDUCE', 33), + (56, 'else') : ('REDUCE', 33), + (56, 'pool') : ('REDUCE', 33), + (56, ',') : ('REDUCE', 33), + (57, ',') : ('SHIFT', 58), + (57, ')') : ('REDUCE', 58), + (58, 'not') : ('SHIFT', 26), + (58, 'let') : ('SHIFT', 8), + (58, 'string') : ('SHIFT', 30), + (58, '~') : ('SHIFT', 15), + (58, 'while') : ('SHIFT', 20), + (58, 'new') : ('SHIFT', 22), + (58, 'id') : ('SHIFT', 18), + (58, 'true') : ('SHIFT', 28), + (58, '(') : ('SHIFT', 13), + (58, 'int') : ('SHIFT', 21), + (58, '{') : ('SHIFT', 14), + (58, 'case') : ('SHIFT', 25), + (58, 'false') : ('SHIFT', 29), + (58, 'isvoid') : ('SHIFT', 27), + (58, 'if') : ('SHIFT', 24), + (59, ')') : ('REDUCE', 59), + (60, ')') : ('REDUCE', 57), + (61, 'type') : ('SHIFT', 62), + (62, '.') : ('SHIFT', 63), + (63, 'id') : ('SHIFT', 64), + (64, '(') : ('SHIFT', 65), + (65, 'not') : ('SHIFT', 26), + (65, 'let') : ('SHIFT', 8), + (65, 'string') : ('SHIFT', 30), + (65, '~') : ('SHIFT', 15), + (65, 'while') : ('SHIFT', 20), + (65, ')') : ('REDUCE', 56), + (65, 'id') : ('SHIFT', 18), + (65, 'new') : ('SHIFT', 22), + (65, 'true') : ('SHIFT', 28), + (65, '(') : ('SHIFT', 13), + (65, 'int') : ('SHIFT', 21), + (65, '{') : ('SHIFT', 14), + (65, 'isvoid') : ('SHIFT', 27), + (65, 'case') : ('SHIFT', 25), + (65, 'false') : ('SHIFT', 29), + (65, 'if') : ('SHIFT', 24), + (66, ')') : ('SHIFT', 67), + (67, 'in') : ('REDUCE', 55), + (67, ';') : ('REDUCE', 55), + (67, 'loop') : ('REDUCE', 55), + (67, 'pool') : ('REDUCE', 55), + (67, ',') : ('REDUCE', 55), + (67, '.') : ('REDUCE', 55), + (67, ')') : ('REDUCE', 55), + (67, 'then') : ('REDUCE', 55), + (67, 'else') : ('REDUCE', 55), + (67, '}') : ('REDUCE', 55), + (67, 'fi') : ('REDUCE', 55), + (67, '=') : ('REDUCE', 55), + (67, '+') : ('REDUCE', 55), + (67, 'of') : ('REDUCE', 55), + (67, '-') : ('REDUCE', 55), + (67, '*') : ('REDUCE', 55), + (67, '/') : ('REDUCE', 55), + (67, '<') : ('REDUCE', 55), + (67, '<=') : ('REDUCE', 55), + (67, '@') : ('REDUCE', 55), + (68, 'fi') : ('REDUCE', 29), + (68, '}') : ('REDUCE', 29), + (68, 'in') : ('REDUCE', 29), + (68, ';') : ('REDUCE', 29), + (68, ')') : ('REDUCE', 29), + (68, 'of') : ('REDUCE', 29), + (68, 'then') : ('REDUCE', 29), + (68, 'loop') : ('REDUCE', 29), + (68, 'else') : ('REDUCE', 29), + (68, 'pool') : ('REDUCE', 29), + (68, ',') : ('REDUCE', 29), + (69, 'of') : ('SHIFT', 70), + (70, 'id') : ('SHIFT', 71), + (71, ':') : ('SHIFT', 72), + (72, 'type') : ('SHIFT', 73), + (73, '=>') : ('SHIFT', 74), + (74, 'id') : ('SHIFT', 18), + (74, 'isvoid') : ('SHIFT', 27), + (74, 'false') : ('SHIFT', 29), + (74, 'if') : ('SHIFT', 24), + (74, '~') : ('SHIFT', 15), + (74, 'string') : ('SHIFT', 30), + (74, '{') : ('SHIFT', 14), + (74, 'case') : ('SHIFT', 25), + (74, 'new') : ('SHIFT', 22), + (74, 'true') : ('SHIFT', 28), + (74, 'int') : ('SHIFT', 21), + (74, '(') : ('SHIFT', 13), + (74, 'not') : ('SHIFT', 26), + (74, 'let') : ('SHIFT', 8), + (74, 'while') : ('SHIFT', 20), + (75, ';') : ('SHIFT', 76), + (76, 'id') : ('SHIFT', 71), + (76, 'esac') : ('REDUCE', 27), + (77, 'esac') : ('REDUCE', 28), + (78, 'esac') : ('SHIFT', 79), + (79, 'fi') : ('REDUCE', 26), + (79, '}') : ('REDUCE', 26), + (79, 'in') : ('REDUCE', 26), + (79, ';') : ('REDUCE', 26), + (79, ')') : ('REDUCE', 26), + (79, 'of') : ('REDUCE', 26), + (79, 'then') : ('REDUCE', 26), + (79, 'loop') : ('REDUCE', 26), + (79, 'else') : ('REDUCE', 26), + (79, 'pool') : ('REDUCE', 26), + (79, ',') : ('REDUCE', 26), + (80, 'then') : ('SHIFT', 81), + (81, 'isvoid') : ('SHIFT', 27), + (81, 'false') : ('SHIFT', 29), + (81, 'case') : ('SHIFT', 25), + (81, 'if') : ('SHIFT', 24), + (81, '~') : ('SHIFT', 15), + (81, 'string') : ('SHIFT', 30), + (81, 'not') : ('SHIFT', 26), + (81, 'new') : ('SHIFT', 22), + (81, 'id') : ('SHIFT', 18), + (81, 'let') : ('SHIFT', 8), + (81, 'while') : ('SHIFT', 20), + (81, 'true') : ('SHIFT', 28), + (81, 'int') : ('SHIFT', 21), + (81, '(') : ('SHIFT', 13), + (81, '{') : ('SHIFT', 14), + (82, 'else') : ('SHIFT', 83), + (83, 'while') : ('SHIFT', 20), + (83, 'id') : ('SHIFT', 18), + (83, 'new') : ('SHIFT', 22), + (83, 'true') : ('SHIFT', 28), + (83, '(') : ('SHIFT', 13), + (83, 'int') : ('SHIFT', 21), + (83, '{') : ('SHIFT', 14), + (83, 'isvoid') : ('SHIFT', 27), + (83, 'case') : ('SHIFT', 25), + (83, '~') : ('SHIFT', 15), + (83, 'false') : ('SHIFT', 29), + (83, 'if') : ('SHIFT', 24), + (83, 'string') : ('SHIFT', 30), + (83, 'not') : ('SHIFT', 26), + (83, 'let') : ('SHIFT', 8), + (84, 'fi') : ('SHIFT', 85), + (85, 'in') : ('REDUCE', 50), + (85, ';') : ('REDUCE', 50), + (85, 'loop') : ('REDUCE', 50), + (85, 'pool') : ('REDUCE', 50), + (85, ',') : ('REDUCE', 50), + (85, '.') : ('REDUCE', 50), + (85, ')') : ('REDUCE', 50), + (85, 'then') : ('REDUCE', 50), + (85, 'else') : ('REDUCE', 50), + (85, '}') : ('REDUCE', 50), + (85, 'fi') : ('REDUCE', 50), + (85, '=') : ('REDUCE', 50), + (85, '+') : ('REDUCE', 50), + (85, 'of') : ('REDUCE', 50), + (85, '-') : ('REDUCE', 50), + (85, '*') : ('REDUCE', 50), + (85, '/') : ('REDUCE', 50), + (85, '<') : ('REDUCE', 50), + (85, '<=') : ('REDUCE', 50), + (85, '@') : ('REDUCE', 50), + (86, 'loop') : ('SHIFT', 87), + (87, 'true') : ('SHIFT', 28), + (87, 'int') : ('SHIFT', 21), + (87, '(') : ('SHIFT', 13), + (87, 'not') : ('SHIFT', 26), + (87, 'let') : ('SHIFT', 8), + (87, 'id') : ('SHIFT', 18), + (87, 'while') : ('SHIFT', 20), + (87, 'false') : ('SHIFT', 29), + (87, 'isvoid') : ('SHIFT', 27), + (87, 'if') : ('SHIFT', 24), + (87, '~') : ('SHIFT', 15), + (87, 'string') : ('SHIFT', 30), + (87, 'new') : ('SHIFT', 22), + (87, '{') : ('SHIFT', 14), + (87, 'case') : ('SHIFT', 25), + (88, 'pool') : ('SHIFT', 89), + (89, 'fi') : ('REDUCE', 17), + (89, '}') : ('REDUCE', 17), + (89, 'in') : ('REDUCE', 17), + (89, ';') : ('REDUCE', 17), + (89, ')') : ('REDUCE', 17), + (89, 'of') : ('REDUCE', 17), + (89, 'then') : ('REDUCE', 17), + (89, 'loop') : ('REDUCE', 17), + (89, 'else') : ('REDUCE', 17), + (89, 'pool') : ('REDUCE', 17), + (89, ',') : ('REDUCE', 17), + (90, 'fi') : ('REDUCE', 16), + (90, '}') : ('REDUCE', 16), + (90, 'in') : ('REDUCE', 16), + (90, ';') : ('REDUCE', 16), + (90, ')') : ('REDUCE', 16), + (90, 'of') : ('REDUCE', 16), + (90, 'then') : ('REDUCE', 16), + (90, 'loop') : ('REDUCE', 16), + (90, 'else') : ('REDUCE', 16), + (90, 'pool') : ('REDUCE', 16), + (90, ',') : ('REDUCE', 16), + (91, ')') : ('SHIFT', 92), + (92, 'in') : ('REDUCE', 54), + (92, ';') : ('REDUCE', 54), + (92, 'loop') : ('REDUCE', 54), + (92, 'pool') : ('REDUCE', 54), + (92, ',') : ('REDUCE', 54), + (92, '.') : ('REDUCE', 54), + (92, ')') : ('REDUCE', 54), + (92, 'then') : ('REDUCE', 54), + (92, 'else') : ('REDUCE', 54), + (92, '}') : ('REDUCE', 54), + (92, 'fi') : ('REDUCE', 54), + (92, '=') : ('REDUCE', 54), + (92, '+') : ('REDUCE', 54), + (92, 'of') : ('REDUCE', 54), + (92, '-') : ('REDUCE', 54), + (92, '*') : ('REDUCE', 54), + (92, '/') : ('REDUCE', 54), + (92, '<') : ('REDUCE', 54), + (92, '<=') : ('REDUCE', 54), + (92, '@') : ('REDUCE', 54), + (93, 'fi') : ('REDUCE', 42), + (93, '/') : ('REDUCE', 42), + (93, '}') : ('REDUCE', 42), + (93, 'else') : ('REDUCE', 42), + (93, 'in') : ('REDUCE', 42), + (93, '<') : ('REDUCE', 42), + (93, '=') : ('REDUCE', 42), + (93, ';') : ('REDUCE', 42), + (93, '<=') : ('REDUCE', 42), + (93, '+') : ('REDUCE', 42), + (93, ')') : ('REDUCE', 42), + (93, 'of') : ('REDUCE', 42), + (93, '-') : ('REDUCE', 42), + (93, 'then') : ('REDUCE', 42), + (93, 'loop') : ('REDUCE', 42), + (93, '*') : ('REDUCE', 42), + (93, 'pool') : ('REDUCE', 42), + (93, ',') : ('REDUCE', 42), + (94, '}') : ('SHIFT', 95), + (95, 'fi') : ('REDUCE', 18), + (95, '}') : ('REDUCE', 18), + (95, 'in') : ('REDUCE', 18), + (95, ';') : ('REDUCE', 18), + (95, ')') : ('REDUCE', 18), + (95, 'of') : ('REDUCE', 18), + (95, 'then') : ('REDUCE', 18), + (95, 'loop') : ('REDUCE', 18), + (95, 'else') : ('REDUCE', 18), + (95, 'pool') : ('REDUCE', 18), + (95, ',') : ('REDUCE', 18), + (96, ';') : ('SHIFT', 97), + (97, 'id') : ('SHIFT', 18), + (97, 'isvoid') : ('SHIFT', 27), + (97, 'false') : ('SHIFT', 29), + (97, 'if') : ('SHIFT', 24), + (97, '~') : ('SHIFT', 15), + (97, 'string') : ('SHIFT', 30), + (97, '{') : ('SHIFT', 14), + (97, 'case') : ('SHIFT', 25), + (97, 'new') : ('SHIFT', 22), + (97, '}') : ('REDUCE', 19), + (97, 'true') : ('SHIFT', 28), + (97, 'int') : ('SHIFT', 21), + (97, '(') : ('SHIFT', 13), + (97, 'not') : ('SHIFT', 26), + (97, 'let') : ('SHIFT', 8), + (97, 'while') : ('SHIFT', 20), + (98, '}') : ('REDUCE', 20), + (99, ')') : ('SHIFT', 100), + (100, 'in') : ('REDUCE', 51), + (100, ';') : ('REDUCE', 51), + (100, 'loop') : ('REDUCE', 51), + (100, 'pool') : ('REDUCE', 51), + (100, ',') : ('REDUCE', 51), + (100, '.') : ('REDUCE', 51), + (100, ')') : ('REDUCE', 51), + (100, 'then') : ('REDUCE', 51), + (100, 'else') : ('REDUCE', 51), + (100, '}') : ('REDUCE', 51), + (100, 'fi') : ('REDUCE', 51), + (100, '=') : ('REDUCE', 51), + (100, '+') : ('REDUCE', 51), + (100, 'of') : ('REDUCE', 51), + (100, '-') : ('REDUCE', 51), + (100, '*') : ('REDUCE', 51), + (100, '/') : ('REDUCE', 51), + (100, '<') : ('REDUCE', 51), + (100, '<=') : ('REDUCE', 51), + (100, '@') : ('REDUCE', 51), + (101, 'in') : ('REDUCE', 23), + (101, ',') : ('SHIFT', 102), + (102, 'id') : ('SHIFT', 9), + (103, 'in') : ('REDUCE', 25), + (104, 'id') : ('SHIFT', 9), + (105, 'in') : ('REDUCE', 24), + (106, 'in') : ('SHIFT', 107), + (107, 'string') : ('SHIFT', 30), + (107, 'not') : ('SHIFT', 26), + (107, 'let') : ('SHIFT', 8), + (107, 'while') : ('SHIFT', 20), + (107, 'new') : ('SHIFT', 22), + (107, 'id') : ('SHIFT', 18), + (107, 'true') : ('SHIFT', 28), + (107, 'int') : ('SHIFT', 21), + (107, '(') : ('SHIFT', 13), + (107, '{') : ('SHIFT', 14), + (107, 'isvoid') : ('SHIFT', 27), + (107, 'case') : ('SHIFT', 25), + (107, '~') : ('SHIFT', 15), + (107, 'false') : ('SHIFT', 29), + (107, 'if') : ('SHIFT', 24), + (108, 'fi') : ('REDUCE', 21), + (108, '}') : ('REDUCE', 21), + (108, 'in') : ('REDUCE', 21), + (108, ';') : ('REDUCE', 21), + (108, ')') : ('REDUCE', 21), + (108, 'of') : ('REDUCE', 21), + (108, 'then') : ('REDUCE', 21), + (108, 'loop') : ('REDUCE', 21), + (108, 'else') : ('REDUCE', 21), + (108, 'pool') : ('REDUCE', 21), + (108, ',') : ('REDUCE', 21), + (109, ';') : ('REDUCE', 9), + (110, ')') : ('REDUCE', 11), + (110, 'id') : ('SHIFT', 111), + (111, ':') : ('SHIFT', 112), + (112, 'type') : ('SHIFT', 113), + (113, ')') : ('REDUCE', 15), + (113, ',') : ('REDUCE', 15), + (114, ')') : ('SHIFT', 115), + (115, ':') : ('SHIFT', 116), + (116, 'type') : ('SHIFT', 117), + (117, '{') : ('SHIFT', 118), + (118, 'id') : ('SHIFT', 18), + (118, 'new') : ('SHIFT', 22), + (118, 'true') : ('SHIFT', 28), + (118, 'int') : ('SHIFT', 21), + (118, '(') : ('SHIFT', 13), + (118, 'isvoid') : ('SHIFT', 27), + (118, '~') : ('SHIFT', 15), + (118, '{') : ('SHIFT', 14), + (118, 'case') : ('SHIFT', 25), + (118, 'false') : ('SHIFT', 29), + (118, 'if') : ('SHIFT', 24), + (118, 'not') : ('SHIFT', 26), + (118, 'let') : ('SHIFT', 8), + (118, 'string') : ('SHIFT', 30), + (118, 'while') : ('SHIFT', 20), + (119, '}') : ('SHIFT', 120), + (120, ';') : ('REDUCE', 10), + (121, ',') : ('SHIFT', 122), + (121, ')') : ('REDUCE', 13), + (122, 'id') : ('SHIFT', 111), + (123, ')') : ('REDUCE', 14), + (124, ')') : ('REDUCE', 12), + (125, '}') : ('SHIFT', 126), + (126, ';') : ('REDUCE', 3), + (127, ';') : ('SHIFT', 128), + (128, 'id') : ('SHIFT', 4), + (128, '}') : ('REDUCE', 7), + (129, '}') : ('REDUCE', 5), + (130, ';') : ('SHIFT', 131), + (131, 'id') : ('SHIFT', 4), + (131, '}') : ('REDUCE', 7), + (132, '}') : ('REDUCE', 6), + (133, 'type') : ('SHIFT', 134), + (134, '{') : ('SHIFT', 135), + (135, 'id') : ('SHIFT', 4), + (135, '}') : ('REDUCE', 7), + (136, '}') : ('SHIFT', 137), + (137, ';') : ('REDUCE', 4), + (138, '$') : ('REDUCE', 0), + (139, ';') : ('SHIFT', 140), + (140, 'class') : ('SHIFT', 1), + (140, '$') : ('REDUCE', 1), + (141, '$') : ('REDUCE', 2), + (142, '$') : ('OK', None) + } + + self.goto = { + (0, '') : 138, + (0, '') : 142, + (0, '') : 139, + (3, '') : 130, + (3, '') : 127, + (3, '') : 125, + (7, '') : 38, + (7, '') : 49, + (7, '') : 52, + (7, '') : 41, + (7, '') : 109, + (7, '') : 46, + (7, '') : 32, + (8, '') : 106, + (12, '') : 38, + (12, '') : 101, + (12, '') : 32, + (12, '') : 41, + (12, '') : 46, + (12, '') : 49, + (12, '') : 52, + (13, '') : 38, + (13, '') : 99, + (13, '') : 49, + (13, '') : 41, + (13, '') : 52, + (13, '') : 46, + (13, '') : 32, + (14, '') : 38, + (14, '') : 49, + (14, '') : 94, + (14, '') : 52, + (14, '') : 41, + (14, '') : 46, + (14, '') : 96, + (14, '') : 32, + (15, '') : 32, + (15, '') : 46, + (15, '') : 93, + (17, '') : 41, + (17, '') : 52, + (17, '') : 46, + (17, '') : 38, + (17, '') : 57, + (17, '') : 60, + (17, '') : 32, + (17, '') : 91, + (17, '') : 49, + (19, '') : 41, + (19, '') : 52, + (19, '') : 46, + (19, '') : 38, + (19, '') : 32, + (19, '') : 90, + (19, '') : 49, + (20, '') : 38, + (20, '') : 32, + (20, '') : 49, + (20, '') : 41, + (20, '') : 52, + (20, '') : 46, + (20, '') : 86, + (24, '') : 41, + (24, '') : 32, + (24, '') : 38, + (24, '') : 80, + (24, '') : 49, + (24, '') : 52, + (24, '') : 46, + (25, '') : 52, + (25, '') : 46, + (25, '') : 69, + (25, '') : 38, + (25, '') : 41, + (25, '') : 32, + (25, '') : 49, + (26, '') : 41, + (26, '') : 52, + (26, '') : 46, + (26, '') : 38, + (26, '') : 32, + (26, '') : 68, + (26, '') : 49, + (27, '') : 32, + (27, '') : 46, + (27, '') : 31, + (35, '') : 41, + (35, '') : 52, + (35, '') : 46, + (35, '') : 38, + (35, '') : 57, + (35, '') : 60, + (35, '') : 32, + (35, '') : 36, + (35, '') : 49, + (39, '') : 41, + (39, '') : 52, + (39, '') : 40, + (39, '') : 46, + (39, '') : 38, + (39, '') : 32, + (39, '') : 49, + (42, '') : 32, + (42, '') : 43, + (42, '') : 46, + (42, '') : 49, + (44, '') : 32, + (44, '') : 45, + (44, '') : 46, + (47, '') : 32, + (47, '') : 48, + (47, '') : 46, + (50, '') : 32, + (50, '') : 51, + (50, '') : 46, + (50, '') : 49, + (53, '') : 54, + (53, '') : 32, + (53, '') : 52, + (53, '') : 46, + (53, '') : 49, + (55, '') : 56, + (55, '') : 32, + (55, '') : 52, + (55, '') : 46, + (55, '') : 49, + (58, '') : 41, + (58, '') : 52, + (58, '') : 46, + (58, '') : 38, + (58, '') : 57, + (58, '') : 32, + (58, '') : 59, + (58, '') : 49, + (65, '') : 41, + (65, '') : 52, + (65, '') : 46, + (65, '') : 38, + (65, '') : 57, + (65, '') : 60, + (65, '') : 32, + (65, '') : 66, + (65, '') : 49, + (70, '') : 78, + (74, '') : 38, + (74, '') : 75, + (74, '') : 49, + (74, '') : 52, + (74, '') : 41, + (74, '') : 46, + (74, '') : 32, + (76, '') : 77, + (81, '') : 52, + (81, '') : 41, + (81, '') : 46, + (81, '') : 38, + (81, '') : 32, + (81, '') : 82, + (81, '') : 49, + (83, '') : 38, + (83, '') : 32, + (83, '') : 41, + (83, '') : 49, + (83, '') : 52, + (83, '') : 84, + (83, '') : 46, + (87, '') : 41, + (87, '') : 88, + (87, '') : 32, + (87, '') : 38, + (87, '') : 52, + (87, '') : 49, + (87, '') : 46, + (97, '') : 38, + (97, '') : 49, + (97, '') : 52, + (97, '') : 41, + (97, '') : 46, + (97, '') : 96, + (97, '') : 98, + (97, '') : 32, + (102, '') : 103, + (104, '') : 105, + (107, '') : 41, + (107, '') : 52, + (107, '') : 46, + (107, '') : 38, + (107, '') : 32, + (107, '') : 108, + (107, '') : 49, + (110, '') : 121, + (110, '') : 114, + (110, '') : 124, + (118, '') : 52, + (118, '') : 38, + (118, '') : 49, + (118, '') : 32, + (118, '') : 41, + (118, '') : 119, + (118, '') : 46, + (122, '') : 121, + (122, '') : 123, + (128, '') : 129, + (128, '') : 130, + (128, '') : 127, + (131, '') : 130, + (131, '') : 132, + (131, '') : 127, + (135, '') : 130, + (135, '') : 127, + (135, '') : 136, + (140, '') : 139, + (140, '') : 141 + } + + + ShiftReduceParser.__init__(self,G) \ No newline at end of file diff --git a/src/utils/parser/LALR_1.py b/src/utils/parser/LALR_1.py new file mode 100644 index 000000000..e9dfb1374 --- /dev/null +++ b/src/utils/parser/LALR_1.py @@ -0,0 +1,64 @@ +from utils.parser.LR_1 import LR1_Parser +from cmp.automata import State, multiline_formatter +from cmp.utils import ContainerSet +from cmp.pycompiler import Item + +class LALR1_Parser(LR1_Parser): + + def build_automaton(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = self.firsts + firsts[G.EOF] = ContainerSet(G.EOF) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0, lookaheads=ContainerSet(G.EOF)) + start = frozenset([start_item.Center()]) + + closure = self.closure_lr1([start_item], 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: + a = self.goto_lr1(current_state.state, symbol, just_kernel=True) + closure = self.closure_lr1(a, firsts) + center = frozenset(item.Center() for item in a) + + if not a: + continue + + try: + next_state = visited[center] + centers = {item.Center(): item for item in next_state.state} + centers = {item.Center(): (centers[item.Center()], item) for item in closure} + + updated_items = set() + for c, (itemA, itemB) in centers.items(): + item = Item(c.production, c.pos, itemA.lookaheads | itemB.lookaheads) + updated_items.add(item) + + updated_items = frozenset(updated_items) + if next_state.state != updated_items: + pending.append(center) + next_state.state = updated_items + + except KeyError: + visited[center] = next_state = State(frozenset(closure), True) + pending.append(center) + + if current_state[symbol.Name] is None: + current_state.add_transition(symbol.Name, next_state) + else: + assert current_state.get(symbol.Name) is next_state, 'Error!!!' + + automaton.set_formatter(multiline_formatter) + return automaton + + def __str__(self): + return 'LALR(1)' \ No newline at end of file diff --git a/src/utils/parser/LR_1.py b/src/utils/parser/LR_1.py new file mode 100644 index 000000000..5125d2d33 --- /dev/null +++ b/src/utils/parser/LR_1.py @@ -0,0 +1,136 @@ +from utils.parser.shift_reduce_parser import ShiftReduceParser +from cmp.automata import State, multiline_formatter +from cmp.utils import ContainerSet +from cmp.pycompiler import Item + +class LR1_Parser(ShiftReduceParser): + + def _build_parsing_table(self): + + automaton = self.build_automaton(self.G) + self.automaton = automaton + + for i, node in enumerate(automaton): + node.idx = i + + for node in automaton: + idx = node.idx + for item in node.state: + + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + p = item.production + if item.IsReduceItem: + if p.Left == self.G.startSymbol: + self._register(self.action,(idx,self.G.EOF.Name),(ShiftReduceParser.OK,None)) + else: + for c in item.lookaheads: + self._register(self.action,(idx,c.Name),(ShiftReduceParser.REDUCE,self.G.Productions.index(p))) + else: + + if item.NextSymbol.IsTerminal: + self._register(self.action,(idx,item.NextSymbol.Name),(ShiftReduceParser.SHIFT, node[item.NextSymbol.Name][0].idx)) + else: + self._register(self.goto,(idx,item.NextSymbol.Name),(node[item.NextSymbol.Name][0].idx)) + + + def build_automaton(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = self.firsts + 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 = self.closure_lr1(start, firsts) + automaton = State(frozenset(closure), True) + + pending = [ start ] + visited = { start: automaton } + + while pending: + current = pending.pop() + current_state = visited[current] + + for symbol in G.terminals + G.nonTerminals: + # (Get/Build `next_state`) + a = self.goto_lr1(current_state.state,symbol,firsts,True) + + if not a: + continue + + try: + next_state = visited[a] + except: + next_state = State(frozenset(self.goto_lr1(current_state.state,symbol,firsts)),True) + visited[a] = next_state + pending.append(a) + + current_state.add_transition(symbol.Name, next_state) + + automaton.set_formatter(multiline_formatter) + return automaton + + + def goto_lr1(self,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 self.closure_lr1(items, firsts) + + + def closure_lr1(self,items, firsts): + closure = ContainerSet(*items) + + changed = True + while changed: + changed = False + + new_items = ContainerSet() + + #por cada item hacer expand y añadirlo a new_items + for item in closure: + e = self.expand(item,firsts) + new_items.extend(e) + + changed = closure.update(new_items) + + return self.compress(closure) + + + def compress(self,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 expand(self,item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() + # (Compute lookahead for child items) + #calcular el first a todos los preview posibles + for p in item.Preview(): + for first in self.compute_local_first(firsts,p): + lookaheads.add(first) + + assert not lookaheads.contains_epsilon + # (Build and return child items) + _list = [] + for production in next_symbol.productions: + _list.append(Item(production,0,lookaheads)) + return _list + + def __str__(self): + return 'LR(1)' \ No newline at end of file diff --git a/src/utils/parser/parser.py b/src/utils/parser/parser.py new file mode 100644 index 000000000..40c08c4d2 --- /dev/null +++ b/src/utils/parser/parser.py @@ -0,0 +1,128 @@ +from cmp.utils import ContainerSet +from itertools import islice + +class Parser: + def __init__(self,G): + self.G = G + if not self.action: + self.firsts = self.compute_firsts() + self.follows = self.compute_follows() + self._build_parsing_table() + + def _build_parsing_table(self): + raise NotImplementedError() + + def _find_conflict(self): + raise NotImplementedError() + + # Computes First(Vt) U First(Vn) U First(alpha) + # P: X -> alpha + def compute_firsts(self): + firsts = {} + change = True + + # init First(Vt) + for terminal in self.G.terminals: + firsts[terminal] = ContainerSet(terminal) + + # init First(Vn) + for nonterminal in self.G.nonTerminals: + firsts[nonterminal] = ContainerSet() + + while change: + change = False + + # P: X -> alpha + for production in self.G.Productions: + X = production.Left + alpha = production.Right + + # get current First(X) + first_X = firsts[X] + + # init First(alpha) + try: + first_alpha = firsts[alpha] + except: + first_alpha = firsts[alpha] = ContainerSet() + + # CurrentFirst(alpha)??? + local_first = self.compute_local_first(firsts, alpha) + + # update First(X) and First(alpha) from CurrentFirst(alpha) + change |= first_alpha.hard_update(local_first) + change |= first_X.hard_update(local_first) + + # First(Vt) + First(Vt) + First(RightSides) + return firsts + + # Computes First(alpha), given First(Vt) and First(Vn) + # alpha in (Vt U Vn)* + def compute_local_first(self,firsts, alpha): + first_alpha = ContainerSet() + + try: + alpha_is_epsilon = alpha.IsEpsilon + except: + alpha_is_epsilon = False + + # alpha == epsilon ? First(alpha) = { epsilon } + if alpha_is_epsilon: + first_alpha.set_epsilon() + + # alpha = X1 ... XN + # First(Xi) subconjunto First(alpha) + # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) + # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) + else: + for item in alpha: + first_symbol = firsts[item] + first_alpha.update(first_symbol) + if not first_symbol.contains_epsilon: + break + else: + first_alpha.set_epsilon() + + # First(alpha) + return first_alpha + + + def compute_follows(self): + follows = { } + change = True + + local_firsts = {} + + # init Follow(Vn) + for nonterminal in self.G.nonTerminals: + follows[nonterminal] = ContainerSet() + follows[self.G.startSymbol] = ContainerSet(self.G.EOF) + + while change: + change = False + + # P: X -> alpha + for production in self.G.Productions: + X = production.Left + alpha = production.Right + + follow_X = follows[X] + + # X -> zeta Y beta + # First(beta) - { epsilon } subset of Follow(Y) + # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) + for i,symbol in enumerate(alpha): + if symbol.IsNonTerminal: + follow_Y = follows[symbol] + try: + firts_beta = local_firsts[alpha,i] + except KeyError: + firts_beta = local_firsts[alpha,i] = self.compute_local_first(self.firsts,islice(alpha,i+1,None)) + + change |= follow_Y.update(firts_beta) + + if firts_beta.contains_epsilon: + change |= follow_Y.update(follow_X) + + # Follow(Vn) + return follows \ No newline at end of file diff --git a/src/utils/parser/shift_reduce_parser.py b/src/utils/parser/shift_reduce_parser.py new file mode 100644 index 000000000..bed02cf7b --- /dev/null +++ b/src/utils/parser/shift_reduce_parser.py @@ -0,0 +1,65 @@ +from utils.parser.parser import Parser + +class ShiftReduceParser(Parser): + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + + def __init__(self, G): + try: + self.action + except AttributeError: + self.action = {} + self.goto = {} + self.automaton = None + + self.error = '' + Parser.__init__(self, G) + + def __call__(self, w): + stack = [ 0 ] + cursor = 0 + output = [] + operations = [] + + while True: + state = stack[-1] + lookahead = w[cursor] + + # (Detect error) + + try: + action, tag = self.action[state, self.G[lookahead.token_type].Name] + + # (Shift case) + if action == self.SHIFT: + stack.append(tag) + cursor += 1 + + # (Reduce case) + elif action == self.REDUCE: + tag = self.G.Productions[tag] + output.append(tag) + for _ in tag.Right: stack.pop() + a = self.goto[stack[-1], tag.Left.Name] + stack.append(a) + + + # (OK case) + elif action == self.OK: + return output, operations + + # (Invalid case) + else: + raise Exception ['Error...'] + + operations.append(action) + + except: + self.error = f'({lookahead.line}, {lookahead.column}) - SyntacticError: ERROR at or near "{lookahead.lex}"' + return None, None + + @staticmethod + def _register(table, key, value): + assert key not in table or table[key] == value, 'Shift-Reduce or Reduce-Reduce conflict!!!' + table[key] = value \ No newline at end of file diff --git a/src/utils/print_code.py b/src/utils/print_code.py new file mode 100644 index 000000000..1ff4ac195 --- /dev/null +++ b/src/utils/print_code.py @@ -0,0 +1,153 @@ +import cmp.visitor as visitor +from utils.ast.AST_Nodes import ast_nodes as nodes + +class PrintCode: + @visitor.on('node') + def visit(self, node, tabs = 0): + pass + + @visitor.when(nodes.ProgramNode) + def visit(self, node, tabs = 0): + return '\n'.join(self.visit(dec, tabs) for dec in node.declarations) + + + @visitor.when(nodes.ClassDeclarationNode) + def visit(self, node, tabs = 0): + parent = '' if node.parent is None else f'inherits {node.parent}' + ans = '\t' * tabs + f'class {node.id} {parent}' + features = '\n'.join(self.visit(feat, tabs + 1) for feat in node.features) + return f'{ans} {{\n{features}\n}};' + + + @visitor.when(nodes.AttrDeclarationNode) + def visit(self, node, tabs = 0): + expr = '' if node.expr is None else f'<- {self.visit(node.expr)}' + return '\t' * tabs + f'{node.id}: {node.type} {expr};' + + + @visitor.when(nodes.MethDeclarationNode) + def visit(self, node, tabs = 0): + params = ', '.join(': '.join(param) for param in node.params) + ans = '\t' * tabs + f'{node.id} ({params}): {node.type}' + body = self.visit(node.body, tabs + 1) + return f'{ans} {{\n{body}\n' + '\t' * tabs + '};' + + + @visitor.when(nodes.AssignNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{node.id} <- {self.visit(node.expr)}' + + + @visitor.when(nodes.CallNode) + def visit(self, node, tabs = 0): + obj = '' if node.obj is None else f'{self.visit(node.obj)}' if node.type else f'{self.visit(node.obj)}.' + args = ', '.join(self.visit(arg) for arg in node.args) + typex = '' if node.type is None else f'@ {node.type}' + return '\t' * tabs + f'{obj}{typex}{node.id} ({args})' + + + @visitor.when(nodes.IfThenElseNode) + def visit(self, node, tabs = 0): + ifx = self.visit(node.if_expr) + then = self.visit(node.then_expr, tabs + 1) + elsex = self.visit(node.else_expr, tabs + 1) + + return '\t' * tabs + f'if {ifx}\n' + '\t' * tabs + f'then\n{then}\n' + '\t' * tabs + f'else\n{elsex}\n' + '\t' * tabs + 'fi' + + + @visitor.when(nodes.WhileNode) + def visit(self, node, tabs = 0): + ans = f'while {self.visit(node.conditional_expr)} loop' + body = self.visit(node.loop_expr, tabs + 1) + + return '\t' * tabs + f'{ans}\n{body}\n' + '\t' * tabs + 'pool' + + + @visitor.when(nodes.BlockNode) + def visit(self, node, tabs = 0): + expr_list = ';\n'.join(self.visit(expr, tabs + 1) for expr in node.expr_list) + return '\t' * tabs + f'{{\n{expr_list};\n' + '\t' * tabs + '}' + + + @visitor.when(nodes.LetNode) + def visit(self, node, tabs = 0): + identifiers = [] + for idx, typex, id_expr in node.identifiers: + if id_expr: + identifiers.append(f'{idx}: {typex} <- {self.visit(id_expr)}') + else: + identifiers.append(f'{idx}: {typex}') + + identifiers = (',\n' + '\t' * (tabs + 1)).join(identifiers) + return '\t' * tabs + f'let {identifiers} in\n{self.visit(node.in_expr, tabs + 1)}' + + + @visitor.when(nodes.CaseNode) + def visit(self, node, tabs = 0): + predicate = self.visit(node.predicate) + branches = '\n'.join(f'\t' * (tabs + 1) + f'{idx}: {typex} =>\n{self.visit(expr, tabs + 2)};' for idx, typex, expr in node.branches) + + return '\t' * tabs + f'case {predicate} of \n{branches}\n' + '\t' * tabs + 'esac' + + @visitor.when(nodes.NotNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'not {self.visit(node.expr)}' + + + @visitor.when(nodes.ConstantNumNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{node.lex}' + + @visitor.when(nodes.ConstantBoolNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{node.lex}' + + @visitor.when(nodes.ConstantStringNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{node.lex}' + + @visitor.when(nodes.VariableNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{node.lex}' + + + @visitor.when(nodes.InstantiateNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'(new {node.lex})' + + @visitor.when(nodes.IsVoidNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'isvoid {self.visit(node.lex)}' + + @visitor.when(nodes.ComplementNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'~ {self.visit(node.lex)}' + + + @visitor.when(nodes.PlusNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{self.visit(node.left)} + {self.visit(node.right)}' + + @visitor.when(nodes.MinusNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{self.visit(node.left)} - {self.visit(node.right)}' + + @visitor.when(nodes.StarNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{self.visit(node.left)} * {self.visit(node.right)}' + + @visitor.when(nodes.DivNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{self.visit(node.left)} / {self.visit(node.right)}' + + @visitor.when(nodes.LessThanNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{self.visit(node.left)} < {self.visit(node.right)}' + + @visitor.when(nodes.LessEqualNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{self.visit(node.left)} <= {self.visit(node.right)}' + + @visitor.when(nodes.EqualNode) + def visit(self, node, tabs = 0): + return '\t' * tabs + f'{self.visit(node.left)} = {self.visit(node.right)}' \ No newline at end of file diff --git a/src/utils/semantic_check/type_builder.py b/src/utils/semantic_check/type_builder.py new file mode 100644 index 000000000..d480a92cc --- /dev/null +++ b/src/utils/semantic_check/type_builder.py @@ -0,0 +1,103 @@ +import cmp.visitor as visitor +from utils.ast.AST_Nodes import ast_nodes as nodes +from cmp.semantic import SemanticError, ErrorType + +NOT_INHERIT_FROM_BASICS_TYPES = '(%s, %s) - SemanticError: Class %s cannot inherit class %s.' +MULTIDEFINED_METHOD = '(%s, %s) - SemanticError: Method %s is multiply defined.' +INHERIT_ERROR = '(%s, %s) - TypeError: Class %s inherits from an undefined class %s.' +PARAMETER_TYPE_UNDEFINED = '(%s, %s) - TypeError: Class %s of formal parameter %s is undefined.' +REDEFINED_ATTRIBUTES = '(%s, %s) - SemanticError: Attribute %s is an attribute of an inherited class.' +RETURN_TYPE_UNDEFINED = '(%s, %s) - TypeError: Undefined return type %s in method %s.' +TYPE_NOT_DEFINED_ATTR = '(%s, %s) - TypeError: Class %s of attribute %s is undefined.' + + +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(nodes.ProgramNode) + def visit(self,node): + for dec in node.declarations: + self.visit(dec) + return + + + @visitor.when(nodes.ClassDeclarationNode) + def visit(self,node): + self.current_type = self.context.get_type(node.id) + + if node.parent is not None: + + if node.parent in ['Int', 'String', 'SELF_TYPE', 'Bool', 'Object']: + self.errors.append(NOT_INHERIT_FROM_BASICS_TYPES % (node.line, node.parent_column, node.id, node.parent)) + + else: + try: + parent_type = self.context.get_type(node.parent) + except SemanticError as se: + parent_type = ErrorType() + self.errors.append(INHERIT_ERROR % (node.line, node.parent_column, node.id, node.parent)) + + try: + self.current_type.set_parent(parent_type) + except SemanticError as se: + self.errors.append(se.text) + + elif not self.current_type.parent: + self.current_type.set_parent(self.context.get_type('Object')) + node.parent = 'Object' + + for feat in node.features: + self.visit(feat) + + return + + + @visitor.when(nodes.AttrDeclarationNode) + def visit(self,node): + try: + attrType = self.context.get_type(node.type) + except SemanticError as se: + self.errors.append(TYPE_NOT_DEFINED_ATTR % (node.line, node.column, node.type, node.id)) + attrType = ErrorType() + + try: + self.current_type.define_attribute(node.id, attrType) + except SemanticError as se: + self.errors.append(REDEFINED_ATTRIBUTES % (node.line, node.column, node.id)) + + return + + + @visitor.when(nodes.MethDeclarationNode) + def visit(self,node): + param_names = [] + param_types = [] + + for name, typex in node.params: + param_names.append(name) + + try: + param_types.append(self.context.get_type(typex)) + except SemanticError as se: + self.errors.append(PARAMETER_TYPE_UNDEFINED % (node.line, node.column, typex, name)) + param_types.append(ErrorType()) + + try: + returnType = self.context.get_type(node.type) + except SemanticError as se: + self.errors.append(RETURN_TYPE_UNDEFINED % (node.line, node.column, node.type, node.id)) + returnType = ErrorType() + + try: + self.current_type.define_method(node.id, param_names, param_types, returnType) + except SemanticError as se: + self.errors.append(MULTIDEFINED_METHOD % (node.line, node.column, node.id)) + + return \ No newline at end of file diff --git a/src/utils/semantic_check/type_checker.py b/src/utils/semantic_check/type_checker.py new file mode 100644 index 000000000..dd57367e9 --- /dev/null +++ b/src/utils/semantic_check/type_checker.py @@ -0,0 +1,498 @@ +import cmp.visitor as visitor +from utils.ast.AST_Nodes import ast_nodes as nodes +from cmp.semantic import SemanticError, ErrorType, Scope + +MAIN_DONT_EXISTS = '(0, 0) - TypeError: COOL program must have a class Main' +MAIN_METHOD_DONT_EXISTS = '(%s, %s) - TypeError: Main class must have a method main()' +MAIN_METHOD_DONT_HAVE_PARAMS = '(%s, %s) - TypeError: main method must not have params' + +SELF_ERROR_ATTR = '(%s, %s) - SemanticError: \'self\' cannot be the name of an attribute.' +SELF_ERROR_LET = '(%s, %s) - SemanticError: \'self\' cannot be bound in a \'let\' expression.' +SELF_IS_READONLY = '(%s, %s) - SemanticError: Variable "self" is read-only.' +SELF_TYPE_ERROR = '(%s, %s) - TypeError: SELF_TYPE cannot be used as a parameter type in method %s' +SELF_TYPE_IN_DISPATCH = '(%s, %s) - TypeError: SELF_TYPE cannot be used as a type of a dispatch' +SELF_TYPE_IN_CASE_BRANCH = '(%s, %s) - TypeError: SELF_TYPE cannot be used as a type of a case branch' + +INCOMPATIBLE_TYPES_ATTR = '(%s, %s) - TypeError: Inferred type %s of initialization of attribute %s does not conform to declared type %s.' +INCOMPATIBLE_TYPES_ARG = '(%s, %s) - TypeError: Argument of \'%s\' has type %s instead of %s.' +INCOMPATIBLE_TYPES_METH = '(%s, %s) - TypeError: Inferred return type %s of method %s does not conform to declared return type %s.' +INCOMPATIBLE_TYPES_IF = '(%s, %s) - TypeError: Predicate of \'%s\' does not have type %s.' +INCOMPATIBLE_TYPES_LET = '(%s, %s) - TypeError: Inferred type %s of initialization of %s does not conform to identifier\'s declared type %s.' +INCOMPATIBLE_TYPES_CALL = '(%s, %s) - TypeError: Expression type %s does not conform to declared static dispatch type %s.' +INCOMPATIBLE_TYPES = '(%s, %s) - TypeError: Cannot convert %s into %s.' + +WRONG_SIGNATURE = '(%s, %s) - SemanticError: Incompatible number of formal parameters in redefined method %s.' +LOCAL_ALREADY_DEFINED = '(%s, %s) - SemanticError: Variable %s is already defined in method %s.' +INVALID_OPERATION = '(%s, %s) - TypeError: non-Int arguments: %s %s %s' +VARIABLE_NOT_DEFINED = '(%s, %s) - NameError: Undeclared identifier %s.' +INHERIT_ERROR = '(%s, %s) - SemanticError: Class %s, or an ancestor of %s, is involved in an inheritance cycle.' +METHOD_PARAMETERS = '(%s, %s) - SemanticError: Method %s defined in %s receive %d parameters' + +DUPLICATE_BRANCH = '(%s, %s) - SemanticError: Duplicate branch %s in case statement.' +UNDEFINED_TYPE_BRANCH = '(%s, %s) - TypeError: Class %s of case branch is undefined.' +UNDEFINED_TYPE_NEW = '(%s, %s) - TypeError: \'new\' used with undefined class %s.' +UNDEFINED_TYPE_LET = '(%s, %s) - TypeError: Class %s of let-bound identifier %s is undefined.' +UNDEFINED_METHOD = '(%s, %s) - AttributeError: Dispatch to undefined method %s. ' + + +class TypeChecker: + def __init__(self, context, errors): + self.context = context + self.current_type = None + self.current_method = None + self.errors = errors + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(nodes.ProgramNode) + def visit(self, node, scope=None): + # verificando que el programa tenga una clase Main + try: + self.context.get_type('Main') + + except SemanticError: + self.errors.append(MAIN_DONT_EXISTS) + + scope = Scope() if scope == None else scope + + for dec in node.declarations: + self.visit(dec, scope.create_child()) + + return scope + + @visitor.when(nodes.ClassDeclarationNode) + def visit(self, node, scope): + self.current_type = self.context.get_type(node.id) + scope.define_variable('self', self.current_type) + + # verificando la existencia del metodo main + if self.current_type.name == 'Main': + try: + self.current_type.get_method('main') + + except SemanticError: + self.errors.append(MAIN_METHOD_DONT_EXISTS % + (node.line, node.column)) + + # verficando herencia circular en los ancestros de current_type (deben llegar a object sin pasar por el nuevamente) + current_parent = self.current_type.parent + + while current_parent != self.context.get_type('Object') and current_parent != None: + if current_parent == self.current_type: + self.errors.append(INHERIT_ERROR % (self.current_type.child.line, self.current_type.child.column, + self.current_type.child.name, self.current_type.child.name)) + self.current_type.parent = ErrorType() + break + + for attr in current_parent.attributes: + scope.define_variable(attr.name, attr.type) + + current_parent = current_parent.parent + + attrs = [feat for feat in node.features if isinstance( + feat, nodes.AttrDeclarationNode)] + for attr in attrs: + self.visit(attr, scope) + + meths = [feat for feat in node.features if isinstance( + feat, nodes.MethDeclarationNode)] + for meth in meths: + self.visit(meth, scope.create_child()) + + return + + @visitor.when(nodes.AttrDeclarationNode) + def visit(self, node, scope): + try: + attr_type = self.context.get_type( + node.type) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError: + attr_type = ErrorType() + + if node.id == 'self': + self.errors.append(SELF_ERROR_ATTR % (node.line, node.column)) + + if node.expr: + type_expr = self.visit(node.expr, scope.create_child()) + + if not type_expr.conforms_to(attr_type): + self.errors.append(INCOMPATIBLE_TYPES_ATTR % ( + node.line, node.column, type_expr.name, node.id, attr_type.name)) + + scope.define_variable(node.id, attr_type) + + return + + @visitor.when(nodes.MethDeclarationNode) + def visit(self, node, scope): + self.current_method = self.current_type.get_method(node.id) + + # verificando que el metodo main no tenga parametros + if self.current_method.name == 'main' and self.current_method.param_names: + self.errors.append(MAIN_METHOD_DONT_HAVE_PARAMS % + (node.line, node.column)) + + # verificabdo redefinicion de metodos + current_parent = self.current_type.parent + + while current_parent != self.context.get_type('Object') and current_parent != None: + try: + parent_method = current_parent.get_method(node.id) + if parent_method != self.current_method: + self.errors.append(WRONG_SIGNATURE % ( + node.line, node.column, self.current_method.name)) + break + except: + pass + current_parent = current_parent.parent + + scope.define_variable('self', self.current_type) + + for name, typex in zip(self.current_method.param_names, self.current_method.param_types): + if scope.is_local(name): + self.errors.append(LOCAL_ALREADY_DEFINED % ( + node.line, node.column, name, self.current_method.name)) + + elif typex == 'SELF_TYPE': + self.errors.append(SELF_TYPE_ERROR % ( + node.line, node.column, self.current_method.name)) + scope.define_variable(name, ErrorType()) + + else: + scope.define_variable(name, self.context.get_type(typex.name)) + + body_type = self.visit(node.body, scope) + try: + returnType = self.context.get_type( + node.type) if node.type != 'SELF_TYPE' else self.current_type + except SemanticError as se: + returnType = ErrorType() + + if not body_type.conforms_to(returnType): + self.errors.append(INCOMPATIBLE_TYPES_METH % ( + node.body_line, node.body_column, body_type.name, node.id, returnType.name)) + + return + + @visitor.when(nodes.AssignNode) + def visit(self, node, scope): + var = scope.find_variable(node.id) + + type_expr = self.visit(node.expr, scope.create_child()) + + if var is None: + self.errors.append(VARIABLE_NOT_DEFINED % + (node.line, node.column, node.id)) + + elif var.name == 'self': + self.errors.append(SELF_IS_READONLY % (node.line, node.column)) + + elif not type_expr.conforms_to(var.type): + self.errors.append(INCOMPATIBLE_TYPES % ( + node.line, node.column, type_expr.name, var.type.name)) + + return type_expr + + @visitor.when(nodes.CallNode) + def visit(self, node, scope): + if node.obj is None: + obj_type = self.current_type + else: + obj_type = self.visit(node.obj, scope) + node.obj.computed_type = obj_type + + if node.type is not None: + if node.type == 'SELF_TYPE': + self.errors.append(SELF_TYPE_IN_DISPATCH % + (node.line, node.column)) + typex = ErrorType() + + else: + try: + typex = self.context.get_type(node.type) + except SemanticError as se: + self.errors.append(se.text) + typex = ErrorType() + + if not obj_type.conforms_to(typex): + self.errors.append(INCOMPATIBLE_TYPES_CALL % ( + node.line, node.column, obj_type.name, typex.name)) + + obj_type = typex + + try: + meth = obj_type.get_method(node.id) + except SemanticError as se: + if se.text: + self.errors.append(UNDEFINED_METHOD % + (node.line, node.column, node.id)) + for arg in node.args: + self.visit(arg, scope) + return ErrorType() + + if len(node.args) != len(meth.param_names): + self.errors.append(METHOD_PARAMETERS % ( + node.line, node.column, meth.name, obj_type.name, len(meth.param_names))) + + for i, arg in enumerate(node.args): + type_arg = self.visit(arg, scope) + if i < len(meth.param_types) and not type_arg.conforms_to(meth.param_types[i]): + self.errors.append(INCOMPATIBLE_TYPES % ( + node.line, node.column, type_arg.name, meth.param_types[i].name)) + + return meth.return_type if meth.return_type.name != 'SELF_TYPE' else obj_type + + @visitor.when(nodes.IfThenElseNode) + def visit(self, node, scope): + if_type = self.visit(node.if_expr, scope.create_child()) + + if not if_type.conforms_to(self.context.get_type('Bool')): + self.errors.append(INCOMPATIBLE_TYPES_IF % + (node.line, node.column, 'if', 'Bool')) + + then_type = self.visit(node.then_expr, scope.create_child()) + else_type = self.visit(node.else_expr, scope.create_child()) + + return then_type.join(else_type) + + @visitor.when(nodes.WhileNode) + def visit(self, node, scope): + type_conditional = self.visit(node.conditional_expr, scope) + + if not type_conditional.conforms_to(self.context.get_type('Bool')): + self.errors.append(INCOMPATIBLE_TYPES % ( + node.line, node.column, type_conditional.name, 'Bool')) + + self.visit(node.loop_expr, scope.create_child()) + + return self.context.get_type('Object') + + @visitor.when(nodes.BlockNode) + def visit(self, node, scope): + type_expr = ErrorType() + + for expr in node.expr_list: + type_expr = self.visit(expr, scope.create_child()) + + return type_expr + + @visitor.when(nodes.LetNode) + def visit(self, node, scope): + for idx, typex, id_expr in node.identifiers: + try: + id_type = self.context.get_type( + typex) if typex != 'SELF_TYPE' else self.current_type + except SemanticError as se: + id_type = ErrorType() + self.errors.append(UNDEFINED_TYPE_LET % + (node.line, node.column, typex, idx)) + + if idx == 'self': + self.errors.append(SELF_ERROR_LET % (node.line, node.column)) + + if scope.is_local(idx): + scope = scope.create_child() + scope.define_variable(idx, id_type) + else: + scope.define_variable(idx, id_type) + + if id_expr is not None: + id_expr_type = self.visit(id_expr, scope.create_child()) + if not id_expr_type.conforms_to(id_type): + self.errors.append(INCOMPATIBLE_TYPES_LET % ( + node.line, node.column, id_expr_type.name, idx, id_type.name)) + + body_type = self.visit(node.in_expr, scope.create_child()) + + return body_type + + @visitor.when(nodes.CaseNode) + def visit(self, node, scope): + self.visit(node.predicate, scope) + case_type = None + + for i, branch in enumerate(node.branches): + (idx, typex, expr) = branch + (line, column) = node.branchesPos[i] + if typex in [b[1] for b in node.branches[:i]]: + self.errors.append(DUPLICATE_BRANCH % (line, column, typex)) + if typex == 'SELF_TYPE': + self.errors.append(SELF_TYPE_IN_CASE_BRANCH % (line, column)) + id_type = ErrorType() + + else: + try: + id_type = self.context.get_type(typex) + except SemanticError as se: + id_type = ErrorType() + self.errors.append(UNDEFINED_TYPE_BRANCH % + (line, column, typex)) + + inner_scope = scope.create_child() + inner_scope.define_variable(idx, id_type) + + type_expr = self.visit(expr, inner_scope) + case_type = case_type.join( + type_expr) if case_type is not None else type_expr + + return case_type + + @visitor.when(nodes.NotNode) + def visit(self, node, scope): + typex = self.visit(node.expr, scope) + + if not typex.conforms_to(self.context.get_type('Bool')): + self.errors.append(INCOMPATIBLE_TYPES_ARG % ( + node.line, node.column, 'not', typex.name, 'Bool')) + return ErrorType() + + return typex + + @visitor.when(nodes.ConstantNumNode) + def visit(self, node, scope): + return self.context.get_type('Int') + + @visitor.when(nodes.ConstantBoolNode) + def visit(self, node, scope): + return self.context.get_type('Bool') + + @visitor.when(nodes.ConstantStringNode) + def visit(self, node, scope): + return self.context.get_type('String') + + @visitor.when(nodes.VariableNode) + def visit(self, node, scope): + var = scope.find_variable(node.lex) + + if var is None: + self.errors.append(VARIABLE_NOT_DEFINED % + (node.line, node.column, node.lex)) + return ErrorType() + + return var.type + + @visitor.when(nodes.InstantiateNode) + def visit(self, node, scope): + if node.lex == 'SELF_TYPE': + return self.current_type + + try: + return self.context.get_type(node.lex) + except SemanticError as se: + self.errors.append(UNDEFINED_TYPE_NEW % + (node.line, node.column, node.lex)) + return ErrorType() + + @visitor.when(nodes.IsVoidNode) + def visit(self, node, scope): + self.visit(node.lex, scope) + return self.context.get_type('Bool') + + @visitor.when(nodes.ComplementNode) + def visit(self, node, scope): + typex = self.visit(node.lex, scope) + + if not typex.conforms_to(self.context.get_type('Int')): + self.errors.append(INCOMPATIBLE_TYPES_ARG % ( + node.line, node.column, '~', typex.name, 'Int')) + return ErrorType() + + return typex + + @visitor.when(nodes.PlusNode) + def visit(self, node, scope): + type_left = self.visit(node.left, scope) + type_right = self.visit(node.right, scope) + + if not type_left.conforms_to(self.context.get_type('Int')) or not type_right.conforms_to(self.context.get_type('Int')): + self.errors.append(INVALID_OPERATION % ( + node.line, node.column, type_left.name, '+', type_right.name)) + return ErrorType() + + else: + return self.context.get_type('Int') + + @visitor.when(nodes.MinusNode) + def visit(self, node, scope): + type_left = self.visit(node.left, scope) + type_right = self.visit(node.right, scope) + + if not type_left.conforms_to(self.context.get_type('Int')) or not type_right.conforms_to(self.context.get_type('Int')): + self.errors.append(INVALID_OPERATION % ( + node.line, node.column, type_left.name, '-', type_right.name)) + return ErrorType() + + else: + return self.context.get_type('Int') + + @visitor.when(nodes.StarNode) + def visit(self, node, scope): + type_left = self.visit(node.left, scope) + type_right = self.visit(node.right, scope) + + if not type_left.conforms_to(self.context.get_type('Int')) or not type_right.conforms_to(self.context.get_type('Int')): + self.errors.append(INVALID_OPERATION % ( + node.line, node.column, type_left.name, '*', type_right.name)) + return ErrorType() + + else: + return self.context.get_type('Int') + + @visitor.when(nodes.DivNode) + def visit(self, node, scope): + type_left = self.visit(node.left, scope) + type_right = self.visit(node.right, scope) + + if not type_left.conforms_to(self.context.get_type('Int')) or not type_right.conforms_to(self.context.get_type('Int')): + self.errors.append(INVALID_OPERATION % ( + node.line, node.column, type_left.name, '/', type_right.name)) + return ErrorType() + + else: + return self.context.get_type('Int') + + @visitor.when(nodes.LessThanNode) + def visit(self, node, scope): + type_left = self.visit(node.left, scope) + type_right = self.visit(node.right, scope) + + if not type_left.conforms_to(self.context.get_type('Int')) or not type_right.conforms_to(self.context.get_type('Int')): + self.errors.append(INVALID_OPERATION % ( + node.line, node.column, type_left.name, '<', type_right.name)) + return ErrorType() + + else: + return self.context.get_type('Bool') + + @visitor.when(nodes.LessEqualNode) + def visit(self, node, scope): + type_left = self.visit(node.left, scope) + type_right = self.visit(node.right, scope) + + if not type_left.conforms_to(self.context.get_type('Int')) or not type_right.conforms_to(self.context.get_type('Int')): + self.errors.append(INVALID_OPERATION % ( + node.line, node.column, type_left.name, '<=', type_right.name)) + return ErrorType() + + else: + return self.context.get_type('Bool') + + @visitor.when(nodes.EqualNode) + def visit(self, node, scope): + type_left = self.visit(node.left, scope) + type_right = self.visit(node.right, scope) + + int_type = self.context.get_type('Int') + bool_type = self.context.get_type('Bool') + string_type = self.context.get_type('String') + + if type_left.name == 'AUTO_TYPE' or type_right.name == 'AUTO_TYPE': + pass + elif (type_left == int_type and not type_right.conforms_to(int_type)) or (type_right == int_type and not type_left.conforms_to(int_type)) or (type_left == bool_type and not type_right.conforms_to(bool_type)) or (type_right == bool_type and not type_left.conforms_to(bool_type)) or (type_left == string_type and not type_right.conforms_to(string_type)) or (type_right == string_type and not type_left.conforms_to(string_type)): + self.errors.append(INVALID_OPERATION % ( + node.line, node.column, type_left.name, '=', type_right.name)) + return ErrorType() + + return bool_type diff --git a/src/utils/semantic_check/type_collector.py b/src/utils/semantic_check/type_collector.py new file mode 100644 index 000000000..18e247a3e --- /dev/null +++ b/src/utils/semantic_check/type_collector.py @@ -0,0 +1,64 @@ +import cmp.visitor as visitor +from utils.ast.AST_Nodes import ast_nodes as nodes +from cmp.semantic import SemanticError + +NOT_REDEFINE_BASIC_TYPES = '(%s, %s) - SemanticError: Redefinition of basic class %s.' +NOT_REDEFINE_CLASSES = '(%s, %s) - SemanticError: Classes may not be redefined' + +class TypeCollector(object): + def __init__(self, context, errors): + self.context = context + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(nodes.ProgramNode) + def visit(self, node): + + # añadimos algunos tipos predeterminados + object_type = self.context.create_type('Object') + self_type = self.context.create_type('SELF_TYPE') + int_type = self.context.create_type('Int') + string_type = self.context.create_type('String') + bool_type = self.context.create_type('Bool') + io_type = self.context.create_type('IO') + + # todos heredan de Object + int_type.set_parent(object_type) + string_type.set_parent(object_type) + bool_type.set_parent(object_type) + io_type.set_parent(object_type) + + # agregar los metodos a los tipos basicos + object_type.define_method('abort', [], [], object_type) + object_type.define_method('type_name', [], [], string_type) + object_type.define_method('copy', [], [], self_type) + + io_type.define_method('out_string', ['x'], [string_type], self_type) + io_type.define_method('out_int', ['x'], [int_type], self_type) + io_type.define_method('in_string', [], [], string_type) + io_type.define_method('in_int', [], [], int_type) + + string_type.define_method('length', [], [], int_type) + string_type.define_method('concat', ['s'], [string_type], string_type) + string_type.define_method('substr', ['i', 'l'], [int_type, int_type], string_type) + + for dec in node.declarations: + self.visit(dec) + return + + + @visitor.when(nodes.ClassDeclarationNode) + def visit(self,node): + try: + if node.id in ["Object", "Int", "String", "Bool", "IO"]: + self.errors.append(NOT_REDEFINE_BASIC_TYPES % (node.line, node.column, node.id)) + node.id = 'error_type' + typex = self.context.create_type(node.id) + typex.line = node.line + typex.column = node.column + except SemanticError as se: + self.errors.append(NOT_REDEFINE_CLASSES % (node.classt_line, node.classt_column)) + return \ No newline at end of file