Para dar soporte al análisis de los árboles y su representación, Parse::Eyapp
provee el método str . El siguiente ejemplo con el depurador muestra el uso del método
str .
El método Parse::Eyapp::Node::str retorna
una cadena que describe como término el árbol enraizado en el nodo
que se ha pasado como argumento.
El método str cuando visita un nodo comprueba la existencia de
un método info para la clase del nodo. Si es así el método
será llamado. Obsérvese como en las líneas 5 y 6 proveemos de métodos
info
a los nodos TERMINAL
y FUNCTION
.
La consecuencia es que en la llamada de la línea 7 el término
que describe al árbol es decorado con los nombres de funciones
y los atributos y números de línea de los terminales.
Los nodos de la clase Parse::Eyapp::Node:Match vienen con un método info asociado el cual muestra el tipo del nodo apuntado, su profundidad y el nómbre del patrón que casó (línea 9 de la sesión).
Veamos un ejemplo que ilustra también algunos "trucos" de depuración. Se han introducido comentarios en el texto de salida:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code> perl -wd usesimple4.plla opción
-w
por warning y -d
para activar el debugger
Loading DB routines from perl5db.pl version 1.28 Editor support available.El mensaje
Editor support available
nos señala
la presencia de los servicios de edición de la entrada.
Si no sale el mensaje instale Term::ReadLine::Gnu en su máquina!
main::(usesimple4.pl:6): $Data::Dumper::Indent = 1;La información entre paréntesis indica el fichero en que estamos y la línea (
usesimple4.pl
)
DB<1> f Simple4.eyp # <-- Quiero poner un break en Simple4.eyp,El comando
f
es esencial cuando se usa eyapp
.
Le indicamos al debugger que queremos usar como fichero
por defecto el de nuestra gramática. Los siguientes
comandos se referirán a Simple4.eyp
:
DB<2> b 654 # <--- break en la línea 654 de Simple4.eyp DB<3> l 654,656 # <-- Se trata de las líneas en las que se establece # la jerarquía de bloques 654:b my @blocks = $SimpleTrans::blocks->m($t); 655: $_->node->{fatherblock} = $_->father->{node} \ for (@blocks[1..$#blocks]); 656: $Data::Dumper::Deepcopy = 1; DB<4> c # c indica que se continúa la ejecución del programa # hasta encontrar un "break"La ejecución del programa continúa produciendo la salida del fuente hasta encontrar el punto de break:
**************** char d,e[1][2]; char f() { int c[2]; char d; return d; } Simple4::Run(Simple4.eyp:654): my @blocks = $SimpleTrans::blocks->m($t); DB<4> n # <--- next ejecuta la siguiente instrucción. Si es una sub no se entra. Simple4::Run(Simple4.eyp:655): $_->node->{fatherblock} = $_->father->{node} \ for (@blocks[1..$#blocks]); DB<4> # La simple pulsación de un retorno de carro # hace que se repita la última instrucción s o n Simple4::Run(Simple4.eyp:655): $_->node->{fatherblock} = $_->father->{node} \ for (@blocks[1..$#blocks]); DB<4> Simple4::Run(Simple4.eyp:656): $Data::Dumper::Deepcopy = 1; DB<4> Simple4::Run(Simple4.eyp:657): print Dumper $t; DB<4> x $t->str # El método str para los objetos Parse::Eyapp::Node devuelve # una descripción del árbol 0 'PROGRAM(FUNCTION(RETURN(TERMINAL,VAR(TERMINAL))))' DB<5> sub TERMINAL::info { @a = join ':', @{$_[0]->{attr}}; "[@a]"} DB<6> sub FUNCTION::info { "[".$_[0]->{function_name}[0]."]" } DB<7> x $t->str 0 'PROGRAM(FUNCTION[f](RETURN(VAR(TERMINAL[d:6]))))' # La variable @Parse::Eyapp::Node::PREFIXES Controla que # prefijos se deben eliminar en la presentación DB<8> p @Parse::Eyapp::Node::PREFIXES Parse::Eyapp::Node:: DB<9> x $blocks[0]->str 0 'Match[PROGRAM:0:blocks](Match[FUNCTION:1:blocks:[f]])' # La información de un nodo Match es la # terna [type:depth:nombre de la treeregexp]
El método str
ha sido concebido como una herramienta de
ayuda a la depuración y verificación de los programas árbol.
La metodología consiste en incorporar las pequeñas funciones
info
al programa en el que trabajamos. Así en Simple4.eyp
añadimos el siguiente código:
640 sub Run { 641 my($self)=shift; ... ................ 663 } 664 665 sub TERMINAL::info { 666 my @a = join ':', @{$_[0]->{attr}}; 667 return "[@a]" 668 } 669 670 sub FUNCTION::info { 671 return "[".$_[0]->{function_name}[0]."]" 672 } 673 674 sub BLOCK::info { 675 return "[".$_[0]->{line}."]" 676 }
En el código mostrado no se ha realizado la optimización consistente en no incluir en el árbol de bloques a aquellos bloques cuya sección de declaraciones es vacía. Asi podemos estudiar la estructura de datos resultante para un pequeño programa como el que sigue a continuación.
Programa | $t->str |
$blocks[0]->str |
1 char d0; 2 char f() { 3 { 4 {} 5 } 6 { 7 { } 8 } 9 { 10 {{}} 11 } 12 } 13 g() { 14 {} 15 { 16 {} 17 } 18 {} 19 } |
PROGRAM( FUNCTION[f]( BLOCK[3]( BLOCK[4] ), BLOCK[6]( BLOCK[7]), BLOCK[9]( BLOCK[10]( BLOCK[10] ) ) ), FUNCTION[g]( BLOCK[14], BLOCK[15]( BLOCK[16] ), BLOCK[18] ) ) |
Match[PROGRAM:0:blocks]( Match[FUNCTION:1:blocks:[f]]( Match[BLOCK:2:blocks:[3]]( Match[BLOCK:3:blocks:[4]] ), Match[BLOCK:2:blocks:[6]]( Match[BLOCK:3:blocks:[7]] ), Match[BLOCK:2:blocks:[9]]( Match[BLOCK:3:blocks:[10]]( Match[BLOCK:4:blocks:[10]] ) ) ), Match[FUNCTION:1:blocks:[g]]( Match[BLOCK:2:blocks:[14]], Match[BLOCK:2:blocks:[15]]( Match[BLOCK:3:blocks:[16]] ), Match[BLOCK:2:blocks:[18]] ) ) |
La tabla
12.1
muestra el fuente y el resultado de $blocks[0]->str
después de la llamada
my @blocks = $SimpleTrans::blocks->m($t);
Obsérvese que los nodos Match
son referencias
a los nodos actuales del árbol.
Veamos un ejemplo mas de sesión con el depurador cuando la entrada proveída es el programa en la tabla 12.1:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code> eyapp Simple4 ; perl -wd usesimple4.pl main::(usesimple4.pl:6): $Data::Dumper::Indent = 1; DB<1> f Simple4.eyp #<-- establecemos el fichero de trabajo DB<2> l 643 #<-- nos paramos antes de la lectura 643: $self->YYData->{INPUT} = $_; DB<3> b 643 $_=~ m{char d0} # <-- nos detendremos sólo si se trata de este programa DB<4> c # <-- a correr! .................. # salida correspondiente a los fuentes anteriores Simple4::Run(Simple4.eyp:643): $self->YYData->{INPUT} = $_; DB<5> b Parse::Eyapp::Node::str # <-- Especificación completa de la subrutina en la que parar DB<6> c ........................ Parse::Eyapp::Node::str(/home/pl/src/perl/YappWithDefaultAction/lib//Parse/Eyapp/Node.pm:543): 543: my $self = CORE::shift; # root of the subtree DB<7> L # <-- Listar todos los breakpoints /home/pl/src/perl/YappWithDefaultAction/lib//Parse/Eyapp/Node.pm: 543: my $self = CORE::shift; # root of the subtree break if (1) Simple4.eyp: 643: $self->YYData->{INPUT} = $_; break if ($_=~ m{char d0}) DB<8> B * # <-- Eliminar todos los breakpoints Deleting all breakpoints... DB<9> b 654 # <-- detengámonos después del matching árbol DB<10> c Simple4::Run(Simple4.eyp:654): $_->node->{fatherblock} = $_->father->{node} for (@blocks[1..$#blocks]); DB<4> x $Parse::Eyapp::Node::INDENT # <-- La variable INDENT controla el estilo en str 0 1 DB<11> x $t->str # <--- Salida con sangrado 0 ' PROGRAM( FUNCTION[f]( BLOCK[3]( BLOCK[4] ), BLOCK[6]( BLOCK[7] ), BLOCK[9]( BLOCK[10]( BLOCK[10] ) ) ), FUNCTION[g]( BLOCK[14], BLOCK[15]( BLOCK[16] ), BLOCK[18] ) )' DB<12> $Parse::Eyapp::Node::INDENT = 0 # <-- Estilo compacto. Por defecto DB<13> x $t->str # <-- Todo en una línea 0 'PROGRAM(FUNCTION[f](BLOCK[3](BLOCK[4]),BLOCK[6](BLOCK[7]),BLOCK[9](BLOCK[10](BLOCK[10]))), FUNCTION[g](BLOCK[14],BLOCK[15](BLOCK[16]),BLOCK[18]))' DB<14> . # Donde estoy? Que línea se va a ejecutar? Simple4::Run(Simple4.eyp:654): $_->node->{fatherblock} = $_->father->{node} for (@blocks[1..$#blocks]); DB<15> c 655 # <-- Ejecutemos hasta la siguiente Simple4::Run(Simple4.eyp:655): $Data::Dumper::Deepcopy = 1; DB<16> x $blocks[0]->str # <-- Veamos la forma del nodo de matching 0 'Match[PROGRAM:0:blocks](Match[FUNCTION:1:blocks:[f]](Match[BLOCK:2: blocks:[3]](Match[BLOCK:3:blocks:[4]]),Match[BLOCK:2:blocks:[6]](Match[ BLOCK:3:blocks:[7]]),Match[BLOCK:2:blocks:[9]](Match[BLOCK:3:blocks:[1 0]](Match[BLOCK:4:blocks:[10]]))),Match[FUNCTION:1:blocks:[g]](Match[B LOCK:2:blocks:[14]],Match[BLOCK:2:blocks:[15]](Match[BLOCK:3:blocks:[1 6]]),Match[BLOCK:2:blocks:[18]]))' # <-- Todo está en una línea DB<17> $Parse::Eyapp::Node::INDENT = 1 DB<18> x $blocks[0]->str 0 ' Match[PROGRAM:0:blocks]( Match[FUNCTION:1:blocks:[f]]( Match[BLOCK:2:blocks:[3]]( Match[BLOCK:3:blocks:[4]] ), Match[BLOCK:2:blocks:[6]]( Match[BLOCK:3:blocks:[7]] ), Match[BLOCK:2:blocks:[9]]( Match[BLOCK:3:blocks:[10]]( Match[BLOCK:4:blocks:[10]] ) ) ), Match[FUNCTION:1:blocks:[g]]( Match[BLOCK:2:blocks:[14]], Match[BLOCK:2:blocks:[15]]( Match[BLOCK:3:blocks:[16]] ), Match[BLOCK:2:blocks:[18]] ) )'
Las variables de paquete que gobiernan la conducta
de str
y sus valores por defecto aparecen en la siguiente lista:
our @PREFIXES = qw(Parse::Eyapp::Node::)
La variable de paquete Parse::Eyapp::Node::PREFIXES contiene
la lista de prefijos de los nombres de tipo que serán eliminados
cuando str
muestra el término. Por defecto contiene
la cadena 'Parse::Eyapp::Node::'
. Es por esta razón
que los nodos de matching que pertenecen a la clase 'Parse::Eyapp::Node::Match
son abreviados a la cadena Match
(línea 9).
our $INDENT = 0
La variable $Parse::Eyapp::Node::INDENT controla el formato de presentación
usado por Parse::Eyapp::Node::str :
0
la representación es compacta.
$Parse::Eyapp::Node::LINESEP
está a 4.
Véase la página
para un ejemplo con $INDENT
a 2 y
$Parse::Eyapp::Node::LINESEP
a 4.
our $STRSEP = ','
Separador de nodos en un término. Por defecto es la coma.
our $DELIMITER = '['
Delimitador de presentación de la información proveída por el método
info
. Por defecto los delimitadores son corchetes. Puede elegirse
uno cualquiera de la lista:
'[', '{', '(', '<'si se define como la cadena vacía o
undef
no se usarán delimitadores.
our $FOOTNOTE_HEADER = "\n---------------------------\n"
Delimitador para las notas a pie de página.
our $FOOTNOTE_SEP = ")\n"
Separador de la etiqueta/número de la nota al pie
our $FOOTNOTE_LEFT = '^{'
, our $FOOTNOTE_RIGHT = '}'
Definen el texto que rodea al número de referencia en el nodo.
El siguiente ejemplo ilustra el manejo de notas a pié de árbol usando str . Se han definido los siguientes métodos:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code> sed -ne '677,$p' Simple6.eyp $Parse::Eyapp::Node::INDENT = 1; sub TERMINAL::info { my @a = join ':', @{$_[0]->{attr}}; return "@a" } sub PROGRAM::footnote { return "Types:\n" .Dumper($_[0]->{types}). "Symbol Table:\n" .Dumper($_[0]->{symboltable}) } sub FUNCTION::info { return $_[0]->{function_name}[0] } sub FUNCTION::footnote { return Dumper($_[0]->{symboltable}) } sub BLOCK::info { return $_[0]->{line} } sub VAR::info { return $_[0]->{definition}{type} if defined $_[0]->{definition}{type}; return "No declarado!"; } *FUNCTIONCALL::info = *VARARRAY::info = \&VAR::info;El resultado para el programa de entrada:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code> cat salida int a,b; int f(char c) { a[2] = 4; b[1][3] = a + b; c = c[5] * 2; return g(c); }produce una salida por
stderr
:
Identifier g not declaredy la siguiente descripción de la estructura de datos:
PROGRAM^{0}( FUNCTION{f}^{1}( ASSIGN( VARARRAY{INT}( TERMINAL{a:4}, INDEXSPEC( INUM( TERMINAL{2:4} ) ) ), INUM( TERMINAL{4:4} ) ), ASSIGN( VARARRAY{INT}( TERMINAL{b:5}, INDEXSPEC( INUM( TERMINAL{1:5} ), INUM( TERMINAL{3:5} ) ) ), PLUS( VAR{INT}( TERMINAL{a:5} ), VAR{INT}( TERMINAL{b:5} ) ) ), ASSIGN( VAR{CHAR}( TERMINAL{c:6} ), TIMES( VARARRAY{CHAR}( TERMINAL{c:6}, INDEXSPEC( INUM( TERMINAL{5:6} ) ) ), INUM( TERMINAL{2:6} ) ) ), RETURN( FUNCTIONCALL{No declarado!}( TERMINAL{g:7}, ARGLIST( VAR{CHAR}( TERMINAL{c:7} ) ) ) ) ) ) --------------------------- 0) Types: $VAR1 = { 'F(X_1(CHAR),INT)' => bless( { 'children' => [ bless( { 'children' => [ bless( { 'children' => [] }, 'CHAR' ) ] }, 'X_1' ), bless( { 'children' => [] }, 'INT' ) ] }, 'F' ), 'CHAR' => bless( { 'children' => [] }, 'CHAR' ), 'INT' => bless( { 'children' => [] }, 'INT' ) }; Symbol Table: $VAR1 = { 'a' => { 'type' => 'INT', 'line' => 1 }, 'b' => { 'type' => 'INT', 'line' => 1 }, 'f' => { 'type' => 'F(X_1(CHAR),INT)', 'line' => 3 } }; --------------------------- 1) $VAR1 = { 'c' => { 'type' => 'CHAR', 'param' => 1, 'line' => 3 } };
Use str
como método de clase
Parse::Eyapp::Node->str(@forest) |
cuando quiera convertir a su representación término mas de un árbol. El código:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code> sed -ne '652,658p' Simple4.eyp \ | cat -n 1 local $Parse::Eyapp::Node::INDENT = 2; 2 local $Parse::Eyapp::Node::DELIMITER = ""; 3 print $t->str."\n"; 4 { 5 local $" = "\n"; 6 print Parse::Eyapp::Node->str(@blocks)."\n"; 7 }
produce la salida:
nereida:~/doc/casiano/PLBOOK/PLBOOK/code> eyapp Simple4 ;\ treereg SimpleTrans.trg ;\ usesimple4.pl **************** f() { int a,b[1][2],c[1][2][3]; char d[10]; b[0][1] = a; } PROGRAM( FUNCTION[f]( ASSIGN( VARARRAY( TERMINAL[b:4], INDEXSPEC( INUM( TERMINAL[0:4] ), INUM( TERMINAL[1:4] ) ) # INDEXSPEC ) # VARARRAY, VAR( TERMINAL[a:4] ) ) # ASSIGN ) # FUNCTION ) # PROGRAM Match[PROGRAM:0:blocks]( Match[FUNCTION:1:blocks:[f]] )
Obsérvense los comentarios # TIPODENODO
que acompañan a los paréntesis
cerrar cuando estos están lejos (esto es, a una distancia de mas de
$Parse::Eyapp::Node::LINESEP líneas) del correspondiente paréntesis abrir.
Tales comentarios son consecuencia de haber establecido el valor de
$Parse::Eyapp::Node::INDENT a 2.