¿Cuál es la diferencia entre el análisis de LL y LR?

225

¿Alguien puede darme un ejemplo simple de análisis LL en comparación con análisis LR?

Creatividad2345
fuente

Respuestas:

484

En un nivel alto, la diferencia entre el análisis LL y el análisis LR es que los analizadores LL comienzan en el símbolo de inicio e intentan aplicar producciones para llegar a la cadena de destino, mientras que los analizadores LR comienzan en la cadena de destino e intentan volver al inicio símbolo.

Un análisis LL es una derivación de izquierda a derecha, más a la izquierda. Es decir, consideramos los símbolos de entrada de izquierda a derecha e intentamos construir una derivación más a la izquierda. Esto se hace comenzando en el símbolo de inicio y expandiendo repetidamente el extremo izquierdo no terminal hasta llegar a la cadena objetivo. Un análisis LR es una derivación de izquierda a derecha, lo que significa que escaneamos de izquierda a derecha e intentamos construir una derivación de la derecha. El analizador selecciona continuamente una subcadena de la entrada e intenta revertirla a un no terminal.

Durante un análisis LL, el analizador elige continuamente entre dos acciones:

  1. Predecir : según el token no terminal más a la izquierda y algunos tokens anticipados, elija qué producción se debe aplicar para acercarse a la cadena de entrada.
  2. Coincidencia : haga coincidir el símbolo de terminal adivinado más a la izquierda con el símbolo de entrada no consumido más a la izquierda.

Como ejemplo, dada esta gramática:

  • S → E
  • E → T + E
  • E → T
  • T → int

Luego, dada la cadena int + int + int, un analizador LL (2) (que usa dos tokens de búsqueda anticipada) analizaría la cadena de la siguiente manera:

Production       Input              Action
---------------------------------------------------------
S                int + int + int    Predict S -> E
E                int + int + int    Predict E -> T + E
T + E            int + int + int    Predict T -> int
int + E          int + int + int    Match int
+ E              + int + int        Match +
E                int + int          Predict E -> T + E
T + E            int + int          Predict T -> int
int + E          int + int          Match int
+ E              + int              Match +
E                int                Predict E -> T
T                int                Predict T -> int
int              int                Match int
                                    Accept

Observe que en cada paso miramos el símbolo más a la izquierda en nuestra producción. Si es un terminal, lo igualamos, y si no es un terminal, predecimos cuál será al elegir una de las reglas.

En un analizador LR, hay dos acciones:

  1. Shift : agregue el siguiente token de entrada a un búfer para su consideración.
  2. Reducir : reduzca una colección de terminales y no terminales en este búfer a algunos no terminales invirtiendo una producción.

Como ejemplo, un analizador LR (1) (con un token de búsqueda anticipada) podría analizar la misma cadena de la siguiente manera:

Workspace        Input              Action
---------------------------------------------------------
                 int + int + int    Shift
int              + int + int        Reduce T -> int
T                + int + int        Shift
T +              int + int          Shift
T + int          + int              Reduce T -> int
T + T            + int              Shift
T + T +          int                Shift
T + T + int                         Reduce T -> int
T + T + T                           Reduce E -> T
T + T + E                           Reduce E -> T + E
T + E                               Reduce E -> T + E
E                                   Reduce S -> E
S                                   Accept

Se sabe que los dos algoritmos de análisis que mencionó (LL y LR) tienen características diferentes. Los analizadores LL tienden a ser más fáciles de escribir a mano, pero son menos potentes que los analizadores LR y aceptan un conjunto de gramáticas mucho más pequeño que los analizadores LR. Los analizadores LR vienen en muchos sabores (LR (0), SLR (1), LALR (1), LR (1), IELR (1), GLR (0), etc.) y son mucho más potentes. También tienden a ser mucho más complejos y casi siempre son generados por herramientas como yacco bison. Los analizadores LL también vienen en muchos sabores (incluido LL (*), que es utilizado por la ANTLRherramienta), aunque en la práctica LL (1) es el más utilizado.

Como un complemento descarado, si desea obtener más información sobre el análisis de LL y LR, acabo de terminar de impartir un curso de compiladores y tengo algunos folletos y diapositivas sobre análisis en el sitio web del curso. Me agradaría dar más detalles sobre cualquiera de ellos si cree que sería útil.

templatetypedef
fuente
40
Sus diapositivas de la conferencia son fenomenales, fácilmente la explicación más divertida que he visto :) Este es el tipo de cosa que realmente despierta intereses.
kizzx2
1
¡También tengo que comentar las diapositivas! Pasando por todos ellos ahora. ¡Ayuda mucho! ¡Gracias!
kornfridge
Realmente disfrutando de las diapositivas también. ¿Supongo que no podría publicar la versión que no es Windows de los archivos del proyecto (y el archivo scanner.l, para pp2)? :)
Erik P.
1
Lo único que puedo contribuir a la excelente respuesta resumida de Matt es que cualquier gramática que pueda ser analizada por un analizador LL (k) (es decir, mirando hacia adelante "k" terminales para decidir sobre la próxima acción de análisis) puede analizarse por un LR ( 1) analizador. Esto le da a uno una pista del increíble poder del análisis LR sobre el análisis LL. Fuente: curso de compilación en UCSC impartido por el Dr. F. DeRemer, creador de analizadores LALR ().
JoGusto
1
Excelente recurso! Gracias por proporcionar diapositivas, folletos, proyectos también.
P. Hinker
58

Josh Haberman en su artículo LL y LR Parsing Demystified afirma que el análisis LL corresponde directamente con la notación polaca , mientras que LR corresponde a la notación polaca inversa . La diferencia entre PN y RPN es el orden de atravesar el árbol binario de la ecuación:

árbol binario de una ecuación

+ 1 * 2 3  // Polish (prefix) expression; pre-order traversal.
1 2 3 * +  // Reverse Polish (postfix) expression; post-order traversal.

Según Haberman, esto ilustra la principal diferencia entre los analizadores LL y LR:

La principal diferencia entre cómo operan los analizadores LL y LR es que un analizador LL genera un recorrido de preorden del árbol de análisis y un analizador LR genera un recorrido de orden posterior.

Para obtener una explicación detallada, ejemplos y conclusiones, consulte el artículo de Haberman .

msiemens
fuente
9

El LL usa de arriba hacia abajo, mientras que el LR usa el enfoque de abajo hacia arriba.

Si analiza un lenguaje de programación:

  • El LL ve un código fuente, que contiene funciones, que contiene expresión.
  • El LR ve la expresión, que pertenece a las funciones, que da como resultado la fuente completa.
betontalpfa
fuente
6

El análisis de LL es perjudicial, en comparación con LR. Aquí hay una gramática que es una pesadilla para un generador de analizador LL:

Goal           -> (FunctionDef | FunctionDecl)* <eof>                  

FunctionDef    -> TypeSpec FuncName '(' [Arg/','+] ')' '{' '}'       

FunctionDecl   -> TypeSpec FuncName '(' [Arg/','+] ')' ';'            

TypeSpec       -> int        
               -> char '*' '*'                
               -> long                 
               -> short                   

FuncName       -> IDENTIFIER                

Arg            -> TypeSpec ArgName         

ArgName        -> IDENTIFIER 

Un FunctionDef se ve exactamente como un FunctionDecl hasta que ';' o '{' se encuentra.

Un analizador LL no puede manejar dos reglas al mismo tiempo, por lo que debe elegir FunctionDef o FunctionDecl. Pero para saber cuál es el correcto tiene que buscar un ';' o '{'. En el momento del análisis gramatical, la búsqueda anticipada (k) parece ser infinita. En el momento del análisis es finito, pero podría ser grande.

Un analizador LR no tiene que mirar hacia adelante, ya que puede manejar dos reglas al mismo tiempo. Por lo tanto, los generadores de analizadores LALR (1) pueden manejar esta gramática con facilidad.

Dado el código de entrada:

int main (int na, char** arg); 

int main (int na, char** arg) 
{

}

Un analizador LR puede analizar el

int main (int na, char** arg)

sin importarle qué regla se reconoce hasta que encuentra un ';' o un '{'.

Un analizador LL se cuelga en el 'int' porque necesita saber qué regla se reconoce. Por lo tanto, debe buscar un ';' o '{'.

La otra pesadilla para los analizadores LL es la recursividad en una gramática. La recursividad izquierda es algo normal en las gramáticas, no hay problema para un generador de analizador LR, pero LL no puede manejarlo.

Así que tienes que escribir tus gramáticas de una manera no natural con LL.


fuente
0

Ejemplo de derivación más a la izquierda: una gramática G que no tiene contexto tiene las producciones

z → xXY (Regla: 1) X → Ybx (Regla: 2) Y → bY (Regla: 3) Y → c (Regla: 4)

Calcule la cadena w = 'xcbxbc' con la derivación más a la izquierda.

z ⇒ xXY (Regla: 1) ⇒ xYbxY (Regla: 2) ⇒ xcbxY (Regla: 4) ⇒ xcbxbY (Regla: 3) ⇒ xcbxbc (Regla: 4)


Derecha Más derivación Ejemplo: K → aKK (Regla: 1) A → b (Regla: 2)

Calcule la cadena w = 'aababbb' con la derivación más correcta.

K ⇒ aKK (Regla: 1) ⇒ aKb (Regla: 2) ⇒ aaKKb (Regla: 1) ⇒ aaKaKKb (Regla: 1) ⇒ aaKaKbb (Regla: 2) ⇒ aaKabbb (Regla: 2) ⇒ aababbb (Regla: 2)

Anupama Thathsarani
fuente