Implementación de Gramática y Generación de Código Intermedio para Compiladores
Introducción a las Reglas Gramaticales y Semánticas
A continuación, se presenta la especificación formal de la gramática y sus respectivas acciones semánticas asociadas para la generación de código en ensamblador MIPS. Este documento detalla el manejo de instrucciones básicas, declaraciones, expresiones aritméticas y gestión de paréntesis dentro de un compilador.
Estructura General del Programa
La estructura principal define cómo se componen las listas de instrucciones que conforman el cuerpo ejecutable del programa.
programa ::= lista_instrucciones:li {: :};
lista_instrucciones ::= instruccion:i {: :} | lista_instrucciones:li {: :};Definición de Instrucciones y Acciones Semánticas
Las instrucciones contemplan operaciones de entrada y salida (leer y mostrar), así como la asignación de valores y variables. Cada regla incluye validaciones semánticas para verificar la existencia previa de variables en la tabla de símbolos.
Instrucción LEER
Permite capturar un valor desde la consola y almacenarlo en la memoria correspondiente a la variable indicada.
instruccion ::= LEER IDENTIFICADOR:id1 PTOYCOMA COMENTARIO:c
{:
if(!variables.containsKey(id)){
variables.put(id, ("var_"+id));
main += "#" + c + "\n";
main += " #Obtener el valor de " + id + " de la consola\n";
main += " li $v0,5 #cargar la llamada de lectura en v0\n";
main += " syscall #hacer la llamada al sistema\n";
main += " sw $v0, " + variables.get(id) + " #store in memory\n";
RESULT = main;
} else {
main += "#" + c + "\n";
main += " #Obtener el valor de " + id + " de la consola\n";
main += " li $v0,5 #cargar la llamada de lectura en v0\n";
main += " syscall #hacer la llamada al sistema\n";
main += " sw $v0, " + variables.get(id) + " #store in memory\n";
RESULT = main;
}
:}Instrucciones MOSTRAR
Imprimen en la consola el valor actual de una variable, validando previamente su declaración en el ámbito del programa.
| MOSTRAR IDENTIFICADOR:id1 PTOYCOMA COMENTARIO:c
{:
if(!variables.containsKey(id1)) {
fallo = true;
error = "ERROR: La variable no ha sido declarada previamente. Línea: " + (id1left+1);
} else {
main += "#" + c + "\n";
main += "#Mostrar el valor de " + id1 + " en la consola\n";
main += "lw $a0, " + variables.get(id1) + " #load in a0\n";
main += "\nli $v0, 1 #cargar la llamada de escritura en v0\n";
main += "syscall #hacer la llamada al sistema\n";
RESULT = main;
}
:}
|
| MOSTRAR IDENTIFICADOR:id1 PTOYCOMA
{:
if(!variables.containsKey(id1)) {
fallo = true;
error = "ERROR: La variable no ha sido declarada previamente. Línea: " + (id1left+1);
} else {
main += "#Mostrar el valor de " + id1 + "\n";
main += "lw $a0, " + variables.get(id1) + " #load in a0\n";
main += "\nli $v0, 1 #cargar la llamada de escritura en v0\n";
main += "syscall #hacer la llamada al sistema\n";
RESULT = main;
}
:}Instrucciones de Asignación
Gestionan la asignación de números literales o resultados de expresiones evaluadas hacia variables específicas.
| IDENTIFICADOR:id1 ASIGNACION NUMERO:n1 PTOYCOMA
{:
if(!variables.containsKey(id1)) {
variables.put(id1, ("var_" + id1));
}
if(!numeros.contains(n1)){
numeros.addElement(n1);
}
main += "#\n";
main += " lw $t1, constants+" + (numeros.indexOf(n1)*4) + "\n";
main += " sw $t1, " + variables.get(id1) + "\n";
RESULT = main;
:}
|
| IDENTFICADOR:id1 ASIGNACION expresion:e PTOYCOMA COMENTARIO:c
{:
if(!variables.containsKey(id1)) {
variables.put(id1, ("var_" + id1));
}
main += "#" + c + "\n";
main += e;
RESULT = main;
:}Evaluación de Expresiones Aritméticas
Se definen las distintas combinaciones posibles para el cálculo de expresiones que involucran números, variables, operadores y estructuras de agrupación con paréntesis.
expresion ::=
RESTA NUMERO:n1 {: %}
| RESTA IDENTIFICADOR:id1 {: %}
| NUMERO:n1 {: %}
| IDENTIFICADOR:id1 {: %}
| RESTA IDENTFICADOR:id1 operador:op IDENTIFICADOR:id2 {: %}
| RESTA IDENTIFICADOR:id1 operador:op NUMERO:n1 {: %}
| IDENTFICADOR:id1 operador:op IDENTIFICADOR:id2 {: %}
| IDENTIFICADOR:id1 operador:op NUMERO:n1 {: %}
| NUMERO:n1 operador:op NUMERO:n2 {: %}
| NUMERO:n1 operador:op IDENTIFICADOR:id1 {: %}
| PAR_AB parentesis:p PAR_CERR {: main += p; RESULT = main; %}
| PAR_AB parentesis:p PAR_CERR operador:op PAR_AB parentesis:p PAR_CERR {: %}
| PAR_AB parentesis:p PAR_CERR operador:op IDENTIFICADOR:id1 {: %}
| PAR_AB parentesis:p PAR_CERR operador:op NUMERO:n1 {: %}
| IDENTIFICADOR:id1 operador:op PAR_AB parentesis:p PAR_CERR {: %}
| NUMERO:n1 operador:op PAR_AB parentesis:p PAR_CERR {: %};Manejo de Paréntesis y Prioridad de Operadores
El tratamiento de los paréntesis permite asegurar la precedencia correcta en operaciones complejas dentro de la pila de registros temporales.
parentesis ::=
IDENTIFICADOR:id1 operador:op parentesis:p {: %}
| NUMERO:n1 operador:op parentesis:p
{:
if(!numeros.contains(n1)) {
numeros.addElement(n1);
}
main += " lw $t" + pos + ", constants+" + (numeros.indexOf(n1)*4) + "\n";
pos++;
main += p;
main += " " + op + " $t0, $t1, $t2";
%}
| IDENTIFICADOR:id1 {: %}
| NUMERO:n1
{:
if(!numeros.contains(n1)) {
numeros.addElement(n1);
}
main += " lw $t" + pos + ", constants+" + (numeros.indexOf(n1)*4) + "\n";
%};Operadores Soportados
Se mapean los operadores aritméticos básicos a sus respectivas instrucciones mnemotécnicas de ensamblador.
operador ::=
SUMA {: RESULT = "add"; %}
| RESTA {: RESULT = "sub"; %}
| MULT {: RESULT = "mult"; %}
| DIV {: RESULT = "div"; %};