Compiladores e intérpretes Análisis Sintáctico III Profesor: Eridan Otto
Análisis Sintáctico III Parser Predictivo no recursivo Definiciones Condiciones Tabla de análisis Análisis
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:definiciones La construcción de un parser predictivo no recursivo as apoyada por dos funciones asociadas a la gramática del lenguaje. Las funciones PRIMERO() y SIGUIENTE(), llenar las entradas de la tabla de parsing, cuando sea posible. El conjunto de tokens obtenido por la función SIGUIENTE puede también ser usado surante el manejo y recuperación de errores. Informalmente ya conocemos la función PRIMERO Informalmente, se define la función SIGUIENTE(A) para un noterminal A, al conjunto de terminales a que pueden aparecer inmediatamente a la derecha de A en alguna forma sentencial, esto es, el conjunto de terminales a tal que exista una derivación de la forma para alguna y .Note que que puede haber, algún momento durante la derivación, en que existan símbolos entre A y a, pero si sucede, estos debieran terminar en y desaparecer. Si A es un símbolo de extrema derecha en alguna forma sentencial, entonces $ (como fin de cadena) está en SIGUIENTE(A).
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:definiciones Si es una cadena de símbolos gramaticales , PRIMERO( ) es el conjunto de terminales que inician las cadenas derivadas de . Calcular el conjunto PRIMERO(X) para todos los símbolos gramaticales X, aplicando las siguientes reglas hasta que no se puedan añadir más terminales o a ningún conjunto PRIMERO. Si X es terminal, entonces PRIMERO(X) es {X} Si X::= es una producción, añadir a PRIMERO(X) Si X es un no terminal y X::=Y1 Y2.. Yk es una produccíon, entonces agrege a a PRIMERO(X) si para algún i , a pertenece a PRIMERO(Yi) y está en todos los PRIMERO(Y1 ), PRIMERO(Y2 )... PRIMERO(Yi-1); Esto es Y1 Y2.. Yk Por ejemplo , todos los elementos de PRIMERO(Y1) están en PRIMERO(X) Si Y1 deriva a entonces se añade PRIMERO(Y2 ), y así subsiguentemente Si no Y1 deriva a no agregue nada más a PRIMERO(X)
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:definiciones Calcule PRIMERO para la gramática; E::= TE’ E’::= +TE’| T ::= FT’ T’::= *FT’| F ::= (E) | Id PRIMERO(E)={(,Id} PRIMERO(T*id)={(,Id} PRIMERO(T)={(,Id} PRIMERO(id*id)={Id} PRIMERO(F)={(,Id} PRIMERO(id)={Id} PRIMERO(E’)={+, } PRIMERO(T’)={*, }
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:definiciones Conjunto SIGUIENTE Conjunto de terminales que pueden aparecer inmediatamente a la derecha de A en alguna forma sentencial, si A es el último símbolo se incluye el separador $ Algoritmo SIGUIENTE(S) = {$}, donde S es el símbolo inicial y $ marca de fin de entrada. La producción A::= , implica La producción A::= , o la producción A::= donde PRIMERO( ) entnces Repetir hasta que no cambieningún conjunto SIGUIENTE para todos los no terminales
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:definiciones Calcule SIGUIENTE para la gramática; E::= TE’ E’::= +TE’| T ::= FT’ T’::= *FT’| F ::= (E) | Id SIGUIENTE(E)={$,)} SIGUIENTE(T)={$,+,)} SIGUIENTE(F)={$,*,+,)} SIGUIENTE(E’)={$, ) } SIGUIENTE(T’)={$,+,) }
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:Condiciones Pregunta: Formalmente qué debe cumplir una gramática para que pueda ser reconocida, sin retroceso, con sólo mirar el siguiente token de entrada, de forma descendente? Formalmente: Si A ::= PRIMERO( ) PRIMERO( ) es conjunto vacío. Es decir que que para ningún terminal a tanto como derivan a la vez cadenas que comiencen con a. No debe ocurrir que y Si , entonces no deriva ninguna cadena que comience con un terminal en SIGUIENTE(A) Estas condiciones definen una gramática de tipo LL(1) Resumiento para cada no terminal y para cada conjunto de producciones: No debe haber conflicto PRIMERO
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:Condiciones Resumiento, una gramática será LL(1): No hay conflictos PRIMERO/PRIMERO, Si para cada no terminal y para cada conjunto de producciones, el conjunto PRIMERO de cada una es disjunto. No hay múltiples alternativas nulas, Si para cada no terminal y para cada conjunto de producciones ,solo hay una sola producción que derive a epsilon No hay conflictos PRIMERO/SIGUIENTE Si hay una producción nula, entonces SIGUIENTE, debe ser disjunto de los conjuntos primero de cada una de las producciones. Estas condiciones deben llevar a que al construir la tabla de parsing, no existan entradas con definiciones múltiples. Esto es, permite el diseño de PDA determinista.
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:Tabla Construcción de una tabla de análisis para un parser predictivo La idea que hay detrás del algoritmo de construcción es la siguiente. Suponga A::= con a perteneciente a los terminales y al PRIMERO( ). El analizador sintáctico expandirá A por cuando el símbolo actual de entrada sea a. La única complicación puede suceder cuando En este caso deberíamos expandir A por alfa si el símbolo actual está en el SIGUIENTE(A). Algoritmo: Para toda (A::= ) que pertenezcan a la gramática haga para todo a que pertenezca a PRIMERO( ) Haga tabla[A,a]:= si pertenece a PRIMERO( ) entonces para todo b que pertenzca SIGUIENTE(A) haga tabla[A,b]:= para todo no terminal A y toto terminal c si tabla [A,c] = null then tabla[A,c]:=error
Compiladores e intérpretes Análisis sintáctico III : parser predictivo,no recursivo:Tabla Ejemplo: Defina la tabla para la gramática; E::= TE’ E’::= +TE’| T ::= FT’ T’::= *FT’| F ::= (E) | Id
Compiladores e intérpretes Análisis sintáctico III: parser predictivo,no recursivo:Análisis Los simbolos de entrada “a” o tokens y los símbolos de la pila “X” determinan la acción del analizador o parser. Hay tres posibilidades: X=a=$, el analizador se detiene y retorna en estado existoso. X=a<>$, el analizador saca X de la pila y mueve el puntero de preanálisis al próximo token Si X pertenece al conjunto de los no terminales, el programa va a la tábla de análisis y consulta tabla[X,a] Si tabla[X,a]=UVW, se sustituye la X de la pila por WVU (U en el tope de la pila) Si tabla[X,a]=error, se llama a la rutina de recuperación de error.
Compiladores e intérpretes Análisis sintáctico III: parser predictivo,no recursivo:Análisis Algoritmo, sea pila una variable tipo stack, y M arreglo para contener tabla Pila := $ Agregar $ al final de la entrada a:= yylex() /*o nexttoken()*/ Push S; Repeat If X in (terminales) or X=$ then If X=a then Pop;a := yylex() ; else error(); If M[X,a]= X::= Y1 Y2.. Yk then Pop; Push Yk Yk-1.. Y1 Until X=$ If X =$ and a=$ then aceptar(); Else error();
Compiladores e intérpretes Análisis sintáctico III: parser predictivo,no recursivo:Análisis Ejemplo, considere la tabla construida y su gramática, realice el parser para la cadena de tokens Id*Id+id.