El fichero YappParse.yp
.
contiene la gramática yapp
del lenguaje
yacc
7.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); %}