La opción de bypass modifica la forma en la que Eyapp construye el árbol:
Si se usa la opción bypass, Eyapp hará automáticamente una operación
de bypass a todo nodo que
resulte con un sólo hijo en tiempo de construcción el árbol.
Además Eyapp cambiará la clase del nodo hijo a la clase del
nodo siendo puenteado si a este se le dió un nombre explícitamente
a través de la directiva %name
.
Hay dos razones principales por las que un nodo pueda resultar - en tiempo de construcción del árbol - con un sólo hijo:
25 exp: 26 %name NUM 27 NUM 28 | %name VAR 29 VAREn este caso un árbol de la forma
PLUS(NUM(TERMINAL[4]), VAR(TERMINAL[a])
se verá transformado en PLUS(NUM[4], VAR[a])
. El primer nodo
TERMINAL
será rebautizado (re-bendecido) en la clase NUM
.
y el segundo en la clase VAR
.
Observe que este proceso de renombre ocurre sólo si
el nodo eliminado tiene un nombre explícito dado con la directiva
%name
. Así en la regla unaria:
pl@nereida:~/LEyapp/examples$ sed -ne '/^line:/,/^;$/p' TreeBypass.eyp | cat -n 1 line: 2 exp '\n' 3 ;ocurre un bypass automático pero el nodo hijo de
exp
conserva su nombre.
Por ejemplo, un árbol como
exp_10(PLUS(NUM(TERMINAL),NUM(TERMINAL)))
se transforma en
PLUS(NUM,NUM)
. Mientras que los nodos TERMINAL
fueron renombrados
como nodos NUM
el nodo PLUS
no lo fué.
Por ejemplo, un árbol como:
PAREN(PLUS(NUM( TERMINAL[2]),NUM(TERMINAL[3])))se convierte en:
PLUS(NUM[2],NUM[3])
pl@nereida:~/LEyapp/examples$ cat -n TreeBypass.eyp 1 # TreeBypass.eyp 2 %right '=' 3 %left '-' '+' 4 %left '*' '/' 5 %left NEG 6 %right '^' 7 8 %tree bypass 9 10 %{ 11 sub NUM::info { 12 my $self = shift; 13 14 $self->{attr}; 15 } 16 17 *VAR::info = \&NUM::info; 18 %} 19 %%Dado que los nodos
TERMINAL
serán rebautizados como nodos VAR
y NUM
,
dotamos a dichos nodos de métodos info
(líneas 11-17)
que serán usados durante
la impresión del árbol con el método str
.
21 line: 22 exp '\n' 23 ; 24 25 exp: 26 %name NUM 27 NUM 28 | %name VAR 29 VAR 30 | %name ASSIGN 31 var '=' exp 32 | %name PLUS 33 exp '+' exp 34 | %name MINUS 35 exp '-' exp 36 | %name TIMES 37 exp '*' exp 38 | %name DIV 39 exp '/' exp 40 | %name UMINUS 41 '-' exp %prec NEG 42 | %name EXPON 43 exp '^' exp 44 | '(' exp ')' 45 ; 46 47 var: 48 %name VAR 49 VAR 50 ; 51 %%Observe las líneas 30-31 y 47-49. La gramática original tenía sólo una regla para la asignación:
exp: %name NUM NUM | %name VAR VAR | %name ASSIGN VAR '=' expSi se hubiera dejado en esta forma un árbol como
ASSIGN(TERMINAL[a], NUM(TERMINAL[4]))
quedaría como
ASSIGN(TERMINAL[a], NUM[4])
.
La introdución de la variable sintáctica var
en la regla de asignación y de su regla unaria (líneas 47-50)
produce un bypass y da lugar al árbol
ASSIGN(VAR[a], NUM[4])
.
53 sub _Error { 54 exists $_[0]->YYData->{ERRMSG} 55 and do { 56 print $_[0]->YYData->{ERRMSG}; 57 delete $_[0]->YYData->{ERRMSG}; 58 return; 59 }; 60 print "Syntax error.\n"; 61 } 62 63 my $input; 64 65 sub _Lexer { 66 my($parser)=shift; 67 68 # topicalize $input 69 for ($input) { 70 s/^[ \t]+//; # skip whites 71 return('',undef) unless $_; 72 return('NUM',$1) if s{^([0-9]+(?:\.[0-9]+)?)}{}; 73 return('VAR',$1) if s/^([A-Za-z][A-Za-z0-9_]*)//; 74 return($1,$1) if s/^(.)//s; 75 } 76 return('',undef); 77 } 78 79 sub Run { 80 my($self)=shift; 81 82 $input = shift; 83 print $input; 84 return $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error, 85 #yydebug => 0xF 86 ); 87 }
pl@nereida:~/LEyapp/examples$ cat -n usetreebypass.pl 1 #!/usr/bin/perl -w 2 # usetreebypass.pl prueba2.exp 3 use strict; 4 use TreeBypass; 5 6 sub slurp_file { 7 my $fn = shift; 8 my $f; 9 10 local $/ = undef; 11 if (defined($fn)) { 12 open $f, $fn or die "Can't find file $fn!\n"; 13 } 14 else { 15 $f = \*STDIN; 16 } 17 my $input = <$f>; 18 return $input; 19 } 20 21 my $parser = TreeBypass->new(); 22 23 my $input = slurp_file( shift() ); 24 my $tree = $parser->Run($input); 25 die "There were errors\n" unless defined($tree); 26 27 $Parse::Eyapp::Node::INDENT = 2; 28 print $tree->str."\n";
pl@nereida:~/LEyapp/examples$ eyapp TreeBypass.eyp pl@nereida:~/LEyapp/examples$ usetreebypass.pl prueba2.exp a=(2+b)*3 ASSIGN( VAR[a], TIMES( PLUS( NUM[2], VAR[b] ), NUM[3] ) # TIMES ) # ASSIGN pl@nereida:~/LEyapp/examples$ usetreebypass.pl prueba3.exp a=2-b*3 ASSIGN( VAR[a], MINUS( NUM[2], TIMES( VAR[b], NUM[3] ) ) # MINUS ) # ASSIGN pl@nereida:~/LEyapp/examples$ usetreebypass.pl prueba4.exp 4*3 TIMES( NUM[4], NUM[3] ) pl@nereida:~/LEyapp/examples$ usetreebypass.pl prueba5.exp 2-)3*4 Syntax error. There were errors
bypass
suele producir un
buen número de podas y reorganizaciones del árbol.
Es preciso tener especial cuidado en su uso.
De hecho, el programa anterior contiene errores.
Obsérvese la conducta del analizador para la entrada
-(2-3)
:
pl@nereida:~/LEyapp/examples$ usetreebypass.pl prueba7.exp -(2-3) UMINUS( NUM[2], NUM[3] )
Que es una salida errónea: además de los bypasses en los terminales
se ha producido un bypass adicional sobre el nodo
MINUS
en el árbol original
UMINUS(MINUS(NUM(TERMINAL[2],NUM(TERMINAL[3]))))
dando lugar al árbol:
UMINUS(NUM[2],NUM[3])
El bypass automático se
produce en la regla del menos unario ya que
el terminal '-'
es por defecto -
un terminal sintáctico:
25 exp: .. ........... 40 | %name UMINUS 41 '-' exp %prec NEG
Mediante la aplicación de la directiva
%no bypass UMINUS
a la regla (línea 16 abajo)
inhibimos la aplicación del bypass a la misma:
pl@nereida:~/LEyapp/examples$ sed -ne '/^exp:/,/^;$/p' TreeBypassNoBypass.eyp | cat -n 1 exp: 2 %name NUM 3 NUM 4 | %name VAR 5 VAR 6 | %name ASSIGN 7 var '=' exp 8 | %name PLUS 9 exp '+' exp 10 | %name MINUS 11 exp '-' exp 12 | %name TIMES 13 exp '*' exp 14 | %name DIV 15 exp '/' exp 16 | %no bypass UMINUS 17 '-' exp %prec NEG 18 | %name EXPON 19 exp '^' exp 20 | '(' exp ')' 21 ;
pl@nereida:~/LEyapp/examples$ usetreebypassnobypass.pl prueba7.exp -(2-3) UMINUS( MINUS( NUM[2], NUM[3] ) ) # UMINUS
Aún mas potente que la directiva es usar el método YYBypassrule el cual permite modificar dinámicamente el estatus de bypass de una regla de producción. Vea esta nueva versión del anterior ejemplo:
pl@nereida:~/LEyapp/examples$ sed -ne '/%%/,/%%/p' TreeBypassDynamic.eyp | cat -n 1 %% 2 3 line: 4 exp '\n' 5 ; 6 7 exp: 8 %name NUM 9 NUM 10 | %name VAR 11 VAR 12 | %name ASSIGN 13 var '=' exp 14 | %name PLUS 15 exp '+' exp 16 | %name MINUS 17 exp '-' exp 18 | %name TIMES 19 exp '*' exp 20 | %name DIV 21 exp '/' exp 22 | %name UMINUS 23 '-' exp %prec NEG 24 { 25 $_[0]->YYBypassrule(0); 26 goto &Parse::Eyapp::Driver::YYBuildAST; 27 } 28 | %name EXPON 29 exp '^' exp 30 | '(' exp ')' 31 ; 32 33 var: 34 %name VAR 35 VAR 36 ; 37 %%El analizador produce un árbol sintáctico correcto cuando aparecen menos unarios:
pl@nereida:~/LEyapp/examples$ usetreebypassdynamic.pl -(2--3*5) -(2--3*5) UMINUS( MINUS( NUM[2], TIMES( UMINUS( NUM[3] ), NUM[5] ) # TIMES ) # MINUS ) # UMINUS