El producto de la traducción - que es la entrada para el intérprete - es un árbol. Cada operación del intérprete es un nodo del árbol. Los argumentos de la operación son los hijos del nodo.
Hay diferentes tipos de operadores. Por ejemplo el operador binario add toma dos valores de la pila y empuja la suma. El operador unario readline toma un manejador de ficheros de la pila y empuja el valor leído.
Algunos operadores actúan sobre listas. Por ejemplo print toma un número variable de elementos de la pila. Los operadores de listas se apoyan en un uso previo del operador pushmark para marcar el comienzo de la listade argumentos.
Los módulos en la familia B::* permiten acceder a dicho árbol
(B, B::Deparse, B::Bytecode, B::ByteLoader, etc.).
El módulo B representa cada operador como una subclase
de la clase B::OP. Estas clases contienen métodos que nos permiten
obtener información sobre o modificar el estado interno del
operador. Por ejemplo obtener los hijos de un operador.
Para tener una visión en profundidad del tema puede consultar
los libros de Simon Cozens
[]
y
[8].
El siguiente programa Btree3.pl implanta una subrutina
treetrav que muestra el árbol
generado por el compilador para una subrutina dada.
pp2@nereida:~/src/perl/B$ cat -n Btree3.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use B::Utils;
4
5 my $s = sub {
6 my $a = shift;
7 print $a+1;
8 };
9
10 sub treetrav {
.. ...........
22 }
23
24 my $b = B::svref_2object($s); # Objeto B::CV
25 print "*** Tree ***\n";
26 my $op = $b->ROOT;
27 treetrav($op, 0);
La llamada a B::svref_2object sobre la subrutina referenciada
por $s nos produce un objeto
B::CV que representa el árbol de codigo resultante.
Cuando treetrav es llamada con el objeto y un sangrado inicial igual a 0
nos produce la siguiente
salida:
pp2@nereida:~/src/perl/B$ Btree3.pl
*** Tree ***
leavesub( #subroutine exit
lineseq( #line sequence
sassign( #scalar assignment
shift( #shift
rv2av( #array dereference
gv( #glob value
) # end gv
) # end rv2av
) # end shift
padsv( #private variable
) # end padsv
) # end sassign
nextstate( #next statement
) # end nextstate
print( #print
add( #addition (+)
padsv( #private variable
) # end padsv
const( #constant item
) # end const
) # end add
) # end print
) # end lineseq
) # end leavesub
La función treetrav usa los métodos name y desc
de la subclase B::OP
para obtener el nombre y la descripción de la operación actual.
El método kids en B::Utils
devuelve la lista de hijos:
10 sub treetrav {
11 my $op = shift;
12 my $indent = shift;
13
14 my $spaces = " "x$indent;
15 print $spaces.$op->name."(\t\t#".$op->desc;
16 print "\n";
17 for ($op->kids()) {
18 treetrav($_, $indent+2);
19 }
20 print "$spaces) # end ".$op->name;
21 print "\n";
22 }
Hay varias subrutinas de recorrido del árbol de
códigos. Una de las mas sencillas es walkoptree_simple
(en B::Utils):
walkoptree_simple($op, \&callback, [$data])
El método START de un objeto B::CV nos da el punto de
entrada en la ejecución. El método next de un objeto B::OP
proporciona el siguiente nodo a ejecutar.
pp2@nereida:~/src/perl/B$ cat -n bex2.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use B;
4
5 my $s = sub {
6 my $a = shift;
7 print $a+1;
8 };
9
10 my $b = B::svref_2object($s);
11 my $op = $b->START;
12 my ($class, $name, $desc);
13 format STDOUT =
14 @<<<<<<<<<@|@<<<<<<<<<<<<@|@<<<<<<<<<<<<<<<<<<<<<<<<<
15 $class, '|', $name, '|', $desc
16 .
17 do {
18 ($class, $name, $desc) = (B::class($op), $op->name, $op->desc);
19 write;
20 } while $op = $op->next and !$op->isa("B::NULL");
Es necesario comprobar que $op no es un objeto B::NULL
ya que el puntero next es un puntero C que B
deja disponible sin envoltorio alguno.
La ejecución produce la secuencia de operaciones en orden de ejecución:
pp2@nereida:~/src/perl/B$ bex2.pl | cat -n 1 COP | nextstate | next statement 2 PADOP | gv | glob value 3 UNOP | rv2av | array dereference 4 UNOP | shift | shift 5 OP | padsv | private variable 6 BINOP | sassign | scalar assignment 7 COP | nextstate | next statement 8 OP | pushmark | pushmark 9 OP | padsv | private variable 10 SVOP | const | constant item 11 BINOP | add | addition (+) 12 LISTOP | print | print 13 UNOP | leavesub | subroutine exit |
my $s = sub {
my $a = shift;
print $a+1;
};
|