Lenguajes Libres de Contexto

Slides:



Advertisements
Presentaciones similares
Ejemplo Práctico de un Compilador Pequeño
Advertisements

Análisis Sintáctico Descendente
INSTITUTO TECNOLÓGICO DE MINATITLÁN
Compiladores e intérpretes Análisis Sintáctico II
Compiladores e intérpretes Generación de código intermedio II
Compiladores e intérpretes
Compiladores e intérpretes Análisis Sintáctico III
Analizador Sintáctico
Gramáticas Libres de Contexto
Autómatas de pila (Pushdown automata)
Gramáticas.
Análisis sintáctico (Look ahead LR)
Organización de Lenguajes y Compiladores 1
ANALIZADOR SINTACTICO
Analizadores Sintácticos Descendentes Predictivos
Unidad 3. Análisis Sintáctico
1 Parsing Un parser podría ser definido como un programa que analiza una porción de texto para determinar su estructura lógica: la fase de parsing en un.
Combinadores SK.
Teoría de Autómatas y Lenguajes Formales Alma María Pisabarro, 2007
Traducción dirigida por la Sintaxis
ANALISIS SINTACTICO DESCENDENTE
Tema 4. Gramáticas y Análisis Sintáctico PARSER
Solución de problemas por búsqueda inteligente
Tema 3. Optimización de Código
Lenguajes Regulares Curso de Compiladores Manuel E. Bermúdez, Ph.D.
ANALISIS SINTACTICO El análisis gramatical es la tarea de determinar la sintaxis, o estructura, de un programa. Por esta razón también se le conoce como.
Teoría de lenguajes y compiladores
APLICACIONES DE PILAS Estructuras de Datos.
Analizador Sintáctico Descendente
Arboles Binarios de expresiones
Tema 2 Lenguajes Formales.
Introducción a la Teoría de Lenguajes Preparado por Manuel E. Bermúdez, Ph.D. Profesor Asociado University of Florida Curso de Compiladores.
Programación de sistemas
TRADUCTOR DE UN PROGRAMA
Análisis sintáctico LR: SLR (LR simple)
Instituto Tecnologico de Durango Programacion de Sistemas II
Procesadores del Lenguaje
Ordenamiento, Heapsort y Colas de prioridad
Lenguajes Independientes del Contexto
Autómatas de Pila Teoría del Autómata.
ANALISIS SINTACTICO Parte I
Estructura de Datos y Algoritmos
Resumen de Compilación Preparado por Manuel E. Bermúdez, Ph.D. Associate Professor University of Florida Traducido por Christian Torres Universidad Ricardo.
Clasificación de Gramáticas y Manejo de Errores
Resolución de Problemas y Algoritmos Uso de iteración con secuencias
ANALISIS SINTACTICO ASCENDENTE
Material de apoyo Unidad 4 Estructura de datos
Teoría de lenguajes y compiladores
Introducción al Análisis Sintáctico
Una introducción a los algoritmos del Parsing. Pregunta inicial… ¿Cómo se puede determinar si un código escrito en un lenguaje de programación tiene sintaxis.
Teoría de lenguajes y compiladores Analizadores lexicográficos
Compiladores e intérpretes Análisis Sintáctico III
Parte II. Algorítmica. 5. Backtracking. 1. Análisis de algoritmos.
Programación de Sistemas
Compiladores e intérpretes
Precedencia y asociatividad de operadores
1 Compilación, pereza y expresiones let(rec) 2 Compilando un programa Describiremos un compilador para la máquina minimal usando un conjunto de esquemas.
Programación de Sistemas FEI – 2008
UNIVERSIDAD LATINA (UNILA)
1 Sebastián Argüello A60490 semana 3 Autómatas y compiladores CI-1322.
Teoría de lenguajes y compiladores
Teoría de lenguajes y compiladores
Reglas Básicas del Álgebra de Boole
Teoría de lenguajes y compiladores
IV. GRAMÁTICAS DISTRIBUIDAS Y TABLAS DE SÍMBOLOS
Programación de Sistemas
Autómatas y Compiladores Semana 13. Ricardo Vargas Del Valle A35469.
REPÚBLICA BOLIVARIANA DE VENEZUELA MINISTERIO DEL PODER POPULAR PARA LA EDUCACION SUPERIOR UNIVERSIDAD VALLE DEL MOMBOY CARVAJAL EDO. TRUJILLO ENERO 2014.
República Bolivariana de Venezuela Ministerio del Poder Popular para la Educación Superior Universidad Valle del Momboy Carvajal, Trujillo Alumnas Luzmila.
Estructura de Datos PILA Universidad Hispanoamericana Prof. Ing. Erick López.
Transcripción de la presentación:

Lenguajes Libres de Contexto Curso de Compiladores Preparado por Manuel E. Bermúdez, Ph.D. Profesor Asociado University of Florida

Gramáticas Libres de Contexto Definición: Una gramática libre de contexto (GLC) es una tupla G = (, , P, S), donde todas las producciones son de la forma A  , donde A   y   (u )*. Derivación Izquierda: En cada paso, el símbolo no-terminal más a la izquierda es el que se re-escribe. Derivación Derecha: En cada paso, el símbolo no-terminal más a la derecha es el que se re-escribe.

Árboles de Derivación Un árbol de derivación describe las re-escrituras, en forma independiente del orden (izquierdo o derecho). Cada rama del árbol corresponde a una producción en la gramática.

Árboles de Derivación Notas: Hojas en el árbol son símbolos terminales. El “contorno” inferior es la sentencia. Recursividad izquierda causa ramificación a la izquierda. Recursividad derecha causa ramificación a la derecha.

Metas del Análisis Sintáctico Examinar la hilera de entrada , y determinar si es legal o no en el lenguaje, i.e. si S =>* . Esto es equivalente a (intentar) construir el árbol de derivación. Beneficio adicional: si el inento es exitoso, el árbol refleja la estructura sintáctica de la hilera de entrada. Por lo tanto, el árbol debiera ser único (para una hilera dada).

Ambigüedad en Gramáticas Definición: Una GLC es ambigua si existen dos derivaciones derechas (o izquierdas, pero no ambas) para alguna sentencia z. Definición (equivalente) : Una GLC es ambigua si existen dos árboles de derivación diferentes, para alguna sentencia z.

Ambigüedad en Gramáticas Dos ambigüedades clásicas (al menos en lenguajes de programación): Recursividad simultánea izquierda/derecha: E → E + E Problema del “else colgante”: S → if E then S → if E then S else S

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas ¿ Qué lenguaje genera esta gramática ? S → a D → EDBC A → BCDEF E → CBA B → ASDFA F → S C → DDCF Respuesta: L(G) = {a} Problema: Algunos no-terminales (y producciones) son “inútiles”: no se pueden usar en la generación de ninguna sentencia.

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Definición: Una GLC es reducida sii para todo A  Ф, a) S =>* αAβ, para algunos α, β  V*, (decimos que A es generable), y b) A =>* z, para algún z  Σ* (decimos que A es terminable) G es reducida sii todo símbolo no-terminal A es generable y también terminable.

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Ejemplo: S → BB A → aA B → bB → a B no es terminable, porque B =>* z, para ningún z  Σ*. A no es generable, porque S =>* αAβ, para ningunos α,βV*.

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Para encontrar cuáles no-terminales son generables: Construir el grafo (Ф, δ), donde (A, B)  δ sii A → αBβ es una producción. Verificar que todos los nodos son alcanzables desde S.

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Ejemplo: S → BB A → aA B → bB → a A no es generable, porque no es alcanzable desde S. S B A

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Algoritmo 1: Calcular no-terminales generables Generable := {S} while(Generable cambia) do para cada A → Bβ do if A  Generable then Generable := Generable U {B} od { Ahora, Generable contiene los no-terminales que son generables }

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Para encontrar cuáles no-terminales son terminables: Construir el grafo (2Ф, δ), donde (N, N U {A})  δ sii A → X1 … Xn es una producción, y para todo i, Xi  Σ o bien Xi  N. Verificar que el nodo Ф (todos los no-terminales) es alcanzable desde el nodo ø (vacío).

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Ejemplo: S → BB A → aA B → bB → a {A, S, B} no es alcanzable desde ø ! Solo {A} es alcanzable desde ø. Conclusión: S y B no son terminables. {A,B} {B} {B,S} {S} ø {A} {A,S} {A,S,B}

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Algoritmo 2: Calcular no-terminales terminables: Terminable := { }; while (Terminable cambia) do para cada A → X1…Xn do if todo no-terminal entre los X’s está en Terminable then Terminable := Terminable U {A} od { Ahora, Terminable contiene los no-terminales que son terminables. }

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Algorithmo 3: Reducción de una gramática: Encontrar todos los no-terminales generables. Encontrar todos los no-terminales terminables. Eliminar cualquier producción A → X1 … Xn si a) A is not generable o si b) algún Xi no es terminable. Si la nueva gramática no es reducida, repetir el proceso.

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Ejemplo: E → E + T F → not F → T Q → P / Q T → F * T P → (E) → P → i Generable: {E, T, F, P}, no Generable: {Q} Terminable: {P, T, E}, no Terminable: {F,Q} Entonces, se elimina toda producción para Q, y toda producción cuya parte derecha contiene F ó Q.

Reducción de Gramáticas 11/04/2017 Reducción de Gramáticas Nueva Gramática: E → E + T → T T → P P → (E) → i Generable: {E , T, P} Ahora, la gramática Terminable: {P, T, E} está reducida.

Reducción de Gramáticas Ejemplo: Resultado: S → AB S → a → a B → b A → aA B → b Generable:{S} Terminable:{S,B} Generable:{S,A,B} no Terminable:{A} Se elimina B → b Se elimina toda producción Resultado final: que contiene A. S → a

Precedencia y Asociatividad de Operadores 25/07/08 Precedencia y Asociatividad de Operadores Construyamos una GLC (gramática libre de contexto) para expresiones, que consista de: El identificador i. + , - (operadores binarios) con baja precedencia y asociativos por la izquierda. * , / (operadores binarios) con precedencia media, y asociativos por la derecha. + y - (operadores unarios) con la más alta precedencia, y asociativos por la derecha.

Gramática para Expresiones 25/07/08 Gramática para Expresiones E → E + T → E - T → T T → F * T → F / T → F F → - F → + F → P P → ( E ) → i E consiste de T's, separados por –’s y +'s, asociativos a la izquierda, con precedencia baja. T consiste de F's, separados por *'s y /'s, asociativos a la derecha, con precedencia media. F consiste de un solo P, precedido por +'s y -'s, con precedencia alta. P consiste de una E entre paréntesis, o una i .

Precedencia y Asociatividad de Operadores 25/07/08 Precedencia y Asociatividad de Operadores Precedencia: Cuanto más abajo en la gramática, más alta la precedencia. Asociatividad: Recursividad izquierda en la gramática, causa asociatividad izquierda del operador, y causa ramificación izquierda en el árbol. Recursividad derecha en la gramática cause asociatividad derecha del operador, y causa ramificación derecha en el árbol.

Árboles de Derivación Hilera de Entrada: - + i - i * ( i + i ) / i + i Construcción (humana) del árbol de derivación: Método Ascendente. En cada pasada se procesan los operadores de mayor precedencia. Los operadores de baja precedencia son los últimos, en la parte superior del árbol.

Precedencia y Asociatividad de Operadores 11/04/2017 Precedencia y Asociatividad de Operadores Ejercicio: Escribir una gramática para expresiones: El identificador i. ‘&’, ‘¢’, ‘*’ (operadores binarios) con baja precedencia y asociativos por la izquierda. ‘%’, ‘#’ (operadores binarios) con precedencia media, y asociativos por la derecha. ‘@’, ‘!’ (operadores binarios) con la más alta precedencia, y asociativos por la izquierda. Paréntesis sobrellevan la precedencia y la associatividad.

Precedencia y Asociatividad de Operadores 11/04/2017 Precedencia y Asociatividad de Operadores Gramática: E0 → E0 & E1 → E0 ¢ E1 → E0 * E1 → E1 E1 → E2 % E1 → E2 # E1 → E2 E2 → E2 @ E3 → E2 ! E3 → E3 E3 → (E0) → i

Precedencia y Asociatividad de Operadores 11/04/2017 Precedencia y Asociatividad de Operadores Ejemplo: Construir el árbol de derivación para: i & i @ i # i ¢ ( i * i & i ! i) % ( i & i ) # i @ i

Árbol de Derivación

Gramáticas de Traducción 11/04/2017 Gramáticas de Traducción Definición: Una gramática de traducción (o esquema de traducción dirigido por sintaxis) es como una GLC, pero con la siguiente generalización: Cada producción es una tupla (A, β, ω)  Ф x V* x V*, llamada una regla de traducción, denotada A → β => ω, donde A es la parte izquierda, β es la parte derecha, y ω es la parte de traducción.

Gramáticas de Traducción 11/04/2017 Gramáticas de Traducción Ejemplo: Traducción de infijo a postfijo para expresiones. E → E + T => E T + → T => T T → P * T => P T * → P => P P → (E) => E Nota: ()’s se eliminan → i => i La parte de traducción describe cómo se genera la salida, conforme se deriva la entrada.

Gramáticas de Traducción 11/04/2017 Gramáticas de Traducción Se deriva un par (, β), donde  y β son las formas sentenciales de la entrada y salida. ( E, E ) => ( E + T, E T + ) => ( T + T, T T + ) => ( P + T, P T + ) => ( i + T, i T + ) => ( i + P * T, i P T * + ) => ( i + i * T, i i T * + ) => ( i + i * i, i i i * + )

Traducción de Hileras a Árboles 11/04/2017 Traducción de Hileras a Árboles t1 … tn N Notación: < N t1 … tn > denota Gramática de traducción de hileras a árboles: E → E + T => < + E T > → T => T T → P * T => < * P T > → P => P P → (E) => E → i => i

Traducción de Hileras a Árboles 11/04/2017 Traducción de Hileras a Árboles Ejemplo: (E, E) => (E + T, < + E T >) => (T + T, < + T T >) => (P + T, < + P T >) => (i + T, < + i T >) => (i + P * T, < + i < * P T > >) => (i + i * T, < + i < * i T > >) => (i + i * P, < + i < * i P > >) => (i + i * i, < + i < * i i > >) i + *

Gramáticas de Traducción 11/04/2017 Gramáticas de Traducción Definición: Una gramática de traducción es simple si para cada regla A →  => β, la secuencia de no-terminales en  es idéntica a la secuencia que aparece en β. Ejemplo: E → E + T => < + E T > → T => T T → P * T => < * P T > → P => P P → (E) => E → i => i

Traducción de Hileras a Árboles 11/04/2017 Traducción de Hileras a Árboles Si la gramática es simple, eliminamos los no-terminales y la notación de árboles en las partes de traducción: E → E + T => + → T T → P * T => * → P P → (E) → i => i Suena familiar ? Notación del TWS

Árboles de Sintaxis Abstracta ASA es una versión condensada del árbol de derivación. Sin “ruido” (nodos intermedios). Es el resultado de usar una gramática de traducción de hilera-a-árbol. Reglas de la forma A → ω => 's'. Se construye un nodo 's', con un hijo por cada símbolo no-terminal en ω. Traducimos del vocabulario de entrada (símbolos en ω), al vocabulario de nombres de nodos del árbol (e.g. ‘s’)

Ejemplo de ASA G: Entrada:: - + i - i * ( i + i ) / i + i Árbol de Derivación ASA:

El Juego de Dominó Sintáctico La gramática: E → E+T T → P*T P → (E) → T → P → i Las piezas de juego: Una cantidad ilimitada de cada pieza. Una pieza por cada regla en la gramática. El tablero de juego: El dominó inicial arriba. Los dominós abajo son la hilera de entrada.

El Juego de Dominó Sintáctico Reglas del juego: Se agregan piezas al tablero. Deben coincidir las partes planas, y los símbolos. Las líneas son infinitamente elásticas, pero no se pueden cruzar. Objetivo del juego: Conectar el dominó de inicio con los dominós de entrada. Que no sobren partes.

Estrategias de Análisis Sintáctico Las mismas que para el juego de dominó sintáctico. Descendente (“top-down”): se comienza con el dominó inicial, se trabaja hacia la hilera de entrada. Ascendente (“bottom-up”): se comienza con la hilera de entrada, se trabaja hacia el dominó inicial. En ambas estrategias, se puede procesar la entrada de izquierda-a-derecha  , o de derecha-a-izquierda  .

Análisis Sintáctico Descendente Se intenta una derivación izquierda, prediciendo la regla que hará coincidir lo que queda de la hilera de entrada. Se usa una hilera (una pila, en realidad) de la cual se pretende derivar la hilera de entrada.

Análisis Sintáctico Descendente Se comienza con S en la pila. A cada paso, dos alternativas:  (la pila) comienza con un símbolo terminal t. Debe coincidir con el siguiente símbolo de entrada.  comienza con un símbolo no-terminal A. Se consulta con un oráculo FOP (Función Omnisciente de Parsing) para determinar cuál producción de A llevaría a coincidir con el siguiente símbolo de entrada. La FOP es la parte “predictiva” del analizador.

Algoritmo Clásico de Análisis Sintáctico Descendente Push (Stack, S); while not Empty (Stack) do if Top(Stack)  then if Top(Stack) = Head(input) then input := tail(input) Pop(Stack) else error (Stack, input) else P:= OPF (Stack, input) Push (Pop(Stack), RHS(P)) od if (not empty(input)) then error

Análisis Sintáctico Descendente La mayoría de los métodos imponen cotas al número de símbolos de la pila y de la hilera de entrada, que se usan para escoger la producción. Para los lenguajes de programación, la escogencia común es (1,1). Debemos definir FOP (A,t), donde A es el primer símbolo en la pila, y t es el primer símbolo de la entrada. Requerimientos de almacenamiento: O(n2), donde n es el tamaño del vocabulario de la gramática, ≈ O(1002).

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente A … ω t … FOP (A, t) = A → ω si ω =>* t, para algún , ω =>* ε, y S =>* At, para algunos , , donde  =>* ε. ó

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Ejemplo S → A B → b (ilustrando 1): A → BAd C → c → C FOP b c d B B → b B → b B → b C C → c C → c C → c S S → A S → A S → A A A → BAd A → C ??? OPF (A, b) = A → BAd porque BAd =>* bAd OPF (A, c) = A → C porque C =>* c i.e., B comienza con b, y C comienza con c. Elementos de color café son opcionales. También el elemento ???

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Ejemplo (ilustrando 2): S → A A → bAd → OPF b d  S S → A S → A A A → bAd A → A → OPF (S, b) = S → A , porque A =>* bAd OPF (S, d) = -------- , porque S =>* αSdβ OPF (S,  ) = S → A , porque S es legal OPF (A, b) = A → bAd , porque A =>* bAd OPF (A, d) = A → , porque S =>* bAd OPF (A,  ) = A → , porque S =>*A

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Definición: First (A) = {t / A =>* t, para algún } Follow (A) = {t / S =>* Atβ, para algún , β} Cálculo de Conjuntos First: Construir grafo (Ф, δ), donde (A,B)  δ si B → A,  =>* ε (i.e. First(A)  First(B)) Agregar a cada nodo un conjunto vacío de terminales. Agregar t a First(A) si A → t,  =>* ε. Propagar los elementos de los conjuntos a lo largo de las aristas del grafo.

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Ejemplo: S → ABCD A → CDA C → A B → BC → a D → AC → b → Anulables = {A, C, D} {a, b} {b} S B A C D Paso 1: Grafo {a} Paso 2: Conjuntos { } {a} Paso 3: Agregar t {a} Paso 4: Propagar

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Cálculo de Conjuntos Follow: Construir grafo (Ф, δ), donde (A,B)  δ si A → B,  =>* ε. Follow(A)  Follow(B), porque cualquier símbolo X que sigue después de A, también sigue después de B, porque A puede terminar en B. A X B  α ε

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Agregar a cada nodo un conjunto vacío de terminales. Agregar  a Follow(S). Agregar First(X) a Follow(A) si B → AX,  =>* ε. Nota: First(t)={t}. Propagar los elementos de los conjuntos a lo largo de las aristas del grafo.

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Ejemplo: S → ABCD A → CDA C → A B → BC → a D → AC → b → Nullable = {A, C, D} First(S) = {a, b} First(C) = {a} First(A) = {a} First(D) = {a} First(B) = {b} { } ┴ {a , } S B ┴ {a } , b, {a,b, } A C ┴ ┴ Blanco: Paso 3 Café: Paso 4 {a } , b, D ┴

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Resumiendo, Follow(S) = {} Follow(A) = Follow(C) = Follow(D) = {a, b, } Follow(B) = {a, }

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Regresando al análisis sintáctico … Deseamos que OPF(A, t) = A → ω si t  First(ω), i.e. ω =>* tβ ó ω =>* ε and t  Follow(A), i.e. S =>* A => *Atβ A α ω t β A α ω ε t β

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Definición: Select (A→ ω) = First(ω) U if ω =>* ε then Follow(A) else ø Así, PT(A, t) = A → ω si t  Select(A → ω) “Parse Table” (PT), en lugar de FOP, porque ya no es omnisciente.

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente Ejemplo: First (S) = {a, b} Follow (S) = { } First (A) = {a} Follow(A) = {a, b, } First (B) = {b} Follow(B) = {a, } First (C) = {a} Follow (C) = {a, b, } First (D) = {a} Follow(D) = {a, b, } Gramática Conjuntos Select S → ABCD {a, b} B → BC {b} → b {b} A → CDA {a, b, } → a {a} → {a, b, } C → A {a, b, } D → AC {a, b, } No disjuntos No disjuntos por parejas Gramática NO es LL(1)

Análisis Sintáctico Descendente 11/04/2017 Análisis Sintáctico Descendente S → ABCD {a, b} C → A {a, b, } B → BC {b} D → AC {a, b, } → b {b} A → CDA {a, b, } → a {a} → {a, b, } Gramática no-LL(1): elementos múltiples en PT. a b ┴ S S → ABCD S → ABCD A A → CDA, A→ a, A → A → CDA, A → A → CDA,A → B B → BC, B → b C C → A C → A C → A D D → AC D → AC D → AC

Gramáticas LL(1) Definición: Una GLC G es LL(1) ( Left-to-right, Left-most, (1)-symbol lookahead) sii pata todo A Ф, y para todo par de producciones A→, A → con   , Select (A → ) ∩ Select (A → ) =  Ejemplo previo: gramática no es LL(1). Qué hacer ? Más tarde.

Ejemplo de gramática LL(1) S → A {b,} A → bAd {b} → {d, } Disjuntos! Gramática es LL(1) ! d b  S S → A S → A A A → A → bAd A lo sumo una producción en cada posición de la tabla.

Ejemplo Construir la tabla de análisis sintáctico para la siguiente gramática. S → begin SL end {begin} → id := E; {id} SL → SL S {begin,id} → S {begin,id} E → E+T {(, id} → T {(, id} T → P*T {(, id} → P {(, id} P → (E) {(} → id {id} * * * * No es LL(1)

Ejemplo (cont’d) Lemma: Rescursividad izquierda siempre produce una gramática no-LL(1) (e.g., SL, E) Prueba: Considere A → A First () or Follow (A) →  First () Follow (A)

Problemas con nuestra Gramática SL tiene recursividad izquierda. E tiene recursividad izquierda. T → P * T comienzan con una → P secuencia en común (P).

Solución al Problema 3 Cambiar: T → P * T { (, id } → P { (, id } a: T → P X { (, id } X → * T { * } → { +, ; , ) } Follow(X) Follow(T) porque T → P X Follow(E) porque E → E+T , E → T = { +, ;, ) } porque E → E+T, S → id := E ; y P → (E) Disjuntos!

Solución al Problema 3 (cont’d) En general, cambiar A → 1 → 2 . . . → n a A →  X X → 1 → n Con suerte, todos los ’s comienzan con símbolos distintos

Solución a los Problemas 1 y 2 Queremos (…((( T + T) + T) + T)…) En su lugar, (T) (+T) (+T) … (+T) Cambiar: E → E + T { (, id } → T { (, id } a: E → T Y { (, id } Y → + T Y { + } → { ; , ) } Follow(Y)  Follow(E) = { ; , ) } Ya no contiene ‘+’, porque eliminamos la producción E → E + T

Solución a los Problemas 1 and 2 (cont’d) En general, Cambiar: A → A1 A →  1 . . . . . . → An →  m a A → 1 X X → 1 X → m X → n X →

Solución a los Problemas 1 and 2 (cont’d) En nuestro ejemplo, Cambiar: SL → SL S { begin, id } → S { begin, id } a: SL → S Z { begin, id } Z → S Z { begin, id } → { end }

Gramática Modificada Disjuntos. La gramática es LL(1) → id := E ; {id} S → begin SL end {begin} → id := E ; {id} SL → S Z {begin,id} Z → S Z {begin,id} → {end} E → T Y (,id} Y → + T Y {+} → {;,)} T → P X {(,id} X → * T {*} → {;,+,)} P → (E) {(} → id {id} Disjuntos. La gramática es LL(1)

Parsing de Descenso Recursivo Estrategia descendente, apropiada para geramáticas LL(1). Una rutina por cada no-terminal. Contenido de pila embebido en la secuencia de llamadas recursivas. Cada rutina escoge y recorre una producción, basado en el siguiente símbolo de entrada, y los conjuntos Select. Buena técnica para escribir un analizador sintáctico a mano.

Parsing de Descenso Recursivo proc S; {S → begin SL end → id := E; } case Next_Token of T_begin : Read(T_begin); SL; Read (T_end); T_id : Read(T_id); Read (T_:=); E; Read (T_;); otherwise Error; end end; “Read (T_X)” verifica que el siguiente token es X, y lo consume. “Next_Token” es el siguiente token.

Parsing de Descenso Recursivo proc SL; {SL → SZ} S; Z; end; proc E; {E → TY} T; Y; Técnicamente, debimos insistir que Next_Token fuera T_begin o T_id, pero S hará eso de todas maneras. Revisión temprana ayuda en la recuperación de errores. Lo mismo para T_( y T_id.

Parsing de Descenso Recursivo proc Z;{Z → SZ → } case Next Token of T_begin, T_id: S;Z; T_end: ; otherwise Error; end end;

Parsing de Descenso Recursivo Se puede usar un ‘case’ proc Y; {Y → +TY → } if Next Token = T_+ then Read (T_+) T; Y; end; proc T; {T → PX} P; X Se pudo haber insistido que Next_Token fuera T_( o T_id.

Parsing de Descenso Recursivo proc X;{X → *T → } if Next Token = T_* then Read (T_*); T; end;

Parsing de Descenso Recursivo proc P; {P →(E) → id } case Next Token of T_(: Read (T_(); E; Read (T_)); T_id: Read (T_id); otherwise Error; end end;

Traducción hilera-a-árbol Podemos obtener el árbol de derivación o el ASA. El árbol puede ser generado en forma ascendente o descendente. Mostraremos cómo obtener Árbol de derivación en forma descendente. ASA para la gramática original, en forma ascendente.

Generación Descendente del AD En cada rutina, y para cada alternativa, escribir la producción escogida EN CUANTO HAYA SIDO ESCOGIDA.

Generación Descendente del AD proc S; {S → begin SL end → id := E; } case Next_Token of T_begin : Write(S → begin SL end); Read(T_begin); SL; Read(T_end); T_id : Write(S → id :=E;); Read(T_id); Read (T_:=); E; Read (T_;); otherwise Error end end;

Generación Descendente del AD proc SL; {SL → SZ} Write(SL → SZ); S; Z; end; proc E; {E → TY} Write(E → TY); T; Y;

Generación Descendente del AD proc Z; {Z → SZ → } case Next_Token of T_begin, T_id: Write(Z → SZ); S; Z; T_end: Write(Z → ); otherwise Error; end end;

Generación Descendente del AD proc Y; {Y → +TY → } if Next_Token = T_+ then Write (Y → +TY); Read (T_+); T; Y; else Write (Y → ); end; proc T; {T → PX} Write (T → PX); P; X

Generación Descendente del AD proc X;{X → *T → } if Next_Token = T_* then Write (X → *T); Read (T_*); T; else Write (X → ); end;

Generación Descendente del AD proc P;{P → (E) → id } case Next_Token of T_(: Write (P → (E)); Read (T_(); E; Read (T_)); T_id: Write (P → id); Read (T_id); otherwise Error; end;

Notas La colocación de las instrucciones Write es obvia precisamente porque la gramática es LL(1). El árbol puede ser construido conforme procede el algoritmo, o puede ser construido por un post-procesador.

S → begin SL end SL → SZ S → id :=E; E → TY T → PX P → (E) P → id X → Y → +TY Y → X → *T Z →

Generación Ascendente del AD Pudimos haber colocado las instrucciones Write al FINAL de cada frase, en lugar del principio. De ser así, generamos el árbol en forma ascendente. En cada rutina, y para cada alternativa, escribimos la producción escogida A →  DESPUÉS de reconocer .

Generación Ascendente del AD proc S;{S → begin SL end → id := E; } case Next_Token of T_begin: Read (T_begin); SL; Read (T_end); Write (S → begin SL end); T_id: Read (T_id); Read (T_:=); E; Read (T_;); Write (S → id:=E;); otherwise Error; end;

Generación Ascendente del AD proc SL; {SL → SZ} S; Z; Write(SL → SZ); end; proc E; {E → TY} T; Y; Write(E → TY);

Generación Ascendente del AD 11/04/2017 Generación Ascendente del AD proc Z; {Z → SZ → } case Next_Token of T_begin, T_id: S; Z; Write(Z → SZ); T_end: Write(Z → ); otherwise Error; end end;

Generación Ascendente del AD proc Y; {Y → +TY → } if Next_Token = T_+ then Read (T_+); T; Y; Write (Y → +TY); else Write (Y → ); end;

Generación Ascendente del AD proc T; {T → PX } P; X; Write (T → PX) end; proc X;{X → *T → } if Next_Token = T_* then Read (T_*); T; Write (X → *T); else Write (X → ); end

Generación Ascendente del AD proc P;{P → (E) → id } case Next_Token of T_(: Read (T_(); E; Read (T_)); Write (P → (E)); T_id: Read (T_id); Write (P → id); otherwise Error; end;

Notas La colocación de las instrucciones Write sigue siendo obvia. Las producciones se emiten conforme las rutinas terminan, en lugar de hacerlo al empezar. Las producciones son emitidas en orden inverso, i.e., la secuencia de producciones debe ser utilizada en orden inverso para producir una derivación derecha. Nuevamente, el árbol puede ser construido conforme procede el algoritmo (usando una pila de árboles), o puede ser construido por un post-procesador.

Ejemplo P → id X → T → P X Y → Y → +T Y E → T Y P → ( E ) P → id X → Hilera de Entrada: begin id := (id + id) * id; end Salida: P → id X → T → P X Y → Y → +T Y E → T Y P → ( E ) P → id X → T → PX X → *T Y → E → TY S → id:=E; Z → SL → SZ S → begin SL end

P → id X → T → P X Y → Y → + T Y E → T Y P → ( E ) X → * T S → id := E; Z → SL → S Z S → begin SL end

Recursividad vs. Iteración No todos los símbolos no-terminales son necesarios. La recursividad de SL, X, Y y Z se puede reemplazar con iteración.

Recursividad vs. Iteración proc S; {S → begin SL end → id := E; case Next_Token of T_begin : Read(T_begin); repeat S; until Next_Token  {T_begin,T_id}; Read(T_end); T_id : Read(T_id); Read (T_:=); E; Read (T_;); otherwise Error; end end; SL → S Z Z → S Z → } SL Reemplaza llamado a SL. Reemplaza recursividad de Z, porque L(Z)=S*

Recursividad vs. Iteración Reemplaza recursividad de Y, porque L(Y)=(+T)*. proc E; {E → TY Y → +TY → } T; while Next_Token = T_+ do Read (T_+); od end;

Recursividad vs. Iteración proc T; {T → PX X → *T → } P; if Next_Token = T_* then Read (T_*); T; end; Reemplaza llamado a X, porque L(X)=(*T)? No hay iteración, porque X no es recursivo.

Recursividad vs. Iteración proc P;{P → (E) → id } case Next_Token of T_(: Read (T_(); E; Read (T_)); T_id: Read (T_id); otherwise Error; end end;

Construcción Ascendente del AD, para la gramática original proc S; { (1)S → begin SL end (2)S → begin SL end → id := E; → id := E; SL → SZ SL → SL S Z → SZ → S → } case Next_Token of T_begin: Read(T_begin); S; Write(SL → S); while Next_Token in {T_begin,T_id} do S; Write(SL → SL S); od Read(T_end); Write(S → begin SL end); T_id: Read(T_id);Read (T_:=);E;Read (T_;); Write(S → id :=E;); otherwise Error; end end;

Construcción Ascendente del AD, para la gramática original proc E; {(1)E → TY (2) E → E+T Y → +TY → T → } T; Write (E → T); while Next_Token = T_+ do Read (T_+); Write (E → E+T); od end while, porque Y es recursivo

Construcción Ascendente del AD, para la gramática original proc T; {(1)T → PX (2) T → P*T X → *T → P → } P; if Next_Token = T_* then Read (T_*); T; Write (T → P*T) else Write (T → P); end; if, porque X no es recursivo

Construcción Ascendente del AD, para la gramática original proc P;{(1)P → (E) (2)P → (E) → id → id } // IGUAL QUE ANTES end;

P → id T → P E → T E → E+T P → (E) T → P*T E → T S → id:=E; SL→ S S → begin SL end T → P*T E → T S → id:=E; SL→ S S → begin SL end

Generación ascendente del ASA, para la gramática original proc S; { S → begin S+ end  'block' → id := E;  'assign' var N:integer; case Next_Token of T_begin : Read(T_begin); S; N:=1; while Next_Token in {T_begin,T_id} do N:=N+1; od Read(T_end); Build Tree ('block',N); T_id : Read(T_id); Read (T_:=); E; Read (T_;); Build Tree ('assign',2); otherwise Error end end; Build Tree (‘x’,n) saca n árboles de la pila, construye un nodo ‘x’ como su padre, y entra el árbol resultante en la pila. Asumimos que se construye un nodo.

Generación ascendente del ASA, para la gramática original proc E; {E → E+T '+' → T } T; while Next_Token = T_+ do Read (T_+) Build Tree ('+',2); od end; Ramificación izquierda en el árbol !

Generación ascendente del ASA, para la gramática original proc T; {T → P*T '*' → P } P; if Next_Token = T_* then Read (T_*) T; Build Tree ('*',2); end; Ramificación derecha en el árbol !

Generación ascendente del ASA, para la gramática original proc P;{P → (E) → id } // IGUAL QUE ANTES, // i.e.,no se construye árbol // encima de E o id. end;

Ejemplo id1 id4 id2 BT('*',2) BT('assign',2) BT('block',1) id3 Hilera de Entrada: begin id1 := (id2 + id3) * id4; end Secuencia de eventos: id1 id4 id2 BT('*',2) BT('assign',2) BT('block',1) id3 BT('+',2)

Resumen Construcción ascendente o descendente del árbol deseado. Gramática original o modificada. Árbol de derivación, o árbol de sintaxis abstracta. Técnica de escogencia: Parser de descenso recursivo, Construcción ascendente del ASA, para la gramática original.

11/04/2017 Parsing LR Las rutinas en el parser de descenso recursivo pueden ser “anotadas” con “items”. Item: una producción con un marcador “.” en la parte derecha. Podemos usar los “items” para describir la operación del parser de descenso recursivo. Existe un NFA (un estado por cada item) que describe todas las secuencias de llamadas en el código de descenso recursivo.

Parser de Descenso Recursivo con items 11/04/2017 Parser de Descenso Recursivo con items Ejemplo: proc E; {E → .E + T, E →.T} T; {E → E. + T, E → T.} while Next_Token = T_+ do {E → E. + T} Read(T_+); {E → E + .T } T; {E → E + T.} od {E → E + T. E → T.} end; T T + T

NFA que conecta items NFA: M = (PP, V, , S’ → .S, { S’ → S.}) 11/04/2017 NFA que conecta items NFA: M = (PP, V, , S’ → .S, { S’ → S.}) PP: conjunto de todos los items posibles (PP: producciones con punto), y  se define tal que simula un llamado a B simula la ejecución de la rutina X, si X es no-terminal, o Read(X), si X es un terminal.  1 A → α.Bβ B → . ω X 2 A → α.Xβ A→X.β

NFA que conecta items Ejemplo: E → E + T T → i S → E  → T T → (E) E ┴ 11/04/2017 NFA que conecta items Ejemplo: E → E + T T → i S → E  → T T → (E) E ┴ S → . E S → E .  S → E  . ε T E → . T E → T . ε ε ε i ε T → . i T → i . ε ( E T → . (E) T → (.E) T → (E) . ) ε T → (E.) ε ε ε E + T E → .E + T E → E. + T E → E +. T E → E + T.

11/04/2017 NFA que conecta items Hay que usar esta máquina con una pila (la secuencia de llamadas recursivas). Para “regresar” de A → ω., retrocedemos |ω| + 1 estados, y avanzamos sobre A. Problema de esta máquina: es no-determnística. No problem. Be happy . Transformémosla a una DFA !

DFA que conecta items ESTE ES UN AUTÓMATA LR(0) + ┴ ┴ ┴ ┴ i i i + E 11/04/2017 DFA que conecta items ┴ E ┴ ┴ ┴ S → . E S → E . S → E . E → .E + T E → E. + T + E → . T T → . i i i T E → E + T. E → E +. T T → . (E) T → i . T → .i ( T → .(E) T i ( T E → T . + T → (.E) E → .E + T E ( T → (E.) ) T → (E) . E → .T E → E. + T T → .i T → .(E) ESTE ES UN AUTÓMATA LR(0)

Parsing LR LR significa “Left-to-Right, Right-most Derivation”. 11/04/2017 Parsing LR LR significa “Left-to-Right, Right-most Derivation”. Necesitamos una pila de estados para operar el parser. Se requieren 0 símbolos de “look-ahead”, por lo que se denomina LR(0). El DFA describe todas las posiciones posibles en el código de descenso recursivo. Una vez construido el automáta, se pueden descartar los items (como siempre con NFA →DFA).

Parsing LR Operación de un parser LR 11/04/2017 Parsing LR Operación de un parser LR Dos movimientos: “shift” y “reduce”. Shift: Avanzar desde el estado actual sobre Next_Token, y agregar el estado nuevo a la pila. Reduce: (sobre A → ω). Remover |ω| estados de la pila. Avanzar desde el nuevo estado, sobre A.

Parsing LR 1 3 2 4 5 6 7 8 9 10 i i + i ┴ + E → T T T T E E T→i T E E 11/04/2017 Parsing LR Pila Entrada Árbol deDerivación 1 i + (i + i) ┴ i + ( i + i ) 14 + (i + i) ┴ 13 + (i + i) ┴ 12 + (i + i) ┴ 127 (i + i) ┴ 1275 i + i) ┴ 12754 + i) ┴ 12753 + i)┴ 12758 + i) ┴ 127587 i) ┴ 1275874 ) ┴ 1275879 ) ┴ 12758 ) ┴ 12758 10 ┴ 1279 ┴ 12 ┴ 126 --------- E → T T 1 3 T ( T E i E i ( 2 T→i 4 5 T + i E ┴ ( E 6 7 8 + T E ) T T E 9 10 E → E+T T → (E)

Parsing LR Representación de Parsers LR Dos Tablas: 11/04/2017 Parsing LR Representación de Parsers LR Dos Tablas: Acción: indexada por estado y símbolo terminal. Contiene los movimientos “shift” y “reduce”. GOTO: indexada por estado y símbolo no-terminal. Contiene las transiciones sobre símbolos no-terminales.

Parsing LR ┴ 1 3 2 4 5 6 7 8 9 10 ACCIÓN GOTO i + ( ) E T Ejemplo: i i 11/04/2017 Parsing LR ACCIÓN GOTO i + ( ) E T ┴ Ejemplo: E → T 1 S/4 S/5 2 3 S/7 S/6 R/E→T 4 R/T→ i 5 8 6 Accept 7 9 S/10 R/ E →E+T 10 T → (E) T 1 3 ( T E i ( 2 T→i 4 5 i + i E ┴ ( 6 7 8 + ) T 9 10 E → E+T T → (E)

Parsing LR Algoritmo Driver_LR: Push(Start_State, S); 11/04/2017 Parsing LR Algoritmo Driver_LR: Push(Start_State, S); while ACTION (Top(S), ) ≠ Accept do case ACTION (Top(S), Next_Token) of Shift/r: Read(Next_Token); Push(r, S) Reduce/A → ω: Pop(S) |ω| veces; Push(GOTO (Top(S), A), S); empty: Error; end;

Parsing LR Construcción Directa del Autómata LR(0): 11/04/2017 Parsing LR Construcción Directa del Autómata LR(0): PT(G) = Closure({S’ → .S  }) U {Closure(P) | P  Successors(P’), P’  PT(G)} Closure(P) = P U {A → .w | B → α.Aβ  Closure(P)} Successors(P) = {Nucleus(P, X) | X  V} Nucleus(P, X) = {A → αX .β | A → α.Xβ  P}

Parsing LR ┴ ┴ E → E + T T → i S → E  → T T → (E) 11/04/2017 E → E + T T → i S → E  → T T → (E) Parsing LR Construcción Directa del Autómata LR(0) previo ┴ E E ) S → .E E → .E + T E → .T T → .i T → .(E) 2 T → (.E) E → .E + T E → .T T → .i T → .(E) 8 T → (E.) E → E. + T 10 1 5 8 E 2 E 8 + 7 T 3 T 3 E → E + T. 9 i 4 i 4 T → (E). ( 10 5 ( 5 ┴ ┴ S → E . E → E. + T S → E. 2 6 6 + 7 T E → E + .T T → .i T → .(E) 9 7 i E → T. 4 3 ( 5 T → i. 4

11/04/2017 Parsing LR Notas: Esta gramática es LR(0) porque no tiene “conflictos”. Un conflicto ocurre cuando un estado contiene a. Conflicto shift-reduce: un item final (A → ω.) y un item no-final (A → α.β), o b. Conflicto reduce-reduce: Dos o más items finales (A → ω. y B → ω.).

Parsing LR ┴ ┴ ┴ Ejemplo: E → E + T T → P * T P → i → T → P P → (E) 1 11/04/2017 Parsing LR Ejemplo: E → E + T T → P * T P → i → T → P P → (E) ┴ S → .E E → .E + T E → .T T → .P * T T → .P P → .i P → .(E) E 2 P → (.E) E → .E + T E → .T T → .P * T T → .P P →. i P → .(E) E 10 T → P * .T T → .P * T T → .P P → .i P → .(E) T 12 1 6 9 E 2 E 10 P 4 T T P 3 3 4 P 4 P i 4 5 ( P 4 P 4 6 i 5 i 5 P → (E.) E → E. + T ) 13 10 ( ( + 6 6 8 ┴ ┴ ┴ S → E . S → E . E → E. + T 7 E → E + T. 2 11 7 E → E + .T T → .P * T T → .P P → .i P → .(E) T + 11 8 8 T → P * T . 12 E → T. P 3 4 P 4 P → (E). 13 T → P. * T T → P. * 4 9 i 5 La gramática no es LR(0). ( 6 P →i. 5

11/04/2017 Parsing LR El conflicto aparece en la table ACCIÓN, como entradas múltiples. + * i ( ) 1 S/5 S/6 2 S/8 S/7 3 R/E→T 4 5 R/P→i 6 S/5 S/6 7 Accept 8 S/5 S/6 9 S/5 S/6 10 S/8 S/13 11 R/E→E+T 12 R/T→P*T 13 R/P→(E) ┴ ACCIÓN R/T→P S/9,R/T→P R/T→P

11/04/2017 Parsing LR Solución: Utilizar “lookahead”, tomando en cuenta el siguiente símbolo de entrada en la decisión de parsing. En LL(1), lookahead se usa al principio de la producción. En LR(1), lookahead se usa al final de la producción. Usaremos: SLR(1) – Simple LR(1) LALR(1) – LookAhead LR(1)

Parsing LR + * i ( ) ┴ SLR(1): 11/04/2017 Parsing LR SLR(1): Calculamos Follow(A) para cada producción A →ω que causa un conflicto. Luego, se coloca “R/A → ω” en ACCIÓN[p,t] solo si t  Follow(A). Aquí, Follow(T)  Follow(E) = {+, ),  }. + * i ( ) ┴ 4 (antes) R/T→P S/9,R/T→P R/T→P R/T→P R/T→P R/T→P 4 (después) R/T→P S/9 R/T→P R/T→P Problema resuelto. La gramática es SLR(1)

Parsing LR ┴ ┴ ┴ ┴ 4 1 2 4 1 3 5 6 2 3 4 5 6 La gramática no es LR(0) 11/04/2017 Parsing LR Ejemplo: S → aSb {anbn/ n > 0} → ┴ S 4 ┴ 1 2 4 S’ → .S S → .aSb S → . S 2 1 S → a 3 a S b 3 5 6 S → aSb ┴ ┴ S → S’ → S . a 2 4 a b  S 1 S/3 R/S→ R/S→ R/S→ 2 S/4 3 5 4 Accept Accept Accept S/6 6 R/S→aSb S → a.Sb S → .aSb S → . S 3 5 a 3 ┴ S’ → S . 4 b S → aS.b 6 5 S → aSb. 6 La gramática no es LR(0)

Parsing LR Análisis SLR(1): 11/04/2017 Parsing LR Análisis SLR(1): Estado 1: Follow(S)={b, }. Ya que a  Follow(S), el conflicto shift/reduce queda resuelto. Estado 3: La misma historia. Las filas 1 y 3 resultantes: a b ┴ S 1 S/3 R/S → R/S → 2 3 S/3 R/S → R/S → 5 Conflictos resueltos. La gramática es SLR(1).

Parsing LR Gramáticas LALR(1) Ejemplo: S → AbAa A → a → Ba B → a 1 2 6 11/04/2017 Parsing LR Gramáticas LALR(1) Ejemplo: S → AbAa A → a → Ba B → a Autómata LR(0): S  1 2 6 A b a 3 7 10 A → a A a 9 11 A → AbAa B a 4 8 S → Ba a A → a 5 Conflicto reduce-reduce. La gramática no es LR(0). B → a

Parsing LR Análisis LR(0): a b ┴ R/A→a,R/B→a R/A→a,R/B→a R/A→a,R/B→a 11/04/2017 Parsing LR Análisis LR(0): a b ┴ R/A→a,R/B→a R/A→a,R/B→a R/A→a,R/B→a La gramática no es LR(0). Análisis SLR(1): Follow(A)={a,b}, No disjuntos. Follow(B)={a} Conflicto no resuelto. a b ┴ 5 R/A→a,R/B→a R/A→a La gramática no es SLR(1).

Parsing LR Técnica LALR(1): 11/04/2017 Parsing LR Técnica LALR(1): I. Para cada reducción conflictiva A → ω en cada estado inconsistent q, hayar todas las transiciones no-terminales (pi, A) tales que II. Calcular Follow(pi, A) (ver abajo), para todo i, y unir los resultados. El conjunto que resulta es el conjunto de “lookahead” LALR(1) para la reducción A → ω en q. A p1 ω q A → ω ω A pn

Parsing LR Cálculo de Follow(p, A): 11/04/2017 Parsing LR Cálculo de Follow(p, A): Es el cálculo ordinario Follow, en otra gramática, llamada G’. Para cada transición (p, A), y cada producción A→w1 w2…wn, tenemos En esta situación, G’ contiene esta producción: (p, A) → (p, w1)(p2, w2)…(pn, wn) G’: Consiste de las transiciones en el autómata LR(0). Refleja la estructura de G, y la del autómata LR(0). A p w1 Wn-1 Wn w2 … p2 p3 pn A → w1…wn

Parsing LR En nuestro ejemplo: G: S → AbAa A → a → Ba B → a 1 2 6 3 7 11/04/2017 Parsing LR En nuestro ejemplo: G: S → AbAa A → a → Ba B → a G’: (1, S) → (1, A)(3, b)(7, A)(9, a) → (1, B)(4, a) (1, A) → (1, a) (7, A) → (7, A) (1, B) → (1, a) S  1 2 6 A b a 3 7 10 A → a A a 9 11 A → AbAa B a 4 8 S → Ba a A → a 5 B → a Estos se separaron !

Parsing LR Para el conflicto en el estado 5, necesitamos 11/04/2017 Parsing LR Para el conflicto en el estado 5, necesitamos Follow(1, A) = {(3, b)} Follow(1, B) = {(4, a)}. Se extraen los símbolos terminales: a b ┴ 5 R/B → a R/A → a Conflicto resuelto. La gramática es LALR(1). A → a {b} a 5 B → a {a}

Parsing LR Ejemplo: S → bBb B → A → aBa A → c → acb 1 2 5 8 3 6 11 7 4 11/04/2017 Parsing LR Ejemplo: S → bBb B → A → aBa A → c → acb Autómata LR(0):  S 1 2 5 8 A → c c b B b 3 6 11 S → bBb A 7 B → A A a B a S → aBa 4 9 12 c b 10 13 S → acb A → c Estado 10 es inconsistente (conflicto shift-reduce). La gramática no es LR(0)

Parsing LR Análisis SLR(1), estado 10: Follow(A)  Follow(B) ={a, b}. 11/04/2017 Parsing LR Análisis SLR(1), estado 10: Follow(A)  Follow(B) ={a, b}. La gramática no es SLR(1). Análisis LALR(1): Se necesita Follow(4, A). G’: (1,S) → (1, b)(3, B)(6, b) (3, B) → (3, A) → (1, a)(4, B)(9, a) (4, B) → (4, A) → (1, a)(4, c)(10, b) (3, B) → (3, c) (4, A) → (4, c) Así, Follow(4, A)  Follow(4, B) = {(9, a)}. El conjunto lookahead es {a}. La gramática es LALR(1).

Resumen de Parsing Parsing Descendente (top-down): 11/04/2017 Resumen de Parsing Parsing Descendente (top-down): Escrito a mano o dirigido por tabla: LL(1) S Parte conocida Parte conocida pila w Parte por predecir α β Entrada procesada Entrada por procesar

Resumen de Parsing Parsing Ascendente (bottom-up): 11/04/2017 Resumen de Parsing Parsing Ascendente (bottom-up): Dirigida por tabla: LR(0), SLR(1), LALR(1). S Parte conocida Parte desconocida pila w Parte conocida α β Entrada procesada Entrada por procesar

Lenguajes Libres de Contexto Curso de Compiladores Preparado por Manuel E. Bermúdez, Ph.D. Profesor Asociado University of Florida