La directiva %tree permite que Parse::Eyapp
genere al árbol sintáctico. Haciendo uso de las directivas
adecuadas podemos controlar la forma del árbol. La directiva
%tree
es una alternativa a la directiva %metatree
y es incompatible con esta última. El objetivo es lograr
una representación adecuada del árbol sintáctico y dejar
para fases posteriores la decoración del mismo.
nereida:~/src/perl/YappWithDefaultAction/examples> cat -n Rule6.yp 1 %{ 2 use Data::Dumper; 3 %} 4 %right '=' 5 %left '-' '+' 6 %left '*' '/' 7 %left NEG 8 %tree 9 10 %% 11 line: exp { $_[1] } 12 ; 13 14 exp: %name NUM 15 NUM 16 | %name VAR 17 VAR 18 | %name ASSIGN 19 VAR '=' exp 20 | %name PLUS 21 exp '+' exp 22 | %name MINUS 23 exp '-' exp 24 | %name TIMES 25 exp '*' exp 26 | %name DIV 27 exp '/' exp 28 | %name UMINUS 29 '-' exp %prec NEG 30 | '(' exp ')' { $_[2] } /* Let us simplify a bit the tree */ 31 ; 32 33 %% 34 35 sub _Error { .. .......... 43 } 44 45 sub _Lexer { .. .......... 62 } 63 64 sub Run { 65 my($self)=shift; 66 $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error, 67 #yyprefix => "Rule6::", 68 #yydebug =>0xFF 69 ); 70 }
Para compilar un programa separado Parse::Eyapp usamos el guión eyapp :
nereida:~/src/perl/YappWithDefaultAction/examples> eyapp Rule6 nereida:~/src/perl/YappWithDefaultAction/examples> ls -ltr | tail -1 -rw-rw---- 1 pl users 5475 2006-11-06 13:53 Rule6.pm
A diferencia de lo que ocurre cuando se usa la directiva %metatree
la construcción del árbol solicitado mediante la directiva %tree
implícitamente considera token sintácticos aquellos terminales que aparecen en
definidos en el programa eyapp
mediante el uso de apóstrofes.
Los token sintácticos no forman parte del árbol construido.
Asi pues -en el ejemplo que nos ocupa - los terminales '='
,
'-'
,
'+'
,
'*'
y
'/'
serán -por defecto - eliminados del árbol sintáctico.
Por ejemplo, para la entrada a=b*32
el siguiente árbol es construido:
nereida:~/src/perl/YappWithDefaultAction/examples> useruleandshift.pl a=b*32 $VAR1 = bless( { 'children' => [ bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' ), bless( { 'children' => [ bless( { 'children' => [ bless( { 'children' => [], 'attr' => 'b', 'token' => 'VAR' }, 'TERMINAL' ) ] }, 'VAR' ), bless( { 'children' => [ bless( { 'children' => [], 'attr' => '32', 'token' => 'NUM' }, 'TERMINAL' ) ] }, 'NUM' ) ] }, 'TIMES' ) ] }, 'ASSIGN' );Esta conducta puede cambiarse usando la directiva %semantic token la cual declara una lista de terminales como semánticos. En tal caso dichos terminales formarán parte del árbol construido. Si por el contrario lo que queremos es cambiar el estatus de un terminal - por ejemplo
NUM
o ID
- a sintáctico usaremos
la directiva %syntactic token .
Si bien la funcionalidad de un %syntactic token es determinar la forma del AST, en ocasiones la diferencia entre terminal sintáctico y semántico es difusa. Un terminal fundamentalmente sintáctico pueden acarrear alguna información útil para las restantes fases de procesado.
Por ejemplo, el atributo número de línea asociado con
el terminal sintáctico '+'
puede ser útil posteriormente:
Si en la fase de comprobación de tipos se observa un error
de tipos incompatibles con el operador, disponer de la línea
asociada con '+'
nos permitirá emitir mensajes de error
mas precisos.
En Parse::Eyapp
el programador puede proveer a la clase
TERMINAL de un método
el cuál será ejecutado durante la construcción del AST cada vez
que un syntaxtic token
es eliminado. El método
recibe como argumento - además de la referencia al nodo terminal -
una referencia al nodo padre del terminal. Sigue un ejemplo:
sub TERMINAL::save_attributes { # $_[0] is a syntactic terminal # $_[1] is the father. push @{$_[1]->{lines}}, $_[0]->[1]; # save the line! }
La tabla 8.1 muestra como el nodo PLUS recoge la información del número de línea del terminal +.
El árbol fué generado usando el método str
. La información
sobre los atributos se hizo mediante la siguiente subrutina:
sub generic_info { my $info; $info = ""; $info .= $_[0]->type_info if $_[0]->can('type_info'); my $sep = $info?":":""; $info .= $sep.$_[0]->{line} if (defined $_[0]->{line}); local $" = ','; $info .= "$sep@{$_[0]->{lines}}" if (defined $_[0]->{lines}); return $info; } *PLUS::info = =\&generic_info;
Programa y Footnotes | AST generado por $t->str |
int f(int a, int b) { return a+b; } -------footnotes----------- 0) Symbol Table: $VAR1 = { 'f' => { 'type' => 'F(X_2(INT,INT),INT)', 'line' => 1 } }; --------------------------- 1) etc. |
PROGRAM^{0}( FUNCTION[f]^{1}( RETURN[2,2]( PLUS[INT:2]( VAR[INT]( TERMINAL[a:2] ), VAR[INT]( TERMINAL[b:2] ) ) # PLUS ) # RETURN ) # FUNCTION ) # PROGRAM |