La Estructura de Datos Generada por YappParse.yp

El fichero YappParse.yp. contiene la gramática yapp del lenguaje yacc35.1. Además de las dos rutinas de soporte típicas, la de tratamiento de errores _Error y la de análisis léxico _Lexer, el fichero contiene una subrutina para el manejo de las reglas _AddRules y otra rutina Parse la cuál actúa como wrapper o filtro sobre el analizador YYParse.

Durante el análisis sintáctico de un programa yapp se construye una estructura de datos para la posterior manipulación y tratamiento de la gramática. Como ejemplo usaremosla gramática:

pl@nereida:~/src/perl/Parse-AutoTree/trunk/scripts$ cat -n int.yp
 1  %right '+'
 2  %left 'a'
 3  %nonassoc 'b'
 4  %%
 5  S:   /* empty rule */                       { print "S -> epsilon\n" }
 6   |   'a' { print "Intermediate\n"; } S 'b'  { print "S -> a S b\n" }
 7   |   '+' S '+' %prec 'a'                    { print "S -> + S + prec a\n" }
 8  ;
 9  %%
10
11  sub _Error {
12          exists $_[0]->YYData->{ERRMSG}
13      and do {
14          print $_[0]->YYData->{ERRMSG};
15          delete $_[0]->YYData->{ERRMSG};
16          return;
17      };
18      print "Syntax error.\n";
19  }
20
21  sub _Lexer {
22      my($parser)=shift;
23
24          defined($parser->YYData->{INPUT})
25      or  $parser->YYData->{INPUT} = <STDIN>
26      or  return('',undef);
27
28      $parser->YYData->{INPUT}=~s/^[ \t\n]//;
29
30      for ($parser->YYData->{INPUT}) {
31          s/^(.)//s and return($1,$1);
32      }
33  }
34
35  sub Run {
36      my($self)=shift;
37      $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error, yydebug => 0x1F );
38  }
Para construir la estructura podemos usar la siguiente subrutina:
sub Parse {
  my $grammar = shift;

  my $x = new Parse::Yapp::Parse;
  my $r = $x->Parse($grammar);

  return $r;
}
La llamada a Parse produce la siguiente estructura de datos:
nereida:~/src/perl/Parse-AutoTree/trunk/scripts> grammar.pl int.yp
$VAR1 = {
  'START' => 'S', # Símbolo de arranque
  'SYMS' => { 'S' => 5, 'b' => 3, 'a' => 2, '+' => 1 }, # Símbolo => línea
  'TERM' => { 
    'b' => [ 'NONASSOC', 2 ],  # terminal => [ Asociatividad, precedencia ]
    'a' => [ 'LEFT', 1 ],      # terminal => [ Asociatividad, precedencia ]
    '+' => [ 'RIGHT', 0 ] },   # terminal => [ Asociatividad, precedencia ]
    # Si el terminal no tiene precedencia toma la forma  terminal => []
  'NTERM' => { 'S' => [ '1', '3', '4' ] }, # variable => [ indice en RULES de las reglas de S ]
  'PREC' => { 'a' => 1 }, # Terminales que son usandos en una directiva %prec
  'NULL' => { 'S' => 1 }, # Variables que producen vacío
  'EXPECT' => 0, # Número de conflictos esperados
  'RULES' => [
     [ '$start', [ 'S', '' ], undef, undef ], # Regla de superarranque
     [
       'S', [], # producción
       undef,   # precedencia explícita de la regla
       [ ' print "S -> epsilon\n" ', 5 ] # [ acción asociada, línea ]
     ],
     [
       '@1-1', [], # Regla intermedia: Variable temporal
       undef,
       [ ' print "Intermediate\n"; ', 6 ]
     ],
     [
       'S', [ 'a', '@1-1', 'S', 'b' ],
       undef,
       [ ' print "S -> a S b\n" ', 6 ]
     ],
     [
       'S', [ '+', 'S', '+' ],
       1, # precedencia explícita de la regla
       [ ' print "S -> + S + prec a\n" ', 7 ]
     ]
   ],
  'HEAD' => undef, # Código de cabecera 
  'TAIL' => [ '... código de cola ...', 9 ], # Línea en la que comienza la sección de cola
};

Las componentes del hash que aparece arriba se corresponden con diversas variables usadas por YYParse durante el análisis. La correspondencia se establece dentro del método Parse cuando se hace la asignación:

   @$parsed{ 'HEAD', 'TAIL', 'RULES', 'NTERM', 'TERM',
              'NULL', 'PREC', 'SYMS',  'START', 'EXPECT' }
    =       (  $head,  $tail,  $rules,  $nterm,  $term,
               $nullable, $precterm, $syms, $start, $expect);
esta asignación es la que crea el hash. Las variables con identificadores en minúsculas son usadas en el analizador. Son visibles en todo el fichero ya que, aunque declaradas léxicas, su declaración se encuentra en la cabecera del analizador:
%{
require 5.004;

use Carp;

my($input,$lexlevel,@lineno,$nberr,$prec,$labelno);
my($syms,$head,$tail,$token,$term,$nterm,$rules,$precterm,$start,$nullable);
my($expect);

%}

Casiano Rodríguez León
2013-04-23