next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Práctica: Establecimiento de la Sup: Análisis de Ámbito Ant: Resultado del Análisis de Err: Si hallas una errata ...


Usando el Método str para Analizar el Árbol

El Método str de los Nodos

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 info

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.pl
la 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]

Modo de uso de str

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  }

Un ejemplo de Salida con Bloques Anidados

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.


Tabla: Representación de AST con str
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.

Usando el Depurador y str Para Ver los AST

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]]
  )
)'

Variables que Gobiernan la Conducta de str

Las variables de paquete que gobiernan la conducta de str y sus valores por defecto aparecen en la siguiente lista:

Footnotes

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 declared
y 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
  }
};

Usando str sobre una Lista de Áboles

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.



Subsecciones
next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Práctica: Establecimiento de la Sup: Análisis de Ámbito Ant: Resultado del Análisis de Err: Si hallas una errata ...
Casiano Rodríguez León
2012-05-22