

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 '=' exp
Si 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
 

