next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Acciones en Medio de Sup: Análisis Sintáctico con Parse::Eyapp Ant: Acciones en Medio de Err: Si hallas una errata ...


Manejo en eyapp de Atributos Heredados

Supongamos que eyapp esta inmerso en la construcción de la antiderivación a derechas y que la forma sentencial derecha en ese momento es:

$ X_m \ldots X_1 X_0 Y_1 \ldots Y_n a_1 \ldots a_0$

y que el mango es $ B \rightarrow Y_1 \ldots Y_n$ y en la entrada quedan por procesar $ a_1 \ldots a_0$ .

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_1$ y $_[n] es el atributo asociado con $ Y_n$ . 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 $ X_0$ , si se llama con -1 el que está dos unidades a la izquierda de la variable actual, esto es, el asociado con $ X_1$ etc. Así $_[-m] denota el atributo de $ X_m$ .

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 $ \rightarrow$ C T { $L{c} = $C{c}; $L{t} = $T{t} } L
2 C $ \rightarrow$ global { $C{c} = "global" }
3 C $ \rightarrow$ local { $C{c} = "local" }
4 T $ \rightarrow$ integer { $T{t} = "integer" }
5 T $ \rightarrow$ float { $T{t} = "float" }
6 L $ \rightarrow$ { $L$ _1$ {t} = $L{t}; $L$ _1$ {c} = $L{c}; } L$ _1$ ','
7   id { set_class($id{v}, $L{c}); set_type($id{v}, $L{t}); }
8 L $ \rightarrow$ 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.

Ejercicio 8.28.1   Evalúe el esquema de traducción para la entrada 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$ _1$ {t} = $L{t}; $L$ _1$ {c} = $L{c}; } no puede ser ejecutada en un programa eyapp es porque el nodo del árbol sintáctico asociado con L$ _1$ no existe en el momento en el que la acción es ejecutada. No es posible guardar algo en L$ _1$ {c} ya que no hay nodo asociado con L$ _1$ . 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

L $ \rightarrow$ id $ \vert$ L$ _1$ ',' id

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:

D $ \Longrightarrow$ C T L $ \Longrightarrow$ C T L, id $ \Longrightarrow$ C T L, id, id $ \Longrightarrow$ C T id, id, id $ \Longrightarrow$
$ \Longrightarrow$ C float id, id, id $ \Longrightarrow$ local float id, id, id

Observe que el orden de recorrido de eyapp es:

local float id, id, id $ \Longleftarrow$ C float id, id $ \Longleftarrow$ C T id, id, id $ \Longleftarrow$
$ \Longleftarrow$ C T L, id, id $ \Longleftarrow$ C T L, id $ \Longleftarrow$ C T L $ \Longleftarrow$ D

en la antiderivación, cuando el mango es una de las dos reglas para listas de identificadores, L $ \rightarrow$ id y L $ \rightarrow$ L, id es decir durante las tres ultimas antiderivaciones:

C T L, id, id $ \Longleftarrow$ C T L, id $ \Longleftarrow$ C T L $ \Longleftarrow$ D

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


next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Acciones en Medio de Sup: Análisis Sintáctico con Parse::Eyapp Ant: Acciones en Medio de Err: Si hallas una errata ...
Casiano Rodríguez León
2012-05-22