Compiladores: Parsing ascendente Francisco J Ballesteros LSUB, URJC Page 1 of 64
Parsing ascendente Normalmente utilizaremos parsers descendentes para problemas pequeños cuando podemos escribir uno predictivo fácilmente En otro caso se suelen utilizar parsers ascendentes trabajan desde las hojas a la raíz del árbol sintáctico intentan reducir los terminales al símbolo inicial de la gramática Page 2 of 64
Parsing ascendente Por ejemplo, para esta gramática EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 3 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 4 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 5 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 6 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 7 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 8 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 9 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 10 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 11 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 12 of 64
Parsing ascendente EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num Page 13 of 64
Parsing ascendente Page 14 of 64
Parsing ascendente Se intenta todo el tiempo reducir la entrada Mirando qué producciones podemos aplicar Y si hay que mirar más terminales de la entrada o hay que aplicar una producción y reducir Page 15 of 64
Parsing ascendente Esto es, dada EXPR ::= EXPR + TERM TERM TERM ::= TERM * FACT FACT FACT ::= ( EXPR ) num derivamos del final al principio... ->EXPR ->TERM ->TERM * FACT ->TERM * 8 ->FACT * 8 ->( EXPR ) * 8 ->( EXPR + TERM ) * 8 ->( EXPR + FACT ) * 8 ->( EXPR + 5 ) * 8 ->( TERM + 5 ) * 8 ->( FACT + 5 ) * 8 ->( 3 + 5 ) * 8 Page 16 of 64
Parsing ascendente El parsing ascendente construye una derivación right-most a la inversa (del final al principio) ->( 3 + 5 ) * 8 ->( FACT + 5 ) * 8 ->( TERM + 5 ) * 8 ->( EXPR + 5 ) * 8 ->( EXPR + FACT ) * 8 ->( EXPR + TERM ) * 8 ->( EXPR ) * 8 ->FACT * 8 ->TERM * 8 ->TERM * FACT ->TERM ->EXPR Page 17 of 64
Gramáticas para parsing ascendente Naturalmente, que no sean ambiguas salvo por ambigüedades resolubles por precedencia de operadores Tampoco recursivas por la derecha igual que bottom-up no tolera las recursivas por la izquierda puesto que crearíamos infinitas reducciones Page 18 of 64
Handle Un handle es un substring (de terminales y no terminales) que encaja con el cuerpo de una producción que si reducimos permite llegar al símbolo inicial Page 19 of 64
Handle: ejemplos en ->( 3 + 5 ) * 8 tenemos el handle 3 por la producción FACT ::= num para dar... ->( FACT + 5 ) * 8 Page 20 of 64
Handle: ejemplos en ->( EXPR + TERM ) * 8 tenemos el handle EXPR + TERM por la producción EXPR ::= EXPR + TERM y al reducir ->( EXPR ) * 8 Page 21 of 64
Handle: ejemplos en ->( EXPR ) * 8 tenemos el handle ( EXPR ) por la producción FACT ::= ( EXPR ) y al reducir ->FACT * 8 Page 22 of 64
Parsing ascendente El proceso de parsing ascendente: Se parte de la cadena (de tokens) a la entrada Se localiza un handle Se reduce Se localiza otro Se reduce... Page 23 of 64
Parsing ascendente con shift y reduce El proceso de parsing ascendente: mantiene una pila de símbolos (terminales y no terminales) si no tenemos un handle: metemos más terminales en la pila si tenemos un handle en la cima de la pila, lo reducimos Tenemos dos operaciones: shift: meter terminales en la pila reduce: cambiar N simbolos de la cima (handle) por parte izqda. Page 24 of 64
Ejemplo de parsing ascendente Repitamos otra vez: Pila: $ Entrada:. ( 3 + 5 ) * 8 donde... $ marca el fondo de la pila: pila vacía. marca por dónde vamos en la entrada Page 25 of 64
Ejemplo de parsing ascendente $. ( 3 + 5 ) * 8 Page 26 of 64
Ejemplo de parsing ascendente $. ( 3 + 5 ) * 8 shift $ ( (. 3 + 5 ) * 8 Page 27 of 64
Ejemplo de parsing ascendente $ ( (. 3 + 5 ) * 8 Page 28 of 64
Ejemplo de parsing ascendente $ ( (. 3 + 5 ) * 8 shift $ ( 3 ( 3. + 5 ) * 8 Page 29 of 64
Ejemplo de parsing ascendente $ ( 3 ( 3. + 5 ) * 8 Page 30 of 64
Ejemplo de parsing ascendente $ ( 3 ( 3. + 5 ) * 8 reduce $ ( FACT ( 3. + 5 ) * 8 con handle 3 FACT ::= num Page 31 of 64
Ejemplo de parsing ascendente $ ( FACT ( 3. + 5 ) * 8 Page 32 of 64
Ejemplo de parsing ascendente $ ( FACT ( 3. + 5 ) * 8 reduce $ ( TERM ( 3. + 5 ) * 8 con handle FACT TERM ::= FACT Page 33 of 64
Ejemplo de parsing ascendente $ ( TERM ( 3. + 5 ) * 8 Page 34 of 64
Ejemplo de parsing ascendente $ ( TERM ( 3. + 5 ) * 8 reduce $ ( EXPR + ( 3. + 5 ) * 8 con handle TERM EXPR ::= TERM Page 35 of 64
Ejemplo de parsing ascendente $ ( EXPR ( 3. + 5 ) * 8 Page 36 of 64
Ejemplo de parsing ascendente $ ( EXPR ( 3. + 5 ) * 8 shift $ ( EXPR + ( 3 +. 5 ) * 8 Page 37 of 64
Ejemplo de parsing ascendente $ ( EXPR + ( 3 +. 5 ) * 8 Page 38 of 64
Ejemplo de parsing ascendente $ ( EXPR + ( 3 +. 5 ) * 8 shift $ ( EXPR + 5 ( 3 + 5. ) * 8 Page 39 of 64
Ejemplo de parsing ascendente En conclusión: stack. input and then... $. ( 3 + 5 ) * 8 shift $ (. 3 + 5 ) * 8 shift $ ( 3. + 5 ) * 8 reduce $ ( FACT. + 5 ) * 8 reduce $ ( TERM. + 5 ) * 8 reduce $ ( EXPR. + 5 ) * 8 shift $ ( EXPR +. 5 ) * 8 shift $ ( EXPR + 5. ) * 8 reduce $ ( EXPR + FACT. ) * 8 reduce $ ( EXPR + TERM. ) * 8 reduce $ ( EXPR. ) * 8 shift $ ( EXPR ). * 8 reduce $ FACT. * 8 reduce $ TERM. * 8 shift $ TERM *. 8 shift $ TERM * 8. reduce $ TERM * FACT. reduce $ TERM. reduce $ EXPR. Page 40 of 64
Conflictos Si hay varias reducciones de handles posibles tenemos un conflicto reduce/reduce Si no se sabe si mirar más símbolos o reducir tememos un conflicto shift/reduce Page 41 of 64
Handles A cada string como ( FACT. + 5 ) * 8 la llamamos forma sentencial. A la derecha del handle sólo hay terminales... $ (. 3 + 5 ) * 8 shift $ ( 3. + 5 ) * 8 reduce $ ( FACT. + 5 ) * 8 reduce $ ( TERM. + 5 ) * 8 reduce... NB: por que la derivación es right-most Page 42 of 64
Handles El handle está siempre en la cima de la pila... $ (. 3 + 5 ) * 8 shift $ ( 3. + 5 ) * 8 reduce $ ( FACT. + 5 ) * 8 reduce $ ( TERM. + 5 ) * 8 reduce... Y por eso podemos utilizar una pila Page 43 of 64
Handles A lo mejor se pueden aplicar varias producciones pero un handle sólo lo es si nos deja derivar hasta el símbolo de inicio El parsing bottom-up radica en identificar handles no hay algoritmo capaz de hacerlo en general para ciertas gramáticas, ciertas heurísticas funcionan Esas gramáticas son las que debemos utilizar ( SLR, LALR ) Page 44 of 64
Identificación de Handles Mantenemos en la cima de la pila un prefijo de un handle Hasta que tenemos el handle (y lo reducimos) El conjunto de prefijos es un lenguaje regular Un AFND puede reconocer los handles Page 45 of 64
Parsers bottom-up, autómatas e Items Escribimos la gramática Utilizamos herramientas para construir el AFND que reconoce los handles El código que ejecuta el autómata es el parser Page 46 of 64
Parsers bottom-up, autómatas e Items El autómata intenta reducir los handles En un instante, tiene múltiples estados activos (es ND) Cada estado es un punto en un proceso de reducir un handle Page 47 of 64
Parsers bottom-up, autómatas e Items Por ejemplo, para FACT ::= ( EXPR ) el autómata intenta avanzar según FACT ::=. ( EXPR ) FACT ::= (. EXPR ) FACT ::= ( EXPR. ) FACT ::= ( EXPR ). Y lo mismo para el resto de reducciones posibles A estas producciones con el. indicado las llamamos items LR(0), o items Page 48 of 64
Parsers bottom-up, autómatas e Items Si tenemos en la pila y entrada $... ( EXPR )... Entonces FACT ::= ( EXPR. ) indica que ( EXPR. es un prefijo del cuerpo de una producción reducible Page 49 of 64
Parsers bottom-up, autómatas e Items En cada estado el autómata mantiene el conjunto de items que indican que hemos visto hasta ahora para poder reducir usando una producción Page 50 of 64
Parsers bottom-up, autómatas e Items Herramientas como yacc toman la gramática generan un AFND para reconocer los handles lo convierten an un AFD cuyos estados son conjuntos de items Las gramáticas aceptables son aquellas no ambiguas en las que las heurísticas de selección de items funcionan esto es, en las que los items son viables para la derivación esto es, en las que lo que reducimos son handles Page 51 of 64
Tipos de gramáticas LR: miran la entrada left-right SLR: simple LR LALR: LR con look-ahead Dicho de otro modo... aquellas para las que las herramientas funcionan (SLR, LALR) pero no hay algoritmo para todas las gramáticas libres de contexto Page 52 of 64
Parsing SLR Si en la pila y entrada tenemos $... a b c x... Reducir si d ::= a b c. es un item en el estado actual y x puede seguir a d según la gramática si x pertenece a follow(d), como veremos... Page 53 of 64
Parsing SLR Si en la pila y entrada tenemos $... a b c x... Desplazar si d ::= a b c. x... es un item en el estado actual Page 54 of 64
Parsing SLR Y si con estas reglas no se puede (hay conflictos) Decimos que la gramática no es SLR! Pero normalmente incluirmos información de precedencia para resolver algunos conflictos Page 55 of 64
Parsing LR Y ahora un ejemplo de un trozo de un AFD para un SLR Page 56 of 64
Parsing LR Page 57 of 64
Parsing LR(1) Es como el SLR Pero utiliza un token de look ahead. Se le llama LR(1), o LALR (look-ahead LR) Los items LR(1) son: FACT ::=. num, x Que quieren decir intentando reducir con esta producción en este punto para reducir sólo si el siguiente símbolos es x Page 58 of 64
Parsing LALR El token de look-ahead hace que más gramáticas funcionen que identifiquen handles al reducir y no partes derechas de producciones cualesquiera Luego las LALR contienen a las SLR Page 59 of 64
Parsing LALR y yacc Yacc es una herramienta que genera el parser dada la gramática El parser es un programa para el autómata (librería) Mas tablas generadas por yacc para describir las transiciones (goto) para describir acciones (action) Las acciones son como no-terminales encajan con la cadena vacía se ejecutan cuando se reducen Page 60 of 64
First y Follow first (X) Es el conjunto de los símbolos terminales que para las producciones X ::=... ej. pueden aparecer como primer terminal a la derecha A ::= x B C C ::= y... first(a) = { x, y } Page 61 of 64
First y Follow follow (X) Es el conjunto de los símbolos terminales que pueden aparecer como primer terminal a la derecha de X en una forma sentencial aceptable ej. A ::= x B C y C ::= z <empty>... follow(b) = { z, y } Page 62 of 64
First y Follow Cuando tengamos conflictos o escribamos la gramática es útil pensar en first y follow Intuitivamente, queremos una gramática lo más rígida posible como hacíamos con parsing predictivo. Si tenemos problemas, podemos aderezar con más puntuación! ej: if condicion sentencia vs o if condicion { sentencia } if (condicion) { sentencia } Page 63 of 64
Questions? Francisco J Ballesteros LSUB, URJC http://lsub.org (http://lsub.org) Page 64 of 64