1 UNIVERSIDAD DE MAGALLANES FACULTAD DE INGENIERÍA DEPARTAMENTO DE COMPUTACIÓN ANÁLISIS SINTÁCTICO PREDICTIVO NO RECURSIVO Elaborado el Martes 12 de Octubre de 2004 I.- INTRODUCCIÓN Es posible realizar un análisis descendente sin utilizar funciones, no como en el caso del análisis recursivo visto en el apunte anterior. Un modelo esquemático para un analizador descendente no recursivo requiere de los siguientes elementos: BÚFER DE ENTRADA $ indica el fin de la entrada y no pertenece al Alfabeto a + b - c $ PILA Contiene Terminales y No Terminales. X Y Z $ Algoritmo de Análisis Sintáctico Predictivo No recursivo SALIDA - Derivación por la izquierda de la cadena de entrada. - Árbol de análisis sintáctico construido desde la raíz. MATRIZ M[A, a] A es un No Terminal y a es un Terminal distinto al símbolo $. Tabla de Análisis Sintáctico (M) En este apunte se tratará cómo implementar este esquema a nivel de diseño, comenzando con la construcción de la Tabla M, basada en dos funciones llamadas primero() y siguiente().
2 II.- ALGORITMO DE ANÁLISIS DESCENDENTE El algoritmo para ejecutar el analizador sintáctico es único. Está basado en una configuración que inicialmente tiene a S$ en la pila, donde S es el símbolo inicial de la gramática G que se está analizando. En principio el búfer de entrada tiene a w$, w es una supuesta cadena perteneciente a G. // sea S el símbolo inicial de G, X el estado en la cima de la pila y a el primer // símbolo apuntado por w push(s) a <-- siguientesímbolo( ) do if( X v X = $ ) if( X = $) return OK! else if( X = a) pop( ) a <-- siguientesímbolo( ) else error( ) else if( M[X,a] = X Y 1 Y 2.. Y k ) pop( ) for( i = k ; i >= 1 ; i -- ) push( Y i ) mostrar( X Y 1 Y 2.. Y k ) else error() while(x!=$) La única diferencia entre un analizador y otro es el contenido de la tabla M. Por lo tanto el diseño de este tipo de analizador se reduce a construir la tabla de análisis descendente.
3 III.- CONSTRUCCIÓN DE LA TABLA La tabla que utiliza el analizador sintáctico se construye a partir de la gramática G de entrada. A esta gramática se le aplican las mismas restricciones vistas para el analizador sintáctico recursivo en el sentido de eliminar la ambiguedad, quitar la recursión por la izquierda y factorizar las reglas que tengan prefijos comunes para un mismo No Terminal. Por ejemplo, sea P el conjunto de reglas de producción para G, igual a: P = expresión término expresión expresión + término expresión - término expresión ε término factor término término * factor término / factor término ε factor núm id - expresión ( expresión ) La construcción esta guiada por dos funciones llamadas primero() y siguiente(), las cuales se aplican de manera recursiva sobre los No Terminales y los Terminales de la gramática G. Cálculo del primero() Informalmente, para calcular el primero(x), donde X es un símbolo gramatical (Terminal o No Terminal), se siguen las siguientes reglas: 1.- Si X es un símbolo terminal, entonces primero(x) = X. Matemáticamente: Si X Σ primero(x) = X
4 2.- Si existe una regla X ε, entonces ε pertenece al primero(x). Matemáticamente: Si X ε ε primero(x) = X 3.- Si existe una regla X Y 1 Y 2.. Y k y ε pertenece al primero(y i ) con i = 1 hasta i = j 1, entonces el primero(y j ) pertenece al primero(x). Matemáticamente: Si X Y 1 Y 2.. Y k ε primero(y i ) i = 1.. j-1 primero(y j ) primero(x) 4.- Si existe una regla X Y 1 Y 2.. Y k y ε pertenece a Y i para toda i = 1 hasta k, entonces ε pertenece al primero(x). Matemáticamente: Ejemplo Si X Y 1 Y 2.. Y k ε primero(y i ) i = 1.. k ε primero(x) Sea la gramática G con reglas de producción P definida más arriba. G tiene Σ = id, núm, (, ), -, +, *, / y N = expresión, expresión, término, término, factor. El cálculo del primero(x) para todos los terminales de G darían los siguientes resultados: primero(expresión) = primero(término) primero(término) = primero(factor) primero(factor) = primero(núm) primero(id) primero( - ) primero( ( ) = núm, id, -, ( primero(expresión ) = primero( + ) primero( - ) ε = +, -, ε primero(término ) = primero( * ) primero( / ) ε = *, /, ε Cálculo del siguiente() Informalmente, para calcular el siguiente(a), donde A es un símbolo gramatical de tipo No Terminal, se siguen las siguientes reglas: 1.- Si A es el símbolo de partida, entonces $ (marca de fin de la entrada) pertenece al siguiente(a). Matemáticamente: Si A = S $ siguiente(a)
5 2.- Si existe una regla B α A β, entonces los símbolos de primero(β) excepto ε, pertenecen al siguiente(a). Matemáticamente: Si B α A β primero(β) ε siguiente(a) 3.- Si existe una regla B α A ó una regla B α A β, donde ε pertenece a primero(β), entonces siguiente(b) pertenece a siguiente(a). Matemáticamente: Si B α A B α A β) ε primero(β) siguiente(b) siguiente(a) Ejemplo Para N = expresión, expresión, término, término, factor. El cálculo del siguiente(a) para todos los No Terminales de G darían los siguientes resultados: siguiente(expresión) = $ primero( ) ) = $, ) siguiente(expresión ) = siguiente(expresión) siguiente(término) = primero(expresión ) ε siguiente(expresión) siguiente(expresión ) = +, -, $, ) siguiente(término ) = siguiente(término) siguiente(factor) = primero(término ) ε siguiente(término) siguiente(término ) = *, /, +, -, $, ) Método para asignar valores a la tabla M La tabla tiene tantas columnas como símbolos terminales tenga el alfabeto, más la marca de fin de la entrada ( $ ). Las filas son tantas como símbolos no terminales. Para el ejemplo entonces se debe construir una tabla como la siguiente: NO SÍMBOLO DE ENTRADA TERMINAL id núm - + * / ( ) $ expresión expresión término término factor La tabla se debe llenar con las reglas de producción pertenecientes a P. Previo a esto se debe asignar un valor numérico a cada regla, por ejemplo:
6 P = expresión término expresión (1) expresión + término expresión (2) - término expresión (3) ε (4) término factor término (5) término * factor término (6) / factor término (7) ε (8) factor núm (9) id (10) - expresión (11) ( expresión ) (12) Informalmente, las reglas para llenar la tabla serían las siguientes: 1.- Para cada producción A α hacer: a) Para cada terminal a perteneciente a primero(α), añadir A α en M[A, a]. b) Si ε está en primero(α), añadir A α en M[A, b], donde b pertenece al siguiente(a). c) Si ε está en primero(α) y $ está en siguiente(a), añadir A α en M[A, $]. 2.- Cualquier entrada no definida es un error. Ejemplo Siguiendo las reglas la tabla debería quedar así: NO SÍMBOLO DE ENTRADA TERMINAL id núm - + * / ( ) $ expresión (1) (1) (1) (1) expresión (3) (2) (4) (4) término (5) (5) (5) (5) término (8) (8) (6) (7) (8) (8) factor (10) (9) (11) (12)
7 Para obtener la tabla se pueden ordenar las reglas y trabajar cada una por separado: Regla A α a primero(a) Si ε primero(a) b siguiente(a) expresión término expresión (1) ( - id núm expresión + término expresión (2) + expresión - término expresión (3) - expresión ε (4) $ ) término factor término (5) ( - id núm término * factor término (6) * término / factor término (7) / término ε (8) + - $ ) factor núm (9) núm factor id (10) id factor - expresión (11) - factor ( expresión ) (12) ( Otra forma de presentar la tabla es reemplazando los números de cada regla por la expresión. Por efectos del ancho de página sólo se muesra un detalle de como quedaría: NO SÍMBOLO DE ENTRADA TERMINAL id núm expresión término expresión término expresión expresión término factor término factor término término factor id núm
8 IV.- PRUEBA DEL ALGORITMO La tabla obtenida puede ser probada junto con el algoritmo, ejecutando este último para una cadena de prueba válida y otra no válida. Por ejemplo, para la cadena de entrada 3 + 5, se obtienen los siguientes resultados: PILA ENTRADA SALIDA $expresión + núm $ M[expresión, núm] $expresión término + núm $ expresión término expresión M[término, núm] $expresión término factor + núm $ término factor término M[factor, núm] $expresión término núm + núm $ factor núm $expresión término núm $ M[término, +] $expresión núm $ término ε M[expresión, +] $expresión término + núm $ expresión + término expresión $expresión término $ M[término, núm] $expresión término factor $ término factor término M[factor, núm] $expresión término núm $ factor núm $expresión término M[término, $] $expresión término ε M[expresión, $] $ expresión ε Si se coloca atención en las salidas se podrá notar que la construcción del árbol de derivación es desde la raíz hacia las hojas. Además la expansión de cada No Terminal se realiza siempre por el que está más a la izquierda. El árbol de derivación resultante es: expresión término expresión factor término + término expresión núm ε factor término ε núm ε
9 V.- CONSIDERACIONES FINALES Toda gramática que genera una tabla M sin casilleros multivaluados se denomina LL(1) (Left to right, Left to most derivation) por realizar el análisis de la entrada de izquierda a derecha y por realizar la derivación de los no terminales siempre por el que está más a la izquierda. El número 1 es por tomar un solo símbolo de la entrada por anticipado para decidir las acciones del análisis sintáctico. Aún cuando se elimine toda la recursión por la izquierda y se factorize por la izquierda, algunas construcciones jamás se convertirán en LL(1). Ejemplo Sea la gramática G con reglas de producción P igual a: P= proposición if expresión then proposición if expresión then proposición else proposición otra_proposición expresión condición Factorizando sería: P= proposición if expresión then proposición proposición (1) otra_proposición (2) proposición ε (3) else proposición (4) expresión condición (5) Esta gramática produce como resultado la siguiente tabla M: NO SÍMBOLO DE ENTRADA TERMINAL if then else otra_proposición condición $ proposición (1) (2) proposición (3) (4) (3) expresión (5)
10 En estos casos se elige la regla proposición else proposición cuando se lee el símbolo else en la entrada, porque no tiene sentido elegir la otra regla. VI.- EJERCICIOS PROPUESTOS Ejercicio 1 Construya el analizador sintáctico no recursivo para la siguiente gramática G: S Switch ( Exp ) C D C Case Exp : I C Case Exp : I D ε Default : I I ε I Ins Considere Exp e Ins como terminales. Ejercicio 2 Construya el analizador sintáctico no recursivo para la siguiente gramática G: E E + E E * E Id ( R ) Celda R Celda Celda : Celda Celda corresponde a un identificador válido para una planilla electrónica, por ejemplo: A0, B1, C30, etc. Id es un identificador válido para un nombre de función como MAX, COS, AVG, etc. Ejercicio 3 Construya el analizador sintáctico no recursivo para la siguiente gramática G: Ejercicio 4 E E Sub R E sup E E Id Const R E sup E E Construya el analizador sintáctico no recursivo para la siguiente gramática G: E T F E Or T T T And F F Not F ( E ) TRUE FALSE