Ejemplo 110: La entrada a traducir es la siguiente, implementada en un lenguaje ficticio al estilo C.
- Léxico: encontrar todos los tokens
- Sintáctico: una vez tenemos los tokens, mostrar el AST
- Semántico: validar el AST
- Generación de código: generar el código resultante
Léxico
- Supóngase un programa:
Ahora supóngase la siguiente entrada:
Y finalmente, supóngase que se han definidos las siguientes constantes asociadas a los tokens del lenguaje:
El resultado es:
(getLexema, getTipo, getLinea) | |
---|---|
1 | (“altura”, 257, 1) |
2 | (”+”, 261, 2) |
3 | (“1,75”, 260, 3) |
4 | (?, 0, 2) |
Nota: el ? indica EOF
- Hacer el analizador léxico del siguiente lenguaje usando ANTLR.
Sintáctico
NOTA: los ejs 2 y 3 son gramáticas equivalentes
-
Decir si es una sentencia válida Si realizamos las correspondientes transformaciones:
-
Dada la siguiente gramática G: (IGUAL QUE EL 4)
Determinar si la siguiente entrada es válida. En caso afirmativo, construir su árbol de análisis gramatical (árbol concreto).
Si que es válida, pues se puede llegar a la cadena realizando transformaciones a partir de programa
- Dada la siguiente gramática, implementar un parser de la misma utilizando la técnica recursiva descendente.
Solución:
- Hacer una especificación ANTLR para un lenguaje con las siguientes características:
Solución:
- Primero implementamos el léxico del lenguaje:
lexer grammar Lexicon;
NUM : [0-9]+;
IDENT : [a-zA-Z0-9_]+;
WS : [ \t\r\n]+ -> skip;
- Ahora la gramática:
- Describir los nodos que hay y decir qué hijos tiene cada uno. Crear la gramática abstracta para el lenguaje del ejemplo. Solución:
- Dada la siguiente especificación sintáctica en ANTLR y la gramática abstracta que define los nodos del AST, añadir la especificación de ANTLR el código que cree el AST de las entradas: Abstracta:
programa ⟶ definicion print;
definicion ⟶ tipo nombre:string;
tipoInt:tipo ⟶ ;
tipoReal:tipo ⟶ ;
print ⟶ expr:expresion;
variable:expresion ⟶ nombre:string;
literalEntero:expresion ⟶ valor:string;
Gramática
Solución: El parser sería:
- Dada la siguiente gramática:
a) Añadir la invocación de funciones y procedimientos en BNF y EBNF. Ejemplos:
print a+f();
print log(n,2);
print doble(doble(a));
print (23)
b) Añadir el uso de arrays de 1 dimensión y tipos primitivos y registros de tipos primitivos c) Añadir el uso de arrays de cualquier dimensión y tipo y registros de cualquier tipo d) Añadir la asignación a=4 (múltiple)
Solución:
programa: sent*
sent: PRINT expr ';'
| expr ';' //asignación múltiple
expr: IDENT
| INT_CONSTANT
| expr '*' expr
| expr '+' expr
| IDENT '(' expr ')' //invocación a funciones
| IDENT '[' expr ']' //arrays de cualquier dimensión
| expr '=' expr //asignación simple
- Hacer una gramática en BNF de un lenguaje con las siguientes características:
- Un conjunto está formado por uno o más elementos entre paréntesis separados por comas.
- Cada elemento puede ser un número u otro conjunto.
- Los números están formados por dígitos de 1 al 3, pudiendo haber espacios entre ellos. Suponer que el analizador léxico devuelve los tokens UNO, DOS y TRES.
- Los números están formados por un número impar de dígitos y son capicúa.
Ejemplos de entradas válidas serían:
(131)
(3, 222, 12 313)
(12121, 2, (333), (2, 3, 1111111))
(2132312, ( ( (1, 2), 2, 131), 1, 2), 3322233)
Se pide hacer una gramática BNF usando las construcciones anteriores. Anotar en cada regla qué construcción sigue.
Solución: La gramática pedida sería:
- Sea un lenguaje para la definición de variables.
int a, b;
double x;
Requisitos:
- Sólo hay dos tipos: entero (int) y real (double).
- En una sola definición, pueden definirse varias variables del mismo tipo.
- En un programa puede haber varias definiciones, pero también sería válido que no hubiera ninguna.
Se pide especificar la sintaxis de dicho lenguaje en BNF y en EBNF. Indicar, además, los patrones usados en las listas.
Solución BNF
Aplicando los patrones anteriores y sustituyendo sus símbolos por los nombres adecuados para este ejercicio, quedaría finalmente esta gramática.
definiciones: ε | definiciones definicion // lista 0+ss
definicion: tipo nombres ';' // secuencia
nombres: IDENT | nombres ',' IDENT // lista 1+cs
Como puede verse, toda regla sigue alguna de las tres construcciones básicas. Lo cual, a medida que el alumno se familiarice con ellas, hará que cada vez le será más fácil entender y crear cualquier gramática.
En el caso concreto de las listas (reglas 1 y 3) se han aplicado los patrones correspondientes a la versión recursiva a izquierda.
Solución EBNF
La solución en EBNF sería:
definiciones: definicion* // lista 0+ss
definicion: tipo nombres ';' // secuencia
nombres: IDENT (',' IDENT)* // lista 1+cs
Como puede verse, las construcciones son las mismas, sólo que en el caso de las listas se han tomado los patrones de otra columna de la tabla (la última).
- Sea la siguiente gramática ambigua.
expr ⟶ expr '+' NUM
| NUM
| '(‘ tipo ')' expr // Suponemos tipo definido
Se pide:
- Demostrar que es ambigua.
- Dar una solución no ambigua con cambio de lenguaje y otra usando reglas de selección en ANTLR.
Solución
1. Demostrar que es Ambigua
Tal y como se dijo anteriormente, no existe un algoritmo que dada una gramática cualquiera indique si es ambigua o no. Pero una forma de demostrar que una gramática en particular sí es ambigua es mostrar una entrada para la cual al menos se puedan crear dos árboles concretos (es decir, que haya dos formas de interpretarla).
Una entrada que demostraría que la gramática es ambigua sería la siguiente:
(double) 3 + 4
Esta entrada puede producir dos árboles concretos (es decir, se puede llegar del símbolo inicial a la cadena mediante dos caminos) ya que hay dos formas de interpretarla:
En el primer árbol se interpreta que primero se convierte el 3 a double y al resultado de la conversión se suma al 4. En el árbol de la derecha se interpreta que primero hay que sumar y luego realizar la conversión de todo ello.
Por tanto, la gramática es ambigua.
2. Solución con Cambio de Lenguaje
Se podría cambiar el lenguaje para que el valor sobre el que se realiza la conversión tuviera que estar delimitado, por ejemplo, con ’<’ y ’>‘. En esta caso, ya no habría dos formas de interpretar la entrada:
Las entradas dejarían claro el alcance de la conversión:
(double)<3 + 4>
(double)<3> + 4
Solución con Reglas de Selección
En vez de cambiar el lenguaje, se puede dejar como está y, dejando la gramática ambigua, determinar qué derivación (interpretación) se quiere para dichas entradas.
De los dos árboles que generaba la entrada anterior, lo habitual es que se quiera interpretar que primero se hace la conversión y luego la suma, es decir, que la conversión tenga mayor prioridad. Por tanto, se quiere seguir la derivación que se corresponda con el árbol de la izquierda.
Para ello, en ANTLR habría que dar mayor prioridad (poner primero) la regla de la conversión y luego la de la suma.
Solución con Gramática Equivalente
Aunque no se ha pedido en el enunciado, por si se tiene curiosidad, aquí estaría la solución hallando una gramática equivalente que no es ambigua.
- Hacer una especificación en ANTLR para un lenguaje con las siguientes características:
- Una entrada tendrá sólo una única sentencia ‘print’ seguida de una expresión y un punto y coma.
- Características de las expresiones:
- Operadores relacionales:
'==' y '!='.
- Operadores lógicos: ’||’ y ’&&’ (or y and respectivamente).
- Operadores aritméticos: ’+’, ’-’, ’*’ y ’/‘.
- Agrupación de expresiones entre paréntesis.
- Operadores relacionales:
Ejemplos de entradas válidas en este lenguaje serían:
Solución: Lo primero, habría que hacer el léxico del lenguaje.
Y a continuación se haría la gramática.
Nótese cómo el orden de las reglas corresponde con la prioridad de los operadores (los operadores de la misma prioridad, por tanto, deben ir en la misma regla).
Supóngase la siguiente entrada válida.
Para dicha entrada, el árbol concreto que correspondería con la interpretación que hará ANTLR de la misma tal y como están ordenadas las reglas sería el siguiente. 15. Crear la gramática abstracta para el lenguaje del siguiente ejemplo:
Aclaraciones:
- Hay sólo dos tipos: int y double.
- Las condiciones del if son de tipo entero (no hay tipo booleano).
- Las definiciones de las variables tienen que estar antes de todas las sentencias.
- Puede haber varias sentencias en cada rama del if.
Solución:
- Dada la siguiente especificación sintáctica en ANTLR y la gramática abstracta que define los nodos del AST, añadir a la especificación de ANTLR el código que cree en AST de las entradas.
Solución:
- Dada la gramática en ANTLR y la gramática abstracta siguientes, añadir al fichero de ANTLR las acciones que construyan el AST.
Solución:
Semántico
Solución:
(G):
(A):
{expression.lvalue} dominio=booleano
(R):
Versión a mano:
Solución: Versión a mano:
-
¿Cuál es el tipo Java de esta tabla de símbolos?
-
Dada la siguiente CFG, definir una gramática atribuida para realizar la fase de Identificación. Nota, se puede usar el objeto st del ejercicio anterior. Una vez que se ha definido la AG, impleméntelo en Java utilizando el patrón de diseño Visitor. (G): Solución: (A): |Atributo|Afecta a|Dominio| |—|—|—| |definition|variable|VarDefinition|
(R):
(G): (A):
Atributo | Afecta a | Dominio |
---|---|---|
type | expression | {int, double, char, error, array} |
(R): |
8. (G): (A):
Atributo | Afecta a | Dominio |
---|---|---|
returnType | statement | type |
(R): |
- Comprobar que las funciones tengan return en la siguiente gramática. Crear la AG correspondiente:
Nota: falta poner que los dos atributos añadidos tienen dominio booleano
Generación de código
-
Nota: un entero ocupa 2 bytes
-
Dado el siguiente programa de alto nivel:
Escribir el código destino MAPL. Suponer que:
- Las direcciones de memoria de a y b son 0 y 2
- Ambas son variables enteras
Solución:
-
(CREO QUE NO ESTÁ BIEN)
-
EJ TIPICO EXAMEN Solución:
-
Añadir un nuevo método getNumberOfBytes():int a todos los tipos
-
Hay dos estrategias para calcular los desplazamientos de los campos de registro:
- Iterando el nodo principal a través de los nodos secundarios de RecordField
- En los nodos secundarios RecordField, modificando una variable global declarada como un campo en el Visitor 1 para campos y 2 para variables globales (R):
- Desplazamiento de las variables globales: (R):
-
Implementación es trivial
---FIN EJERCICIO---
(R):
- AGs para Generación de Código (G): (A): code (R):
- Ejercicio de contexto: (G): (A): code y pushValue (R):
Solución:
Solucion:
-
Hacer la plantilla de código execute de una sentencia while Solución:
-
Hacer la plantilla de código execute para una sentencia do/while
-
Especificar la plantilla de código execute para el bucle for