

En Parse::Eyapp las acciones semánticas son convertidas en subrutinas
anónimas. Mas bien en métodos anónimos dado que el primer argumento ($_[0])
de toda acción semántica es una referencia al objeto analizador sintáctico.
Los restantes parámetros
se corresponden con los atributos de los símbolos
en la parte derecha de la regla de producción ($_[1] ...).
Esto es, si la regla es:
{ action(@_) }
cuando se ''reduzca'' por la regla
la acción será llamada con argumentos
action(parserref, $X_1, $X_2, ..., $X_n) |
donde $X_1, $X_2, etc. denotan los atributos de
,
, etc.
y parserref es una referencia al objeto analizador sintáctico.
El valor retornado por la acción semántica es asignado como valor del atributo
del lado izquierdo de la regla
.
Cuando una regla
no tiene acción semántica asociada se ejecuta la acción por defecto.
La acción por defecto en eyapp es { $_[1] }:
{ $_[1] }
El efecto de tal acción
es asignar al atributo de la variable sintáctica en la parte izquierda
el atributo del primer símbolo en la parte derecha. Si la parte derecha
fuera vacía se asigna undef.
La directiva %defaultaction permite especificar una acción por defecto
alternativa. Observe esta nueva versión del programa anterior:
pl@nereida:~/LEyapp/examples$ cat -n CalcSyntaxDefaultWithHOPLexer.eyp
1 # CalcSyntaxDefaultWithHOPLexer.eyp
2 %right '='
3 %left '-' '+'
4 %left '*' '/'
5 %left NEG
6 %right '^'
7
8 %defaultaction {
9 my $parser = shift;
10
11 print $parser->YYLhs." --> ";
12 my $i = 0;
13 for ($parser->YYRightside) {
14 print "$_";
15 print "[$_[$i]]" if /NUM|VAR/;
16 print " ";
17 $i++;
18 }
19 print "\n";
20 }
21
22 %{
23 use HOP::Lexer qw(string_lexer);
24 %}
25 %%
26 line: (exp '\n')*
27 ;
28
29 exp:
30 NUM
31 | VAR
32 | VAR '=' exp
33 | exp '+' exp
34 | exp '-' exp
35 | exp '*' exp
36 | exp '/' exp
37 | '-' exp %prec NEG
38 | exp '^' exp
39 | '(' exp ')'
40 ;
El método YYLhs (línea 11) retorna el nombre de la variable sintáctica asociada con la regla actual. El método YYRightside (línea 13) devuelve la lista de nombres de los símbolos de la parte derecha de la regla de producción actual.
Observe como la modificación de la acción con defecto nos permite eliminar todas las acciones del cuerpo de la gramática.
Al ejecutar el cliente obtenemos una salida similar a la del programa anterior:
pl@nereida:~/LEyapp/examples$ eyapp CalcSyntaxDefaultWithHOPLexer.eyp
pl@nereida:~/LEyapp/examples$ cat -n prueba4.exp
1 4*3
pl@nereida:~/LEyapp/examples$ usecalcsyntaxdefaultwithhoplexer.pl prueba4.exp | cat -n
1 exp --> NUM[4]
2 exp --> NUM[3]
3 exp --> exp * exp
4 line --> STAR-2
Posiblemente llame su atención la línea line --> STAR-2 en la salida.
Es consecuencia de la existencia de la línea 26 que contiene una aplicación
del operador de repetición * a una expresión entre paréntesis:
26 line: (exp '\n')*De hecho la gramática anterior es desplegada como se indica en el fichero
CalcSyntaxDefaultWithHOPLexer.output
obtenido al compilar usando la opción -v de eyapp:
pl@nereida:~/LEyapp/examples$ eyapp -v CalcSyntaxDefaultWithHOPLexer.eyp
pl@nereida:~/LEyapp/examples$ ls -ltr | tail -1
-rw-r--r-- 1 pl users 8363 2007-10-30 11:45 CalcSyntaxDefaultWithHOPLexer.output
pl@nereida:~/LEyapp/examples$ sed -ne '/Rules/,/^$/p' CalcSyntaxDefaultWithHOPLexer.output
Rules:
------
0: $start -> line $end
1: PAREN-1 -> exp '\n'
2: STAR-2 -> STAR-2 PAREN-1
3: STAR-2 -> /* empty */
4: line -> STAR-2
5: exp -> NUM
6: exp -> VAR
7: exp -> VAR '=' exp
8: exp -> exp '+' exp
9: exp -> exp '-' exp
10: exp -> exp '*' exp
11: exp -> exp '/' exp
12: exp -> '-' exp
13: exp -> exp '^' exp
14: exp -> '(' exp ')'
En esta ocasión hemos elegido un método alternativo para construir el analizador léxico: Usar el módulo HOP::Lexer.
El módulo HOP::Lexer
es cargado en las líneas 22-24. Los delimitadores %{
y %} permiten introducir código Perl en cualquier
lugar de la sección de cabecera.
Estudie la documentación del módulo HOP::Lexer
y compare la solución que sigue con la dada
en la sección anterior ( véase 8.2).
Este es el código en la sección de cola:
42 %%
43
44 sub _Error {
.. ..................................
52 }
53
54 sub Run {
55 my($self)=shift;
56 my $input = shift;
57
58 my @lexemes = (
59 ['SPACE', qr{[ \t]+}, sub { () } ],
60 ['NUM', qr{[0-9]+(?:\.[0-9]+)?}],
61 ['VAR', qr{[A-Za-z][A-Za-z0-9_]*}],
62 ["\n", qr{\n}],
63 ['ANY', qr{.},
64 sub {
65 my ($label, $val) = @_;
66 return [ $val, $val ];
67 }
68 ]
69
70 );
71 my $lexer = string_lexer($input, @lexemes);
72
73 return $self->YYParse(
74 yylex => sub {
75 my $parser = shift;
76 my $token = $lexer->();
77 return @$token if defined($token);
78 return ('', undef);
79 },
80 yyerror => \&_Error,
81 #yydebug => 0xF
82 );
83 }
Cuando YYParse se llama con la opción yydebug activada
(descomente la línea 81 si quiere ver el efecto) la ejecución
del analizador produce un informe detallado (version de eyapp 0.83 o superior)
de su avance durante el análisis:
pl@nereida:~/LEyapp/examples$ usecalcsyntaxdefaultwithhoplexer.pl prueba4.exp >& warn pl@nereida:~/LEyapp/examples$ head -30 warn ---------------------------------------- In state 0: Stack:[0] Don't need token. Reduce using rule 3 (STAR-2 --> /* empty */): Back to state 0, then go to state 1. ---------------------------------------- In state 1: Stack:[0,1] Need token. Got >NUM< Shift and go to state 4. ---------------------------------------- In state 4: Stack:[0,1,4] Don't need token. Reduce using rule 5 (exp --> NUM): Back to state 1, then go to state 5. ---------------------------------------- In state 5: Stack:[0,1,5] Need token. Got >*< Shift and go to state 13. ---------------------------------------- In state 13: Stack:[0,1,5,13] Need token. Got >NUM< Shift and go to state 4. ---------------------------------------- In state 4: Stack:[0,1,5,13,4] Don't need token. Reduce using rule 5 (exp --> NUM): Back to state 13, then go to state 21.

