diff --git a/.gitignore b/.gitignore
index 4acafde18..ea399309b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -408,3 +408,4 @@ dmypy.json
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
+/src/code.cl
diff --git a/doc/Readme.md b/doc/Readme.md
index 3b2569f5c..a01d8c49a 100644
--- a/doc/Readme.md
+++ b/doc/Readme.md
@@ -1,33 +1,441 @@
-# Documentación
+# Informe de Complementos de Compilación
+## Compilador de Cool
+***
-## Readme
-Modifique el contenido de este documento para documentar de forma clara y concisa los siguientes aspectos:
+## Autores
+- Claudia Olavarrieta Martínez
+- Marcos Adrián Valdivié Rodríguez
+- Adrián Hernández Pérez
-- Cómo ejecutar (y compilar si es necesario) su compilador.
-- Requisitos adicionales, dependencias, configuración, etc.
-- Opciones adicionales que tenga su compilador.
+## Uso del compilador
-## Sobre los Equipos de Desarrollo
+**Requerimientos**
-Para desarrollar el compilador del lenguaje COOL se trabajará en equipos de 2 o 3 integrantes. El proyecto de Compilación será recogido y evaluado únicamente a través de Github. Es imprescindible tener una cuenta de Github para cada participante, y que su proyecto esté correctamente hosteado en esta plataforma.
+Para ejecutar el proyecto es necesario instalar Python y las dependencias que
+se listan en requirements.txt
-**⚠️ NOTA**: Debe completar el archivo `team.yml` con los datos correctos de cada miembro de su equipo.
+Para instalar las dependencias puede ubicarse en la carpeta del proyecto y ejecutar
-## Sobre los Materiales a Entregar
+```bash
+pip install -r requirements.txt
+```
-Para la evaluación del proyecto Ud. debe entregar un informe en formato PDF (`report.pdf`) en esta carpeta, que resuma de manera organizada y comprensible la arquitectura e implementación de su compilador.
-El documento no tiene límite de extensión.
-En él explicará en más detalle su solución a los problemas que, durante la implementación de cada una de las fases del proceso de compilación, hayan requerido de Ud. especial atención.
+**Ejecución**
+
+Para ejecutar el proyecto debe situarse en la carpeta *src* y utilizar alguno de los
+dos comandos que se muestran a continuación:
+```bash
+python Main.py -f path/to/file.cl
+python Main.py --file path/to/file.cl
+```
+
+Como salida se guarda en un fichero del mismo nombre del introducido como parámetro
+con extensión *.mips* el código compilado y listo para ejecutarse.
+
+## Arquitectura del compilador
+
+El compilador está compuesto por distintos módulos que son los encargados de tomar
+el código escrito inicialmente en COOL y obtener el resultado final en código MIPS que
+permita su ejecución.
+
+## Organización
+
+La estructura de archivos del proyecto es la siguiente:
+
+```
+cool-compiler-2021
+|___doc
+ img
+ src
+ |__Main.py
+ core
+ |__cil
+ |__BaseCoolToCilVisitor.py
+ CilAst.py
+ CoolToCilVisitor.py
+ |__cool
+ |__CoolAst.py
+ CoolAstFormatter.py
+ |__lexer
+ |__Lexer.py
+ |__mips
+ |__CilToMipsVisitor.py
+ MipsBasics.asm
+ MipsAst.py
+ MipsAstFormatter.py
+ |__parser
+ |__Parser.py
+ |__semantic
+ |__TypeBuilder.py
+ TypeChecker.py
+ TypeCollector.py
+ |__tools
+ |__Automata.py
+ Errors.py
+ Evaluation.py
+ FirstAndFollows.py
+ ParserLR1.py
+ Parsing.py
+ Pycompiler.py
+ Semantic.py
+ Utils.py
+ visitor.py
+
+
+```
+
+Se omitieron algunos archivos pues no son relevantes en la implementación del compilador.
+
+### Fases (_Pipeline_)
+
+El fichero _Main.py_ contiene el pipeline de ejecución del compilador
+1. Lexer
+2. Parser
+3. Recolección de Tipos
+4. Construcción de Tipos
+5. Chequeo de Tipos
+6. COOL a CIL
+7. CIL a Mips
+8. MIPS a representación como string
+9. Escritura en el archivo final
+
+#### Lexer
+
+El Lexer es el encargado de dado un string con el código del programa COOL separar el
+mismo en tokens para luego ser usado por el parser. En esta fase se utilizó el paquete
+_ply_ el cual contiene herramientas de tokenización. Se definieron las expresiones
+regulares y símbolos que correspondían a los tokens de la gramática, definiendo reglas
+especiales para poder también reconocer los string y comentarios anidados. Además,
+se almacena por cada Token la línea y la columna correspondiente en el código,
+lo que permite tener mayor información en los mensajes de error y para nuestro uso
+en la depuración.
+
+
+
+#### Parser
+
+El Parser define la estructura que tendrá el Árbol de Sintaxis Abstracta (AST) del lenguaje COOL, además
+de la gramática que se usará para parsear.
+El archivo donde se definen los símbolos y producciones de la gramática puede verse al final
+de este fichero en la sección Gramática.
+
+
+Se utilizó un parser LR1 que había sido implementado y probado en proyectos anteriores
+de la asignatura. Fue necesaria la inclusión de nuevas reglas y la edición de algunas anteriores para la
+solución de problemas con la precedencia de los operadores y la ambigüedad de la gramática.
+Durante esta fase se reconocen y reportan los errores léxicos del programa.
+
+
+#### Recolección de tipos
+
+En esta fase se recorren todas las declaraciones de clases, se crean los tipos asociados
+y se valida que no se estén redefiniendo estos. Primeramente, se añaden los tipos builtin
+(Object, IO, Bool, String, Int) y luego los tipos definidos por el usuario, revisando
+que no existan nombres de clases repetidos.
+
+#### Construcción de Tipos
+
+En esta fase se recorren nuevamente las declaraciones de clases añadiendo los métodos y
+atributos de cada clase. Se define la herencia para cada clase, en caso de que no exista se hereda de la
+clase Object. Además, se revisa que exista una clase Main con un método main que indica el inicio
+de la ejecución del programa COOL.
+
+#### Chequeo de tipos
+
+En esta fase se hace el chequeo semántico de cada tipo. Se evalúa para cada expresión su tipo de retorno
+y se valida que estos cumplan las reglas semánticas definidas en el lenguaje. Además, se chequea nuevamente
+las clases definidas por el usuario en busca de la existencia de herencia cíclica.
+Algunos de los errores que se chequean en esta fase son:
+- Las operaciones aritméticas solo están definidas para el tipo Int.
+- Que las variables y los métodos hayan sido definidos previamente a su uso.
+- Que no exista redefinición de métodos o atributos en las clases.
+- Que no exista herencia cíclica.
+- Que las palabras claves self y SELF_TYPE sean usadas correctamente.
+- Que las funciones de clases sean llamadas con la cantidad correcta de parámetros.
+- Que el objeto empleado en cada expresión sea compatible con el tipo declarado para la misma.
+
+#### COOL a CIL
+
+Durante esta fase se realiza la conversión del lenguaje COOL a un lenguaje intermedio(CIL).
+El fichero CilAst contiene la definición de las clases usadas para conformar el AST del lenguaje CIL.
+En el fichero BaseCooltoCilVisitor se definen los métodos básicos para registrar
+una variable, parámetro, función y atributo, entre otros. Además se
+registran los tipos builtin, es decir, se escriben en código CIL las instrucciones
+para registrar los tipos Object, IO, String, Int y Bool, así como las funciones y atributos de cada uno de estos..
+El fichero CoolToCilVisitor es el encargado de transformar el AST de COOL en un AST de CIL, para facilitar
+luego la traducción de este al lenguaje MIPS. Este fichero cuenta con un visitor que se encarga de transformar
+cada nodo del AST de un lenguaje a otro, algunos de los aspectos a destacar son:
+- En el visitor del ProgramNode se define la función entry, que es por donde se comenzará la ejecución del
+programa MIPS.
+- En el visitor del ClassDeclarationNode se definen las funciones init e init_attr corespondientes a cada
+clase, estas funciones son las encargadas de reservar la memoria necesaria para cada uno de los tipos
+definidos por el usuario.
+- Especial énfasis en el visitor del CaseOfNode, este se encarga de generar todas las instrucciones
+necesarias para validar correctamente la rama que debe ejecutarse, o mostrar un error en tiempo de ejecución
+en caso de no haber ninguna válida. Para lograr esto primero se ordenan los tipos involucrados en las ramas del
+case según su profundidad en el árbol de herencia del programa, de mayor a menor. Luego se visitan estos,
+revisando para cada uno todos sus descendientes en dicho árbol de herencia y comprobando, en tiempo de
+ejecución, si estos coinciden con el tipo del objeto que se está analizando. El primer objeto para el que
+se halle una correspondencia define la rama por la que debe continuar la ejecución del programa.
+- En el visitor del DivNode se añaden las instrucciones necesarias para verificar que el divisor es distinto
+de cero, y lanzar un error en tiempo de ejecución en caso contrario.
+- El visitor del EqualNode se encarga de revisar primeramente los tipos de los objetos que están siendo
+comparados, Int-Int, String-String y Bool-Bool son comparados por valor, mientras que cualquier otro par
+es comparado por referencia.
+- El visitor del FunctionCallNode se encarga de comprobar que el objeto al cual se le hace el dispatch sea
+distinto de Void y mostrar un error en ejecución en caso contrario.
+
+#### CIL a MIPS
+Esta es la fase final donde se traduce de CIL al lenguaje MIPS que da la salida del programa.
+Dentro del fichero mips_basics.asm se encuentran algunas funciones predefinidas en mips: malloc, copy,
+read_string, equal_string, length, substring y concat.
+El fichero MIPSAst contiene la definición de las clases necesarias para representar el código MIPS.
+El fichero CilToMipsVisitor visita cada nodo del AST de CIL y lo traduce a sus correspondientes
+instrucciones en codigo Mips. Gran dificultad trajo en esta fase el uso correcto de las tablas de dispatch
+y los atributos de clase en combinación con la herencia, haciendo necesaria una especificación sobre la
+**representación en memoria** que tendría cada objeto. Sobre esto útimo podemos explicar que se decidió representar
+los objetos como:
+- Marca de clase (4 bytes): Un entero usado para identificar cada tipo del programa.
+- Tamaño (4 bytes): Un entero empleado para representar el tamaño, en doble palabras, de la representación del objeto
+en memoria.
+- Puntero a la tabla de dispatch (4 bytes): Un entero que representa la posición en memoria donde se encuentra
+la tabla de dispatch del objeto.
+- Definición de atributos de la clase padre.
+- Definición de atributos de la clase hijo.
+Primero son definidos los atributos de la clase padre de forma recursiva,
+luego son definidos los atributos de la clase hijo, colocando estos de forma ordenada según los nombres que tenían
+en el código COOL inicial.
+Las tablas de dispatch de cada tipo se definen de manera similar, primero las direcciones de memoria correspondientes
+a las funciones de las clases padres, o a las de la clase hijo en caso de que hayan sido redefinidas, y luego las
+direcciones de memoria de las funciones de la clase hijo, ordenadas alfabéticamente según sus nombres iniciales.
+Finalmente las funciones init e init_attr de la clase correspondiente. Cada dirección de memoria corresponde a un
+entero de 32 bits. El orden de las funciones en la tabla de dispatch inicia por las del padre para poder ejecutar
+correctamente el llamado a una función redefinida en un objeto del tipo hijo cuando este es tratado como un
+objeto del tipo padre (polimorfismo).
+
+Finalmente, el fichero MipsAstFormatter es el encargado de transformar el AST de MIPS a formato string para
+luego escribir este en el archivo final.
+
+# Invocación de métodos virtuales
+Para mostrar como se resolvió la invocación de métodos virtuales usaremos un ejemplo. Supongamos que tenemos una clase A
+que posee las funciones foo1 y foo2, y una clase B que hereda de A que posee las funciones foo1 (Sobreescribiendo a foo1
+en A) y foo3.
+
+El código MIPS a continuación corresponde a la plantilla que se genera para las clases A y B respectivamente cuando
+el programa es compilado.
+
+```MIPS
+type_6_dispatch:
+ .word function_abort_at_Object
+ .word function_copy_at_Object
+ .word function_type_name_at_Object
+ .word function_foo1_at_A
+ .word function_foo2_at_A
+ .word __init_attr_at_A
+ .word __init_at_A
+
+type_6_prototype:
+ .word 5
+ .word 4
+ .word type_6_dispatch
+ .word -1
+
+
+type_7_dispatch:
+ .word function_abort_at_Object
+ .word function_copy_at_Object
+ .word function_type_name_at_Object
+ .word function_foo1_at_B
+ .word function_foo2_at_A
+ .word function_foo3_at_B
+ .word __init_attr_at_B
+ .word __init_at_B
+
+type_7_prototype:
+ .word 6
+ .word 4
+ .word type_7_dispatch
+ .word -1
+```
+
+Al instanciar un objeto se copia la plantilla correspondiente a la representacion en memoria de dicho objeto, es decir,
+se copia la sección prototype del objeto, y se instancian cada uno de sus atributos de forma recursiva. La sección
+prototype posee la dirección de memoria correspondiente a la tabla de dispatch del objeto. Esta dirección es la que es
+usada para hacer los llamados a los métodos de la clase.
+
+Cada vez que se intente llamar a un método de una clase lo primero que se hace es buscar la dirección de la tabla de
+dispatch del objeto al cual se le está haciendo el llamado, y luego el índice del método que se está llamando,
+para luego efectuar un jump and link (jal) a la dirección a la que apunta la tabla de dispatch sumando cuatro veces el
+índice de dicho método.
+
+Ahora, observemos que la tabla de dispatch de la clase B posee todos los métodos de la clase A en
+el mismo orden que esta, es decir, el offset correspondiente al método foo1 es el mismo para la clase A y para la clase
+B. De esta forma si se utiliza un objeto de
+tipo B como si fuera de tipo A, llamando a un método del mismo, digamos foo1, entonces el compilador solo tiene que
+ocuparse de buscar el índice del método dentro de la clase A y la dirección de la tabla de dispatch del objeto que se
+está usando (guardada en la sección de memoria correspondiente a dicho objeto), luego, como el índice del método en A
+corresponde con el índice del método sobreescrito en B solo faltaría sumarle dicho offset a la tabla de dispatch para
+obtener el método que debe usarse, en este caso ```function_foo1_at_B```. La clase ***DynamicCallNode*** de Cil es la
+encargada de manejar la lógica de los llamados a métodos virtuales a objetos cuando se traduce de Cil a Mips.
+
+La clase ***StaticCallNode*** de Cil es usada explícitamente para los llamados a un método de un tipo en específico
+usando la sintaxis ```object@TYPE.method()``` definida en COOL. Esta clase, en vez de buscar la tabla de dispatch
+correspondiente al objeto que se está llamando, busca la tabla de dispatch del objeto que se especifica luego del
+caracter @, de esta forma, una vez calculado el offset de dicho método, este corresponderá precisamente al método
+correspondiente al tipo especificado.
+
+En conclusión, si se hace ```(new B).foo1()``` se llama al método foo1 en B haciendo uso de la tabla de dispatch del
+objeto B que se creó, sin embargo, si se hace ```(new B)@A.foo1()``` se llama al método foo1 en A haciendo uso de la
+tabla de dispatch del prototype del objeto A. Mientras que si se hace ```object.foo1()``` en una función que reciba
+un parámetro de tipo A, pero a la que se le está pasando un objeto de tipo B, se llamará al método correspondiente en B,
+utilizando la tabla de dispatch del objeto que se está pasando como parámetro.
+
+El índice del método en la tabla de dispatch es calculado en tiempo de compilación usando el índice del método
+en el array de métodos de cada tipo recolectado durante la traducción de Cil a Mips. Para que estos índices coincidan
+se colocaron, para cada tipo, primeramente los métodos correspondientes al padre, y luego los métodos correspondientes
+específicamente al hijo, ordenados estos últimos de forma lexicográfica.
+
+# Boxing/Unboxing
+
+Para el proceso de boxing-unboxing se utilizó un mecanismo similar al de las funciones virtuales. Cuando se necesite
+acceder a un atributo de un objeto, se toma la dirección en memoria del objeto y el índice de dicho atributo dentro del
+objeto, utilizando el índice del nombre del atributo en el
+array de attributos recolectado durante la traducción de Cil a Mips. Estos índices son los que se usan siempre que es
+necesario obtener o cambiar un atributo a un objeto, y se definen de forma unívoca durante el ya mencionado proceso de
+recolección.
+
+Los atributos de cada objeto se encuentran colocados luego de la dirección de la tabla de dispatch del mismo (tres doble
+palabras luego de la dirección de memoria del objeto). Además, el índice de un atributo para una clase hijo coincide con
+la del mismo atributo para la clase padre, haciendo posible de esta forma el uso de los atributos durante el polimorfismo.
+
+Los objetos de tipo Int, Bool y String poseen un atributo llamado _value_ que es usado para guardar el valor del objeto,
+en los dos primeros casos, y la dirección de memoria donde se encuentra la cadena de caracteres real, en el tercer caso.
+Este atributo es el usado para el trabajo con dichos objetos.
+
+Para las operaciones aritméticas y de comparación fue necesario tomar el valor real de los objetos que se operan haciéndole
+unboxing a los mismos, y luego operando dichos valores directamente. Haciéndole boxing a la respuesta para poder luego
+guardarla. Por ejemplo, si se desea comparar dos enteros, estos serán dos objetos en memoria que poseen luego de la tabla
+dispatch un entero correspondiente al valor real de los mismos, el proceso de unboxing consiste en tomar dichos valores
+y compararlos, para luego instanciar un objeto de tipo Bool y colocarle luego de la tabla de dispatch (en el
+atributo value) el resultado.
+
+# Gramática
+
+### Terminales
+A continuación se muestran los terminales de la gramática, donde entre paréntesis se muestran
+los símbolos que corresponden a COOL en los casos en que era requerido:
+
+- *classx (class), inherits, function*
+- *ifx (if), then, elsex (else), fi*
+- *whilex (while), loop, pool*
+- *let, inx (in)*
+- *case, of, esac*
+- *semi (;), colon (:), comma (,), dot(.), at (@), opar((), cpar ())*
+- *ocur ({), ccur (}), larrow (<-), rarrow (=>)*
+- *plus (+), minus (-), star (\*), div (/), isvoid, compl (~)*
+- *notx (not), less (<), leq (<=), equal (=)*
+- *new, idx (id), typex (type), integer, string, boolx (bool)*
+
+
+### No Terminales
+
+A continuación se muestran los no terminales de la gramática definida:
+
+- __program__
+- __class_list, def_class__
+- __feature_list, feature__
+- __param_list, param__
+- __expr_1, expr_2, member_call, expr_list, let_list, case_list__
+- __comp_expr, arith, arith_2, term, factor, factor_2__
+- __atom, func_call, arg_list__
+
+### Producciones de la Gramática
+A continuación se muestran las producciones de la gramática. En *cursiva* se muestran los terminales
+y en **negrita** los no terminales
+
+__program__ ⟶ __class_list__
+
+__class_list__ ⟶ __def_class class_list__
+__class_list__ ⟶ __def_class__
+
+**def_class** ⟶ *class* *typex* { **feature_list** } ;
+**def_class** ⟶ *class* *typex* *inherits* *typex* { **feature_list** } ;
+
+**feature_list** ⟶ **feature feature_list**
+**feature_list** ⟶ 𝜺
+
+**feature** ⟶ *idx* : *typex* ;
+**feature** ⟶ *idx* : *typex* <- **expr_1**;
+**feature** ⟶ *idx* (**param_list**) : *typex* { **expr_1** } ;
+**feature** ⟶ *idx* () : *typex* { **expr_1** };
+**feature** ⟶ **function** *idx* (**param_list**) : *typex* {**expr_1**};
+**feature** ⟶ **function** *idx* () : *typex* {**expr_1**};
+
+**param_list** ⟶ **param**
+**param_list** ⟶ **param** , **param_list**
+
+**param** ⟶ *idx* : *typex*
+
+**expr_1** ⟶ *idx* <- **expr_1**
+**expr_1** ⟶ *not* **expr_1**
+**expr_1** ⟶ **expr_2** = **expr_1**
+**expr_1** ⟶ **expr_2**
+
+**expr_2** ⟶ **arith** < **arith**
+**expr_2** ⟶ **arith** <= **arith**
+**expr_2** ⟶ **arith**
+
+**arith** ⟶ **arith** + **factor**
+**arith** ⟶ **arith** - **factor**
+**arith** ⟶ **factor**
+
+**factor** ⟶ **factor** \* **term**
+**factor** ⟶ **factor** / **term**
+**factor** ⟶ **term**
+
+**term** ⟶ ~ **term**
+**term** ⟶ *isvoid* **term**
+**term** ⟶ **atom**
+
+**atom** ⟶ (**expr_1**)
+**atom** ⟶ *string*
+**atom** ⟶ *bool*
+**atom** ⟶ *idx*
+**atom** ⟶ *ifx* **expr_1** *then* **expr_1** *else* **expr_1** *fi*
+**atom** ⟶ *whilex* **expr_1** *loop* **expr_1** *pool*
+**atom** ⟶ *new* *typex*
+
+**atom** ⟶ { **expr_list** }
+**expr_list** ⟶ **expr_1** ;
+**expr_list** ⟶ **expr_1** ; **expr_list**
+
+**atom** ⟶ *case* **expr_1** *of* **case_list** *esac*
+**case_list** ⟶ *idx* : *typex* => **expr_1** ;
+**case_list** ⟶ *idx* : *typex* => **expr_1** ; **case_list**
+
+**atom** ⟶ **func_call**
+**func_call** ⟶ **atom** @ *typex* . *idx* (**arg_list**)
+**func_call** ⟶ **atom** @ *typex* . *idx* ()
+**func_call** ⟶ **atom** . *idx* (**arg_list**)
+**func_call** ⟶ **atom** . *idx* ()
+
+**func_call** ⟶ *idx* (**arg_list**)
+**func_call** ⟶ *idx* ()
+
+**arg_list** ⟶ **expr_list**
+**arg_list** ⟶ **expr_list** , **arg_list**
+
+
+## Licencia
+
+[MIT](https://github.com/NinjaProgrammers/cool-compiler-2021/blob/Proyecto-CMP/LICENSE)
+
+
+
+
-## Estructura del reporte
-Usted es libre de estructurar su reporte escrito como más conveniente le parezca. A continuación le sugerimos algunas secciones que no deberían faltar, aunque puede mezclar, renombrar y organizarlas de la manera que mejor le parezca:
-- **Uso del compilador**: detalles sobre las opciones de líneas de comando, si tiene opciones adicionales (e.j., `--ast` genera un AST en JSON, etc.). Básicamente lo mismo que pondrá en este Readme.
-- **Arquitectura del compilador**: una explicación general de la arquitectura, en cuántos módulos se divide el proyecto, cuantas fases tiene, qué tipo de gramática se utiliza, y en general, como se organiza el proyecto. Una buena imagen siempre ayuda.
-- **Problemas técnicos**: detalles sobre cualquier problema teórico o técnico interesante que haya necesitado resolver de forma particular.
-## Sobre la Fecha de Entrega
-Se realizarán recogidas parciales del proyecto a lo largo del curso. En el Canal de Telegram se anunciará la fecha y requisitos de cada entrega.
diff --git a/doc/team.yml b/doc/team.yml
index c16162532..3e667bbea 100644
--- a/doc/team.yml
+++ b/doc/team.yml
@@ -1,10 +1,10 @@
members:
- - name: Nombre Apellido1 Apellido2
- github: github_id
- group: CXXX
- - name: Nombre Apellido1 Apellido2
- github: github_id
- group: CXXX
- - name: Nombre Apellido1 Apellido2
- github: github_id
- group: CXXX
+ - name: Marcos Adrian Valdivie Rodriguez
+ github: mavaldivie
+ group: C412
+ - name: Claudia Olavarrieta Martinez
+ github: ClaudiOM
+ group: C411
+ - name: Adrian Hernandez Perez
+ github: AdrianHP
+ group: C411
diff --git a/requirements.txt b/requirements.txt
index 9eb0cad1a..46bf2782f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
-pytest
-pytest-ordering
+ply>=3.11
+pydot>=1.4.2
+pytest>=7.0.1
diff --git a/src/Main.py b/src/Main.py
new file mode 100644
index 000000000..d35d66565
--- /dev/null
+++ b/src/Main.py
@@ -0,0 +1,75 @@
+from core.lexer.Lexer import Lexer
+from core.parser.Parser import CoolParser
+from core.tools.Evaluation import evaluate_reverse_parse
+from core.semantic.TypeCollector import Type_Collector
+from core.semantic.TypeBuilder import Type_Builder
+from core.semantic.TypeChecker import Type_Checker
+from core.cil.CoolToCilVisitor import COOLToCILVisitor
+from core.mips.CilToMipsVisitor import CILToMIPSVisitor
+from core.mips.MipsAstFormatter import MIPSAstFormatter
+
+
+def main(args):
+ try:
+ with open(args.file, 'r') as fd:
+ code = fd.read()
+ except Exception as e:
+ print(f"(0,0) - CompilerError: {e.args}")
+ print(args.file)
+ exit(1)
+
+ lexer = Lexer()
+ tokens = lexer.tokenize(code)
+ for t in lexer.errors:
+ print(t)
+ if any(lexer.errors): exit(1)
+
+ productions, operations = CoolParser(tokens)
+ for e in CoolParser.errors:
+ print(e)
+ if any(CoolParser.errors): exit(1)
+ COOLast = evaluate_reverse_parse(productions, operations, tokens)
+
+ type_Collector = Type_Collector()
+ type_Collector.visit(COOLast)
+ for e in type_Collector.errors:
+ print(e)
+ if any(type_Collector.errors): exit(1)
+ context = type_Collector.Context
+
+ type_Builder = Type_Builder(context)
+ type_Builder.visit(COOLast)
+ for e in type_Builder.errors:
+ print(e)
+ if any(type_Builder.errors): exit(1)
+
+ type_Checker = Type_Checker(context)
+ scope = type_Checker.visit(COOLast)
+ for e in type_Checker.errors:
+ print(e)
+ if any(type_Checker.errors): exit(1)
+
+ CILVisitor = COOLToCILVisitor(type_Checker.Context)
+ CILast = CILVisitor.visit(COOLast, scope)
+
+ MIPSVisitor = CILToMIPSVisitor()
+ MIPSAst = MIPSVisitor.visit(CILast)
+ MIPSFormatter = MIPSAstFormatter()
+ mipsCode = MIPSFormatter.visit(MIPSAst)
+
+ out_file = args.file.split(".")
+ out_file[-1] = "mips"
+ out_file = ".".join(out_file)
+
+ with open(out_file, 'w') as f:
+ f.write(mipsCode)
+
+
+if __name__ == "__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser(description='CoolCompiler pipeline')
+ parser.add_argument('-f', '--file', type=str, default='code.cl', help='file to read')
+
+ args = parser.parse_args()
+ main(args)
diff --git a/src/coolc.sh b/src/coolc.sh
index 3088de4f9..cdfddb895 100755
--- a/src/coolc.sh
+++ b/src/coolc.sh
@@ -1,11 +1,13 @@
-# Incluya aquí las instrucciones necesarias para ejecutar su compilador
+#!/bin/bash
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 "CMA COOL COMPILER v1.0"
+echo "Copyright (c) 2019: Marcos Valdivie, Claudia Olavarrieta, Adrian Hernandez"
# Llamar al compilador
-echo "Compiling $INPUT_FILE into $OUTPUT_FILE"
+
+#echo "Compiling $INPUT_FILE into $OUTPUT_FILE"
+python3 Main.py -f $INPUT_FILE
\ No newline at end of file
diff --git a/src/core/cil/BaseCoolToCilVisitor.py b/src/core/cil/BaseCoolToCilVisitor.py
new file mode 100644
index 000000000..7da7756b7
--- /dev/null
+++ b/src/core/cil/BaseCoolToCilVisitor.py
@@ -0,0 +1,302 @@
+from ..cil import CilAst as cil
+from ..tools.Semantic import VariableInfo
+
+
+class BaseCOOLToCILVisitor:
+ def __init__(self, context):
+ self.dottypes = []
+ self.dotdata = []
+ self.dotcode = []
+
+ self.types_map = {}
+ self.current_type = None
+ self.current_method = None
+ self.current_function = None
+
+ self.context = context
+ self.vself = VariableInfo('self', None)
+ self.value_types = ['String', 'Int', 'Bool']
+
+ self.var_names = {}
+
+ self.breakline_data = self.register_data('\n', 0, 0)
+ self.emptystring_data = self.register_data('', 0, 0)
+
+ @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, line, column):
+ name = f'local_param_{self.current_function.name}_{vinfo.name}_{len(self.params)}'
+ param_node = cil.ParamNode(name, line, column)
+ self.params.append(param_node)
+ self.var_names[vinfo.name] = cil.VarNode(name, line, column)
+ return self.var_names[vinfo.name]
+
+ def register_local(self, vinfo, line, column):
+ name = vinfo.name
+ vinfo.name = f'local_{self.current_function.name}_{vinfo.name}_{len(self.localvars)}'
+ local_node = cil.LocalNode(vinfo.name, line, column)
+ self.localvars.append(local_node)
+ self.var_names[name] = cil.VarNode(vinfo.name, line, column)
+ return self.var_names[name]
+
+ def register_attribute(self, name, type, line, column):
+ name = f'attr_{type}_{name}'
+ return cil.AttributeNode(name, type, line, column)
+
+
+ def define_internal_local(self, line, column):
+ vinfo = VariableInfo('internal', None)
+ return self.register_local(vinfo, line, column)
+
+ def register_instruction(self, instruction):
+ self.instructions.append(instruction)
+ return instruction
+
+ def to_function_name(self, method_name, type_name):
+ return f'function_{method_name}_at_{type_name}'
+
+ def register_function(self, function_name, line, column):
+ function_node = cil.FunctionNode(function_name, [], [], [], line, column)
+ self.dotcode.append(function_node)
+ return function_node
+
+ def register_type(self, name, line, column):
+ type_node = cil.TypeNode(name, line, column)
+ self.types_map[name] = type_node
+ self.dottypes.append(type_node)
+ return type_node
+
+ def register_data(self, value, line, column):
+ vname = f'data_{len(self.dotdata)}'
+ data_node = cil.DataNode(vname, value, line, column)
+ self.dotdata.append(data_node)
+ return data_node
+
+ def register_label(self, label, line, column):
+ lname = f'{label}_{self.current_function.labels_count}'
+ self.current_function.labels_count += 1
+ return cil.LabelNode(lname, line, column)
+
+ def init_name(self, name):
+ return f'__init_at_{name}'
+
+ def init_attr_name(self, name):
+ return f'__init_attr_at_{name}'
+
+ def register_runtime_error(self, condition, msg, line, column):
+ error_node = self.register_label('error_label', line, column)
+ continue_node = self.register_label('continue_label', line, column)
+ self.register_instruction(cil.GotoIfNode(condition, error_node.label, line, column))
+ self.register_instruction(cil.GotoNode(continue_node.label, line, column))
+ self.register_instruction(error_node)
+ data_node = self.register_data(msg, line, column)
+ self.register_instruction(cil.ErrorNode(data_node, line, column))
+ self.register_instruction(continue_node)
+
+ def register_builtin(self):
+ # Object
+ line, column = 0, 0
+ type_node = self.register_type('Object', line, column)
+
+ self.current_function = self.register_function(self.init_name('Object'), line, column)
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.AllocateNode('Object', instance, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ self.current_function = self.register_function(self.to_function_name('abort', 'Object'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ vname = self.define_internal_local(line, column)
+ abort_data = self.register_data('Abort called from class ', line, column)
+ self.register_instruction(cil.LoadNode(vname, abort_data, line, column))
+ self.register_instruction(cil.PrintStringNode(vname, line, column))
+ self.register_instruction(cil.TypeOfNode(vname, self_param, line, column))
+ self.register_instruction(cil.PrintStringNode(vname, line, column))
+ self.register_instruction(cil.LoadNode(vname, self.breakline_data, line, column))
+ self.register_instruction(cil.PrintStringNode(vname, line, column))
+ self.register_instruction(cil.ExitNode())
+
+ self.current_function = self.register_function(self.to_function_name('type_name', 'Object'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ result = self.define_internal_local(line, column)
+ self.register_instruction(cil.TypeOfNode(result, self_param, line, column))
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.ArgNode(result, line, column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ self.current_function = self.register_function(self.to_function_name('copy', 'Object'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ result = self.define_internal_local(line, column)
+ self.register_instruction(cil.CopyNode(result, self_param, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, result))
+
+ type_node.methods = {name: self.to_function_name(name, 'Object') for name in ['abort', 'type_name', 'copy']}
+ type_node.methods['init'] = self.init_name('Object')
+ obj_methods = ['abort', 'type_name', 'copy']
+
+ # IO
+ type_node = self.register_type('IO', line, column)
+
+ self.current_function = self.register_function(self.init_name('IO'), line, column)
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.AllocateNode('IO', instance, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ self.current_function = self.register_function(self.to_function_name('out_string', 'IO'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ x = self.register_param(VariableInfo('x', None), line, column)
+ vname = self.define_internal_local(line, column)
+ self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'String', line, column))
+ self.register_instruction(cil.PrintStringNode(vname, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, self_param))
+
+ self.current_function = self.register_function(self.to_function_name('out_int', 'IO'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ x = self.register_param(VariableInfo('x', None), line, column)
+ vname = self.define_internal_local(line, column)
+ self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'Int', line, column))
+ self.register_instruction(cil.PrintIntNode(vname, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, self_param))
+
+ self.current_function = self.register_function(self.to_function_name('in_string', 'IO'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ result = self.define_internal_local(line, column)
+ self.register_instruction(cil.ReadStringNode(result, line, column))
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.ArgNode(result, line, column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, value=instance))
+
+ self.current_function = self.register_function(self.to_function_name('in_int', 'IO'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ result = self.define_internal_local(line, column)
+ self.register_instruction(cil.ReadIntNode(result, line, column))
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.ArgNode(result, line, column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods}
+ type_node.methods.update({name: self.to_function_name(name, 'IO') for name in
+ ['out_string', 'out_int', 'in_string', 'in_int']})
+ type_node.methods['init'] = self.init_name('IO')
+
+ # String
+ type_node = self.register_type('String', line, column)
+ type_node.attributes = {name:self.register_attribute(name, 'String', 0, 0) for name in ['value', 'length']}
+
+ self.current_function = self.register_function(self.init_name('String'), line, column)
+ val = self.register_param(VariableInfo('val', None), line, column)
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.AllocateNode('String', instance, line, column))
+ self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'String', line, column))
+ result = self.define_internal_local(line, column)
+ self.register_instruction(cil.LengthNode(result, val, line, column))
+ attr = self.define_internal_local(line, column)
+ self.register_instruction(cil.ArgNode(result, line, column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), attr, line, column))
+ self.register_instruction(cil.SetAttribNode(instance, 'length', attr, 'String', line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ self.current_function = self.register_function(self.to_function_name('length', 'String'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ result = self.define_internal_local(line, column)
+ self.register_instruction(cil.GetAttribNode(result, self_param, 'length', 'String', line, column))
+ self.register_instruction(cil.ReturnNode(line, column, result))
+
+ self.current_function = self.register_function(self.to_function_name('concat', 'String'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ s = self.register_param(VariableInfo('s', None), line, column)
+ str_1 = self.define_internal_local(line, column)
+ str_2 = self.define_internal_local(line, column)
+ length_1 = self.define_internal_local(line, column)
+ length_2 = self.define_internal_local(line, column)
+ self.register_instruction(cil.GetAttribNode(str_1, self_param, 'value', 'String', line, column))
+ self.register_instruction(cil.GetAttribNode(str_2, s, 'value', 'String', line, column))
+ self.register_instruction(cil.GetAttribNode(length_1, self_param, 'length', 'String', line, column))
+ self.register_instruction(cil.GetAttribNode(length_2, s, 'length', 'String', line, column))
+ self.register_instruction(cil.GetAttribNode(length_1, length_1, 'value', 'Int', line, column))
+ self.register_instruction(cil.GetAttribNode(length_2, length_2, 'value', 'Int', line, column))
+ self.register_instruction(cil.PlusNode(length_1, length_1, length_2, line, column))
+
+ result = self.define_internal_local(line, column)
+ self.register_instruction(cil.ConcatNode(result, str_1, str_2, length_1, line, column))
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.ArgNode(result, line, column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ self.current_function = self.register_function(self.to_function_name('substr', 'String'), line, column)
+ self_param = self.register_param(self.vself, line, column)
+ i = self.register_param(VariableInfo('i', None), line, column)
+ l = self.register_param(VariableInfo('l', None), line, column)
+ result = self.define_internal_local(line, column)
+ index_value = self.define_internal_local(line, column)
+ length_value = self.define_internal_local(line, column)
+ length_wrapper = self.define_internal_local(line, column)
+ length_attr = self.define_internal_local(line, column)
+ length_substr = self.define_internal_local(line, column)
+ less_value = self.define_internal_local(line, column)
+ str_value = self.define_internal_local(line, column)
+ self.register_instruction(cil.GetAttribNode(str_value, self_param, 'value', 'String', line, column))
+ self.register_instruction(cil.GetAttribNode(index_value, i, 'value', 'Int', line, column))
+ self.register_instruction(cil.GetAttribNode(length_value, l, 'value', 'Int', line, column))
+ # Check Out of range error
+ self.register_instruction(cil.GetAttribNode(length_wrapper, self_param, 'length', 'String', line, column))
+ self.register_instruction(cil.GetAttribNode(length_attr, length_wrapper, 'value', 'Int', line, column))
+ self.register_instruction(cil.PlusNode(length_substr, length_value, index_value, line, column))
+ self.register_instruction(cil.LessNode(less_value, length_attr, length_substr, line, column))
+ self.register_runtime_error(less_value, 'Substring out of range', line, column)
+ self.register_instruction(cil.SubstringNode(result, str_value, index_value, length_value, line, column))
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.ArgNode(result, line, column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods}
+ type_node.methods.update({name: self.to_function_name(name, 'String') for name in ['length', 'concat', 'substr']})
+ type_node.methods['init'] = self.init_name('String')
+
+ # Int
+ type_node = self.register_type('Int', line, column)
+ type_node.attributes = {name:self.register_attribute(name, 'Int', 0, 0) for name in ['value']}
+
+ self.current_function = self.register_function(self.init_name('Int'), line, column)
+ val = self.register_param(VariableInfo('val', None), line, column)
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.AllocateNode('Int', instance, line, column))
+ self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'Int', line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ type_node.methods = {method:self.to_function_name(method, 'Object') for method in obj_methods}
+ type_node.methods['init'] = self.init_name('Int')
+
+ # Bool
+ type_node = self.register_type('Bool', line, column)
+ type_node.attributes = {name:self.register_attribute(name, 'Bool', 0, 0) for name in ['value']}
+
+ self.current_function = self.register_function(self.init_name('Bool'), line, column)
+ val = self.register_param(VariableInfo('val', None), line, column)
+ instance = self.define_internal_local(line, column)
+ self.register_instruction(cil.AllocateNode('Bool', instance, line, column))
+ self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'Bool', line, column))
+ self.register_instruction(cil.ReturnNode(line, column, instance))
+
+ type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods}
+ type_node.methods['init'] = self.init_name('Bool')
+
+
diff --git a/src/core/cil/CilAst.py b/src/core/cil/CilAst.py
new file mode 100644
index 000000000..ae3b85863
--- /dev/null
+++ b/src/core/cil/CilAst.py
@@ -0,0 +1,300 @@
+from ..tools import visitor
+
+class Node:
+ def __init__(self, line=0, column=0):
+ self.line = line
+ self.column = column
+
+
+class ProgramNode(Node):
+ def __init__(self, dottypes, dotdata, dotcode, line, column):
+ super().__init__(line,column)
+ self.dottypes = dottypes
+ self.dotdata = dotdata
+ self.dotcode = dotcode
+
+class TypeNode(Node):
+ def __init__(self, name, line, column):
+ super().__init__(line, column)
+ self.name = name
+ self.attributes = {}
+ self.methods = {}
+
+class DataNode(Node):
+ def __init__(self, vname, value, line, column):
+ super().__init__(line, column)
+ self.name = vname
+ self.value = value
+
+class FunctionNode(Node):
+ def __init__(self, fname, params, localvars, instructions, line, column):
+ super().__init__(line, column)
+ self.name = fname
+ self.params = params
+ self.localvars = localvars
+ self.instructions = instructions
+ self.labels_count = 0
+
+class LocalNode(Node):
+ def __init__(self, name, line, column):
+ super().__init__(line, column)
+ self.name = name
+
+class InstructionNode(Node):
+ pass
+
+class AssignNode(InstructionNode):
+ def __init__(self, dest, source, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.source = source
+
+class ArithmeticNode(InstructionNode):
+ def __init__(self, dest, left, right, line, column):
+ super().__init__(line, column)
+ 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 LessNode(ArithmeticNode):
+ pass
+
+class EqualNode(ArithmeticNode):
+ pass
+
+class EqualStringNode(ArithmeticNode):
+ pass
+
+class LessEqualNode(ArithmeticNode):
+ pass
+
+class GetAttribNode(InstructionNode):
+ def __init__(self, dest, obj, attr, computed_type, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.obj = obj
+ self.attr = attr
+ self.computed_type = computed_type
+
+ def __repr__(self):
+ return f"{self.dest} = GETATTR {self.obj} {self.attr}"
+
+
+class SetAttribNode(InstructionNode):
+ def __init__(self, obj, attr, value, computed_type, line, column):
+ super().__init__(line, column)
+ self.obj = obj
+ self.attr = attr
+ self.value = value
+ self.computed_type = computed_type
+
+class AllocateNode(InstructionNode):
+ def __init__(self, itype, dest, line, column):
+ super().__init__(line, column)
+ self.type = itype
+ self.dest = dest
+
+class TypeOfNode(InstructionNode):
+ def __init__(self, dest, obj, line, column):
+ super().__init__(line, column)
+ self.obj = obj
+ self.dest = dest
+
+ def __repr__(self):
+ return f"{self.dest} = TYPEOF {self.obj}"
+
+
+class LabelNode(InstructionNode):
+ def __init__(self, label, line, column):
+ super().__init__(line, column)
+ self.label = label
+
+ def __repr__(self):
+ return f"LABEL {self.label}:"
+
+
+class GotoNode(InstructionNode):
+ def __init__(self, label, line, column):
+ super().__init__(line, column)
+ self.label = label
+
+ def __repr__(self):
+ return f"GOTO {self.label}"
+
+
+class GotoIfNode(InstructionNode):
+ def __init__(self, condition, label, line, column):
+ super().__init__(line, column)
+ self.condition = condition
+ self.label = label
+
+ def __repr__(self):
+ return f"GOTO {self.label} if {self.condition}"
+
+class StaticCallNode(InstructionNode):
+ def __init__(self, function, dest, line, column):
+ super().__init__(line, column)
+ self.function = function
+ self.dest = dest
+
+ def __repr__(self):
+ return f"{self.dest} = CALL {self.function}"
+
+class DynamicCallNode(InstructionNode):
+ def __init__(self, type, method, dest, line, column):
+ super().__init__(line, column)
+ self.type = type
+ self.method = method
+ self.dest = dest
+
+ def __repr__(self):
+ return f"{self.dest} = VCALL {self.type} {self.method}"
+
+class ArgNode(InstructionNode):
+ def __init__(self, name, line, column):
+ super().__init__(line, column)
+ self.name = name
+
+ def __repr__(self):
+ return f"ARG {self.name}"
+
+
+class ReturnNode(InstructionNode):
+ def __init__(self, line, column, value=None):
+ super().__init__(line, column)
+ self.value = value
+
+ def __repr__(self):
+ return f"RETURN {self.value}"
+
+
+class LoadNode(InstructionNode):
+ def __init__(self, dest, msg, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.msg = msg
+
+ def __repr__(self):
+ return f"{self.dest} LOAD {self.msg}"
+
+class LengthNode(InstructionNode):
+ def __init__(self, dest, source, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.source = source
+
+class ConcatNode(InstructionNode):
+ def __init__(self, dest, prefix, suffix, length, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.prefix = prefix
+ self.suffix = suffix
+ self.length = length
+
+class PrefixNode(InstructionNode):
+ pass
+
+class SubstringNode(InstructionNode):
+ def __init__(self, dest, str_value, index, length, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.str_value = str_value
+ self.index = index
+ self.length = length
+
+class ToStrNode(InstructionNode):
+ def __init__(self, dest, value, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.value = value
+
+class ReadStringNode(InstructionNode):
+ def __init__(self, dest, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+
+class ReadIntNode(InstructionNode):
+ def __init__(self, dest, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+
+class PrintStringNode(InstructionNode):
+ def __init__(self, str_addr, line, column):
+ super().__init__(line, column)
+ self.str_addr = str_addr
+
+class PrintIntNode(InstructionNode):
+ def __init__(self, value, line, column):
+ super().__init__(line, column)
+ self.value = value
+
+class ExitNode(InstructionNode):
+ pass
+
+class CopyNode(InstructionNode):
+ def __init__(self, dest, value, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.value = value
+
+class ErrorNode(InstructionNode):
+ def __init__(self, data, line, column):
+ super().__init__(line, column)
+ self.data = data
+
+class VoidNode(InstructionNode):
+ def __str__(self):
+ return 'VOID'
+
+class NameNode(InstructionNode):
+ def __init__(self, dest, value, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.value = value
+
+class NotNode(InstructionNode):
+ def __init__(self, dest, value, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.value = value
+
+class ComplementNode(InstructionNode):
+ def __init__(self, dest, value, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.value = value
+
+class VarNode(InstructionNode):
+ def __init__(self, name, line, column):
+ super().__init__(line, column)
+ self.name = name
+
+ def __str__(self):
+ return f'{self.name}'
+
+class AttributeNode(VarNode):
+ def __init__(self, name, type, line, column):
+ super().__init__(name, line, column)
+ self.type = type
+
+ def __str__(self):
+ return f'{self.type}.{self.name}'
+
+class ParamNode(VarNode):
+ def __init__(self, name, line, column):
+ super().__init__(name, line, column)
+
+ def __str__(self):
+ return f'PARAM {self.name}'
\ No newline at end of file
diff --git a/src/core/cil/CilAstFormatter.py b/src/core/cil/CilAstFormatter.py
new file mode 100644
index 000000000..96bfbe31b
--- /dev/null
+++ b/src/core/cil/CilAstFormatter.py
@@ -0,0 +1,206 @@
+from ..tools import visitor
+from .CilAst import *
+
+
+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}' for x in node.methods)
+
+ return f'type {node.name} {{\n\t{attributes}\n\t{methods}\n}}'
+
+ @visitor.when(DataNode)
+ def visit(self, node):
+ return f'DATA "{node.value}"'
+
+ @visitor.when(FunctionNode)
+ def visit(self, node):
+ params = '\n\t'.join(self.visit(x) for x in node.params)
+ localvars = '\n\t'.join(self.visit(x) for x in node.localvars)
+ instructions = '\n\t'.join(self.visit(x) for x in node.instructions)
+
+ return f'function {node.name} {{\n\t{params}\n\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.obj}'
+
+ @visitor.when(StaticCallNode)
+ def visit(self, node):
+ return f'{node.dest} = CALL {node.function}'
+
+ @visitor.when(DynamicCallNode)
+ def visit(self, node):
+ return f'{node.dest} = VCALL {node.type} {node.method}'
+
+ @visitor.when(ArgNode)
+ def visit(self, node):
+ return f'ARG {node.name}'
+
+ @visitor.when(ReturnNode)
+ def visit(self, node):
+ return f'RETURN {node.value if node.value is not None else ""}'
+
+ @visitor.when(LoadNode)
+ def visit(self, node):
+ return f'{node.dest} = LOAD {self.visit(node.msg)}'
+
+ @visitor.when(PrintStringNode)
+ def visit(self, node: PrintStringNode):
+ return f'PRINTSTRING {node.str_addr}'
+
+ @visitor.when(PrintIntNode)
+ def visit(self, node: PrintIntNode):
+ return f'PRINTINT {node.value}'
+
+ @visitor.when(ExitNode)
+ def visit(self, node: ExitNode):
+ return f'EXIT'
+
+ @visitor.when(CopyNode)
+ def visit(self, node):
+ return f'{node.dest} = COPY {node.value}'
+
+ @visitor.when(GetAttribNode)
+ def visit(self, node: GetAttribNode):
+ return f'{node.dest} = GETATTRIB {node.obj}.{node.attr} {node.computed_type}'
+
+ @visitor.when(ErrorNode)
+ def visit(self, node: ErrorNode):
+ return f'ERROR {self.visit(node.data)}'
+
+ @visitor.when(ReadStringNode)
+ def visit(self, node: ReadStringNode):
+ return f'{node.dest} = READ'
+
+ @visitor.when(ReadIntNode)
+ def visit(self, node: ReadIntNode):
+ return f'{node.dest} = READ'
+
+ @visitor.when(SetAttribNode)
+ def visit(self, node: SetAttribNode):
+ return f'SETATTR {node.obj}.{node.attr}: {node.computed_type} = {node.value}'
+
+ @visitor.when(LessNode)
+ def visit(self, node: LessNode):
+ return f'{node.dest} = {node.left} < {node.right}'
+
+ @visitor.when(GotoIfNode)
+ def visit(self, node: GotoIfNode):
+ return f'GOTOIF {node.condition} {node.label}'
+
+ @visitor.when(GotoNode)
+ def visit(self, node: GotoNode):
+ return f'GOTO {node.label}'
+
+ @visitor.when(LabelNode)
+ def visit(self, node: LabelNode):
+ return f'LABEL {node.label}'
+
+ @visitor.when(SubstringNode)
+ def visit(self, node: SubstringNode):
+ return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index} up to {node.length}]'
+
+ @visitor.when(ConcatNode)
+ def visit(self, node: ConcatNode):
+ return f'{node.dest} = CONCAT {node.prefix} + {node.suffix}'
+
+ @visitor.when(LengthNode)
+ def visit(self, node: LengthNode):
+ return f'{node.dest} = LENGTH {node.source}'
+
+ @visitor.when(EqualNode)
+ def visit(self, node: EqualNode):
+ return f'{node.dest} = {node.left} == {node.right}'
+
+ @visitor.when(NameNode)
+ def visit(self, node: NameNode):
+ return f'{node.dest} = NAME {node.value}'
+
+ @visitor.when(EqualStringNode)
+ def visit(self, node: EqualStringNode):
+ return f'{node.dest} = {node.left} == {node.right}'
+
+ @visitor.when(ComplementNode)
+ def visit(self, node: ComplementNode):
+ return f'{node.dest} = ~{node.value}'
+
+ @visitor.when(LessEqualNode)
+ def visit(self, node: LessEqualNode):
+ return f'{node.dest} = {node.left} <= {node.right}'
+
+ @visitor.when(PrefixNode)
+ def visit(self, node: PrefixNode):
+ return f'PREFFIXNODE'
+
+ @visitor.when(ToStrNode)
+ def visit(self, node: ToStrNode):
+ return f'{node.dest} = str({node.value})'
+
+ @visitor.when(VoidNode)
+ def visit(self, node: VoidNode):
+ return 'VOID'
+
+ @visitor.when(NotNode)
+ def visit(self, node: NotNode):
+ return f'{node.dest} = NOT {node.value}'
+
+ @visitor.when(VarNode)
+ def visit(self, node: VarNode):
+ return f'{node.name}'
+
+ @visitor.when(AttributeNode)
+ def visit(self, node: AttributeNode):
+ return f'ATTRIBUTE {node.type}.{node.name}'
+
+ @visitor.when(ParamNode)
+ def visit(self, node: ParamNode):
+ return f'{node.name}'
+
+
+printer = PrintVisitor()
diff --git a/src/core/cil/CoolToCilVisitor.py b/src/core/cil/CoolToCilVisitor.py
new file mode 100644
index 000000000..1078be34e
--- /dev/null
+++ b/src/core/cil/CoolToCilVisitor.py
@@ -0,0 +1,663 @@
+import core.cool.CoolAst as cool
+from .BaseCoolToCilVisitor import *
+from core.tools import visitor
+from ..tools.Semantic import Scope
+
+
+class COOLToCILVisitor(BaseCOOLToCILVisitor):
+ def __init__(self, context):
+ super().__init__(context)
+
+ def collect_types(self, node):
+ self.types_map[node.id.lex] = type = self.register_type(node.id.lex, node.id.line, node.id.column)
+ # Guardar métodos de las clases padres
+ iter_type = self.context.get_type(node.id.lex)
+
+ generation = []
+ while iter_type is not None:
+ generation.append(iter_type)
+ iter_type = iter_type.parent
+
+ generation.reverse()
+ for i in generation:
+ methods = sorted(i.methods)
+ attributes = sorted(i.attributes)
+ for meth in methods:
+ type.methods[meth] = self.to_function_name(meth, i.name)
+ for attr in attributes:
+ type.attributes[attr.name] = cil.AttributeNode(attr.name, i.name, i.line, i.column)
+
+ @visitor.on('node')
+ def visit(self, node):
+ pass
+
+ @visitor.when(cool.ProgramNode)
+ def visit(self, node: cool.ProgramNode, scope: Scope):
+ self.current_function = self.register_function('entry', line=0, column=0)
+ main_instance = self.define_internal_local(line=0, column=0)
+ self.register_instruction(cil.StaticCallNode(self.init_name('Main'), main_instance, line=0, column=0))
+ self.register_instruction(cil.ArgNode(main_instance, line=0, column=0))
+ self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'),
+ main_instance, line=0, column=0))
+ self.register_instruction(cil.ReturnNode(line=0, column=0, value=0))
+
+ self.register_builtin()
+ self.current_function = None
+ for x in node.declarations:
+ self.collect_types(x)
+
+ for x, y in zip(node.declarations, scope.childs):
+ self.visit(x, y)
+
+ return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode, line=0, column=0)
+
+ @visitor.when(cool.ClassDeclarationNode)
+ def visit(self, node: cool.ClassDeclarationNode, scope: Scope):
+ self.current_type = self.context.get_type(node.id.lex)
+ type = self.types_map[node.id.lex]
+
+ self.current_function = self.register_function(self.init_attr_name(node.id.lex),
+ line=node.line, column=node.column)
+ type.methods['__init_attr'] = self.current_function.name
+ self_param = self.register_param(self.vself, line=node.line, column=node.column)
+ self.localvars.extend(type.attributes.values())
+ self.var_names.update({i:cil.AttributeNode(j.name, type.name, node.line, node.column)
+ for i,j in type.attributes.items()})
+
+ self.vself.name = self_param
+ # Inicializando los atributos de la clase y llamando al constructor del padre
+ if self.current_type.parent.name not in ('Object', 'IO'):
+ variable = self.define_internal_local(line=node.line, column=node.column)
+ self.register_instruction(cil.ArgNode(self_param, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(
+ self.init_attr_name(self.current_type.parent.name), variable, line=node.line, column=node.column))
+
+ # Inicializando los atributos de la clase
+ for feat, child in zip(node.features, scope.childs):
+ if isinstance(feat, cool.AttrDeclarationNode):
+ self.visit(feat, child)
+ self.register_instruction(cil.SetAttribNode(self_param, feat.id.lex, feat.ret_expr, node.id.lex,
+ line=node.line, column=node.column))
+ self.register_instruction(cil.ReturnNode(node.line, node.column, self_param))
+
+ # TypeNode de la clase
+ # type = self.types_map[node.id.lex]
+ # type.attributes = [i.name for i in self.current_type.attributes]
+
+ # Visitar funciones dentro de la clase
+ for feat, child in zip(node.features, scope.childs):
+ if isinstance(feat, cool.FuncDeclarationNode):
+ self.visit(feat, child)
+ self.vself.name = 'self'
+
+ # Allocate de la clase
+ self.current_function = self.register_function(self.init_name(node.id.lex), line=node.line, column=node.column)
+ type.methods['__init'] = self.current_function.name
+ self.localvars.extend(type.attributes.values())
+ instance = self.define_internal_local(line=node.line, column=node.column)
+ self.register_instruction(cil.AllocateNode(node.id.lex, instance, line=node.line, column=node.column))
+
+ variable = self.define_internal_local(line=node.line, column=node.column)
+ self.register_instruction(cil.ArgNode(instance, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_attr_name(node.id.lex), variable,
+ line=node.line, column=node.column))
+
+ self.register_instruction(cil.ReturnNode(value=variable, line=node.line, column=node.column))
+
+ self.current_function = None
+ self.current_type = None
+
+ @visitor.when(cool.AttrDeclarationNode)
+ def visit(self, node: cool.AttrDeclarationNode, scope: Scope):
+ variable = self.define_internal_local(line=node.line, column=node.column)
+ if node.expression:
+ self.visit(node.expression, scope.childs[0])
+ self.register_instruction(cil.AssignNode(variable, node.expression.ret_expr,
+ line=node.expression.line, column=node.expression.column))
+ elif node.type.lex in self.value_types:
+ if node.type.lex == 'SELF_TYPE':
+ stype = self.current_type.name
+ else:
+ stype = node.type.lex
+
+ if stype == 'Int':
+ self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column))
+ elif stype == 'Bool':
+ self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column))
+ elif stype == 'String':
+ data = self.emptystring_data
+ self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column))
+ self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name(stype), variable,
+ line=node.line, column=node.column))
+ node.ret_expr = variable
+
+ @visitor.when(cool.FuncDeclarationNode)
+ def visit(self, node: cool.FuncDeclarationNode, scope: Scope):
+ self.current_method = self.current_type.get_method(node.id.lex)
+ type = self.types_map[self.current_type.name]
+ self.current_function = self.register_function(self.to_function_name(self.current_method.name,
+ self.current_type.name),
+ line=node.line, column=node.column)
+ self.localvars.extend(type.attributes.values())
+
+ self_param = self.register_param(self.vself, line=node.line, column=node.column)
+ self.vself.name = self_param
+ for param, type in node.params:
+ self.register_param(VariableInfo(param.lex, type.lex), line=param.line, column=param.column)
+
+ self.visit(node.body, scope.childs[0])
+ self.register_instruction(cil.ReturnNode(value=node.body.ret_expr,
+ line=node.body.line, column=node.body.column))
+ self.current_method = None
+ self.vself.name = 'self'
+
+ @visitor.when(cool.IfThenElseNode)
+ def visit(self, node: cool.IfThenElseNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ condition = self.define_internal_local(line=node.line, column=node.column)
+
+ then_label = self.register_label('then_label', line=node.line, column=node.column)
+ continue_label = self.register_label('continue_label', line=node.line, column=node.column)
+
+ # IF
+ self.visit(node.condition, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool',
+ line=node.condition.line, column=node.condition.column))
+ self.register_instruction(cil.GotoIfNode(condition, then_label.label, line=node.condition.line,
+ column=node.condition.column))
+
+ # ELSE
+ self.visit(node.else_body, scope.childs[2])
+ self.register_instruction(cil.AssignNode(ret, node.else_body.ret_expr, line=node.else_body.line,
+ column=node.else_body.column))
+ self.register_instruction(cil.GotoNode(continue_label.label, line=node.else_body.line,
+ column=node.else_body.column))
+
+ # THEN
+ self.register_instruction(then_label)
+ self.visit(node.if_body, scope.childs[1])
+ self.register_instruction(cil.AssignNode(ret, node.if_body.ret_expr, line=node.if_body.line,
+ column=node.if_body.column))
+
+ self.register_instruction(continue_label)
+ node.ret_expr = ret
+
+ @visitor.when(cool.WhileLoopNode)
+ def visit(self, node: cool.WhileLoopNode, scope: Scope):
+ while_label = self.register_label('while_label', line=node.line, column=node.column)
+ loop_label = self.register_label('loop_label', line=node.line, column=node.column)
+ pool_label = self.register_label('pool_label', line=node.line, column=node.column)
+ condition = self.define_internal_local(line=node.line, column=node.column)
+
+ self.register_instruction(while_label)
+ self.visit(node.condition, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool',
+ line=node.condition.line, column=node.condition.column))
+ self.register_instruction(cil.GotoIfNode(condition, loop_label.label, line=node.condition.line,
+ column=node.condition.column))
+ self.register_instruction(cil.GotoNode(pool_label.label, line=node.condition.line,
+ column=node.condition.column))
+
+ self.register_instruction(loop_label)
+ self.visit(node.body, scope.childs[1])
+ self.register_instruction(
+ cil.GotoNode(while_label.label, line=node.condition.line, column=node.condition.column))
+
+ self.register_instruction(pool_label)
+ node.ret_expr = cil.VoidNode()
+
+ @visitor.when(cool.BlockNode)
+ def visit(self, node: cool.BlockNode, scope: Scope):
+ for expr, child in zip(node.expressions, scope.childs):
+ self.visit(expr, child)
+ node.ret_expr = node.expressions[-1].ret_expr
+
+ @visitor.when(cool.LetInNode)
+ def visit(self, node: cool.LetInNode, scope: Scope):
+ for (id, type, expr), child in zip(node.let_body, scope.childs[:-1]):
+ variable = self.register_local(VariableInfo(id.lex, type.lex), line=id.line, column=id.column)
+ if expr:
+ self.visit(expr, child)
+ self.register_instruction(cil.AssignNode(variable, expr.ret_expr, line=expr.line, column=expr.column))
+ elif type.lex in self.value_types:
+ if type.lex == 'SELF_TYPE':
+ stype = self.current_type.name
+ else:
+ stype = type.lex
+ if stype == 'Int':
+ self.register_instruction(cil.ArgNode(0, line=node.line, column=node.column))
+ elif stype == 'Bool':
+ self.register_instruction(cil.ArgNode(0, line=node.line, column=node.column))
+ elif stype == 'String':
+ data = self.emptystring_data
+ self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column))
+ self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name(stype), variable,
+ line=node.line, column=node.column))
+
+ self.visit(node.in_body, scope.childs[-1])
+ node.ret_expr = node.in_body.ret_expr
+
+ @visitor.when(cool.CaseOfNode)
+ def visit(self, node: cool.CaseOfNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ vtype = self.define_internal_local(line=node.line, column=node.column)
+ cond = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.expression, scope.childs[0])
+ self.register_instruction(cil.TypeOfNode(vtype, node.expression.ret_expr, line=node.expression.line,
+ column=node.expression.column))
+
+ isvoid = self.define_internal_local(line=node.line, column=node.column)
+ self.register_instruction(cil.EqualNode(isvoid, node.expression.ret_expr, cil.VoidNode(),
+ line=node.expression.line, column=node.expression.column))
+ self.register_runtime_error(isvoid, f'{node.expression.line, node.expression.column} - '
+ f'RuntimeError: Case on void',
+ line=node.expression.line, column=node.expression.column)
+
+ end_label = self.register_label('case_end_label', line=node.line, column=node.column)
+
+ branch_type = self.define_internal_local(line=node.line, column=node.column)
+ seen = []
+ labels = []
+ branches = sorted(node.branches, key=lambda x: self.context.get_type(x[1].lex).depth, reverse=True)
+ for p, (id, type, expr) in enumerate(branches):
+ labels.append(self.register_label(f'case_label_{p}', line=id.line, column=id.column))
+
+ for t in self.context.subtree(type.lex):
+ if t not in seen:
+ seen.append(t)
+ self.register_instruction(cil.NameNode(branch_type, t.name, line=id.line, column=id.column))
+ self.register_instruction(cil.EqualNode(cond, branch_type, vtype, line=id.line, column=id.column))
+ self.register_instruction(cil.GotoIfNode(cond, labels[-1].label, line=id.line, column=id.column))
+
+ data = self.register_data(f'{node.expression.line, node.expression.column} - '
+ f'RuntimeError: Case statement without a match branch',
+ line=node.expression.line, column=node.expression.column)
+ self.register_instruction(cil.ErrorNode(data, line=node.expression.line, column=node.expression.column))
+
+ for p, label in enumerate(labels):
+ id, type, expr = branches[p]
+ sc = scope.childs[p + 1]
+
+ self.register_instruction(label)
+ var = self.register_local(VariableInfo(id.lex, vtype), line=id.line, column=id.column)
+ self.register_instruction(cil.AssignNode(var, node.expression.ret_expr, line=node.expression.line,
+ column=node.expression.column))
+ self.visit(expr, sc)
+ self.register_instruction(cil.AssignNode(ret, expr.ret_expr, line=expr.line, column=expr.column))
+ self.register_instruction(cil.GotoNode(end_label.label, line=expr.line, column=expr.column))
+
+ self.register_instruction(end_label)
+ node.ret_expr = ret
+
+ @visitor.when(cool.AssignNode)
+ def visit(self, node: cool.AssignNode, scope: Scope):
+ var = self.var_names[node.id.lex]
+ self.visit(node.expression, scope.childs[0])
+ self.register_instruction(cil.AssignNode(var, node.expression.ret_expr,
+ line=node.expression.line, column=node.expression.column))
+ node.ret_expr = var
+
+ @visitor.when(cool.NotNode)
+ def visit(self, node: cool.NotNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+ neg_value = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.expression, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Bool',
+ line=node.expression.line, column=node.expression.column))
+ self.register_instruction(cil.NotNode(neg_value, value,
+ line=node.expression.line, column=node.expression.column))
+ self.register_instruction(cil.ArgNode(neg_value, line=node.expression.line, column=node.expression.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column))
+
+ node.ret_expr = ret
+
+ @visitor.when(cool.LessEqualNode)
+ def visit(self, node: cool.LessEqualNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ left = self.define_internal_local(line=node.line, column=node.column)
+ right = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.left, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int',
+ line=node.left.line, column=node.left.line))
+ self.visit(node.right, scope.childs[1])
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.line))
+ self.register_instruction(cil.LessEqualNode(value, left, right, line=node.line, column=node.column))
+
+ self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column))
+
+ node.ret_expr = ret
+
+ @visitor.when(cool.LessNode)
+ def visit(self, node: cool.LessNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ left = self.define_internal_local(line=node.line, column=node.column)
+ right = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.left, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int',
+ line=node.left.line, column=node.left.line))
+ self.visit(node.right, scope.childs[1])
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.line))
+ self.register_instruction(cil.LessNode(value, left, right, line=node.line, column=node.column))
+
+ self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column))
+
+ node.ret_expr = ret
+
+ @visitor.when(cool.PlusNode)
+ def visit(self, node: cool.PlusNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ left = self.define_internal_local(line=node.line, column=node.column)
+ right = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.left, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int',
+ line=node.left.line, column=node.left.line))
+ self.visit(node.right, scope.childs[1])
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.line))
+ self.register_instruction(cil.PlusNode(value, left, right, line=node.line, column=node.column))
+
+ self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret,line=node.line, column=node.column))
+
+ node.ret_expr = ret
+
+ @visitor.when(cool.MinusNode)
+ def visit(self, node: cool.MinusNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ left = self.define_internal_local(line=node.line, column=node.column)
+ right = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.left, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int',
+ line=node.left.line, column=node.left.line))
+ self.visit(node.right, scope.childs[1])
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.line))
+ self.register_instruction(cil.MinusNode(value, left, right, line=node.line, column=node.column))
+
+ self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column))
+
+ node.ret_expr = ret
+
+ @visitor.when(cool.StarNode)
+ def visit(self, node: cool.StarNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ left = self.define_internal_local(line=node.line, column=node.column)
+ right = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.left, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int',
+ line=node.left.line, column=node.left.line))
+ self.visit(node.right, scope.childs[1])
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.line))
+ self.register_instruction(cil.StarNode(value, left, right, line=node.line, column=node.column))
+
+ self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.DivNode)
+ def visit(self, node: cool.DivNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ left = self.define_internal_local(line=node.line, column=node.column)
+ right = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+ zero = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.left, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int',
+ line=node.left.line, column=node.left.line))
+ self.visit(node.right, scope.childs[1])
+
+ self.register_instruction(cil.ArgNode(0, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), zero, line=node.line, column=node.column))
+ self.register_instruction(cil.GetAttribNode(zero, zero, 'value', 'Int',
+ line=node.right.line, column=node.right.line))
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.line))
+ self.register_instruction(cil.EqualNode(zero, zero, right,
+ line=node.line, column=node.column))
+ self.register_runtime_error(zero, f'{node.right.line, node.right.column} - '
+ f'RuntimeError: Division by zero',
+ line=node.right.line, column=node.right.column)
+
+ self.register_instruction(cil.DivNode(value, left, right, line=node.line, column=node.column))
+
+ self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.IsVoidNode)
+ def visit(self, node: cool.IsVoidNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ answer = self.define_internal_local(line=node.line, column=node.column)
+
+ void = cil.VoidNode()
+ self.visit(node.expression, scope.childs[0])
+ self.register_instruction(cil.EqualNode(answer, node.expression.ret_expr, void,
+ line=node.expression.line, column=node.expression.column))
+
+ self.register_instruction(cil.ArgNode(answer, line=node.expression.line, column=node.expression.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.ComplementNode)
+ def visit(self, node: cool.ComplementNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+ answer = self.define_internal_local(line=node.line, column=node.column)
+
+ self.visit(node.expression, scope.childs[0])
+ self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Int',
+ line=node.expression.line, column=node.expression.column))
+ self.register_instruction(cil.ComplementNode(answer, value,
+ line=node.expression.line, column=node.expression.column))
+
+ self.register_instruction(cil.ArgNode(answer,line=node.expression.line, column=node.expression.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.EqualNode)
+ def visit(self, node: cool.EqualNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ left = self.define_internal_local(line=node.line, column=node.column)
+ right = self.define_internal_local(line=node.line, column=node.column)
+ type_left = self.define_internal_local(line=node.line, column=node.column)
+ type_int = self.define_internal_local(line=node.line, column=node.column)
+ type_string = self.define_internal_local(line=node.line, column=node.column)
+ type_bool = self.define_internal_local(line=node.line, column=node.column)
+ equal = self.define_internal_local(line=node.line, column=node.column)
+ value = self.define_internal_local(line=node.line, column=node.column)
+
+ int_comparisson = self.register_label('int_comparisson', line=node.line, column=node.column)
+ string_comparisson = self.register_label('string_comparisson', line=node.line, column=node.column)
+ bool_comparisson = self.register_label('bool_comparisson', line=node.line, column=node.column)
+ continue_label = self.register_label('continue_label', line=node.line, column=node.column)
+
+ self.visit(node.left, scope.childs[0])
+ self.visit(node.right, scope.childs[1])
+
+ self.register_instruction(cil.TypeOfNode(type_left, node.left.ret_expr,
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.NameNode(type_int, 'Int', line=node.line, column=node.column))
+ self.register_instruction(cil.NameNode(type_string, 'String', line=node.line, column=node.column))
+ self.register_instruction(cil.NameNode(type_bool, 'Bool', line=node.line, column=node.column))
+
+ self.register_instruction(cil.EqualNode(equal, type_left, type_int,
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.GotoIfNode(equal, int_comparisson.label,
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.EqualNode(equal, type_left, type_string,
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.GotoIfNode(equal, string_comparisson.label,
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.EqualNode(equal, type_left, type_bool,
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.GotoIfNode(equal, bool_comparisson.label,
+ line=node.left.line, column=node.left.column))
+
+ self.register_instruction(cil.EqualNode(value, node.left.ret_expr, node.right.ret_expr,
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.GotoNode(continue_label.label,
+ line=node.left.line, column=node.left.column))
+
+ self.register_instruction(int_comparisson)
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.column))
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int',
+ line=node.right.line, column=node.right.column))
+ self.register_instruction(cil.EqualNode(value, left, right, line=node.line, column=node.column))
+ self.register_instruction(cil.GotoNode(continue_label.label, line=node.line, column=node.column))
+
+ self.register_instruction(string_comparisson)
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'String',
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'String',
+ line=node.right.line, column=node.right.column))
+ self.register_instruction(cil.EqualStringNode(value, left, right,
+ line=node.right.line, column=node.right.column))
+ self.register_instruction(cil.GotoNode(continue_label.label, line=node.line, column=node.column))
+
+ self.register_instruction(bool_comparisson)
+ self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Bool',
+ line=node.left.line, column=node.left.column))
+ self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Bool',
+ line=node.right.line, column=node.right.column))
+ self.register_instruction(cil.EqualNode(value, left, right,
+ line=node.right.line, column=node.right.column))
+ self.register_instruction(cil.GotoNode(continue_label.label, line=node.line, column=node.column))
+
+ self.register_instruction(continue_label)
+
+ self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.FunctionCallNode)
+ def visit(self, node: cool.FunctionCallNode, scope: Scope):
+ args = []
+ for arg, child in zip(node.args, scope.childs[1:]):
+ self.visit(arg, child)
+ args.append(cil.ArgNode(arg.ret_expr, line=arg.line, column=arg.column))
+
+ self.visit(node.obj, scope.childs[0])
+
+ void = cil.VoidNode()
+ isvoid = self.define_internal_local(line=node.line, column=node.column)
+ self.register_instruction(cil.EqualNode(isvoid, node.obj.ret_expr, void,
+ line=node.obj.line, column=node.obj.column))
+ self.register_runtime_error(isvoid, f'{node.id.line, node.id.column} - RuntimeError: Dispatch on void',
+ line=node.id.line, column=node.id.column)
+
+ self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column))
+ for arg in args: self.register_instruction(arg)
+ ret = self.define_internal_local(line=node.line, column=node.column)
+ if node.type is not None:
+ stype = node.type.lex
+ self.register_instruction(cil.StaticCallNode(self.types_map[stype].methods[node.id.lex],
+ ret, line=node.id.line, column=node.id.column))
+ else:
+ stype = node.obj.static_type.name
+ self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column))
+ self.register_instruction(cil.DynamicCallNode(stype, self.types_map[stype].methods[node.id.lex],
+ ret, line=node.id.line, column=node.id.column))
+
+
+ node.ret_expr = ret
+
+ @visitor.when(cool.MemberCallNode)
+ def visit(self, node: cool.MemberCallNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+
+ args = []
+ for arg, child in zip(node.args, scope.childs):
+ self.visit(arg, child)
+ args.append(cil.ArgNode(arg.ret_expr, line=arg.line, column=arg.column))
+
+ self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column))
+ for arg in args: self.register_instruction(arg)
+ self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column))
+
+ stype = self.current_type.name
+ self.register_instruction(cil.DynamicCallNode(stype, self.types_map[stype].methods[node.id.lex], ret, line=node.id.line, column=node.id.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.NewNode)
+ def visit(self, node: cool.NewNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+
+ if node.type.lex == 'SELF_TYPE':
+ stype = self.current_type.name
+ else:
+ stype = node.type.lex
+
+ if stype == 'Int':
+ self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column))
+ elif stype == 'Bool':
+ self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column))
+ elif stype == 'String':
+ data = self.emptystring_data
+ variable = self.define_internal_local(line=node.line, column=node.column)
+ self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column))
+ self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name(stype), ret,
+ line=node.type.line, column=node.type.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.IdNode)
+ def visit(self, node: cool.IdNode, scope: Scope):
+ if node.token.lex == 'self':
+ node.ret_expr = self.vself.name
+ else:
+ node.ret_expr = self.var_names[node.token.lex]
+
+ @visitor.when(cool.StringNode)
+ def visit(self, node: cool.StringNode, scope: Scope):
+ try:
+ data = [i for i in self.dotdata if i.value == node.token.lex][0]
+ except IndexError:
+ data = self.register_data(node.token.lex, line=node.token.line, column=node.token.column)
+
+ variable = self.define_internal_local(line=node.line, column=node.column)
+ ret = self.define_internal_local(line=node.line, column=node.column)
+
+ self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column))
+ self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('String'), ret,
+ line=node.line, column=node.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.IntegerNode)
+ def visit(self, node: cool.IntegerNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+
+ self.register_instruction(cil.ArgNode(node.token.lex, line=node.token.line, column=node.token.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column))
+ node.ret_expr = ret
+
+ @visitor.when(cool.BoolNode)
+ def visit(self, node: cool.BoolNode, scope: Scope):
+ ret = self.define_internal_local(line=node.line, column=node.column)
+
+ self.register_instruction(cil.ArgNode(1 if node.token.lex else 0, line=node.token.line, column=node.token.column))
+ self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column))
+ node.ret_expr = ret
diff --git a/src/core/cool/CoolAst.py b/src/core/cool/CoolAst.py
new file mode 100644
index 000000000..4efb93764
--- /dev/null
+++ b/src/core/cool/CoolAst.py
@@ -0,0 +1,210 @@
+# Clases necesarias para representar el AST del programa COOL
+class Node:
+ pass
+
+# Raiz del AST
+class ProgramNode(Node):
+ def __init__(self, declarations):
+ self.declarations = declarations
+ self.line = declarations[0].line
+ self.column = declarations[0].column
+
+
+class DeclarationNode(Node):
+ pass
+
+
+class ClassDeclarationNode(DeclarationNode):
+ def __init__(self, classx, idx, features, parent=None):
+ self.id = idx
+ self.parent = parent
+ self.features = features
+ self.line = classx.line
+ self.column = classx.column
+
+
+class AttrDeclarationNode(DeclarationNode):
+ def __init__(self, idx, typex, expression=None):
+ self.id = idx
+ self.type = typex
+ self.expression = expression
+ self.line = idx.line
+ self.column = idx.column
+
+
+class FuncDeclarationNode(DeclarationNode):
+ def __init__(self, idx, params, return_type, body):
+ self.id = idx
+ self.params = params
+ self.type = return_type
+ self.body = body
+ self.line = idx.line
+ self.column = idx.column
+
+
+class ExpressionNode(Node):
+ pass
+
+
+class IfThenElseNode(ExpressionNode):
+ def __init__(self, ifx, condition, if_body, else_body):
+ self.condition = condition
+ self.if_body = if_body
+ self.else_body = else_body
+ self.line = ifx.line
+ self.column = ifx.column
+
+
+class WhileLoopNode(ExpressionNode):
+ def __init__(self, whilex, condition, body):
+ self.condition = condition
+ self.body = body
+ self.line = whilex.line
+ self.column = whilex.column
+
+
+class BlockNode(ExpressionNode):
+ def __init__(self, brace, expressions):
+ self.expressions = expressions
+ self.line = brace.line
+ self.column = brace.column
+
+
+class LetInNode(ExpressionNode):
+ def __init__(self, let, let_body, in_body):
+ self.let_body = let_body
+ self.in_body = in_body
+ self.line = let.line
+ self.column = let.column
+
+
+class CaseOfNode(ExpressionNode):
+ def __init__(self, case, expression, branches):
+ self.expression = expression
+ self.branches = branches
+ self.line = case.line
+ self.column = case.column
+
+
+class AssignNode(ExpressionNode):
+ def __init__(self, idx, expression):
+ self.id = idx
+ self.expression = expression
+ self.line = idx.line
+ self.column = idx.column
+
+
+class UnaryNode(ExpressionNode):
+ def __init__(self, expression):
+ self.expression = expression
+ self.line = expression.line
+ self.column = expression.column
+
+
+class NotNode(UnaryNode):
+ def __init__(self, notx, expression):
+ super().__init__(expression)
+ self.line = notx.line
+ self.column = notx.column
+
+
+class BinaryNode(ExpressionNode):
+ def __init__(self, left, operator, right):
+ self.left = left
+ self.right = right
+ self.line = operator.line
+ self.column = operator.column
+
+
+class LessEqualNode(BinaryNode):
+ pass
+
+
+class LessNode(BinaryNode):
+ pass
+
+
+class EqualNode(BinaryNode):
+ pass
+
+
+class ArithmeticNode(BinaryNode):
+ pass
+
+
+class PlusNode(ArithmeticNode):
+ pass
+
+
+class MinusNode(ArithmeticNode):
+ pass
+
+
+class StarNode(ArithmeticNode):
+ pass
+
+
+class DivNode(ArithmeticNode):
+ pass
+
+
+class IsVoidNode(UnaryNode):
+ def __init__(self, isvoid, expression):
+ super().__init__(expression)
+ self.line = isvoid.line
+ self.column = isvoid.column
+
+
+class ComplementNode(UnaryNode):
+ def __init__(self, complement, expression):
+ super().__init__(expression)
+ self.line = complement.line
+ self.column = complement.column
+
+
+class FunctionCallNode(ExpressionNode):
+ def __init__(self, obj, idx, args, typex=None):
+ self.obj = obj
+ self.id = idx
+ self.args = args
+ self.type = typex
+ self.line = obj.line
+ self.column = obj.column
+
+
+class MemberCallNode(ExpressionNode):
+ def __init__(self, idx, args):
+ self.id = idx
+ self.args = args
+ self.line = idx.line
+ self.column = idx.column
+
+
+class NewNode(ExpressionNode):
+ def __init__(self, new, typex):
+ self.type = typex
+ self.line = new.line
+ self.column = new.column
+
+
+class AtomicNode(ExpressionNode):
+ def __init__(self, token):
+ self.token = token
+ self.line = token.line
+ self.column = token.column
+
+
+class IntegerNode(AtomicNode):
+ pass
+
+
+class IdNode(AtomicNode):
+ pass
+
+
+class StringNode(AtomicNode):
+ pass
+
+
+class BoolNode(AtomicNode):
+ pass
diff --git a/src/core/cool/CoolAstFormatter.py b/src/core/cool/CoolAstFormatter.py
new file mode 100644
index 000000000..8d0d2f4bf
--- /dev/null
+++ b/src/core/cool/CoolAstFormatter.py
@@ -0,0 +1,116 @@
+from core.tools import visitor
+from core.cool.CoolAst import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode
+from core.cool.CoolAst import IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode
+from core.cool.CoolAst import AssignNode, UnaryNode, BinaryNode
+from core.cool.CoolAst import FunctionCallNode, MemberCallNode, NewNode, AtomicNode
+
+# Visitor para imprimir el AST
+
+class FormatVisitor:
+ @visitor.on('node')
+ def visit(self, node, tabs):
+ pass
+
+ @visitor.when(ProgramNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\__ProgramNode [ ... ]'
+ statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations)
+ return f'{ans}\n{statements}'
+
+ @visitor.when(ClassDeclarationNode)
+ def visit(self, node, tabs=0):
+ parent = '' if node.parent is None else f"inherits {node.parent.lex}"
+ ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id.lex} {parent} {{ ... }}'
+ features = '\n'.join(self.visit(child, tabs + 1) for child in node.features)
+ return f'{ans}\n{features}'
+
+ @visitor.when(AttrDeclarationNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id.lex}: {node.type.lex}' + (' <- ' if node.expression else '') + ';'
+ expr = self.visit(node.expression, tabs + 1) if node.expression else None
+ return f'{ans}' + (f'\n{expr}' if expr else '')
+
+ @visitor.when(FuncDeclarationNode)
+ def visit(self, node, tabs=0):
+ params = ', '.join(': '.join(tok.lex for tok in param) for param in node.params)
+ ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id.lex}({params}): {node.type.lex} {{ }}'
+ body = self.visit(node.body, tabs + 1)
+ return f'{ans}\n{body}'
+
+ @visitor.when(IfThenElseNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\_IfThenElseNode: if then else fi'
+ cond = self.visit(node.condition, tabs + 1)
+ if_body = self.visit(node.if_body, tabs + 1)
+ else_body = self.visit(node.else_body, tabs + 1)
+ return f'{ans}\n{cond}\n{if_body}\n{else_body}'
+
+ @visitor.when(WhileLoopNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\_WhileNode: while loop pool'
+ cond = self.visit(node.condition, tabs + 1)
+ body = self.visit(node.body, tabs + 1)
+ return f'{ans}\n{cond}\n{body}'
+
+ @visitor.when(BlockNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\_BlockNode: {{ ; ... ; }}'
+ expressions = '\n'.join(self.visit(expr, tabs + 1) for expr in node.expressions)
+ return f'{ans}\n{expressions}'
+
+ @visitor.when(LetInNode)
+ def visit(self, node, tabs=0):
+ let_body = ', '.join(f'{idx.lex}: {typex.lex}' + (' <- ' if expr else '') for idx, typex, expr in node.let_body)
+ ans = '\t' * tabs + f'\\_LetInNode: let {let_body} in '
+ lets = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.let_body if expr)
+ body = self.visit(node.in_body, tabs + 1)
+ return f'{ans}\n{lets}\n{body}'
+
+ @visitor.when(CaseOfNode)
+ def visit(self, node, tabs=0):
+ case_body = ' '.join(f'{idx.lex}: {typex.lex} => ;' for idx, typex, expr in node.branches)
+ ans = '\t' * tabs + f'\\_CaseOfNode: case of {case_body} esac'
+ expression = self.visit(node.expression, tabs + 1)
+ body = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.branches)
+ return f'{ans}\n{expression}\n{body}'
+
+ @visitor.when(AssignNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\_AssingNode: {node.id.lex} <- '
+ expr = self.visit(node.expression, tabs + 1)
+ return f'{ans}\n{expr}'
+
+ @visitor.when(UnaryNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\__{node.__class__.__name__} '
+ expression = self.visit(node.expression, tabs + 1)
+ return f'{ans}\n{expression}'
+
+ @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(FunctionCallNode)
+ def visit(self, node, tabs=0):
+ obj = self.visit(node.obj, tabs + 1)
+ typex = f'@{node.type.lex}' if node.type else ''
+ ans = '\t' * tabs + f'\\__FunctionCallNode: {typex}.{node.id.lex}(, ..., )'
+ args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args)
+ return f'{ans}\n{obj}\n{args}'
+
+ @visitor.when(MemberCallNode)
+ def visit(self, node, tabs=0):
+ ans = '\t' * tabs + f'\\__MemberCallNode: {node.id.lex}(, ..., )'
+ args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args)
+ return f'{ans}\n{args}'
+
+ @visitor.when(NewNode)
+ def visit(self, node, tabs=0):
+ return '\t' * tabs + f'\\__ NewNode: new {node.type.lex}'
+
+ @visitor.when(AtomicNode)
+ def visit(self, node, tabs=0):
+ return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.token.lex}'
\ No newline at end of file
diff --git a/src/core/lexer/Lexer.py b/src/core/lexer/Lexer.py
new file mode 100644
index 000000000..48ddbbf27
--- /dev/null
+++ b/src/core/lexer/Lexer.py
@@ -0,0 +1,272 @@
+import ply.lex as lex
+from core.tools.Utils import Token
+from core.parser.Parser import CoolGrammar
+from core.tools.Errors import LexicographicError
+
+class Lexer:
+ states = (
+ ('comment', 'exclusive'),
+ ('string', 'exclusive')
+ )
+
+ # Palabras reservadas del lenguaje COOL
+ reserved = {
+ 'class': 'CLASS',
+ 'inherits': 'INHERITS',
+ 'function': 'FUNCTION',
+ 'if': 'IF',
+ 'then': 'THEN',
+ 'else': 'ELSE',
+ 'fi': 'FI',
+ 'while': 'WHILE',
+ 'loop': 'LOOP',
+ 'pool': 'POOL',
+ 'let': 'LET',
+ 'in': 'IN',
+ 'case': 'CASE',
+ 'of': 'OF',
+ 'esac': 'ESAC',
+ 'new': 'NEW',
+ 'isvoid': 'ISVOID'
+ }
+
+ t_ignore = ' \f\r\t\v'
+ t_comment_ignore = ''
+ t_string_ignore = ''
+
+ tokens = [
+ # Identifiers
+ 'TYPE', 'ID',
+ # Primitive data types
+ 'INTEGER', 'STRING', 'BOOL',
+ # Special keywords
+ 'ACTION',
+ # Operators
+ 'ASSIGN', 'LESS', 'LESSEQUAL', 'EQUAL', 'INT_COMPLEMENT', 'NOT',
+ # Literals
+ 'PLUS', 'MINUS', 'STAR', 'DIVIDE', 'COLON', 'SEMICOLON',
+ 'OPAR', 'CPAR', 'OCUR', 'CCUR', 'AT', 'DOT', 'COMMA',
+ ] + list(reserved.values())
+
+ tokens_dict = {}
+ for tok in tokens:
+ try:
+ tokens_dict[tok] = CoolGrammar[tok.lower()].Name
+ except:
+ pass
+
+ tokens_dict['ACTION'] = CoolGrammar['=>'].Name
+ tokens_dict['ASSIGN'] = CoolGrammar['<-'].Name
+ tokens_dict['LESS'] = CoolGrammar['<'].Name
+ tokens_dict['LESSEQUAL'] = CoolGrammar['<='].Name
+ tokens_dict['EQUAL'] = CoolGrammar['='].Name
+ tokens_dict['INT_COMPLEMENT'] = CoolGrammar['~'].Name
+
+ tokens_dict['PLUS'] = CoolGrammar['+'].Name
+ tokens_dict['MINUS'] = CoolGrammar['-'].Name
+ tokens_dict['STAR'] = CoolGrammar['*'].Name
+ tokens_dict['DIVIDE'] = CoolGrammar['/'].Name
+ tokens_dict['COLON'] = CoolGrammar[':'].Name
+ tokens_dict['SEMICOLON'] = CoolGrammar[';'].Name
+ tokens_dict['OPAR'] = CoolGrammar['('].Name
+ tokens_dict['CPAR'] = CoolGrammar[')'].Name
+ tokens_dict['OCUR'] = CoolGrammar['{'].Name
+ tokens_dict['CCUR'] = CoolGrammar['}'].Name
+ tokens_dict['AT'] = CoolGrammar['@'].Name
+ tokens_dict['DOT'] = CoolGrammar['.'].Name
+ tokens_dict['COMMA'] = CoolGrammar[','].Name
+
+
+ def __init__(self):
+ self.lexer = lex.lex(module=self)
+ self.comment_level = 0
+ self.code = ''
+ self.current_string = ''
+ self.errors = []
+
+
+ # Expresiones regulares
+
+ def t_INTEGER(self, t):
+ r'[0-9]+'
+ t.value = int(t.value)
+ return t
+
+ def t_BOOL(self, t):
+ r't[rR][uU][eE]|f[aA][lL][sS][eE]'
+ t.value = True if t.value == 'true' else False
+ return t
+
+
+ # Other tokens with precedence before TYPE and ID
+
+ def t_NOT(self, t):
+ r'[nN][oO][tT]'
+ return t
+
+ # Identifiers
+
+ def t_TYPE(self, t):
+ r'[A-Z][A-Za-z0-9_]*'
+
+ try:
+ t.type = self.reserved[t.value.lower()]
+ except KeyError:
+ pass
+
+ return t
+
+ def t_ID(self, t):
+ r'[a-z][A-Za-z0-9_]*'
+
+ try:
+ t.type = self.reserved[t.value.lower()]
+ except KeyError:
+ pass
+
+ return t
+
+
+ t_ASSIGN = r'<-'
+ t_LESS = r'<'
+ t_LESSEQUAL = r'<='
+ t_EQUAL = r'='
+ t_INT_COMPLEMENT = r'~'
+ t_ACTION = r'=>'
+
+ t_PLUS = r'\+'
+ t_MINUS = r'-'
+ t_STAR = r'\*'
+ t_DIVIDE = r'/'
+ t_COLON = r':'
+ t_SEMICOLON = r';'
+ t_OPAR = r'\('
+ t_CPAR = r'\)'
+ t_OCUR = r'{'
+ t_CCUR = r'}'
+ t_AT = r'@'
+ t_DOT = r'\.'
+ t_COMMA = r','
+
+ ####################
+ ##### COMMENTS #####
+ ####################
+ def t_LINECOMMENT(self, t):
+ r'--.*'
+ pass
+
+ def t_COMMENTBEGIN(self, t):
+ r'\(\*'
+ self.comment_level += 1
+ t.lexer.begin('comment')
+
+ def t_comment_COMMENTBEGIN(self, t):
+ r'\(\*'
+ self.comment_level += 1
+
+ def t_comment_COMMENTEND(self, t):
+ r'\*\)'
+ self.comment_level -= 1
+ if self.comment_level == 0:
+ t.lexer.begin('INITIAL')
+
+ def t_comment_eof(self, t):
+ self.errors.append(LexicographicError(t.lineno,
+ self.find_column(t), 'EOF in comment'))
+ self.lexer.begin('INITIAL')
+
+ def t_comment_error(self, t):
+ t.lexer.skip(1)
+
+ ############################
+ ##### STRING CONSTANTS #####
+ ############################
+
+ def t_STRINGBEGIN(self, t):
+ r'"'
+ self.current_string = ''
+ self.lexer.begin('string')
+
+ def t_string_NULL(self, t):
+ r'\0'
+ self.errors.append(LexicographicError(t.lineno,
+ self.find_column(t), 'Null caracter in string'))
+ self.lexer.begin('INITIAL')
+
+ def t_string_NEWLINE(self, t):
+ r'\\\n'
+ self.current_string += '\n'
+ t.lexer.lineno += 1
+
+ def t_string_INVALID_NEWLINE(self, t):
+ r'\n'
+ t.lexer.lineno += 1
+ self.errors.append(LexicographicError(t.lineno,
+ self.find_column(t), 'Unterminated string constant'))
+ self.lexer.begin('INITIAL')
+
+ def t_string_SCAPED_SPECIAL_CHARACTER(self, t):
+ r'\\(b|t|f)'
+ self.current_string += t.value
+
+ def t_string_SCAPED_CHARACTER(self, t):
+ r'\\.'
+ self.current_string += '\\' + t.value[1]
+
+ def t_string_eof(self, t):
+ self.errors.append(LexicographicError(t.lineno,
+ self.find_column(t), 'EOF in string constant'))
+ self.lexer.begin('INITIAL')
+
+ def t_string_STRINGEND(self, t):
+ r'"'
+ t.value = self.current_string
+ t.type = 'STRING'
+ self.lexer.begin('INITIAL')
+ return t
+
+ def t_string_CHARACTER(self, t):
+ r'.'
+ self.current_string += t.value
+
+ def t_string_error(self, t):
+ return t
+
+ ###########################
+ ###### SPECIAL RULES ######
+ ###########################
+
+ def t_ANY_newline(self, t):
+ r'\n+'
+ t.lexer.lineno += len(t.value)
+
+ def find_column(self, token):
+ line_start = self.code.rfind('\n', 0, token.lexpos) + 1
+ return (token.lexpos - line_start) + 1 \
+ + 3 * len([i for i in self.code[line_start:token.lexpos] if i == '\t'])
+
+ def t_error(self, t):
+ self.errors.append(LexicographicError(t.lineno,
+ self.find_column(t), f'ERROR "{t.value[0]}"'))
+ t.lexer.skip(1)
+
+
+ '''
+ Dado un string retorna el arreglo de tokens resultante de analizar dicho string
+ '''
+ def tokenize(self, code):
+ tokens = []
+ self.code = code
+
+ self.lexer.input(code)
+ while True:
+ token = self.lexer.token()
+ if token is None:
+ break
+
+ tokens.append(Token(token.value, self.tokens_dict[token.type],
+ token.lineno, self.find_column(token)))
+
+ tokens.append(Token('$', CoolGrammar.EOF.Name))
+
+ return tokens
diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py
new file mode 100644
index 000000000..4e8271284
--- /dev/null
+++ b/src/core/mips/CilToMipsVisitor.py
@@ -0,0 +1,1004 @@
+import core.cil.CilAst as cil
+import core.mips.MipsAst as mips
+from ..tools import visitor
+
+
+class CILToMIPSVisitor:
+ def __init__(self):
+ self.type_label_count = 0
+ self.data_label_count = 0
+ self.code_label_count = 0
+
+ self._types_section = {}
+ self._data_section = {}
+ self._functions_section = {}
+
+ self._current_function = None
+ self._function_names = {}
+ self._pushed_args = 0
+ self._labels = {}
+
+ self.registers = mips.REGISTERS
+
+ def generate_type_label(self):
+ self.type_label_count += 1
+ return f'type_{self.type_label_count}'
+
+ def generate_data_label(self):
+ self.data_label_count += 1
+ return f'data_{self.data_label_count}'
+
+ def generate_code_label(self):
+ self.code_label_count += 1
+ return f'label_{self.code_label_count}'
+
+ def enter_function(self, name, function):
+ self._functions_section[name] = function
+ self._current_function = function
+ self._labels = {}
+
+ def exit_function(self):
+ self._current_function = None
+
+ def get_free_register(self):
+ r, rest = self.registers
+ self.registers = rest
+ return r
+
+ def free_register(self, reg):
+ self.registers.append(reg)
+
+ def register_label(self, old_label, new_label):
+ self._labels[old_label] = new_label
+
+ def get_label(self, label):
+ return self._labels[label]
+
+ def get_var_location(self, node):
+ if isinstance(node, cil.AttributeNode):
+ return mips.RegisterRelativeLocation(mips.SP_REG, 0)
+ if isinstance(node, str):
+ return self._current_function.get_var_location(node)
+ return self._current_function.get_var_location(node.name)
+
+ @visitor.on('node')
+ def collect_func_names(self, node):
+ pass
+
+ @visitor.when(cil.ProgramNode)
+ def collect_func_names(self, node):
+ for f in node.dotcode:
+ self.collect_func_names(f)
+
+ @visitor.when(cil.FunctionNode)
+ def collect_func_names(self, node):
+ if node.name == "entry":
+ self._function_names[node.name] = 'main'
+ else:
+ self._function_names[node.name] = node.name
+
+ @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", "", line=node.line, column=node.column)
+
+ for i in node.dottypes:
+ self.visit(i)
+ for i in node.dotdata:
+ self.visit(i)
+ for i in node.dotcode:
+ self.visit(i)
+
+ return mips.ProgramNode([i for i in self._data_section.values()],
+ [i for i in self._types_section.values()],
+ [i for i in self._functions_section.values()],
+ line=node.line, column=node.column)
+
+ @visitor.when(cil.TypeNode)
+ def visit(self, node):
+ name_label = self.generate_data_label()
+ self._data_section[node.name] = mips.StringConst(name_label, node.name, line=node.line, column=node.column)
+
+ type_label = self.generate_type_label()
+ methods = [self._function_names[key] for key in node.methods.values()]
+ defaults = []
+ if node.name == "String":
+ defaults = [('value', 'default_str'), ('len', 'type_4_proto')]
+
+ type = mips.MIPSType(type_label, name_label, list(node.attributes.keys()), methods,
+ len(self._types_section), defaults)
+ self._types_section[node.name] = type
+
+ @visitor.when(cil.DataNode)
+ def visit(self, node):
+ label = self.generate_data_label()
+ self._data_section[node.name] = mips.StringConst(label, node.value, line=node.line, column=node.column)
+
+ @visitor.when(cil.FunctionNode)
+ def visit(self, node):
+ label = self._function_names[node.name]
+ params = [param.name for param in node.params]
+ localvars = [local.name for local in node.localvars]
+ size_for_locals = len(localvars) * mips.DOUBLE_WORD
+
+ new_func = mips.FunctionNode(label, params, localvars, line=node.line, column=node.column)
+ self.enter_function(node.name, new_func)
+ self._current_function = new_func
+ self._labels = {}
+
+ for instruction in node.instructions:
+ if isinstance(instruction, cil.LabelNode):
+ mips_label = self.generate_code_label()
+ self.register_label(instruction.label, mips_label)
+
+ instructions = []
+ instructions.extend(mips.push_register(mips.RA_REG, node.line, node.column))
+ instructions.extend(mips.push_register(mips.FP_REG, line=node.line, column=node.column))
+ instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 8, line=node.line, column=node.column))
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals,
+ line=node.line, column=node.column))
+
+ for i in node.instructions:
+ instructions.extend(self.visit(i))
+
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, size_for_locals,
+ line=node.line, column=node.column))
+ instructions.extend(mips.pop_register(mips.FP_REG, line=node.line, column=node.column))
+ instructions.extend(mips.pop_register(mips.RA_REG, node.line, node.column))
+
+ if self._current_function.label != 'main':
+ instructions.append(mips.JumpRegisterNode(mips.RA_REG, line=node.line, column=node.column))
+ else:
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10, line=node.line, column=node.column))
+ instructions.append(mips.SyscallNode())
+
+ new_func.instructions = instructions
+ self._current_function = None
+
+ @visitor.when(cil.AssignNode)
+ def visit(self, node):
+ instructions = []
+
+ if isinstance(node.source, cil.VoidNode):
+ reg1 = mips.ZERO_REG
+ elif isinstance(node.source, int):
+ reg1 = mips.REGISTERS[0]
+ instructions.append(mips.LoadInmediateNode(reg1, int(node.source), line=node.line, column=node.column))
+ else:
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.source))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source),
+ line=node.line, column=node.column))
+ if isinstance(node.source, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ if isinstance(node.dest, cil.AttributeNode):
+ self_var = self._current_function.params[0]
+ reg2 = mips.REGISTERS[1]
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(self_var), node.line, node.column))
+
+ index = self._types_section[node.dest.type].attributes.index(node.dest.name) + 3
+ instructions.append(mips.AdditionInmediateNode(reg2, reg2, index * 4,
+ line=node.line, column=node.column))
+ instructions.append(mips.StoreWordNode(reg1, mips.RegisterRelativeLocation(reg2, 0),
+ line=node.line, column=node.column))
+ else:
+ instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+
+ return instructions
+
+ @visitor.when(cil.PlusNode)
+ def visit(self, node):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ if isinstance(node.left, int):
+ instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ if isinstance(node.right, int):
+ instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg3 = mips.REGISTERS[0]
+ instructions.append(mips.AdditionNode(reg3, reg1, reg2, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.MinusNode)
+ def visit(self, node):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ if isinstance(node.left, int):
+ instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ if isinstance(node.right, int):
+ instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg3 = mips.REGISTERS[0]
+ instructions.append(mips.SubstractionNode(reg3, reg1, reg2, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.StarNode)
+ def visit(self, node):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ if isinstance(node.left, int):
+ instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ if isinstance(node.right, int):
+ instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg3 = mips.REGISTERS[0]
+ instructions.append(mips.MultiplyNode(reg3, reg1, reg2, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.DivNode)
+ def visit(self, node):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ if isinstance(node.left, int):
+ instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ if isinstance(node.right, int):
+ instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ instructions.append(mips.DivideNode(reg1, reg2, line=node.line, column=node.column))
+ instructions.append(mips.MoveLowNode(reg1, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.AllocateNode)
+ def visit(self, node):
+ instructions = []
+
+ if isinstance(node.type, int):
+ type = node.type
+ else:
+ type = self._types_section[node.type].index
+
+ reg1 = mips.REGISTERS[0]
+ reg2 = mips.REGISTERS[1]
+
+ instructions.append(mips.LoadInmediateNode(reg1, type, line=node.line, column=node.column))
+ instructions.extend(mips.create_object(reg1, reg2, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.TypeOfNode)
+ def visit(self, node):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ reg2 = mips.REGISTERS[1]
+
+ instructions.extend(self.visit(node.obj))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj),
+ line=node.line, column=node.column))
+ if isinstance(node.obj, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0),
+ line=node.line, column=node.column))
+ instructions.append(mips.ShiftLeftNode(reg2, reg2, 2, node.line, node.column))
+ instructions.append(mips.LoadAddressNode(reg1, mips.TYPES_LABEL, node.line, node.column))
+ instructions.append(mips.AdditionNode(reg1, reg1, reg2, node.line, node.column))
+ instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0),
+ line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.StaticCallNode)
+ def visit(self, node):
+ instructions = []
+ func_name = self._function_names[node.function]
+ instructions.append(mips.JalNode(func_name, line=node.line, column=node.column))
+
+ if self._pushed_args > 0:
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG,
+ self._pushed_args * mips.DOUBLE_WORD,
+ line=node.line, column=node.column))
+ self._pushed_args = 0
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.DynamicCallNode)
+ def visit(self, node):
+ instructions = []
+
+ type = self._types_section[node.type]
+ method = type.methods.index(self._function_names[node.method])
+
+ reg1 = mips.REGISTERS[0]
+ instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(mips.SP_REG, 0),
+ line=node.line, column=node.column))
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, node.line, node.column))
+ self._pushed_args -= 1
+
+ instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8),
+ line=node.line, column=node.column))
+ instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, method * 4),
+ line=node.line, column=node.column))
+ instructions.append(mips.JalrNode(mips.RA_REG, reg1, line=node.line, column=node.column))
+
+ if self._pushed_args > 0:
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG,
+ self._pushed_args * mips.DOUBLE_WORD,
+ line=node.line, column=node.column))
+ self._pushed_args = 0
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.ArgNode)
+ def visit(self, node):
+ self._pushed_args += 1
+ instructions = []
+ reg = mips.REGISTERS[0]
+ if isinstance(node.name, int):
+ instructions.append(mips.LoadInmediateNode(reg, node.name,
+ line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.name))
+ instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name),
+ line=node.line, column=node.column))
+ if isinstance(node.name, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.extend(mips.push_register(reg, line=node.line, column=node.column))
+ return instructions
+
+ @visitor.when(cil.ReturnNode)
+ def visit(self, node):
+ instructions = []
+
+ if node.value is None or isinstance(node.value, cil.VoidNode):
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0, line=node.value.line, column=node.value.column))
+ elif isinstance(node.value, int):
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value,
+ line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.value))
+ instructions.append(mips.LoadWordNode(mips.V0_REG, self.get_var_location(node.value),
+ line=node.line, column=node.column))
+ if isinstance(node.value, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ return instructions
+
+ @visitor.when(cil.LoadNode)
+ def visit(self, node):
+ instructions = []
+
+ reg = mips.REGISTERS[0]
+ instructions.append(mips.LoadAddressNode(reg, self._data_section[node.msg.name].label, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest)
+ , line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.PrintStringNode)
+ def visit(self, node: cil.PrintStringNode):
+ instructions = []
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.str_addr))
+ instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.str_addr),
+ line=node.line, column=node.column))
+ if isinstance(node.str_addr, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.SyscallNode())
+ return instructions
+
+ @visitor.when(cil.PrintIntNode)
+ def visit(self, node: cil.PrintIntNode):
+ instructions = []
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.value))
+ instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value),
+ line=node.line, column=node.column))
+ if isinstance(node.value, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.SyscallNode())
+ return instructions
+
+ @visitor.when(cil.ExitNode)
+ def visit(self, node: cil.ExitNode):
+ instructions = []
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10, line=node.line, column=node.column))
+ instructions.append(mips.SyscallNode())
+ return instructions
+
+ @visitor.when(cil.CopyNode)
+ def visit(self, node):
+ instructions = []
+
+ reg = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.value))
+ instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.value),
+ line=node.line, column=node.column))
+ if isinstance(node.value, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(reg, 4),
+ line=node.line, column=node.column))
+ instructions.append(mips.ShiftLeftNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2,
+ line=node.line, column=node.column))
+ instructions.append(mips.JalNode("malloc", line=node.line, column=node.column))
+ instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], mips.ARG_REGISTERS[0]
+ , line=node.line, column=node.column))
+ instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg, line=node.line, column=node.column))
+ instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG, line=node.line, column=node.column))
+ instructions.append(mips.JalNode("copy", line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest)
+ , line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.GetAttribNode)
+ def visit(self, node: cil.GetAttribNode):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ reg2 = mips.REGISTERS[1]
+ instructions.extend(self.visit(node.obj))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj),
+ line=node.line, column=node.column))
+ if isinstance(node.obj, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ tp = self._types_section[node.computed_type]
+ offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD
+ instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset),
+ line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.ErrorNode)
+ def visit(self, node: cil.ErrorNode):
+ instructions = []
+
+ mips_label = self._data_section[node.data.name].label
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4, line=node.line, column=node.column))
+ instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], mips_label, line=node.line, column=node.column))
+ instructions.append(mips.SyscallNode())
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10, line=node.line, column=node.column))
+ instructions.append(mips.SyscallNode())
+
+ return instructions
+
+ @visitor.when(cil.ReadIntNode)
+ def visit(self, node: cil.ReadIntNode):
+ instructions = []
+
+ instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5, line=node.line, column=node.column))
+ instructions.append(mips.SyscallNode())
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.ReadStringNode)
+ def visit(self, node: cil.ReadStringNode):
+ instructions = []
+
+ instructions.append(mips.JalNode('read_string', node.line, node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), node.line, node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ return instructions
+
+ @visitor.when(cil.SetAttribNode)
+ def visit(self, node: cil.SetAttribNode):
+ instructions = []
+
+ tp = self._types_section[node.computed_type]
+ offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD
+
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.obj))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj),
+ line=node.line, column=node.column))
+ if isinstance(node.obj, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ if type(node.value) == int:
+ instructions.append(mips.LoadInmediateNode(reg2, node.value,
+ line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.value))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.value),
+ line=node.line, column=node.column))
+ if isinstance(node.value, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset),
+ line=node.line, column=node.column))
+ return instructions
+
+ @visitor.when(cil.LessNode)
+ def visit(self, node: cil.LessNode):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.LessNode(reg2, reg1, reg2, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.GotoIfNode)
+ def visit(self, node: cil.GotoIfNode):
+ instructions = []
+ mips_label = self.get_label(node.label)
+
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.condition))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.condition),
+ line=node.line, column=node.column))
+ if isinstance(node.condition, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.BranchOnNotEqualNode(reg1, mips.ZERO_REG, mips_label,
+ line=node.line, column=node.column))
+
+ return instructions
+
+ @visitor.when(cil.GotoNode)
+ def visit(self, node: cil.GotoNode):
+ mips_label = self.get_label(node.label)
+ return [mips.JumpNode(mips_label, line=node.line, column=node.column)]
+
+ @visitor.when(cil.LabelNode)
+ def visit(self, node: cil.LabelNode):
+ mips_label = self.get_label(node.label)
+ return [mips.LabelNode(mips_label, line=node.line, column=node.column)]
+
+ @visitor.when(cil.SubstringNode)
+ def visit(self, node: cil.SubstringNode):
+ instructions = []
+
+ reg1 = mips.ARG_REGISTERS[0]
+ instructions.extend(self.visit(node.str_value))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.str_value),
+ line=node.line, column=node.column))
+ if isinstance(node.str_value, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg1 = mips.ARG_REGISTERS[1]
+ if type(node.index) == int:
+ instructions.append(mips.LoadInmediateNode(reg1, node.index, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.index))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.index),
+ line=node.line, column=node.column))
+ if isinstance(node.index, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg1 = mips.ARG_REGISTERS[2]
+ if type(node.index) == int:
+ instructions.append(mips.LoadInmediateNode(reg1, node.length, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.length))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.length),
+ line=node.line, column=node.column))
+ if isinstance(node.length, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ instructions.append(mips.JalNode("substr", line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ return instructions
+
+ @visitor.when(cil.ConcatNode)
+ def visit(self, node: cil.ConcatNode):
+ instructions = []
+
+ reg = mips.ARG_REGISTERS[0]
+ instructions.extend(self.visit(node.prefix))
+ instructions.append(
+ mips.LoadWordNode(reg, self.get_var_location(node.prefix), line=node.line, column=node.column))
+ if isinstance(node.prefix, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ reg = mips.ARG_REGISTERS[1]
+ instructions.extend(self.visit(node.suffix))
+ instructions.append(
+ mips.LoadWordNode(reg, self.get_var_location(node.suffix), line=node.line, column=node.column))
+ if isinstance(node.suffix, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ reg = mips.ARG_REGISTERS[2]
+ instructions.extend(self.visit(node.length))
+ instructions.append(
+ mips.LoadWordNode(reg, self.get_var_location(node.length), line=node.line, column=node.column))
+ if isinstance(node.length, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.JalNode("concat", line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ return instructions
+
+ @visitor.when(cil.LengthNode)
+ def visit(self, node: cil.LengthNode):
+ instructions = []
+
+ reg = mips.ARG_REGISTERS[0]
+ instructions.extend(self.visit(node.source))
+ instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source),
+ line=node.line, column=node.column))
+ if isinstance(node.source, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ instructions.append(mips.JalNode("length", line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.EqualNode)
+ def visit(self, node: cil.EqualNode):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ if type(node.left) == cil.VoidNode:
+ instructions.append(mips.LoadInmediateNode(reg1, 0, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ if type(node.right) == cil.VoidNode:
+ instructions.append(mips.LoadInmediateNode(reg2, 0, line=node.line, column=node.column))
+ else:
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ instructions.append(mips.EqualNode(reg1, reg1, reg2, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ return instructions
+
+ @visitor.when(cil.NameNode)
+ def visit(self, node: cil.NameNode):
+ instructions = []
+
+ reg = mips.REGISTERS[0]
+ instructions.append(mips.LoadAddressNode(reg, mips.TYPES_LABEL, line=node.line, column=node.column))
+
+ tp_number = self._types_section[node.value].index
+ instructions.append(mips.AdditionInmediateNode(reg, reg, tp_number * 4, line=node.line, column=node.column))
+ instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0),
+ line=node.line, column=node.column))
+
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ return instructions
+
+ @visitor.when(cil.EqualStringNode)
+ def visit(self, node: cil.EqualStringNode):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg1, line=node.line, column=node.column))
+
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg1, line=node.line, column=node.column))
+
+ instructions.append(mips.JalNode("equal_str", line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.ComplementNode)
+ def visit(self, node: cil.ComplementNode):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.value))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value),
+ line=node.line, column=node.column))
+ if isinstance(node.value, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ instructions.append(mips.ComplementNode(reg1, reg1, line=node.line, column=node.column))
+ instructions.append(mips.AdditionInmediateNode(reg1, reg1, 1, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.LessEqualNode)
+ def visit(self, node: cil.LessEqualNode):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.left))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left),
+ line=node.line, column=node.column))
+ if isinstance(node.left, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ reg2 = mips.REGISTERS[1]
+ instructions.extend(self.visit(node.right))
+ instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right),
+ line=node.line, column=node.column))
+ if isinstance(node.right, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ instructions.append(mips.LessEqualNode(reg1, reg1, reg2, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ return instructions
+
+ '''
+ @visitor.when(PrefixNode)
+ def visit(self, node: PrefixNode):
+ return f'PREFFIXNODE'
+
+ @visitor.when(ToStrNode)
+ def visit(self, node: ToStrNode):
+ return f'{node.dest} = str({node.value})'
+ '''
+
+ @visitor.when(cil.NotNode)
+ def visit(self, node: cil.NotNode):
+ instructions = []
+
+ reg1 = mips.REGISTERS[0]
+ instructions.extend(self.visit(node.value))
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value),
+ line=node.line, column=node.column))
+ if isinstance(node.value, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+ instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column))
+ instructions.extend(self.visit(node.dest))
+ instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest),
+ line=node.line, column=node.column))
+ if isinstance(node.dest, cil.AttributeNode):
+ instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4,
+ node.line, node.column))
+
+ return instructions
+
+ @visitor.when(cil.VarNode)
+ def visit(self, node: cil.VarNode):
+ return []
+
+ @visitor.when(cil.ParamNode)
+ def visit(self, node: cil.ParamNode):
+ return []
+
+ @visitor.when(cil.AttributeNode)
+ def visit(self, node: cil.AttributeNode):
+ instructions = []
+
+ self_var = self._current_function.params[0]
+ reg1 = mips.REGISTERS[4]
+ instructions.append(mips.LoadWordNode(reg1, self.get_var_location(self_var), node.line, node.column))
+
+ index = self._types_section[node.type].attributes.index(node.name) + 3
+ instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, index * 4),
+ line=node.line, column=node.column))
+ instructions.extend(mips.push_register(reg1, node.line, node.column))
+ return instructions
\ No newline at end of file
diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py
new file mode 100644
index 000000000..ddfe2331e
--- /dev/null
+++ b/src/core/mips/MipsAst.py
@@ -0,0 +1,415 @@
+DOUBLE_WORD = 4
+REGISTERS_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7', 't8']
+ARG_REGISTERS_NAMES = ['a0', 'a1', 'a2', 'a3']
+
+TYPES_LABEL = "types_table"
+PROTOTYPE_LABEL = "prototype_table"
+
+
+class Register:
+ def __init__(self, name):
+ self.name = name
+
+
+REGISTERS = [Register(i) for i in REGISTERS_NAMES]
+ARG_REGISTERS = [Register(i) for i in ARG_REGISTERS_NAMES]
+FP_REG = Register('fp')
+SP_REG = Register('sp')
+RA_REG = Register('ra')
+V0_REG = Register('v0')
+V1_REG = Register('v1')
+ZERO_REG = Register('zero')
+LOW_REG = Register('lo')
+GP_REG = Register('gp')
+
+
+class Node:
+ def __init__(self, line=0, column=0):
+ self.line = line
+ self.column = column
+
+
+class ProgramNode(Node):
+ def __init__(self, data, types, functions, line, column):
+ super().__init__(line, column)
+ self.data = data
+ self.types = types
+ self.functions = functions
+
+
+class FunctionNode(Node):
+ def __init__(self, label, params, localvars, line, column):
+ super().__init__(line, column)
+ self.label = label
+ self.instructions = []
+ self.params = params
+ self.localvars = localvars
+
+ def add_instruction(self, instruction):
+ self.instructions.append(instruction)
+
+ def get_param_stack_location(self, name):
+ index = self.params.index(name)
+ offset = ((len(self.params) - 1) - index) * DOUBLE_WORD
+ return RegisterRelativeLocation(FP_REG, offset)
+
+ def get_local_stack_location(self, name):
+ index = self.localvars.index(name)
+ offset = (len(self.localvars) + 2 - index) * DOUBLE_WORD
+ return RegisterRelativeLocation(FP_REG, -offset)
+
+ def get_var_location(self, name):
+ try:
+ return self.get_param_stack_location(name)
+ except ValueError:
+ return self.get_local_stack_location(name)
+
+
+class InstructionNode(Node):
+ pass
+
+
+class AbsoluteNode(InstructionNode):
+ # rdest <- abs(rsrc)
+ def __init__(self, rdest, rsrc, line, column):
+ '''
+ Put the absolute value of register rsrc in register rdest
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc = rsrc
+
+
+class AdditionNode(InstructionNode):
+ # rd <- rs + rt
+ def __init__(self, rdest, rsrc1, rsrc2, line, column):
+ '''
+ Put the sum of registers rsrc1 and rsrc2 into register rdest.
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+ self.rsrc2 = rsrc2
+
+
+class AdditionInmediateNode(InstructionNode):
+ def __init__(self, rdest, rsrc, imm, line, column):
+ '''
+ Put the sum of register rsrc and the sign-extended immediate into register rdest
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc = rsrc
+ self.imm = imm
+
+
+class DivideNode(InstructionNode):
+ def __init__(self, rsrc1, rsrc2, line, column):
+ '''
+ Put the quotient of register rsrc1 and src2 into register Hi/Lo.
+ '''
+ super().__init__(line, column)
+ self.rsrc1 = rsrc1
+ self.rsrc2 = rsrc2
+
+class MoveLowNode(InstructionNode):
+
+ def __init__(self, dest, line, column):
+ '''
+ Put the content of register low into register rsrc.
+ '''
+ super().__init__(line, column)
+ self.dest = dest
+
+class MoveHighNode(InstructionNode):
+
+ def __init__(self, dest, line, column):
+ '''
+ Put the content of register Hi into register rsrc.
+ '''
+ super().__init__(line, column)
+ self.dest = dest
+
+
+
+class MultiplyNode(InstructionNode):
+ def __init__(self, rdest, rsrc1, rsrc2, line, column):
+ '''
+ Put the product of register rsrc1 and src2 into register rdest.
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+ self.rsrc2 = rsrc2
+
+
+class NegateNode(InstructionNode):
+ def __init__(self, rdest, rsrc1, line, column):
+ '''
+ Put the negation of register rsrc1 into register rdest.
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+
+
+class SubstractionNode(InstructionNode):
+ def __init__(self, rdest, rsrc1, rsrc2, line, column):
+ '''
+ Put the difference of register rsrc1 and rsrc2 into register rdest.
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+ self.rsrc2 = rsrc2
+
+
+class LessNode(InstructionNode):
+ def __init__(self, rdest, rsrc1, rsrc2, line, column):
+ '''
+ Set register rdest to 1 if register rsrc1 is less than rsrc2, and 0 otherwise
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+ self.rsrc2 = rsrc2
+
+
+class LessInmediateNode(InstructionNode):
+ def __init__(self, rdest, rsrc1, imm, line, column):
+ '''
+ Set register rdest to 1 if register rsrc1 is less than imm, and 0 otherwise
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+ self.imm = imm
+
+
+class EqualNode(InstructionNode):
+ def __init__(self, rdest, rsrc1, rsrc2, line, column):
+ '''
+ Set register rdest to 1 if register rsrc1 equals rsrc2, and 0 otherwise
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+ self.rsrc2 = rsrc2
+
+
+class LessEqualNode(InstructionNode):
+ def __init__(self, rdest, rsrc1, rsrc2, line, column):
+ '''
+ Set register rdest to 1 if register rsrc1 is less than or equal to rsrc2, and 0 otherwise
+ '''
+ super().__init__(line, column)
+ self.rdest = rdest
+ self.rsrc1 = rsrc1
+ self.rsrc2 = rsrc2
+
+
+class JumpNode(InstructionNode):
+ def __init__(self, label, line, column):
+ '''
+ Unconditionally jump to the instruction at the label.
+ '''
+ super().__init__(line, column)
+ self.label = label
+
+class JumpRegisterNode(InstructionNode):
+ def __init__(self, reg, line, column):
+ '''
+ Unconditionally jump to the instruction at the label.
+ '''
+ super().__init__(line, column)
+ self.reg = reg
+
+
+class JalNode(InstructionNode):
+ def __init__(self, label, line, column):
+ '''
+ Unconditionally jump to the instruction at target. Save the address of the next
+ instruction in register ra (rd said the manual).
+ '''
+ super().__init__(line, column)
+ self.label = label
+
+class JalrNode(InstructionNode):
+ def __init__(self, reg1, reg2, line, column):
+ '''
+ Unconditionally jump to the instruction at target register. Save the address of the next
+ instruction in register ra (rd said the manual).
+ '''
+ super().__init__(line, column)
+ self.reg1 = reg1
+ self.reg2 = reg2
+
+class MoveNode(InstructionNode):
+ def __init__(self, reg1, reg2, line, column):
+ super().__init__(line, column)
+ self.reg1 = reg1
+ self.reg2 = reg2
+
+
+class StoreWordNode(InstructionNode):
+ def __init__(self, reg, addr, line, column):
+ super().__init__(line, column)
+ self.reg = reg
+ self.addr = addr
+
+
+class LoadInmediateNode(InstructionNode):
+ def __init__(self, reg, value, line, column):
+ super().__init__(line, column)
+ self.reg = reg
+ self.value = value
+
+
+class LoadWordNode(InstructionNode):
+ def __init__(self, reg, addr, line, column):
+ super().__init__(line, column)
+ self.reg = reg
+ self.addr = addr
+
+
+class LoadAddressNode(InstructionNode):
+ def __init__(self, reg, label, line, column):
+ '''
+ Load computed address , not the contents of the location, into register rdest
+ '''
+ super().__init__(line, column)
+ self.reg = reg
+ self.label = label
+
+
+class BranchOnNotEqualNode(InstructionNode):
+ def __init__(self, reg1, reg2, label, line, column):
+ super().__init__(line, column)
+ self.reg1 = reg1
+ self.reg2 = reg2
+ self.label = label
+
+
+class LabelNode(InstructionNode):
+ def __init__(self, name, line, column):
+ super().__init__(line, column)
+ self.name = name
+
+
+class NotNode(InstructionNode):
+ def __init__(self, dest, src, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.src = src
+
+class ComplementNode(InstructionNode):
+ def __init__(self, dest, src, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.src = src
+
+
+class ShiftLeftNode(InstructionNode): # Shift Left Logical
+ def __init__(self, dest, src, bits, line, column):
+ super().__init__(line, column)
+ self.dest = dest
+ self.src = src
+ self.bits = bits
+
+
+class SyscallNode(InstructionNode):
+ pass
+
+
+class DataNode(Node):
+ def __init__(self, label, line, column):
+ super().__init__(line, column)
+ self.label = label
+
+
+class StringConst(DataNode):
+ def __init__(self, label, string, line, column):
+ super().__init__(label, line, column)
+ self.string = string
+
+
+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) + DOUBLE_WORD
+
+ @property
+ def string_name_label(self):
+ return self.name
+
+
+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
+
+
+def push_register(reg, line, column):
+ move_stack = AdditionInmediateNode(SP_REG, SP_REG, -DOUBLE_WORD, line, column)
+ save_location = RegisterRelativeLocation(SP_REG, 0)
+ save_register = StoreWordNode(reg, save_location, line, column)
+ return [move_stack, save_register]
+
+
+def pop_register(reg, line, column):
+ load_value = LoadWordNode(reg, RegisterRelativeLocation(SP_REG, 0), line, column)
+ move_stack = AdditionInmediateNode(SP_REG, SP_REG, DOUBLE_WORD, line, column)
+ return [load_value, move_stack]
+
+
+def create_object(reg1, reg2, line, column):
+ instructions = []
+
+ instructions.append(ShiftLeftNode(reg1, reg1, 2, line, column))
+ instructions.append(LoadAddressNode(reg2, PROTOTYPE_LABEL, line, column))
+ instructions.append(AdditionNode(reg2, reg2, reg1, line, column))
+ instructions.append(LoadWordNode(reg2, RegisterRelativeLocation(reg2, 0), line, column))
+ instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4), line, column))
+ instructions.append(ShiftLeftNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2, line, column))
+ instructions.append(JalNode("malloc", line, column))
+ instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0], line, column))
+ instructions.append(MoveNode(ARG_REGISTERS[0], reg2, line, column))
+ instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG, line, column))
+ instructions.append(JalNode("copy", line, column))
+
+ return instructions
diff --git a/src/core/mips/MipsAstFormatter.py b/src/core/mips/MipsAstFormatter.py
new file mode 100644
index 000000000..c07c10925
--- /dev/null
+++ b/src/core/mips/MipsAstFormatter.py
@@ -0,0 +1,205 @@
+from core.tools import visitor
+from core.mips.MipsAst import *
+
+class MIPSAstFormatter:
+ @visitor.on('node')
+ def visit(self, node):
+ pass
+
+ @visitor.when(ProgramNode)
+ def visit(self, node):
+
+ data = f'.data\n' + '\n.word 0\n'.join(self.visit(i) for i in node.data) + '\n'
+
+ names_table = f"{TYPES_LABEL}:\n" + "\n".join([f"\t.word\t{tp.name}" for tp in node.types])
+ proto_table = f"{PROTOTYPE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.label}_prototype" for tp in node.types])
+
+ types = "\n\n\n".join([self.visit(tp) for tp in node.types])
+ code = '\n\n'.join(self.visit(i) for i in node.functions) + '\n'
+
+ mipsCode = f'{data}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\n\n.text\n.globl main\n{code}\n\n'
+
+ with open('core/mips/MipsBasics.asm', 'r') as f:
+ mipsCode += "".join(f.readlines())
+ return mipsCode
+
+
+ @visitor.when(MIPSType)
+ def visit(self, node):
+ methods = "\n".join([f"\t.word\t {m}" for m in node.methods])
+ dispatch_table = f"{node.label}_dispatch:\n{methods}"
+ proto_begin = f"{node.label}_prototype:\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\t0' for attr in node.attributes])
+ proto_end = f"\t.word\t-1"
+ proto = f"{proto_begin}\n{proto_attr}\n{proto_end}" if proto_attr != "" else f"{proto_begin}\n{proto_end}"
+
+ return f'{dispatch_table}\n\n{proto}'
+
+ @visitor.when(FunctionNode)
+ def visit(self, node):
+ return f'{node.label}:\n\t' + \
+ f'\n\t'.join(self.visit(i) for i in node.instructions)
+
+ @visitor.when(AbsoluteNode)
+ def visit(self, node):
+ return f'abs {self.visit(node.rdest)} {self.visit(node.rsrc)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(AdditionNode)
+ def visit(self, node):
+ return f'add {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(AdditionInmediateNode)
+ def visit(self, node):
+ return f'addi {self.visit(node.rdest)} {self.visit(node.rsrc)} {self.visit(node.imm)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(DivideNode)
+ def visit(self, node):
+ return f'div {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(MultiplyNode)
+ def visit(self, node):
+ return f'mul {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(NegateNode)
+ def visit(self, node):
+ return f'neg {self.visit(node.rdest)} {self.visit(node.rsrc1)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(SubstractionNode)
+ def visit(self, node):
+ return f'sub {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(LessNode)
+ def visit(self, node):
+ return f'slt {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(LessInmediateNode)
+ def visit(self, node):
+ return f'slti {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.imm)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(EqualNode)
+ def visit(self, node):
+ return f'seq {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(LessEqualNode)
+ def visit(self, node):
+ return f'sle {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(JumpNode)
+ def visit(self, node):
+ return f'j {self.visit(node.label)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(JumpRegisterNode)
+ def visit(self, node):
+ return f'jr {self.visit(node.reg)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(JalNode)
+ def visit(self, node):
+ return f'jal {self.visit(node.label)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(JalrNode)
+ def visit(self, node):
+ return f'jalr {self.visit(node.reg1)} {self.visit(node.reg2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(MoveNode)
+ def visit(self, node):
+ return f'move {self.visit(node.reg1)} {self.visit(node.reg2)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(StoreWordNode)
+ def visit(self, node):
+ return f'sw {self.visit(node.reg)} {self.visit(node.addr)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(LoadInmediateNode)
+ def visit(self, node):
+ return f'li {self.visit(node.reg)} {self.visit(node.value)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(LoadWordNode)
+ def visit(self, node):
+ return f'lw {self.visit(node.reg)} {self.visit(node.addr)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(LoadAddressNode)
+ def visit(self, node):
+ return f'la {self.visit(node.reg)} {self.visit(node.label)}'
+
+ @visitor.when(BranchOnNotEqualNode)
+ def visit(self, node):
+ return f'bne {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.label)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(LabelNode)
+ def visit(self, node):
+ return f'{self.visit(node.name)}:'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(NotNode)
+ def visit(self, node):
+ return f'xori {self.visit(node.dest)} {self.visit(node.src)} 1'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(ComplementNode)
+ def visit(self, node):
+ return f'not {self.visit(node.dest)} {self.visit(node.src)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(MoveLowNode)
+ def visit(self, node):
+ return f'mflo {self.visit(node.dest)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(MoveHighNode)
+ def visit(self, node):
+ return f'mfhi {self.visit(node.dest)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(ShiftLeftNode)
+ def visit(self, node):
+ return f'sll {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.bits)}'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(SyscallNode)
+ def visit(self, node):
+ return 'syscall'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(StringConst)
+ def visit(self, node):
+ return f'{node.label}: .asciiz "{node.string}"'.ljust(50) + \
+ f'#line: {node.line} column: {node.column}'
+
+ @visitor.when(RegisterRelativeLocation)
+ def visit(self, node):
+ return f'{node._offset}({self.visit(node._register)})'
+
+ @visitor.when(LabelRelativeLocation)
+ def visit(self, node):
+ return f'{node._label}'
+
+ @visitor.when(Register)
+ def visit(self, node):
+ return f'${node.name}'
+
+ @visitor.when(int)
+ def visit(self, node):
+ return str(node)
+
+ @visitor.when(str)
+ def visit(self, node):
+ return node
\ No newline at end of file
diff --git a/src/core/mips/MipsBasics.asm b/src/core/mips/MipsBasics.asm
new file mode 100644
index 000000000..002c926a4
--- /dev/null
+++ b/src/core/mips/MipsBasics.asm
@@ -0,0 +1,378 @@
+
+# Args:
+# $a0 size to alloc
+# Return:
+# $v0 address of allocated block
+
+malloc:
+ addiu $sp $sp -12 # Save content of registers in sp
+ sw $a0 0($sp)
+ sw $t0 4($sp)
+ sw $t1 8($sp)
+
+ li $t0 4
+ div $a0 $t0 # Size of string / wordsize
+ mfhi $t1 # t2 holds remainder of division
+
+ sub $t0 $t0 $t1 # Convert t1 to multiple of 4
+ add $a0 $a0 $t0
+
+ li $v0 9
+ syscall
+
+ lw $a0 0($sp) # Return original content to registers
+ lw $t0 4($sp)
+ lw $t1 8($sp)
+ addiu $sp $sp 12
+ jr $ra
+
+
+# COPY
+# $a0 address from
+# $a1 address to
+# $a2 size
+
+copy:
+ addiu $sp $sp -16 # Save content of registers in sp
+ sw $a0 0($sp)
+ sw $a1 4($sp)
+ sw $a2 8($sp)
+ sw $t0 12($sp)
+
+copy_loop:
+ beq $a2 $zero copy_end # Copy finished (copy size is 0)
+ lw $t0 0($a0) # Load in t0 content of source address a0
+ sw $t0 0($a1) # Save in destiny a1 content of t0
+ addiu $a0 $a0 4 # Increase source address a0
+ addiu $a1 $a1 4 # Increase destiny addres a1
+ addi $a2 $a2 -4 # Decrease copy size
+ j copy_loop
+
+copy_end:
+ lw $a0 0($sp) # Return original content to registers
+ lw $a1 4($sp)
+ lw $a2 8($sp)
+ lw $t0 12($sp)
+ addiu $sp $sp 16
+
+ jr $ra
+
+
+
+
+# LEN
+# a0 begin address
+# return size in v0
+length:
+ # Save content of registers
+ addiu $sp $sp -8
+ sw $t0 0($sp)
+ sw $t1 4($sp)
+
+ move $t0 $a0 # Move to t0 the address to begin
+ move $v0 $zero # Set v0 to zero
+
+len_loop:
+ lb $t1 0($t0) # Save in t1 first byte of address
+ beq $t1 $zero len_end # Finish object if t1 is zero
+ addi $v0 $v0 1 # Increase count in v0
+ addiu $t0 $t0 1 # Increase address pointer
+ j len_loop # Finish loop
+
+len_end:
+ # Return original content to registers
+ lw $t0 0($sp)
+ lw $t1 4($sp)
+ addiu $sp $sp 8
+
+ jr $ra
+
+
+# SUBSTRING
+# a0 Pointer to beginning of string
+# a1 Pointer to beginning of substring
+# a2 Size of substring
+
+
+substr:
+ # Save content of registers
+ addiu $sp $sp -32
+ sw $t0 0($sp)
+ sw $t1 4($sp)
+ sw $t2 8($sp)
+ sw $t3 12($sp)
+ sw $a0 16($sp)
+ sw $a1 20($sp)
+ sw $a2 24($sp)
+ sw $ra 28($sp)
+
+ move $t0 $a0 # t0 points to beginning o string
+ add $t0 $t0 $a1
+ li $t1 4 # t1 Word size
+
+ div $a2 $t1 # Size of substring / wordsize
+ mfhi $t2 # t2 holds remainder of division
+
+ bne $t2 $zero substr_allign_size # Branch if division is not exact
+ move $t1 $a2 # t1 size of substring
+ j substr_new_block
+
+substr_allign_size:
+ sub $t1 $t1 $t2 # Convert t1 to multiple of 4 to...
+ add $t1 $t1 $a2 # reserve memory via malloc
+
+substr_new_block:
+ move $a0 $t1 # Store in a0 size of space to reserve via malloc
+ jal malloc # Malloc
+ move $t3 $v0 # Pointer to beginning of reserved space
+ move $t1 $zero # Count
+
+substr_copy_loop:
+ beq $t1 $a2 substr_end # Copy finished
+ lb $t2 0($t0) # Load byte from string into t2 temporal
+ sb $t2 0($t3) # Savebyte from t2 into t3
+ addiu $t0 $t0 1 # Increase pointer to string
+ addiu $t3 $t3 1 # Increase pointer to reserved space
+ addiu $t1 $t1 1 # Increase count
+ j substr_copy_loop
+
+substr_end:
+ sb $zero 0($t3) # Set next byte of substring to zero
+
+ # Return original values to registers
+ lw $t0 0($sp)
+ lw $t1 4($sp)
+ lw $t2 8($sp)
+ lw $t3 12($sp)
+ lw $a0 16($sp)
+ lw $a1 20($sp)
+ lw $a2 24($sp)
+ lw $ra 28($sp)
+ addiu $sp $sp 32
+
+ jr $ra
+
+
+
+# CONCAT
+# a0 pointer to string 1
+# a1 pointer to string 2
+# a2 size of string 1 + size of string 2
+
+concat:
+ # Save content of registers
+ 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 # t0 pointer to string 1
+ move $t1 $a1 # t1 pointer to string 2
+
+
+ addiu $a0 $a2 1 # Save in a0 size in a2 + 1
+ li $t2 4 # t2 = 4
+ div $a0 $t2 # a0 / t2
+ mfhi $a0 # a0 remainder of division
+ bne $a0 $zero concat_allign_size # Branch if size is multiple of 4
+ addiu $a0 $a2 1 # Add 1 t size
+ j concat_size_alligned
+
+concat_allign_size:
+ sub $t2 $t2 $a0 # Convert t1 to multiple of 4 to...
+ add $a0 $a2 $t2 # reserve memory via malloc
+ addiu $a0 $a0 1 # Add 1 t size
+
+concat_size_alligned:
+ jal malloc # a0 stores size to reserve
+ move $t2 $v0 # t2 is pointer to empty space
+ j concat_copy_first_loop
+
+concat_copy_first_loop:
+ lb $a0 0($t0) # move to a0 content of t0
+ beq $a0 $zero concat_copy_second_loop # a0 == 0 finish
+ sb $a0 0($t2) # move to t2 content of a0
+ addiu $t0 $t0 1 # Increase t0 pointer
+ addiu $t2 $t2 1 # Increase t2 pointer
+ j concat_copy_first_loop
+
+concat_copy_second_loop:
+ lb $a0 0($t1) # move to a0 content of t1
+ beq $a0 $zero concat_end # a0 == 0 finish
+ sb $a0 0($t2) # move to t2 content of a0
+ addiu $t1 $t1 1 # Increase t1 pointer
+ addiu $t2 $t2 1 # Increase t2 pointer
+ j concat_copy_second_loop
+
+concat_end:
+ # Return original values to registers
+ 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
+
+read_string:
+ addiu $sp $sp -28
+ sw $ra 0($sp)
+ sw $t0 4($sp)
+ sw $t1 8($sp)
+ sw $a0 12($sp)
+ sw $a1 16($sp)
+ sw $a2 20($sp)
+ sw $t2 24($sp)
+
+ li $t0 8
+
+ addi $a0 $t0 4
+ jal malloc
+ move $t1 $v0
+ move $t2 $zero
+
+ read_string_loop:
+
+ addu $a0 $t1 $t2
+ subu $a1 $t0 $t2
+ addu $t2 $t2 $a1
+ jal read_string_up_to
+
+ beq $v0 $zero read_string_not_finished
+ move $v0 $t1
+ j read_string_finished
+
+ read_string_not_finished:
+ sll $t0 $t0 1
+
+ addi $a0 $t0 4
+ jal malloc
+
+ move $a0 $t1
+ move $t1 $v0
+ move $a1 $v0
+ move $a2 $t2
+ jal copy
+
+ j read_string_loop
+
+ read_string_finished:
+
+ lw $ra 0($sp)
+ lw $t0 4($sp)
+ lw $t1 8($sp)
+ lw $a0 12($sp)
+ lw $a1 16($sp)
+ lw $a2 20($sp)
+ lw $t2 24($sp)
+ addiu $sp $sp 28
+ jr $ra
+
+# Lee a lo sumo $a1 caracteres y los guarda en la direccion
+# a la que apunta $a0, $a1 debe ser divisible por 4
+# retorna en $v0 1 si se leyo un \n
+read_string_up_to:
+ addiu $sp $sp -28
+ sw $ra 0($sp)
+ sw $t0 4($sp)
+ sw $t1 8($sp)
+ sw $t2 12($sp)
+ sw $t3 16($sp)
+ sw $t4 20($sp)
+ sw $t5 24($sp)
+
+ move $t0 $a0
+ move $t1 $zero
+ li $t2 10
+ addu $t3 $t0 $a1
+ addiu $a1 $a1 1
+
+ li $v0 8
+ syscall
+ lw $a0 0($a0)
+ beq $a0 $zero eol_terminated
+
+ li $v0 0
+
+ eol_check:
+ beq $t0 $t3 read_loop_continue
+
+ lb $t1 0($t0)
+ beq $t1 $t2 eol_terminated
+
+ addiu $t0 $t0 1
+ j eol_check
+
+ eol_terminated:
+ sb $zero 0($t0) # put null character at the end of string
+ li $v0 1
+ read_loop_continue:
+
+ lw $ra 0($sp)
+ lw $t0 4($sp)
+ lw $t1 8($sp)
+ lw $t2 12($sp)
+ lw $t3 16($sp)
+ lw $t4 20($sp)
+ lw $t5 24($sp)
+ addiu $sp $sp 28
+ jr $ra
+
+
+# EQUAL STRING
+
+equal_str:
+ # a0 pointer to string 1
+ # a1 pointer to string 2
+ # v0 answer
+
+ # Save content of registers
+ addiu $sp $sp -24
+ sw $t0 0($sp)
+ sw $t1 4($sp)
+ sw $a0 8($sp)
+ sw $a1 12($sp)
+ sw $t2 16($sp)
+ sw $t3 20($sp)
+
+ move $t0 $a0
+ move $t1 $a1
+
+equal_str_loop:
+ lb $t2 0($t0)
+ lb $t3 0($t1)
+ bne $t2 $t3 equal_str_different_strings
+ beq $t2 $zero equal_str_finished_first
+ beq $t3 $zero equal_str_finished_second
+ addi $t1 $t1 1
+ addi $t0 $t0 1
+ j equal_str_loop
+
+equal_str_different_strings:
+ move $v0 $zero
+ j equal_str_end
+
+equal_str_finished_first:
+ beq $t3 $zero equal_str_finished_second
+ move $v0 $zero
+ j equal_str_end
+
+equal_str_finished_second:
+ li $v0 1
+
+equal_str_end:
+ lw $t0 0($sp)
+ lw $t1 4($sp)
+ lw $a0 8($sp)
+ lw $a1 12($sp)
+ lw $t2 16($sp)
+ lw $t3 20($sp)
+ addiu $sp $sp 24
+
+ jr $ra
\ No newline at end of file
diff --git a/src/core/parser/Parser.py b/src/core/parser/Parser.py
new file mode 100644
index 000000000..4a393ecc2
--- /dev/null
+++ b/src/core/parser/Parser.py
@@ -0,0 +1,138 @@
+from core.tools.Pycompiler import Grammar
+from core.tools.ParserLR1 import LR1Parser
+from core.cool.CoolAst import *
+
+
+# Representacion de la gramatica de COOL utilizando la clase grammar
+CoolGrammar = Grammar()
+
+# noterminales
+program = CoolGrammar.NonTerminal('', startSymbol=True)
+class_list, def_class = CoolGrammar.NonTerminals(' ')
+feature_list, feature = CoolGrammar.NonTerminals(' ')
+param_list, param = CoolGrammar.NonTerminals(' ')
+expr_1, expr_2, member_call, expr_list, let_list, case_list = CoolGrammar.NonTerminals(
+ ' ')
+comp_expr, arith, arith_2, term, factor, factor_2 = CoolGrammar.NonTerminals(
+ ' ')
+atom, func_call, arg_list = CoolGrammar.NonTerminals(' ')
+
+# terminales
+classx, inherits, function = CoolGrammar.Terminals('class inherits function')
+ifx, then, elsex, fi = CoolGrammar.Terminals('if then else fi')
+whilex, loop, pool = CoolGrammar.Terminals('while loop pool')
+let, inx = CoolGrammar.Terminals('let in')
+case, of, esac = CoolGrammar.Terminals('case of esac')
+semi, colon, comma, dot, at, opar, cpar, ocur, ccur, larrow, rarrow = CoolGrammar.Terminals(
+ '; : , . @ ( ) { } <- =>')
+plus, minus, star, div, isvoid, compl = CoolGrammar.Terminals('+ - * / isvoid ~')
+notx, less, leq, equal = CoolGrammar.Terminals('not < <= =')
+new, idx, typex, integer, string, boolx = CoolGrammar.Terminals('new id type integer string bool')
+
+# Producciones
+program %= class_list, lambda h, s: ProgramNode(s[1])
+
+# Lista de clases
+class_list %= def_class + class_list, lambda h, s: [s[1]] + s[2]
+class_list %= def_class, lambda h, s: [s[1]]
+
+# Defincicion de la clase
+def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[1], s[2], s[4])
+def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(
+ s[1], s[2], s[6], s[4])
+
+# Lista de propiedades de la clase
+feature_list %= feature + feature_list, lambda h, s: [s[1]] + s[2]
+feature_list %= CoolGrammar.Epsilon, lambda h, s: []
+
+# Atributos de la clase
+feature %= idx + colon + typex + semi, lambda h, s: AttrDeclarationNode(s[1], s[3])
+feature %= idx + colon + typex + larrow + expr_1 + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5])
+
+# Metodos constructores de la clase
+feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(
+ s[1], s[3], s[6], s[8])
+feature %= idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [],
+ s[5], s[7])
+# Metodos de la clase
+feature %= function + idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(
+ s[2], s[4], s[7], s[9])
+feature %= function + idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[2], [],
+ s[6], s[8])
+# Lista de parametros de funcion
+param_list %= param, lambda h, s: [s[1]]
+param_list %= param + comma + param_list, lambda h, s: [s[1]] + s[3]
+
+# parametro de funcion
+param %= idx + colon + typex, lambda h, s: (s[1], s[3])
+
+### Expresiones ###
+# Expresion Let-in
+expr_1 %= let + let_list + inx + expr_1, lambda h, s: LetInNode(s[1], s[2], s[4])
+let_list %= idx + colon + typex, lambda h, s: [(s[1], s[3], None)]
+let_list %= idx + colon + typex + larrow + expr_1, lambda h, s: [(s[1], s[3], s[5])]
+let_list %= idx + colon + typex + comma + let_list, lambda h, s: [(s[1], s[3], None)] + s[5]
+let_list %= idx + colon + typex + larrow + expr_1 + comma + let_list, lambda h, s: [(s[1], s[3], s[5])] + s[7]
+
+expr_1 %= idx + larrow + expr_1, lambda h, s: AssignNode(s[1], s[3])
+expr_1 %= notx + expr_1, lambda h, s: NotNode(s[1], s[2])
+expr_1 %= expr_2 + equal + expr_1, lambda h, s: EqualNode(s[1], s[2], s[3])
+expr_1 %= expr_2, lambda h, s: s[1]
+
+expr_2 %= arith + less + arith, lambda h, s: LessNode(s[1], s[2], s[3])
+expr_2 %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[2], s[3])
+expr_2 %= arith, lambda h, s: s[1]
+
+#Expresiones aritmeticas
+arith %= arith + plus + factor, lambda h, s: PlusNode(s[1], s[2], s[3])
+arith %= arith + minus + factor, lambda h, s: MinusNode(s[1], s[2], s[3])
+arith %= factor, lambda h, s: s[1]
+
+factor %= factor + star + term, lambda h, s: StarNode(s[1], s[2], s[3])
+factor %= factor + div + term, lambda h, s: DivNode(s[1], s[2], s[3])
+factor %= term, lambda h, s: s[1]
+
+term %= compl + term, lambda h, s: ComplementNode(s[1], s[2])
+term %= isvoid + term, lambda h, s: IsVoidNode(s[1], s[2])
+term %= atom, lambda h, s: s[1]
+
+# Encapsulaciones atomicas
+atom %= opar + expr_1 + cpar, lambda h, s: s[2]
+atom %= integer, lambda h, s: IntegerNode(s[1])
+atom %= string, lambda h, s: StringNode(s[1])
+atom %= boolx, lambda h, s: BoolNode(s[1])
+atom %= idx, lambda h, s: IdNode(s[1])
+atom %= ifx + expr_1 + then + expr_1 + elsex + expr_1 + fi, lambda h, s: IfThenElseNode(s[1], s[2], s[4], s[6])
+atom %= whilex + expr_1 + loop + expr_1 + pool, lambda h, s: WhileLoopNode(s[1], s[2], s[4])
+
+# Expresion new
+atom %= new + typex, lambda h, s: NewNode(s[1], s[2])
+
+# Encapsulamiento entre corchetes
+atom %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[1], s[2])
+expr_list %= expr_1 + semi, lambda h, s: [s[1]]
+expr_list %= expr_1 + semi + expr_list, lambda h, s: [s[1]] + s[3]
+
+# Expresion Case of
+atom %= case + expr_1 + of + case_list + esac, lambda h, s: CaseOfNode(s[1], s[2], s[4])
+case_list %= idx + colon + typex + rarrow + expr_1 + semi, lambda h, s: [(s[1], s[3], s[5])]
+case_list %= idx + colon + typex + rarrow + expr_1 + semi + case_list, lambda h, s: [(s[1], s[3], s[5])] + s[7]
+
+atom %= func_call, lambda h, s: s[1]
+
+# Llamado a funcion
+func_call %= atom + at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[5], s[7], s[3])
+func_call %= atom + at + typex + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[5], [], s[3])
+func_call %= atom + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[3], s[5])
+func_call %= atom + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[3], [])
+
+# Llamado a miembro de clase
+func_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3])
+func_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], [])
+
+# Lista de argumentos
+arg_list %= expr_1, lambda h, s: [s[1]]
+arg_list %= expr_1 + comma + arg_list, lambda h, s: [s[1]] + s[3]
+
+# parser
+CoolParser = LR1Parser(CoolGrammar)
diff --git a/src/core/semantic/TypeBuilder.py b/src/core/semantic/TypeBuilder.py
new file mode 100644
index 000000000..3c8624411
--- /dev/null
+++ b/src/core/semantic/TypeBuilder.py
@@ -0,0 +1,161 @@
+from core.tools import visitor
+from core.tools.Semantic import *
+from core.parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode
+from core.tools.Errors import *
+
+# Visitor encargado de contruir los tipos. Una vez que se conocen los nombres
+# de los tipos que intervienen en el codigo COOL, este visitor les annade sus
+# metodos y atributos, asi como el tipo padre.
+
+class Type_Builder:
+ def __init__(self, Context : Context):
+ self.Context = Context
+ self.Current_Type = None
+
+ self.errors = []
+
+ # Construye los tipos builtin
+ self.Object_Type = self.Context.get_type('Object')
+
+ self.IO_Type = self.Context.get_type('IO')
+ self.IO_Type.set_parent(self.Object_Type)
+ self.IO_Type.depth = 1
+
+ self.Int_Type = self.Context.get_type('Int')
+ self.Int_Type.set_parent(self.Object_Type)
+ self.Int_Type.depth = 1
+ self.Int_Type.sealed = True
+
+ self.String_Type = self.Context.get_type('String')
+ self.String_Type.set_parent(self.Object_Type)
+ self.String_Type.depth = 1
+ self.String_Type.sealed = True
+
+ self.Bool_Type = self.Context.get_type('Bool')
+ self.Bool_Type.set_parent(self.Object_Type)
+ self.Bool_Type.depth = 1
+ self.Bool_Type.sealed = True
+
+ self.IO_Type.define_method('out_string', ['x'], [self.String_Type], SelfType(), 0, 0)
+ self.IO_Type.define_method('out_int', ['x'], [self.Int_Type], SelfType(), 0, 0)
+ self.IO_Type.define_method('in_int', [], [], self.Int_Type, 0, 0)
+ self.IO_Type.define_method('in_string', [], [], self.String_Type, 0, 0)
+
+ self.Object_Type.define_method('abort', [], [], self.Object_Type, 0, 0)
+ self.Object_Type.define_method('type_name', [], [], self.String_Type, 0, 0)
+ self.Object_Type.define_method('copy', [], [], SelfType(), 0, 0)
+
+ self.String_Type.define_method('length', [], [], self.Int_Type, 0, 0)
+ self.String_Type.define_method('concat', ['x'], [self.String_Type], self.String_Type, 0, 0)
+ self.String_Type.define_method('substr', ['l', 'r'], [self.Int_Type, self.Int_Type], self.String_Type, 0, 0)
+
+ @visitor.on('node')
+ def visit(self, node):
+ pass
+
+ @visitor.when(ProgramNode)
+ def visit(self, node : ProgramNode):
+ for type in node.declarations:
+ self.visit(type)
+
+ try:
+ self.Context.get_type('Main').get_method('main')
+ except SemanticException:
+ # Cada programa COOL debe tener una clase MAIN
+ self.errors.append(SemanticError(0, 0,
+ f'Class Main and its method main must be defined'))
+
+ @visitor.when(ClassDeclarationNode)
+ def visit(self, node : ClassDeclarationNode):
+ self.Current_Type = self.Context.get_type(node.id.lex)
+
+ if node.parent:
+ try:
+ parent_type = self.Context.get_type(node.parent.lex)
+ except SemanticException as ex:
+ self.errors.append(TypeError(node.parent.line, node.parent.column,
+ f'Class {node.id} inherits from an undefined class {node.parent.lex}'))
+ parent_type = self.Object_Type
+
+ try:
+ self.Current_Type.set_parent(parent_type)
+ except SemanticException as ex:
+ self.errors.append(SemanticError(node.parent.line, node.parent.column,
+ f'Class {node.id.lex} cannot inherit class {parent_type.name}'))
+
+ parent = self.Current_Type.parent
+ # Revisa que no haya herencia ciclica
+ while parent:
+ if parent == self.Current_Type:
+ self.errors.append(SemanticError(node.line, node.column,
+ f'Class {node.id.lex}, or an ancestor of {node.id.lex}, '
+ f'is involved in an inheritance cycle'))
+ self.Current_Type.parent = self.Object_Type
+ break
+ parent = parent.parent
+
+ else:
+ self.Current_Type.set_parent(self.Object_Type)
+
+
+ for feat in node.features:
+ self.visit(feat)
+
+ @visitor.when(AttrDeclarationNode)
+ def visit(self, node : AttrDeclarationNode):
+ try:
+ attr_type = self.Context.get_type(node.type.lex)
+ except SemanticException as ex:
+ # Existio un error al tratar de obtener el tipo del atributo
+ self.errors.append(TypeError(node.type.line, node.type.column, ex.text))
+ attr_type = ErrorType()
+
+ try:
+ self.Current_Type.define_attribute(node.id.lex, attr_type, node.line, node.column)
+ except SemanticException as ex:
+ # Existio un error al tratar de definir el atributo
+ self.errors.append(SemanticError(node.line, node.column, ex.text))
+
+ @visitor.when(FuncDeclarationNode)
+ def visit(self, node : FuncDeclarationNode):
+ param_names, param_types = [], []
+
+ for name, type in node.params:
+ try:
+ type = self.Context.get_type(type.lex)
+ except SemanticException as ex:
+ # Existio un error al tratar de obtener el tipo del parametro
+ self.errors.append(TypeError(type.line, type.column,
+ f'Class {type.lex} of formal parameter {name.lex} is undefined'))
+ type = ErrorType()
+ else:
+ if isinstance(type, SelfType):
+ self.errors.append(SemanticError(name.line, name.column,
+ f'\'self\' cannot be the name of a formal parameter'))
+ arg_type = ErrorType()
+
+ if name.lex in param_names:
+ self.errors.append(SemanticError(name.line, name.column,
+ f'Formal parameter {name.lex} is multiply defined'))
+
+ param_names.append(name.lex)
+ param_types.append(type)
+
+
+
+ try:
+ return_type = self.Context.get_type(node.type.lex)
+ except SemanticException as ex:
+ # Existio un error al tratar de obtener el tipo del parametro de retorno
+ self.errors.append(TypeError(node.type.line, node.type.column,
+ f'Undefined return type {node.type.lex} in method {node.id.lex}'))
+ return_type = ErrorType()
+
+ if return_type is SelfType:
+ return_type = self.Current_Type
+
+ try:
+ self.Current_Type.define_method(node.id.lex, param_names, param_types, return_type, node.line, node.column)
+ except SemanticException as ex:
+ # Existio un error al tratar de definir el metodo
+ self.errors.append(SemanticError(node.line, node.column, ex.text))
diff --git a/src/core/semantic/TypeChecker.py b/src/core/semantic/TypeChecker.py
new file mode 100644
index 000000000..11c3911a9
--- /dev/null
+++ b/src/core/semantic/TypeChecker.py
@@ -0,0 +1,519 @@
+from core.tools import visitor
+from core.parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\
+ IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode,\
+ AssignNode, LessEqualNode, LessNode, EqualNode, ArithmeticNode,\
+ NotNode, IsVoidNode, ComplementNode, FunctionCallNode, MemberCallNode, NewNode,\
+ IntegerNode, IdNode, StringNode, BoolNode
+from core.tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType
+from core.tools.Errors import *
+
+# Este es el visitor encargado de terminar el chequeo semantico.
+# Revisa la compatibilidad de tipos, la compatibilidad en la herencia,
+# que las variables hayan sido previamente definidas, asi como los
+# metodos y atributos de clase, crea el scope para las variables, el
+# cual sera rehusado para inferir las variables que se requieran.
+# Observar que cada vez que el visitor llama recursivamente crea un scope
+# hijo en el scope actual, esto se hace para que las variables previamente
+# declaradas en ambitos hermanos no sean utiles en el ambito actual.
+
+class Type_Checker:
+
+ def __init__(self, Context : Context):
+ self.Context = Context
+ self.errors = []
+ self.Current_Type = None
+ self.Current_Method = None
+
+ self.Object_Type = self.Context.get_type('Object')
+ self.IO_Type = self.Context.get_type('IO')
+ self.String_Type = self.Context.get_type('String')
+ self.Int_Type = self.Context.get_type('Int')
+ self.Bool_Type = self.Context.get_type('Bool')
+
+ self.builtin_Types = [
+ self.Object_Type,
+ self.IO_Type,
+ self.String_Type,
+ self.Int_Type,
+ self.Bool_Type
+ ]
+
+ @visitor.on('node')
+ def visit(self, node, scope):
+ pass
+
+ @visitor.when(ProgramNode)
+ def visit(self, node : ProgramNode, scope : Scope = None):
+ scope = Scope()
+
+ # Calculate depth for classes
+ for declaration in node.declarations:
+ current = self.Context.get_type(declaration.id.lex)
+ parent = current.parent
+ while parent is not None:
+ current.depth += 1
+ parent = parent.parent
+
+ for declaration in node.declarations:
+ self.visit(declaration, scope.create_child())
+
+ return scope
+
+ @visitor.when(ClassDeclarationNode)
+ def visit(self, node : ClassDeclarationNode, scope : Scope):
+ self.Current_Type = self.Context.get_type(node.id.lex)
+
+ # Incluyo cada uno de los atributos de los padres en su respectivo orden
+ current = self.Current_Type
+ attributtes = []
+ while current.parent:
+ current = current.parent
+ for att in reversed(current.attributes):
+ attributtes.append(att)
+
+ scope.define_variable('self', self.Current_Type, node.line, node.column)
+ for att in reversed(attributtes):
+ scope.define_variable(att.name, att.type, att.line, att.column)
+
+ for att in self.Current_Type.attributes:
+ if scope.is_defined(att.name):
+ self.errors.append(SemanticError(att.line, att.column,
+ f'Attribute {att.name} is an attribute of an inherited class'))
+ scope.define_variable(att.name, att.type, att.line, att.column)
+
+ for feature in node.features:
+ self.visit(feature, scope.create_child())
+ node.static_type = self.Current_Type
+
+ @visitor.when(AttrDeclarationNode)
+ def visit(self, node : AttrDeclarationNode, scope : Scope):
+ expr = node.expression
+ attr = self.Current_Type.get_attribute(node.id.lex)
+ node_type = self.Current_Type if attr.type is SelfType else attr.type
+
+ if expr:
+ self.visit(expr, scope.create_child())
+ expr_type = expr.static_type
+
+ # Chequeo compatibilidad de tipos
+ if not expr_type.conforms_to(node_type):
+ self.errors.append(TypeError(node.expression.line, node.expression.column,
+ f'Inferred type {expr_type.name} of initialization of attribute {attr.name} '
+ f'does not conform to declared type {node_type.name}'))
+
+ if attr.name.lower() == 'self':
+ self.errors.append(SemanticError(node.line, node.column,
+ '\'self\' cannot be the name of an attribute'))
+
+ node.static_type = node_type
+
+ @visitor.when(FuncDeclarationNode)
+ def visit(self, node : FuncDeclarationNode, scope : Scope):
+ self.Current_Method = self.Current_Type.get_method(node.id.lex)
+
+ if self.Current_Type.parent:
+ try:
+ inherited_method = self.Current_Type.parent.get_method(node.id.lex)
+
+ if len(self.Current_Method.param_names) != len(inherited_method.param_names):
+ self.errors.append(SemanticError(node.line, node.column,
+ f'Incompatible number of formal parameters in redefined method {self.Current_Method.name}'))
+ else:
+ for par1, par2, p in zip(self.Current_Method.param_types, inherited_method.param_types, node.params):
+ if par1.name != par2.name:
+ self.errors.append(SemanticError(p[0].line, p[0].column,
+ f'In redefined method {self.Current_Method.name}, parameter type {par1.name} '
+ f'is different from original type {par2.name}'))
+
+ if self.Current_Method.return_type.name != inherited_method.return_type.name:
+ self.errors.append(SemanticError(node.line, node.column,
+ f'In redefined method {self.Current_Method.name}, return type {self.Current_Method.return_type.name} '
+ f'is different from original return type {inherited_method.return_type.name}'))
+
+ except SemanticException:
+ pass
+
+
+ # Defino cada uno de los parametros de metodo
+ for pname, ptype in zip(self.Current_Method.param_names, self.Current_Method.param_types):
+ scope.define_variable(pname, ptype, node.line, node.column)
+
+ if pname.lower() == 'self':
+ self.errors.append(SemanticError(node.line, node.column,
+ '\'self\' cannot be the name of a formal parameter'))
+
+ # Chequeo consistencia en el cuerpo del metodo
+ self.visit(node.body, scope.create_child())
+
+ expr_type = node.body.static_type
+ return_type = self.Current_Method.return_type
+
+ # Chequeo consistencia entre el tipo de retorno definido y el tipo de retorno
+ # del cuerpo del metodo
+ if not expr_type.conforms_to(return_type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'Inferred return type {expr_type.name} of method {self.Current_Method.name} '
+ f'does not conform to declared return type {return_type.name}'))
+ node.static_type = return_type
+
+ @visitor.when(IfThenElseNode)
+ def visit(self, node : IfThenElseNode, scope : Scope):
+ # Chequeo consistencia en la condicion del if
+ self.visit(node.condition, scope.create_child())
+
+ condition_type = node.condition.static_type
+ # Chequeo que el tipo de la condicion sea booleano
+ if not condition_type.conforms_to(self.Bool_Type):
+ self.errors.append(TypeError(node.condition.line, node.condition.column,
+ 'Predicate of \'if\' does not have type Bool'))
+
+ # Chequeo consistencia en las expresiones del then y el else
+ self.visit(node.if_body, scope.create_child())
+ self.visit(node.else_body, scope.create_child())
+
+ if_type = node.if_body.static_type
+ else_type = node.else_body.static_type
+
+ node.static_type = if_type.type_union(else_type)
+
+ @visitor.when(WhileLoopNode)
+ def visit(self, node : WhileLoopNode, scope : Scope):
+ self.visit(node.condition, scope.create_child())
+ condition_type = node.condition.static_type
+
+ # Chequeo que la condicion sea de tipo booleano
+ if not condition_type.conforms_to(self.Bool_Type):
+ self.errors.append(TypeError(node.condition.line, node.condition.column,
+ 'Loop condition does not have type Bool'))
+
+ # Chequeo consistencias en el cuerpo del while
+ self.visit(node.body, scope.create_child())
+
+ node.static_type = self.Object_Type
+
+ @visitor.when(BlockNode)
+ def visit(self, node : BlockNode, scope : Scope):
+
+ # Chequeo consistencias en cada una de las instrucciones del cuerpo del bloque
+ for expr in node.expressions:
+ self.visit(expr, scope.create_child())
+
+ node.static_type = node.expressions[-1].static_type
+
+ @visitor.when(LetInNode)
+ def visit(self, node : LetInNode, scope : Scope):
+ for id, type, expr in node.let_body:
+
+ if id.lex.lower() == 'self':
+ self.errors.append(SemanticError(id.line, id.column,
+ '\'self\' cannot be bound in a \'let\' expression'))
+
+ # Por cada una de las declaraciones del let
+ try:
+ type = self.Context.get_type(type.lex)
+ except SemanticException as ex:
+ # Chequeo que el tipo exista
+ self.errors.append(TypeError(id.line, id.column,
+ f'Class {type.lex} of let-bound identifier {id.lex} is undefined'))
+ type = ErrorType()
+
+ # Si es Self_Type tomo el tipo correspondiente
+ type = self.Current_Type if isinstance(type, SelfType) else type
+
+ child = scope.create_child()
+ if expr:
+ # Chequeo consistencias en la declaracion y la compatibilidad de tipos
+ self.visit(expr, child)
+ if not expr.static_type.conforms_to(type):
+ self.errors.append(TypeError(id.line, id.column,
+ f'Inferred type {expr.static_type.name} of initialization of '
+ f'{id.lex} does not conform to identifier\'s declared type {type.name}'))
+
+ # Defino la variable
+ scope.define_variable(id.lex, type, node.line, node.column)
+
+ # Chequeo consistencias en el cuerpo del let in
+ self.visit(node.in_body, scope.create_child())
+ node.static_type = node.in_body.static_type
+
+ @visitor.when(CaseOfNode)
+ def visit(self, node : CaseOfNode, scope : Scope):
+ # Chequeo consistencias en el case
+ self.visit(node.expression, scope.create_child())
+
+ branchs = []
+
+ node.static_type = None
+ for id, type, expr in node.branches:
+ # Por cada instruccion en el cuerpo del case-of
+ try:
+ type = self.Context.get_type(type.lex)
+ except SemanticException as ex:
+ # Chequeo que el tipo exista
+ self.errors.append(TypeError(type.line, type.column,
+ f'Class {type.lex} of case branch is undefined'))
+ type = ErrorType()
+
+ # Chequeo que no sea un tipo especial
+ if isinstance(type, SelfType):
+ self.errors.append(SemanticError(id.line, id.column,
+ f'SELF_TYPE cannot be used as a case branch'))
+
+ child = scope.create_child()
+ # Declaro la variable y chequeo consistencias en la expresion
+ child.define_variable(id.lex, type, node.line, node.column)
+ self.visit(expr, child)
+
+ if type.name in branchs:
+ self.errors.append(SemanticError(id.line, id.column,
+ f'Duplicate branch {type.name} in case statement'))
+ branchs.append(type.name)
+
+ node.static_type = node.static_type.type_union(expr.static_type) if node.static_type else expr.static_type
+
+
+ @visitor.when(AssignNode)
+ def visit(self, node : AssignNode, scope : Scope):
+ # Chequeo consistencias en la expresion
+ self.visit(node.expression, scope.create_child())
+ expr_type = node.expression.static_type
+
+ if node.id.lex.lower() == 'self':
+ self.errors.append(SemanticError(node.line, node.column,
+ 'Cannot assign to \'self\''))
+
+ # Chequeo que la variable este declarada y que su tipo sea valido
+ if scope.is_defined(node.id.lex):
+ var_type = scope.find_variable(node.id.lex).type
+ if isinstance(var_type, SelfType):
+ var_type = self.Current_Type
+
+ if not expr_type.conforms_to(var_type):
+ self.errors.append(TypeError(node.expression.line, node.expression.column,
+ f'Inferred type {expr_type.name} of initialization of attribute {node.id.lex} '
+ f'does not conform to declared type {var_type.name}'))
+ else:
+ self.errors.append(NameError(node.line, node.column,
+ f'Undeclared identifier {node.id.lex}'))
+
+ node.static_type = expr_type
+
+ @visitor.when(NotNode)
+ def visit(self, node : NotNode, scope : Scope):
+ # Chequeo la consistencia de la expresion
+ self.visit(node.expression, scope.create_child())
+
+ # Chequeo que la expresion sea booleana
+ if not node.expression.static_type.conforms_to(self.Bool_Type):
+ self.errors.append(TypeError(node.expression.line, node.expression.column,
+ f'Argument of \'not\' has type {node.expression.static_type.name} instead of Bool'))
+
+ node.static_type = self.Bool_Type
+
+ @visitor.when(LessEqualNode)
+ def visit(self, node : LessEqualNode, scope : Scope):
+ # Chequeo la consistencia de ambos miembros
+ self.visit(node.left, scope.create_child())
+ self.visit(node.right, scope.create_child())
+ left_type = node.left.static_type
+ right_type = node.right.static_type
+
+ # Chequeo que ambos miembros posean tipo int
+ if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'non-Int arguments: {left_type.name} <= {right_type.name}'))
+
+ node.static_type = self.Bool_Type
+
+ @visitor.when(LessNode)
+ def visit(self, node: LessNode, scope: Scope):
+ # Chequeo la consistencia de ambos miembros
+ self.visit(node.left, scope.create_child())
+ self.visit(node.right, scope.create_child())
+ left_type = node.left.static_type
+ right_type = node.right.static_type
+
+ # Chequeo que ambos miembros posean tipo int
+ if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'non-Int arguments: {left_type.name} < {right_type.name}'))
+
+ node.static_type = self.Bool_Type
+
+ @visitor.when(EqualNode)
+ def visit(self, node: EqualNode, scope: Scope):
+ # Chequeo la consistencia de ambos miembros
+ self.visit(node.left, scope.create_child())
+ self.visit(node.right, scope.create_child())
+ left_type = node.left.static_type
+ right_type = node.right.static_type
+
+ # Chequeo que ambos miembros posean tipos comparables
+ if left_type.conforms_to(self.Int_Type) ^ right_type.conforms_to(self.Int_Type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'Illegal comparison with a basic type'))
+ elif left_type.conforms_to(self.String_Type) ^ right_type.conforms_to(self.String_Type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'Illegal comparison with a basic type'))
+ elif left_type.conforms_to(self.Bool_Type) ^ right_type.conforms_to(self.Bool_Type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'Illegal comparison with a basic type'))
+
+ node.static_type = self.Bool_Type
+
+ @visitor.when(ArithmeticNode)
+ def visit(self, node : ArithmeticNode, scope : Scope):
+ # Chequeo la consistencia de ambos miembros
+ self.visit(node.left, scope.create_child())
+ self.visit(node.right, scope.create_child())
+ left_type = node.left.static_type
+ right_type = node.right.static_type
+
+ # Chequeo que ambos miembros posean tipo int
+ if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'non-Int arguments: {left_type.name} and {right_type.name}'))
+
+ node.static_type = self.Int_Type
+
+ @visitor.when(IsVoidNode)
+ def visit(self, node : IsVoidNode, scope : Scope):
+ # Chequeo la consistencia de la expresion
+ self.visit(node.expression, scope.create_child())
+ node.static_type = self.Bool_Type
+
+ @visitor.when(ComplementNode)
+ def visit(self, node : ComplementNode, scope : Scope):
+ # Chequeo la consistencia de la expresion
+ self.visit(node.expression, scope.create_child())
+
+ # Chequeo que la expresion sea de tipo booleana
+ if not node.expression.static_type.conforms_to(self.Int_Type):
+ self.errors.append(TypeError(node.expression.line, node.expression.column,
+ f'Argument of \'~\' has type {node.expression.static_type.name} instead of Int'))
+
+ node.static_type = self.Int_Type
+
+ @visitor.when(FunctionCallNode)
+ def visit(self, node : FunctionCallNode, scope : Scope):
+ # Chequeo la consistencia de la expresion a la cual se le pide la funcion
+ self.visit(node.obj, scope.create_child())
+ obj_type = node.obj.static_type
+
+ try:
+ if node.type:
+ # Chequeo que el tipo exista
+ try:
+ node_type = self.Context.get_type(node.type.lex)
+ except SemanticException as ex:
+ self.errors.append(TypeError(node.line, node.column,
+ f'Class {node.type.lex} not defined'))
+ node_type = ErrorType()
+
+ # Chequeo que el tipo no sea un tipo especial
+ if isinstance(node_type, SelfType):
+ self.errors.append(TypeError(node.line, node.column,
+ 'SELF_TYPE cannot be used in a dispatch'))
+
+ # Chequeo que los tipos sean compatibles
+ if not obj_type.conforms_to(node_type):
+ self.errors.append(TypeError(node.line, node.column,
+ f'Expression type {obj_type.name} does not conform '
+ f'to declared static dispatch type {node_type.name}'))
+
+ obj_type = node_type
+
+
+ obj_method = obj_type.get_method(node.id.lex)
+ return_type = obj_type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type
+
+ except SemanticException as ex:
+ self.errors.append(AttributeError(node.id.line, node.id.column,
+ f'Dispatch to undefined method {node.id.lex}'))
+ return_type = ErrorType()
+ obj_method = None
+
+ # Chequeo consistencias en los argumentos con los que se llama al metodo
+ for arg in node.args:
+ self.visit(arg, scope.create_child())
+
+ if obj_method and len(node.args) == len(obj_method.param_types):
+ for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names):
+ if not arg.static_type.conforms_to(param_type):
+ # Chequeo compatibilidad de tipos entre los argumentos
+ self.errors.append(TypeError(arg.line, arg.column,
+ f'In call of method {obj_method.name}, type {arg.static_type.name} of '
+ f'parameter {param_name} does not conform to declared type {param_type.name}'))
+
+ elif obj_method:
+ # Chequeo que la cantidad de argumentos sea igual a las solicitadas por el metodo
+ self.errors.append(SemanticError(node.id.line, node.id.column,
+ f'Method {obj_method.name} called with wrong number of arguments'))
+
+ node.static_type = return_type
+
+
+ @visitor.when(MemberCallNode)
+ def visit(self, node : MemberCallNode, scope : Scope):
+ # Chequeo que el metodo exista en el tipo actual
+ try:
+ obj_method = self.Current_Type.get_method(node.id.lex)
+ return_type = self.Current_Type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type
+ except SemanticException as ex:
+ self.errors.append(AttributeError(node.id.line, node.id.column,
+ f'Dispatch to undefined method {node.id.lex}'))
+ obj_method = None
+ return_type = ErrorType()
+
+ # Chequeo la consistencia en los argumentos
+ for arg in node.args:
+ self.visit(arg, scope.create_child())
+
+ if obj_method and len(node.args) == len(obj_method.param_types):
+ # Chequeo la compatibiidad entre los tipos de los argumentos
+ for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names):
+ if not arg.static_type.conforms_to(param_type):
+ self.errors.append(TypeError(arg.line, arg.column,
+ f'In call of method {obj_method.name}, type {arg.static_type.name} of '
+ f'parameter {param_name} does not conform to declared type {param_type.name}'))
+
+ elif obj_method:
+ # Chequeo que la cantidad de argumentos coincida con los que requiere el metodo
+ self.errors.append(SemanticError(node.id.line, node.id.column,
+ f'Method {obj_method.name} called with wrong number of arguments'))
+
+ node.static_type = return_type
+
+ @visitor.when(NewNode)
+ def visit(self, node : NewNode, scope : Scope):
+ # Chequeo que el tipo exista
+ try:
+ type = self.Context.get_type(node.type.lex)
+ except SemanticException as ex:
+ self.errors.append(TypeError(node.type.line, node.type.column,
+ f'\'new\' used with undeclared class {node.type.lex}'))
+ type = ErrorType()
+
+ node.static_type = type
+
+ @visitor.when(IntegerNode)
+ def visit(self, node : IntegerNode, scope : Scope):
+ node.static_type = self.Int_Type
+
+ @visitor.when(StringNode)
+ def visit(self, node: StringNode, scope: Scope):
+ node.static_type = self.String_Type
+
+ @visitor.when(BoolNode)
+ def visit(self, node: BoolNode, scope: Scope):
+ node.static_type = self.Bool_Type
+
+ @visitor.when(IdNode)
+ def visit(self, node: IdNode, scope: Scope):
+ # Chequeo que la variable exista
+ if scope.is_defined(node.token.lex):
+ node.static_type = scope.find_variable(node.token.lex).type
+ else:
+ self.errors.append(NameError(node.line, node.column,
+ f'Undeclared identifier {node.token.lex}'))
+ node.static_type = ErrorType()
diff --git a/src/core/semantic/TypeCollector.py b/src/core/semantic/TypeCollector.py
new file mode 100644
index 000000000..bde0032a2
--- /dev/null
+++ b/src/core/semantic/TypeCollector.py
@@ -0,0 +1,38 @@
+from core.tools import visitor
+from core.tools.Semantic import *
+from core.parser.Parser import ProgramNode, ClassDeclarationNode
+from core.tools.Errors import SemanticError
+
+# Visitor encargado de coleccionar los nombres de las clases que se definen
+# en el codigo del programa COOL, chequea ademas que no se redeclaren e
+# incluye los tipos builtin dentro del contexto
+
+class Type_Collector:
+ def __init__(self):
+ self.errors = []
+
+ self.Context = Context()
+
+ self.Context.add_type(SelfType())
+
+ self.Context.create_type('Object', 0, 0)
+ self.Context.create_type('String', 0, 0)
+ self.Context.create_type('IO', 0, 0)
+ self.Context.create_type('Int', 0, 0)
+ self.Context.create_type('Bool', 0, 0)
+
+ @visitor.on('node')
+ def visit(self, node):
+ pass
+
+ @visitor.when(ProgramNode)
+ def visit(self, node : ProgramNode):
+ for type in node.declarations:
+ self.visit(type)
+
+ @visitor.when(ClassDeclarationNode)
+ def visit(self, node : ClassDeclarationNode):
+ try:
+ self.Context.create_type(node.id.lex, node.line, node.column)
+ except SemanticException as ex:
+ self.errors.append(SemanticError(node.line, node.column, ex.text))
diff --git a/src/core/tools/Automata.py b/src/core/tools/Automata.py
new file mode 100644
index 000000000..2bdadd2d1
--- /dev/null
+++ b/src/core/tools/Automata.py
@@ -0,0 +1,210 @@
+try:
+ import pydot
+except:
+ pass
+
+# Esta es la clase dada en clases practicas
+# para representar un DFA,
+# o cualkier grafo en general
+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, dir = 'LR'):
+ G = pydot.Dot(rankdir=dir, 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 = f'\'{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/core/tools/Errors.py b/src/core/tools/Errors.py
new file mode 100644
index 000000000..4d1b8e0f1
--- /dev/null
+++ b/src/core/tools/Errors.py
@@ -0,0 +1,66 @@
+
+class Error:
+ def __init__(self, line = None, column = None, text = ''):
+ self.line = line
+ self.column = column
+ self.text = text
+ def __str__(self):
+ raise NotImplementedError()
+ def __repr__(self):
+ raise NotImplementedError()
+
+class CompilerError(Error):
+ def __str__(self):
+ return f'{self.line, self.column} - ' \
+ f'CompilerError: {self.text}'
+ def __repr__(self):
+ return f'{self.line, self.column} - ' \
+ f'CompilerError: {self.text}'
+
+class LexicographicError(Error):
+ def __str__(self):
+ return f'{self.line, self.column} - ' \
+ f'LexicographicError: {self.text}'
+ def __repr__(self):
+ return f'{self.line, self.column} - ' \
+ f'LexicographicError: {self.text}'
+
+class SyntacticError(Error):
+ def __str__(self):
+ return f'{self.line, self.column} - ' \
+ f'SyntacticError: ERROR at or near {self.text}'
+ def __repr__(self):
+ return f'{self.line, self.column} - ' \
+ f'SyntacticError: ERROR at or near {self.text}'
+
+class SemanticError(Error):
+ def __str__(self):
+ return f'{self.line, self.column} - ' \
+ f'SemanticError: {self.text}'
+ def __repr__(self):
+ return f'{self.line, self.column} - ' \
+ f'SemanticError: {self.text}'
+
+class TypeError(Error):
+ def __str__(self):
+ return f'{self.line, self.column} - ' \
+ f'TypeError: {self.text}'
+ def __repr__(self):
+ return f'{self.line, self.column} - ' \
+ f'TypeError: {self.text}'
+
+class NameError(Error):
+ def __str__(self):
+ return f'{self.line, self.column} - ' \
+ f'NameError: {self.text}'
+ def __repr__(self):
+ return f'{self.line, self.column} - ' \
+ f'NameError: {self.text}'
+
+class AttributeError(Error):
+ def __str__(self):
+ return f'{self.line, self.column} - ' \
+ f'AttributeError: {self.text}'
+ def __repr__(self):
+ return f'{self.line, self.column} - ' \
+ f'AttributeError: {self.text}'
\ No newline at end of file
diff --git a/src/core/tools/Evaluation.py b/src/core/tools/Evaluation.py
new file mode 100644
index 000000000..52cbd4958
--- /dev/null
+++ b/src/core/tools/Evaluation.py
@@ -0,0 +1,44 @@
+from .Parsing import ShiftReduceParser
+
+'''
+Este metodo retorna el ast dado el conjunto de producciones,
+las operaciones y los tokens
+'''
+def evaluate_reverse_parse(right_parse, operations, tokens):
+ if not right_parse or not operations or not tokens:
+ return
+
+ right_parse = iter(right_parse)
+ tokens = iter(tokens)
+ stack = []
+ for operation in operations:
+ # Si hay que hacer un shift, pasemos a analizar
+ # el proximo token e incluyamoslo en la pila
+ if operation == ShiftReduceParser.SHIFT:
+ token = next(tokens)
+ stack.append(token)
+ # Si hay que hacer un reduce, tomamos los elementos
+ # necesarios de la pila y los sustituimos por el nonterminal
+ # correspondiente, el cual incluimos en la pila
+ 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 syntheticed attributes.'
+ rule = attributes[0]
+
+ if len(body):
+ syntheticed = [None] + stack[-len(body):]
+ value = rule(None, syntheticed)
+ stack[-len(body):] = [value]
+ else:
+ stack.append(rule(None, None))
+ else:
+ raise Exception('Invalid action!!!')
+
+ # La pila debe terminar con el program node
+ # correspondiente a la raiz del ast, todos los
+ # tokens deben haber sido analizados
+ assert len(stack) == 1
+ assert next(tokens).token_type == '$'#######estaba EOF, creo que tiene que ver con lo que cambie al poner el tokentype en el lexer
+ return stack[0]
\ No newline at end of file
diff --git a/src/core/tools/FirstsAndFollows.py b/src/core/tools/FirstsAndFollows.py
new file mode 100644
index 000000000..1a8e05c61
--- /dev/null
+++ b/src/core/tools/FirstsAndFollows.py
@@ -0,0 +1,128 @@
+from .Utils import ContainerSet
+
+'''
+Dada una forma oracional alpha computa sus firsts
+'''
+def compute_local_first(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()
+ return first_alpha
+
+ # 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)
+ for symbol in alpha:
+ first_alpha.update(firsts[symbol])
+ if not firsts[symbol].contains_epsilon:
+ break
+ else:
+ first_alpha.set_epsilon()
+
+ return first_alpha
+
+
+'''
+Computa los firsts de todos los simbolos de la gramatica
+'''
+def compute_firsts(G):
+ firsts = {}
+ change = True
+
+ # Los firsts de los terminales son ellos mismos
+ for terminal in G.terminals:
+ firsts[terminal] = ContainerSet(terminal)
+
+ # Inicializa los firsts de los noterminales como un conjunto vacio
+ for nonterminal in G.nonTerminals:
+ firsts[nonterminal] = ContainerSet()
+
+ # Metodo de punto fijo, mientras halla algun cambio en los firsts
+ # de algun simbolo, itera por todas las producciones recalculando los firsts
+ # del noterminal correspondiente
+ while change:
+ change = False
+
+ # P: X -> alpha
+ for production in G.Productions:
+ X = production.Left
+ alpha = production.Right
+
+ # get current First(X)
+ first_X = firsts[X]
+
+ # init First(alpha)
+ try:
+ first_alpha = firsts[alpha]
+ except KeyError:
+ first_alpha = firsts[alpha] = ContainerSet()
+
+ # CurrentFirst(alpha)
+ local_first = 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)
+
+ return firsts
+
+'''
+Computa los follows de cada noterminal de la gramatica
+'''
+from itertools import islice
+def compute_follows(G, firsts):
+ follows = {}
+ change = True
+
+ local_firsts = {}
+
+ # Inicializa los follows de los noterminales como un conjunto vacio
+ for nonterminal in G.nonTerminals:
+ follows[nonterminal] = ContainerSet()
+ # EOF pertenece a los follows del simbolo inicial
+ follows[G.startSymbol] = ContainerSet(G.EOF)
+
+ # Metodo de punto fijo, mientras haya cambios en los follows
+ # de algun noterminal, itera por todas las producciones y recalcula
+ # los follows de cada noterminal
+ while change:
+ change = False
+
+ # P: X -> alpha
+ for production in G.Productions:
+ X = production.Left
+ alpha = production.Right
+
+ follow_X = follows[X]
+
+ # Si la produccion actual es de la forma X -> v Y w
+ # donde v y w son formas oracionales entonces cada elemento
+ # que pertenece al first de w (excepto epsilon) pertenece tambien al follow
+ # de Y. Si ademas w ->* epsilon entonces los follows de X pertenecen
+ # al follows de Y.
+ # 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:
+ try:
+ first_beta = local_firsts[alpha, i]
+ except KeyError:
+ first_beta = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None))
+
+ change |= follows[symbol].update(first_beta)
+
+ if first_beta.contains_epsilon:
+ change |= follows[symbol].update(follow_X)
+
+ return follows
+
+
diff --git a/src/core/tools/ParserLR1.py b/src/core/tools/ParserLR1.py
new file mode 100644
index 000000000..1829ecca8
--- /dev/null
+++ b/src/core/tools/ParserLR1.py
@@ -0,0 +1,185 @@
+from .Pycompiler import Item
+from .Utils import ContainerSet
+from .FirstsAndFollows import compute_firsts, compute_local_first
+from .Automata import State
+from .Parsing import ShiftReduceParser
+
+'''
+Recibe un item LR(1) y devuelve el conjunto de items que
+sugiere incluir (directamente) debido a la presencia de
+un . delante de un no terminal.
+expand("𝑌→𝛼.𝑋𝛿,𝑐") = { "𝑋→.𝛽,𝑏" | 𝑏∈𝐹𝑖𝑟𝑠𝑡(𝛿𝑐) }
+'''
+def expand(item, firsts):
+ next_symbol = item.NextSymbol
+ if next_symbol is None or not next_symbol.IsNonTerminal:
+ return []
+
+ lookaheads = ContainerSet()
+ # (Compute lookahead for child items)
+ # Preview retorna los elementos que se encuantran detras del punto
+ # en el item mas sus lookaheads
+ for prev in item.Preview():
+ lookaheads.update(compute_local_first(firsts, prev))
+
+ assert not lookaheads.contains_epsilon
+
+ # (Build and return child items)
+ return [Item(x, 0, lookaheads) for x in next_symbol.productions]
+
+'''
+Recibe un conjunto de items LR(1) y devuelve el mismo conjunto pero
+en el que los items con mismo centro están unidos (se combinan los lookahead).
+'''
+def compress(items):
+ centers = {}
+
+ for item in items:
+ center = item.Center()
+ try:
+ lookaheads = centers[center]
+ except KeyError:
+ centers[center] = lookaheads = set()
+ lookaheads.update(item.lookaheads)
+
+ return {Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items()}
+
+'''
+Computa la clausura de un conjunto de items
+'''
+# Metodo de punto fijo, mientras haya cambios en el conjunto de items,
+# itero por todos los items X -> v.Yw,p, donde X y Y son noterminales y
+# v y w son formas oracionales, e incluyo los items de la forma
+# Y -> Z,b donde b ∈ First(wp), para todas las producciones de Y.
+# Dischos items se calculan mediantes el metodo expand.
+# 𝐶𝐿(𝐼) = 𝐼 ∪ { 𝑋→.𝛽,𝑏 }
+# tales que 𝑌→𝛼.𝑋𝛿,𝑐 ∈ 𝐶𝐿(𝐼) y 𝑏 ∈ 𝐹𝑖𝑟𝑠𝑡(𝛿𝑐)
+
+def closure_lr1(items, firsts):
+ closure = ContainerSet(*items)
+
+ changed = True
+ while changed:
+ changed = False
+
+ new_items = ContainerSet()
+ for item in closure:
+ new_items.extend(expand(item, firsts))
+
+ changed = closure.update(new_items)
+
+ return compress(closure)
+
+'''
+Recibe como parámetro un conjunto de items y un símbolo, y devuelve el
+conjunto goto(items, symbol). El método permite setear el parámentro
+just_kernel=True para calcular solamente el conjunto de items kernels
+en lugar de todo el conjunto de items. En caso contrario, se debe proveer
+el conjunto con los firsts de la gramática, puesto que serán usados al
+calcular la clausura.
+'''
+# 𝐺𝑜𝑡𝑜(𝐼,𝑋) = 𝐶𝐿({ 𝑌→𝛼𝑋.𝛽,𝑐 | 𝑌→𝛼.𝑋𝛽,𝑐 ∈ 𝐼})
+def goto_lr1(items, symbol, firsts=None, just_kernel=False):
+ assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`'
+ items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol)
+ return items if just_kernel else closure_lr1(items, firsts)
+
+'''
+Computa el automata LR1 correspondiente a la gramatica
+'''
+# El estado inicial es la clausura del item S' -> .S, $.
+# Todos los estados son finales.
+# Las transiciones ocurren con terminales y no terminales.
+# La función de transición está dada por la función goto.
+# f(Ii, c) = Goto(Ii, c)
+def build_LR1_automaton(G):
+ assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented'
+
+ firsts = compute_firsts(G)
+ firsts[G.EOF] = ContainerSet(G.EOF)
+
+ start_production = G.startSymbol.productions[0]
+ start_item = Item(start_production, 0, lookaheads=(G.EOF,))
+ start = frozenset([start_item])
+
+ # El estado inicial es la clausura del item S' -> .S, $
+ closure = closure_lr1(start, firsts)
+ automaton = State(frozenset(closure), True)
+
+ pending = [start]
+ visited = {start: automaton}
+
+ # BFS para construir el automata
+ # Mientras hallan estados pendientes
+ while pending:
+ # Tomo el siguiente estado a analizar
+ current = pending.pop()
+ current_state = visited[current]
+
+ # Itero por cada simbolo de la gramatica
+ for symbol in G.terminals + G.nonTerminals:
+ # Chequeo si el estado actual posee transicion con ese simbolo a algun estado x
+ next_state_key = goto_lr1(current_state.state, symbol, just_kernel=True)
+
+ # Si no la posee, continuo al siguiente simbolo
+ if not next_state_key:
+ continue
+ try:
+ next_state = visited[next_state_key]
+ except KeyError:
+ # Si el estado x no ha sido visto por el bfs, lo incluyo
+ # en la lista de pending
+ next_state_items = goto_lr1(current_state.state, symbol, firsts)
+ next_state = State(frozenset(next_state_items), True)
+ pending.append(next_state_key)
+ visited[next_state_key] = next_state
+
+ # incluto la transicion del estado actual a x con el simbolo actual
+ current_state.add_transition(symbol.Name, next_state)
+
+ #automaton.set_formatter(multiline_formatter)
+ return automaton
+
+
+# Recordar que la diferencia entre los parsers Shift-Reduce es solo la forma
+# en que se llenan las tablas ACTION y GOTO
+class LR1Parser(ShiftReduceParser):
+ def _build_parsing_table(self):
+ G = self.G.AugmentedGrammar(True)
+
+ automaton = build_LR1_automaton(G)
+ 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(...)`)
+ if item.IsReduceItem:
+ if item.production.Left == G.startSymbol:
+ # Sea 𝐼𝑖 el estado que contiene el item "𝑆′→𝑆.,$" (𝑆′ distinguido).
+ # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,$]=‘𝑂𝐾‘
+ self._register(self.action, (idx, G.EOF.Name), (self.OK, None))
+ else:
+ # Sea "𝑋→𝛼.,𝑠" un item del estado 𝐼𝑖.
+ # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑠]=‘𝑅𝑘‘ (producción k es 𝑋→𝛼)
+ for c in item.lookaheads:
+ self._register(self.action, (idx, c.Name), (self.REDUCE, item.production))
+ else:
+ next_symbol = item.NextSymbol
+ try:
+ next_state = node[next_symbol.Name][0]
+ if next_symbol.IsNonTerminal:
+ # Sea "𝑋→𝛼.𝑌𝜔,𝑠" item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑌)=𝐼𝑗.
+ # Entonces 𝐺𝑂𝑇𝑂[𝐼𝑖,𝑌]=𝑗
+ self._register(self.goto, (idx, next_symbol.Name), next_state.idx)
+ else:
+ # Sea "𝑋→𝛼.𝑐𝜔,𝑠" un item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑐)=𝐼𝑗.
+ # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑐]=‘𝑆𝑗‘
+ self._register(self.action, (idx, next_symbol.Name), (self.SHIFT, next_state.idx))
+ except KeyError:
+ print(f'Node: {node} without transition with symbol {next_symbol}')
+ return
+
+
diff --git a/src/core/tools/Parsing.py b/src/core/tools/Parsing.py
new file mode 100644
index 000000000..17eae0e2d
--- /dev/null
+++ b/src/core/tools/Parsing.py
@@ -0,0 +1,95 @@
+from .Errors import *
+
+# Clase base para los parsers Shift-Reduce
+class ShiftReduceParser:
+ SHIFT = 'SHIFT'
+ REDUCE = 'REDUCE'
+ OK = 'OK'
+
+ def __init__(self, G, verbose=False):
+ self.G = G
+ self.verbose = verbose
+ self.action = {}
+ self.goto = {}
+ self.HasConflict = False
+ self.errors = []
+ self._build_parsing_table()
+
+ def _build_parsing_table(self):
+ raise NotImplementedError()
+
+ '''
+ Retorna las producciones y operaciones necesarias para el
+ proceso de parsing del string w.
+ '''
+ def __call__(self, w):
+ stack = [0]
+ cursor = 0
+ output = []
+ operations = []
+ self.errors = []
+
+ while True:
+ # Estado del automata en el que me encuentro
+ state = stack[-1]
+ # Siguiente simbolo a analizar
+ lookahead = w[cursor].token_type
+ if self.verbose: print(stack, '<---||--->', w[cursor:])
+ #(Detect error)
+ try:
+ action, tag = self.action[state, lookahead][0]
+ except KeyError:
+ # Si no existe transicion desde el estado actual con el simbolo
+ # correspondiente es porque ha ocurrido un error
+ lookahead = w[cursor]
+ line = lookahead.line
+ column = lookahead.column
+ if lookahead.lex == '$':
+ self.errors.append(SyntacticError(0, 0, 'EOF'))
+ else:
+ self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"'))
+ return None, None
+
+ # Si la accion es Shift, incluyo en la pila el simbolo actual y
+ # el estado al que necesito moverme
+ if action == self.SHIFT:
+ stack.append(lookahead)
+ stack.append(tag)
+ operations.append(self.SHIFT)
+ cursor += 1
+ # Si la accion es Reduce, saco de la pila los simbolos correspondientes
+ # a la produccion q debo reducir
+ elif action == self.REDUCE:
+ output.append(tag)
+ for i in range(len(tag.Right)):
+ stack.pop()
+ assert stack[-1] == tag.Right[-i - 1].Name, 'Symbol does not match'
+ stack.pop()
+ # Tomo el estado al que debo moverme
+ index = self.goto[stack[-1], tag.Left.Name][0]
+ # Incluyo en la pila el simbolo reducido y el estado al
+ # que necesito moverme
+ stack.append(tag.Left.Name)
+ stack.append(index)
+ operations.append(self.REDUCE)
+ #(OK case)
+ elif action == self.OK:
+ return output, operations
+ #(Invalid case)
+ else:
+ lookahead = w[cursor]
+ line = lookahead.line
+ column = lookahead.column
+ if lookahead.lex == '$':
+ self.errors.append(SyntacticError(0, 0, 'EOF'))
+ else:
+ self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"'))
+ return None, None
+
+ def _register(self, table, key, value):
+ if key not in table:
+ table[key] = [value]
+ elif not any(i for i in table[key] if i == value):
+ self.HasConflict = True
+ table[key].append(value)
+
diff --git a/src/core/tools/Pycompiler.py b/src/core/tools/Pycompiler.py
new file mode 100644
index 000000000..74ad3bc0d
--- /dev/null
+++ b/src/core/tools/Pycompiler.py
@@ -0,0 +1,560 @@
+import json
+
+#Clase base para los simbolos de la gramatica, terminales y noterminales
+class Symbol(object):
+
+ def __init__(self, name, grammar):
+ self.Name = name
+ self.Grammar = grammar
+
+ def __str__(self):
+ return self.Name
+
+ def __repr__(self):
+ return self.Name
+
+ def __add__(self, other):
+ if isinstance(other, Symbol):
+ return Sentence(self, other)
+
+ if isinstance(other, Sentence):
+ return Sentence(*((self,) + other._symbols))
+
+ 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)
+
+ def __add__(self, other):
+ if isinstance(other, Symbol):
+ return Sentence(self, other)
+
+ if isinstance(other, Sentence):
+ return Sentence(self, *(other._symbols))
+
+ if other:
+ raise TypeError(other)
+
+ def __iter__(self):
+ yield self
+
+ @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)
+
+ def __iter__(self):
+ yield self
+
+ def __add__(self, other):
+ if isinstance(other, Symbol):
+ return Sentence(self, other)
+
+ if isinstance(other, Sentence):
+ return Sentence(self, *(other._symbols))
+
+ if other:
+ raise TypeError(other)
+
+ @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))
+
+ if other:
+ raise TypeError(other)
+
+ def __or__(self, other):
+ if isinstance(other, Sentence):
+ return SentenceList(self, other)
+
+ if isinstance(other, Symbol):
+ return SentenceList(self, Sentence(other))
+
+ if isinstance(other, SentenceList):
+ return SentenceList(self, *(other._sentences))
+
+ if 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)
+
+ if isinstance(other, SentenceList):
+ return SentenceList(self._sentences + other._sentences)
+
+class Epsilon(Terminal, Sentence):
+
+ def __init__(self, grammar):
+ super().__init__('epsilon', grammar)
+
+
+ def __str__(self):
+ return "ε"
+
+ def __repr__(self):
+ return 'epsilon'
+
+ def __iter__(self):
+ yield from ()
+
+ def __len__(self):
+ return 0
+
+ def __add__(self, other):
+ return other
+
+ def __eq__(self, other):
+ return isinstance(other, (Epsilon,))
+
+ def __hash__(self):
+ return hash("")
+
+ @property
+ def IsEpsilon(self):
+ return True
+
+class Production(object):
+
+ def __init__(self, nonTerminal, sentence):
+
+ self.Left = nonTerminal
+ self.Right = sentence
+
+ def __str__(self):
+
+ return '%s --> %s' % (self.Left, self.Right)
+
+ def __repr__(self):
+ return '%s -> %s' % (self.Left, self.Right)
+
+ def __iter__(self):
+ yield self.Left
+ yield self.Right
+
+ def __eq__(self, other):
+ return isinstance(other, Production) and self.Left == other.Left and self.Right == other.Right
+
+ def __hash__(self):
+ return hash((self.Left, self.Right))
+
+ @property
+ def IsEpsilon(self):
+ return self.Right.IsEpsilon
+
+class AttributeProduction(Production):
+
+ def __init__(self, nonTerminal, sentence, attributes):
+ if not isinstance(sentence, Sentence) and isinstance(sentence, Symbol):
+ sentence = Sentence(sentence)
+ super(AttributeProduction, self).__init__(nonTerminal, sentence)
+
+ self.attributes = attributes
+
+ def __str__(self):
+ return '%s := %s' % (self.Left, self.Right)
+
+ def __repr__(self):
+ return '%s -> %s' % (self.Left, self.Right)
+
+ def __iter__(self):
+ yield self.Left
+ yield self.Right
+
+
+ @property
+ def IsEpsilon(self):
+ return self.Right.IsEpsilon
+
+ # sintetizar en ingles??????, pending aggrement
+ def syntetice(self):
+ pass
+
+class Grammar():
+
+ def __init__(self):
+
+ self.Productions = []
+ self.nonTerminals = []
+ self.terminals = []
+ self.startSymbol = None
+ # production type
+ self.pType = None
+ self.Epsilon = Epsilon(self)
+ self.EOF = EOF(self)
+
+ self.symbDict = { '$': self.EOF }
+
+ def NonTerminal(self, name, startSymbol = False):
+
+ name = name.strip()
+ if not name:
+ raise Exception("Empty name")
+
+ term = NonTerminal(name,self)
+
+ if startSymbol:
+
+ if self.startSymbol is None:
+ self.startSymbol = term
+ else:
+ raise Exception("Cannot define more than one start symbol.")
+
+ self.nonTerminals.append(term)
+ self.symbDict[name] = term
+ return term
+
+ def NonTerminals(self, names):
+
+ ans = tuple((self.NonTerminal(x) for x in names.strip().split()))
+
+ return ans
+
+
+ def Add_Production(self, production):
+
+ if len(self.Productions) == 0:
+ self.pType = type(production)
+
+ assert type(production) == self.pType, "The Productions most be of only 1 type."
+
+ production.Left.productions.append(production)
+ self.Productions.append(production)
+
+
+ def Terminal(self, name):
+
+ name = name.strip()
+ if not name:
+ raise Exception("Empty name")
+
+ term = Terminal(name, self)
+ self.terminals.append(term)
+ self.symbDict[name] = term
+ return term
+
+ def Terminals(self, names):
+
+ ans = tuple((self.Terminal(x) for x in names.strip().split()))
+
+ return ans
+
+
+ def __str__(self):
+
+ mul = '%s, '
+
+ ans = 'Non-Terminals:\n\t'
+
+ nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n'
+
+ ans += nonterminals % tuple(self.nonTerminals)
+
+ ans += 'Terminals:\n\t'
+
+ terminals = mul * (len(self.terminals)-1) + '%s\n'
+
+ ans += terminals % tuple(self.terminals)
+
+ ans += 'Productions:\n\t'
+
+ ans += str(self.Productions)
+
+ return ans
+
+ def __getitem__(self, name):
+ try:
+ return self.symbDict[name]
+ except KeyError:
+ return None
+
+ @property
+ def to_json(self):
+
+ productions = []
+
+ for p in self.Productions:
+ head = p.Left.Name
+
+ body = []
+
+ for s in p.Right:
+ body.append(s.Name)
+
+ productions.append({'Head':head, 'Body':body})
+
+ d={'NonTerminals':[symb.Name for symb in self.nonTerminals], 'Terminals': [symb.Name for symb in self.terminals],\
+ 'Productions':productions}
+
+ # [{'Head':p.Left.Name, "Body": [s.Name for s in p.Right]} for p in self.Productions]
+ return json.dumps(d)
+
+ @staticmethod
+ def from_json(data):
+ data = json.loads(data)
+
+ G = Grammar()
+ dic = {'epsilon':G.Epsilon}
+
+ for term in data['Terminals']:
+ dic[term] = G.Terminal(term)
+
+ for noTerm in data['NonTerminals']:
+ dic[noTerm] = G.NonTerminal(noTerm)
+
+ for p in data['Productions']:
+ head = p['Head']
+ dic[head] %= Sentence(*[dic[term] for term in p['Body']])
+
+ return G
+
+ def copy(self):
+ G = Grammar()
+ G.pType = self.pType
+ for terminal in self.terminals:
+ G.Terminal(terminal.Name)
+ for nonterminal in self.nonTerminals:
+ G.NonTerminal(nonterminal.Name, nonterminal == self.startSymbol)
+ for prod in self.Productions:
+ left = G.symbDict[prod.Left.Name]
+ if prod.IsEpsilon:
+ if self.pType is AttributeProduction:
+ G.Add_Production(AttributeProduction(left, G.Epsilon, prod.attributes))
+ else:
+ G.Add_Production(Production(left, G.Epsilon))
+ else:
+ right = Sentence()
+ for symbol in prod.Right:
+ right = right + G.symbDict[symbol.Name]
+ if self.pType is AttributeProduction:
+ G.Add_Production(AttributeProduction(left, right, prod.attributes))
+ else:
+ G.Add_Production(Production(left, right))
+ 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/core/tools/Semantic.py b/src/core/tools/Semantic.py
new file mode 100644
index 000000000..10006a697
--- /dev/null
+++ b/src/core/tools/Semantic.py
@@ -0,0 +1,296 @@
+# Aqui estan las clases necesarias para representar los tipos del programa
+
+# Necesario para enviar errores semanticos
+class SemanticException(Exception):
+ @property
+ def text(self):
+ return self.args[0]
+
+
+# Representa un atributo en un tipo del programa
+class Attribute:
+ def __init__(self, name : str, type, line : int, column : int, expression = None):
+ self.name = name
+ self.type = type
+ self.line = line
+ self.column = column
+
+ def __str__(self):
+ return f'[attrib] {self.name}: {self.type.name};'
+
+ def __repr__(self):
+ return str(self)
+
+ def __lt__(self, other):
+ return self.name < other.name
+
+# Representa un metodo en un tipo del programa
+class Method:
+ def __init__(self, name : str, param_names : list, param_types : list, return_type):
+ self.name = name
+ self.param_names = param_names
+ self.param_types = param_types
+ self.return_type = return_type
+
+ def __str__(self):
+ params = ', '.join(f'{n}: {t.name}' for n, t in zip(self.param_names, self.param_types))
+ return f'[method] {self.name}({params}): {self.return_type.name};'
+
+ def __eq__(self, other):
+ return other.name == self.name and \
+ other.return_type == self.return_type and \
+ other.param_types == self.param_types
+
+#Clase base para representar los tipos del programa
+class Type:
+ def __init__(self, name:str, line, column, sealed=False):
+ self.name = name
+ self.attributes = []
+ self.methods = {}
+ self.parent = None
+ # Profundidad en el arbol de herencia
+ self.depth = 0
+ # True si la clase esta sellada (no se puede heredar de ella)
+ self.sealed = sealed
+ self.line = line
+ self.column = column
+
+ def set_parent(self, parent):
+ # Si el padre esta definido o es un tipo sellado entonces hay un error semantico
+ if self.parent is not None:
+ raise SemanticException(f'Parent type is already set for {self.name}.')
+ if parent.sealed:
+ raise SemanticException(f'Cannot inherit from sealed type {parent.name}')
+ self.parent = parent
+
+ # Retorna el tipo en el arbol de herencia de mayor profundidad
+ # que es padre comun de ambos tipos
+ def type_union(self, other):
+ if self is VoidType or other is VoidType:
+ return VoidType()
+
+ if self is ErrorType or other is ErrorType:
+ return ErrorType()
+
+ x, y = self, other
+ while x.depth > y.depth:
+ x = x.parent
+ while y.depth > x.depth:
+ y = y.parent
+
+ while x and x != y:
+ x = x.parent
+ y = y.parent
+
+ return x
+
+ # Retorna el atributo del tipo actual con el nombre correspondiente
+ def get_attribute(self, name:str):
+ try:
+ return next(attr for attr in self.attributes if attr.name == name)
+ except StopIteration:
+ if self.parent is None:
+ raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.')
+ try:
+ return self.parent.get_attribute(name)
+ except SemanticException:
+ raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.')
+
+ # Define un atributo para el tipo actual con el nombre y el tipo correspondiente
+ def define_attribute(self, name:str, typex, line, column):
+ try:
+ attribute = next(attr for attr in self.attributes if attr.name == name)
+ except StopIteration:
+ attribute = Attribute(name, typex, line, column)
+ self.attributes.append(attribute)
+ return attribute
+ else:
+ raise SemanticException(f'Attribute "{name}" is multiply defined in class.')
+
+ # Retorna el metodo del tipo actual con el nombre correspondiente
+ def get_method(self, name:str):
+ try:
+ return self.methods[name]
+ except KeyError:
+ if self.parent is None:
+ raise SemanticException(f'Method "{name}" is not defined in {self.name}.')
+ try:
+ return self.parent.get_method(name)
+ except SemanticException:
+ raise SemanticException(f'Method "{name}" is not defined in {self.name}.')
+
+ # Define un metodo para el tipo actual con el nombre, los parametros y el tipo de
+ # retorno correspondientes
+ def define_method(self, name:str, param_names:list, param_types:list, return_type, line, column):
+ if name in self.methods:
+ raise SemanticException(f'Method {name} is multiply defined')
+
+ method = self.methods[name] = Method(name, param_names, param_types, return_type)
+ method.return_info = VariableInfo(f'{self.name}_{name}_returnType', return_type)
+
+ return method
+
+ # Returns true if other is a parent of current type
+ def conforms_to(self, other):
+ return other.is_special_type() \
+ or self.name == other.name \
+ or (self.parent is not None and self.parent.conforms_to(other))
+
+ def is_special_type(self):
+ return False
+
+ def is_void_type(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.values())
+ output += '\n' if self.methods else ''
+ output += '}\n'
+ return output
+
+ def __repr__(self):
+ return str(self)
+
+# Special_Types
+class SelfType(Type):
+ def __init__(self):
+ Type.__init__(self, 'SELF_TYPE', 0, 0)
+ self.sealed = True
+
+ def conforms_to(self, other):
+ return False
+
+ def is_special_type(self):
+ return True
+
+ def __eq__(self, other):
+ return isinstance(other, SelfType)
+
+class VoidType(Type):
+ def __init__(self):
+ Type.__init__(self, 'VOID_TYPE', 0, 0)
+ self.sealed = True
+
+ def type_union(self, other):
+ return self
+
+ def conforms_to(self, other):
+ return True
+
+ def is_special_type(self):
+ return True
+
+ def is_void_type(self):
+ return True
+
+ def __eq__(self, other):
+ return isinstance(other, Type)
+
+class ErrorType(Type):
+ def __init__(self):
+ Type.__init__(self, '', 0, 0)
+ self.sealed = True
+
+ def type_union(self, other):
+ return self
+
+ def conforms_to(self, other):
+ return True
+
+ def is_special_type(self):
+ return True
+
+ def __eq__(self, other):
+ return isinstance(other, Type)
+
+# Clase para representar una variable dentro del programa
+class VariableInfo:
+ def __init__(self, name, vtype):
+ self.name = name
+ self.type = vtype
+
+
+# Clase para representar el contexto en el que se guardan las variables y los
+# tipos durante el chequeo semantico del programa
+class Context:
+ def __init__(self):
+ self.types = {}
+
+ # Incluye un tipo dentro del contexto
+ def add_type(self, type : Type):
+ if type.name in self.types:
+ raise SemanticException('Classes may not be redefined')
+ self.types[type.name] = type
+ return type
+
+ # Crea un tipo dentro del contexto
+ def create_type(self, name : str, line, column):
+ if name in self.types:
+ raise SemanticException('Classes may not be redefined')
+ type = self.types[name] = Type(name, line, column)
+ return type
+
+ # Obtiene un tipo del contexto con el nombre correspondiente
+ def get_type(self, name : str):
+ try:
+ return self.types[name]
+ except KeyError:
+ raise SemanticException(f'Type {name} not defined.')
+
+ def subtree(self, name : str):
+ type = self.get_type(name)
+ return (i for i in self.types.values() if i.conforms_to(type))
+
+ 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)
+
+
+# Clase para representar el scope durante el chequeo semantico
+class Scope:
+ def __init__(self, parent = None):
+ self.parent = parent
+ self.locals = []
+ self.childs : [Scope] = []
+
+ # Crea un scope hijo
+ def create_child(self):
+ self.childs.append(Scope(self))
+ return self.childs[-1]
+
+ # Define una variable en el scope
+ def define_variable(self, name : str, type : Type, line : int, column : int):
+ self.locals.append(VariableInfo(name, type))
+ return self.locals[-1]
+
+ # Retorna una variable definida en el scope, None si no esta definida
+ def find_variable(self, name : str):
+ try:
+ return next(i for i in self.locals if i.name == name)
+ except StopIteration:
+ return self.parent.find_variable(name) if self.parent is not None else None
+
+ # True si la variable esta definida en el scope
+ def is_defined(self, name : str):
+ return self.find_variable(name) is not None
+
+ # True si la variable fue definida en el scope actual y no en alguno
+ # de sus scope padres
+ def is_local(self, name : str):
+ return any(i for i in self.locals if i.name == name)
+
+ def __iter__(self):
+ for var in self.locals:
+ yield var
+ for ch in self.childs:
+ for var in ch:
+ yield var
diff --git a/src/core/tools/Utils.py b/src/core/tools/Utils.py
new file mode 100644
index 000000000..f8dcc227c
--- /dev/null
+++ b/src/core/tools/Utils.py
@@ -0,0 +1,221 @@
+from .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, line = None, column = None):
+ self.lex = lex
+ self.token_type = token_type
+ self.line = line
+ self.column = column
+
+ def __str__(self):
+ return str(self.token_type)
+
+ def __repr__(self):
+ return repr(self.token_type)
+
+ @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/core/tools/visitor.py b/src/core/tools/visitor.py
new file mode 100644
index 000000000..964842836
--- /dev/null
+++ b/src/core/tools/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/tests/lexer/iis1.cl b/tests/lexer/iis1.cl
index 12cb52beb..d5a4a096d 100644
--- a/tests/lexer/iis1.cl
+++ b/tests/lexer/iis1.cl
@@ -1,6 +1,6 @@
(* Integers, Identifiers, and Special Notation *)
-0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++
+0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++
5! = 120, 2 + 2 = 5 or E = mc2; p + 1 @ p = 1: for x in range(len(b))
new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid })
class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___
diff --git a/tests/lexer/iis1_error.txt b/tests/lexer/iis1_error.txt
index 9e6d66cac..a1d71cc56 100644
--- a/tests/lexer/iis1_error.txt
+++ b/tests/lexer/iis1_error.txt
@@ -1 +1 @@
-(4, 2) - LexicographicError: ERROR "!"
+(4, 2) - LexicographicError: ERROR "!"
\ No newline at end of file
diff --git a/tests/lexer_test.py b/tests/lexer_test.py
index 2a27223d3..f492b1959 100644
--- a/tests/lexer_test.py
+++ b/tests/lexer_test.py
@@ -10,4 +10,4 @@
@pytest.mark.run(order=1)
@pytest.mark.parametrize("cool_file", tests)
def test_lexer_errors(compiler_path, cool_file):
- compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt')
\ No newline at end of file
+ compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt')
diff --git a/tests/utils/utils.py b/tests/utils/utils.py
index 961cf7cbc..4e7a35363 100644
--- a/tests/utils/utils.py
+++ b/tests/utils/utils.py
@@ -45,7 +45,7 @@ def get_file_name(path: str):
def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str, cmp=first_error, timeout=100):
try:
- sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout)
+ sp = subprocess.run(['bash', compiler_path, cool_file_path], stdout=subprocess.PIPE, timeout=timeout)
return_code, output = sp.returncode, sp.stdout.decode()
except subprocess.TimeoutExpired:
assert False, COMPILER_TIMEOUT
@@ -66,8 +66,9 @@ def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str
See the file README for a full copyright notice\.
(?:Loaded: .+\n)*'''
def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: str, output_file_path: str, timeout=100):
+ print(compiler_path, cool_file_path, input_file_path, output_file_path)
try:
- sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout)
+ sp = subprocess.run(['bash', compiler_path, cool_file_path], timeout=timeout)
assert sp.returncode == 0, TEST_MUST_COMPILE % get_file_name(cool_file_path)
except subprocess.TimeoutExpired:
assert False, COMPILER_TIMEOUT
@@ -76,7 +77,7 @@ def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: st
try:
fd = open(input_file_path, 'rb')
- sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), capture_output=True, timeout=timeout)
+ sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), timeout=timeout, capture_output=True)
fd.close()
mo = re.match(SPIM_HEADER, sp.stdout.decode())
if mo: