

El requerimiento de Language::AttributeGrammars
de que los hijos de un nodo puedan ser accedidos
a través de un método cuyo nombre sea el nombre del hijo
no encaja con la forma oficial de acceso
a los hijos que usa Parse::Eyapp.
La directiva %tree de Parse::Eyapp dispone
de la opción alias la cual hace que los identificadores
que aparecen en los usos de la notación punto y
de la notación dolar se interpreten como nombres de métodos
que proveen acceso al hijo correspondiente
(véase la sección
8.16)
nereida:~/src/perl/attributegrammar/Language-AttributeGrammar-0.08/examples> cat -n Calc.pl
1 #!/usr/bin/perl -w
2 use strict;
3 use Parse::Eyapp;
4 use Data::Dumper;
5 use Language::AttributeGrammar;
6
7 my $grammar = q{
.. ............... # precedence declarations
!15 %tree bypass alias
16
17 %%
18 line: $exp { $_[1] }
19 ;
20
21 exp:
22 %name NUM
23 $NUM
24 | %name VAR
25 $VAR
26 | %name ASSIGN
27 $VAR '=' $exp
28 | %name PLUS
29 exp.left '+' exp.right
30 | %name MINUS
31 exp.left '-' exp.right
32 | %name TIMES
33 exp.left '*' exp.right
34 | %name DIV
35 exp.left '/' exp.right
!36 | %no bypass UMINUS
37 '-' $exp %prec NEG
38 | '(' $exp ')' { $_[2] } /* Let us simplify a bit the tree */
39 ;
40
41 %%
.. ................ # as usual
78 }; # end grammar
79
80
81 $Data::Dumper::Indent = 1;
82 Parse::Eyapp->new_grammar(
83 input=>$grammar,
84 classname=>'Rule6',
85 firstline =>7,
86 outputfile => 'Calc.pm',
87 );
88 my $parser = Rule6->new();
89 $parser->YYData->{INPUT} = "a = -(2*3+5-1)\n";
90 my $t = $parser->Run;
91 print "\n***** Before ******\n";
92 print Dumper($t);
93
! 94 my $attgram = new Language::AttributeGrammar <<'EOG';
! 95
! 96 # Compute the expression
! 97 NUM: $/.val = { $<attr> }
! 98 TIMES: $/.val = { $<left>.val * $<right>.val }
! 99 PLUS: $/.val = { $<left>.val + $<right>.val }
!100 MINUS: $/.val = { $<left>.val - $<right>.val }
!101 UMINUS: $/.val = { -$<exp>.val }
!102 ASSIGN: $/.val = { $<exp>.val }
!103 EOG
!104
!105 sub NUM::attr {
!106 $_[0]->{attr};
!107 }
108
!109 my $res = $attgram->apply($t, 'val');
110
111 $Data::Dumper::Indent = 1;
112 print "\n***** After ******\n";
113 print Dumper($t);
114 print Dumper($res);
En la línea 36 hemos usado la directiva %no bypass para evitar el
puenteo automático del menos unario que causa la clausula bypass
que cualifica a la directiva %tree de la línea 15
(repase la sección 8.15).
El método NUM::attr en las
líneas 105-107 no es necesario y podría haber sido obviado.
Es posible evitar el uso de la opción alias y forzar a Language::AttributeGrammar a acceder a los hijos de un nodo Parse::Eyapp::Node redefiniendo el método Language::AttributeGrammar::Parser::_get_child el cual es usado por Language::AttributeGrammar cuando quiere acceder a un atributo de un nodo.
pl@nereida:~/src/perl/attributegrammar/Language-AttributeGrammar-0.08/examples$ \
cat -n CalcNumbers4.pl
1 #!/usr/bin/perl -w
2 use strict;
3 use warnings;
4 use Parse::Eyapp;
5 use Parse::Eyapp::Base qw{push_method pop_method};
6 use Data::Dumper;
7 use Language::AttributeGrammar;
8
9 my $grammar = q{
10 %right '='
11 %left '-' '+'
12 %left '*' '/'
13 %left NEG
14 %tree bypass
15
16 %%
17 line: $exp { $_[1] }
18 ;
19
20 exp:
21 %name NUM
22 $NUM
23 | %name VAR
24 $VAR
25 | %name ASSIGN
26 $var '=' $exp
27 | %name PLUS
28 exp.left '+' exp.right
29 | %name MINUS
30 exp.left '-' exp.right
31 | %name TIMES
32 exp.left '*' exp.right
33 | %name DIV
34 exp.left '/' exp.right
35 | %no bypass UMINUS
36 '-' $exp %prec NEG
37 | '(' $exp ')' { $_[2] } /* Let us simplify a bit the tree */
38 ;
39
40 var: %name VAR
41 VAR
42 ;
43 %%
44
.. ... # tail as usual
80 }; # end grammar
81
82
83 $Data::Dumper::Indent = 1;
84 Parse::Eyapp->new_grammar(input=>$grammar, classname=>'SmallCalc' );
85 my $parser = SmallCalc->new();
86 $parser->YYData->{INPUT} = "a = -(2*3+ b = 5-1)\n";
87 my $t = $parser->Run;
88
89 print "\n***** Syntax Tree ******\n";
90 push_method(qw{NUM VAR}, info => sub { $_->{attr} });
91 print $t->str;
92
93 #symbol table
94 our %s;
95
96 my $attgram = new Language::AttributeGrammar <<'EOG';
97
98 # Compute the expression
99 NUM: $/.val = { $/->{attr} }
100 TIMES: $/.val = { $<0>.val * $<1>.val }
101 PLUS: $/.val = { $<0>.val + $<1>.val }
102 MINUS: $/.val = { $<0>.val - $<1>.val }
103 UMINUS: $/.val = { -$<0>.val }
104 ASSIGN: $/.val = { $<1>.val }
105 ASSIGN: $/.entry = { $::s{$<0>->{attr}} = $<1>.val; $<0>->{attr} }
106 EOG
107
108 # rewrite _get_child, save old version
109 push_method(
110 'Language::AttributeGrammar::Parser',
111 _get_child => sub {
112 my ($self, $child) = @_;
113
114 $self->child($child);
115 }
116 );
117
118 my $res = $attgram->apply($t, 'entry');
119
120 # Restore old version of Language::AttributeGrammar::Parser::_get_child
121 pop_method('Language::AttributeGrammar::Parser', '_get_child');
122
123 $Data::Dumper::Indent = 1;
124 print "\n***** After call to apply******\n";
125 push_method(qw{TIMES PLUS MINUS UMINUS ASSIGN},
126 info => sub { defined($_->{val})? $_->{val} : "" }
127 );
128 print $t->str."\n"; # Tree $t isn't modified nor decorated
129
130 #The results are held in the symbol table
131 print "$_ = $s{$_}\n" for keys(%s);
Cuando se ejecuta este programa el volcado de la tabla de símbolos se produce la salida:
pl@nereida:~/src/perl/attributegrammar/Language-AttributeGrammar-0.08/examples$ \
CalcNumbers4.pl
***** Syntax Tree ******
ASSIGN(VAR[a],UMINUS(PLUS(TIMES(NUM[2],NUM[3]),ASSIGN(VAR[b],MINUS(NUM[5],NUM[1])))))
***** After call to apply******
ASSIGN(VAR[a],UMINUS(PLUS(TIMES(NUM[2],NUM[3]),ASSIGN(VAR[b],MINUS(NUM[5],NUM[1])))))
a = -10
Obsérvese que el árbol original no ha cambiado después de la llamada a
apply.
Teniendo en cuenta que la entrada era a = -(2*3+b=5-1)\n puede sorprender
que b no haya sido introducido en la tabla de símbolos.
La razón para la ausencia de b en la tabla de símbolos reside en
el hecho de que el atributo entry para el nodo raíz del
árbol asociado con b=5-1 nunca es evaluado, ya que no existe un camino
de dependencias desde el atributo solicitado en la llamada a apply
hasta el mismo. El problema se arregla creando una dependencia
que de lugar a la conectividad deseada en el grafo de dependencias
y provoque la ejecución del código de
inserción en la tabla de símbolos como un efecto lateral.
nereida:~/src/perl/attributegrammar/Language-AttributeGrammar-0.08/examples> \
sed -ne '108,119p' CalcNumbers3.pl | cat -n
1 my $attgram = new Language::AttributeGrammar <<'EOG';
2
3 # Compute the expression
4 NUM: $/.val = { $/->{attr} }
5 TIMES: $/.val = { $<0>.val * $<1>.val }
6 PLUS: $/.val = { $<0>.val + $<1>.val }
7 MINUS: $/.val = { $<0>.val - $<1>.val }
8 UMINUS: $/.val = { -$<0>.val }
! 9 ASSIGN: $/.val = { $::s{$<0>->{attr}} = $<1>.val; $<1>.val }
10 EOG
11
12 my $res = $attgram->apply($t, 'val');
nereida:~/src/perl/attributegrammar/Language-AttributeGrammar-0.08/examples> \
CalcNumbers3.pl
a = -10
b = 4
El código de la línea 9 indica que para calcular el atributo val de un nodo
de tipo ASSIGN es necesario no sólo calcular el atributo val del
segundo hijo ($<1>.val) sino tambien ejecutar el código de inserción
$::s{$<0>->{attr}} = $<1>.val.

