Oscar++ Mi propio compilador

Pues así como lo escuchan, que Java ni que Python, Oscar++ es el lenguaje de la siguiente generación….

Espero que haya quedado lo suficientemente claro el nivel de sarcasmos del párrafo anterior, pero por si no lo notaste lo reafirmo.

Oscar++ es en realidad un proyecto que desarrolle para la materia de Programación de sistema por allá del año 2000, cuando era un ingenuo y feliz estudiante de de la carrera de Ing. en sistemas.

El proyecto era “simple”, desarrollar un compilador capaz de tomar un código fuente, pasarlo por un analizador léxico, sintáctico y semántico para validar que el código escrito cumplía con la definición del lenguaje, seguido, era crear un árbol sintáctico abstracto y generar a partir de el, un código intermedio.

Observa que he dicho “simple” entre comillas, pues al principio y en la teoría parecía serlo, sin embargo, cuando llego el momento de hacerlo, las cosas cambiaron, la teoría se iba por un caño y era el momento de comenzar a prender y entender todos los conceptos involucrados en la generación de todos estos pasos.

Lo curioso es que rápidamente comprendí como es que lo tenía que hacer y lo logre, incluso termine vendiendo el proyectos a otros compañeros de la universidad, pero esa es otra historia.

En esta artículo no quisiera entrar en detalle sobre las fases de un compilador, pues para eso ya he creado un artículo al respecto.

Instalando el proyecto

Pero bueno, basta de palabrerías y vamos al proyecto, en principio, esta es la forma en que se ve corriendo:

La aplicación se compone de un pequeño editor de texto donde podemos poner nuestro código, una barra de tareas para guarda, abrir, compilar, etc. y del lado izquierdo mis datos de la universidad para que el maestro pudiera avaluar mi trabajo, de los cuales algunos los he borrado por obvias razones.

⚠ Antes de continuar, me gustaría hacer una aclaración, este es un proyecto que desarrolle hace muchos años, sin experiencia y con pésimas prácticas de programación, por lo que podrías encontrar muchas cosas raras de esas que dan miedo ver, pero he querido mantener el proyecto tal cual lo presente en su momento, pues lo único que busco es documentar el trabajo que hice en aquel entonces, por lo que quiero aclarar que no estoy dispuesto a dar soporte o agregar nueva funcionalidad.

Dicho lo anterior, empecemos con descargar el código fuente desde GitHub: https://github.com/oscarjb1/university_oscar-.git

El proyecto está en Eclipse por lo que solo bastará con que lo importes para verlo listo y funcionando en todo su esplendor 😂.

La clase principal es oscar.Main, por lo que solo tendremos que darle click derecho y luego run. El proyecto no requiere de ninguna librería, por lo que debería de funcionar sin ningún problema.

Como funciona Oscar++

El proyecto se compone de dos versiones del compilador:

La primera, Gramática Chica es una versión del compilador que soporta una sintaxis muy pequeña, donde podemos definir y asignar variables y definir sentencias if , la ventaja es que esta gramática genera el árbol sintáctico abstracto y el código intermedio. Por otra, tenemos la Gramática Grande, la cual es una versión del compilador mucho más grande que soporta clases, método, if y algunas expresiones básicas, sin embargo, esta versión no soporta la generación de árboles abstractos ni código intermedio.

La razón por la que existen dos versiones es por que eran dos proyectos diferentes, el primero era en realidad el que tenía que entregar, pero el profesor daba puntos extras al que lograr la segunda gramática que estaba enfocada para los alumnos de Posgrado, al final decidir hacer los dos para mejorar la calificación.

Pero bueno, para probar el compilador deberemos abrir alguno de los programas que generé para probarlo, los cuales puedes encontrar en la testfiles que se encuentra en la raíz del proyecto, los archivos que comienzan con GC son para la gramática chica, mientras que los que comienzan com GG son para la gramática grande, así que abrimos cualquiera de estos y le damos en compilar:

Si todo sale bien, veremos un mensaje de compilación exitosa, como se ve en la imagen anterior.

La sintaxis permitida por el compilador se encuentra definida en la carpeta oscar.fuentes, la cual contiene dos archivos con extensión jj, Oscar+.jj es para la gramática chica y Oscar++.jj es para la gramática grande.

Los archivos extensión jj son utilizados por herramienta llamada JavaCC (
Java Compiler Compiler), la cual es una herramienta para generar el Parses, el cual corresponde al paso por medio del cual se valida la gramática para asegurarse que todos los tokens tiene sentido y están en un orden determinado, sin embargo, no agrega la validación de tipos de datos ni el árbol sintáctico abstracto, el cual yo creo y valido más adelante.

El archivo de JavaCC necesita una serie de expresiones en formato BNF, el cual es un lenguaje para describir lenguajes, es decir, mediante BNF defino la sintaxis valida para mi lenguaje.

Además, creo una especia de método que en realidad definen secciones del lenguaje, por ejemplo:

void programa():{}{
	claseMain() (declaracionClase())* <EOF>
}

Lo que hace el código anterior es decirle a JavaCC que nuestro programa debe de contener una Clase principal (Main Class) seguido de cero o muchas clases, seguido del fin del archivo, es por eso que la gramática grande acepta varias clases dentro del mismo archivo.

A su vez, claseMain() y declaraciónClase() son otros método que definen la sintaxis de la clase principal y las clases secundarias respectivamente.

El resultado de la compilación de estos dos archivos nos da como resultado una serie de clases que podrás encontrar en las carpetas oscar.compilador1 y oscar.compilador2, la primera corresponde a la gramática chica y la 2 a la grande. Sin embargo, las clases que puedes ver allí no están exactamente como las genera JavaCC, si no que las modifique para generar el árbol sintáctico abstracto, veamos el siguiente fragmento de código para explicar este punto:

final public VarDeclaracion declaracionVariable() throws ParseException, SemanticException {
    ....
}

Fragmento de código de la gramática chica

final public void declaracionVariable() throws ParseException {
    ....
}

Fragmento del código de la gramática grande

Quiero que observes los dos fragmentos de código anterior, en la gramática pequeña retornamos siempre un objeto, mientras que en la gramática grande no retornamos nada (void), esto se debe a que en la gramática chica generamos un árbol sintáctico abstracto, por lo que el retorno lo utilizamos para ir construyendo un árbol basado en objetos, el cual conocemos como árbol sintáctico abstracto.

Para no hacerte el cuento muy largo, el árbol sintáctico abstracto se crea a partir de TODAS las clases que están en los paquetes oscar.ArbolAbstracto.xxx, lo cual corresponde a 5 paquetes y 23 clases, solo para generar el árbol sintáctico abstracto.

Lo irónico de esto es que un árbol sintáctico abstracto es en realidad la implementación del patrón de diseño Composite, lo cual yo no tenía ni idea en aquel entonces. Más tarde escribiría el libro Introducción a los patrones de diseño donde explico este y otros 24 patrones de diseño 😉

Otra diferencia que no había mencionado, es que el compilador chico tiene un diccionario de símbolos, lo que valida que no existan variables con el mismo nombre.

Árbol sintáctico abstracto

El árbol sintáctico abstracto se generará solo para la gramática chica en la pestaña llamada [Arbol Generado], la cual se muestra algo así:

VarDeclaracion( )
IntegerTipo
Identificador

VarDeclaracion( )
IntegerTipo
Identificador

If( )
Compracion( )
IdentificadorExp( )
IdentificadorExp( )


Print( )
IdentificadorExp( )

Print( )
IdentificadorExp( )

En este momento me doy cuenta que le falto un poco de orden a lo que se muestra, pero lo que vemos es básicamente las clases ordenadas como se encuentran en el árbol. Por ejemplo las tres primeras líneas:

VarDeclaracion( )
IntegerTipo
Identificador

Esto que en realidad quiere decir es que es VarDeclaracion( IntegerTipo, Identificador) sin embargo, es solo una forma de representarlo. Al final el árbol se puede recuperar y crear una versión más amigable.

Código intermedio

El código intermedio es un código que se genera a partir del código original con la intención de crear un código más fácil de interpretar por un interprete.

Para ser honestos, no recuerdo en que me base para crear este código, pero creo que era un lenguaje basado en Lisp, lo que si te puedo decir es que el código objeto se crea con la clase GeneracionCodigoObjeto del paquete oscar.CodigoObjeto.

Al final, el código intermedio solo se muestra para la gramática chica y lo puedes ver en la pestaña [Codigo Intermedio], el cual se ve de la siguiente manera:

// Declaracion de Variable
ADDI $T1, $ZERO, $ZERO
SW $T1, 0($ESP)
ADDI $ESP, $ESP, -4

// Declaracion de Variable
ADDI $T1, $ZERO, $ZERO
SW $T1, 0($ESP)
ADDI $ESP, $ESP, -4

// Statement IF
LW $T1, -0($FP)
LW $T2, -4($FP)
BEQ $T1, $T2, ETIQUETA0
ADDI $T1, $ZERO, 0
J ETIQUETA1
ETIQUETA0:
ADDI $T1, $ZERO, 1
ETIQUETA1:
ADDI $T2, $ZERO, $ZERO
BEQ $T1, $T2, ETIQUETA2
// Imprimir Linea
LW $T1, -4($FP)
PRINT $T1

J ETIQUETA3
ETIQUETA2: 
// Imprimir Linea
LW $T1, -0($FP)
PRINT $T1

ETIQUETA3:

Y como dice Porky de los Looney Tunes, “Y eso es todo amigo“, la verdad hice todo lo posible por recordar y documentar todo el proceso y como funcionaba, sin embargo, siento que muchas cosas quedaron por fuera de este artículo que me hubiera gustado documentar, pero es tanta información y mucha teoría que esto da para un libro jeje, así que lo dejo hasta aquí y si tienes dudas, me puedes preguntar 🙂

Conclusiones

Como y has podido comprobar Oscar++ no es para nada la competencia de Java ni de Python, si no que es solo un proyecto universitario (proyecto final) el cual combina muchos conceptos y teoría que uno debe de dominar para lograr crear aunque sea un compilador simple como este.

En realidad este fue uno de los proyectos de los que más me divertí en toda la carrera y espero realmente que este material que te estoy entregando te sea de gran ayuda para que comiences con el píe derecho.

2 thoughts to “Oscar++ Mi propio compilador”

  1. Excelente Oscar. Tengo 10 años aprox. programando y por “n” o “y” situaciòn jamas habia explorado dos de mis mas grandes interrogantes en este campo: Como funciona un compilador y Como se crea un lenguaje .
    Esto me sirve de punto de partida ,saludos!

    1. Hola Enrique, es muy interesante como funcionan los compiladores, sobre todo por que son áreas del conocimiento que no solomos explorar en situaciones normales.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *