Como se ha mencionado anteriormente,
la directiva %tree
es
es equivalente a escribir:
%default action { goto &Parse::Eyapp::Driver::YYBuildAST }
Donde el método Parse::Eyapp::Driver::YYBuildAST
se encarga de
retornar la referencia al objeto nodo recién construído.
La ubicación de acciones semánticas explícitas para una regla altera la forma del árbol de análisis. El método Parse::Eyapp::Driver::YYBuildAST no inserta en la lista de hijos de un nodo el atributo asociado con dicho símbolo a menos que este sea una referencia. Esto conlleva que una forma de eliminar un subárbol es estableciendo una acción semántica explícita que no retorne un nodo.
El siguiente ejemplo ilustra como utilizar esta propiedad
en el análisis de un lenguaje mínimo en el que los programas
(P
) son secuencias de
declaraciones (DS
) seguidas de sentencias (SS
).
En un compilador típico, las declaraciones influyen en el análisis semántico (por ejemplo, enviando mensajes de error si el objeto ha sido repetidamente declarado o si es usado sin haber sido previamente declarado, etc.) y la información proveída por las mismas suele guardarse en una tabla de símbolos (declarada en la línea 5 en el siguiente código) para su utilización durante las fases posteriores.
pl@nereida:~/LEyapp/examples$ sed -ne '1,58p' RetUndef.eyp | cat -n 1 /* File RetUndef.eyp */ 2 3 %tree 4 %{ 5 use Data::Dumper; 6 my %s; # symbol table 7 %} 8 9 %% 10 P: 11 %name PROG 12 $DS $SS 13 { 14 print Dumper($DS); 15 $SS->{symboltable} = \%s; 16 return $SS; 17 } 18 ; 19 20 SS: 21 %name ST 22 S 23 | %name STS 24 SS S 25 ; 26 27 DS: 28 %name DEC 29 D 30 {} 31 | %name DECS 32 DS D 33 {} 34 ; 35 36 D : %name INTDEC 37 'int' $ID 38 { 39 die "Error: $ID declared twice\n" if exists($s{$ID}); 40 $s{$ID} = 'int'; 41 } 42 | %name STRDEC 43 'string' $ID 44 { 45 die "Error: $ID declared twice\n" if exists($s{$ID}); 46 $s{$ID} = 'string'; 47 } 48 ; 49 50 S: %name EXP 51 $ID 52 { 53 die "Error: $ID not declared\n" unless exists($s{$ID}); 54 goto &Parse::Eyapp::Driver::YYBuildAST; # build the node 55 } 56 ; 57 58 %%
Las acciones explícitas en las líneas
37-41 y 44-47 no retornan referencias y eso significa
que el subárbol de D
no formará parte del árbol de
análisis. En vez de ello las acciones tienen por efecto
almacenar el tipo en la entrada de la tabla de símbolos
asociada con la variable.
Cuando en la parte derecha de una regla de producción un símbolo
va precedido de un dolar como ocurre en el ejemplo con ID
:
36 D : %name INTDEC 37 'int' $ID 38 { 39 die "Error: $ID declared twice\n" if exists($s{$ID}); 40 $s{$ID} = 'int'; 41 }
le estamos indicamos a eyapp que construya una copia en una variable léxica $ID
del atributo asociado con ese símbolo. Así el fragmento anterior es equivalente
a escribir:
D : %name INTDEC 'int' ID { my $ID = $_[2]; die "Error: $ID declared twice\n" if exists($s{$ID}); $s{$ID} = 'int'; }
Nótese que la tabla de símbolos es una variable léxica. Para evitar su pérdida cuando termine la fase de análisis sintáctico se guarda como un atributo del nodo programa:
10 P: 11 %name PROG 12 $DS $SS 13 { 14 print Dumper($DS); 15 $SS->{symboltable} = \%s; 16 return $SS; 17 } 18 ;
Si no se desea que el nombre del atributo coincida con el nombre de la variable se utiliza la notación punto. Por ejemplo:
exp : exp.left '+' exp.right { $left + $right } ;
es equivalente a:
exp : exp.left '+' exp.right { my $left = $_[1]; my $right = $_[2]; $left + $right } ;
Si queremos ejecutar ciertas tareas antes de la construcción de
un nodo tendremos que insertar una acción semántica para las mismas.
Para evitar la pérdida del subárbol deberemos llamar explícitamente
a Parse::Eyapp::Driver::YYBuildAST
. Este es el caso
cuando se visitan los nodos sentencia (S
):
50 S: %name EXP 51 $ID 52 { 53 die "Error: $ID not declared\n" unless exists($s{$ID}); 54 goto &Parse::Eyapp::Driver::YYBuildAST; # build the node 55 } 56 ;
Veamos el programa cliente:
pl@nereida:~/LEyapp/examples$ cat -n useretundef.pl 1 #!/usr/bin/perl -w 2 use strict; 3 use Parse::Eyapp; 4 use RetUndef; 5 use Data::Dumper; 6 7 sub TERMINAL::info { $_[0]{attr} } 8 9 my $parser = RetUndef->new(); 10 my $t = $parser->Run; 11 print $t->str,"\n"; 12 13 $Data::Dumper::Indent = 1; 14 print Dumper($t);Observe como no hay nodos de tipo
D
al ejecutar el
programa con entrada int a string b a b
:
pl@nereida:~/LEyapp/examples$ useretundef.pl int a string b a b $VAR1 = undef; STS(ST(EXP(TERMINAL[a])),EXP(TERMINAL[b]))El volcado de
Dumper
en la línea 14 de RetUndef.eyp
nos muestra que el atributo asociado con DS
es undef
.
El volcado de Dumper
en la línea 14 de useretundef.pl
da el siguiente resultado:
$VAR1 = bless( { 'symboltable' => { 'a' => 'int', 'b' => 'string' }, 'children' => [ bless( { 'children' => [ bless( { 'children' => [ bless( { 'children' => [], 'attr' => 'a', 'token' => 'ID' }, 'TERMINAL' ) ] }, 'EXP' ) ] }, 'ST' ), bless( { 'children' => [ bless( { 'children' => [], 'attr' => 'b', 'token' => 'ID' }, 'TERMINAL' ) ] }, 'EXP' ) ] }, 'STS' );