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 = -10Obsé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 = 4El 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
.