eyapp
esta inmerso
en la construcción de la antiderivación a derechas y que la forma sentencial
derecha en ese momento es:
y que el mango es y en la entrada quedan por procesar .
Es posible acceder en eyapp
a los valores de los atributos de los estados en la pila
del analizador que se encuentran ``por debajo'' o si se quiere
``a la izquierda'' de los estados asociados
con la regla por la que se reduce. Para ello se usa una llamada al método
YYSemval. La llamada es de la forma
$_[0]->YYSemval( index )
, donde index
es un entero.
Cuando se usan los valores 1
...n
devuelve lo mismo
que $_[1]
, ...$_[n]
. Esto es
$_[1]
es el atributo asociado con
y $_[n]
es el atributo
asociado con
. Cuando se usa con el valor
0 devolverá el valor del atributo asociado con el símbolo que esta a la izquierda
del mango actual, esto es el atributo asociado con
,
si se llama con -1 el que está dos unidades a la izquierda de la variable actual,
esto es, el asociado con
etc. Así $_[-m]
denota el atributo
de
.
Esta forma de acceder a los atributos es especialmente útil cuando se trabaja con atributos heredados. Esto es, cuando un atributo de un nodo del árbol sintáctico se computa en términos de valores de atributos de su padre y/o sus hermanos. Ejemplos de atributos heredados son la clase y tipo en la declaración de variables. Supongamos que tenemos el siguiente esquema de traducción para calcular la clase (C) y tipo (T) en las declaraciones (D) de listas (L) de identificadores:
1 | D | C T { $L{c} = $C{c}; $L{t} = $T{t} } L |
2 | C | global { $C{c} = "global" } |
3 | C | local { $C{c} = "local" } |
4 | T | integer { $T{t} = "integer" } |
5 | T | float { $T{t} = "float" } |
6 | L | { $L
{t} = $L{t}; $L
{c} = $L{c}; } L
',' |
7 | id { set_class($id{v}, $L{c}); set_type($id{v}, $L{t}); } |
|
8 | L | id { set_class($id{v}, $L{c}); set_type($id{v}, $L{t}); } |
Los atributos c
y t
denotan respectivamente
la clase y el tipo.
global float x,y
. Represente el árbol de análisis, las
acciones incrustadas y determine el orden de ejecución.
Olvide por un momento la notación usada en las acciones y
suponga que se tratara de acciones eyapp
. ¿En que orden
construye eyapp
el árbol y en que orden ejecutará las
acciones?
Observe que la simple razón por la cual la acción intermedia
{ $L
{t} = $L{t}; $L
{c} = $L{c}; }
no puede ser ejecutada en un programa eyapp
es porque
el nodo
del árbol sintáctico asociado con L
no existe
en el momento en el que la acción es ejecutada. No es posible
guardar algo en L
{c}
ya que no hay nodo asociado
con L
.
La situación es
distinta si se esta trabajando con un esquema de traducción
ya que estos primero construyen el árbol y luego lo recorren.
A la hora de transformar este esquema de traducción en un programa
eyapp
es importante darse cuenta que en cualquier derivación a derechas
desde D, cuando se reduce por una de las reglas
el símbolo a la izquierda de L es T y el que esta a la izquierda de T es C. Considere, por ejemplo la derivación a derechas:
Observe que el orden de recorrido de eyapp
es:
en la antiderivación, cuando el mango es una de las dos reglas para listas de identificadores, L id y L L, id es decir durante las tres ultimas antiderivaciones:
las variables a la izquierda del mango son
T y C. Esto ocurre siempre.
Estas observaciones nos conducen al siguiente
programa eyapp
:
$ cat -n Inherited.yp 1 %token FLOAT INTEGER 2 %token GLOBAL 3 %token LOCAL 4 %token NAME 5 6 %% 7 declarationlist 8 : # vacio 9 | declaration ';' declarationlist 10 ; 11 12 declaration 13 : class type namelist { ; } 14 ; 15 16 class 17 : GLOBAL 18 | LOCAL 19 ; 20 21 type 22 : FLOAT 23 | INTEGER 24 ; 25 26 namelist 27 : NAME 28 { printf("%s de clase %s, tipo %s\n", 29 $_[1], $_[0]->YYSemval(-1),$_[0]->YYSemval(0)); } 30 | namelist ',' NAME 31 { printf("%s de clase %s, tipo %s\n", 32 $_[3], $_[0]->YYSemval(-1),$_[0]->YYSemval(0)); } 33 ; 34 %%
A continuación escribimos el programa que usa
el módulo generado por eyapp
:
$ cat -n useinherited.pl 1 #!/usr/bin/perl -w 2 use strict; 3 use Inherited; 4 5 sub Error { 6 exists $_[0]->YYData->{ERRMSG} 7 and do { 8 print $_[0]->YYData->{ERRMSG}; 9 delete $_[0]->YYData->{ERRMSG}; 10 return; 11 }; 12 print "Error sintáctico\n"; 13 } 14 15 { # hagamos una clausura con la entrada 16 my $input; 17 local $/ = undef; 18 print "Entrada (En Unix, presione CTRL-D para terminar):\n"; 19 $input = <stdin>; 20 21 sub scanner { 22 23 { # Con el redo del final hacemos un bucle "infinito" 24 if ($input =~ m|\G\s*INTEGER\b|igc) { 25 return ('INTEGER', 'INTEGER'); 26 } 27 elsif ($input =~ m|\G\s*FLOAT\b|igc) { 28 return ('FLOAT', 'FLOAT'); 29 } 30 elsif ($input =~ m|\G\s*LOCAL\b|igc) { 31 return ('LOCAL', 'LOCAL'); 32 } 33 elsif ($input =~ m|\G\s*GLOBAL\b|igc) { 34 return ('GLOBAL', 'GLOBAL'); 35 } 36 elsif ($input =~ m|\G\s*([a-z_]\w*)\b|igc) { 37 return ('NAME', $1); 38 } 39 elsif ($input =~ m/\G\s*([,;])/gc) { 40 return ($1, $1); 41 } 42 elsif ($input =~ m/\G\s*(.)/gc) { 43 die "Caracter invalido: $1\n"; 44 } 45 else { 46 return ('', undef); # end of file 47 } 48 redo; 49 } 50 } 51 } 52 53 my $debug_level = (@ARGV)? oct(shift @ARGV): 0x1F; 54 my $parser = Inherited->new(); 55 $parser->YYParse( yylex => \&scanner, yyerror => \&Error, yydebug => $debug_level );En las líneas de la 15 a la 51 esta nuestro analizador léxico. La entrada se lee en una variable local cuyo valor permanece entre llamadas: hemos creado una clausura con la variable
$input
(véase la sección
[4] para mas detalles
sobre el uso de clausuras en Perl). Aunque la variable $input
queda inaccesible desde fuera de la clausura, persiste entre llamadas
como consecuencia de que la subrutina scanner
la utiliza.
A continuación sigue un ejemplo de ejecución:
$ ./useinherited.pl 0 Entrada (En Unix, presione CTRL-D para terminar): global integer x, y, z; local float a,b; x de clase GLOBAL, tipo INTEGER y de clase GLOBAL, tipo INTEGER z de clase GLOBAL, tipo INTEGER a de clase LOCAL, tipo FLOAT b de clase LOCAL, tipo FLOAT