diff --git a/doc/Readme.md b/doc/Readme.md index bcde1b87..258cffef 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -8,4 +8,4 @@ Gilberto González Rodríguez | C411 | [@ginrod](https://github.com/ginrod) # Uso del compilador -Para usar el compilador es necesario tener instalado Python 3.7 o superior. Como requisito está el paquete **ply** el cual puede ser instalado usando pip. Si se quiere hacer uso de los tests automáticos además de PLY es necesario instalar **pytest** y **pytest-ordering**. Todos los requerimientos pueden ser instalados ejecutando **python -m pip install -r requeriments.txt** desde la raíz del proyecto. El módulo que contiene toda la lógica del compilador es **cil_to_mips.py**. Para utilizarlo el path relativo a **cil_to_mips.py** o un path absoluto de un fichero con código fuente de COOL se debe pasar como argumento al módulo, por ejemplo, ejecutar **python cil\_to\_mips.py **. Un archivo en el mismo path del código fuente será creado, con el mismo nombre, pero con extensión .mips. Este fichero contendrá código MIPS con pseudo instrucciones y se puede probar en cualquier implementación del simulador SPIM, como por ejemplo, QtSpim. \ No newline at end of file +Para usar el compilador es necesario tener instalado Python 3.7 o superior. Como requisito está el paquete **ply** el cual puede ser instalado usando pip. Si se quiere hacer uso de los tests automáticos además de PLY es necesario instalar **pytest** y **pytest-ordering**. Todos los requerimientos pueden ser instalados ejecutando **python -m pip install -r requeriments.txt** desde la raíz del proyecto. El módulo que contiene toda la lógica del compilador es **cil_to_mips.py**. Para utilizarlo el path relativo a **cil_to_mips.py** o un path absoluto de un fichero con código fuente de COOL se debe pasar como argumento al módulo, por ejemplo, ejecutar **python cil\_to\_mips.py \**. Un archivo en el mismo path del código fuente será creado, con el mismo nombre, pero con extensión .mips. Este fichero contendrá código MIPS con pseudo instrucciones y se puede probar en cualquier implementación del simulador SPIM, como por ejemplo, QtSpim.Otra alternativa para ejecutar el compilador es hacer uso del ejecutable **coolc.sh** contenido en la carpeta src de la siguientes forma: **./cool.sh \**. \ No newline at end of file diff --git a/doc/informe/a.png b/doc/informe/a.png deleted file mode 100644 index d5c04143..00000000 Binary files a/doc/informe/a.png and /dev/null differ diff --git a/doc/informe/b.png b/doc/informe/b.png deleted file mode 100644 index 3e745b12..00000000 Binary files a/doc/informe/b.png and /dev/null differ diff --git a/doc/informe/c.png b/doc/informe/c.png deleted file mode 100644 index ac987234..00000000 Binary files a/doc/informe/c.png and /dev/null differ diff --git a/doc/informe/dispatch_table.png b/doc/informe/dispatch_table.png deleted file mode 100644 index d491c965..00000000 Binary files a/doc/informe/dispatch_table.png and /dev/null differ diff --git a/doc/informe/document.pdf b/doc/informe/document.pdf deleted file mode 100644 index 05d6130f..00000000 Binary files a/doc/informe/document.pdf and /dev/null differ diff --git a/doc/informe/document.tex b/doc/informe/document.tex deleted file mode 100644 index c79a6387..00000000 --- a/doc/informe/document.tex +++ /dev/null @@ -1,233 +0,0 @@ -\documentclass[12pt]{article} - -%Packages -\usepackage[latin1]{inputenc} -% Esto es para que el LaTeX sepa que el texto está en español: -\usepackage[spanish]{babel} -\usepackage[x11names,table]{xcolor} - -% Paquetes de la AMS: -%\usepackage[total={6in,11in},top=0.50in, left=1in]{geometry} -\usepackage[top=1in, left=1in, right=1in, bottom=1in]{geometry} -\usepackage{amsmath, amsthm, amsfonts} -\usepackage{graphics} -\usepackage{listings} -\usepackage{float} -\usepackage{epsfig} -\usepackage{amssymb} - -\lstset{ % - language=Python, % lenguaje - basicstyle=\normalsize\ttfamily, - keywordstyle=\color{blue}, - commentstyle=\color{blue!50}, - backgroundcolor=\color{gray!9}, - identifierstyle = \color{gray!161}, - stringstyle = \color{yellow}, - numberstyle = \color{green}, - columns=fullflexible, - showspaces=false -} - - - -\newtheorem{thm}{Teorema}[section] -\newtheorem{cor}[thm]{Corolario} -\newtheorem{lem}[thm]{Lema} -\newtheorem{prop}[thm]{Proposición} -\theoremstyle{definition} -\newtheorem{defn}[thm]{Definicion} -\theoremstyle{remark} -\newtheorem{rem}[thm]{Observación} - -\def\RR{\mathbb{R}} - -\renewcommand{\labelenumi}{$\bullet$} -\newtheorem{definition}{Definición}[section] -\newtheorem{theorem}{Teorema}[section] -\newtheorem{corollary}{Corolario}[section] -\newtheorem{lemma}{Lema}[section] -\newtheorem{proposition}{Proposición}[section] -\newcommand{\statement}[3]{ - \begin{center} - { \fcolorbox {gray!11}{gray!11}{ - \begin{minipage}[h!]{\textwidth} - \begin{#1}\label{#3} - #2 - \end{#1} - \end{minipage} } } - \end{center}} - \renewcommand{\proof}[1]{{\it Demostración}\\ #1 \hfill\blacksquare} -\newcommand{\pagediv}[4] -{ - \begin{figure}[!h] - \begin{minipage}[b]{#1\textwidth} - #3 - \end{minipage} \hfill - \begin{minipage}[b]{#2\textwidth} - #4 - \end{minipage} - \end{figure} - -} - - - -%define title -\author{ - Dalianys P\'erez Perera\\ - Dayany Alfaro González\\ - Gilberto González Rodríguez\\ - C-411 -} -\title{\textbf{Complementos de Compilación} \\ -\textbf{COOL-Compiler}\\ - } - -\date{} -\begin{document} -%generates the title -\maketitle - -\selectlanguage{spanish} - -\newpage -%insert table contents -\tableofcontents -\newpage - -%\section{Introducción} -%COOL es un pequeño lenguaje orientado a objetos, con tipado estático, herencia simple, polimorfismo, recolección automática de basura y un sistema de tipos unificado. Admite control de flujo condicional e iterativo, además de la coincidencia de patrones. Todo en COOL es una expresión. - -%El propósito de la asignatura Complementos de Compilación es desarrollar durante el curso un compilador para el lenguaje COOL que sea capaz de convertir programas escritos en Cool en programas escritos en MIPS. - -\section{Arquitectura del compilador} -De manera general la estructura de nuestro compilador se divide en varios módulos, pertenecientes a cada una de las fases por las que atravesó la implementación del mismo. A continuación se explicarán las fases mencionadas y, a su vez, los módulos correspondientes. -\subsection{Análisis Lexicográfico y Sintáctico} -En estas dos fases se emplearon las herramientas de construcción de compiladores lex y yacc a través del paquete de \textit{Python} \lstinline|ply|. El mismo incluye la compatibilidad con el análisis sintáctico LALR(1), así como la validación de entrada, informes de errores y resulta ser bastante exigente con la especificación de reglas gramaticales y de tokens. - -PLY consta de dos módulos separados: \lstinline|lex.py| y \lstinline|yacc.py|. El primero se utiliza para dividir el texto de entrada en una colección de tokens especificados por una colección de reglas en forma de expresiones regulares(\textit{tokenización}), mientras que el segundo se utiliza para reconocer la sintaxis del lenguaje que se ha especificado en forma de gramática libre del contexto. Las dos herramientas están diseñadas para trabajar juntas. - -El módulo \lstinline|yacc.py| implementa la componente de \textit{parsing} de PLY teniendo como salida un árbol de sintaxis abstracta representativo del programa de entrada. Yacc utiliza la técnica de análisis sintáctico LR o \textit{shift-reduce}. Tanto lex como yacc proveen formas de manejar los errores lexicográficos y sintácticos, que se determinan cuando fallan las reglas que les fueron definidas. Es importante señalar que tiene como requisito la especificación de la sintaxis en términos de una gramática BNF(notación de Backus-Naur). Esta notación es utilizada para expresar gramáticas libres del contexto. En \lstinline|grammar.md| se encuentra especificada la gramática utilizada en el proyecto. - - El lexer y el parser del proyecto se encuentran implementados en los módulos \lstinline|lexer.py| y \lstinline|cparser.py| respectivamente, además \lstinline|ast_nodes.py| ofrece la jerarquía del AST de COOL propuesta. - - - -\subsection{Análisis Semántico} -Durante esta fase se analiza el cumplimiento de todos los predicados semánticos del lenguaje y, por tanto, el uso correcto de los tipos declarados. A continuación centraremos la atención en el problema de la verificación de dichos tipos. - -Primeramente tenemos que hacer un recorrido por todo el \textit{AST} para encontrar las definiciones de tipos, las cuales serán almacenadas en el concepto \lstinline|Context|. Esto lo haremos utilizando el patrón \textit{visitor}, el mismo será utilizado en las siguientes pasadas al \textit{AST}. Es importante conocer los nombres de las clases definidas de antemano, ya que podemos tener una declaración de un tipo $A$ con un atributo de tipo $B$, donde la declaración del tipo $B$ aparece luego de la de $A$. Con la clase \lstinline|TypeCollector| se logra crear un contexto inicial que solo contendrá los nombres de los tipos, por eso es que solo visita los nodos del \textit{AST} de tipo \lstinline|Program| y \lstinline|Class|. - -\begin{lstlisting} -class TypeCollector(object): - def __init__(self, errors=[]): - self.context = None - self.errors = errors - - @visitor.on('node') - def visit(self, node): - - @visitor.when(AST.Program) - def visit(self, node): - - @visitor.when(AST.Class) - def visit(self, node): -\end{lstlisting} -Importante mencionar que la información referente a los tipos se almacena en el contexto a través de la clase \lstinline|Type|, y que la misma incluye la función \lstinline|conforms_to| con el objetivo de establecer la relación de conformidad entre tipos y garantizar el principio de sustitución. - -Posteriormente se pasa a construir los tipos como tal, con sus definiciones de atributos y métodos, por tal motivo se visitarán además los nodos que definen a los mismos en el \textit{AST}. Durante esta pasada de la clase \lstinline|TypeBuilder| también se chequea que la jerarquía de tipos conformada esté correcta y sea un árbol con raíz en el tipo $Object$. Esto último se resolvió comprobando si el grafo de tipos representa un orden topológico. - -Por último tenemos un \lstinline|TypeChecker | que verificará la consistencia de tipos en todos los nodos del \textit{AST}. El mismo recibe el contexto construido anteriormente y procesa por completo el \textit{AST}. A lo largo de este recorrido fue esencial el uso del concepto \lstinline|Scope|, el cual permite gestionar las variables definidas en los distintos niveles de visibilidad, así como saber con qué tipo se definieron. Cada clase tiene su propio ámbito o \textit{Scope} y a su vez cada método definido en esta. No obstante, la importancia de este concepto también se demuestra en el chequeo de las expresiones $Let$ y $Case$, pues ambas poseen un ámbito interno para nuevas variables locales que podrían definir, por tanto, el \textit{Scope} "hijo" que se le pasa a estos nodos permite desambiguar entre todas las variables declaradas. - -Para manejar los errores de forma consistente, cada una de las clases anteriores posee como atributo una lista de errores de tipo \lstinline|ErrorSemantic|. De modo que ante cualquier error de chequeo de tipos, simplemente se crea una instancia de esta clase y se añade a la lista. Aclarar que a cada error se le pasa la línea y columna del nodo del \textit{AST} correspondiente. - - -\subsection{Generación de Código} -Esta fase comprende dos etapas esenciales. Primeramente es necesario traducir el código de COOL a un lenguaje que nos permita generar código de forma más sencilla. Este lenguaje se denomina \textit{CIL} y todo programa en él tiene 3 secciones: .TYPES, .DATA y .CODE. Durante esta etapa se realiza un recorrido del AST de COOL y se obtiene un \textit{AST} de \textit{CIL}, representando toda la información y semántica necesaria del programa de COOL. Con ello logramos un mayor nivel de abstracción al disponer de instrucciones en 3-direcciones y de cualquier cantidad de registros. - -Durante la segunda etapa se realiza un recorrido sobre el \textit{AST} de \textit{CIL} conformado para generar el código de MIPS finalmente. En ambas, se emplea el patrón \textit{visitor}. -\subsubsection{CIL} -El módulo correspondiente a la generación del código intermedio es \lstinline|cool_to_cil.py|. Aquí se encuentra el recorrido realizado sobre el \textit{AST} de COOL por medio de la clase \lstinline|COOLToCILVisitor| la cual hereda de \lstinline|BaseCOOLToCILVisitor|. Esta última contiene una serie de atributos claves y métodos auxiliares para facilitar la generación: - -\begin{itemize} - \item Las variables de instancia \lstinline|dottypes|, \lstinline|dotdata| y \lstinline|dotcode| almacenan los nodos correspondientes a las secciones .TYPES, .DATA y .CODE respectivamente de un programa en \textit{CIL}. - \item Las variables \lstinline|current_type| y \lstinline|current_method| almacenan instancias de \lstinline|Type| y \lstinline|Method| respectivamente. - \item La variable \lstinline|current_function| almacena el nodo cil.FunctionNode que está en proceso de construcción (estos nodos pertenecen a la sección .CODE). - - \item Para definir parámetros, variables locales e instrucciones dentro de \lstinline|current_function| se usan las funciones auxiliares \lstinline|register_param|, \lstinline|register_local| y \lstinline|register_instruction| respectivamente. - - \item Los métodos \lstinline|register_function| y \lstinline|register_type| almacenan instancias de cil.FunctionNode y cil.TypeNode en las variables \lstinline|dotcode| y \lstinline|dottypes| respectivamente. -\end{itemize} - - -\subsubsection{MIPS} - -En el m\'odulo \texttt{cil\_to\_mips.py} se encapsula el proceso de generar c\'odigo MIPS. Se tiene la clase \texttt{CILToMIPSVisitor} que se encarga de recorrer el \textit{AST} de \textit{CIL} generado anteriormente. - -\begin{itemize} - - \item \textbf{Objetos}\\ - A la hora de generar c\'odigo MIPS es necesario establecer ciertos convenios, y uno de los m\'as importantes es la forma en que se van a organizar los objetos en memoria. Para mostrar el dise\~no utilizado se va a hacer uso de las clases declaradas en la Figura \ref{classes}. - - \begin{figure}[h] - \centering - \includegraphics{a.png} - \includegraphics{b.png} - \includegraphics{c.png} - \caption{Declaraci\'on de clases de ejemplo en Cool.} - \label{classes} - \end{figure} - - En la Figura \ref{layouts} a la izquierda se muestra como quedar\'ian dispuestos en memoria los objetos de tipo A, B y C definidos en la Figura \ref{classes}. Para cada objeto se va a tener que, a partir de su direcci\'on de memoria, en el \textit{offset} 0 se va a encontrar un tag que va a ser un n\'umero \'unico para cada clase. Este n\'umero se va a corresponder con el orden en que se visita cada clase en un recorrido de tipo \textit{DFS} sobre el \'arbol que representa la jerarqu\'ia de clases comenzando por la clase \texttt{Object} que siempre tendr\'ia tag 0. A continuaci\'on en el \textit{offset} 4 se tiene un puntero que apunta a una direcci\'on del segmento de datos donde se encuentra alamacenado el nombre de la clase. En el \textit{offset} 8 va a estar contenido un n\'umero que representa el tama\~no de la clase, lo cual se podr\'ia ver como cu\'antos cuadros ocupa. Le sigue en el \textit{offset} 12 un puntero que apunta a una direcci\'on donde van a estar dispuestas las funciones declaradas por la clase o heredadas, lo cual se puede apreciar en el cuadro que se muestra a la derecha y se explicar\'a con m\'as detalle luego. Por \'ultimo a partir del \textit{offset} 16 se van a encontrar el valor de los atributos declarados por la clase o heredados. Estos atributos van a aparecer en el orden en que fueron declarados, empezando por los pertenecientes al ancestro m\'as lejano hasta llegar a los declarados por la propia clase. - - - \begin{figure}[h!] - \centering - \includegraphics[width=4in,height=1.6in]{object_layout.png} - \includegraphics[width=2 in,height=1.6in]{dispatch_table.png} - \caption{Forma de colocar los objetos en memoria.} - \label{layouts} - \end{figure} - - En el cuadro que se encuentra en la parte derecha de la Figura \ref{layouts} se tiene c\'omo se van a organizar las funciones pertenecientes a una clase. Como se puede observar se va a seguir el mismo criterio de orden que en los atributos. La diferencia es que una clase puede redefinir las funciones heredadas y esto se va a reflejar como una sustituci\'on en el \textit{offset} que le corresponde a la funci\'on heredada, como se muestra en el ejemplo, que B redefine la funci\'on f y por tanto en ese \textit{offset} va a apuntar a su propia definici\'on, al contrario de C que va apuntar a la definida por A. - - Este dise\~no est\'a pensado para lograr el polimorfismo en los objetos pues, tanto para atributos como funciones, siempre los heredados van a estar en el mismo \textit{offset} que en los ancestros, lo que beneficia que en los casos que un clase quiera sustituir a un ancestro sus atributos y funciones van a tener la misma forma de buscarse. - - En el caso de las clases \textit{built-in} tambi\'en se va a hacer uso del dise\~no explicado y los valores de estas van a estar almacenados como un atributo. El valor \texttt{void} va a estar representado como una direcci\'on est\'atica en memoria. - - \item \textbf{Funciones} - - La forma en que se resuelven los llamados a funciones incluye otros de los convenios utilizados. Uno de los principales problemas a resolver fue d\'onde se iban a almacenar los par\'ametros que recibe una funci\'on. Para esto se decidi\'o hacer uso de la pila, de forma que cuando se ejecuta la primera instrucci\'on de una funci\'on los par\'ametros van a estar colocados en la pila en orden contrario al que fueron declarados y se podr\'a acceder a ellos usando como referencia el registro \texttt{\$sp}. - - Otra situaci\'on a resolver es que para llevar a cabo cualquier operaci\'on es necesario almacenar valores temporales, que denominaremos \textit{locals} de una funci\'on. Estos valores tambi\'en van a estar almacenados en la pila. Desde CIL se tiene conocimiento de cu\'antos \textit{locals} se van a necesitar y lo primero que va a hacer una funci\'on es "salvar" \ espacio en la pila para guardarlos y se va a tener conocimiento del \textit{offset} que va a corresponder a cada \textit{local} que tambi\'en van a ser referenciados usando \texttt{\$sp}. - - Al final de la ejecuci\'on de una funci\'on el valor que esta retorna se va a almacenar siempre en el registro \texttt{\$a1}. Por \'ultimo la funci\'on se encarga de sacar de la pila tanto los \textit{locals} como los par\'ametros. - - - -\end{itemize} - - - - - -\section{Problemas técnicos} -\subsection{Análisis Lexicográfico y Sintáctico} -Durante la fase lexicográfica el mayor reto enfrentado fue la tokenizaci\'on de \textit{strings} y comentarios de m\'ultiples l\'ineas. Esto fue resuelto mediante el uso del concepto de estado que provee la herramienta \texttt{ply}. Cuando se detecta \texttt{"} o $\backslash \ast$ el lexer entra en un nuevo estado en el que solo se regir\'ia por las reglas definidas para dicho estado. Este comportamiento se mantendr\'ia hasta que se detecte \texttt{"} o $\ast \backslash$ y se regresa al procesamiento normal. En el caso de los comentarios se mantiene un contador de los $\backslash \ast$ encontrados para manejar los comentarios anidados. - - -\subsection{Análisis Semántico} -Mencionar quizás: - -. herencia - -. lowest commun ancestor -\subsection{Generación de Código} -Uno de los problemas m\'as interesantes que se present\'o fue la implementaci\'on de la funcionalidad que provee la expresi\'on \texttt{case}. Es necesario dado un tipo \texttt{T} determinar, en tiempo de ejecuci\'on, entre un conjunto de tipos $T_i, 1< i \leq n$ cu\'al es el menor tipo con el que \texttt{T} se conforma. Para resolver esto se le asign\'o a cada tipo un tag, el cual se va a corresponder con el orden en que se visita cada clase en un recorrido de tipo \textit{DFS} sobre el \'arbol que representa la jerarqu\'ia de clases comenzando por la clase \texttt{Object} que siempre tendr\'ia tag 0. Adem\'as cada tipo \texttt{T} va a saber cu\'al es el tipo con mayor tag \textit{max\_tag}(T) al que se puede llegar en un recorrido \textit{DFS} desde \'el. Por la forma en que est\'a definido el tag se puede decir que un tipo \texttt{T} se conforma con un tipo \texttt{A} si $\mathtt{tag_A \leq tag_T \leq \mathit{max\_tag}(\mathtt{A})}$. Por tanto si se chequean los tipos $T_i$ ordenados de mayor a menor seg\'un el tag el primero que cumpla dicha condici\'on va a ser el tipo buscado. - -\section{Uso del compilador} - -Para usar el compilador es necesario tener instalado Python 3.7 o superior. Como requisito est\'a el paquete ply el cual puede ser instalado usando pip. Si se quiere hacer uso de los tests autom\'aticos adem\'as de PLY es necesario instalar pytest y pytest-ordering. Todos los requerimientos pueden ser instalados ejecutando \texttt{python -m pip install -r requeriments.txt} desde la ra\'iz del proyecto. El m\'odulo que contiene toda la l\'ogica del compilador es \texttt{cil\_to\_mips.py}. Para utilizarlo el path relativo a \texttt{cil\_to\_mips.py} o un path absoluto de un fichero con c\'odigo fuente de COOL se debe pasar como argumento al m\'odulo, por ejemplo, ejecutar \texttt{python cil\_to\_mips.py }. Un archivo en el mismo path del c\'odigo fuente ser\'a creado, con el mismo nombre, pero con extensi\'on .mips. Este fichero contendr\'a código MIPS con pseudo instrucciones y se puede probar en cualquier implementaci\'on del simulador SPIM, como por ejemplo, QtSpim. - -\end{document} diff --git a/doc/informe/object_layout.png b/doc/informe/object_layout.png deleted file mode 100644 index a125a593..00000000 Binary files a/doc/informe/object_layout.png and /dev/null differ