next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Transformaciones Árbol con Parse::Eyapp Sup: Análisis Semántico con Parse::Eyapp Ant: El módulo Language::AttributeGrammar Err: Si hallas una errata ...


Usando Language::AttributeGrammars con Parse::Eyapp

La cláusula alias de %tree

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.

Redefiniendo el acceso a los hijos

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);

Efectos Laterales

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.



Subsecciones
next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Transformaciones Árbol con Parse::Eyapp Sup: Análisis Semántico con Parse::Eyapp Ant: El módulo Language::AttributeGrammar Err: Si hallas una errata ...
Casiano Rodríguez León
2013-03-05