En eyapp
toda regla de producción tiene un nombre.
El nombre de una regla puede ser dado explícitamente por el programador
mediante la directiva %name
.
Por ejemplo, en el fragmento de código que sigue se da el nombre
ASSIGN
a la regla exp: VAR '=' exp
.
Si no se da un nombre explícito, la regla tendrá un nombre implícito.
El nombre implícito de una regla se forma concatenando el nombre de la
variable sintáctica en la parte izquierda con un subguión y el número de orden de la
regla de producción: Lhs_#
. Evite dar nombres con ese patrón
a sus reglas de producción. Los patrones de la forma
/${lhs}_\d+$/
donde ${lhs}
es el nombre de la variable sintáctica
estan reservados por eyapp
.
pl@nereida:~/LEyapp/examples$ cat -n Lhs.eyp 1 # Lhs.eyp 2 3 %right '=' 4 %left '-' '+' 5 %left '*' '/' 6 %left NEG 7 8 %defaultaction { 9 my $self = shift; 10 my $name = $self->YYName(); 11 bless { children => [ grep {ref($_)} @_] }, $name; 12 } 13 14 %% 15 input: 16 /* empty */ 17 { [] } 18 | input line 19 { 20 push @{$_[1]}, $_[2] if defined($_[2]); 21 $_[1] 22 } 23 ; 24 25 line: '\n' { } 26 | exp '\n' { $_[1] } 27 ; 28 29 exp: 30 NUM { $_[1] } 31 | VAR { $_[1] } 32 | %name ASSIGN 33 VAR '=' exp 34 | %name PLUS 35 exp '+' exp 36 | %name MINUS 37 exp '-' exp 38 | %name TIMES 39 exp '*' exp 40 | %name DIV 41 exp '/' exp 42 | %name UMINUS 43 '-' exp %prec NEG 44 | '(' exp ')' { $_[2] } 45 ;
Dentro de una acción semántica el nombre de la regla actual puede ser recuperado mediante el método YYName del analizador sintáctico.
La acción por defecto (lineas 8-12) computa como atributo de la parte izquierda
una referencia a un objeto que tiene como clase el nombre de la regla.
Ese objeto tiene un atributo children
que es una referencia a
la lista de hijos del nodo.
La llamada a grep
11 bless { children => [ grep {ref($_)} @_] }, $name;tiene como efecto obviar (no incluir) aquellos hijos que no sean referencias. Observe que el analizador léxico sólo retorna referencias a objetos para los terminales
NUM
y VAR
.
47 %% 48 49 sub _Error { 50 exists $_[0]->YYData->{ERRMSG} 51 and do { 52 print $_[0]->YYData->{ERRMSG}; 53 delete $_[0]->YYData->{ERRMSG}; 54 return; 55 }; 56 print "Syntax error.\n"; 57 } 58 59 sub _Lexer { 60 my($parser)=shift; 61 62 for ($parser->YYData->{INPUT}) { 63 s/^[ \t]+//; 64 return('',undef) unless $_; 65 s/^([0-9]+(?:\.[0-9]+)?)// 66 and return('NUM', bless { attr => $1}, 'NUM'); 67 s/^([A-Za-z][A-Za-z0-9_]*)// 68 and return('VAR',bless {attr => $1}, 'VAR'); 69 s/^(.)//s 70 and return($1, $1); 71 } 72 return('',undef); 73 } 74 75 sub Run { 76 my($self)=shift; 77 78 $self->YYData->{INPUT} = <>; 79 return $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error ); 80 }
Para los terminales NUM
y VAR
el analizador léxico
devuelve una referencia a un objeto.
No así para el resto de los terminales.
pl@nereida:~/LEyapp/examples$ cat -n uselhs.pl 1 #!/usr/bin/perl -w 2 use Lhs; 3 use Data::Dumper; 4 5 $parser = new Lhs(); 6 my $tree = $parser->Run; 7 $Data::Dumper::Indent = 1; 8 if (defined($tree)) { print Dumper($tree); } 9 else { print "Cadena no válida\n"; }
Al darle la entrada a=(2+3)*b
el analizador produce el
árbol
ASSIGN(TIMES(PLUS(NUM[2],NUM[3]), VAR[b]))
Veamos el resultado de una ejecución:
pl@nereida:~/LEyapp/examples$ uselhs.pl a=(2+3)*b $VAR1 = [ bless( { 'children' => [ bless( { 'attr' => 'a' }, 'VAR' ), bless( { 'children' => [ bless( { 'children' => [ bless( { 'attr' => '2' }, 'NUM' ), bless( { 'attr' => '3' }, 'NUM' ) ] }, 'PLUS' ), bless( { 'attr' => 'b' }, 'VAR' ) ] }, 'TIMES' ) ] }, 'ASSIGN' ) ];
Es posible cambiar en tiempo de ejecución el nombre de una regla. Observe la siguiente variante del ejemplo anterior en el que se cambia - en tiempo de ejecución - el nombre de la regla del menos binario.
29 exp: 30 NUM { $_[1] } 31 | VAR { $_[1] } 32 | %name ASSIGN 33 VAR '=' exp 34 | %name PLUS 35 exp '+' exp 36 | %name MINUS 37 exp '-' exp 38 { 39 my $self = shift; 40 $self->YYName('SUBSTRACT'); # rename it 41 $self->YYBuildAST(@_); # build the node 42 } 43 | %name TIMES 44 exp '*' exp 45 | %name DIV 46 exp '/' exp 47 | %name UMINUS 48 '-' exp %prec NEG 49 | '(' exp ')' { $_[2] } 50 ;
Al ejecutar el cliente observamos como los nodos han sido bendecidos en
la clase SUBSTRACT
:
pl@nereida:~/LEyapp/examples$ useyynamedynamic.pl 2-b $VAR1 = [ bless( { 'children' => [ bless( { 'attr' => '2' }, 'NUM' ), bless( { 'attr' => 'b' }, 'VAR' ) ] }, 'SUBSTRACT' ) ];