diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62ff3680..cba66d81 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,6 +11,8 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 + with: + python-version: 3.8 - name: Install requirements run: pip install -r requirements.txt @@ -30,6 +32,8 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 + with: + python-version: 3.8 - name: Install requirements run: pip install -r requirements.txt @@ -49,6 +53,8 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 + with: + python-version: 3.8 - name: Install requirements run: pip install -r requirements.txt @@ -68,6 +74,8 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 + with: + python-version: 3.8 - name: Install requirements run: pip install -r requirements.txt diff --git a/.gitignore b/.gitignore index 4acafde1..a9bdc81a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,410 +1,414 @@ -# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig - -# Created by https://www.gitignore.io/api/visualstudiocode,linux,latex,python -# Edit at https://www.gitignore.io/?templates=visualstudiocode,linux,latex,python - -### LaTeX ### -## Core latex/pdflatex auxiliary files: -*.aux -*.lof -*.log -*.lot -*.fls -*.out -*.toc -*.fmt -*.fot -*.cb -*.cb2 -.*.lb - -## Intermediate documents: -*.dvi -*.xdv -*-converted-to.* -# these rules might exclude image files for figures etc. -# *.ps -# *.eps -# *.pdf - -## Generated if empty string is given at "Please type another file name for output:" -.pdf - -## Bibliography auxiliary files (bibtex/biblatex/biber): -*.bbl -*.bcf -*.blg -*-blx.aux -*-blx.bib -*.run.xml - -## Build tool auxiliary files: -*.fdb_latexmk -*.synctex -*.synctex(busy) -*.synctex.gz -*.synctex.gz(busy) -*.pdfsync - -## Build tool directories for auxiliary files -# latexrun -latex.out/ - -## Auxiliary and intermediate files from other packages: -# algorithms -*.alg -*.loa - -# achemso -acs-*.bib - -# amsthm -*.thm - -# beamer -*.nav -*.pre -*.snm -*.vrb - -# changes -*.soc - -# comment -*.cut - -# cprotect -*.cpt - -# elsarticle (documentclass of Elsevier journals) -*.spl - -# endnotes -*.ent - -# fixme -*.lox - -# feynmf/feynmp -*.mf -*.mp -*.t[1-9] -*.t[1-9][0-9] -*.tfm - -#(r)(e)ledmac/(r)(e)ledpar -*.end -*.?end -*.[1-9] -*.[1-9][0-9] -*.[1-9][0-9][0-9] -*.[1-9]R -*.[1-9][0-9]R -*.[1-9][0-9][0-9]R -*.eledsec[1-9] -*.eledsec[1-9]R -*.eledsec[1-9][0-9] -*.eledsec[1-9][0-9]R -*.eledsec[1-9][0-9][0-9] -*.eledsec[1-9][0-9][0-9]R - -# glossaries -*.acn -*.acr -*.glg -*.glo -*.gls -*.glsdefs - -# uncomment this for glossaries-extra (will ignore makeindex's style files!) -# *.ist - -# gnuplottex -*-gnuplottex-* - -# gregoriotex -*.gaux -*.gtex - -# htlatex -*.4ct -*.4tc -*.idv -*.lg -*.trc -*.xref - -# hyperref -*.brf - -# knitr -*-concordance.tex -# TODO Comment the next line if you want to keep your tikz graphics files -*.tikz -*-tikzDictionary - -# listings -*.lol - -# luatexja-ruby -*.ltjruby - -# makeidx -*.idx -*.ilg -*.ind - -# minitoc -*.maf -*.mlf -*.mlt -*.mtc[0-9]* -*.slf[0-9]* -*.slt[0-9]* -*.stc[0-9]* - -# minted -_minted* -*.pyg - -# morewrites -*.mw - -# nomencl -*.nlg -*.nlo -*.nls - -# pax -*.pax - -# pdfpcnotes -*.pdfpc - -# sagetex -*.sagetex.sage -*.sagetex.py -*.sagetex.scmd - -# scrwfile -*.wrt - -# sympy -*.sout -*.sympy -sympy-plots-for-*.tex/ - -# pdfcomment -*.upa -*.upb - -# pythontex -*.pytxcode -pythontex-files-*/ - -# tcolorbox -*.listing - -# thmtools -*.loe - -# TikZ & PGF -*.dpth -*.md5 -*.auxlock - -# todonotes -*.tdo - -# vhistory -*.hst -*.ver - -# easy-todo -*.lod - -# xcolor -*.xcp - -# xmpincl -*.xmpi - -# xindy -*.xdy - -# xypic precompiled matrices -*.xyc - -# endfloat -*.ttt -*.fff - -# Latexian -TSWLatexianTemp* - -## Editors: -# WinEdt -*.bak -*.sav - -# Texpad -.texpadtmp - -# LyX -*.lyx~ - -# Kile -*.backup - -# KBibTeX -*~[0-9]* - -# auto folder when using emacs and auctex -./auto/* -*.el - -# expex forward references with \gathertags -*-tags.tex - -# standalone packages -*.sta - -### LaTeX Patch ### -# glossaries -*.glstex - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history - -# End of https://www.gitignore.io/api/visualstudiocode,linux,latex,python - -# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) - +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig + +# Created by https://www.gitignore.io/api/visualstudiocode,linux,latex,python +# Edit at https://www.gitignore.io/?templates=visualstudiocode,linux,latex,python + +### LaTeX ### +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# KBibTeX +*~[0-9]* + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +### LaTeX Patch ### +# glossaries +*.glstex + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ +src/.idea +.idea/* +./src/.idea/* + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +# End of https://www.gitignore.io/api/visualstudiocode,linux,latex,python + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + +venv diff --git a/INFORME DEL PROYECTO DE COMPILACION.docx b/INFORME DEL PROYECTO DE COMPILACION.docx new file mode 100644 index 00000000..7d9a6745 Binary files /dev/null and b/INFORME DEL PROYECTO DE COMPILACION.docx differ diff --git a/INFORME DEL PROYECTO DE COMPILACION.pdf b/INFORME DEL PROYECTO DE COMPILACION.pdf new file mode 100644 index 00000000..e227549f Binary files /dev/null and b/INFORME DEL PROYECTO DE COMPILACION.pdf differ diff --git a/LICENSE b/LICENSE index ad9e4865..f543cdb4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2020 School of Math and Computer Science, University of Havana - -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. +MIT License + +Copyright (c) 2020 School of Math and Computer Science, University of Havana + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/doc/Readme.md b/doc/Readme.md index 402477c8..d7562e37 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -1,39 +1,40 @@ -# Documentación - -> Introduzca sus datos (de todo el equipo) en la siguiente tabla: - -**Nombre** | **Grupo** | **Github** ---|--|-- -Nombre1 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) -Nombre2 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) -Nombre3 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) - -## Readme - -Modifique el contenido de este documento para documentar de forma clara y concisa los siguientes aspectos: - -- Cómo ejecutar (y compilar si es necesario) su compilador. -- Requisitos adicionales, dependencias, configuración, etc. -- Opciones adicionales que tenga su compilador. - -### Sobre los Equipos de Desarrollo - -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. Próximamente les daremos las instrucciones mínimas necesarias para ello. - -### Sobre los Materiales a Entregar - -Para la evaluación del proyecto Ud. debe entregar un informe en formato PDF (`report.pdf`) que resuma de manera organizada y comprensible la arquitectura e implementación de su compilador. -El documento **NO** debe exceder las 5 cuartillas. -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. - -### 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 [@matcom_cmp](https://t.me/matcom_cmp) se anunciará la fecha y requisitos de cada primera entrega. +# Documentación + + +> Introduzca sus datos (de todo el equipo) en la siguiente tabla: + +**Nombre** | **Grupo** | **Github** +--|--|-- +Lazaro Jesus Suarez Nuñes | C412 | [@LazardStrife](https://github.com/LazardStrife) +Marcos Antonio Maceo Reyes | C412 | [@stdevMac](https://github.com/stdevMac) + + +## Readme + +Modifique el contenido de este documento para documentar de forma clara y concisa los siguientes aspectos: + +- Cómo ejecutar (y compilar si es necesario) su compilador. +- Requisitos adicionales, dependencias, configuración, etc. +- Opciones adicionales que tenga su compilador. + +### Sobre los Equipos de Desarrollo + +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. Próximamente les daremos las instrucciones mínimas necesarias para ello. + +### Sobre los Materiales a Entregar + +Para la evaluación del proyecto Ud. debe entregar un informe en formato PDF (`report.pdf`) que resuma de manera organizada y comprensible la arquitectura e implementación de su compilador. +El documento **NO** debe exceder las 5 cuartillas. +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. + +### 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 [@matcom_cmp](https://t.me/matcom_cmp) se anunciará la fecha y requisitos de cada primera entrega. diff --git a/doc/informe.pdf b/doc/informe.pdf new file mode 100644 index 00000000..35648833 Binary files /dev/null and b/doc/informe.pdf differ diff --git a/requirements.txt b/requirements.txt index 9eb0cad1..f23029c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -pytest -pytest-ordering +pytest +pytest-ordering +ply diff --git a/src/Readme.md b/src/Readme.md index 1200371b..30fc9fe4 100644 --- a/src/Readme.md +++ b/src/Readme.md @@ -1,78 +1,83 @@ -# COOL: Proyecto de Compilación - -La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la -Universidad de La Habana, consiste este curso en la implementación de un compilador completamente -funcional para el lenguaje _COOL_. - -_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. - -### Sobre el Lenguaje COOL - -Ud. podrá encontrar la especificación formal del lenguaje COOL en el documento _"COOL Language Reference Manual"_, que se distribuye junto con el presente texto. - -## Código Fuente - -### Compilando su proyecto - -Si es necesario compilar su proyecto, incluya todas las instrucciones necesarias en un archivo [`/src/makefile`](/src/makefile). -Durante la evaluación su proyecto se compilará ejecutando la siguiente secuencia: - -```bash -$ cd source -$ make clean -$ make -``` - -### Ejecutando su proyecto - -Incluya en un archivo [`/src/coolc.sh`](/src/coolc.sh) todas las instrucciones que hacen falta para lanzar su compilador. Recibirá como entrada un archivo con extensión `.cl` y debe generar como salida un archivo `.mips` cuyo nombre será el mismo que la entrada. - -Para lanzar el compilador, se ejecutará la siguiente instrucción: - -```bash -$ cd source -$ ./coolc.sh -``` - -### Sobre el Compilador de COOL - -El compilador de COOL se ejecutará como se ha definido anteriormente. -En caso de que no ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código de salida 0, generar (o sobrescribir si ya existe) en la misma carpeta del archivo **.cl** procesado, y con el mismo nombre que éste, un archivo con extension **.mips** que pueda ser ejecutado con **spim**. Además, reportar a la salida estándar solamente lo siguiente: - - - - -En caso de que ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código -de salida (exit code) 1 y reportar a la salida estándar (standard output stream) lo que sigue... - - - - _1 - ... - _n - -... donde `_i` tiene el siguiente formato: - - (,) - : - -Los campos `` y `` indican la ubicación del error en el fichero **.cl** procesado. En caso -de que la naturaleza del error sea tal que no pueda asociárselo a una línea y columna en el archivo de -código fuente, el valor de dichos campos debe ser 0. - -El campo `` será alguno entre: - -- `CompilerError`: se reporta al detectar alguna anomalía con la entrada del compilador. Por ejemplo, si el fichero a compilar no existe. -- `LexicographicError`: errores detectados por el lexer. -- `SyntacticError`: errores detectados por el parser. -- `NameError`: se reporta al referenciar un `identificador` en un ámbito en el que no es visible. -- `TypeError`: se reporta al detectar un problema de tipos. Incluye: - - incompatibilidad de tipos entre `rvalue` y `lvalue`, - - operación no definida entre objetos de ciertos tipos, y - - tipo referenciado pero no definido. -- `AttributeError`: se reporta cuando un atributo o método se referencia pero no está definido. -- `SemanticError`: cualquier otro error semántico. - -### Sobre la Implementación del Compilador de COOL - -El compilador debe estar implementado en `python`. Usted debe utilizar una herramienta generadora de analizadores -lexicográficos y sintácticos. Puede utilizar la que sea de su preferencia. +# COOL: Proyecto de Compilación + +## Integrantes + +- Lazaro Jesus Suarez Nuñez +- Marcos Antonio Maceo Reyes + +La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la +Universidad de La Habana, consiste este curso en la implementación de un compilador completamente +funcional para el lenguaje _COOL_. + +_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. + +### Sobre el Lenguaje COOL + +Ud. podrá encontrar la especificación formal del lenguaje COOL en el documento _"COOL Language Reference Manual"_, que se distribuye junto con el presente texto. + +## Código Fuente + +### Compilando su proyecto + +Si es necesario compilar su proyecto, incluya todas las instrucciones necesarias en un archivo [`/src/makefile`](/src/makefile). +Durante la evaluación su proyecto se compilará ejecutando la siguiente secuencia: + +```bash +$ cd source +$ make clean +$ make +``` + +### Ejecutando su proyecto + +Incluya en un archivo [`/src/coolc.sh`](/src/coolc.sh) todas las instrucciones que hacen falta para lanzar su compilador. Recibirá como entrada un archivo con extensión `.cl` y debe generar como salida un archivo `.mips` cuyo nombre será el mismo que la entrada. + +Para lanzar el compilador, se ejecutará la siguiente instrucción: + +```bash +$ cd source +$ ./coolc.sh +``` + +### Sobre el Compilador de COOL + +El compilador de COOL se ejecutará como se ha definido anteriormente. +En caso de que no ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código de salida 0, generar (o sobrescribir si ya existe) en la misma carpeta del archivo **.cl** procesado, y con el mismo nombre que éste, un archivo con extension **.mips** que pueda ser ejecutado con **spim**. Además, reportar a la salida estándar solamente lo siguiente: + + + + +En caso de que ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código +de salida (exit code) 1 y reportar a la salida estándar (standard output stream) lo que sigue... + + + + _1 + ... + _n + +... donde `_i` tiene el siguiente formato: + + (,) - : + +Los campos `` y `` indican la ubicación del error en el fichero **.cl** procesado. En caso +de que la naturaleza del error sea tal que no pueda asociárselo a una línea y columna en el archivo de +código fuente, el valor de dichos campos debe ser 0. + +El campo `` será alguno entre: + +- `CompilerError`: se reporta al detectar alguna anomalía con la entrada del compilador. Por ejemplo, si el fichero a compilar no existe. +- `LexicographicError`: errores detectados por el lexer. +- `SyntacticError`: errores detectados por el parser. +- `NameError`: se reporta al referenciar un `identificador` en un ámbito en el que no es visible. +- `TypeError`: se reporta al detectar un problema de tipos. Incluye: + - incompatibilidad de tipos entre `rvalue` y `lvalue`, + - operación no definida entre objetos de ciertos tipos, y + - tipo referenciado pero no definido. +- `AttributeError`: se reporta cuando un atributo o método se referencia pero no está definido. +- `SemanticError`: cualquier otro error semántico. + +### Sobre la Implementación del Compilador de COOL + +El compilador debe estar implementado en `python`. Usted debe utilizar una herramienta generadora de analizadores +lexicográficos y sintácticos. Puede utilizar la que sea de su preferencia. diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/ast.py b/src/ast.py new file mode 100644 index 00000000..527990f1 --- /dev/null +++ b/src/ast.py @@ -0,0 +1,237 @@ +# clase base del AST +class Node: + def __init__(self, line_number): + self.lineNumber = line_number + + def getLineNumber(self): + return 0 if len(self.lineNumber) != 1 else self.lineNumber[0][0] + + def getColumnNumber(self): + return 0 if len(self.lineNumber) != 1 else self.lineNumber[0][1] + + +# nodo del programa, classes es una lista de nodos ClassNode +class ProgramNode(Node): + def __init__(self, classes, line_number): + super().__init__(line_number) + self.classes = classes + + +# nodo de clase, features es una lista de FeatureNode +class ClassNode(Node): + def __init__(self, type_name, features, father_type_name, line_number): + super().__init__(line_number) + self.typeName = type_name + self.features = features + self.fatherTypeName = father_type_name + + +# nodo base de caracteristicas de clases +class FeatureNode(Node): + pass + + +# nodo de caracteristicas de atributo +class AttributeFeatureNode(FeatureNode): + def __init__(self, attr_id, type_name, expression, line_number): + super().__init__(line_number) + self.id = attr_id + self.typeName = type_name + self.expression = expression + + +# nodo de caracteristicas de metodos +class FunctionFeatureNode(FeatureNode): + def __init__(self, func_id, parameters, type_name, statement, line_number): + super().__init__(line_number) + self.id = func_id + self.parameters = parameters + self.typeName = type_name + self.statement = statement + + +# nodo de parametro +class ParameterNode(Node): + def __init__(self, param_id, type_name, line_number): + super().__init__(line_number) + self.id = param_id + self.typeName = type_name + + +# nodo base de expresiones +class StatementNode(Node): + pass + + +# nodo de expresion de asignacion +class AssignStatementNode(StatementNode): + def __init__(self, assign_id, expression, line_number): + super().__init__(line_number) + self.id = assign_id + self.expression = expression + + +# nodo de expresiones condicionales +class ConditionalStatementNode(StatementNode): + def __init__(self, eval_expr, if_expr, else_expr, line_number): + super().__init__(line_number) + self.evalExpr = eval_expr + self.ifExpr = if_expr + self.elseExpr = else_expr + + +# nodo de expresiones while +class LoopStatementNode(StatementNode): + def __init__(self, eval_expr, loop_expr, line_number): + super().__init__(line_number) + self.evalExpr = eval_expr + self.loopExpr = loop_expr + + +# nodo de expresiones block, expressiones es una lista de expresiones +class BlockStatementNode(StatementNode): + def __init__(self, expressions, line_number): + super().__init__(line_number) + self.expressions = expressions + + +# nodo de expresiones let, variables es una lista de AttributeFeatureNode +class LetStatementNode(StatementNode): + def __init__(self, variables, expression, line_number): + super().__init__(line_number) + self.variables = variables + self.expression = expression + + +# nodo de expresiones case, body es una lista de CaseBranchNode +class CaseStatementNode(StatementNode): + def __init__(self, expression, body, line_number): + super().__init__(line_number) + self.expression = expression + self.body = body + + +# nodo de una rama de expresion case +class CaseBranchNode(StatementNode): + def __init__(self, case_id, type_name, expression, line_number): + super().__init__(line_number) + self.id = case_id + self.typeName = type_name + self.expression = expression + + +# nodo de expresion new +class NewStatementNode(StatementNode): + def __init__(self, type_name, line_number): + super().__init__(line_number) + self.typeName = type_name + + +# nodo de llamado de funcion, dispatchType es None si no es de tipo alternativo donde se +# especifica la clase del metodo, sino es un string con la clase correspondiente +# args es una lista de StatementNode +class FunctionCallStatement(StatementNode): + def __init__(self, instance, dispatch_type, function, args, line_number): + super().__init__(line_number) + self.instance = instance + self.dispatchType = dispatch_type + self.function = function + self.args = args + self.instance_type = "" + + +# nodo base de las expresiones aritmeticas y de comparacion +class ExpressionNode(Node): + pass + + +# nodo base de atomos (variables y constantes) +class AtomicNode(ExpressionNode): + def __init__(self, lex, line_number): + super().__init__(line_number) + self.lex = lex + + +# nodo base para operaciones unarias +class UnaryNode(ExpressionNode): + def __init__(self, expression, line_number): + super().__init__(line_number) + self.expression = expression + + +# nodo base para operaciones binarias +class BinaryNode(ExpressionNode): + def __init__(self, left, right, line_number): + super().__init__(line_number) + self.left = left + self.right = right + + +# nodo de constantes numericas +class ConstantNumericNode(AtomicNode): + pass + + +# nodo de constantes de cadenas +class ConstantStringNode(AtomicNode): + pass + + +# nodo de constantes booleanas +class ConstantBoolNode(AtomicNode): + pass + + +# nodo de variables +class VariableNode(AtomicNode): + pass + + +# nodo de expresiones not +class NotNode(UnaryNode): + pass + + +# nodo de expresiones isvoid +class IsVoidNode(UnaryNode): + pass + + +# nodo de expresion complemento de entero +class ComplementNode(UnaryNode): + pass + + +# nodo de <= +class LessEqualNode(BinaryNode): + pass + + +# nodo de < +class LessNode(BinaryNode): + pass + + +# nodo de = +class EqualNode(BinaryNode): + pass + + +# nodo de + +class PlusNode(BinaryNode): + pass + + +# nodo de - +class MinusNode(BinaryNode): + pass + + +# nodo de * +class TimesNode(BinaryNode): + pass + + +# nodo de / +class DivideNode(BinaryNode): + pass diff --git a/src/cil_ast.py b/src/cil_ast.py new file mode 100644 index 00000000..7a9c0620 --- /dev/null +++ b/src/cil_ast.py @@ -0,0 +1,422 @@ +# clase base del AST del lenguaje CIL +class Node: + pass + + +# nodo del programa +class ProgramNode(Node): + def __init__(self, types, data, code): + self.types = types + self.data = data + self.code = code + + +# nodo de los tipos +class TypeNode(Node): + def __init__(self, type_name, attributes_owner, attributes, methods): + self.attributes_owner = attributes_owner + self.attributes = attributes + self.methods = methods + self.type_name = type_name + + def GetCode(self): + result = "" + result += "\ttype " + self.type_name + " {" + + for key in self.attributes_owner.keys(): + result += "\n\t\tattribute " + key + ":" + self.attributes_owner[key] + + for key in self.methods.keys(): + result += "\n\t\tmethod " + key + ":" + self.methods[key] + "_" + key + + result += "\n\t}" + + return result + + +# nodo de los datos que se definen en la region .data del MIPS +class DataNode(Node): + def __init__(self, id, value): + self.id = id + self.value = value + + def GetCode(self): + if type(self.value) == type("asd"): + new_value = self.value.replace("\n", "\\n") + else: + new_value = str(self.value) + return "\t" + self.id + " \"" + new_value + "\"" + + +# nodo de funciones +class FunctionNode(Node): + def __init__(self, name, params, locals, body): + self.name = name + self.params = params + self.locals = locals + self.body = body + + def GetCode(self): + result = "\tfunction " + self.name + " {\n" + for p in self.params: + result += "\t\t" + p.GetCode() + "\n" + for local in self.locals: + result += "\t\t" + local.GetCode() + "\n" + for s in self.body: + result += "\t\t" + s.GetCode() + "\n" + + result += "\t}" + + return result + + +# nodo base de instrucciones en CIL +class InstructionNode(Node): + pass + + +# nodo para variables locales +class LocalNode(Node): + def __init__(self, id): + self.id = id + + def GetCode(self): + return "LOCAL " + self.id + + def __str__(self): + return str(self.id) + + +# nodo para la operacion de salvado de variables locales +class LocalSaveNode(Node): + def GetCode(self): + return "LOCALSAVE" + + +# nodo para parametros de funciones +class ParamNode(Node): + def __init__(self, id): + self.id = id + + def GetCode(self): + return "PARAM " + self.id + + def __str__(self): + return str(self.id) + + +# nodo para la operacion mov +class MovNode(InstructionNode): + def __init__(self, result, value): + self.result = result + self.value = value + + def GetCode(self): + return "MOV " + str(self.result) + " " + str(self.value) + + +# nodo base de operaciones unarias +class UnaryOpNode(InstructionNode): + def __init__(self, value, result): + self.value = value + self.result = result + + +# nodo de la operacion not +class NtNode(UnaryOpNode): + def GetCode(self): + return "NOT " + str(self.result) + " " + str(self.value) + + +# nodo de la operacion de complemento de entero +class CmpNode(UnaryOpNode): + def GetCode(self): + return "CMP " + str(self.result) + " " + str(self.value) + + +# nodo de la operacion isvoid +class VDNode(UnaryOpNode): + def GetCode(self): + return "VD " + str(self.result) + " " + str(self.value) + + +# nodo base de las operaciones binarias +class BinaryOpNode(InstructionNode): + def __init__(self, left, right, result): + self.left = left + self.right = right + self.result = result + + +# nodo de la operacion suma +class AddNode(BinaryOpNode): + def GetCode(self): + return "ADD " + str(self.result) + " " + str(self.left) + " " + str(self.right) + + +# nodo de la operacion resta +class SubNode(BinaryOpNode): + def GetCode(self): + return "SUB " + str(self.result) + " " + str(self.left) + " " + str(self.right) + + +# nodo de la operacion multiplicacion +class MulNode(BinaryOpNode): + def GetCode(self): + return "Mul " + str(self.result) + " " + str(self.left) + " " + str(self.right) + + +# nodo de la operacion division +class DivNode(BinaryOpNode): + def GetCode(self): + return "Div " + str(self.result) + " " + str(self.left) + " " + str(self.right) + + +# nodo de la operacion <= +class LENode(BinaryOpNode): + def GetCode(self): + return "LE " + str(self.result) + " " + str(self.left) + " " + str(self.right) + + +# nodo de la operacion < +class LNode(BinaryOpNode): + def GetCode(self): + return "L " + str(self.result) + " " + str(self.left) + " " + str(self.right) + + +# nodo de la operacion = +class ENode(BinaryOpNode): + def GetCode(self): + return "E " + str(self.result) + " " + str(self.left) + " " + str(self.right) + + +# nodo de la operacion que pide un atributo de un objeto +class GetAttributeNode(InstructionNode): + def __init__(self, _type, value, attr, result): + self.type_name = _type + self.value = value + self.attr = attr + self.result = result + + def GetCode(self): + return "GATTR " + str(self.result) + " " + str(self.value) + " " + str(self.attr) + + +# nodo de la operacion que modifica un atributo de un objeto +class SetAttributeNode(InstructionNode): + def __init__(self, _type, instance, attr, value): + self.type_name = _type + self.value = value + self.instance = instance + self.attr = attr + + def GetCode(self): + return "SATTR " + str(self.instance) + " " + str(self.attr) + " " + str(self.value) + + +# nodo de la operacion de crear inicializar una variable local +class AllocateNode(InstructionNode): + def __init__(self, _type, result): + self.type = _type + self.result = result + + def GetCode(self): + return "ALLOC " + str(self.result) + " " + str(self.type) + + +# nodo de la operacion de salida del programa +class AbortNode(InstructionNode): + def __init__(self, caller_type): + self.caller_type = caller_type + + def GetCode(self): + return "EXIT" + + +# nodo de la operacion de copiar una valor de una variable local en otra +class CopyNode(InstructionNode): + def __init__(self, value, result): + self.result = result + self.value = value + + def GetCode(self): + return "COPY " + str(self.result) + " " + str(self.value) + + +# nodo de la operacion que devuelve el entero que representa el tipo +class TypeOfNode(InstructionNode): + def __init__(self, result, variable): + self.result = result + self.variable = variable + + def GetCode(self): + return "TYPE " + str(self.result) + " " + str(self.variable) + + +# nodo de la operacion de llamado de una funcion +class DispatchCallNode(InstructionNode): + def __init__(self, type_addr, method, result): + self.type_addr = type_addr + self.method = method + self.result = result + + def GetCode(self): + return "CALL " + str(self.result) + " " + str(self.type_addr) + " " + str(self.method) + + +# nodo de la operacion de agragar un argumento para la proxima funcion que se llame +class ArgNode(InstructionNode): + def __init__(self, value): + self.value = value + + def GetCode(self): + return "ARG " + str(self.value) + + +# nodo de la operacion condicional +class IfGotoNode(InstructionNode): + def __init__(self, predicate, label): + self.predicate = predicate + self.label = label + + def GetCode(self): + return "IF " + str(self.predicate) + " GOTO " + self.label + + +# nodo de la operacion de salto incondicional +class GotoNode(InstructionNode): + def __init__(self, label): + self.label = label + + def GetCode(self): + return "GOTO " + self.label + + +# nodo para las etiquetas de salto +class LabelNode(InstructionNode): + def __init__(self, label_name): + self.label_name = label_name + + def GetCode(self): + return "LABEL " + self.label_name + + +# nodo de la operacion de retorno de funciones +class ReturnNode(InstructionNode): + def __init__(self, return_value=""): + self.return_value = return_value + + def GetCode(self): + return "RETURN " + str(self.return_value) + + +# nodo de la operacion de strlen +class StrlenNode(InstructionNode): + def __init__(self, str, result): + self.result = result + self.str = str + + def GetCode(self): + return "STRLEN " + str(self.result) + " " + self.str + + +# nodo de la operacion concat +class StrcatNode(InstructionNode): + def __init__(self, str_a, str_b, result): + self.result = result + self.str_a = str_a + self.str_b = str_b + + def GetCode(self): + return "STRCAT " + str(self.result) + " " + str(self.str_a) + " " + str(self.str_b) + + +# nodo de la operacion strsub +class StrsubNode(InstructionNode): + def __init__(self, str, i, len, result): + self.str = str + self.i = i + self.len = len + self.result = result + + def GetCode(self): + return "STRSUB " + str(self.result) + " " + str(self.str) + " " + str(self.i) + " " + str(self.len) + + +# nodo de la expresion de cargar un dato en una variable local +class LoadDataNode(InstructionNode): + def __init__(self, result, data): + self.data = data + self.result = result + + def GetCode(self): + return "LDATA " + str(self.result) + " " + str(self.data) + + +# nodo de la expresion que calcula si el tipo de una variable local hereda de otro +class IsSonNode(InstructionNode): + def __init__(self, class_son, class_father, result): + self.son = class_son + self.father = class_father + self.result = result + + def GetCode(self): + return "ISSON " + self.son + " " + self.father + " " + self.result + + +# nodo de la expresion que lee una cadena de entrada +class ReadNode(InstructionNode): + def __init__(self, result): + self.result = result + + def GetCode(self): + return "READ " + str(self.result) + + +# nodo de la operacion de leer un entero +class ReadIntNode(InstructionNode): + def __init__(self, result): + self.result = result + + def GetCode(self): + return "RINT " + str(self.result) + + +# nodo de la operacion de imprimir una cadena +class PrintNode(InstructionNode): + def __init__(self, str): + self.str = str + + def GetCode(self): + return "PRINT " + str(self.str) + + +# nodo de la operacion de imprimir un entero +class PrintIntNode(InstructionNode): + def __init__(self, val): + self.val = val + + def GetCode(self): + return "PINT " + str(self.val) + + +# nodo de la operacion que a partir de una variable entera ponga en otra que +# es una cadena de caracteres el nombre del tipo que representa dicho entero +class TypeNameNode(InstructionNode): + def __init__(self, result, type_addr): + self.result = result + self.type_addr = type_addr + + def GetCode(self): + return "TYPENAME " + str(self.result) + " " + str(self.type_addr) + + +# nodo de la operacion que a partir de una cadena de caracteres pone en una variable local +# el entero que representa ese tipo +class TypeAddressNode(InstructionNode): + def __init__(self, result, type_name): + self.result = result + self.type_name = type_name + + def GetCode(self): + return "TYPEADDR " + str(self.result) + " " + str(self.type_name) diff --git a/src/cil_generator.py b/src/cil_generator.py new file mode 100644 index 00000000..38a127c6 --- /dev/null +++ b/src/cil_generator.py @@ -0,0 +1,847 @@ +import ast as ast +from type_defined import * +from string_data_visitor import * +from cil_ast import * +from semantic import * + +# aqui se almacena la informacion del MIPS +TYPES = {} +DATA = {} +CODE = [] + +# utilizados para minimizar cantidad de variables locale declaradas en MIPS +MAX_PARAM_COUNT = 0 +MAX_LOCAL_COUNT = 2 + +MAIN_LOCAL = None + + +def generate_cil(ast): + global TYPES, DATA, CODE, T_LOCALS, CILCODE + CILCODE = generate_all(ast) + + # son_father_tuples almacena las tuplas de padre-hijo que existen en las clases + # para hacer chequeos de herencia en ejecucion, esto es utilizado para las expresiones case + son_father_tuples = [] + for t1 in AllTypes.values(): + for t2 in AllTypes.values(): + if t1.name == "SELF_TYPE" or t2.name == "SELF_TYPE": + continue + if is_ancestor(t1, t2): + son_father_tuples.append((t2.name, t1.name)) + + return [TYPES, DATA, CODE, MAX_LOCAL_COUNT, MAX_PARAM_COUNT, CILCODE, son_father_tuples] + + +def generate_all(ast): + result = "TYPES -->\n\n" + result += generate_cil_types(ast) + result += "END <--\n\n" + + result += "DATA -->\n\n" + result += generate_cil_data(ast) + result += "END <--\n\n" + + result += "CODE -->\n\n" + result += generate_cil_code(ast) + result += "END <--" + + return result + + +def generate_cil_types(ast): + result = "" + + global TYPES + + # pone el TYPES la informacion necesaria de los tipos y sus caracteristicas + for key in ast.keys(): + if key == "SELF_TYPE": + continue + + attributes_owner = ast[key].get_attribute_owner() + attributes = ast[key].get_attributes_as_dict() + methods_owner = ast[key].get_method_owner() + new_type = TypeNode(key, attributes_owner, attributes, methods_owner) + TYPES[key] = new_type + + result += new_type.GetCode() + "\n\n" + + return result + + +def generate_cil_data(ast): + global DATA + + # se utiliza el patron visitor para encontrar las cadenas de caracteres en el codigo + # para ponerlas en DATA + result = "" + vis = FormatVisitorS() + data = {} + + for val in ast.values(): + for attr in val.attributes.values(): + if attr.attribute_name != "self" and attr.expression: + for s in vis.visit(attr.expression): + data[s] = s.lex + for method in val.methods.values(): + if method.expression: + for s in vis.visit(method.expression): + data[s] = s.lex + + i = 0 + for s in data.values(): + new_data = DataNode("data_" + str(i), s) + DATA[s] = new_data + result += new_data.GetCode() + "\n" + i += 1 + + result += "\n" + return result + + +# aqui se genera el codigo CIL de las funciones de las diferentes clases y de la funcion de +# entrada del programa +def generate_cil_code(ast): + result = "" + + Main_class_attributes = [] + + # codigo de las funciones basicas + result += generate_built_in_functions() + + # codigo de las funciones declaradas + for types in ast.values(): + if types.name == "SELF_TYPE": + continue + result += generate_attributes_initialization(types.name, types.get_attributes_as_dict().values()) + if types.name == "Object" or types.name == "IO" or types.name == "String": + continue + if types.name == "Main": + for a in types.attributes.values(): + if a.attribute_name == "self": + continue + Main_class_attributes += [[a.attribute_type.name, a.attribute_name, a.expression]] + for method in types.methods.values(): + result += generate_function(types.name, method) + + # a partir de aqui se genera el codigo de la clase de entrada del programa + global F_PARAM, F_LOCALS, LABEL_COUNTER, D_LOCALS, V_TYPE, CURR_TYPE, C_ATTRIBUTES, LET_LOCALS, MAIN_LOCAL + C_ATTRIBUTES = {} + F_LOCALS = {} + D_LOCALS = {} + V_TYPE = {} + LET_LOCALS = {} + CURR_TYPE = "" + statements = [] + F_PARAM = {} + + Main_instance = get_local() + MAIN_LOCAL = Main_instance + result_local = get_local() + + main_func = FunctionNode('main', [], [], + [AllocateNode("Main", Main_instance.id)]) + + F_LOCALS["self"] = Main_instance + + Main_type_local = get_local() + + main_func.body += [LocalSaveNode(), ArgNode(Main_instance.id), + AllocateNode("Int", Main_type_local.id), + TypeOfNode(Main_type_local.id, Main_instance.id), + DispatchCallNode(Main_type_local.id, "main", result_local.id)] + + global MAX_LOCAL_COUNT + MAX_LOCAL_COUNT = max(len(F_LOCALS), MAX_LOCAL_COUNT) + + _locals = F_LOCALS.copy() + locals_aux = [] + for key in _locals.keys(): + locals_aux += [_locals[key]] + + main_func.locals = locals_aux + + global CODE + + CODE.append(main_func) + + result += main_func.GetCode() + "\n\n" + + return result + + +# obtener una variable local para almacenar algun dato +def get_local(): + global F_LOCALS, F_PARAM, MAX_LOCAL_COUNT + + id = "local_" + str(len(F_LOCALS)) + + if id in F_PARAM: + return F_PARAM[id] + + local = LocalNode(id) + F_LOCALS[id] = local + + return local + + +# obtener una etiqueta +def get_label(): + global LABEL_COUNTER + LABEL_COUNTER += 1 + return "label_" + str(LABEL_COUNTER) + + +# aqui se genera el codigo CIL de las funciones de los tipos basicos +def generate_built_in_functions(): + code = [FunctionNode("IO_out_string", [ParamNode('self'), ParamNode('str')], [], + [PrintNode('str'), + ReturnNode('self')]), + + FunctionNode('IO_out_int', [ParamNode('self'), ParamNode('int')], [], + [PrintIntNode('int'), + ReturnNode('self')]), + + FunctionNode('IO_in_string', [ParamNode('self')], [get_local()], + [AllocateNode("String", 'local_0'), + ReadNode('local_0'), + ReturnNode('local_0')]), + + FunctionNode('IO_in_int', [ParamNode('self')], [get_local()], + [AllocateNode("Int", "local_0"), + ReadIntNode('local_0'), + ReturnNode('local_0')]), + + FunctionNode('Object_type_name', [ParamNode('self')], [get_local(), get_local()], + [AllocateNode("Int", "local_0"), + AllocateNode("String", "local_1"), + TypeOfNode('local_0', 'self'), + TypeNameNode("local_1", "local_0"), + ReturnNode("local_1")]), + + # en el CopyNode no hace falta hacer allocate en memoria + # para local_4 ya que esto se hace en ejecucion y depende + # del tipo de self + FunctionNode('Object_copy', [ParamNode('self')], [get_local()], + [CopyNode('self', 'local_0'), + ReturnNode('local_0')]), + + FunctionNode('String_length', [ParamNode('self')], [get_local()], + [AllocateNode("Int", "local_0"), + StrlenNode('self', 'local_0'), + ReturnNode('local_0')]), + + FunctionNode('String_concat', [ParamNode('self'), ParamNode('str')], [get_local()], + [AllocateNode("String", "local_0"), + StrcatNode('self', 'str', 'local_0'), + ReturnNode('local_0')]), + + FunctionNode('String_substr', [ParamNode('self'), ParamNode('from'), ParamNode('to')], [get_local()], + [AllocateNode("String", "local_0"), + StrsubNode('self', 'from', 'to', 'local_0'), + ReturnNode('local_0')]), + + FunctionNode("Object_abort", [ParamNode('self')], [get_local()], + [AllocateNode("Int", 'local_0'), + TypeOfNode('local_0', 'self'), + AbortNode('local_0')])] + + global MAX_PARAM_COUNT + MAX_PARAM_COUNT = 3 + + global CODE + CODE = CODE + code + + result = "" + for f in code: + result += f.GetCode() + "\n\n" + + return result + + +C_ATTRIBUTES = {} +F_PARAM = {} +F_LOCALS = {} +LET_LOCALS = {} +CASE_LOCALS = {} +D_LOCALS = {} +V_TYPE = {} +CURR_TYPE = "" +LABEL_COUNTER = 0 + + +# clase base donde de el codigo y la variable donde se almacena el resultado +class Node_Result: + def __init__(self, node, result): + self.node = node + self.result = result + + +# aqui se genera el codigo de una funcion +def generate_function(type_name, method): + result = "" + + f_name = type_name + "_" + method.name + + global MAX_LOCAL_COUNT, F_PARAM, F_LOCALS, LABEL_COUNTER, D_LOCALS, V_TYPE, CURR_TYPE, C_ATTRIBUTES, LET_LOCALS + C_ATTRIBUTES = {} + F_LOCALS = {} + D_LOCALS = {} + V_TYPE = {} + LET_LOCALS = {} + CURR_TYPE = type_name + statements = [] + F_PARAM = {} + CASE_LOCALS = {} + + parameters = [ParamNode("self")] + F_PARAM["self"] = ParamNode("self") + for p in method.args_names: + node = ParamNode(p) + parameters.append(node) + F_PARAM[node.id] = node + + for attr in AllTypes[type_name].get_attributes_as_dict().values(): + C_ATTRIBUTES[attr.attribute_name] = attr + + instruction = convert_expression(method.expression) + statements += instruction.node + + statements.append(ReturnNode(instruction.result.id)) + + MAX_LOCAL_COUNT = max(len(F_LOCALS), MAX_LOCAL_COUNT) + + _locals = F_LOCALS.copy() + locals_aux = [] + for key in _locals.keys(): + locals_aux += [_locals[key]] + + global MAX_PARAM_COUNT + MAX_PARAM_COUNT = max(MAX_PARAM_COUNT, len(parameters)) + + CODE.append(FunctionNode(f_name, parameters, locals_aux, statements)) + + result += CODE[-1].GetCode() + "\n\n" + + return result + + +# aqui se genera el codigo de inicializacion de los atributos de una clase +def generate_attributes_initialization(type_name, attributes): + result = "" + + code = FunctionNode(type_name + "_Attributes_Initialization", [ParamNode("instance")], [], []) + + body = [] + + global MAX_LOCAL_COUNT, F_PARAM, F_LOCALS, LABEL_COUNTER, D_LOCALS, V_TYPE, CURR_TYPE, C_ATTRIBUTES, LET_LOCALS + C_ATTRIBUTES = {} + F_LOCALS = {} + D_LOCALS = {} + V_TYPE = {} + LET_LOCALS = {} + CURR_TYPE = type_name + statements = [] + F_PARAM = {} + CASE_LOCALS = {} + + F_PARAM["self"] = ParamNode("instance") + + aux_local = get_local() + body += [AllocateNode("void", aux_local.id), ] + + for attr in attributes: + if attr.attribute_name == "self": + continue + C_ATTRIBUTES[attr.attribute_name] = attr + if attr.expression != None: + res = convert_expression(attr.expression) + body += res.node + body += [SetAttributeNode(type_name, 'instance', attr.attribute_name, res.result.id)] + else: + body += [SetAttributeNode(type_name, 'instance', attr.attribute_name, aux_local.id)] + + body += [ReturnNode(aux_local.id)] + + MAX_LOCAL_COUNT = max(len(F_LOCALS), MAX_LOCAL_COUNT) + + _locals = F_LOCALS.copy() + locals_aux = [] + for key in _locals.keys(): + locals_aux += [_locals[key]] + + code.locals = locals_aux + code.body = body + global CODE + CODE.append(code) + + result += CODE[-1].GetCode() + "\n\n" + + return result + + +# para convertir una expresion en codigo CIL +# (la expresion viene como se formo en el AST del parser) +def convert_expression(expression): + if type(expression) is AssignStatementNode: + return convert_assign(expression) + + elif type(expression) is ConditionalStatementNode: + return convert_conditional(expression) + + elif type(expression) is LoopStatementNode: + return convert_loop(expression) + + elif type(expression) is BlockStatementNode: + return convert_block(expression) + + elif type(expression) is LetStatementNode: + return convert_let(expression) + + elif type(expression) is CaseStatementNode: + return convert_case(expression) + + elif type(expression) is CaseBranchNode: + return convert_case_branch(expression) + + elif type(expression) is NewStatementNode: + return convert_new(expression) + + elif type(expression) is FunctionCallStatement: + return convert_function_call(expression) + + elif type(expression) is ConstantNumericNode: + return convert_integer(expression) + + elif type(expression) is ConstantStringNode: + return convert_string(expression) + + elif type(expression) is ConstantBoolNode: + return convert_bool(expression) + + elif type(expression) is VariableNode: + return convert_variable(expression) + + elif type(expression) is NotNode: + return convert_not(expression) + + elif type(expression) is IsVoidNode: + return convert_is_void(expression) + + elif type(expression) is ComplementNode: + return convert_complement(expression) + + elif type(expression) is LessEqualNode: + return convert_less_equal(expression) + + elif type(expression) is LessNode: + return convert_less(expression) + + elif type(expression) is EqualNode: + return convert_equal(expression) + + elif type(expression) is PlusNode: + return convert_binary_arithmetic_operation(expression) + + elif type(expression) is MinusNode: + return convert_binary_arithmetic_operation(expression) + + elif type(expression) is TimesNode: + return convert_binary_arithmetic_operation(expression) + + elif type(expression) is DivideNode: + return convert_binary_arithmetic_operation(expression) + + +# codigo para generar el CIL de una expresion case +def convert_case(case): + nodes = [] + expr = convert_expression(case.expression) + nodes += expr.node + expr_type_local = get_local() + nodes += [AllocateNode("Int", expr_type_local.id), + TypeOfNode(expr_type_local.id, expr.result.id)] + + case_types = [] + case_labels = [] + + # primero se ordenan las ramas de forma tal que las primeras tengan tipo + # con una mayor profundidad en el arbol de herencia, entonces se tomara la + # primera rama tal que el valor de la expresion herede del tipo de la rama + for i in range(0, len(case.body) - 1): + for j in range(i, len(case.body)): + if AllTypes[case.body[i].typeName].get_tree_depth() < AllTypes[case.body[j].typeName].get_tree_depth(): + aux = case.body[i] + case.body[i] = case.body[j] + case.body[j] = aux + + for c in case.body: + lcl_type_int = get_local() + nodes += [AllocateNode("Int", lcl_type_int.id), + TypeAddressNode(lcl_type_int.id, c.typeName)] + case_types.append(lcl_type_int) + case_labels.append(get_label()) + + result = get_local() + + for i, case_branch in enumerate(case.body): + predicate = get_local() + nodes.append(AllocateNode("Bool", predicate.id)) + nodes.append(IsSonNode(expr_type_local.id, case_types[i].id, predicate.id)) + nodes.append(IfGotoNode(predicate.id, case_labels[i])) + + end_label = get_label() + nodes.append(GotoNode(end_label)) + + global CASE_LOCALS + + for i, case_branch in enumerate(case.body): + nodes.append(LabelNode(case_labels[i])) + CASE_LOCALS[case_branch.id] = expr.result + branch = convert_expression(case_branch.expression) + nodes += branch.node + nodes.append(MovNode(result.id, branch.result.id)) + nodes.append(GotoNode(end_label)) + CASE_LOCALS.pop(case_branch.id) + + nodes.append(LabelNode(end_label)) + + return Node_Result(nodes, result) + + +# codigo para generar el CIL de una expresion de asignacion +def convert_assign(assign): + global C_ATTRIBUTES, CURR_TYPE, MAIN_LOCAL, LET_LOCALS, F_PARAM + + expr = convert_expression(assign.expression) + + if assign.id in LET_LOCALS: + node = expr.node + [MovNode(LET_LOCALS[assign.id].id, expr.result.id)] + + return Node_Result(node, LET_LOCALS[assign.id]) + + if assign.id in F_PARAM: + node = expr.node + [MovNode(F_PARAM[assign.id].id, expr.result.id)] + + return Node_Result(node, F_PARAM[assign.id]) + + if CURR_TYPE == "": + node = expr.node + [SetAttributeNode("Main", MAIN_LOCAL.id, assign.id, expr.result.id)] + return Node_Result(node, expr.result) + + if assign.id in C_ATTRIBUTES: + node = expr.node + [SetAttributeNode(CURR_TYPE, "self", assign.id, expr.result.id)] + return Node_Result(node, expr.result) + else: + node = expr.node + return Node_Result(node, expr.result) + + +# codigo para generar el CIL de una expresion de operacion aritmetica +def convert_binary_arithmetic_operation(op): + left = convert_expression(op.left) + right = convert_expression(op.right) + + node = [] + + result = get_local() + + node = left.node + right.node + [AllocateNode("Int", result.id)] + + if type(op) == ast.PlusNode: + node.append(AddNode(left.result.id, right.result.id, result.id)) + + elif type(op) == ast.MinusNode: + node.append(SubNode(left.result.id, right.result.id, result.id)) + + elif type(op) == ast.TimesNode: + node.append(MulNode(left.result.id, right.result.id, result.id)) + + elif type(op) == ast.DivideNode: + node.append(DivNode(left.result.id, right.result.id, result.id)) + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion condicional +def convert_conditional(expression): + predicate = convert_expression(expression.evalExpr) + + if_expr = convert_expression(expression.ifExpr) + + else_expr = convert_expression(expression.elseExpr) + + label_if = get_label() + label_else = get_label() + + result = get_local() + + node = predicate.node + [ + IfGotoNode(predicate.result.id, label_if)] + else_expr.node + [ + MovNode(result.id, else_expr.result.id), + GotoNode(label_else), + LabelNode(label_if)] + if_expr.node + [ + MovNode(result.id, if_expr.result.id), LabelNode(label_else)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion while +def convert_loop(loop): + predicate = convert_expression(loop.evalExpr) + + expr = convert_expression(loop.loopExpr) + + predicate_label = get_label() + expr_label = get_label() + end_label = get_label() + + result = get_local() + + node = [AllocateNode("void", result.id), LabelNode(predicate_label)] + predicate.node + [ + IfGotoNode(predicate.result.id, expr_label), + GotoNode(end_label), + LabelNode(expr_label)] + expr.node + [ + GotoNode(predicate_label), + LabelNode(end_label)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion de = +def convert_equal(equal): + left = convert_expression(equal.left) + right = convert_expression(equal.right) + + result = get_local() + + node = [AllocateNode("Bool", result.id)] + left.node + right.node + [ + ENode(left.result.id, right.result.id, result.id)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion de < +def convert_less(l): + left = convert_expression(l.left) + right = convert_expression(l.right) + + result = get_local() + + node = [AllocateNode("Bool", result.id)] + left.node + right.node + [ + LNode(left.result.id, right.result.id, result.id)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion <= +def convert_less_equal(le): + left = convert_expression(le.left) + right = convert_expression(le.right) + + result = get_local() + + node = [AllocateNode("Bool", result.id)] + left.node + right.node + [ + LENode(left.result.id, right.result.id, result.id)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una constante entera +def convert_integer(integer): + result = get_local() + + nodes = [AllocateNode("Int", result.id), MovNode(result.id, int(integer.lex))] + + return Node_Result(nodes, result) + + +# codigo para generar el CIL de una constante booleana +def convert_bool(bool): + result = get_local() + + if bool.lex == "true": + val = True + else: + val = False + + nodes = [AllocateNode("Bool", result.id), MovNode(result.id, val)] + + return Node_Result(nodes, result) + + +# codigo para generar el CIL de una variable en el codigo +def convert_variable(id): + global LET_LOCALS, F_PARAM, C_ATTRIBUTES, CURR_TYPE, MAIN_LOCAL, CASE_LOCALS + + if id.lex in CASE_LOCALS: + return Node_Result([], CASE_LOCALS[id.lex]) + + if id.lex in LET_LOCALS: + return Node_Result([], LET_LOCALS[id.lex]) + + if id.lex in F_PARAM: + return Node_Result([], F_PARAM[id.lex]) + + if CURR_TYPE == "": + result = get_local() + nodes = [GetAttributeNode("Main", MAIN_LOCAL.id, id.lex, result.id)] + return Node_Result(nodes, result) + + if id.lex in C_ATTRIBUTES: + result = get_local() + return Node_Result([GetAttributeNode(CURR_TYPE, "self", id.lex, result.id)], result) + + result = get_local() + + return Node_Result([AllocateNode("void", result.id)], result) + + +# codigo para generar el CIL de una expresion new +def convert_new(new_node): + global CURR_TYPE + + result = get_local() + nodes = [] + + if new_node.typeName == "SELF_TYPE": + if CURR_TYPE == "": + new_node.typeName = "Main" + else: + new_node.typeName = CURR_TYPE + + nodes.append(AllocateNode(new_node.typeName, result.id)) + + return Node_Result(nodes, result) + + +# codigo para generar el CIL de una cadena de caracteres +def convert_string(s): + result = get_local() + + node = [AllocateNode("String", result.id), LoadDataNode(result.id, DATA[s.lex].id)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion let +def convert_let(let): + global LET_LOCALS + nodes = [] + + for attr in let.variables: + if attr.expression: + a = convert_expression(attr.expression) + nodes += a.node + local = get_local() + nodes.append(MovNode(local.id, a.result.id)) + LET_LOCALS[attr.id] = local + else: + local = get_local() + nodes.append(AllocateNode(attr.typeName, local.id)) + LET_LOCALS[attr.id] = local + + expr = convert_expression(let.expression) + nodes += expr.node + + for attr in let.variables: + LET_LOCALS.pop(attr.id) + + return Node_Result(nodes, expr.result) + + +# codigo para generar el CIL de una expresion de complemeto de entero +def convert_complement(complement_node): + expr = convert_expression(complement_node.expression) + + result = get_local() + + node = [AllocateNode("Int", result.id)] + expr.node + [CmpNode(expr.result.id, result.id)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion not +def convert_not(not_node): + expr = convert_expression(not_node.expression) + + result = get_local() + + node = [AllocateNode("Bool", result.id)] + expr.node + [NtNode(expr.result.id, result.id)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion isvoid +def convert_is_void(isvoid): + expr = convert_expression(isvoid.expression) + + result = get_local() + + node = expr.node + [AllocateNode("Bool", result.id), VDNode(expr.result.id, result.id)] + + return Node_Result(node, result) + + +# codigo para generar el CIL de una expresion block +def convert_block(block): + nodes = [] + result = None + + for e in block.expressions: + expr = convert_expression(e) + nodes += expr.node + result = expr.result + + return Node_Result(nodes, result) + + +# codigo para generar el CIL de una llamada a una funcion +def convert_function_call(call): + global V_TYPE, CURR_TYPE + nodes = [] + + instance_is_self = type(call.instance) == VariableNode and call.instance.lex == "self" + + if not instance_is_self: + ins = convert_expression(call.instance) + nodes += ins.node + instance = ins.result.id + else: + if CURR_TYPE == "": + global MAIN_LOCAL + instance = MAIN_LOCAL.id + else: + global F_PARAM + instance = F_PARAM["self"].id + + arguments = [] + + # primero se genera el CIL de los argumentos + for a in call.args: + arg = convert_expression(a) + nodes += arg.node + arguments.append(arg.result) + + # se salvan los valores actuales de las variables locales + nodes += [LocalSaveNode()] + + nodes.append(ArgNode(instance)) + + for a in arguments: + nodes.append(ArgNode(a.id)) + + result = get_local() + + ins_type = get_local() + nodes += [AllocateNode("Int", ins_type.id)] + + if not call.dispatchType: + nodes += [TypeOfNode(ins_type.id, instance)] + nodes.append(DispatchCallNode(ins_type.id, call.function, result.id)) + else: + nodes.append(TypeAddressNode(ins_type.id, call.dispatchType)) + nodes.append(DispatchCallNode(ins_type.id, call.function, result.id)) + + return Node_Result(nodes, result) diff --git a/src/compiler.py b/src/compiler.py new file mode 100644 index 00000000..ae403068 --- /dev/null +++ b/src/compiler.py @@ -0,0 +1,121 @@ +from lexer import make_lexer +from parser import make_parser +from semantic import check_semantic +from cil_generator import generate_cil +from mips_generator import generate_mips +import sys + + +def main(): + cool_program_code = "" + + # se comprueba primero el fichero de entrada + if len(sys.argv) < 2: + print('Must contains cool file') + exit(1) + + p = sys.argv[1] + current = '' + index = 0 + while True: + if str(current).endswith(".cl"): + p = current + break + if index > len(p): + print("Cool program files must end with a \`.cl\` extension.\r\n") + exit(1) + break + current += p[index] + index += 1 + + # se hace una primera pasada por el texto de entrada para poner los \0 en \\0 + # para que puedan ser notados por el lexer ... asi mismo se hace con los \t para + # que puedan ser cambiados por la correspondiente cantidad de espacios en blanco + with open(str(p)) as file: + while True: + i = file.read(1) + if not i: + break + if i == '\0': + cool_program_code += r'\0' + continue + else: + if i == "\t": + cool_program_code += " " + continue + cool_program_code += i + + data = cool_program_code + + new_data = "" + + # se ponen en blanco los comentarios multilinea + i = 0 + while i < len(data): + if data[i] == '(' and i < len(data) - 1 and data[i + 1] == '*': + counter = 0 + j = i + 2 + new_data += " " + paster = "" + matched = False + while j < len(data) - 1: + if data[j] == '(' and data[j + 1] == '*': + counter += 1 + j += 2 + new_data += " " + continue + if data[j] == '*' and data[j + 1] == ')': + new_data += " " + if counter == 0: + matched = True + break + else: + counter -= 1 + if data[j] == '\n': + paster += "\n" + j += 1 + new_data += " " + if matched: + new_data += paster + i = j + 2 + continue + new_data += data[i] + i += 1 + + s = new_data + + lexer, errors = make_lexer(s) + + # se imprimen los errores del lexer + if len(errors) > 0: + for er in errors: + print(er) + exit(1) + + ast, errors = make_parser(s) + + # se imprimen ls errores del parser + if len(errors) > 0: + for er in errors: + print(er) + exit(1) + + errors, types = check_semantic(ast) + + # se imprimen los errores en el chequeo semantico + if len(errors) > 0: + for er in errors: + print(er) + exit(1) + + cil = generate_cil(types) + + mips = generate_mips(cil) + + # se crea el archivo mips correspondiente y se guarda el codigo generado + with open(p[:-2] + "mips", "w") as p: + p.write(mips) + + +if __name__ == "__main__": + main() diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f..05f5d7e5 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,11 +1,13 @@ -# Incluya aquí las instrucciones necesarias para ejecutar su compilador - -INPUT_FILE=$1 -OUTPUT_FILE=${INPUT_FILE:0: -2}mips - -# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos - -# Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +# Incluya aquí las instrucciones necesarias para ejecutar su compilador + +INPUT_FILE=$1 +OUTPUT_FILE=${INPUT_FILE:0: -2}mips + +# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto +echo "Cool ml-uh v0.0.1" +echo "Copyright (c) 2019: Lazaro Jesus Suarez Nuñez, Marcos Antonio Maceo Reyes" + +# Llamar al compilador +#echo "Compiling $INPUT_FILE into $OUTPUT_FILE" + +python compiler.py "$INPUT_FILE" diff --git a/src/graph.py b/src/graph.py new file mode 100644 index 00000000..a979193d --- /dev/null +++ b/src/graph.py @@ -0,0 +1,31 @@ +from collections import defaultdict + + +# clase para representar el grafo que se forma con los tipos +# y sus relaciones de herencia +class Graph: + def __init__(self, vertex): + self.graph = defaultdict(list) + self.vertex_id = [] + self.vertex = vertex + self.visited = [] + + def addNewEdge(self, item): + self.graph[item] = [] + + def addEdge(self, begin, end): + self.graph[begin].append(end) + + def dfs(self, vertex): + self.vertex_id = list(self.graph.keys()) + self.visited = [False] * len(self.vertex_id) + + self.dfs_util(vertex) + return self.visited.count(False) == 0 + + def dfs_util(self, vertex): + self.visited[self.vertex_id.index(vertex)] = True + + for x in self.graph[vertex]: + if not self.visited[self.vertex_id.index(x)]: + self.dfs_util(x) diff --git a/src/lexer.py b/src/lexer.py new file mode 100644 index 00000000..810504af --- /dev/null +++ b/src/lexer.py @@ -0,0 +1,490 @@ +from ply import lex as lex + +# para devolver los errores que se encuentren cuando se analice el texto de entrada +errors = [] + +# la llave es como aparecen las palabras clave en el codigo en COOL y el valor es +# el tipo de token que se genera +reserved = { + 'class': 'CLASS', + 'else': 'ELSE', + 'false': 'FALSE', + 'fi': 'FI', + 'if': 'IF', + 'inherits': 'INHERITS', + 'in': 'IN', + 'isvoid': 'ISVOID', + 'let': 'LET', + 'loop': 'LOOP', + 'pool': 'POOL', + 'then': 'THEN', + 'while': 'WHILE', + 'case': 'CASE', + 'esac': 'ESAC', + 'new': 'NEW', + 'of': 'OF', + 'not': 'NOT', + 'true': 'TRUE', +} + +# aqui estan los demas tipos de tokens que no corresponden a palabras clave +tokens = [ + 'NUMBER', + 'STRING', + 'PLUS', + 'MINUS', + 'TIMES', + 'DIVIDE', + 'LPAREN', + 'RPAREN', + 'ATTRIBUTEID', + 'CLASSID', + 'LBRACE', + 'RBRACE', + 'COMMA', + 'SEMICOLON', + 'COLON', + 'ASSIGNATION', + 'ARROW', + 'DOT', + 'LESS', + 'LESSEQUAL', + 'GREATER', + 'GREATEREQUAL', + 'EQUAL', + 'COMPLEMENT', + 'DISPATCH' + ] + list(reserved.values()) + +# estos son caracteres que se van a ignorar en la entrada +t_ignore = ' \t\r\v\f' + + +# aqui se leen las lineas que a partir de los -- y se ignoran hasta el siguiente \n +def t_INLINECOMMENT(t): + r'--.*' + pass + + +# por aqui comienza la maquina de estado para leer los comentarios multilinea +def t_start_comment(t): + r'\(\*' + t.lexer.push_state("COMMENT") + t.lexer.counter = 1 + t.lexer.star = False + t.lexer.lparen = False + + +# por aqui comienza la maquina de estado para leer valores de cadenas +def t_start_string(t): + r'"' + t.lexer.push_state("STRING") + t.lexer.string_backslashed = False + t.lexer.stringbuf = "" + t.lexer.string_containsNull = False + t.lexer.string_nullrow = 0 + t.lexer.string_nullcol = 0 + + +# las siguientes cuatro funciones forman los tokens de operaciones aritmeticas a medida +# que aparecen los correspondientes simbolos en la entrada +def t_PLUS(t): + r'\+' + t.value = '+' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_MINUS(t): + r'-' + t.value = '-' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_TIMES(t): + r'\*' + t.value = '*' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_DIVIDE(t): + r'/' + t.value = '/' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +################################################################################### + + +# las siguientes funciones forman los tokens de diferentes simbolos del lenguaje +def t_LPAREN(t): + r'\(' + t.value = '(' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_RPAREN(t): + r'\)' + t.value = ')' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_LBRACE(t): + r'\{' + t.value = '{' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_RBRACE(t): + r'\}' + t.value = '}' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_COMMA(t): + r',' + t.value = ',' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_SEMICOLON(t): + r';' + t.value = ';' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_COLON(t): + r':' + t.value = ':' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_ASSIGNATION(t): + r'<-' + t.value = '<-' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_ARROW(t): + r'=>' + t.value = '=>' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_DOT(t): + r'\.' + t.value = '.' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +###################################################################################### + +# aqui se forman los tokens que corresponden a operaciones de comparacion +# las funciones de LESSEQUAL y LESS tienen que definirse en este orden sino +# habra problemas ya que el simbolo de LESS es un prefijo de LESSEQUAL, igualmente +# pasa con GREATER y GREATEREQUAL +def t_LESSEQUAL(t): + r'<=' + t.value = '<=' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_LESS(t): + r'<' + t.value = '<' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_GREATEREQUAL(t): + r'>=' + t.value = '>=' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_GREATER(t): + r'>' + t.value = '>' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +def t_EQUAL(t): + r'=' + t.value = '=' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +########################################################################## + + +# a continuacion la funcion que forma los tokens de los simbolos de operaciones +# de complemento de entero +def t_COMPLEMENT(t): + r'~' + t.value = '~' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +# simbolo del tipo de dispatch alternativo +def t_DISPATCH(t): + r'@' + t.value = '@' + t.colno = find_column(t.lexer.lexdata, t) + return t + + +# aqui se forman los tokens de numeros que aparecen en la entrada +def t_NUMBER(t): + r'\d+' + t.value = int(t.value) + t.colno = find_column(t.lexer.lexdata, t) + return t + + +# esto define las diferentes maquinas de estado +states = ( + ('STRING', 'exclusive'), + ('COMMENT', 'exclusive'), +) + + +# cuando estamos leyendo un string si recivimos un \n entonces debe haber un \ antes +# (pues una cadena no puede saltarse de la linea) sino se devuelve un error +def t_STRING_newline(t): + r'\n' + global errors + t.lexer.lineno += 1 + if not t.lexer.string_backslashed: + errors.append( + "(%s, %s) - LexicographicError: Unterminated string constant" % (t.lineno, find_column(t.lexer.lexdata, t))) + t.lexer.pop_state() + else: + t.lexer.string_backslashed = False + + +# si estamos leyendo un comentario multilinea y encontramos el fin del codigo +# devolvemos un error +def t_COMMENT_eof(t): + r'\$' + global errors + s = t.lexer.lexdata + lineCount = s.count('\n') + 1 + posCount = 1 + i = len(s) - 1 + while i >= 0 and s[i] != '\n': + posCount += 1 + i -= 1 + errors.append("(%s, %s) - LexicographicError: EOF in comment" % (lineCount, find_column(t.lexer.lexdata, t) - 1)) + + +# si se encuentra un * y antes se habia leido un ( entoces aumenta en uno la cantidad +# de comentarios multilinea abiertos, sino marcamos que avimos un * por si el siguientes +# caracter es ) +def t_COMMENT_star(t): + r'\*' + if (t.lexer.lparen): + t.lexer.lparen = False + t.lexer.counter += 1 + else: + t.lexer.star = True + + +# si se encuentra un ( recordarlo por si aparece un *, y si habia un * antes marcar +# que ya no lo hay +def t_COMMENT_lparen(t): + r'\(' + t.lexerlparen = True + if (t.lexer.star): + t.lexer.start = False + + +# se devuelve un error si se encuentra un fin de fichero cuando se esta leyendo +# una cadena de caracteres +def t_STRING_eof(t): + r'\$' + global errors + errors.append("(%s, %s) - LexicographicError: EOF in string constant" % + (t.lineno, find_column(t.lexer.lexdata, t) - 1)) + t.lexer.pop_state() + + +# si se encuentra un ) ver si antes habia un *, en este caso disminuir en uno +# la cantidad de indexaciones de comentarios multilinea, si la cantidad actual +# se hace 0 entonces significa que ya terminamos de leer el comentario multilinea +# y salimos de la maquina de estado para continuar tokenizando con lo siguientes +# que se lea +def t_COMMENT_rparen(t): + r'\)' + if t.lexer.star: + t.lexer.star = False + t.lexer.counter -= 1 + if t.lexer.counter == 0: + t.lexer.pop_state() + + +# finalizamos la cadena cuando volvemos a encontrar otro " y se comprueba si se +# detecto algun \0 para devolver el error, al final se sale de la maquina de estado +def t_STRING_end(t): + r'"' + global errors + if not t.lexer.string_backslashed: + t.lexer.pop_state() + if t.lexer.string_containsNull: + errors.append("(%s, %s) - LexicographicError: String contains null character" % ( + t.lexer.string_nullrow, t.lexer.string_nullcol)) + t.lexer.skip(1) + else: + t.value = t.lexer.stringbuf + t.type = "STRING" + t.colno = find_column(t.lexer.lexdata, t) + return t + else: + t.lexer.stringbuf += '"' + t.lexer.string_backslashed = False + + +# cuando estamos leyendo un comentario multilinea todo lo que veamos lo ignoramos +def t_COMMENT_anything(t): + r'(.|\n)' + pass + + +# cuando estamos leyendo una cadena de caracteres comprobamos cuando aparecen caracteres +# especiales, y agragamos todo a la cadena actual que estamos leyendo +def t_STRING_anything(t): + r'[^\n]' + if t.lexer.string_backslashed: + if t.value == 'b': + t.lexer.stringbuf += '\b' + elif t.value == 't': + t.lexer.stringbuf += '\t' + elif t.value == 'n': + t.lexer.stringbuf += '\n' + elif t.value == 'f': + t.lexer.stringbuf += '\f' + elif t.value == '\\': + t.lexer.stringbuf += '\\' + elif t.value == '0': + t_STRING_error(t) + else: + t.lexer.stringbuf += t.value + t.lexer.string_backslashed = False + else: + if t.value != '\\': + t.lexer.stringbuf += t.value + else: + t.lexer.string_backslashed = True + + +t_STRING_ignore = '' + + +# si se esta leyendo una cadena y se encuentra un \0 se guarda el error y se sigue +# leyendo +def t_STRING_error(t): + t.lexer.string_containsNull = True + t.lexer.string_nullrow = t.lineno + t.lexer.string_nullcol = find_column(t.lexer.lexdata, t) - 1 + + +t_COMMENT_ignore = '' + + +# para cuando se encuentran caracteres que dan error como \0 se ignoran +def t_COMMENT_error(t): + t.lexer.counter = 0 + t.lexer.star = False + t.lexer.lparen = False + + +# aunque estemos leyendo un comentario se anotan los finales de linea pues despues +# sera necesario en el retorno de errores +def t_COMMENT_newline(t): + r'\n+' + t.lexer.lineno += len(t.value) + + +# para cuando se encuentran fines de linea en el codigo +def t_newline(t): + r'\n+' + t.lexer.lineno += len(t.value) + + +# aqui se forman los tokens de identificadoes de atributos y de palabras clave, +# notar que como se especifica en COOL los identificadores comienzan con minusculas +def t_ATTRIBUTEID(t): + r'[a-z][a-zA-Z_0-9]*' + if reserved.get(t.value.lower()) is None: + t.type = 'ATTRIBUTEID' + else: + t.type = reserved.get(t.value.lower()) + t.colno = find_column(t.lexer.lexdata, t) + return t + + +# aqui se forman los tokens de nombres de tipos y de valores booleanos que se encuentran +# en la entrada, pues ambos empiezan en mayusculas +def t_CLASSID(t): + r'[A-Z][a-zA-Z_0-9]*' + if reserved.get(t.value.lower()) is None or reserved.get(t.value.lower()) == 'TRUE' or reserved.get( + t.value.lower()) == 'FALSE': + t.type = 'CLASSID' + else: + t.type = reserved.get(t.value.lower()) + t.colno = find_column(t.lexer.lexdata, t) + return t + + +# para devolver la columna de un simbolo pues lexpos es el valor de offset dentro de la +# cadena con el codigo de entrada, entonces hay que buscar el \n proximo por la izquierda +# del simbolo en cuestion y contar la cantidad de caracteres +def find_column(input, token): + line_start = input.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + + +# si se encuentran errores de simbolos de entrada que no se han definido en las anteriores +# expresiones regulares, y no es el simbolo de final del texto, entonces retornar el error +# correspondiente +def t_error(t): + global errors + if not (t.value[0] == '$' and t.lexpos + 1 == len(t.lexer.lexdata)): + errors.append( + '(%s, %s) - LexicographicError: ERROR "%s"' % (t.lineno, find_column(t.lexer.lexdata, t), t.value[0])) + t.lexer.skip(1) + + +# esto es para ejecutar el lexer desde compiler.py +def make_lexer(data): + global errors + errors = [] + + data = data + '$' + lexer.input(data) + while True: + tok = lexer.token() + if not tok: + break + lexer.lineno = 1 + return lexer, errors + + +lexer = lex.lex() diff --git a/src/mips_generator.py b/src/mips_generator.py new file mode 100644 index 00000000..efd3ba6c --- /dev/null +++ b/src/mips_generator.py @@ -0,0 +1,1911 @@ +from cil_ast import * +from type_defined import * + +DATA = [] +CIL_TYPES = {} +LABELS_COUNT = 0 +PARAM_COUNT = 0 +MAX_LOCALS_COUNT = 0 +MAX_PARAMS_COUNT = 0 + + +def next_label(): + # para devolver algun nombre de etiqueta + global LABELS_COUNT + result = "custom_label_" + str(LABELS_COUNT) + LABELS_COUNT += 1 + return result + + +def generate_mips(cil): + global CIL_TYPES, LABELS_COUNT + LABELS_COUNT = 0 + CIL_TYPES = cil[0] + + # esto es llamado desde type_defined para ponerle a cada nombre de metodo + # un identificador + refresh_methods_id() + + data = generate_data(cil[1], cil[3], cil[4]) + code = generate_code(cil[2], cil[6]) + + return data + code + + +def generate_data(data, locals, max_param_count): + # aqui se genera lo que va en la seccion .data + global DATA, CIL_TYPES, MAX_LOCALS_COUNT, MAX_PARAMS_COUNT + MAX_PARAMS_COUNT = max_param_count + MAX_LOCALS_COUNT = locals + + if len(data) == 0 and locals == 0: + return "" + + result = ".data\n\n" + + result += "abort_msg: .asciiz \"Abort called from class \"\n" + result += "end_of_line: .asciiz \"\\n\"\n" + result += "string_read_buffer: .space 1024\n" + for t in CIL_TYPES.values(): + result += "type_" + t.type_name + ": .asciiz \"" + t.type_name + "\"\n" + + for l in range(0, locals): + result += "local_" + str(l) + ": .word 0, 0\n" + + for d in data.values(): + DATA.append(d.id) + result += d.id + ": .asciiz \"" + d.value.replace("\n", "\\n") + "\"\n" + + global PARAMS + + for i in range(0, max_param_count): + result += "param_" + str(i) + ": .word 0, 0\n" + PARAMS["param_" + str(i)] = None + + result += "\n\n" + + return result + + +PARAMS = {} +CURR_PARAM_COUNT = 0 +CURR_FUNC = "" + + +def generate_locals_write(): + # se le pasa en $s0 la cantidad de variables a escribir en la funcion que la llama + result = "locals_write:\n" + result += "addi $t7, $s0, 0\n" + + locals_write_end_label = next_label() + + counter = 0 + + for i in range(0, MAX_LOCALS_COUNT): + result += "beq $t7, 0, " + locals_write_end_label + "\n" + name = "local_" + str(counter) + result += "la $t1, " + name + "\n" + result += "lw $t0, ($t1)\n" + result += "sw $t0, ($sp)\n" + result += "lw $t0, 4($t1)\n" + result += "sw $t0, 4($sp)\n" + result += "addi $sp, $sp, 8\n" + result += "addi $t7, $t7, -1\n" + counter += 1 + + result += locals_write_end_label + ":\n" + + result += "jr $ra\n\n" + + return result + + +def generate_locals_load(): + # se le pasa en s0 la cantidad de variables a leer en la funcion que la llama + result = "locals_load:\n" + result += "addi $t7, $s0, 0\n" + + locals_load_end_label = next_label() + + counter = 0 + + result += "li $t6, 8\n" + + global MAX_LOCALS_COUNT + for i in range(0, MAX_LOCALS_COUNT): + result += "beq $t7, 0, " + locals_load_end_label + "\n" + name = "local_" + str(counter) + result += "mult $t7, $t6\n" + result += "mflo $t5\n" + result += "sub $t4, $sp, $t5\n" + result += "lw $t0, ($t4)\n" + result += "addi $t5, $t5, -4\n" + result += "sub $t4, $sp, $t5\n" + result += "lw $t1, ($t4)\n" + result += "la $t2, " + name + "\n" + result += "sw $t0, ($t2)\n" + result += "sw $t1, 4($t2)\n" + result += "addi $t7, $t7, -1\n" + counter += 1 + + result += locals_load_end_label + ":\n" + result += "mult $s0, $t6\n" + result += "mflo $t5\n" + result += "sub $sp, $sp, $t5\n" + + result += "jr $ra\n\n" + + return result + + +def generate_params_load(): + # en s0 te mandan la cantidad de parametros a cargar + result = "params_load:\n" + + result += "addi $t7, $s0, 0\n" + result += "li $t6, 8\n" + result += "mult $t7, $t6\n" + result += "mflo $t5\n" + result += "addi $t5, 4\n" + result += "sub $t6, $sp, $t5\n" + + counter = 0 + + params_load_end_label = next_label() + + global MAX_PARAMS_COUNT + for i in range(0, MAX_PARAMS_COUNT): + result += "sub $t5, $sp, $t6\n" + result += "blt $t5, 8, " + params_load_end_label + "\n" + name = "param_" + str(counter) + result += "lw $t0, ($t6)\n" + result += "addi $t6, 4\n" + result += "lw $t1, ($t6)\n" + result += "addi $t6, 4\n" + result += "la $t2, " + name + "\n" + result += "sw $t0, ($t2)\n" + result += "sw $t1, 4($t2)\n" + counter += 1 + + result += params_load_end_label + ":\n" + result += "jr $ra\n\n" + + return result + + +def generate_allocate_Int(): + # en s0 esta la direccion en donde se va a poner un entero + result = "allocate_Int:\n" + + result += "la $t1, type_Int\n" + result += "sw $t1, ($s0)\n" + result += "li $t1, 0\n" + result += "sw $t1, 4($s0)\n" + + result += "jr $ra\n\n" + + return result + + +# en s0 esta la direccion en donde se va a poner un booleano +def generate_allocate_Bool(): + result = "allocate_Bool:\n" + + result += "la $t1, type_Bool\n" + result += "sw $t1, ($s0)\n" + result += "li $t1, 0\n" + result += "sw $t1, 4($s0)\n" + + result += "jr $ra\n\n" + + return result + + +# en s0 esta la direccion en donde se va a poner una cadena +def generate_allocate_String(): + result = "allocate_String:\n" + + result += "la $t1, type_String\n" + result += "sw $t1, ($s0)\n" + result += "li $a0, 5\n" + result += "li $v0, 9\n" + result += "syscall\n" + result += "sw $v0, 4($s0)\n" + result += "li $t1, 0\n" + result += "sw $t1, ($v0)\n" + result += "sb $t1, 4($v0)\n" + + result += "jr $ra\n\n" + + return result + + +# en s0 esta la direccion en donde se va a poner void +def generate_allocate_Void(): + result = "allocate_Void:\n" + + result += "li $t1, 0\n" + result += "sw $t1, ($s0)\n" + result += "sw $t1, 4($s0)\n" + + result += "jr $ra\n\n" + + return result + + +# en s0 y s1 estan la direccion de dos variables donde estan los objetos que se +# quiere saber si el tipo de uno hereda del tipo del otro +def generate_is_descendant(son_father_tuples): + result = "is_descendant:\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + + result += "li $v0, 0\n" + + is_descendant_end_label = next_label() + + for t in son_father_tuples: + is_descendant_next_label = next_label() + result += "la $t2, type_" + t[0] + "\n" + result += "la $t3, type_" + t[1] + "\n" + result += "seq $t2, $t2, $s0\n" + result += "seq $t3, $t3, $s1\n" + result += "add $t2, $t2, $t3\n" + result += "bne $t2, 2, " + is_descendant_next_label + "\n" + result += "li $v0, 1\n" + result += "j " + is_descendant_end_label + "\n" + result += is_descendant_next_label + ":\n" + + result += is_descendant_end_label + ":\n" + + result += "sw $v0, 4($s7)\n" + + result += "jr $ra\n\n" + + return result + + +# se entran $s0, $s1 y $s2, la primera es la +# direccion inicio desde donde copiar, la segunda es el offset, o sea, +# cuantos elementos copiar, y la tercera la direccion donde se va a pegar +def generate_copy_from_to(): + result = "copy_from_to:\n" + + copy_from_to_loop_label = next_label() + copy_from_to_end_label = next_label() + + result += copy_from_to_loop_label + ":\n" + result += "beq $s1, 0, " + copy_from_to_end_label + "\n" + result += "lb $t3, ($s0)\n" + result += "sb $t3, ($s2)\n" + result += "addi $s1, $s1, -1\n" + result += "addi $s0, $s0, 1\n" + result += "addi $s2, $s2, 1\n" + result += "j " + copy_from_to_loop_label + "\n" + result += copy_from_to_end_label + ":\n" + + result += "jr $ra\n\n" + + return result + + +# se entra en s0 la direccion de comienzo de una cadena y devuelve en s1 la cantidad +# de caracteres hasta llegar al final de la cadena +def generate_get_length(): + result = "get_length:\n" + + end_label = next_label() + loop_label = next_label() + + result += "li $s1, 0\n" + result += loop_label + ":\n" + result += "lb $t0, ($s0)\n" + result += "beq $t0, 0, " + end_label + "\n" + result += "addi $s1, $s1, 1\n" + result += "addi $s0, $s0, 1\n" + result += "j " + loop_label + "\n" + result += end_label + ":\n" + + result += "jr $ra\n\n" + + return result + + +# se entran s0, s1 y s2 .. los dos primeros son las direcciones de las variables +# a comparar y la ultima es la direccion de la variable booleana donde poner el resultado +def generate_equality_function(): + result = "equality_function:\n" + + compare_strings_label = next_label() + compare_strings_false_label = next_label() + compare_strings_loop_label = next_label() + equal_end_label = next_label() + + result += "lw $t3, ($s0)\n" + result += "la $t4, type_String\n" + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + result += "beq $t3, $t4, " + compare_strings_label + "\n" + result += "seq $s0, $s0, $s1\n" + result += "j " + equal_end_label + "\n" + result += compare_strings_label + ":\n" + result += "lw $t3, ($s0)\n" + result += "lw $t4, ($s1)\n" + result += "bne $t3, $t4, " + compare_strings_false_label + "\n" + result += "li $t3, 0\n" + result += "li $t4, 0\n" + result += compare_strings_loop_label + ":\n" + result += "lb $t3, 4($s0)\n" + result += "lb $t4, 4($s1)\n" + result += "bne $t3, $t4, " + compare_strings_false_label + "\n" + result += "addi $s0, $s0, 1\n" + result += "addi $s1, $s1, 1\n" + result += "bne $t3, 0, " + compare_strings_loop_label + "\n" + result += "li $s0, 1\n" + result += "j " + equal_end_label + "\n" + result += compare_strings_false_label + ":\n" + result += "li $s0, 0\n" + result += equal_end_label + ":\n" + result += "sw $s0, 4($s2)\n" + + result += "jr $ra\n\n" + + return result + + +# se manda en s0 la variable de la instancia que llama al abort +def generate_abort(): + result = "abort:\n" + + # write abort message + result += "la $a0, abort_msg\n" + result += "li $v0, 4\n" + result += "syscall\n" + + # write type who called abort + result += "lw $a0, 4($s0)\n" + result += "li $v0, 4\n" + result += "syscall\n" + + # write an end of line + result += "la $a0, end_of_line\n" + result += "li $v0, 4\n" + result += "syscall\n" + + result += "li $v0, 10\n" + result += "syscall\n" + + result += "jr $ra\n\n" + + return result + + +# esta funcion pone en la variable que esta en s1 el contenido de la variable +# que esta en s0 (por referencia si es un objeto o cadena de caracteres) +def generate_mov_func(): + result = "mov_func:\n" + + result += "lw $t2, ($s0)\n" + result += "sw $t2, ($s1)\n" + + result += "lw $t2, 4($s0)\n" + result += "sw $t2, 4($s1)\n" + + result += "jr $ra\n\n" + + return result + + +# mandas en s0 la direccion de una variable y te agraga su informacion a la pila +def generate_put_argument(): + result = "put_argument:\n" + + result += "lw $t1, ($s0)\n" + result += "sw $t1, ($sp)\n" + result += "lw $t1, 4($s0)\n" + result += "sw $t1, 4($sp)\n" + result += "addi $sp, $sp, 8\n" + + result += "jr $ra\n\n" + + return result + + +# mandas en s0 la direccion de la primera cadena y en s6 la de la segunda cadena +# y te devuelve en s2 la direccion de la variable con la cadena resultante +def generate_concat_func(): + result = "concat_func:\n" + + result += "sw $ra, ($sp)\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s6, 4($s6)\n" + + result += "lw $s1, ($s0)\n" + result += "lw $t7, ($s6)\n" + result += "add $a0, $s1, $t7\n" + result += "addi $a0, $a0, 5\n" + result += "li $v0, 9\n" + result += "syscall\n" + + result += "sw $v0, 4($s2)\n" + result += "addi $a0, $a0, -5\n" + result += "sw $a0, ($v0)\n" + result += "addi $s2, $v0, 4\n" + + result += "addi $s0, $s0, 4\n" + result += "jal copy_from_to\n" + + result += "addi $s0, $s6, 4\n" + result += "addi $s1, $t7, 1\n" + result += "jal copy_from_to\n" + + result += "lw $ra, ($sp)\n" + + result += "jr $ra\n\n" + + return result + + +# mandas en s0 la direccion de la variable a imprimir (la variable tiene que ser +# de tipo cadena de caracteres) +def generate_print_func(): + result = "print_func:\n" + + result += "addi $a0, $s0, 0\n" + result += "lw $a0, 4($a0)\n" + result += "addi $a0, $a0, 4\n" + + result += "li $v0, 4\n" + result += "syscall\n" + + result += "jr $ra\n\n" + + return result + + +# mandas en s0 la direccion de una variable entera y te imprime el entero +def generate_print_int_func(): + result = "print_int_func:\n" + + result += "lw $a0, 4($s0)\n" + result += "li $v0, 1\n" + result += "syscall\n" + + result += "jr $ra\n\n" + + return result + + +# lee un entero y lo pone en la variable entera de direccion s0 +def generate_read_int_func(): + result = "read_int_func:\n" + + result += "li $v0, 5\n" + result += "syscall\n" + + result += "sw $v0, 4($s0)\n" + + result += "jr $ra\n\n" + + return result + + +# mandas en s0 la direccion de comienzo de una cadena de caracteres +# y en s2 la direccion de la variable donde quieres ponerla +def generate_load_data_func(): + result = "load_data_func:\n" + + result += "sw $ra, ($sp)\n" + + result += "addi $t7, $s0, 0\n" + result += "jal get_length\n" + result += "addi $s0, $t7, 0\n" + + result += "addi $a0, $s1, 5\n" + result += "li $v0, 9\n" + result += "syscall\n" + + result += "sw $v0, 4($s2)\n" + result += "sw $s1, ($v0)\n" + + result += "addi $s1, $s1, 1\n" + result += "addi $s2, $v0, 4\n" + + result += "jal copy_from_to\n" + + result += "lw $ra, ($sp)\n" + + result += "jr $ra\n\n" + + return result + + +# se lee una cadena de caracteres y se pone en la variable de direccion s2 +def generate_read_string_func(): + result = "read_string_func:\n" + + result += "sw $ra, ($sp)\n" + + result += "la $a0, string_read_buffer\n" + result += "li $v0, 8\n" + result += "syscall\n" + + result += "la $s0, string_read_buffer\n" + + result += "jal get_length\n" + + result += "la $s0, string_read_buffer\n" + + wasnt_empty_string_label = next_label() + + result += "bne $s1, 0, " + wasnt_empty_string_label + "\n" + + result += "addi $s1, $s1, 1\n" + + result += wasnt_empty_string_label + ":\n" + + result += "addi $a0, $s1, 4\n" + result += "li $v0, 9\n" + result += "syscall\n" + + result += "sw $v0, 4($s2)\n" + result += "addi $s1, $s1, -1\n" + result += "sw $s1, ($v0)\n" + + result += "addi $s2, $v0, 4\n" + + result += "jal copy_from_to\n" + + result += "li $t3, 0\n" + result += "sb $t3, ($s2)\n" + + result += "lw $ra, ($sp)\n" + + result += "jr $ra\n\n" + + return result + + +def generate_copy_func(): + # se mandan en s6 y s7 las direcciones de las variables de origen y destino de la + # copia (se copia solo el valor, o sea se crea una copia) + result = "copy_func:\n" + + result += "sw $ra, ($sp)\n" + + result += "lw $t2, ($s6)\n" + result += "sw $t2, ($s7)\n" + + copy_int_or_bool_label = next_label() + copy_string_label = next_label() + copy_string_loop_start_label = next_label() + copy_object_loop_start_label = next_label() + copy_end_label = next_label() + + result += "la $t3, type_Int\n" + result += "beq $t2, $t3, " + copy_int_or_bool_label + "\n" + result += "la $t3, type_Bool\n" + result += "beq $t2, $t3, " + copy_int_or_bool_label + "\n" + result += "la $t3, type_String\n" + result += "beq $t2, $t3, " + copy_string_label + "\n" + result += "lw $s0, 4($s6)\n" + result += "lw $a0, ($s0)\n" + result += "li $v0, 9\n" + result += "syscall\n" + result += "sw $v0, 4($s7)\n" + result += "addi $s2, $v0, 0\n" + result += "add $s1, $a0, 0\n" + result += "jal copy_from_to\n" + result += "j " + copy_end_label + "\n" + result += copy_int_or_bool_label + ":\n" + result += "lw $t2, 4($s6)\n" + result += "sw $t2, 4($s7)\n" + result += "j " + copy_end_label + "\n" + result += copy_string_label + ":\n" + result += "lw $s0, 4($s6)\n" + result += "lw $t6, ($s0)\n" + result += "addi $a0, $t6, 5\n" + result += "li $v0, 9\n" + result += "syscall\n" + result += "sw $v0, 4($s7)\n" + result += "addi $s2, $v0, 0\n" + result += "add $s1, $a0, 0\n" + result += "jal copy_from_to\n" + result += copy_end_label + ":\n" + + result += "lw $ra, ($sp)\n" + + result += "jr $ra\n\n" + + return result + + +# en s0 esta la direccion de la variable de cadena, en s7 la posicion en donde +# empieza la subcadena (una variable entera), en s1 la cantidad de caracteres (otra +# variable entera) y en s6 la direccion de la variable local de destino +def generate_substring_func(): + result = "substring_func:\n" + + result += "sw $ra, ($sp)\n" + + result += "lw $s0, 4($s0)\n" + result += "addi $s0, $s0, 4\n" + result += "lw $s7, 4($s7)\n" + result += "add $s0, $s0, $s7\n" + result += "lw $s1, 4($s1)\n" + + result += "addi $a0, $s1, 5\n" + result += "li $v0, 9\n" + result += "syscall\n" + + result += "sw $v0, 4($s6)\n" + result += "sw $s1, ($v0)\n" + result += "addi $s2, $v0, 4\n" + result += "jal copy_from_to\n" + result += "li $t3, 0\n" + result += "sb $t3, ($s2)\n" + + result += "lw $ra, ($sp)\n" + + result += "jr $ra\n\n" + + return result + + +# en las dos siguientes funciones se mandan en s0 y s1 las direcciones de las variables +# que se quieren comparar y en s2 se retorna la direccion de la variable booleana +# donde quedara el valor del resultado correspondiente de la operacion + +def generate_less_func(): + result = "less_func:\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + result += "slt $s0, $s0, $s1\n" + result += "sw $s0, 4($s2)\n" + + result += "jr $ra\n\n" + + return result + + +def generate_less_equal_func(): + result = "less_equal_func:\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + result += "sle $s0, $s0, $s1\n" + result += "sw $s0, 4($s2)\n" + + result += "jr $ra\n\n" + + return result + + +################################################################################## + +# en las cuatro siguientes funciones se mandan en s0 y s1 las direcciones de las variables +# con las que se quiere operar y en s2 se retorna la direccion de la variable entera +# donde quedara el valor del resultado correspondiente de la operacion + +def generate_divide_func(): + result = "divide_func:\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + result += "div $s0, $s0, $s1\n" + result += "sw $s0, 4($s2)\n" + + result += "jr $ra\n\n" + + return result + + +def generate_multiply_func(): + result = "multiply_func:\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + result += "mult $s0, $s1\n" + result += "mflo $s0\n" + result += "sw $s0, 4($s2)\n" + + result += "jr $ra\n\n" + + return result + + +def generate_substract_func(): + result = "substract_func:\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + result += "sub $s0, $s0, $s1\n" + result += "sw $s0, 4($s2)\n" + + result += "jr $ra\n\n" + + return result + + +def generate_add_func(): + result = "add_func:\n" + + result += "lw $s0, 4($s0)\n" + result += "lw $s1, 4($s1)\n" + result += "add $s0, $s0, $s1\n" + result += "sw $s0, 4($s2)\n" + + result += "jr $ra\n\n" + + return result + + +####################################################################################### + +# se manda en s0 la direccion de la variable del objeto al que se le quiere llamar un metodo +# y en s1 el valor entero del identificador que corresponde al metodo que se quiere llamar +# ... y se devuelve en s3 la direccion del metodo correspondiente que se debe llamar +def generate_call_func(): + result = "call_func:\n" + + result += "lw $s0, 4($s0)\n" + + call_func_end_label = next_label() + + for t in CIL_TYPES.values(): + + is_not_this_type_label = next_label() + + result += "la $t1, type_" + t.type_name + "\n" + result += "bne $s0, $t1, " + is_not_this_type_label + "\n" + + for m in t.methods.keys(): + is_not_this_method_label = next_label() + + result += "bne $s1, " + str(METHODS_NAME_TO_ID[m]) + ", " + is_not_this_method_label + "\n" + + result += "la $s3, " + t.methods[m] + "_" + m + "\n" + + result += "j " + call_func_end_label + "\n" + + result += is_not_this_method_label + ":\n" + + result += is_not_this_type_label + ":\n" + + result += call_func_end_label + ":\n" + + result += "jr $ra\n\n" + + return result + + +# aqui se genera el codigo correspondiente a la seccion .text +def generate_code(functions_code, son_father_tuples): + result = ".text\n\n" + + # aqui se generan funciones para casi cada operacion en CIL + # esto para que solamente sea mandarles la direccion de las + # variables locales a operar + result += generate_allocate_Int() + result += generate_allocate_Bool() + result += generate_allocate_String() + result += generate_allocate_Void() + result += generate_locals_write() + result += generate_locals_load() + result += generate_params_load() + result += generate_copy_from_to() + result += generate_get_length() + result += generate_equality_function() + result += generate_abort() + result += generate_is_descendant(son_father_tuples) + result += generate_mov_func() + result += generate_put_argument() + result += generate_concat_func() + result += generate_print_func() + result += generate_read_int_func() + result += generate_read_string_func() + result += generate_load_data_func() + result += generate_print_int_func() + result += generate_copy_func() + result += generate_substring_func() + result += generate_less_func() + result += generate_less_equal_func() + result += generate_divide_func() + result += generate_multiply_func() + result += generate_substract_func() + result += generate_add_func() + result += generate_call_func() + + global PARAMS, CURR_LOCAL_COUNT, CURR_PARAM_COUNT, PARAM_COUNT, CURR_FUNC + + for f in functions_code: + CURR_PARAM_COUNT = 0 + CURR_LOCAL_COUNT = len(f.locals) + CURR_FUNC = f.name + + result += f.name + ":\n" + + counter = len(f.params) + + # al principio de una funcion cargar los parametros desde la pila + for p in f.params: + param_name = "param_" + str(CURR_PARAM_COUNT) + PARAM_COUNT += 1 + PARAMS[p.id] = param_name + result += "lw $t0, -" + str(8 * counter) + "($sp)\n" + result += "lw $t1, -" + str(8 * counter - 4) + "($sp)\n" + result += "la $t2, " + param_name + "\n" + result += "sw $t0, ($t2)\n" + result += "sw $t1, 4($t2)\n" + + counter -= 1 + CURR_PARAM_COUNT += 1 + + # guardar la direccion de retorno en la pila + if len(f.params) > 0: + result += "sw $ra, ($sp)\n" + result += "addi $sp, $sp, 4\n" + + for i in f.body: + result += convert_cil_instruction(i) + + result += "\n\n" + + result = result[0:-2] + "li $v0, 10\n" + result += "syscall\n" + + return result + + +def convert_cil_instruction(instruction): + # se recive un nodo del AST del CIL para generar el codigo MIPS correspondiente + if type(instruction) == PrintNode: + return convert_PrintNode(instruction) + + elif type(instruction) == PrintIntNode: + return convert_PrintIntNode(instruction) + + elif type(instruction) == TypeNameNode: + return convert_TypeNameNode(instruction) + + elif type(instruction) == TypeAddressNode: + return convert_TypeAddressNode(instruction) + + elif type(instruction) == ReturnNode: + return convert_ReturnNode(instruction) + + elif type(instruction) == ReadNode: + return convert_ReadNode(instruction) + + elif type(instruction) == ReadIntNode: + return convert_ReadIntNode(instruction) + + elif type(instruction) == TypeOfNode: + return convert_TypeOfNode(instruction) + + elif type(instruction) == CopyNode: + return convert_CopyNode(instruction) + + elif type(instruction) == StrlenNode: + return convert_StrlenNode(instruction) + + elif type(instruction) == StrcatNode: + return convert_StrcatNode(instruction) + + elif type(instruction) == StrsubNode: + return convert_StrsubNode(instruction) + + elif type(instruction) == AllocateNode: + return convert_AllocateNode(instruction) + + elif type(instruction) == ArgNode: + return convert_ArgNode(instruction) + + elif type(instruction) == DispatchCallNode: + return convert_DispatchCallNode(instruction) + + elif type(instruction) == SetAttributeNode: + return convert_SetAttributeNode(instruction) + + elif type(instruction) == GetAttributeNode: + return convert_GetAttributeNode(instruction) + + elif type(instruction) == ENode: + return convert_ENode(instruction) + + elif type(instruction) == LNode: + return convert_LNode(instruction) + + elif type(instruction) == LENode: + return convert_LENode(instruction) + + elif type(instruction) == DivNode: + return convert_DivNode(instruction) + + elif type(instruction) == MulNode: + return convert_MulNode(instruction) + + elif type(instruction) == SubNode: + return convert_SubNode(instruction) + + elif type(instruction) == AddNode: + return convert_AddNode(instruction) + + elif type(instruction) == VDNode: + return convert_VDNode(instruction) + + elif type(instruction) == AbortNode: + return convert_AbortNode(instruction) + + elif type(instruction) == CmpNode: + return convert_CmpNode(instruction) + + elif type(instruction) == NtNode: + return convert_NtNode(instruction) + + elif type(instruction) == MovNode: + return convert_MovNode(instruction) + + elif type(instruction) == IfGotoNode: + return convert_IfGotoNode(instruction) + + elif type(instruction) == GotoNode: + return convert_GotoNode(instruction) + + elif type(instruction) == LabelNode: + return convert_LabelNode(instruction) + + elif type(instruction) == LoadDataNode: + return convert_LoadDataNode(instruction) + + elif type(instruction) == LocalSaveNode: + return convert_LocalSaveNode(instruction) + + elif type(instruction) == IsSonNode: + return convert_IsSonNode(instruction) + + else: + print(str(type(instruction)) + " doesn't have convert method") + return "" + + +# en la mayoria de los siguientes metodos lo que se hace es llamar a una funcion ya generada +# previamente pasando argumentos por los registros de salvado (los $sx) .. esto se hace para +# que no se generen tantas instrucciones MIPS innecesarias + +def convert_IsSonNode(instruction): + result = "" + + global PARAMS + + if instruction.son in PARAMS: + son = PARAMS[instruction.son] + else: + son = instruction.son + + if instruction.father in PARAMS: + father = PARAMS[instruction.father] + else: + father = instruction.father + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s0, " + son + "\n" + result += "la $s1, " + father + "\n" + result += "la $s7, " + dest + "\n" + + result += "jal is_descendant\n" + + return result + + +def convert_TypeNameNode(instruction): + global DATA, PARAMS + + result = "" + + if instruction.type_addr in PARAMS: + t_addr = PARAMS[instruction.type_addr] + else: + t_addr = instruction.type_addr + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s0, " + t_addr + "\n" + result += "lw $s0, 4($s0)\n" + # poner en $s1 el tamanyo del string que comienza en ($s0) + result += "jal get_length\n" + + result += "la $s0, " + t_addr + "\n" + result += "lw $s0, 4($s0)\n" + + result += "la $s2, " + dest + "\n" + + result += "addi $a0, $s1, 5\n" + result += "li $v0, 9\n" + result += "syscall\n" + + result += "sw $v0, 4($s2)\n" + result += "sw $s1, ($v0)\n" + + result += "addi $s2, $v0, 4\n" + result += "addi $s1, $s1, 1\n" + + # copio deste ($s0) una cantidad de ($s1) bytes en ($s3) + result += "jal copy_from_to\n" + + return result + + +def convert_TypeAddressNode(instruction): + global DATA, PARAMS + + result = "" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $t0, type_" + instruction.type_name + "\n" + result += "la $t1, " + dest + "\n" + result += "sw $t0, 4($t1)\n" + + return result + + +def convert_PrintIntNode(instruction): + global DATA, PARAMS + + result = "" + + if instruction.val in PARAMS: + dest = PARAMS[instruction.val] + else: + dest = instruction.val + + result += "la $s0, " + dest + "\n" + result += "jal print_int_func\n" + + return result + + +def convert_LocalSaveNode(instruction): + global CURR_LOCAL_COUNT + result = "li $s0, " + str(CURR_LOCAL_COUNT) + "\n" + + result += "jal locals_write\n" + return result + + +def convert_LoadDataNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s0, " + instruction.data + "\n" + + result += "la $s2, " + dest + "\n" + + result += "jal load_data_func\n" + + return result + + +def convert_ReadNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s2, " + dest + "\n" + + result += "jal read_string_func\n" + + return result + + +def convert_ReadIntNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s0, " + dest + "\n" + + result += "jal read_int_func\n" + + return result + + +def convert_PrintNode(instruction): + result = "" + + global DATA + + if instruction.str in PARAMS: + dest = PARAMS[instruction.str] + else: + dest = instruction.str + + # ponemos en a0 la direccion del string + result += "la $s0, " + dest + "\n" + + result += "jal print_func\n" + + return result + + +def convert_ReturnNode(instruction): + result = "" + + global DATA, PARAMS + + # devolvemos en v0 la direccion en memoria del valor de retorno, si es void seria 0 + + if instruction.return_value == "": + result += "li $v0, 0\n" + else: + if instruction.return_value in PARAMS: + dest = PARAMS[instruction.return_value] + else: + dest = instruction.return_value + + result += "la $v0, " + dest + "\n" + + global CURR_PARAM_COUNT + + result += "lw $ra, -4($sp)\n" + result += "addi $sp, $sp, -" + str(8 * CURR_PARAM_COUNT + 4) + "\n" + + result += "jr $ra\n" + + return result + + +def convert_TypeOfNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.variable in PARAMS: + val = PARAMS[instruction.variable] + else: + val = instruction.variable + + result += "la $t0, " + val + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $t1, " + dest + "\n" + + result += "lw $t2, ($t0)\n" + result += "sw $t2, 4($t1)\n" + + return result + + +def convert_CopyNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + result += "la $s6, " + val + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s7, " + dest + "\n" + + result += "jal copy_func\n" + + return result + + +def convert_StrlenNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.str in PARAMS: + val = PARAMS[instruction.str] + else: + val = instruction.str + + # en t0 va la direccion del string + result += "la $t0, " + val + "\n" + result += "lw $t0, 4($t0)\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + # en $t1 va la direccion donde pondremos el valor de salida + result += "la $t1, " + dest + "\n" + + result += "lw $t2, ($t0)\n" + result += "sw $t2, 4($t1)\n" + + return result + + +def convert_StrcatNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.str_a in PARAMS: + val_a = PARAMS[instruction.str_a] + else: + val_a = instruction.str_a + + # en s0 va la direccion del primer string + result += "la $s0, " + val_a + "\n" + + if instruction.str_b in PARAMS: + val_b = PARAMS[instruction.str_b] + else: + val_b = instruction.str_b + + # en s6 va la direccion del segundo string + result += "la $s6, " + val_b + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + # en s2 va la direccion del string resultante + result += "la $s2, " + dest + "\n" + + result += "jal concat_func\n" + + return result + + +def convert_StrsubNode(instruction): + global DATA, PARAMS + + result = "" + + if instruction.str in PARAMS: + val = PARAMS[instruction.str] + else: + val = instruction.str + + if instruction.i in PARAMS: + index = PARAMS[instruction.i] + else: + index = instruction.i + + if instruction.len in PARAMS: + lenght = PARAMS[instruction.len] + else: + lenght = instruction.len + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s0, " + val + "\n" + result += "la $s7, " + index + "\n" + result += "la $s1, " + lenght + "\n" + result += "la $s6, " + dest + "\n" + + result += "jal substring_func\n" + + return result + + +def convert_AllocateNode(instruction): + result = "" + + global DATA, PARAMS, CIL_TYPES + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s0, " + dest + "\n" + + if instruction.type == "Int": + + result += "jal allocate_Int\n" + + elif instruction.type == "Bool": + + result += "jal allocate_Bool\n" + + elif instruction.type == "String": + + result += "jal allocate_String\n" + + elif instruction.type == "void": + + result += "jal allocate_Void\n" + + else: + result += "la $t1, type_" + instruction.type + "\n" + result += "sw $t1, ($s0)\n" + + object_size = 4 + attrs = [] + for a in CIL_TYPES[instruction.type].attributes.values(): + if a.attribute_name == "self": + continue + object_size += 8 + attrs.append(a.attribute_type.name) + + result += "li $a0, " + str(object_size) + "\n" + result += "li $v0, 9\n" + result += "syscall\n" + + # ponemos la direccion en la variable local correspondiente + result += "sw $v0, 4($s0)\n" + + # ponemos el tamanyo correspondiente + result += "sw $a0, ($v0)\n" + + result += "li $t3, 0\n" + # ponemos las letras correspondientes a cada attributo + for l in attrs: + result += "la $t1, type_" + l + "\n" + result += "sw $t1, 4($v0)\n" + result += "sw $t3, 8($v0)\n" + result += "addi $v0, $v0, 8\n" + + global CURR_LOCAL_COUNT, CURR_PARAM_COUNT + result += "li $s0, " + str(CURR_LOCAL_COUNT) + "\n" + result += "jal locals_write\n" + + result += "la $s0, " + dest + "\n" + result += "jal put_argument\n" + + result += "jal " + instruction.type + "_Attributes_Initialization\n" + + result += "li $s0, " + str(CURR_LOCAL_COUNT) + "\n" + result += "jal locals_load\n" + result += "li $s0, " + str(CURR_PARAM_COUNT) + "\n" + result += "jal params_load\n" + + return result + + +def convert_ArgNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + result += "la $s0, " + val + "\n" + + result += "jal put_argument\n" + + return result + + +def convert_DispatchCallNode(instruction): + result = "" + + global DATA, PARAMS, CIL_TYPES + + if instruction.type_addr in PARAMS: + t_addr = PARAMS[instruction.type_addr] + else: + t_addr = instruction.type_addr + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + dispatch_end_label = next_label() + + result += "la $s0, " + t_addr + "\n" + result += "li $s1, " + str(METHODS_NAME_TO_ID[instruction.method]) + "\n" + + result += "jal call_func\n" + + result += "jal $s3\n" + + result += "lw $s6, ($v0)\n" + result += "lw $s7, 4($v0)\n" + + global CURR_LOCAL_COUNT, CURR_PARAM_COUNT + result += "li $s0, " + str(CURR_LOCAL_COUNT) + "\n" + result += "jal locals_load\n" + result += "li $s0, " + str(CURR_PARAM_COUNT) + "\n" + result += "jal params_load\n" + + result += "la $t0, " + dest + "\n" + result += "sw $s6, ($t0)\n" + result += "sw $s7, 4($t0)\n" + + return result + + +def convert_SetAttributeNode(instruction): + result = "" + + global CIL_TYPES, DATA, PARAMS + + offset = 4 + + for a in CIL_TYPES[instruction.type_name].attributes.values(): + if a.attribute_name == "self": + continue + if a.attribute_name == instruction.attr: + break + offset += 8 + + if instruction.instance in PARAMS: + ins = PARAMS[instruction.instance] + else: + ins = instruction.instance + + # en t0 metemos la direccion del valor a modificar + result += "la $t0, " + ins + "\n" + result += "lw $t0, 4($t0)\n" + result += "addi $s1, $t0, " + str(offset) + "\n" + + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + # en t1 metemos la direccion del valor nuevo + result += "la $s0, " + val + "\n" + + result += "jal mov_func\n" + + return result + + +def convert_GetAttributeNode(instruction): + result = "" + + global CIL_TYPES, DATA, PARAMS + + offset = 4 + + for a in CIL_TYPES[instruction.type_name].attributes.values(): + if a.attribute_name == "self": + continue + if a.attribute_name == instruction.attr: + break + offset += 8 + + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + # en t0 metemos la direccion del valor a devolver + result += "la $t0, " + val + "\n" + result += "lw $t0, 4($t0)\n" + result += "addi $s0, $t0, " + str(offset) + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s1, " + dest + "\n" + + result += "jal mov_func\n" + + return result + + +def convert_ENode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.left in PARAMS: + left = PARAMS[instruction.left] + else: + left = instruction.left + + # poner en s0 la direccion del primer dato + result += "la $s0, " + left + "\n" + + if instruction.right in PARAMS: + right = PARAMS[instruction.right] + else: + right = instruction.right + + # poner en s1 la direccion del segundo dato + result += "la $s1, " + right + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + # poner en s2 la direccion del booleano de salida + result += "la $s2, " + dest + "\n" + + result += "jal equality_function\n" + + return result + + +def convert_LNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.left in PARAMS: + left = PARAMS[instruction.left] + else: + left = instruction.left + + result += "la $s0, " + left + "\n" + + if instruction.right in PARAMS: + right = PARAMS[instruction.right] + else: + right = instruction.right + + result += "la $s1, " + right + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s2, " + dest + "\n" + + result += "jal less_func\n" + + return result + + +def convert_LENode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.left in PARAMS: + left = PARAMS[instruction.left] + else: + left = instruction.left + + result += "la $s0, " + left + "\n" + + if instruction.right in PARAMS: + right = PARAMS[instruction.right] + else: + right = instruction.right + + result += "la $s1, " + right + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s2, " + dest + "\n" + + result += "jal less_equal_func\n" + + return result + + +def convert_DivNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.left in PARAMS: + left = PARAMS[instruction.left] + else: + left = instruction.left + + result += "la $s0, " + left + "\n" + + if instruction.right in PARAMS: + right = PARAMS[instruction.right] + else: + right = instruction.right + + result += "la $s1, " + right + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s2, " + dest + "\n" + + result += "jal divide_func\n" + + return result + + +def convert_MulNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.left in PARAMS: + left = PARAMS[instruction.left] + else: + left = instruction.left + + result += "la $s0, " + left + "\n" + + if instruction.right in PARAMS: + right = PARAMS[instruction.right] + else: + right = instruction.right + + result += "la $s1, " + right + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s2, " + dest + "\n" + + result += "jal multiply_func\n" + + return result + + +def convert_SubNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.left in PARAMS: + left = PARAMS[instruction.left] + else: + left = instruction.left + + result += "la $s0, " + left + "\n" + + if instruction.right in PARAMS: + right = PARAMS[instruction.right] + else: + right = instruction.right + + result += "la $s1, " + right + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s2, " + dest + "\n" + + result += "jal substract_func\n" + + return result + + +def convert_AddNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.left in PARAMS: + left = PARAMS[instruction.left] + else: + left = instruction.left + + result += "la $s0, " + left + "\n" + + if instruction.right in PARAMS: + right = PARAMS[instruction.right] + else: + right = instruction.right + + result += "la $s1, " + right + "\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $s2, " + dest + "\n" + + result += "jal add_func\n" + + return result + + +def convert_VDNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + result += "la $t0, " + val + "\n" + result += "lw $t0, 4($t0)\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $t1, " + dest + "\n" + + result += "seq $t2, $t0, 0\n" + result += "sw $t2, 4($t1)\n" + + return result + + +def convert_CmpNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + result += "la $t0, " + val + "\n" + result += "lw $t0, 4($t0)\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $t1, " + dest + "\n" + + result += "li $t2, -1\n" + result += "mult $t0, $t2\n" + result += "mflo $t0\n" + result += "sw $t0, 4($t1)\n" + + return result + + +def convert_NtNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + result += "la $t0, " + val + "\n" + result += "lw $t0, 4($t0)\n" + + if instruction.result in PARAMS: + dest = PARAMS[instruction.result] + else: + dest = instruction.result + + result += "la $t1, " + dest + "\n" + + result += "seq $t0, $t0, 0\n" + result += "sw $t0, 4($t1)\n" + + return result + + +def convert_MovNode(instruction): + result = "" + + if type(instruction.value) == type(5): + result += "li $t0, " + str(instruction.value) + "\n" + result += "la $t1, " + instruction.result + "\n" + result += "sw $t0, 4($t1)\n" + elif type(instruction.value) == type(True): + if instruction.value: + result += "li $t0, 1\n" + else: + result += "li $t0, 0\n" + result += "la $t1, " + instruction.result + "\n" + result += "sw $t0, 4($t1)\n" + else: + if instruction.value in PARAMS: + val = PARAMS[instruction.value] + else: + val = instruction.value + + if instruction.result in PARAMS: + res = PARAMS[instruction.result] + else: + res = instruction.result + + result += "la $s0, " + val + "\n" + result += "la $s1, " + res + "\n" + + result += "jal mov_func\n" + + return result + + +def convert_AbortNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.caller_type in PARAMS: + caller = PARAMS[instruction.caller_type] + else: + caller = instruction.caller_type + + result += "la $s0, " + caller + "\n" + + result += "jal abort\n" + + return result + + +def convert_IfGotoNode(instruction): + result = "" + + global DATA, PARAMS + + if instruction.predicate in PARAMS: + pred = PARAMS[instruction.predicate] + else: + pred = instruction.predicate + + result += "la $t0, " + pred + "\n" + result += "lw $t0, 4($t0)\n" + result += "beq $t0, 1, " + instruction.label + "\n" + + return result + + +def convert_GotoNode(instruction): + return "j " + instruction.label + "\n" + + +def convert_LabelNode(instruction): + return instruction.label_name + ":\n" diff --git a/src/parser.py b/src/parser.py new file mode 100644 index 00000000..1ea004d8 --- /dev/null +++ b/src/parser.py @@ -0,0 +1,407 @@ +import ply.yacc as yacc +from lexer import tokens +from ast import * + +# no terminal de entrada de la gramatica +start = 'program' + +# para devolver los errores que se encuentran +errors = [] + + +# para devolver la columna de los simbolos +def find_column(p, lex_pos): + line_start = p.lexer.lexdata.rfind('\n', 0, lex_pos) + 1 + return (lex_pos - line_start) + 1 + + +# para mandar la posicion de los simbolos a los nodos del AST +def GetPosition(p, x): + return p.lineno(x), find_column(p, p.lexpos(x)) + + +# no terminal de la entrada de la gramatica, el programa consiste en una lista de clases +# devuelve un nodo ProgramNode +def p_program(p): + '''program : class_list''' + p[0] = ProgramNode(p[1], GetPosition(p, 1)) + + +# no terminal de lista de clases y sus producciones +# devuelve una lista con ClassNode +def p_class_list(p): + '''class_list : class_definition class_list + | class_definition''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = [p[1]] + p[2] + + +# no terminal de estructura de una clases +# devuelve un ClassNode +def p_class_definition(p): + '''class_definition : CLASS CLASSID LBRACE class_feature_list RBRACE SEMICOLON + | CLASS CLASSID INHERITS CLASSID LBRACE class_feature_list RBRACE SEMICOLON''' + if len(p) == 7: + p[0] = ClassNode(p[2], p[4], None, [GetPosition(p, 2)]) + else: + p[0] = ClassNode(p[2], p[6], p[4], [GetPosition(p, 4)]) + + +# para el simbolo vacio +def p_empty(p): + 'empty :' + pass + + +# no terminal de lista de caracteristicas de la clase (atributos y metodos) +# devuelve una lista de FeatureNode +def p_class_feature_list(p): + '''class_feature_list : feature class_feature_list + | empty''' + + if len(p) == 3: + p[0] = [p[1]] + p[2] + else: + p[0] = [] + + +# no terminal de una caracteristica y sus producciones +# devuelve lo que devuelva el no terminal de la caracteristica correspondiente +def p_feature(p): + '''feature : attribute_feature + | function_feature''' + p[0] = p[1] + + +# no terminal de la caracteristica de atributo +# devuelve un nodo de tipo AttributeFeatureNode +def p_attribute_feature(p): + '''attribute_feature : ATTRIBUTEID COLON CLASSID SEMICOLON + | ATTRIBUTEID COLON CLASSID ASSIGNATION expression SEMICOLON''' + if len(p) == 5: + p[0] = AttributeFeatureNode(p[1], p[3], None, [GetPosition(p, 1)]) + else: + p[0] = AttributeFeatureNode(p[1], p[3], p[5], [GetPosition(p, 1)]) + + +# no terminal de la caracteristica de metodos +# devuelve un nodo FunctionFeatureNode +def p_function_feature(p): + '''function_feature : ATTRIBUTEID LPAREN parameters_list RPAREN COLON CLASSID LBRACE expression RBRACE SEMICOLON + | ATTRIBUTEID LPAREN RPAREN COLON CLASSID LBRACE expression RBRACE SEMICOLON''' + if len(p) == 10: + p[0] = FunctionFeatureNode(p[1], [], p[5], p[7], [GetPosition(p, 1)]) + else: + p[0] = FunctionFeatureNode(p[1], p[3], p[6], p[8], [GetPosition(p, 1)]) + + +# no terminal de la lista de parametros de un metodo +# devuelve una lista de ParameterNode +def p_parameter_list(p): + '''parameters_list : parameter COMMA parameters_list + | parameter''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = [p[1]] + p[3] + + +# no teminal de un parametro de una funcion +# devuelve un nodo ParameterNode +def p_parameter(p): + '''parameter : ATTRIBUTEID COLON CLASSID''' + p[0] = ParameterNode(p[1], p[3], [GetPosition(p, 3)]) + + +# no teminal de una expresion en COOL +# devuelve un nodo de tipo ExpressionNode +def p_expression(p): + '''expression : not_form + | mixed_expression''' + p[0] = p[1] + + +# no teminal de una expresion de tipo not +# devuelve un nodo NotNode +def p_not_form(p): + '''not_form : NOT mixed_expression''' + if len(p) == 2: + p[0] = p[1] + else: + p[0] = NotNode(p[2], [GetPosition(p, 1)]) + + +# no terminal para expresiones de comparacion +# devuelve el correspondiente nodo, y si lo que corresponde no es una operacion +# de comparacion entonces se devuelve lo que devuelva la produccion correspondiente +def p_mixed_expression(p): + '''mixed_expression : mixed_expression LESSEQUAL arithmetic_expression + | mixed_expression LESS arithmetic_expression + | mixed_expression EQUAL expression + | arithmetic_expression''' + if len(p) > 2: + if p[2] == "<": + p[0] = LessNode(p[1], p[3], [GetPosition(p, 2)]) + else: + if p[2] == "=": + p[0] = EqualNode(p[1], p[3], [GetPosition(p, 2)]) + else: + p[0] = LessEqualNode(p[1], p[3], [GetPosition(p, 2)]) + else: + p[0] = p[1] + + +# no teminal de las operaciones de suma y resta +# devuelve el correspondiente nodo y sino lo que devuelva la ultima produccion +def p_arithmetic_expression(p): + '''arithmetic_expression : arithmetic_expression PLUS term + | arithmetic_expression MINUS term + | term''' + if len(p) == 2: + p[0] = p[1] + else: + if p[2] == "+": + p[0] = PlusNode(p[1], p[3], [GetPosition(p, 2)]) + else: + p[0] = MinusNode(p[1], p[3], [GetPosition(p, 2)]) + + +# no teminal de las operaciones de multiplicacion y division +# devuelve el correspondiente nodo y sino lo que devuelva la ultima produccion +def p_term(p): + '''term : term TIMES isvoid_form + | term DIVIDE isvoid_form + | isvoid_form''' + if len(p) == 2: + p[0] = p[1] + else: + if p[2] == "*": + p[0] = TimesNode(p[1], p[3], [GetPosition(p, 2)]) + else: + p[0] = DivideNode(p[1], p[3], [GetPosition(p, 2)]) + + +# no terminal de la operacion isvoid +# devuelve un nodo IsVoidNode, sino lo que devuelva la ultima produccion +def p_isvoid_form(p): + '''isvoid_form : ISVOID expression + | complement_form''' + if len(p) == 2: + p[0] = p[1] + else: + p[0] = IsVoidNode(p[2], [GetPosition(p, 1)]) + + +# no terminal de la operacion de complemento de entero +# devuelve un nodo ComplementNode, sino lo que devuelva la ultima produccion +def p_complement_form(p): + '''complement_form : COMPLEMENT expression + | program_atom''' + if len(p) == 2: + p[0] = p[1] + else: + p[0] = ComplementNode(p[2], [GetPosition(p, 1)]) + + +######### a partir de aqui todas son producciones del no terminal program_atom ######## + + +# producciones para constantes booleanas +def p_program_atom_boolean(p): + '''program_atom : TRUE + | FALSE''' + p[0] = ConstantBoolNode(p[1], [GetPosition(p, 1)]) + + +# produccion para constantes de cadenas de caracteres +def p_program_atom_string(p): + '''program_atom : STRING''' + p[0] = ConstantStringNode(p[1], [GetPosition(p, 1)]) + + +# produccion para constantes enteras +def p_program_atom_int(p): + '''program_atom : NUMBER''' + p[0] = ConstantNumericNode(p[1], [GetPosition(p, 1)]) + + +# produccion para identificadores que se encuentran +def p_program_atom_id(p): + '''program_atom : ATTRIBUTEID''' + p[0] = VariableNode(p[1], [GetPosition(p, 1)]) + + +# produccion para expresiones encerradas por parentesis +def p_program_atom_parentesis(p): + '''program_atom : LPAREN expression RPAREN''' + p[0] = p[2] + + +# produccion para expressiones new +def p_program_atom_new(p): + '''program_atom : NEW CLASSID''' + p[0] = NewStatementNode(p[2], [GetPosition(p, 1)]) + + +# producciones para expressiones de llamado de metodos de la clase actual +def p_program_atom_member(p): + '''program_atom : member_call''' + p[0] = p[1] + + +def p_member_call(p): + '''member_call : ATTRIBUTEID LPAREN RPAREN + | ATTRIBUTEID LPAREN argument_list RPAREN''' + if len(p) == 4: + p[0] = FunctionCallStatement(VariableNode("self", p.lineno), None, p[1], [], [GetPosition(p, 1)]) + else: + p[0] = FunctionCallStatement(VariableNode("self", p.lineno), None, p[1], p[3], [GetPosition(p, 1)]) + + +# producciones para lista de argumentos +def p_argument_list(p): + '''argument_list : expression + | expression COMMA argument_list''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = [p[1]] + p[3] + + +# produccion para expressiones de llamados de funcion de una instancia determinada +def p_program_atom_function(p): + '''program_atom : program_atom function_call''' + p[0] = FunctionCallStatement(p[1], (p[2])[0], (p[2])[1], (p[2])[2], p[1].lineNumber) + + +# producciones de lo que viene a partir de la instancia en un llamado de funcion +def p_function_call(p): + '''function_call : DOT ATTRIBUTEID LPAREN argument_list RPAREN + | DOT ATTRIBUTEID LPAREN RPAREN + | DISPATCH CLASSID DOT ATTRIBUTEID LPAREN argument_list RPAREN + | DISPATCH CLASSID DOT ATTRIBUTEID LPAREN RPAREN''' + + if len(p) == 5: + p[0] = [None, p[2], []] + else: + if len(p) == 6: + p[0] = [None, p[2], p[4]] + else: + if len(p) == 7: + p[0] = [p[2], p[4], []] + else: + p[0] = [p[2], p[4], p[6]] + + +# produccion para expressiones de asignacion +def p_program_atom_assign(p): + '''program_atom : ATTRIBUTEID ASSIGNATION expression''' + p[0] = AssignStatementNode(p[1], p[3], [GetPosition(p, 1)]) + + +# produccion para expressiones de tipo case +def p_program_atom_case(p): + '''program_atom : CASE expression OF case_body ESAC''' + p[0] = CaseStatementNode(p[2], p[4], GetPosition(p, 1)) + + +# producciones para las ramas de las expresiones case +def p_case_body(p): + '''case_body : ATTRIBUTEID COLON CLASSID ARROW expression SEMICOLON case_body + | ATTRIBUTEID COLON CLASSID ARROW expression SEMICOLON''' + if (len(p) == 7): + p[0] = [CaseBranchNode(p[1], p[3], p[5], [GetPosition(p, 3)])] + else: + p[0] = [CaseBranchNode(p[1], p[3], p[5], [GetPosition(p, 3)])] + p[7] + + +# produccion para expressiones let +def p_program_atom_let(p): + '''program_atom : LET let_body IN expression''' + p[0] = LetStatementNode(p[2], p[4], [GetPosition(p, 1)]) + + +# producciones para las asignaciones e inicializaciones de las expreciones let +def p_let_body(p): + '''let_body : ATTRIBUTEID COLON CLASSID + | ATTRIBUTEID COLON CLASSID ASSIGNATION expression + | ATTRIBUTEID COLON CLASSID COMMA let_body + | ATTRIBUTEID COLON CLASSID ASSIGNATION expression COMMA let_body''' + if len(p) == 4: + p[0] = [AttributeFeatureNode(p[1], p[3], None, [GetPosition(p, 3)])] + else: + if len(p) == 8: + p[0] = [AttributeFeatureNode(p[1], p[3], p[5], [GetPosition(p, 3)])] + p[7] + else: + if p[4] == "<-": + p[0] = [AttributeFeatureNode(p[1], p[3], p[5], [GetPosition(p, 3)])] + else: + p[0] = [AttributeFeatureNode(p[1], p[3], None, [GetPosition(p, 3)])] + p[5] + + +# produccion para expressiones block +def p_program_atom_block(p): + '''program_atom : LBRACE expression_list RBRACE''' + p[0] = BlockStatementNode(p[2], [GetPosition(p, 1)]) + + +# producciones de listas de expresiones para la expresion block +def p_expression_list(p): + '''expression_list : expression SEMICOLON expression_list + | expression SEMICOLON''' + if len(p) == 3: + p[0] = [p[1]] + else: + p[0] = [p[1]] + p[3] + + +# produccion para expressiones while +def p_program_atom_while(p): + '''program_atom : WHILE expression LOOP expression POOL''' + p[0] = LoopStatementNode(p[2], p[4], [GetPosition(p, 2)]) + + +# produccion para expressiones condicionales +def p_program_atom_if(p): + '''program_atom : IF expression THEN expression ELSE expression FI''' + p[0] = ConditionalStatementNode(p[2], p[4], p[6], [GetPosition(p, 1)]) + + +# devolver los errores cuando se encuentren +def p_error(p): + global errors + if p is None: + errors.append("(0, 0) - SyntacticError: ERROR at or near EOF") + return + word = p.value + w = False + if p.type == 'ASSIGNATION': + word = 'ASSIGN' + w = True + if p.type == 'ESAC': + word = 'ESAC' + w = True + if p.type == 'NEW': + word = 'NEW' + w = True + if p.type == 'CLASS': + word = 'CLASS' + w = True + if w: + errors.append("(%s, %s) - SyntacticError: ERROR at or near %s " % (p.lineno, p.colno, word)) + else: + errors.append("(%s, %s) - SyntacticError: ERROR at or near \"%s\"" % (p.lineno, p.colno, word)) + pass + + +parser = yacc.yacc(debug=1) + + +# para ejecutar el parser desde compiler.py +def make_parser(code): + global errors + errors = [] + result = parser.parse(code) + return result, errors diff --git a/src/parsetab.py b/src/parsetab.py new file mode 100644 index 00000000..8a2366b9 --- /dev/null +++ b/src/parsetab.py @@ -0,0 +1,95 @@ + +# parsetab.py +# This file is automatically generated. Do not edit. +# pylint: disable=W,C,R +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'programARROW ASSIGNATION ATTRIBUTEID CASE CLASS CLASSID COLON COMMA COMPLEMENT DISPATCH DIVIDE DOT ELSE EQUAL ESAC FALSE FI GREATER GREATEREQUAL IF IN INHERITS ISVOID LBRACE LESS LESSEQUAL LET LOOP LPAREN MINUS NEW NOT NUMBER OF PLUS POOL RBRACE RPAREN SEMICOLON STRING THEN TIMES TRUE WHILEprogram : class_listclass_list : class_definition class_list\n | class_definitionclass_definition : CLASS CLASSID LBRACE class_feature_list RBRACE SEMICOLON\n | CLASS CLASSID INHERITS CLASSID LBRACE class_feature_list RBRACE SEMICOLONempty :class_feature_list : feature class_feature_list\n | emptyfeature : attribute_feature\n | function_featureattribute_feature : ATTRIBUTEID COLON CLASSID SEMICOLON\n | ATTRIBUTEID COLON CLASSID ASSIGNATION expression SEMICOLONfunction_feature : ATTRIBUTEID LPAREN parameters_list RPAREN COLON CLASSID LBRACE expression RBRACE SEMICOLON\n | ATTRIBUTEID LPAREN RPAREN COLON CLASSID LBRACE expression RBRACE SEMICOLONparameters_list : parameter COMMA parameters_list\n | parameterparameter : ATTRIBUTEID COLON CLASSIDexpression : not_form\n | mixed_expressionnot_form : NOT mixed_expressionmixed_expression : mixed_expression LESSEQUAL arithmetic_expression\n | mixed_expression LESS arithmetic_expression\n | mixed_expression EQUAL expression\n | arithmetic_expressionarithmetic_expression : arithmetic_expression PLUS term\n | arithmetic_expression MINUS term\n | termterm : term TIMES isvoid_form\n | term DIVIDE isvoid_form\n | isvoid_formisvoid_form : ISVOID expression\n | complement_formcomplement_form : COMPLEMENT expression\n | program_atomprogram_atom : TRUE\n | FALSEprogram_atom : STRINGprogram_atom : NUMBERprogram_atom : ATTRIBUTEIDprogram_atom : LPAREN expression RPARENprogram_atom : NEW CLASSIDprogram_atom : member_callmember_call : ATTRIBUTEID LPAREN RPAREN\n | ATTRIBUTEID LPAREN argument_list RPARENargument_list : expression\n | expression COMMA argument_listprogram_atom : program_atom function_callfunction_call : DOT ATTRIBUTEID LPAREN argument_list RPAREN\n | DOT ATTRIBUTEID LPAREN RPAREN\n | DISPATCH CLASSID DOT ATTRIBUTEID LPAREN argument_list RPAREN\n | DISPATCH CLASSID DOT ATTRIBUTEID LPAREN RPARENprogram_atom : ATTRIBUTEID ASSIGNATION expressionprogram_atom : CASE expression OF case_body ESACcase_body : ATTRIBUTEID COLON CLASSID ARROW expression SEMICOLON case_body\n | ATTRIBUTEID COLON CLASSID ARROW expression SEMICOLONprogram_atom : LET let_body IN expressionlet_body : ATTRIBUTEID COLON CLASSID\n | ATTRIBUTEID COLON CLASSID ASSIGNATION expression\n | ATTRIBUTEID COLON CLASSID COMMA let_body\n | ATTRIBUTEID COLON CLASSID ASSIGNATION expression COMMA let_bodyprogram_atom : LBRACE expression_list RBRACEexpression_list : expression SEMICOLON expression_list\n | expression SEMICOLONprogram_atom : WHILE expression LOOP expression POOLprogram_atom : IF expression THEN expression ELSE expression FI' + +_lr_action_items = {'CLASS':([0,3,21,63,],[4,4,-4,-5,]),'$end':([1,2,3,5,21,63,],[0,-1,-3,-2,-4,-5,]),'CLASSID':([4,8,18,30,32,52,60,79,107,132,],[6,15,22,59,61,81,89,103,121,141,]),'LBRACE':([6,15,29,39,43,45,51,54,56,57,58,61,64,65,67,68,69,71,72,73,74,89,90,106,109,110,111,112,115,116,133,136,140,148,],[7,20,56,56,56,56,56,56,56,56,56,90,56,56,56,56,56,56,56,56,56,112,56,56,56,56,56,56,56,56,56,56,56,56,]),'INHERITS':([6,],[8,]),'RBRACE':([7,9,10,11,12,13,17,20,27,28,35,37,38,40,41,42,44,46,47,48,49,50,53,66,70,75,76,77,81,85,91,92,95,96,97,98,99,100,101,104,108,109,113,114,120,122,125,129,131,135,138,139,145,147,150,151,],[-6,16,-6,-8,-9,-10,-7,-6,34,-11,-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-12,-20,-31,-33,-47,-41,108,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-63,126,-44,-56,-62,137,-49,-53,-64,-14,-48,-13,-51,-65,-50,]),'ATTRIBUTEID':([7,10,12,13,19,20,28,29,33,39,43,45,51,54,55,56,57,58,64,65,66,67,68,69,71,72,73,74,78,90,105,106,109,110,111,112,115,116,117,133,134,136,138,140,145,148,149,154,],[14,14,-9,-10,23,14,-11,35,23,35,35,35,35,35,84,35,35,35,35,35,-12,35,35,35,35,35,35,35,102,35,119,35,35,35,35,35,35,35,130,35,84,35,-14,35,-13,35,84,119,]),'COLON':([14,23,25,31,84,119,],[18,30,32,60,107,132,]),'LPAREN':([14,29,35,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,102,106,109,110,111,112,115,116,130,133,136,140,148,],[19,51,65,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,116,51,51,51,51,51,51,51,140,51,51,51,51,]),'SEMICOLON':([16,22,34,35,36,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,86,91,92,95,96,97,98,99,100,101,104,108,114,120,126,129,131,135,137,139,147,150,151,152,],[21,28,63,-39,66,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,109,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,138,-49,-53,-64,145,-48,-51,-65,-50,154,]),'RPAREN':([19,24,26,35,37,38,40,41,42,44,46,47,48,49,50,53,59,62,65,70,75,76,77,80,81,91,92,93,94,95,96,97,98,99,100,101,104,108,114,116,120,127,128,129,131,135,139,140,146,147,150,151,],[25,31,-16,-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-17,-15,92,-20,-31,-33,-47,104,-41,-52,-43,114,-45,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,129,-56,-46,139,-49,-53,-64,-48,147,151,-51,-65,-50,]),'ASSIGNATION':([22,35,121,],[29,64,133,]),'COMMA':([26,35,37,38,40,41,42,44,46,47,48,49,50,53,59,70,75,76,77,81,91,92,94,95,96,97,98,99,100,101,104,108,114,120,121,129,131,135,139,142,147,150,151,],[33,-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-17,-20,-31,-33,-47,-41,-52,-43,115,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,134,-49,-53,-64,-48,149,-51,-65,-50,]),'NOT':([29,43,45,51,54,56,57,58,64,65,69,90,106,109,110,111,112,115,116,133,136,140,148,],[39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,]),'ISVOID':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,]),'COMPLEMENT':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,]),'TRUE':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,]),'FALSE':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,]),'STRING':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,]),'NUMBER':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,]),'NEW':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,]),'CASE':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,]),'LET':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,]),'WHILE':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,]),'IF':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,]),'DOT':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,103,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,78,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,117,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'DISPATCH':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,79,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'TIMES':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,-24,73,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,-21,-22,-23,73,73,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'DIVIDE':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,-24,74,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,-21,-22,-23,74,74,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'PLUS':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,71,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,71,71,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'MINUS':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,72,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,72,72,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'LESSEQUAL':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,67,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,67,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'LESS':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,68,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,68,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'EQUAL':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,69,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,69,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'OF':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,82,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,105,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'LOOP':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,87,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,110,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'THEN':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,88,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,111,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,-51,-65,-50,]),'POOL':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,123,129,131,135,139,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,135,-49,-53,-64,-48,-51,-65,-50,]),'ELSE':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,124,129,131,135,139,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,136,-49,-53,-64,-48,-51,-65,-50,]),'IN':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,83,91,92,95,96,97,98,99,100,101,104,108,114,120,121,129,131,135,139,142,143,147,150,151,153,],[-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,106,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-57,-49,-53,-64,-48,-58,-59,-51,-65,-50,-60,]),'FI':([35,37,38,40,41,42,44,46,47,48,49,50,53,70,75,76,77,81,91,92,95,96,97,98,99,100,101,104,108,114,120,129,131,135,139,144,147,150,151,],[-39,-18,-19,-24,-27,-30,-32,-34,-35,-36,-37,-38,-42,-20,-31,-33,-47,-41,-52,-43,-21,-22,-23,-25,-26,-28,-29,-40,-61,-44,-56,-49,-53,-64,-48,150,-51,-65,-50,]),'ESAC':([118,154,155,],[131,-55,-54,]),'ARROW':([141,],[148,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'program':([0,],[1,]),'class_list':([0,3,],[2,5,]),'class_definition':([0,3,],[3,3,]),'class_feature_list':([7,10,20,],[9,17,27,]),'feature':([7,10,20,],[10,10,10,]),'empty':([7,10,20,],[11,11,11,]),'attribute_feature':([7,10,20,],[12,12,12,]),'function_feature':([7,10,20,],[13,13,13,]),'parameters_list':([19,33,],[24,62,]),'parameter':([19,33,],[26,26,]),'expression':([29,43,45,51,54,56,57,58,64,65,69,90,106,109,110,111,112,115,116,133,136,140,148,],[36,75,76,80,82,86,87,88,91,94,97,113,120,86,123,124,125,94,94,142,144,94,152,]),'not_form':([29,43,45,51,54,56,57,58,64,65,69,90,106,109,110,111,112,115,116,133,136,140,148,],[37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,]),'mixed_expression':([29,39,43,45,51,54,56,57,58,64,65,69,90,106,109,110,111,112,115,116,133,136,140,148,],[38,70,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,]),'arithmetic_expression':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,90,106,109,110,111,112,115,116,133,136,140,148,],[40,40,40,40,40,40,40,40,40,40,40,95,96,40,40,40,40,40,40,40,40,40,40,40,40,40,]),'term':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,90,106,109,110,111,112,115,116,133,136,140,148,],[41,41,41,41,41,41,41,41,41,41,41,41,41,41,98,99,41,41,41,41,41,41,41,41,41,41,41,41,]),'isvoid_form':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,100,101,42,42,42,42,42,42,42,42,42,42,42,42,]),'complement_form':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,]),'program_atom':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,]),'member_call':([29,39,43,45,51,54,56,57,58,64,65,67,68,69,71,72,73,74,90,106,109,110,111,112,115,116,133,136,140,148,],[53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,]),'function_call':([46,],[77,]),'let_body':([55,134,149,],[83,143,153,]),'expression_list':([56,109,],[85,122,]),'argument_list':([65,115,116,140,],[93,127,128,146,]),'case_body':([105,154,],[118,155,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> program","S'",1,None,None,None), + ('program -> class_list','program',1,'p_program','parser.py',23), + ('class_list -> class_definition class_list','class_list',2,'p_class_list','parser.py',29), + ('class_list -> class_definition','class_list',1,'p_class_list','parser.py',30), + ('class_definition -> CLASS CLASSID LBRACE class_feature_list RBRACE SEMICOLON','class_definition',6,'p_class_definition','parser.py',39), + ('class_definition -> CLASS CLASSID INHERITS CLASSID LBRACE class_feature_list RBRACE SEMICOLON','class_definition',8,'p_class_definition','parser.py',40), + ('empty -> ','empty',0,'p_empty','parser.py',48), + ('class_feature_list -> feature class_feature_list','class_feature_list',2,'p_class_feature_list','parser.py',54), + ('class_feature_list -> empty','class_feature_list',1,'p_class_feature_list','parser.py',55), + ('feature -> attribute_feature','feature',1,'p_feature','parser.py',65), + ('feature -> function_feature','feature',1,'p_feature','parser.py',66), + ('attribute_feature -> ATTRIBUTEID COLON CLASSID SEMICOLON','attribute_feature',4,'p_attribute_feature','parser.py',72), + ('attribute_feature -> ATTRIBUTEID COLON CLASSID ASSIGNATION expression SEMICOLON','attribute_feature',6,'p_attribute_feature','parser.py',73), + ('function_feature -> ATTRIBUTEID LPAREN parameters_list RPAREN COLON CLASSID LBRACE expression RBRACE SEMICOLON','function_feature',10,'p_function_feature','parser.py',83), + ('function_feature -> ATTRIBUTEID LPAREN RPAREN COLON CLASSID LBRACE expression RBRACE SEMICOLON','function_feature',9,'p_function_feature','parser.py',84), + ('parameters_list -> parameter COMMA parameters_list','parameters_list',3,'p_parameter_list','parser.py',93), + ('parameters_list -> parameter','parameters_list',1,'p_parameter_list','parser.py',94), + ('parameter -> ATTRIBUTEID COLON CLASSID','parameter',3,'p_parameter','parser.py',103), + ('expression -> not_form','expression',1,'p_expression','parser.py',109), + ('expression -> mixed_expression','expression',1,'p_expression','parser.py',110), + ('not_form -> NOT mixed_expression','not_form',2,'p_not_form','parser.py',116), + ('mixed_expression -> mixed_expression LESSEQUAL arithmetic_expression','mixed_expression',3,'p_mixed_expression','parser.py',126), + ('mixed_expression -> mixed_expression LESS arithmetic_expression','mixed_expression',3,'p_mixed_expression','parser.py',127), + ('mixed_expression -> mixed_expression EQUAL expression','mixed_expression',3,'p_mixed_expression','parser.py',128), + ('mixed_expression -> arithmetic_expression','mixed_expression',1,'p_mixed_expression','parser.py',129), + ('arithmetic_expression -> arithmetic_expression PLUS term','arithmetic_expression',3,'p_arithmetic_expression','parser.py',144), + ('arithmetic_expression -> arithmetic_expression MINUS term','arithmetic_expression',3,'p_arithmetic_expression','parser.py',145), + ('arithmetic_expression -> term','arithmetic_expression',1,'p_arithmetic_expression','parser.py',146), + ('term -> term TIMES isvoid_form','term',3,'p_term','parser.py',159), + ('term -> term DIVIDE isvoid_form','term',3,'p_term','parser.py',160), + ('term -> isvoid_form','term',1,'p_term','parser.py',161), + ('isvoid_form -> ISVOID expression','isvoid_form',2,'p_isvoid_form','parser.py',173), + ('isvoid_form -> complement_form','isvoid_form',1,'p_isvoid_form','parser.py',174), + ('complement_form -> COMPLEMENT expression','complement_form',2,'p_complement_form','parser.py',184), + ('complement_form -> program_atom','complement_form',1,'p_complement_form','parser.py',185), + ('program_atom -> TRUE','program_atom',1,'p_program_atom_boolean','parser.py',198), + ('program_atom -> FALSE','program_atom',1,'p_program_atom_boolean','parser.py',199), + ('program_atom -> STRING','program_atom',1,'p_program_atom_string','parser.py',204), + ('program_atom -> NUMBER','program_atom',1,'p_program_atom_int','parser.py',209), + ('program_atom -> ATTRIBUTEID','program_atom',1,'p_program_atom_id','parser.py',214), + ('program_atom -> LPAREN expression RPAREN','program_atom',3,'p_program_atom_parentesis','parser.py',219), + ('program_atom -> NEW CLASSID','program_atom',2,'p_program_atom_new','parser.py',225), + ('program_atom -> member_call','program_atom',1,'p_program_atom_member','parser.py',231), + ('member_call -> ATTRIBUTEID LPAREN RPAREN','member_call',3,'p_member_call','parser.py',235), + ('member_call -> ATTRIBUTEID LPAREN argument_list RPAREN','member_call',4,'p_member_call','parser.py',236), + ('argument_list -> expression','argument_list',1,'p_argument_list','parser.py',244), + ('argument_list -> expression COMMA argument_list','argument_list',3,'p_argument_list','parser.py',245), + ('program_atom -> program_atom function_call','program_atom',2,'p_program_atom_function','parser.py',253), + ('function_call -> DOT ATTRIBUTEID LPAREN argument_list RPAREN','function_call',5,'p_function_call','parser.py',258), + ('function_call -> DOT ATTRIBUTEID LPAREN RPAREN','function_call',4,'p_function_call','parser.py',259), + ('function_call -> DISPATCH CLASSID DOT ATTRIBUTEID LPAREN argument_list RPAREN','function_call',7,'p_function_call','parser.py',260), + ('function_call -> DISPATCH CLASSID DOT ATTRIBUTEID LPAREN RPAREN','function_call',6,'p_function_call','parser.py',261), + ('program_atom -> ATTRIBUTEID ASSIGNATION expression','program_atom',3,'p_program_atom_assign','parser.py',277), + ('program_atom -> CASE expression OF case_body ESAC','program_atom',5,'p_program_atom_case','parser.py',283), + ('case_body -> ATTRIBUTEID COLON CLASSID ARROW expression SEMICOLON case_body','case_body',7,'p_case_body','parser.py',288), + ('case_body -> ATTRIBUTEID COLON CLASSID ARROW expression SEMICOLON','case_body',6,'p_case_body','parser.py',289), + ('program_atom -> LET let_body IN expression','program_atom',4,'p_program_atom_let','parser.py',297), + ('let_body -> ATTRIBUTEID COLON CLASSID','let_body',3,'p_let_body','parser.py',303), + ('let_body -> ATTRIBUTEID COLON CLASSID ASSIGNATION expression','let_body',5,'p_let_body','parser.py',304), + ('let_body -> ATTRIBUTEID COLON CLASSID COMMA let_body','let_body',5,'p_let_body','parser.py',305), + ('let_body -> ATTRIBUTEID COLON CLASSID ASSIGNATION expression COMMA let_body','let_body',7,'p_let_body','parser.py',306), + ('program_atom -> LBRACE expression_list RBRACE','program_atom',3,'p_program_atom_block','parser.py',321), + ('expression_list -> expression SEMICOLON expression_list','expression_list',3,'p_expression_list','parser.py',327), + ('expression_list -> expression SEMICOLON','expression_list',2,'p_expression_list','parser.py',328), + ('program_atom -> WHILE expression LOOP expression POOL','program_atom',5,'p_program_atom_while','parser.py',336), + ('program_atom -> IF expression THEN expression ELSE expression FI','program_atom',7,'p_program_atom_if','parser.py',341), +] diff --git a/src/semantic.py b/src/semantic.py new file mode 100644 index 00000000..4d81fad4 --- /dev/null +++ b/src/semantic.py @@ -0,0 +1,678 @@ +from ast import * +from graph import Graph +from type_defined import AllTypes, CoolType, object_type, BasicTypes + + +# aqui chequeamos si algun tipo esta declarado doble y formamos el diccionario AllTypes +def check_type_declaration(ast: ProgramNode): + for cls in ast.classes: + if cls.typeName in BasicTypes: + return f'({cls.getLineNumber()}, {cls.getColumnNumber()}) - ' \ + f'SemanticError: Redefinition of basic class {cls.typeName}.' + if cls.typeName in AllTypes: + return f'({cls.getLineNumber()}, {cls.getColumnNumber()}) - ' \ + f'SemanticError: Classes may not be redefined' + + AllTypes[cls.typeName] = CoolType(cls.typeName, None) + + return [] + + +# aqui chequeamos que las clases hereden de tipos definidos y validos +def check_type_inheritance(ast: ProgramNode): + for cls in ast.classes: + if cls.fatherTypeName: + if cls.fatherTypeName in AllTypes: + father_type = AllTypes[cls.fatherTypeName] + if father_type.inherit: + if not (father_type.name == cls.typeName): + AllTypes[cls.typeName].parent_type = father_type + else: + return f"({cls.getLineNumber()}, {cls.getColumnNumber()}) - SemanticError: Class {cls.typeName}, " \ + f"or an ancestor of {cls.typeName}, is involved in an inheritance cycle" + else: + return f'({cls.getLineNumber()}, {cls.getColumnNumber()}) - SemanticError: Class {cls.typeName} ' \ + f'cannot inherit class {father_type.name}.' + else: + return f"({cls.getLineNumber()}, {cls.getColumnNumber()}) - TypeError: Class {cls.typeName} " \ + f"inherits from an undefined class {cls.fatherTypeName}. " + else: + AllTypes[cls.typeName].parent_type = object_type + + return [] + + +# aqui chequeamos las caracteristicas de los tipos, que no esten repetidas +# y cumplan con las restricciones de herencia + +def check_features(ast: ProgramNode): + checked_types = [False for _ in ast.classes] + created_types_names = [cls.typeName for cls in ast.classes] + left_check = len(checked_types) + + while left_check > 0: + for i, cls in enumerate(ast.classes): + if checked_types[i]: + continue + + # esto de aqui para que se chequeen los tipos como si estuvieramos + # buscando a lo ancho en el arbol de los tipos y sus relaciones de herencia + # el primero seria el tipo Object y despues sus hijos + if cls.fatherTypeName: + if cls.fatherTypeName in created_types_names: + if not checked_types[created_types_names.index(cls.fatherTypeName)]: + continue + + class_type = AllTypes[cls.typeName] + class_type.add_attribute("self", class_type.name, "self") + for feature in cls.features: + if type(feature) is FunctionFeatureNode: + parameters_type = [] + parameters_name = [] + for arg in feature.parameters: + if arg.id == 'self': + return f'({arg.getLineNumber()}, {arg.getColumnNumber()}) - SemanticError: \'self\' ' \ + f'cannot be the name of a formal parameter.' + parameters_name.append(arg.id) + parameters_type.append(arg.typeName) + method_added = class_type.add_method(feature.id, parameters_type, parameters_name, feature.typeName, + feature.statement) + if len(method_added) == 2: + return f'({feature.parameters[method_added[1]].getLineNumber()}, ' \ + f'{feature.parameters[method_added[1]].getColumnNumber()}) ' \ + f'{method_added[0]}' + elif len(method_added) == 1: + return f'({feature.getLineNumber()}, ' \ + f'{feature.getColumnNumber()}) ' \ + f'{method_added[0]}' + continue + if type(feature) is AttributeFeatureNode: + if feature.id == 'self': + return f'({feature.getLineNumber()}, {feature.getColumnNumber()}) - SemanticError: \'self\' ' \ + f'cannot be the name of an attribute.' + feature_added_error = class_type.add_attribute(feature.id, feature.typeName, feature.expression) + if len(feature_added_error) == 1: + return f'({feature.getLineNumber()}, {feature.getColumnNumber()}) {feature_added_error[0]}' + elif len(feature_added_error) == 2: + return f'({feature.getLineNumber()}, {feature.getColumnNumber() + len(feature.id) + 2}) ' \ + f'{feature_added_error[0]}' + continue + return 'Unknown attribute or Method' + + class_methods = class_type.get_self_methods() + class_inherited_methods = class_type.get_methods_inherited() + + for k in class_methods.keys(): + if k in class_inherited_methods: + if class_methods[k].return_type != class_inherited_methods[k].return_type: + return f'({feature.getLineNumber()}, {feature.getColumnNumber()}) - SemanticError: ' \ + f'In redefined method {feature.id}, return type {class_methods[k].return_type.name} ' \ + f'is different from original return type {class_inherited_methods[k].return_type.name}.' + if len(class_methods[k].args_types) != len(class_inherited_methods[k].args_types): + return f'({feature.getLineNumber()}, {feature.getColumnNumber()}) - SemanticError: ' \ + f'Incompatible number of formal parameters in redefined method {feature.id}.' + for counter in range(0, len(class_methods[k].args_types)): + if (class_methods[k].args_types[counter]).name != \ + (class_inherited_methods[k].args_types[counter]).name: + return f'({feature.getLineNumber()}, {feature.getColumnNumber()}) - SemanticError: ' \ + f'In redefined method {feature.id}, parameter type ' \ + f'{class_methods[k].args_types[counter].name} is different ' \ + f'from original type {class_inherited_methods[k].args_types[counter].name}.' + + left_check = left_check - 1 + checked_types[i] = True + + return [] + + +# para el chequeo de expresiones +CURR_TYPE = "" + + +# aqui se chequea que las expresiones esten devolviendo el tipo correcto +def check_expressions(ast: ProgramNode): + global CURR_TYPE, TYPE_CHANGES + + change = False + + for cls in ast.classes: + attrs = {} + cls_type = AllTypes[cls.typeName] + CURR_TYPE = cls_type.name + attrs_type = cls_type.get_attributes() + + for attr in attrs_type: + attrs[attr.attribute_name] = attr.attribute_type.name + attrs["self"] = cls.typeName + + for feature in cls.features: + if type(feature) is AttributeFeatureNode: + feature_type = feature.typeName + if feature.expression: + error, expression_type = get_expression_return_type(feature.expression, False, attrs, {}, {}, False, + {}) + if len(error) > 0: + return error + # si el tipo que devuelve la expresion no hereda del tipo de retorno entonces retornamos un error + if expression_type != feature.typeName and not is_ancestor(AllTypes[feature_type], + AllTypes[expression_type]): + return f'({feature.getLineNumber()}, {feature.getColumnNumber()}) - TypeError: Inferred type ' \ + f'{expression_type} of initialization of attribute test ' \ + f'does not conform to declared type {feature_type}.' + + functions = AllTypes[cls.typeName].get_methods() + + for feature in cls.features: + if type(feature) is FunctionFeatureNode: + feature_type = feature.typeName + + params = {"self": cls.typeName} + for parameter in feature.parameters: + params[parameter.id] = parameter.typeName + error, expression_type = get_expression_return_type(feature.statement, True, attrs, functions, + params, + False, {}) + + if len(error) > 0: + return error + + if feature_type not in AllTypes: + return f'({feature.statement.getLineNumber()}, {feature.statement.getColumnNumber()}) - TypeError: ' \ + f'Undefined return type {feature_type} in method test.' + # si el tipo que devuelve la expresion no hereda del tipo de retorno entonces retornamos un error + if not is_ancestor(AllTypes[feature_type], AllTypes[expression_type]): + return f'({feature.statement.getLineNumber()}, {feature.statement.getColumnNumber()}) - TypeError: ' \ + f'Inferred return type {expression_type} of method {feature.id} does not conform to declared ' \ + f'return type {feature_type}.' + + return [] + + +# dada una lista de tipos devuelve el primer ancestro en comun de ellos +def GetFirstCommonAncestor(types): + result = types[0] + for i in range(1, len(types)): + result = AllTypes[get_first_common_ancestor(result, types[i])] + + return result.name + + +# dados dos tipos devuelve el primer ancestro en comun +def get_first_common_ancestor(typeA, typeB): + if is_ancestor(typeA, typeB): + return typeA.name + if is_ancestor(typeB, typeA): + return typeB.name + return get_first_common_ancestor(typeA.parent_type, typeB.parent_type) + + +# devuelve True si youngerNode hereda de olderNode (youngerNode y olderNode son tipos) +def is_ancestor(olderNode, youngerNode): + if olderNode is None or youngerNode is None: + return False + if olderNode.name == youngerNode.name: + return True + return is_ancestor(olderNode, youngerNode.parent_type) + + +# dado el nodo de una expresion devuelve el tipo de retorno de dicha expresion, +# y detecta errores de malas operaciones entre tipos, entre otros +def get_expression_return_type(expression, insideFunction, attributes, functions, parameters, insideLet, letVars, + insideCase=False, caseVar={}, inside_loop=False): + if type(expression) is AssignStatementNode: + if expression.id == 'self': + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - SemanticError: ' \ + f'Cannot assign to \'self\'.', '' + + error1, type1 = get_expression_return_type(expression.expression, insideFunction, attributes, functions, + parameters, insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + + if insideLet: + if expression.id in letVars and not is_ancestor(AllTypes[letVars[expression.id]], AllTypes[type1]): + return "Errorrrrrrrr asigning another type to a let variable" + + if insideCase: + if expression.id in caseVar and not is_ancestor(AllTypes[caseVar[expression.id]], AllTypes[type1]): + return "Errorrrrrrrr asigning another type to a case variable" + + if insideFunction: + if expression.id in parameters and not is_ancestor(AllTypes[parameters[expression.id]], AllTypes[type1]): + return "Errorrrrrrrr asigning another type to a parameter" + if expression.id in attributes and not is_ancestor(AllTypes[attributes[expression.id]], AllTypes[type1]): + return "Errorrrrrrrr asigning another type to an attribute" + + return [], type1 + + + elif type(expression) is ConditionalStatementNode: + error1, type1 = get_expression_return_type(expression.evalExpr, insideFunction, attributes, functions, + parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + if type1 != "Bool": + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: Predicate of \'if\' ' \ + f'does not have type Bool.', "" + + error2, type2 = get_expression_return_type(expression.ifExpr, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + error3, type3 = get_expression_return_type(expression.elseExpr, insideFunction, attributes, functions, + parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error3) > 0: + return error3, "" + + thenType = AllTypes[type2] + elseType = AllTypes[type3] + return [], get_first_common_ancestor(thenType, elseType) + + elif type(expression) is LoopStatementNode: + error1, type1 = get_expression_return_type(expression.evalExpr, insideFunction, attributes, functions, + parameters, + insideLet, letVars, insideCase, caseVar, inside_loop=True) + if len(error1) > 0: + return error1, "" + if type1 != "Bool": + return f'({expression.loopExpr.getLineNumber()}, {expression.loopExpr.getColumnNumber()}) - ' \ + f'TypeError: Loop condition does not have type Bool.', '' + error2, type2 = get_expression_return_type(expression.loopExpr, insideFunction, attributes, functions, + parameters, + insideLet, letVars, insideCase, caseVar, inside_loop=True) + if len(error2) > 0: + return error2, "" + return [], "Object" + + elif type(expression) is BlockStatementNode: + lastType = "" + for expr in expression.expressions: + eError, eType = get_expression_return_type(expr, insideFunction, attributes, functions, parameters, + insideLet, + letVars, insideCase, caseVar, inside_loop) + if len(eError) > 0: + return eError, "" + lastType = eType + + return [], lastType + + elif type(expression) is LetStatementNode: + letVariables = letVars + for item in attributes.keys(): + letVars[item] = attributes[item] + for variable in expression.variables: + if variable.id == 'self': + return f'({variable.getLineNumber()}, {variable.getColumnNumber()}) - SemanticError: \'self\' cannot ' \ + f'be bound in a \'let\' expression.', '' + if not (variable.typeName in AllTypes): + return f"({variable.getLineNumber()}, {variable.getColumnNumber()}) - TypeError: Class " \ + f"{variable.typeName} of let-bound identifier {variable.id} is undefined.", "" + if variable.expression is not None: + error0, type0 = get_expression_return_type(variable.expression, insideFunction, attributes, functions, + parameters, True, letVariables, insideCase, caseVar, + inside_loop) + if len(error0) > 0: + return "Error in let variable initialization expression", "" + if not is_ancestor(AllTypes[variable.typeName], AllTypes[type0]): + return f'({variable.getLineNumber()}, {variable.getColumnNumber()}) - TypeError: ' \ + f'Inferred type {type0} of initialization of {variable.id} does not conform to ' \ + f'identifier\'s declared type {variable.typeName}. ', '' + letVariables[variable.id] = variable.typeName + errorLet, typeLet = get_expression_return_type(expression.expression, insideFunction, attributes, functions, + parameters, True, letVariables, insideCase, caseVar, inside_loop) + if len(errorLet) > 0: + return errorLet, "" + return [], typeLet + + elif type(expression) is CaseStatementNode: + eError, eType = get_expression_return_type(expression.expression, insideFunction, attributes, functions, + parameters, insideLet, letVars, insideCase, caseVar, inside_loop) + if len(eError) > 0: + return eError, "" + caseBranchesTypes = [] + case_types = [] + for caseBranch in expression.body: + for s in case_types: + if s == caseBranch.typeName: + return f'({caseBranch.getLineNumber()}, {caseBranch.getColumnNumber()}) - SemanticError: Duplicate branch {caseBranch.typeName} in case statement', "" + + case_types.append(caseBranch.typeName) + error0, type0 = get_expression_return_type(caseBranch, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error0) > 0: + return f'({caseBranch.getLineNumber()}, {caseBranch.getColumnNumber()}) - {error0}', "" + + duplicated_type = False + for t in caseBranchesTypes: + if t.name == type0: + duplicated_type = True + break + + if not duplicated_type: + caseBranchesTypes.append(AllTypes[type0]) + + return [], GetFirstCommonAncestor(caseBranchesTypes) + + elif type(expression) is CaseBranchNode: + if not (expression.typeName in AllTypes): + return f"TypeError: Class {expression.typeName} of case branch is undefined.", "" + error0, type0 = get_expression_return_type(expression.expression, insideFunction, attributes, functions, + parameters, insideLet, letVars, True, + {expression.id: expression.typeName}) + if len(error0) > 0: + return error0, "" + + return [], type0 + + elif type(expression) is NewStatementNode: + if expression.typeName in AllTypes: + return [], AllTypes[expression.typeName].name + else: + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: \'new\' used ' \ + f'with undefined class {expression.typeName}.', '' + + elif type(expression) is FunctionCallStatement: + + e, t = get_expression_return_type(expression.instance, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + + if len(e) > 0: + return e, "" + if expression.dispatchType is not None: + if not (expression.dispatchType in AllTypes): + return "Ancestor class type not defined", "" + expType = AllTypes[t] + ancType = AllTypes[expression.dispatchType] + if is_ancestor(ancType, expType): + methods = ancType.get_methods() + if expression.function in methods: + if len(methods[expression.function].args_names) != len(expression.args): + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - SemanticError: ' \ + f'Method {expression.function} called with wrong number of arguments.', '' + i = 0 + for arg in expression.args: + aError, aType = get_expression_return_type(arg, insideFunction, attributes, functions, + parameters, + insideLet, insideCase, caseVar, inside_loop) + + if len(aError) > 0: + return aError, "" + if aType != ((methods[expression.function]).args_types[i]).name: + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: ' \ + f'In call of method {expression.function}, type {((methods[expression.function]).args_types[i]).name} of parameter {((methods[expression.function]).args_names[i])} ' \ + f'does not conform to declared type {aType}. ', '' + i = i + 1 + return [], methods[expression.function].return_type.name + else: + return f'({1}, {1}) - AttributeError: Dispatch to undefined method {1}.', '' + + else: + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: ' \ + f'Expression type {expType.name} does not conform to declared static dispatch type ' \ + f'{ancType.name}. ', '' + + methods = AllTypes[t].get_methods() + if expression.function in methods: + if len(methods[expression.function].args_names) != len(expression.args): + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - SemanticError: ' \ + f'Method {expression.function} called with wrong number of arguments.', '' + i = 0 + for arg in expression.args: + aError, aType = get_expression_return_type(arg, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(aError) > 0: + return aError, "" + if not is_ancestor(AllTypes[((methods[expression.function]).args_types[i]).name], AllTypes[aType]): + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: ' \ + f'In call of method {expression.function}, type {((methods[expression.function]).args_types[i]).name} of parameter {(methods[expression.function]).args_names[i]} ' \ + f'does not conform to declared type {aType}. ', '' + i = i + 1 + + return [], methods[expression.function].return_type.name + else: + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - AttributeError: ' \ + f'Dispatch to undefined method {expression.function}.', '' + + elif type(expression) is ConstantNumericNode: + return [], "Int" + elif type(expression) is ConstantStringNode: + return [], "String" + elif type(expression) is ConstantBoolNode: + return [], "Bool" + + elif type(expression) is VariableNode: + if insideLet: + if expression.lex in letVars: + return [], letVars[expression.lex] + if insideCase: + if expression.lex in caseVar: + return [], caseVar[expression.lex] + if insideFunction or inside_loop: + if expression.lex in parameters: + return [], parameters[expression.lex] + if expression.lex in attributes: + return [], attributes[expression.lex] + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - NameError: ' \ + f'Undeclared identifier {expression.lex}.', '' + + elif type(expression) is NotNode: + error1, type1 = get_expression_return_type(expression.expression, insideFunction, attributes, functions, + parameters, insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + if type1 != "Bool": + return f'({expression.expression.getLineNumber()}, {expression.expression.getColumnNumber()}) - TypeError: ' \ + f'Argument of \'not\' has type {type1} instead of Bool', '' + return [], "Bool" + + elif type(expression) is IsVoidNode: + error1, type1 = get_expression_return_type(expression.expression, insideFunction, attributes, functions, + parameters, insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + + return [], "Bool" + + elif type(expression) is ComplementNode: + error1, type1 = get_expression_return_type(expression.expression, insideFunction, attributes, functions, + parameters, insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + if type1 != "Int": + return f'({expression.expression.getLineNumber()}, {expression.expression.getColumnNumber()}) - TypeError: Argument of \'~\' ' \ + f'has type {type1} instead of Int', '' + return [], "Int" + + elif type(expression) is LessEqualNode: + error1, type1 = get_expression_return_type(expression.left, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + error2, type2 = get_expression_return_type(expression.right, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + if type1 != "Int" or type2 != "Int": + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: non-Int arguments: ' \ + f'{type1} <= {type2}', '' + return [], "Bool" + + elif type(expression) is LessNode: + error1, type1 = get_expression_return_type(expression.left, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + error2, type2 = get_expression_return_type(expression.right, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + if type1 != "Int" or type2 != "Int": + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: non-Int arguments: ' \ + f'{type1} < {type2}', '' + return [], "Bool" + + elif type(expression) is EqualNode: + error1, type1 = get_expression_return_type(expression.left, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + error2, type2 = get_expression_return_type(expression.right, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + + if type1 not in BasicTypes and type2 not in BasicTypes: + return [], 'Bool' + if type1 != type2: + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: ' \ + f'Illegal comparison with a basic type.', '' + return [], "Bool" + + elif type(expression) is PlusNode: + error1, type1 = get_expression_return_type(expression.left, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + error2, type2 = get_expression_return_type(expression.right, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + if type1 != "Int" or type2 != "Int": + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: non-Int arguments: ' \ + f'{type1} + {type2} ', '' + return [], "Int" + + elif type(expression) is MinusNode: + error1, type1 = get_expression_return_type(expression.left, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + error2, type2 = get_expression_return_type(expression.right, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + if type1 != "Int" or type2 != "Int": + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: non-Int arguments: ' \ + f'{type1} - {type2}', '' + return [], "Int" + + elif type(expression) is TimesNode: + error1, type1 = get_expression_return_type(expression.left, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + error2, type2 = get_expression_return_type(expression.right, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + if type1 != "Int" or type2 != "Int": + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: non-Int arguments: ' \ + f'{type1} * {type2}', '' + return [], "Int" + + elif type(expression) is DivideNode: + error1, type1 = get_expression_return_type(expression.left, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error1) > 0: + return error1, "" + error2, type2 = get_expression_return_type(expression.right, insideFunction, attributes, functions, parameters, + insideLet, letVars, insideCase, caseVar, inside_loop) + if len(error2) > 0: + return error2, "" + if type1 != "Int" or type2 != "Int": + return f'({expression.getLineNumber()}, {expression.getColumnNumber()}) - TypeError: non-Int arguments: ' \ + f'{type1} / {type2}', '' + return [], "Int" + + +# se realiza un DFS sobre el grafo de los tipos empezando por Object, +# si no se llega a algun tipo entonces existe un ciclo +def get_cyclic_class(graph: Graph, ast: ProgramNode): + for cls in ast.classes: + if not graph.visited[graph.vertex_id.index(cls.typeName)]: + nodes = graph.graph[cls.typeName] + for cls2 in ast.classes: + if cls2.typeName == nodes[0]: + return f"({cls2.getLineNumber()}, {cls2.getColumnNumber()}) - SemanticError: Class {cls2.typeName}, " \ + f"or an ancestor of {cls2.typeName}, is involved in an inheritance cycle" + return f"({cls.getLineNumber()}, {cls.getColumnNumber()}) - SemanticError: Class {cls.typeName}, " \ + f"or an ancestor of {cls.typeName}, is involved in an inheritance cycle" + return [] + + +# aqui chequeamos que no hayan ciclos en el grafo que se forma +# con los tipos y sus relaciones de herencia +def check_cyclic_inheritance(ast): + graph = Graph(len(AllTypes.keys())) + + for cool_type_name in AllTypes.keys(): + graph.addNewEdge(cool_type_name) + + for cool_type in AllTypes.values(): + if cool_type.parent_type is not None: + graph.addEdge(cool_type.parent_type.name, cool_type.name) + elif cool_type.name != 'Object': + graph.addEdge('Object', cool_type.name) + + return [] if graph.dfs('Object') else get_cyclic_class(graph, ast) + + +def check_methods_params(ast): + for cls in ast.classes: + cls_type = AllTypes[cls.typeName] + attrs = cls_type.get_attributtes() + for feature in enumerate(cls.features): + if type(feature) is FunctionFeatureNode: + for param in feature.parameters: + if param.id in attrs: + return f'({param.getLineNumber()}, {param.getColumnNumber()}) - ' + return [] + + +# para devolver los errores semanticos +def check_semantic(ast: ProgramNode): + errors = [] + + # aqui chequeamos si algun tipo esta declarado doble + type_declaration_output = check_type_declaration(ast) + if len(type_declaration_output) > 0: + errors.append(type_declaration_output) + return errors, AllTypes + + # aqui chequeamos que las clases hereden de tipos definidos y validos + inheritance_check_output = check_type_inheritance(ast) + if len(inheritance_check_output) > 0: + errors.append(inheritance_check_output) + return errors, AllTypes + + # aqui chequeamos que no hayan ciclos en el grafo que se forma + # con los tipos y sus relaciones de herencia + cyclic_inheritance_check = check_cyclic_inheritance(ast) + if len(cyclic_inheritance_check) > 0: + errors.append(cyclic_inheritance_check) + return errors, AllTypes + + # aqui chequeamos las caracteristicas de los tipos, que no esten repetidas + # y cumplan con lo que tengan que cumplir + feature_check_output = check_features(ast) + if len(feature_check_output) > 0: + errors.append(feature_check_output) + return errors, AllTypes + + # aqui se chequea que las expresiones esten devolviendo el tipo correcto + expressions_check_output = check_expressions(ast) + if len(expressions_check_output) > 0: + errors.append(expressions_check_output) + return errors, AllTypes + + # finalmente chequeamos que exista la clase Main con el metodo main + if 'Main' not in AllTypes: + errors.append('Main not declared') + return errors, AllTypes + if 'main' not in AllTypes["Main"].methods: + errors.append('main method not declared') + return errors, AllTypes + + return [], AllTypes diff --git a/src/string_data_visitor.py b/src/string_data_visitor.py new file mode 100644 index 00000000..1da09a40 --- /dev/null +++ b/src/string_data_visitor.py @@ -0,0 +1,139 @@ +from visitor_helper import * +from ast import * + + +# se utiliza para encontrar cadenas de caracteres en el texto y devolverlas +class FormatVisitorS(object): + @on('node') + def visit(self, node, tabs): + pass + + @when(ProgramNode) + def visit(self, node, tabs=0): + result = [] + for c in node.classes: + result += self.visit(c) + return result + + @when(ClassNode) + def visit(self, node, tabs=0): + result = [] + for f in node.features: + result += self.visit(f) + return result + + @when(AttributeFeatureNode) + def visit(self, node, tabs=0): + return self.visit(node.expression) + + @when(FunctionFeatureNode) + def visit(self, node, tabs=0): + return self.visit(node.statement) + + @when(ParameterNode) + def visit(self, node, tabs=0): + return [] + + @when(AssignStatementNode) + def visit(self, node, tabs=0): + return self.visit(node.expression) + + @when(ConditionalStatementNode) + def visit(self, node, tabs=0): + return self.visit(node.evalExpr) + self.visit(node.ifExpr) + self.visit(node.elseExpr) + + @when(LoopStatementNode) + def visit(self, node, tabs=0): + return self.visit(node.evalExpr) + self.visit(node.loopExpr) + + @when(BlockStatementNode) + def visit(self, node, tabs=0): + result = [] + for e in node.expressions: + result += self.visit(e) + return result + + @when(LetStatementNode) + def visit(self, node, tabs=0): + result = [] + for v in node.variables: + result += self.visit(v) + return result + self.visit(node.expression) + + @when(CaseStatementNode) + def visit(self, node, tabs=0): + result = [] + for cs in node.body: + result += self.visit(cs) + return self.visit(node.expression) + result + + @when(CaseBranchNode) + def visit(self, node, tabs=0): + return self.visit(node.expression) + + @when(NewStatementNode) + def visit(self, node, tabs=0): + return [] + + @when(FunctionCallStatement) + def visit(self, node, tabs=0): + result = [] + for arg in node.args: + result += self.visit(arg) + return self.visit(node.instance) + result + + @when(ConstantNumericNode) + def visit(self, node, tabs=0): + return [] + + @when(ConstantStringNode) + def visit(self, node, tabs=0): + return [node] + + @when(ConstantBoolNode) + def visit(self, node, tabs=0): + return [] + + @when(VariableNode) + def visit(self, node, tabs=0): + return [] + + @when(NotNode) + def visit(self, node, tabs=0): + return self.visit(node.expression) + + @when(IsVoidNode) + def visit(self, node, tabs=0): + return self.visit(node.expression) + + @when(ComplementNode) + def visit(self, node, tabs=0): + return self.visit(node.expression) + + @when(LessEqualNode) + def visit(self, node, tabs=0): + return self.visit(node.left) + self.visit(node.right) + + @when(LessNode) + def visit(self, node, tabs=0): + return self.visit(node.left) + self.visit(node.right) + + @when(EqualNode) + def visit(self, node, tabs=0): + return self.visit(node.left) + self.visit(node.right) + + @when(PlusNode) + def visit(self, node, tabs=0): + return self.visit(node.left) + self.visit(node.right) + + @when(MinusNode) + def visit(self, node, tabs=0): + return self.visit(node.left) + self.visit(node.right) + + @when(TimesNode) + def visit(self, node, tabs=0): + return self.visit(node.left) + self.visit(node.right) + + @when(DivideNode) + def visit(self, node, tabs=0): + return self.visit(node.left) + self.visit(node.right) diff --git a/src/type_defined.py b/src/type_defined.py new file mode 100644 index 00000000..4fd45511 --- /dev/null +++ b/src/type_defined.py @@ -0,0 +1,284 @@ +# clase que representa un tipo en COOL +class CoolType: + # inherit dice si de este tipo se puede heredar + def __init__(self, name, parent_type, inherit=True): + self.name = name + self.parent_type = parent_type + self.inherit = inherit + self.methods = {} + self.attributes = {} + + def defined(self, method_name): + return method_name in self.methods.keys() + + # en el chequeo semantico se van agregando los metodos y atributos y si se encuentra + # algun tipo de error se devuelve + def add_method(self, method_name, args_types, args_names, return_type, expression=None): + if not self.defined(method_name): + if len(args_types) != len(args_names): + args_names = ['a' * i for i, _ in enumerate(args_types)] + final_args_type = [] + names_added = [] + for i, arg in enumerate(args_types): + type_of_arg = get_type_by_name(arg) + if type_of_arg is None: + return [f'- TypeError: Class {arg} of formal parameter b is undefined.'] + if len(args_names) > i: + final_args_type.append(type_of_arg) + if args_names[i] in names_added: + return [f'- SemanticError: Formal parameter {args_names[i]} is multiply defined.', i] + else: + names_added.append(args_names[i]) + type_of_return = get_type_by_name(return_type) + if type_of_return is None: + return 'Method should return something' + self.methods[method_name] = CoolMethod(method_name, final_args_type, args_names, type_of_return, expression) + return [] + else: + # TODO Update error + return [f'- SemanticError: Method {method_name} is multiply defined.'] + + def get_attributes_as_dict(self): + node = self + attr = {} + nodes_list = [] + while node: + nodes_list = [node] + nodes_list + node = node.parent_type + + for n in nodes_list: + for attrs in n.attributes.keys(): + if attrs in attr: + continue + attr[attrs] = n.attributes[attrs] + + return attr + + def get_attributes(self): + node = self + attr = [] + while node: + for attrs in node.attributes.values(): + if attrs in attr: + continue + attr.append(attrs) + node = node.parent_type + return attr + + def get_self_methods(self): + return self.methods + + # para devolver los metodos que se heredan + def get_methods_inherited(self): + node = self.parent_type + methods = {} + while node: + for methodName in node.methods.keys(): + methods[methodName] = node.methods[methodName] + node = node.parent_type + return methods + + # para devolver que tipo es el que define cada uno de mis metodos + def get_owner(self, func): + mine = self.get_self_methods() + + if func in mine: + return self.name + + owner = "" + node = self.parent_type + while node: + for methodName in node.methods.keys(): + if func in node.methods: + return node.name + node = node.parent_type + + return owner + + def get_methods(self): + selfMethods = self.get_self_methods() + inheritedMethods = self.get_methods_inherited() + result = {} + + for key in selfMethods: + if key in result: + continue + result[key] = selfMethods[key] + + for key in inheritedMethods: + if key in result: + continue + result[key] = inheritedMethods[key] + + return result + + def get_method_without_inherit(self, method_name, args): + try: + method = self.methods[method_name] + except KeyError: + return False, None, 'error getting method' + if len(args) != len(method.args): + # TODO Update error message + return False, None, "error , mismatch method args length" + for i, a in enumerate(args): + if not inherits(a, method.args[i]): + return False, None, 'error args methods' + return True, method, None + + def get_method(self, method_name, args): + exist, method, error_message = self.get_method_without_inherit(method_name, args) + if not exist and self.parent_type: + return self.parent_type.get_method(method_name, args) + elif exist: + return exist, None + return None, error_message + + def add_attribute(self, attribute_name, attribute_type, expression): + attr = self.get_attribute_inherited(attribute_name) + if attr is not None: + return [f'- SemanticError: Attribute {attribute_name} is an attribute of an inherited class.'] + if attribute_name in self.attributes.keys(): + return [f'- SemanticError: Attribute {attribute_name} is multiply defined in class.'] + class_attr_type = get_type_by_name(attribute_type) + if not class_attr_type: + return [f'- TypeError: Class {attribute_type} of attribute {attribute_name} is undefined.', 1] + self.attributes[attribute_name] = CoolAttribute(attribute_name, class_attr_type, expression) + return [] + + # devolver los atributos heredados + def get_attribute_inherited(self, attribute_name): + t = self.parent_type + while t is not None: + if attribute_name in t.attributes: + return t.attributes[attribute_name] + t = t.parent_type + return None + + # para devolver los tipos que definen mis atributos + def get_attribute_owner(self): + node = self + AO = {} + nodes_list = [] + while node: + nodes_list = [node] + nodes_list + node = node.parent_type + + for n in nodes_list: + for attrs in n.attributes.values(): + if attrs.attribute_name == "self": + continue + AO[attrs.attribute_name] = n.name + + return AO + + # para devolver a que profundidad estoy del arbol que se forma con los tipos + # y la herencia entre ellos + def get_tree_depth(self): + node = self + + result = 0 + + while node: + result += 1 + node = node.parent_type + + return result + + # para devolver que tipo es el que define cada uno de mis metodos + def get_method_owner(self): + node = self + MO = {} + while node: + for method in node.methods.values(): + if method.name in MO: + continue + MO[method.name] = node.name + node = node.parent_type + return MO + + +# clase base de un atributo en COOL +class CoolAttribute: + def __init__(self, attribute_name, attribute_type, expression=None): + self.attribute_name = attribute_name + self.attribute_type = attribute_type + self.expression = expression + + +# clase base de un metodo en COOL +class CoolMethod: + def __init__(self, method_name, args_types, args_names, return_type, expression=None): + self.name = method_name + self.args_types = args_types + self.args_names = args_names + self.return_type = return_type + self.expression = expression + + +# para ponerles identificadores a los metodos que existen, +# esto es usado para optimizar la generacion de codigo en mips +METHODS_NAME_TO_ID = {} + + +def refresh_methods_id(): + global AllTypes, METHODS_NAME_TO_ID + id = 0 + for t in AllTypes.keys(): + for m in AllTypes[t].methods.keys(): + if m in METHODS_NAME_TO_ID: + continue + METHODS_NAME_TO_ID[m] = id + id += 1 + + +# devuelve True si el tipo a hereda de b +def inherits(a, b): + current = a + while current != b: + if current is None: + return False + current = current.parent + return True + + +# devuelve un tipo a partir de su nombre +def get_type_by_name(type_name): + if type_name in AllTypes: + return AllTypes[type_name] + return None + + +# declarando los tipos por defecto +object_type = CoolType('Object', None) +self_type = CoolType('SELF_TYPE', None, False) +io_type = CoolType('IO', object_type) +string_type = CoolType('String', object_type, False) +int_type = CoolType('Int', object_type, False) +bool_type = CoolType('Bool', object_type, False) + +# AllTypes contendra toda la informacion de todos los tipos +AllTypes = { + 'Object': object_type, + 'SELF_TYPE': self_type, + 'IO': io_type, + 'String': string_type, + 'Int': int_type, + 'Bool': bool_type +} + +# finalmente agregamos los metodos de los tipos basicos +object_type.add_method('abort', [], [], 'Object') +object_type.add_method('type_name', [], [], 'String') +object_type.add_method('copy', [], [], 'SELF_TYPE') + +io_type.add_method('out_string', ['String'], ['s'], 'IO') +io_type.add_method('out_int', ['Int'], ['i'], 'IO') +io_type.add_method('in_string', [], [], 'String') +io_type.add_method('in_int', [], [], 'Int') + +string_type.add_method('length', [], [], 'Int') +string_type.add_method('concat', ['String'], ['s'], 'String') +string_type.add_method('substr', ['Int', 'Int'], ['a', 'b'], 'String') + +# lista de los tipos basicos +BasicTypes = ['Object', 'SELF_TYPE', 'IO', 'String', 'Int', 'Bool'] diff --git a/src/visitor_helper.py b/src/visitor_helper.py new file mode 100644 index 00000000..354a8f0c --- /dev/null +++ b/src/visitor_helper.py @@ -0,0 +1,85 @@ +# 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/codegen_test.py b/tests/codegen_test.py index 48df768f..49db4080 100644 --- a/tests/codegen_test.py +++ b/tests/codegen_test.py @@ -14,4 +14,4 @@ @pytest.mark.parametrize("cool_file", tests) def test_codegen(compiler_path, cool_file): compare_outputs(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_input.txt',\ - tests_dir + cool_file[:-3] + '_output.txt') \ No newline at end of file + tests_dir + cool_file[:-3] + '_output.txt') diff --git a/tests/utils/utils.py b/tests/utils/utils.py index 961cf7cb..8e559bb0 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -23,6 +23,7 @@ def parse_error(error: str): def first_error(compiler_output: list, errors: list): line, column, error_type, _ = parse_error(errors[0]) + print(compiler_output[0]) oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) assert line == oline and column == ocolumn and error_type == oerror_type,\