2010-04-20 47 views
89

¿Cuál es la diferencia real entre los analizadores LR, SLR y LALR? Sé que SLR y LALR son tipos de analizadores LR, pero ¿cuál es la diferencia real en lo que respecta a sus tablas de análisis?¿Cuál es la diferencia entre los analizadores LR, SLR y LALR?

¿Y cómo mostrar si una gramática es LR, SLR o LALR? Para una gramática LL solo tenemos que mostrar que ninguna celda de la tabla de análisis debe contener múltiples reglas de producción. ¿Alguna regla similar para LALR, SLR y LR?

Por ejemplo, ¿Cómo podemos demostrar que la gramática

S --> Aa | bAc | dc | bda 
A --> d 

es LALR (1), pero no SLR (1)?


EDITAR (ybungalobill): No he tenido una respuesta satisfactoria sobre lo que es la diferencia entre LALR y LR. Entonces, las tablas de LALR son de menor tamaño pero solo pueden reconocer un subconjunto de gramáticas de LR. ¿Puede alguien dar más detalles sobre la diferencia entre LALR y LR, por favor? LALR (1) y LR (1) serán suficientes para una respuesta. ¡Ambos usan 1 avance de token y ambos son manejados por una tabla! ¿Cómo son diferentes?

+0

Bueno, incluso estoy buscando una respuesta adecuada sobre esto, LALR (1) es solo una ligera modificación de LR (1), donde el tamaño de la tabla se reduce para que podamos minimizar el uso de la memoria ... – vikkyhacks

Respuesta

41

Los analizadores SLR, LALR y LR se pueden implementar utilizando exactamente la misma maquinaria basada en tablas.

Fundamentalmente, el algoritmo de análisis recoge la siguiente entrada testigo T, y consulta el estado actual S (y asociado de búsqueda hacia delante, GOTO, y las tablas de reducción) para decidir qué hacer:

  • SHIFT: Si la corriente la tabla dice que SHIFT en el token T, el par (S, T) se inserta en la pila de análisis, el estado cambia según lo que dice la tabla GOTO para el token actual (por ejemplo, GOTO (T)), otro token de entrada T 'es captado, y el proceso se repite
  • REDUCIR: cada estado tiene 0, 1 o muchas reducciones posibles que puedan ocurrir en el estado. Si el analizador es LR o LALR, el token se compara con los conjuntos de anticipación para todas las reducciones válidas para el estado. Si el token coincide con un conjunto de anticipación para una reducción para la regla de gramática G = R1 R2 .. Rn, se produce una reducción de pila y un desplazamiento: se llama a la acción semántica para G, la pila aparece n (a partir de Rn) veces, el par (S, G) se coloca en la pila, el nuevo estado S 'se establece en GOTO (G), y el ciclo se repite con el mismo token T. Si el analizador es un analizador SLR, hay como máximo una regla de reducción para el estado y así la acción de reducción se puede hacer a ciegas sin buscar para ver qué reducción se aplica. Es útil para un analizador SLR saber si hay es una reducción o no; esto es fácil de decir si cada estado registra explícitamente el número de reducciones asociadas con él, y ese recuento es necesario para las versiones L (AL) R en la práctica de todos modos.
  • ERROR: Si no es posible SHIFT ni REDUCE, se declara un error de sintaxis.

Entonces, si todos usan la misma maquinaria, ¿cuál es el punto?

El valor pretendido en SLR es su simplicidad en la implementación; no tiene que escanear las posibles reducciones comprobando los conjuntos de anticipación porque hay como máximo uno, y esta es la única acción viable si no hay salidas MAYO del estado. La reducción aplicable se puede unir específicamente al estado, por lo que la maquinaria de análisis SLR no tiene que buscarla. En la práctica, los analizadores L (AL) R manejan un conjunto útilmente más grande de lenguajes, y es tan poco el trabajo extra para implementar que nadie implementa SLR excepto como un ejercicio académico.

La diferencia entre LALR y LR tiene que ver con la tabla generador. Los generadores de analizadores de LR realizan un seguimiento de todas las posibles reducciones de estados específicos y su conjunto preciso de anticipación; terminas con estados en los que cada reducción está asociada con su conjunto exacto de anticipación desde su contexto izquierdo. Esto tiende a construir conjuntos bastante grandes de estados. Los generadores de analizadores LALR están dispuestos a combinar estados si las tablas GOTO y los conjuntos de cabeceras para las reducciones son compatibles y no entran en conflicto; esto produce un número considerablemente menor de estados, al precio de no ser capaz de distinguir ciertas secuencias de símbolos que LR puede distinguir. Entonces, los analizadores LR pueden analizar un conjunto de lenguajes más grande que los analizadores LALR, pero tienen tablas de analizador mucho más grandes. En la práctica, uno puede encontrar las gramáticas LALR que están lo suficientemente cerca de los lenguajes de destino que el tamaño de la máquina de estado vale la pena optimizar; los lugares donde el analizador LR sería mejor se manejan mediante una verificación ad hoc fuera del analizador sintáctico.

Entonces: Los tres usan la misma maquinaria. SLR es "fácil" en el sentido de que puedes ignorar un poco de la maquinaria pero no vale la pena. LR analiza un conjunto más amplio de lenguajes, pero las tablas de estado tienden a ser bastante grandes. Eso deja a LALR como la elección práctica.

Una vez dicho todo esto, vale la pena saber que GLR parsers puede analizar cualquier lenguaje libre de contexto, usando la maquinaria más complicada pero exactamente las mismas tablas (incluyendo la versión más pequeña utilizada por LALR). Esto significa que GLR es estrictamente más poderoso que LR, LALR y SLR; más o menos si puedes escribir una gramática BNF estándar, GLR analizará de acuerdo con ella. La diferencia en la maquinaria es que GLR está dispuesto a intentar múltiples análisis cuando hay conflictos entre la tabla GOTO y/o los conjuntos de búsqueda anticipada. (Cómo GLR hace esto de manera eficiente es puro genio [no mío], pero no encajará en esta publicación de SO).

Eso para mí es un hecho enormemente útil. Construyo analizadores de programa y los transformadores y analizadores de código son necesarios pero "poco interesantes"; el trabajo interesante es lo que haces con el resultado analizado y, por lo tanto, el foco está en hacer el trabajo posterior al análisis. Usar GLR significa que puedo construir gramáticas que funcionen con relativa facilidad, en comparación con piratear una gramática para obtener la forma utilizable de LALR. Esto es muy importante cuando se intenta tratar con lenguajes no académicos como C++ o Fortran, donde literalmente se necesitan miles de reglas para manejar bien todo el idioma, y ​​no se quiere pasar la vida tratando de hackear las reglas de la gramática. cumplir con las limitaciones de LALR (o incluso LR).

Como una especie de ejemplo famoso, C++ se considera extremadamente difícil de analizar ... por los chicos que realizan el análisis LALR. C++ es fácil de analizar utilizando maquinaria GLR utilizando prácticamente las reglas proporcionadas en la parte posterior del manual de referencia de C++. (Tengo precisamente ese analizador, y maneja no solo C++ vainilla, sino también una variedad de dialectos de proveedores también. Esto solo es posible en la práctica porque estamos usando un analizador GLR, en mi humilde opinión).

[EDITAR Noviembre de 2011: ampliamos nuestro analizador para manejar todo C++ 11. GLR hizo eso mucho más fácil de hacer.]

+0

AFAIK C++ no se puede analizar con LR porque necesita una mirada infinita hacia adelante. Así que no puedo pensar en ningún truco que permita analizarlo con LR. También los analizadores LRE parecen prometedores. – ybungalobill

+4

GCC solía analizar C++ utilizando Bison == LALR. Siempre puedes aumentar tu analizador sintáctico con una sustancia extra para manejar los casos (lookahead, is-this-a-typename) que te dan angustia. La pregunta es "¿Qué tan doloroso es un truco?" Para GCC fue bastante doloroso, pero lo hicieron funcionar. Eso no significa que esto se recomiende, que es mi punto sobre el uso de GLR. –

+0

No entiendo cómo usar GLR te ayuda con C++.Si no sabes si algo es un nombre de tipo o no, entonces simplemente no sabes cómo analizar 'x * y;' - ¿cómo ayuda el uso de GLR con eso? – Mehrdad

14

Los analizadores LALR combinan estados similares dentro de una gramática LR para producir tablas de estado del analizador que son exactamente del mismo tamaño que la gramática SLR equivalente, que generalmente son un orden de magnitud menor que las pure LR tablas de análisis. Sin embargo, para las gramáticas LR que son demasiado complejas para ser LALR, estos estados fusionados resultan en conflictos de analizador, o producen un analizador sintáctico que no reconoce completamente la gramática LR original.

Por cierto, menciono algunas cosas sobre esto en mi algoritmo de tabla de análisis MLR (k) here.

Adición

La respuesta corta es que las tablas de análisis sintáctico LALR son más pequeñas, pero la maquinaria analizador es la misma. Una gramática LALR determinada producirá tablas de análisis mucho más grandes si se generan todos los estados LR, con muchos estados redundantes (casi idénticos).

Las tablas LALR son más pequeñas porque los estados similares (redundantes) se combinan entre sí, eliminando efectivamente la información de contexto/búsqueda anticipada que codifican los estados por separado. La ventaja es que obtienes tablas de análisis mucho más pequeñas para la misma gramática.

El inconveniente es que no todas las gramáticas LR se pueden codificar como tablas LALR porque las gramáticas más complejas tienen caras más complicadas, lo que da como resultado dos o más estados en lugar de un único estado fusionado.

La principal diferencia es que el algoritmo para producir tablas LR contiene más información entre las transiciones de estado a estado, mientras que el algoritmo LALR no lo hace. Entonces, el algoritmo LALR no puede decir si un estado fusionado dado debería realmente dejarse como dos o más estados separados.

+3

+1 Me gusta la idea de Honalee. Mi generador de analizador G/L (AL) R tenía las semillas de algo así: produce el mínimo LALR máquina, y luego iba a dividir los estados donde había conflictos, pero nunca llegué a cabo. Esto parece una buena manera de producir un tamaño mínimo "LR" como conjunto de tablas de análisis. Mientras que no ayudará a GLR en términos de lo que puede analizar, puede reducir el número de análisis paralelos que GLR tiene que llevar y que serían útiles. –

3

La diferencia básica entre las tablas de analizador generadas con SLR frente a LR es que las acciones de reducción se basan en las siguientes configuraciones de tablas SLR. Esto puede ser demasiado restrictivo y, en última instancia, causar un conflicto de cambio-reducción.

Un analizador LR, por otro lado, reduce las decisiones solo en el conjunto de terminales que pueden seguir al no terminal siendo reducidos. Este conjunto de terminales a menudo es un subconjunto apropiado del conjunto Follows de un terminal no terminal y, por lo tanto, tiene menos posibilidades de entrar en conflicto con las acciones del cambio.

Los analizadores LR son más potentes por este motivo. Las tablas de análisis de LR pueden ser extremadamente grandes, sin embargo.

Un analizador LALR comienza con la idea de construir una tabla de análisis LR, pero combina los estados generados de una manera que da como resultado un tamaño de tabla significativamente menor. La desventaja es que una pequeña posibilidad de conflictos sería introducida para algunas gramáticas que una tabla LR hubiera evitado de otra manera.

Los analizadores LALR son ligeramente menos potentes que los analizadores LR, pero aún más potentes que los analizadores SLR. YACC y otros generadores de analizadores sintácticos similares tienden a usar LALR por este motivo.

P.S. Para mayor brevedad, SLR, LALR y LR más arriba significan realmente SLR (1), LALR (1) y LR (1), por lo que se anticipa una anticipación de token.

10

Otra respuesta más (YAA).

Los algoritmos de análisis sintáctico para SLR (1), LALR (1) y LR (1) son idénticas como Ira Baxter dicho,
sin embargo, las tablas del analizador puede ser diferente debido a la algoritmo analizador generación.

Un generador de analizadores SLR crea una máquina de estados LR (0) y calcula los aspectos básicos de la gramática (PRIMER y SIGUIENTE conjuntos). Este es un enfoque simplificado y puede informar conflictos que realmente no existen en la máquina de estado LR (0).

Un generador de analizador LALR crea una máquina de estado LR (0) y calcula las señales de búsqueda de la máquina de estado LR (0) (a través de las transiciones de terminal). Este es un enfoque correcto, pero ocasionalmente informa conflictos que no existirían en una máquina de estados LR (1).

Un generador de analizador LR de Canonical calcula una máquina de estado LR (1) y las señales de búsqueda ya forman parte de la máquina de estados LR (1). Estas tablas de analizador pueden ser muy grandes.

Un generador de analizador LR mínimo calcula una máquina de estado LR (1), pero combina estados compatibles durante el proceso y luego calcula los look-aheads de la máquina de estado LR (1) mínima. Estas tablas del analizador son del mismo tamaño o un poco más grandes que las tablas del analizador LALR, por lo que ofrecen la mejor solución.

LRSTAR 8.0 pueden generar analizadores LALR (1), LR mínimo (1) y LR (k) en C++.

3

Los analizadores SLR reconocen un subconjunto adecuado de gramáticas reconocibles por los analizadores LALR (1), que a su vez reconocen un subconjunto apropiado de gramáticas reconocibles por los analizadores LR (1).

Cada uno de estos se construye como una máquina de estado, donde cada estado representa un conjunto de reglas de producción de la gramática (y posición en cada una) mientras analiza la entrada.

El Dragon Book ejemplo de un LALR (1) gramática que no es SLR es la siguiente:

S → L = R | R 
L → * R | id 
R → L 

Aquí es uno de los estados de este gramática:

S → L•= R 
R → L• 

El indica la posición del analizador en cada una de las posibles producciones. No sabe en cuál de las producciones está realmente hasta que llega al final y trata de reducir.

Aquí, el analizador podría cambiar = o reducir R → L.

una SLR (también conocido como LR (0)) analizador determinará si se podría reducir mediante la comprobación de si el siguiente símbolo de entrada está en el follow establece de R (es decir, el conjunto de todos los terminales de la gramática que puede seguir R). Como = también está en este conjunto, el analizador SLR encuentra un conflicto de desplazamiento-reducción.

Sin embargo, una (1) analizador LALR usaría el conjunto de todos los terminales que pueden producirse por este particular la producción de R, que sólo es $ (es decir, final de la entrada). Por lo tanto, no hay conflicto.

Como comentaron los comentaristas anteriores, los analizadores LALR (1) tienen la misma cantidad de estados que los analizadores SLR. Un algoritmo de propagación anticipada se utiliza para unir lookaheads a producciones de estado SLR a partir de los correspondientes estados LR (1). El analizador LALR (1) resultante puede introducir conflictos de reducción-reducción no presentes en el analizador LR (1), pero no puede introducir conflictos de desplazamiento-reducción.

En su ejemplo, la siguiente LALR (1) Estado provoca un desplazamiento y reducción de conflictos en una implementación SLR:

S → b d•a/$ 
A → d•/c 

El símbolo después / es el conjunto de seguimiento para cada producción en el LALR (1) analizador. En SLR, siga (A) incluye a, que también podría ser desplazado.

-1

Una respuesta simple es que todas las gramáticas LR (0) son gramáticas LALR (1). En comparación con LR (1), LALR (1) tiene más estados en el DFA asociado (casi estados dobles). Y esa es la razón principal para que las gramáticas LALR (1) tomen más tiempo para mostrar errores que LR (1) gramáticas. Y una cosa más importante de saber con respecto a estas dos gramáticas es que en LR (1) las gramáticas no podemos encontrar cambios/reducir o reducir/reducir conflictos. Pero en LALR (1) existe la posibilidad de reducir/reducir el conflicto.

+0

Esto no es exacto –

4

Supongamos que un analizador sintáctico sin una inspección anticipada está felizmente analizando cadenas para su gramática.

Usando su ejemplo dado se encuentra con una cadena dc, ¿qué hace? ¿Lo reduce a S, porque dc es una cadena válida producida por esta gramática? O tal vez estábamos tratando de analizar bdc porque incluso eso es una cadena aceptable?

Como seres humanos sabemos que la respuesta es simple, solo tenemos que recordar si acabamos de analizar b o no. Pero las computadoras son estúpidas :)

Dado que un analizador SLR (1) tenía la potencia adicional sobre LR (0) para realizar una búsqueda anticipada, sabemos que cualquier cantidad de anticipación no nos puede decir qué hacer en este caso; en cambio, tenemos que mirar hacia atrás en nuestro pasado. Así viene el analizador canónico de LR al rescate. Recuerda el contexto pasado.

La forma en que recuerda este contexto es que se autodisciplina, que siempre que encuentre un b, comenzará a caminar en un camino hacia la lectura bdc, como una posibilidad. Entonces, cuando ve un d, sabe si ya está caminando por un camino. Por lo tanto, un analizador CLR (1) puede hacer cosas que un analizador SLR (1) no puede.

Pero ahora, dado que tuvimos que definir tantas rutas, ¡los estados de la máquina son muy grandes!

Así que fusionamos caminos que parecen iguales, pero como se esperaba, podría dar lugar a problemas de confusión. Sin embargo, estamos dispuestos a correr el riesgo a costa de reducir el tamaño.

Este es su analizador LALR (1).


Ahora cómo hacerlo algorítmicamente.

Al dibujar los conjuntos de configuración para el lenguaje anterior, verá un conflicto shift-reduce en dos estados. Para eliminarlos, es posible que desee considerar una SLR (1), que toma decisiones mirando a un follow, pero observará que todavía no podrá hacerlo. De este modo, dibujaría los conjuntos de configuración nuevamente, pero esta vez con la restricción de que cada vez que calcule el cierre, las producciones adicionales que se agreguen deben tener seguimiento (s) estricto (s). Consulte cualquier libro de texto sobre lo que debe seguir.

+0

Esto no es exacto –

Cuestiones relacionadas