

Parse::Eyapp::Treeregexp permite la transformación de
árboles mediante el uso de Expresiones Regulares Arbol.
Las expresiones regulares árbol serán introducidas en mas detalle en la
sección
4.9.
El siguiente ejemplo modifica el anterior esquema de traducción de infijo a postfijo para producir un código de postfijo mas eficiente. Para ello se transforma el árbol generado durante la fase Tree Construction Time y antes de la fase Execution Time. El código Treeregexp que define el conjunto de transformaciones se encuentra en las líneas 74-103.
Las transformaciones consisten en
UMINUS a un número negativo
Después de ello se realiza la traducción quedando la misma
como el atributo t del
nodo raíz (línea 120).
A partir de este momento,
si el traductor tuviera un mayor número de fases
de posterior tratamiento del árbol,
los nodos de tipo código y los nodos hoja cuya funcionalidad es
puramente sintáctica como los terminales =, * etc.
pueden ser eliminados. Es por eso que los suprimimos en las
líneas 122-123.
Veamos primero el código y luego lo discutiremos en mas detalle:
nereida:~/src/perl/YappWithDefaultAction/examples> cat -n TSwithtreetransformations.eyp
1 # File TSwithtreetransformations.eyp
2 %right '='
3 %left '-' '+'
4 %left '*' '/'
5 %left NEG
6
7 %{
8 # Treeregexp is the engine for tree transformations
9 use Parse::Eyapp::Treeregexp;
10 use Data::Dumper;
11 $Data::Dumper::Indent = 1;
12 $Data::Dumper::Deepcopy = 1;
13 $Data::Dumper::Deparse = 1;
14 %}
15
16 %metatree
17
18 %defaultaction {
19 if (@_==4) { # binary operations: 4 = lhs, left, operand, right
20 $lhs->{t} = "$_[1]->{t} $_[3]->{t} $_[2]->{attr}";
21 return
22 }
23 die "Fatal Error. Unexpected input\n".Dumper(@_);
24 }
25
26 %%
27 line: %name PROG
28 exp <%name EXP + ';'>
29 { @{$lhs->{t}} = map { $_->{t}} ($lhs->child(0)->Children()); }
30
31 ;
32
33 exp: %name NUM NUM { $lhs->{t} = $_[1]->{attr}; }
34 | %name VAR VAR { $lhs->{t} = $_[1]->{attr}; }
35 | %name ASSIGN VAR '=' exp { $lhs->{t} = "$_[1]->{attr} $_[3]->{t} =" }
36 | %name PLUS exp '+' exp
37 | %name MINUS exp '-' exp
38 | %name TIMES exp '*' exp
39 | %name DIV exp '/' exp
40 | %name UMINUS '-' exp %prec NEG { $_[0]->{t} = "$_[2]->{t} NEG" }
41 | '(' exp ')' %begin { $_[2] } /* skip parenthesis */
42 ;
43
44 %%
45
46 # subroutines _Error and _Lexer
.. ................................
66
67 sub Run {
68 my($self)=shift;
69 print "input: "; $x = <>;
70 my $tree = $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error,
71 #yydebug => 0xFF
72 );
73
74 my $transform = Parse::Eyapp::Treeregexp->new( STRING => q{
75
76 delete_code : CODE => { $delete_code->delete() }
77
78 {
79 sub not_semantic {
80 my $self = shift;
81 return 1 if $self->{token} eq $self->{attr};
82 return 0;
83 }
84 }
85
86 delete_tokens : TERMINAL and { not_semantic($TERMINAL) } => { $delete_tokens->delete() }
87
88 delete = delete_code delete_tokens;
89
90 uminus: UMINUS(., NUM($x), .) => { $x->{attr} = -$x->{attr}; $_[0] = $NUM }
91
92 constantfold: /TIMES|PLUS|DIV|MINUS/(NUM($x), ., NUM($y))
93 => {
94 $x->{attr} = eval "$x->{attr} $W->{attr} $y->{attr}";
95 $_[0] = $NUM[0];
96 }
97
98 zero_times: TIMES(NUM($x), ., .) and { $x->{attr} == 0 } => { $_[0] = $NUM }
99 times_zero: TIMES(., ., NUM($x)) and { $x->{attr} == 0 } => { $_[0] = $NUM }
100
101 algebraic_transformations = constantfold zero_times times_zero;
102
103 },
104 PACKAGE => 'TSwithtreetransformations',
105 OUTPUTFILE => 'main.pm',
106 SEVERITY => 0,
107 NUMBERS => 0,
108 );
109
110 # Create the transformer
111 $transform->generate();
112 # Get the AST
113
114 our ($uminus);
115 $uminus->s($tree);
116
117 our (@algebraic_transformations);
118 $tree->s(@algebraic_transformations);
119
120 $tree->translation_scheme();
121
122 our (@delete);
123 $tree->s(@delete);
124 print Dumper($tree);
125 }
La estructura de un programa Treeregexp es sencilla. Consiste en la repetición de tres tipos de expresiones regulares árbol: las treeregexp propiamente dichas, código auxiliar para las transformaciones y definiciones de familias de transformaciones.
treeregexplist:
treeregexp*
;
treeregexp:
IDENT ':' treereg ('=>' CODE)? # Treeregexp
| CODE # Código auxiliar
| IDENT '=' IDENT + ';' # Familia de transformaciones
;
Las expresiones regulares árbol propiamente dichas siguen la regla
IDENT ':' treereg ('=>' CODE)?
Podemos ver ejemplos de instancias de esta regla en las líneas
76, 86, 90, 92-96, 98 y 99. El identificador IDENT da el nombre
a la regla. Actualmente (2006) existen estos tipos de treereg :
treereg:
/* patrones con hijos */
IDENT '(' childlist ')' ('and' CODE)?
| REGEXP (':' IDENT)? '(' childlist ')' ('and' CODE)?
| SCALAR '(' childlist ')' ('and' CODE)?
| '.' '(' childlist ')' ('and' CODE)?
/* hojas */
| IDENT ('and' CODE)?
| REGEXP (':' IDENT)? ('and' CODE)?
| '.' ('and' CODE)?
| SCALAR ('and' CODE)?
| ARRAY
| '*'
Una regla como
zero_times: TIMES(NUM($x), ., .) and { $x->{attr} == 0 } => { $_[0] = $NUM }
crea un objeto transformación (concretamente un objeto de la clase Parse::Eyapp:YATW )
que puede ser referido a través de la variable escalar $zero_times.
La primera parte de la regla zero_times indica que
para que se produzca el emparejamiento es necesario que el nodo visitado sea
del tipo TIMES y su primer
hijo es de tipo NUM. Una referencia al nodo hijo de NUM será automáticamente
guardada en la variable léxica $x.
El efecto de un escalar en una treeregexp es casar con cualquier nodo y almacenar su referencia en la variable.
La aparición de $x en la treeregexp anterior
casará con cualquier nodo. La referencia al nodo que ha casado queda en $x.
Asi $x podrá ser usado en el patrón árbol semántico
o condición semántica(esto es,
en el código opcional que va precedido de la palabra reservada and)
y en la acción de transformación árbol (el código opcional que va precedido de la
flecha gorda =>).
Un punto también casa con cualquier nodo. Puede verse como una abreviación de
la expresión regular árbol escalar. Una referencia al nodo que casa
queda almacenada en la variable
léxica especial $W . Si la expresión regular árbol tiene
varios puntos sus referencias quedan almacenadas en la variable array @W.
Es un error usar el identificador W en una expresión regular escalar
escalar. Por ejemplo, una treeregexp como:
constantfold: /TIMES|PLUS|DIV|MINUS/(NUM($W), ., NUM($y))
da lugar al error:
*Error* Can't use $W to identify an scalar treeregexp at line 100.
La segunda parte de la regla es opcional y comienza con la palabra reservada and
seguida de un código que explicita las condiciones semánticas que debe cumplir el nodo
para que se produzca el casamiento. En el ejemplo se explicita que el attributo del nodo
(forzosamente del tipo TERMINAL en este caso) referenciado por $x debe ser cero.
Es posible dentro de las partes de código referirse a los nodos del árbol.
Cuando la rutina de transformación generada por el compilador para una
treeregexp es llamada, el primer argumento $_[0] contiene la referencia al nodo
que esta siendo visitado.
Parse::Eyapp::Treeregexp crea variables léxicas con nombres
los tipos de los nodos a los que referencian.
Así la subrutina generada para la transformación zero_times
zero_times: TIMES(NUM($x), ., .) and { $x->{attr} == 0 } => { $_[0] = $NUM }
guarda en la variable lexica $TIMES una copia de $_[0] y en
la variable léxica $NUM una referencia al nodo $TIMES->child(0) .
Si un tipo de nodo se repite en la treeregexp la variable léxica
asociada con dicho tipo se autodeclara como un array. Este es el caso de
la transformación constantfold
en la cual aparecen dos nodos de tipo NUM:
92 constantfold: /TIMES|PLUS|DIV|MINUS/(NUM($x), ., NUM($y))
93 => {
94 $x->{attr} = eval "$x->{attr} $W->{attr} $y->{attr}";
95 $_[0] = $NUM[0];
96 }
La variable @NUM es automáticamente declarada: $NUM[0] es una
referencia al primer nodo NUM y $NUM[1] es una referencia
al segundo.
La tercera parte de la regla es también opcional y viene precedida de la flecha gorda.
Habitualmente contiene el código que transforma el árbol.
Para lograr la modificación del nodo del árbol visitado el programador
Treeregexp deberá usar $_[0]. Recuerde que
los elementos en @_ son alias de los argumentos. Si el código
de la tercera parte fuera reescrito como:
{ $TIMES = $NUM }
no funcionaría ya que estaríamos modificando la variable léxica que referencia al
nodo raíz del subarbol que ha casado.
Es posible usar una expresión regular lineal alias expresión regular clásica alias regexp para explicitar el tipo de un nodo como indica la regla de producción:
treereg: REGEXP (':' IDENT)? '(' childlist ')' ('and' CODE)?
La treeregexp para el plegado de constantes constituye un ejemplo:
92 constantfold: /TIMES|PLUS|DIV|MINUS/(NUM($x), ., NUM($y))
93 => {
94 $x->{attr} = eval "$x->{attr} $W->{attr} $y->{attr}";
95 $_[0] = $NUM[0];
96 }
La expresión regular deberá especificarse entre barras de división (/)
y es posible especificar opciones después del segundo slash (e, i, etc.).
El identificador opcional después de la regexp indica el nombre para la
variable léxica que almacenará una copia de la referencia al nodo del árbol.
En el ejemplo $bin podría usarse para referenciar al nodo apuntado por $_[0].
Si no se especifica identificador quedará almacenado en la variable léxica
especial $W. Si la expresión regular árbol tiene
varias regexp (y/o puntos) sus referencias quedan almacenadas en la variable array @W.
La semántica de las expresiones regulares lineales
es modificada ligéramente por Parse::Eyapp::Treeregexp.
Por defecto se asume la opción x . El compilador de expresiones regulares árbol
procede a la inserción automática de la opción x.
Use la nueva opción X ((X mayúscula)
para evitar esta conducta.
Tampoco es necesario añadir anclas de frontera
de palabra \b a los identificadores que aparecen en la expresión
regular lineal: el compilador de expresiones regulares árbol
las insertará. Use la nueva opción B (B mayúscula) para negar esta conducta.
El siguiente ejemplo tomado del análisis de tipos en el compilador
de Simple C muestra como no es encesario usar x:
67 # Binary Operations
68 bin: / PLUS
69 |MINUS
70 |TIMES
71 |DIV
72 |MOD
73 |GT
74 |GE
75 |LE
76 |EQ
77 |NE
78 |LT
79 |AND
80 |EXP
81 |OR
82 /($x, $y)
83 => {
84 $x = char2int($_[0], 0);
85 $y = char2int($_[0], 1);
86
87 if (($x->{t} == $INT) and ( $y->{t} == $INT)) {
88 $_[0]->{t} = $INT;
89 return 1;
90 }
91 type_error("Incompatible types with operator '".($_[0]->lexeme)."'", $_[0]->line);
92 }
Con la semántica habitual de las regexp la palabra reservada
WHILE casaría con la subexpresión LE en la línea 76
provocando un análisis de tipos erróneo para esta clase de nodos.
La inserción automática de anclas por parte de Parse::Eyapp::Treeregexp
evita este - normalmente indeseable - efecto.
Las transformaciones creadas por Parse::Eyapp::Treeregexp pueden agruparse por familias.
Esta es la función de la regla de producción:
treeregexp: IDENT '=' IDENT + ';'
En el ejemplo creamos una nueva familia denominada algebraic_transformations
mediante la asignación de la línea 101:
algebraic_transformations = constantfold zero_times times_zero;Las transformaciones en esa familia pueden ser accedidas posteriormente para su aplicación mediante la variable de paquete
@algebraic_transformations (véanse las líneas
117-118).
En medio de la definición de cualquier regla treeregexp es posible insertar código de apoyo siempre que se sitúe entre llaves:
78 {
79 sub not_semantic {
80 my $self = shift;
81 return 1 if $self->{token} eq $self->{attr};
82 return 0;
83 }
84 }
85
86 delete_tokens : TERMINAL and { not_semantic($TERMINAL) } => { $delete_tokens->delete() }
Los objetos de la clase Parse::Eyapp::YATW como $delete_tokens disponen
de un método delete que permite eliminar con seguridad un hijo de la raíz del
subárbol que ha casado. En este caso los nodos que casan son los de la clase TERMINAL
en los que el valor de la clave token coincide con el valor de la clave attr.
Un programa Treeregexp puede - como en el ejemplo - proporcionarse como una
cadena de entrada al método new de la clase Parse::Eyapp::Treeregexp
o bien escribirse en un fichero separado (la extensión .trg es usada por
defecto) y compilado con el script treereg que acompaña a la distribución de
Parse::Eyapp.
La primera fase en la ejecución de un programa Treeregexp
es la fase de creación del paquete Treeregexp
que contendrá las subrutinas
de reconocimiento de los patrones árbol definidos en el programa Treeregexp.
En el ejemplo esta fase tiene lugar en las líneas
74-111 con las llamadas a new (que crea el objeto programa Treeregexp)
y generate (que crea el paquete conteniendo las subrutinas reconocedoras).
74 my $transform = Parse::Eyapp::Treeregexp->new( STRING => q{
75
76 delete_code : CODE => { $delete_code->delete() }
... ................................................
103 },
104 PACKAGE => 'TSwithtreetransformations',
105 OUTPUTFILE => 'main.pm',
106 SEVERITY => 0,
107 NUMBERS => 0,
108 );
109
110 # Create the transformer
111 $transform->generate();
Durante esta fase se crea un objeto transformación i(perteneciente a la clase Parse::Eyapp::YATW )
por cada expresión regular árbol que aparece en el programa Treeregexp.
Las variables contenedor de cada uno de esos objetos tienen por nombre el que se
les dió a las correspondientes expresiones regulares árbol. En nuestro ejemplo, y después
de esta fase habrán sido creadas variables escalares de paquete
$delete_tokens, $delete, $uminus, $constantfold, $zero_times
y $times_zero
asociadas con cada una de las expresiones regulares árbol definidas.
También se crearán variables array para cada una de las
familias de transformaciones especificadas: Asi la variable
@delete contiene ($delete_tokens, $delete) y la variable
@algebraic_transformations
es igual a ($constantfold, $zero_times, $times_zero). La variable de paquete especial
@all es un array que contiene todas las transformaciones definidas en el programa.
Una vez creados los objetos transformación y las familias de transformaciones
Parse::Eyapp::YATW podemos proceder a transformar el árbol
mediante su uso.
Esto puede hacerse mediante el método s el cual procede
a modificar el árbol pasado como parámetro.
114 our ($uminus); 115 $uminus->s($tree);En el ejemplo la llamada
$uminus->s($tree) da lugar al
recorrido primero-profundo de $tree. Cada vez que un nodo
casa con la regla:
UMINUS(., NUM($x), .) # first child is '-', second the number and third the codese le aplica la transformación:
{ $x->{attr} = -$x->{attr}; $_[0] = $NUM }
Esta transformación hace que el nodo UMINUS visitado sea sustituido
por un nodo de tipo NUM cuyo atributo sea el número de su hijo
NUM cambiado de signo.
Las líneas 117-118 nos muestran como someter un árbol a un conjunto de transformaciones:
117 our (@algebraic_transformations); 118 $tree->s(@algebraic_transformations);Los objetos nodo (esto es, los que pertenecen a la clase Parse::Eyapp::Node ) disponen del método s que recibe como argumentos una familia de transformaciones. La familia de transformaciones es aplicada iterativamente al árbol hasta que este no cambia.
Nótese que consecuencia de esta definición es que es posible escribir transformaciones
que dan lugar a bucles infinitos. Por ejemplo si en @algebraic_transformations
incluimos una transformación que aplique las propiedades conmutativas de la
suma:
commutative_add: PLUS($x, ., $y, .)
=> { my $t = $x; $_[0]->child(0, $y); $_[0]->child(2, $t)}
el programa podría caer en un bucle infinito ya que la transformación es
susceptible de ser aplicada indefinidamente. Sin embargo no se produce bucle infinito
si llamamos al código asociado a la transformación:
$commutative_add->($tree);
ya que en este caso se produce un sólo recursivo descendente aplicando la transformación
$commutative_add.
El uso de transformaciones conmutativas no tiene porque dar lugar a
la no finalización del programa. La parada del programa se puede
garantizar si podemos asegurar que la aplicación reiterada
del patrón implica la desaparición del mismo. Por ejemplo,
la transformación comasocfold puede ser añadida
a la familia algebraic_transformations sin introducir
problemas de parada:
comasocfold: TIMES(DIV(NUM($x), ., $b), ., NUM($y))
=> {
$x->{attr} = $x->{attr} * $y->{attr};
$_[0] = $DIV;
}
algebraic_transformations = constantfold zero_times times_zero comasocfold;
La introducción de esta transformación permite el plegado de entradas como a=2;b=2/a*3:
nereida:~/src/perl/YappWithDefaultAction/examples> usetswithtreetransformations3.pl
a=2;b=2/a*3
$VAR1 = bless( { 'children' => [
bless( { 'children' => [
bless( { 'children' => [
bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' ),
bless( { 'children' => [
bless( { 'children' => [], 'attr' => 2, 'token' => 'NUM' }, 'TERMINAL' ) ],
't' => 2
}, 'NUM' )
],
't' => 'a 2 ='
}, 'ASSIGN' ),
bless( { 'children' => [
bless( { 'children' => [], 'attr' => 'b', 'token' => 'VAR' }, 'TERMINAL' ),
bless( { 'children' => [
bless( { 'children' => [
bless( { 'children' => [], 'attr' => 6, 'token' => 'NUM' }, 'TERMINAL' )
],
't' => 6
}, 'NUM' ),
bless( { 'children' => [
bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' )
],
't' => 'a'
}, 'VAR' )
],
't' => '6 a /'
}, 'DIV' )
],
't' => 'b 6 a / ='
}, 'ASSIGN' )
]
}, 'EXP' )
],
't' => [ 'a 2 =', 'b 6 a / =' ]
}, 'PROG' );
Una vez compilado el analizador, escribimos el programa que usa el módulo generado:
nereida:~/src/perl/YappWithDefaultAction/examples> cat -n usetswithtreetransformations.pl
1 #!/usr/bin/perl -w
2 use strict;
3 use TSwithtreetransformations;
4 use Parse::Eyapp::Treeregexp;
5
6 my $parser = TSwithtreetransformations->new();
7 $parser->Run;
Al ejecutarlo obtenemos la siguiente salida:
nereida:~/src/perl/YappWithDefaultAction/examples> eyapp TSwithtreetransformations.eyp ; \\
usetswithtreetransformations.pl
input: a=2*-3;b=a*(2-1-1);c=a+b
$VAR1 = bless( { 'children' => [
bless( { 'children' => [
| bless( { 'children' => [
| | bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' ),
| | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => -6, 'token' => 'NUM' }, 'TERMINAL' )
| | | ],
| | | 't' => -6
| | }, 'NUM' )
| | ],
| | 't' => 'a -6 ='
| }, 'ASSIGN' ),
| bless( { 'children' => [
| | bless( { 'children' => [], 'attr' => 'b', 'token' => 'VAR' }, 'TERMINAL' ),
| | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => 0, 'token' => 'NUM' }, 'TERMINAL' )
| | | ],
| | | 't' => 0
| | }, 'NUM' )
| | ],
| | 't' => 'b 0 ='
| }, 'ASSIGN' ),
| bless( { 'children' => [
| | bless( { 'children' => [], 'attr' => 'c', 'token' => 'VAR' }, 'TERMINAL' ),
| | bless( { 'children' => [
| | | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' )
| | | ],
| | | 't' => 'a'
| | | }, 'VAR' ),
| | | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => 'b', 'token' => 'VAR' }, 'TERMINAL' )
| | | ],
| | | 't' => 'b'
| | | }, 'VAR' )
| | | ],
| | | 't' => 'a b +'
| | }, 'PLUS' )
| | ],
| | 't' => 'c a b + ='
| }, 'ASSIGN' )
| ]
}, 'EXP' )
],
't' => [ 'a -6 =', 'b 0 =', 'c a b + =' ]
}, 'PROG' );
Como puede verse la traducción de la frase de
entrada a=2*-3;b=a*(2-1-1);c=a+b
queda como atributo t del nodo raíz PROG.
Una expresión regular árbol array se escribe insertando un array Perl
en la expresión regular árbol, por ejemplo @a.
Una expresión regular árbol array @a casa con la secuencia mas corta de hijos del nodo
tal que la siguiente expresión regular árbol (no array) casa.
La lista de nodos
que han casado con la expresión regular árbol array quedará en la variable
léxica @a.
Por ejemplo, despúes de un casamiento de un árbol $t con la expresión regular árbol
BLOCK(@a, ASSIGN($x, $e), @b), la variable léxica
@a contendrá la lista de nodos hijos de $t
que precede a la primera aparición de ASSIGN($x, $e).
Si no
existe expresión regular árbol siguiente - el caso de @b en el ejemplo -
la expresión regular
array casará con todos los nodos hijo a partir del último casamiento (no array).
Asi @b contendrá la lista de referencias a los nodos hijos
de $t posteriores al nodo ASSIGN($x, $e).
Es ilegal escribir dos expresiones regulares arbol array
seguidas (por ejemplo A(@a, @b)).
El siguiente ejemplo muestra como usar las expresiones árbol array para mover las asignaciones invariantes de un bucle fuera del mismo (líneas 104-116):
nereida:~/src/perl/YappWithDefaultAction/t> cat -n 34moveinvariantoutofloopcomplexformula.t
1 #!/usr/bin/perl -w
2 use strict;
5 use Parse::Eyapp;
6 use Data::Dumper;
7 use Parse::Eyapp::Treeregexp;
8
9 my $grammar = q{
10 %{
11 use Data::Dumper;
12 %}
13 %right '='
14 %left '-' '+'
15 %left '*' '/'
16 %left NEG
17 %tree
18
19 %%
20 block: exp <%name BLOCK + ';'> { $_[1] }
21 ;
22
23 exp: %name NUM
24 NUM
25 | %name WHILE
26 'while' exp '{' block '}'
27 | %name VAR
28 VAR
29 | %name ASSIGN
30 VAR '=' exp
31 | %name PLUS
32 exp '+' exp
33 | %name MINUS
34 exp '-' exp
35 | %name TIMES
36 exp '*' exp
37 | %name DIV
38 exp '/' exp
39 | %name UMINUS
40 '-' exp %prec NEG
41 | '(' exp ')' { $_[2] } /* Let us simplify a bit the tree */
42 ;
43
44 %%
.. .......................................................................
87 }; # end grammar
88
.. ..................
99 $parser->YYData->{INPUT} = "a =1000; c = 1; while (a) { c = c*a; b = 5; a = a-1 }\n";
100 my $t = $parser->Run;
101 print "\n***** Before ******\n";
102 print Dumper($t);
104 my $p = Parse::Eyapp::Treeregexp->new( STRING => q{
105 moveinvariant:
106 BLOCK(@prests, WHILE(VAR($b), BLOCK(@a, ASSIGN($x, $e), @c)), @possts )
107 and { is_invariant($ASSIGN, $WHILE) } /* Check if ASSIGN is invariant relative */
108 => { /* to the while loop */
109 my $assign = $ASSIGN;
110 $BLOCK[1]->delete($ASSIGN);
111 $BLOCK[0]->insert_before($WHILE, $assign);
112 }
113 },
114 #outputfile => 'main.pm',
115 firstline => 104,
116 );
Al ejecutar el programa con la entrada
"a =1000; c = 1; while (a) { c = c*a; b = 5; a = a-1 }\n"
obtenemos el árbol modificado:
bless( { 'children' => [
bless( { 'children' => [ # a = 1000
| bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' ),
| bless( { 'children' => [
| bless( { 'children' => [], 'attr' => '1000', 'token' => 'NUM' }, 'TERMINAL' )
| ]
| }, 'NUM' )
| ]
}, 'ASSIGN' ),
bless( { 'children' => [ # c = 1
| bless( { 'children' => [], 'attr' => 'c', 'token' => 'VAR' }, 'TERMINAL' ),
| bless( { 'children' => [ bless( { 'children' => [], 'attr' => '1', 'token' => 'NUM' }, 'TERMINAL' )
| ]
| }, 'NUM' )
| ]
}, 'ASSIGN' ),
bless( { 'children' => [ # b = 5 moved out of loop
| bless( { 'children' => [], 'attr' => 'b', 'token' => 'VAR' }, 'TERMINAL' ),
| bless( { 'children' => [ bless( { 'children' => [], 'attr' => '5', 'token' => 'NUM' }, 'TERMINAL' )
| ]
| }, 'NUM' )
| ]
}, 'ASSIGN' ),
bless( { 'children' => [ # while
| bless( { 'children' => [ # ( a )
| bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' )
| ]
| }, 'VAR' ),
| bless( { 'children' => [ # BLOCK {}
| | bless( { 'children' => [ # c = c * a
| | | bless( { 'children' => [], 'attr' => 'c', 'token' => 'VAR' }, 'TERMINAL' ),
| | | bless( { 'children' => [
| | | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => 'c', 'token' => 'VAR' }, 'TERMINAL' )
| | | ]
| | | }, 'VAR' ),
| | | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' )
| | | ]
| | | }, 'VAR' )
| | | ]
| | | }, 'TIMES' )
| | | ]
| | }, 'ASSIGN' ),
| | bless( { 'children' => [ # a = a - 1
| | | bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' ),
| | | bless( { 'children' => [
| | | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' )
| | | ]
| | | }, 'VAR' ),
| | | bless( { 'children' => [
| | | bless( { 'children' => [], 'attr' => '1', 'token' => 'NUM' }, 'TERMINAL' )
| | | ]
| | | }, 'NUM' )
| | | ]
| | | }, 'MINUS' )
| | | ]
| | }, 'ASSIGN' )
| | ]
| }, 'BLOCK' )
| ]
}, 'WHILE' )
]
}, 'BLOCK' );
Una expresión regular árbol estrella casa con la secuencia mas corta de hijos del nodos
tal que la siguiente expresión regular árbol casa. Si no
existe expresión regular árbol siguiente - esto es, la expresión regular
estrella es la última de la lista como en A(B(C,.), *)- la expresión regular
estrella casará con todos los nodos hijo a partir del último casamiento.
Una expresión regular árbol array se escribe insertando el símbolo *
en la expresión regular árbol. Las listas de nodos
que han casado con la expresiones regulares árbol estrella quedaran en las variables
léxicas @W_0, @W_1, @W_2, etc.
En este sentido una expresión regular árbol estrella no es mas que
una abreviación para la expresión regular árbol @W_#
siendo # el número de orden de aparición.
Como se ha mencionado anteriormente el compilador de expresiones
regulares árbol traduce cada transformación árbol en una subrutina Perl.
Con mayor precisión: se crea un objeto Parse::Eyapp:YATW que es el
encargado de gestionar la transformación. Para que una subrutina pueda
ser convertida en un objeto YATW deben ajustarse al Protocolo YATW de LLamada.
Actualmente (2006) la subrutina asociada con un objeto YATW es llamada como sigue:
pattern_sub(
$_[0], # Node being visited
$_[1], # Father of this node
$index, # Index of this node in @Father->children
$self, # The YATW pattern object
);
Los cuatro argumentos tienen el siguiente significado:
$_[0]) en la lista de nodos del padre
La subrutina debe devolver cierto (TRUE) si se produce matching
y falso en otro caso. Recuerde que el método s de los nodos (no así
el de los objetos YATW) permancerá aplicando las transformaciones
hasta que no se produzcan emparejamientos. Por tanto es importante
asegurarse cuando se usa la forma $node->s(@transformations)
que la aplicación reiterada de las transformaciones conduce
a situaciones en las que eventualmente las subrutinas retornan
el valor falso.
Es posible que el protocolo de llamada YATW cambie en un futuro próximo.

