Proletool es una herramienta, de uso por internet, para facilitar el
aprendizaje/enseñanza de la asignatura Procesadores de Lenguajes. Ha
sido diseñada y desarrollada en la Escuela Superior de Informática de
la Universidad de Castilla-La Mancha por el profesor J.J.
Castro-Schez, y el equipo formado por P.A. Santos, J. Santos, R.
Miguel y D. Arenal. Esta herramienta permite que los usuarios
(estudiantes/profesores) puedan formular problemas sobre construcción
de analizadores léxicos y sintácticos ascendentes (SLR1, LR1, LALR1) y
descendentes (LL1), así como, si lo desea, plantear soluciones para
los mismos. La herramienta analizará los problemas presentados y los
corregirá, y en el caso de que se hayan presentado soluciones, también
chequeará como de correctas son. Esta información será empleada para
proporcionar un feedback que oriente al usuario en la realización de
más ejercicios en función de los errores cometidos.
Además, Proletool también posee un módulo que permite experimentar con
los analizadores construidos realizando simulaciones de análisis de
cadenas, de este modo se pretende facilitar la
comprensión de su funcionamiento.
Su arquitectura funcional es la que se muestra a continuación:
El mecanismo de comunicación con la herramienta Proletool es un
lenguaje formal que se describe a continuación.
El lenguaje Proletool es un lenguaje formal que ha sido diseñado y
propuesto para permitir la comunicación con la herramienta del mismo
nombre. Este lenguaje es libre de contexto para la generación de
infinitas cadenas de entrada, esto significa que se podrán plantear a
la herramienta infinitos ejercicios/soluciones. El lenguaje es "case
sensitive", es decir se distingue entre minúsculas y mayúsculas.
Los elementos (tokens) que conforman el vocabulario del lenguaje
Proletool son los siguientes:
Identificadores (id). La estructura de un identificador se corresponde con el de una secuencia de números, letras o el símbolo '_'. Los identificadores no pueden comenzar con un número y su longitud debe ser de uno como mínimo. Ejemplo de identificadores válidos:
gramática_1
__gramatica0001
OtraGramatica
_007
_0
Las únicas cadenas que no se pueden usar como identificadores son las palabras
reservadas.
Expresiones regulares (regexp). Son descripciones válidas de expresiones regulares (a+, a*, abc, etc). Más adelante se profundizará
sobre la forma de construir expresiones regulares.
Implicación (:=) Este símbolo es empleado para la definición de
tokens en los analizadores léxicos y para la definición símbolos
gramaticales en las reglas o producciones.
Igualdad (=). Este símbolo es empleado para asignar valores.
Alternativa (|). Este símbolo es empleado para facilitar la
definición de símbolos gramáticas, ya que permite el agrupamiento de
las reglas o producciones que definen a un mismo símbolo. Es el 'o'.
Separadores/agrupadores: Son un conjunto de símbolos empleados para
facilitar la interpretabilidad del lenguaje. Estos símbolos son los
siguientes:
En el grupo de los agrupadores tenemos:
Llaves ({,},%{,%})
Paréntesis ((,)).
Corchetes ([,]).
En el grupo de los separadores:
Punto y Coma (;). Este símbolo es empleado para finalizar cada línea o sentencia del lenguaje.
Coma (,). La coma es empleada para separar los elementos que aparecen en listas.Paréntesis ((,)).
Cada símbolo de este grupo es un token.
Palabras reservadas. A continuación se muestran las palabras reservadas del lenguaje:
lexer
global
analysis
LL1
SLR1
LR1
LALR1
terminal
nonterminal
grammar
exercise
use
solution
nullables
firsts
followers
lookaheads
ll1_parsing_table
slr1_parsing_table
lr1_parsing_table
lalr1_parsing_table
action_table
goto_table
accept
error
Cualquier carácter entrecomillas (char_iq). Cualquier carácter ASCII
entre comillas simples es también un elemento del vocabulario del
lenguaje y es considerado un símbolo terminal de la gramática. Se
suelen usar en las definiciones de las reglas o producciones
gramaticales. Los caracteres permitidos son:
Este tipo de símbolos, sólo puede ser de un único caracter, es decir que
símbolos del tipo:'>>' o '+=' no son válidos.
Para poner la comilla simple o la barra invertida hay que utilizar el caracter
de escape. Es decir: '\'' '\\'
Los caracteres alfabéticos, y numéricos (los componentes de los
identificadores), no se pueden poner entre comillas, ya que estos se pueden
poner directamente sin entrecomillar.
Comentarios. Los comentarios se pueden hacer de diversas formas:
Comentario de línea:
Este se hace poniendo los caracteres: '//'.
Comentario de bloque: El inicio del comentario de bloque esta precedido
por los caracteres '/*' y finaliza con los caracteres '*/'.
Ejemplo de comentarios:
// Esto es un comentario de línea ...
/*
* Esto es un comentario de bloque ...
*/
En resumen son los comentarios al estilo C++, Java.
Un archivo válido tiene cuatro secciones: definición de analizadores
léxicos, definición global, definición de ejercicios, propuesta de
soluciones.
// Zona de definición de analizadores léxicos
lexer lexer1
%{
...
%}
lexer lexer2
%{
...
%}
...
// Fin de zona de definición de analizadores léxicos
// Zona de definición global
global
{
...
}
// Fin de zona de definición global
// Zona de definición de ejercicios
grammar gramatica1
{
...
}
grammar gramatica2
{
...
}
...
// Fin de zona de definición de ejercicios
// Zona de propuesta de soluciones
solution gramatica1
{
...
}
solution gramatica2
{
...
}
...
// Fin de zona de propuesta de soluciones
A continuación se mostrará brevemente cual es el contenido de cada parte:
Zona de Definición de Analizadores Léxicos:
En esta zona se pueden definir uno o varios analizadores léxicos, que
posteriormente podrán ser utilizados en la definición de una
gramática. No es obligatorio incluir Definición de analizador léxico.
Más adelante se determinará como se realiza la definición de
analizadores léxicos y como se establece su uso.
Zona de Definición Global:
En esta zona se pueden definir elementos del lenguaje (símbolos, tipos
de análisis o producciones) que pueden emplearse en la zona de
definición de ejercicios. El propósito de esta sección es facilitar la
escritura de las sección de definición de ejercicios. Más adelante se
explicará cómo se realiza y en qué consiste.
Zona de Definición de Ejercicios:
En esta zona es donde el usuario podrá plantear ejercicios a la
herramienta. Se podrán proponer tantos ejercicios como el usuario
desee, por lo menos uno. Cada ejercicio estará perfectamente definido
y unívocamente identificado. Más adelante se profundizará sobre este
aspecto.
Zona de Propuesta de Soluciones:
En esta zona es donde se propondrán las soluciones a los ejercicios
propuestos en la sección anterior. No es obligatorio presentar
soluciones, ya que la herramienta en caso de que no existan
simplemente mostrará las soluciones de los ejercicios planteados. Más
adelante se proporcionará más información sobre como escribir
soluciones y relacionarlas con ejercicios.
La zona de definición de analizadores léxicos, global y propuesta de soluciones es opcional, pero debe haber como mínimo un ejercicio declarado en la zona de definición de ejercicios.
Dentro del cuerpo de las declaraciones globales y gramáticas locales se puede
poner lo mismo a excepción de los grupos de producciones, que ya se explicará
en otro apartado.
Todos los símbolos declarados globalmente, son accesibles desde todas las
gramáticas locales, es decir si se declaran globalmente no hace falta volver a
declararlos localmente. Lo que no se puede hacer es, en un mismo ambito definir dos
veces el mismo símbolo. Ejemplo:
global {
nonterminal A, B, C;
terminal a, b;
// no se puede declarar dos veces un símbolo esto seria erroneo
terminal a, B;
}
grammar gram_1
{
// no hace falta declarar A, a, b ya que estan declarados globalmente.
A := a b;
}
Si se declara un símbolo globalmente y este se vuelve a declarar localmente, no
pasa nada, tiene más prioridad el local. Claro si al declararlo de forma local
es del mismo tipo no pasa nada, pero si se declara localmente de un tipo
diferente al global tiene más precedencia el local. Ejemplo:
global
{
// en la parte global se declara como no terminal
nonterminal A;
}
grammar gram_1
{
nonterminal S;
// esta delaracion tiene mas prioridad que la global
// por lo tanto 'A' es un terminal.
terminal A;
S := A;
}
El nombre de una gramática es un símbolo global, si se vuelve a declarar
localmente, por ejemplo como un terminal no pasa nada, siempre tiene más
precedencia la declaración local.
Todos los símbolos declarados localmente en una gramática no interfieren en
otra gramática y se pueden volver a declarar. Ejemplo:.
grammar gram_1
{
terminal uno, dos;
}
grammar gram_2
{
/* esto no generea conflicto al ser gramáticas distintas */
terminal uno, dos;
}
Una parte fundamental del lenguaje en dos zonas del lenguaje
(Definición Global y Definición de Ejercicios) es la escritura de
producciones o reglas gramaticales, es por ello por lo que se presta
especial atención a la forma en la que se definen.
Una producción tiene dos partes:
Parte izquierda:
En la parte izquierda sólo pondrá aparecer un
identificador de un címbolo que haya sido declarado como
nonterminal.
Parte derecha: EEn la derecha podrán aparecer secuencias de
símbolos declarados como terminal y/o nonterminal o
incluso caracteres entrecomillados que son considerados símbolos
terminales y que no hay que declararlos. Para poner la palabra vacía
simplemente hay que dejar la parte derecha en blanco. Además el
símbolo '|' tiene el significado de la 'or'. Ejemplo:
grammar gramatica_de_prueba
{
nonterminal A, B, C;
terminal a, b, c;
// la parte derecha son dos terminales eguidos
// ademas como se puede ver he puesto el caracter +,
// y no hace falta declararlo.
A := a '+' b;
// si se deja en blanco entonces es la palabra vacia.
A := ;
// lo anterior también se podria haber puesto en una
// unica sentencia utilizando la or.
// A := a b | ;
}
Aspectos a destacar en la escritura de las producciones:
La definición de una producción siempre termina con el símbolo ';'.
Las partes izquierda y derecha de una producción se separa por el símbolo ':='.
Los símbolos de la parte derecha de una producción se separan
con espacios, si se ponen dos identificadores seguidos, si entre
medias se ponen un carácter('+', '/', ...) no hace falta poner
espacios. Por ejemplo: p := a'+'b
Con el símbolo '|' se consigue declarar varias producciones en
una misma expresión.
Una parte derecha de una producción en blanco implica el uso de
la palabra vacía
Antes de poder utilizar un símbolo en una producción hay que declararlo. Si se
quiere se pueden repetir producciones no se genera ningún conflicto. Ejemplo:
global
{
nonterminal A, B, C;
terminal a, b, c;
// esto es un grupo.
prods_globales =
{
A := a c | b;
B := a;
// no hace falta poner espacios, cuando se utilizan
// caracteres entrecomillados. Además como ya se ha comentado,
// no hace falta declarar el terminal '+'
B := a'+'b;
// no pasa nada simplemente se ignoran, y se dará un aviso.
B := a;
B := a;
B := a;
}
}
Con el objetivo de facilitar la simulación de las gramáticas, se permite de manera opcional
la definición de analizadores léxicos, que actuarán como reconocedores de los terminales usados en las definiciones de los ejercicios que se incluyen en la zona de definición de ejercicios.
En un archivo se puede incluir y utilizar uno o varios analizadores léxicos.
Donde cada elemento tiene el siguiente significado:
lexer. Palabra reservada que indica el comienzo de una definición léxica.
tokens. Palabra reservada necesaria para definir los tokens.
pass. La utilización de pass es opcional y se usa cuando se
desea que ciertos tokens sean ignorado una vez reconocidos en la entrada (no se le pasan a el analizador sintáctico).
identificador_lexer. Un identificador válido (globalLexer, lexerTest, etc.. ).
identificador_token. Un identificador valido (id, MAS, PARENTESIS, etc...).
expresion_regular. Descripción válida de una expresion regular (a+, a*, abc, etc). Más adelante, se establecen cuáles son las expresiones regulares válidas.
lista_identificadores. Se puede definir un único identificador o varios separados por comas (Ejemplo:
pass id; o pass id1, id2, id3;).
Para poder usar en un ejercicio un analizador léxico definido en esta zona, deberá indicarse explícitamente por medio de la orden: useidentificador_lexer;.
Se deben tener en cuenta las siguientes consideraciones cuando se utilize un lexer:
Si se utiliza la sentencia analysis dentro de una gramática, use deberá estar situada detrás
de esta sentencia.
Si se utiliza la sentencia nonterminal dentro de una gramática, use deberá estar situada delante
de esta sentencia.
No esta permitido definir terminales dentro de la gramática donde se utiliza un lexer.
No esta permitido usar un lexer dentro de un bloque de definiciones globales.
Un ejemplo de lo dicho anteriormente es el siguiente:
lexer globalLexer
%{
tokens
{
//definicion de los tokens.
}
%}
global
{
...
}
grammar gramatica_local
{
analysis LALR1;
use globalLexer;
nonterminal Uno, Dos;
...
}
grammar gramatica_local2
{
analysis LALR1;
use globalLexer;
...
}
grammar gramatica_local3
{
use globalLexer;
nonterminal Uno, Dos;
...
}
grammar gramatica_local4
{
use globalLexer;
...
}
Una expresión regular válida se puede definir como:
Un caracter simple: a , b, c, #, @, etc..
Una cadena: "caracter+".
Una clase: [ (caracter|rangos)+ ] Los únicos rangos permitidos son:
0-9. Rango numérico ascendente o un subconjunto de éste (también ascendente).
a-z. Rango de letrás ascendente o un subconjunto de éste (también ascendente).
A-Z. Como en el caso anterior pero con létras mayúsculas.
Una secuencia de escape: \r, \n, \t, \b, \f, \\, \" . Las secuencias de escape se pueden utilizar en cualquier parte de la
expresión regular excepto la secuencia \" que no esta permitida dentro de las cadenas.
. : se reconoce un caracter simple. Esta definición no es aplicable dentro de una clase o cadena en las que se evalúa como el carácter punto.
Dentro de la definición de una expresión regular no se permiten el carácter de salto de línea(\n) o el de retorno de carro(\r).
Si a y b son dos expresiones regulares válidas están permitidas las siguientes operaciones:
a b. La expresión regular resultante reconoce lo que reconozca a seguido de lo renocozca b (concatenación).
a | b. La expresión regular resultante reconoce lo que reconozca a o lo que reconozca b (unión).
a*. La expresión regular resultante reconoce cero o más ocurrencias de a. (clausura de Kleene).
a+. Es equivalente a aa*.
a?. Es opcional reconocer a.
!a. Se reconoce todo aquello que no es reconocido por a.
( a ). Se reconoce lo que sea reconocido por a.
La forma en el que se resolverán los casos en los cuales varias expresiones regulares reconozcan la misma cadena de entrada
será por el orden de definición. La primera expresión regular definida tendrá prioridad sobre las demás.
Un ejemplo de definición y utilización completo de un lexer es el siguiente:
lexer calcLexer
%{
tokens
{
id := [a-zA-Z]+;
num := -?[0-9][0-9]*;
MAS := "+";
MENOS := "-";
POR := "*";
DIV := "/";
PAR_IZ := "(";
PAR_DE := ")";
nulos := [ \r\n\t];
}
pass nulos;
%}
grammar calc
{
analysis LALR1;
use calcLexer;
nonterminal E, T, F;
E := E MAS T | E MENOS T | T;
T := T POR F | T DIV F | F;
F := PAR_IZ E PAR_DE;
F := id | num;
}
En esta zona se definen símbolos, tanto terminales como no terminales, y grupos de producciones identificados que pueden ser usados en la definición de ejercicios. De esta forma, se simplifica la escritura de ejercicios similares, pero obliga a tener en cuenta el ámbito de visibilidad de las declaraciones. Ver resolución del ámbito ya explicado anteriormente.
En el cuerpo de la parte global lo primero que se pone es el tipo de análisis a
realizar, este se declara poniendo la palabra reservada analysis seguida
de una lista con los análisis que se quiere realizar. Esta lista no puede ser
vacía y como máximo se pueden declarar 4 tipos de análisis. Ejemplo:
global {
// se ha elegido el analisis LL1 y LR1.
analysis LL1, LR1;
}
La declaración del tipo de análisis sólo se puede hacer una vez (en cada
gramática o globalmente).
Además es opcional, si se quiere no hace falta ponerla, por defecto se
realizará un análisis LL1. Los tipos válidos de análisis son: LL1, SLR1, LR1,
LALR1.
Otro elemento que se puede declarar son los símbolos de los ejercicios, que
pueden ser terminales y no terminales. Su declaración es
opcional. Además, no importa el orden en que se declaren, y ésta si se puede repetir
varias veces.
global {
terminal a, b, c, d;
nonterminal A, J, P;
// no importa el orden y se puede poner mas de uno
nonterminal Uno, Dos, Tres;
terminal k, l;
}
Como se puede ver la declaración de terminales esta precedida de la palabra
reservada 'terminal', y los no terminales de la palabra 'nonterminal'.
Ejemplo resumen de la parte de declaraciones:
global {
analysis LL1, SLR1;
nonterminal A, J, P;
terminal a, b, c, d;
}
También se pueden declarar conjuntos de producciones, a los que se les asigna un identificador que luego podrá ser invocado en la definición de un ejercicio.
global {
analysis LL1;
nonterminal Z, N, A, C, H;
terminal b, c, j, t, k, h, n, z;
prod_comunes = {
N := b k | c H;
A := h N | z;
H := n | k;
}
}
grammar gram_no_ll1 {
Z := b N A c | N c | b j | c A | t;
prod_comunes; // Uso del conjunto previamente declarado
}
En esta zona se especifican los ejercicios que se desean resolver. Proletool resuelve ejercicios sobre generación de analizadores léxicos y analizadores sintácticos descendentes y ascendentes, mostrando las tablas de análisis generadas, así como la información que permite construírlas.
Un ejercicio es una especificación de una gramática libre de contexto, G=(V, T, P, S), donde V es el conjunto de símbolos no terminales, T el conjunto de símbolos terminales, P el conjunto de producciones de la gramática, y S es el símbolo inicial de la gramática, a la que se le asocia uno o varios tipos de análisis sintáctico (LL1, SLR1, LR1, LALR1). En esta zona se pueden especificar tantos ejercicios como se deseen.
En el cuerpo de la parte de ejercicios lo primero que se pone es el tipo de análisis a
realizar. Este se declara poniendo la palabra reservada analysis seguida
de una lista con los análisis que se quiere realizar (LL1, SLR1, LR1, LALR1). Esta lista puede ser vacía si se declara globalmente. Después se pueden declarar los símbolos terminales y no terminales, en el orden que se quieran y en tantas líneas como se consideren. Su declaración es también opcional si se declara globalmente. También se puede usar un analizador léxico como ya se ha explicado anteriormente. Para finalizar, se especifican las producciones de la gramática. Ejemplo:
grammar gramatica_1 {
analysis LR1, LALR1;
nonterminal S, C;
terminal c, d;
S := C C;
C := c C | d;
}
Hay que destacar que el símbolo inicial de la gramática, o axioma, no es necesario declararlo, porque por defecto se considera que es el primer no terminal de la parte izquierda de la primera producción declarada. En el ejemplo anterior, el axioma sería la S.
En esta zona se define la propuesta de las soluciones, tanto para la información básica (anulables, iniciales y seguidores), como para los análisis descendente (símbolos de predicción y tabla LL1), y ascendente (tablas SLR1, LR1 y LALR1).
Una solución está formada por 4 partes: gramática (de la cuál es la que se propone la solución), propuesta de solución de información básica, de análisis descendente y de análisis ascendente:
Gramática.
En esta sección indica el nombre de la gramática, anteriormente definida, sobre la cuál se propone una solución. Se usa la palabra reservada solution seguido del nombre de la gramática. Es la única parte obligatoria a escribir en la parte de solución. Cualquier otro campo, que se verán a continuación, se puede eliminar.
Información básica.
En esta sección se especifican las soluciones a los anulables, iniciales y seguidores. Se deben especificar en este orden necesariamente. Se utilizan las palabras reservadas nullables, firsts y followers como indicadores, y entre llaves se escribe la solución. En el caso de los anulables, se escribe una lista con los símbolos no terminales, candidatos a ser anulables, separados por comas. En iniciales y seguidores, se indica el símbolo no terminal, seguido de un igual y seguido, entre llaves, de la lista de los iniciales o seguidores de dicho no terminal. Ejemplo:
nullables{
A, B, D;
}
firsts{
A = {id, num};
B = {id};
}
followers{
A = {num, $};
}
Análisis descendente.
En esta sección se especifican las símbolos de predicción y la tabla de análisis LL1. Los símbolos de predicción se deben especificar después de la información básica, y antes de los análisis (ascendentes y descendentes). La tabla LL1 se puede ubicar en cualquier posición tras los símbolos de predicción, alternándose con las tablas de análisis ascendente. Se utilizan las palabras reservadas lookaheadsy ll1_parsing_table como indicadores, y entre llaves se escribe la solución. En el caso de los símbolos de predicción, se escribe el número de la producción (ver consideraciones), seguido de un igual, y, entre llaves, la lista de los símbolos de predicción de dicha producción. Para el análisis LL1, se escribe, entre corchetes, el símbolo no terminal, una coma, y el símbolo terminal, como referencia a la casilla de la tabla. Después un igual y el número de producción de dicha casilla. Se puede indicar más de una producción, poniéndolas entre llaves, para los casos en los que hay conflicto. Ejemplo:
Análisis ascendente.
En esta sección se especifican las tablas de análisis SLR1, LR1 y LALR1. El orden de aparición se puede alternar, junto la tabla de análisis descendente, pero siempre después de los símbolos de predicción. Se utilizan las palabras reservadas slr1_parsing_table, lr1_parsing_table y lalr1_parsing_table como indicadores, y entre llaves se escribe la solución. El interior de las 3 tablas es idéntico. Por un lado, se especifica la tabla de acción, con la palabra reservada action_table, y entre llaves, las casillas de la tabla. Su estructura es parecida a la tabla LL1, pero el interior de los corchetes contiene primero el estado y después el terminal. Después del igual se especifican los desplazamientos, reducciones, aceptar o error. Por otro lado, se especifica la tabla de ir a, con la palabra reservada goto_table, y entre llaves, las casillas de la tabla. El interior de los corchetes contiene primero el estado y después el no terminal. Después del igual se especifica el estado. .Ejemplo:
En la generación del informe aparecen algunos símbolos especiales que no han
sido especificados en la gramática. El significado de los mismos es el
siguiente:
'@':
Es el símbolo elegido para identificar a la palabra vacía.
'$':
Es el símbolo final. Sólo se puede definir en la parte de la solución, pero no en las anteriores.
El símbolo que aparece con una ' (por ejemplo. S') al lado es el símbolo
inicial de la gramática extendida, para los análisis ascendentes
Aquí se muestran mensajes de salida generados desde el compilador. Estos pueden
ser de los siguientes:
Errores: Hay distintos tipos de errores:
En la mayoría de los errores los dos primeros campos son la fila y la columna
del archivo fuente donde ha ocurrido el error.
Léxicos. Estos ocurren al encontrar en el archivo fuente caracteres
ilegales:
45:18: carácter ilegal: &
Sintácticos. Los dos primeros campos son la fila y la columna donde esta
el error sintáctico, y después está el mensaje del error sintáctico. Ejemplo:
: 35:28: Syntax error en 'kkk'
Semánticos. Estos son errores como declarar dos veces un mismo símbolo,
o utilizar un símbolo antes de declararlo. Por ejemplo, en la siguiente figura
se muestra el mensaje de error que se muestra al declarar dos veces un símbolo:
35:29: En 'gram_ll1': el símbolo 'Z3' ya esta definido.
Si no se ha podido compilar ninguna gramática se indicará con un mensaje como
este:
Error: no se ha podido compilar ninguna gramática.
Conflictos: Los tipos de conflictos son los siguientes:
Conflicto LL(1): Los primero que se indica es el tipo de análisis del
conflicto, después se indica en qué gramática se ha producido, y entre
corchetes se muestra la fila y la columna de la tabla de análisis donde esta el
conflicto. Además se muestran las producciones que han entrado en conflicto.
Ejemplo:
[ll1] : En 'gram_ll1': Conflicto LL(1) [X][a]:
Producción: X -> Y
Producción: X -> a
Conflicto SLR(1) ó LR(1): Lo primero que se muestra es el tipo de
análisis, después la gramática donde está el conflicto, luego el tipo de
conflicto, que puede ser D/R o R/R. Igual que en el anterior entre corchetes se
muestra la posición del conflicto en la tabla de análisis. Ejemplo, de un
conflicto D/R:
[slr1]
: En 'gramática': Conflicto D/R [3][b]: Desplazamiento
: S -> x * b | {= >6}
Reducción : B -> x *|
Como se puede ver si el conflicto es D/R se muestra el item a que indica el
desplazamiento y el de reducción. Si el conflicto es R/R se muestran los items
que han entrado en conflicto.
Warnings: Por ejemplo, se muestra un warning cuando se declaran dos veces
la misma producción. No es un error, pero es conveniente avisarlo. Ejemplo:
En 'gram_ll1': 'Z -> b Z2 ' esta repetida
Si se hace un análisis LL(1) y la gramática es recursiva por la izquierda
también se avisará al usuario, indicando cual es el camino recursivo.
Barra de navegación de soluciones de ejercicios
[ Inicio ]
La primera información que aparece, (tras mostrar los errores o warnings, si se han cometido), es una tabla de navegación como la que aparece a continuación:
Cuando se genera el informe se copia en dicho informe la gramática de la cual
proviene el análisis. El formato puede que no sea el mismo, porque las
producciones que se declaran con el símbolo '|', se separan y se pone una
debajo de la otra. Por ejemplo al escribir estas producciones:
Z := d | X Y Z;
Y := c | ;
X := Y | a;
En el informe las producciones, aparecerían sin el símbolo '|' y numeradas
según el orden de declaración.
Al generar las tablas de análisis siempre se genera información básica:
anulables, iniciales y seguidores. Esta información es de gran
utilidad, y además facilita el encontrar errores y por ejemplo ver la causa de
conflictos.
Si no se ha especificado la parte de solución, un ejemplo de salida es el que se muestra a continuación. La primera tabla es de los anulables, donde se muestra la lista de los símbolos anulables. La segunda es de los iniciales, donde se muestra para cada no terminal, su lista de iniciales:
Anulables
A
B
C
No terminal
Iniciales
A
f
d
e
c
a
B
b
c
C
c
Si se ha especificado la solución, entonces se muestra, en los anulables, la lista de acertados (tick verde), no indicados (admiración naranja) y fallados (cruz roja). En iniciales y seguidores, se muestra, por cada no terminal, los acertados, no indicados y fallados. La unión de los campos de aciertos y no indicados conforman la solución real. Ejemplo, en el que la primera tabla pertenece a los anulables, y la segunda a los seguidores:
Si se ha decidido hacer un análisis sintáctico descendente, entonces aparecerán los símbolos de predicción y la tabla de análisis LL1. Si no se ha especificado la solución de ninguno de ambos, entonces, los símbolos de predicción se mostrarán por cada producción de la gramática, y la tabla LL1, en las filas están los no terminales y en las columnas los terminales. La cabecera de la tabla se ordena por orden alfabético, dejando al siempre al final el símbolo '$'. En caso de haber más de una producción, el fondo será rojo, debido a que existe un conflicto. Ejemplo, primero símbolos de predicción, después, tabla LL1:
Producción
Símbolos de Predicción
A->
C
C
c
$
A->
C
E
c
e
f
A->
D
B
d
A->
a
B
a
B->
λ
$
B->
C
c
$
B->
b
b
C->
λ
c
e
f
$
C->
c
c
D->
d
d
E->
e
e
E->
f
f
a
b
c
d
e
f
$
A
A->
a
B
A->
C
E
A->
C
C
A->
D
B
A->
C
E
A->
C
E
A->
C
C
B
B->
b
B->
C
B->
λ
B->
C
C
C->
λ
C->
c
C->
λ
C->
λ
C->
λ
D
D->
d
E
E->
e
E->
f
Si se han indicado las soluciones, entonces las tablas tendrán el aspecto que se ve a continuación. La tabla de símbolos de predicción sigue la filosofía de la información básica, mientras que en la tabla LL1 se muestra cada producción de un color. El verde es una producción correctamente colocada, naranja es aquélla que no ha sido indicada y roja, que se ha fallado. La unión de las verdes y naranjas forma la solución. Ejemplo:
Cuando se especifica que se realice algún análisis ascendente, se muestran 2 tipos de información: el autómata (en forma de tabla y gráfico) y la tabla de análisis.
El autómata se da de forma tabular. Donde cada
entrada de esta tabla representa un estado de autómata. Dentro del estado están
los items de dicho estado donde en estos items se da la siguiente
información (de izquierda a derecha):
Identificador de la producción.
Producción a partir de la cual se ha generado el item.
Marcador del item.
Símbolos de predicción encerrados entre llaves (si los tiene) .
Transición si es un desplazamiento, o la palabra reducir si es una reducción.
Un ejemplo, de autómata especificado en forma tabular se puede ver en la
siguiente tabla.
Posteriormente se muestra el autómata en forma de gráfico, para que sea más visual.
A continuación, si no se ha especificado la solución, se muestra la tabla de análisis sintáctico ascendente. Las columnas se dividen en dos partes (están sombreadas de color diferente), la
primera de ella es la tabla de acción y esta ordenada por orden alfabético. El
ultimo símbolo de la esta parte siempre es el símbolo final '$'. Un ejemplo, de
ello se puede ver en la siguiente tabla:
a
b
c
$
A
B
C
0
d2
d4
d6
r5
r7
1
3
5
1
aceptar
2
d4
d6
r5
r7
7
5
3
r2
4
r3
5
r4
6
r6
7
r1
La otra es la parte de Ir_A, y también esta ordenada por orden alfabético.
El significado de los símbolos dentro de la tabla son:
Desplazamientos: Van precedidos por la letra 'd'. Por ejemplo 'd5'.
Reducciones: Van precedidos por la letra 'r'. Por ejemplo 'r3'.
Transición de estados:
Estos están en la parte de la tabla de Ir_A.
Aceptación: Es el estado de aceptación y esta identificado con la
palabra 'aceptar'
Error: Entradas de la tabla para la que no hay definida una transición,
estas son las entradas con un símbolo '-'
Al igual que en la descendente si hay un conflicto, la casilla conflictiva se
marcará en rojo.
Cuando se producen conflictos, estos se muestran marcando la casilla conflictiva en rojo.
Si se ha indicado la solución a la tabla, entonces se muestra siguiendo los mismos criterios que en el análisis descendente: de color verde los aciertos, naranja aquéllo no indicado (que junto con los verdes, forman la solución) y rojo los datos erróneos. Ejemplo:
a
b
c
$
A
B
C
0
d2
d3
d4
d6
r9
r5
r7
r9
1
3
5
4
1
aceptar
2
d4
d6
r5
r7
7
5
3
r2
4
r3
5
r4
6
r6
7
r1
Hay que tener una serie de consideraciones a la hora de comparar los resultados obtenidos a mano con los dados por la herramienta.
Lo primero a destacar es que el símbolo inicial de la gramática es el no
terminal de la parte izquierda de la primera producción de la gramática.
Otro aspecto importante es conocer la forma en que en los análisis ascendentes
se generan los estados del autómata, ya que dos tablas de análisis pueden ser
equivalentes y no parecerlo porque se ha podido numerar de forma diferente los
estados que se han utilizado para generar dicha tabla. Para facilitar la
comprobación de ejercicios se va a seguir una convención única en la generación
del autómata de reconocimiento.
Los estados se van numerando en el orden natural, como si el análisis se
hiciera manualmente. Cuando se tiene un estado y se le van a calcular las
transiciones a otros estados los items dentro de dicho estados están ordenado
de la siguiente forma:
Primero los items que proceden de una transición desde otro estado. Si hay
varios, estos se ordenan por el orden en que la producción relacionada con el
item aparece en la gramática.
Después están los items calculados a partir del cierre de los items iniciales.
El orden en que se va calculando el cierre es desde la parte superior del
estado hasta la inferior. Los items con la misma parte izquierda calculados en
el cierre si hay varios se ordenan entre ellos por el orden en que aparece su
producción asicioada en la gramática.
Las transiciones se van calculando en el orden en que están puestos los items
dentro del estado. Por ejemplo, si tuviéramos la siguiente gramática:
1: E =>
E + T 2: E =
> T 3: T
= > ( E ) 4:
T = > id
Parte del autómata que se generaría se puede ver en la siguiente figura.
Del estado 0 visto en la figura anterior podemos destacar lo siguiente:
El primer item del estado es el item por el que empezó el estado: "S' = > E".
Después están los items calculados por el cierre de dicho item.
Los items que tienen la misma parte izquierda , están ordenados por el orden en
que sus producciones fueron declaradas en la gramática. Si en la gramática se
hubiera puesto "E = > T" antes que "E = > E + T" entonces
en el estado el item "E = > T"
hubiera estado antes.
Los estados generados a partir del primer estado se van generando en el orden,
en que se va recorriendo sus items. Es decir, primero la transición del item "S'
= > E", después la del item "E = > T", ...
La forma en que se ha generado estos estados se repite con todos los estados.
En resumen la ordenación es como si el calculo del autómata se hiciera
manualmente, sabiendo la forma en que siempre el sistema genera los estados, si
nosotros al hacer los ejercicios manualmente los hacemos de esta forma, después
será más fácil comprobar los resultados de nuestros propios ejercicios.
Barra de navegación de resultados y recomendaciones
Al finalizar la visualización de la solución de la gramática, si se ha especificado su solución, entonces se mostrarán los resultados obtenidos, de aciertos y fallos cometidos, así como recomendaciones dependiendo de los errores cometidos. Inicialmente hay una tabla de navegación para moverse por los distintos resultados de forma rápida:
Por un lado se puede acceder a los resultados de las soluciones, divididos en información básica, análisis sintáctico descendente y ascendente. Con el botón ver, podemos acceder directamente a la tabla de la solución equivalente para comprobar los datos. Abajo de la tabla hay un enlace a las recomendaciones que se dan para la compilación realizada.
Las tablas de resultados muestran 3 columnas (menos en análisis sintáctico ascendente): el tipo de resultado, los aciertos y los fallos cometidos sobre ese tipo. En análisis ascendente se muestran los tipos de resultados y si ha acertado o no en dicho tipo. Se dividen en secciones, separando información básica de análisis sintácticos, y a su vez éstos divididos en sus partes correspondientes. Un ejemplo es esta tabla, de parte de los iniciales, y otra de parte de un análisis sintáctico SLR1:
Tipo
Cálculo de los iniciales de un no terminal, cuando la producción que lo define comienza por un terminal.
1
2
Cálculo de los iniciales de un no terminal, cuando la producción que lo define comienza por un no terminal que no es anulable.
0
0
Cálculo de los iniciales de un no terminal, cuando la producción que lo define comienza por un no terminal anulable y el inicial pertenece a ese no terminal.
1
2
Cálculo de los iniciales de un no terminal, cuando la producción que lo define comienza por un no terminal anulable y el inicial no pertenece a ese no terminal.
0
0
Cálculo de los iniciales de un no terminal. Se han indicado símbolos iniciales que no lo son.
0
Tipo
Resultado
Construir la Tabla de análisis ascendente SLR1 (parte de acción), al calcular los estados del autómata que reconoce prefijos viables.
Construir la Tabla de análisis ascendente SLR1 (parte de acción), al determinar los símbolos que producen transición.
Construir la Tabla de análisis ascendente SLR1 (parte de acción), al tratar el símbolo delimitador de fin de cadena (dolar), no hay reducciones/desplazamientos asociados a dicho símbolo.
Construir la Tabla de análisis ascendente SLR1 (parte de acción), al calcular el primer estado del autómata que reconoce prefijos viables.
Construir la Tabla de análisis ascendente SLR1 (parte de ir a), al no determinar bien los símbolos que producen cambio de estado tras una reducción.
Construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han indicado acciones erróneas.
Construir la Tabla de análisis ascendente SLR1 (parte de ir a). Se han indicado cambios de estado erróneos.
Warning al construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han omitido desplazamientos.
Warning al construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han omitido reducciones.
Warning al construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han omitido error o el estado de aceptación de la cadena analizada.
Warning al construir la Tabla de análisis ascendente SLR1 (parte de ir a). Se han omitido cambios de estado.
Construir la Tabla de análisis ascendente SLR1. Se comenten errores a la hora de construir la Tabla.
La causa por la cuál no se evalúa más exhaustivamente los análisis sintácticos ascendentes es por la gran cantidad de información de la que precisan, por lo que se ha reducido para no hacer necesario introducir toda la información.
Por cada fallo cometido se da una recomendación al final, indicando el tipo de fallo y unos ejercicios a realizar que ayudarán en dicho ejemplo:
Tipo
Ejercicios
Fallo en el cálculo de los anulables de un símbolo no terminal, cuando éste tiene una producción a la cadena vacía.
Fallo en el cálculo de seguidores de un no terminal, cuando el no terminal aparece en la parte derecha de una producción y a su derecha no hay nada o todo lo que hay se puede anular.
[ Inicio ]
Para simular desde el informe generado al compilar, hay que darle al botón
donde pone simular. Una vez estes en dicha ventana, sólo hay que escribir el
texto de entrada y darle a simular.
Otro aspecto a resaltar es el formato del texto de entrada a simular. Sólo se
puede introducir los terminales declarados en la gramática y los símbolos
especiales. Un ejemplo, con esta gramática:
grammar calc
{
analysis LALR1;
nonterminal E, T, F;
terminal id;
E := E '+' T | E '-' T | T;
T := T '*' F | T '/' F | F;
F := '(' E ')';
F := id;
}
Los únicos terminales permitidos son: id, +, -, /, (, ).
Por ejemplo un texto a simular podría ser el siguiente:
(id + id) / id
Como se puede ver aunque en la gramática el simbolo +, va entrecomillado,
cuando se introduce en el simulador estos símbolos no se ponen entre comillas.
Para simular paso a paso desde el informe generado al compilar, primero hay que pulsar el
botón Simular Step. Se abrirá a continuación una ventana, en la que sólo hay que escribir
un texto de entrada y pulsar el botón simular.
Ejemplo de entorno de simulación para gramáticas LL1
Los botones de simulación realizan las siguientes acciones:
Botón prev: Decrementa el índice de simulación de la tabla pila/entrada en una unidad.
Botón next: Avanza el índice de simulación de la tabla pila/entrada en una unidad.
Botón reset: El índice de simulación, se posiciona en la entrada cero de tabla pila/entrada.
Con esta acción, la simulación se lleva al estado inicial.
Botón end: El indice de simulación, se posiciona en la última entrada de la tabla pila/entrada.
Mediante esta acción se comprueba si la cadena es aceptada.
Botón Arbol sintáctico: Pulsando una vez el botón se muestra el árbol sintáctico
correspondiente al estado actual de la simulación. Si se quiere ocultar el arbol,
bastará con pulsar de nuevo el botón.
Los botones prev, next, reset y end, actualizan
el estado de todas las tablas de simulación y el arból sintáctico (si se esta visualizando)
Para finalizar, introduciendo un número y pulsando enter en la caja de texto Ir a
el índice de simulación se posicionaría en la entrada especificada de la tabla pila/entrada.
Con el objeto de facilitar el proceso de simulación, se han definido métodos abreviados
de teclado. Los métodos abreviados se clásifican según el navegador
que se este utilizando:
Firefox
ALT-SHIFT-B: Botón prev.
ALT-SHIFT-N: Botón next.
ALT-SHIFT-,: Botón reset.
ALT-SHIFT-.: Botón end.
ALT-SHIFT-M: Árbol Sintáctico.
ALT-SHIFT-G: Ir entrada.
Internet Explorer
ALT-B: Botón prev.
ALT-N: Botón next.
ALT-,: Botón reset.
ALT-.: Botón end.
ALT-M: Árbol Sintáctico.
ALT-G: Ir entrada.
Conocimientos adquiridos
[ Inicio ]
Conforme un usuario va realizando compilaciones en las que ha especificado una solución, sus resultados se han ido registrando. Utilizando esta información, se calcula, por cada tipo de problema, cómo va evolucionando. Esta información se puede observar en el menú de la izquierda, en la opción de Resultados. Aquí, cada usuario podrá ver una tabla, en la que se muestran los tipos de problema, los aciertos y fallos recientes (de las últimas 5 compilaciones), los aciertos y fallos históricos, una evaluación (de rojo a verde, rojo indica una mala evaluación, verde, buena) y unos ejemplosd de recomendación, si fueren necesarios. Ejemplo:
Reciente
Histórico
Descripción
Evaluación
Recomendación
Información Básica
Anulables
Fallo en el cálculo de los anulables de un símbolo no terminal, cuando éste tiene una producción a la cadena vacía.
4
0
4
0
Vas muy bien
Fallo en el cálculo de los anulables de un símbolo no terminal, cuando éste se hace anulable por una producción en la que todos los símbolos de la derecha son no terminales y éstos son anulables.
0
4
0
4
No hay ejemplos
Iniciales
Fallo en el cálculo de los iniciales de un no terminal, cuando la producción que lo define comienza por un terminal.
8
4
8
4
No hay ejemplos
Fallo en el cálculo de los iniciales de un no terminal, cuando la producción que lo define comienza por un no terminal anulable y el inicial pertenece a ese no terminal.
12
0
12
0
Vas muy bien
Fallo en el cálculo de los iniciales de un no terminal, cuando la producción que lo define comienza por un no terminal anulable y el inicial no pertenece a ese no terminal.
0
4
0
4
No hay ejemplos
Seguidores
Fallo en el cálculo de los seguidores de un no terminal, cuando el no terminal aparece en la parte derecha de una producción con un símbolo terminal a su derecha.
4
0
4
0
Vas muy bien
Fallo en el cálculo de seguidores de un no terminal, cuando el no terminal aparece en la parte derecha de una producción y a su derecha no hay nada o todo lo que hay se puede anular.
4
4
4
4
No hay ejemplos
Fallo en el cálculo de seguidores de un no terminal. Se han indicado símbolos seguidores que no lo son.
0
8
0
8
No hay ejemplos
Análisis Descendente
Símbolos de Predicción
Fallo en el cálculo de los símbolos de predicción de una producción, cuando su parte derecha comienza por un terminal.
8
4
8
4
No hay ejemplos
Fallo en el cálculo de los símbolos de predicción de una producción, cuando su parte derecha comienza por un no terminal anulable y no indica sus iniciales.
4
8
4
8
No hay ejemplos
Fallo en el cálculo de los símbolos de predicción de una producción, cuando su parte derecha comienza por un no terminal anulable y el símbolo indicado no pertenece a este.
4
0
4
0
Vas muy bien
Fallo en el cálculo de los símbolos de predicción de una producción, cuando los símbolos de su parte derecha son todos anulables.
0
8
0
8
No hay ejemplos
Fallo en el cálculo de los símbolos de predicción de una producción. Se han indicado símbolos de predicción que no lo son.
0
12
0
12
No hay ejemplos
Tabla LL1
Fallo al rellenar la Tabla de análisis LL1, al manejar producciones que comienzan por terminal.
0
8
0
8
No hay ejemplos
Fallo al rellenar la Tabla de análisis LL1, al manejar producciones que comienzan por un no terminal anulable, olvidando sus iniciales.
8
4
8
4
No hay ejemplos
Fallo al rellenar la Tabla de análisis LL1, al manejar producciones que comienzan por un no terminal anulable y no pertenece a este.
4
4
4
4
No hay ejemplos
Fallo al rellenar la Tabla de análisis LL1, al manejar producciones en los que todos los símbolos de la parte derecha son anulables.
0
8
0
8
No hay ejemplos
Fallo al rellenar la Tabla de análisis LL1. Ha cometido errores al rellenar la tabla.
0
24
0
24
No hay ejemplos
Análisis Ascendente
Tabla SLR1
Fallo al construir la Tabla de análisis ascendente SLR1 (parte de acción), al calcular los estados del autómata que reconoce prefijos viables.
0
4
0
4
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente SLR1 (parte de acción), al determinar los símbolos que producen transición.
0
4
0
4
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente SLR1 (parte de acción), al tratar el símbolo delimitador de fin de cadena (dolar), no hay reducciones/desplazamientos asociados a dicho símbolo.
4
0
4
0
Vas muy bien
Fallo al construir la Tabla de análisis ascendente SLR1 (parte de acción), al calcular el primer estado del autómata que reconoce prefijos viables.
0
4
0
4
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente SLR1 (parte de ir a), al no determinar bien los símbolos que producen cambio de estado tras una reducción.
0
4
0
4
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han indicado acciones erróneas.
0
4
0
4
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente SLR1 (parte de ir a). Se han indicado cambios de estado erróneos.
0
4
0
4
No hay ejemplos
Warning al construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han omitido desplazamientos.
0
4
0
4
No hay ejemplos
Warning al construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han omitido reducciones.
0
4
0
4
No hay ejemplos
Warning al construir la Tabla de análisis ascendente SLR1 (parte de acción). Se han omitido error o el estado de aceptación de la cadena analizada.
0
4
0
4
No hay ejemplos
Warning al construir la Tabla de análisis ascendente SLR1 (parte de ir a). Se han omitido cambios de estado.
0
4
0
4
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente SLR1. Se comenten errores a la hora de construir la Tabla.
0
4
0
4
No hay ejemplos
Tabla LR1
Fallo al construir la Tabla de análisis ascendente LR1 (parte de acción), al calcular los estados del autómata que reconoce prefijos viables.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LR1 (parte de acción), al determinar los símbolos que producen transición.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LR1 (parte de acción), al tratar el símbolo delimitador de fin de cadena (dolar), no hay reducciones/desplazamientos asociados a dicho símbolo.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LR1 (parte de acción), al calcular el primer estado del autómata que reconoce prefijos viables.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LR1 (parte de ir a), al no determinar bien los símbolos que producen cambio de estado tras una reducción.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LR1 (parte de acción). Se han indicado acciones erróneas.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LR1 (parte de ir a). Se han indicado cambios de estado erróneos.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LR1 (parte de acción). Se han omitido desplazamientos.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LR1 (parte de acción). Se han omitido reducciones.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LR1 (parte de acción). Se han omitido error o el estado de aceptación de la cadena analizada.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LR1 (parte de ir a). Se han omitido cambios de estado.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LR1. Se comenten errores a la hora de construir la Tabla.
0
1
0
1
No hay ejemplos
Tabla LALR1
Fallo al construir la Tabla de análisis ascendente LALR1 (parte de acción), al calcular los estados del autómata que reconoce prefijos viables.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LALR1 (parte de acción), al determinar los símbolos que producen transición.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LALR1 (parte de acción), al tratar el símbolo delimitador de fin de cadena (dolar), no hay reducciones/desplazamientos asociados a dicho símbolo.
1
0
1
0
Vas muy bien
Fallo al construir la Tabla de análisis ascendente LALR1 (parte de acción), al calcular el primer estado del autómata que reconoce prefijos viables.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LALR1 (parte de ir a), al no determinar bien los símbolos que producen cambio de estado tras una reducción.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LALR1 (parte de acción). Se han indicado acciones erróneas.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LALR1 (parte de ir a). Se han indicado cambios de estado erróneos.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LALR1 (parte de acción). Se han omitido desplazamientos.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LALR1 (parte de acción). Se han omitido reducciones.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LALR1 (parte de acción). Se han omitido error o el estado de aceptación de la cadena analizada.
0
1
0
1
No hay ejemplos
Warning al construir la Tabla de análisis ascendente LALR1 (parte de ir a). Se han omitido cambios de estado.
0
1
0
1
No hay ejemplos
Fallo al construir la Tabla de análisis ascendente LALR1. Se comenten errores a la hora de construir la Tabla.