

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
|

