next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: La opción alias de Sup: Análisis Sintáctico con Parse::Eyapp Ant: Nombres para los Atributos Err: Si hallas una errata ...


Bypass Automático

La directiva %tree admite la opción bypass . Esta opción ayuda en la fase de construcción del AST automatizando operaciones de bypass. Entendemos por operación de bypass a la operación de retornar el nodo hijo al nodo padre del actual (abuelo del hijo), obviando así la presencia del nodo actual en el AST final.

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:

Un Ejemplo con bypass Automático

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

Rutinas de Soporte

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  }

El Programa Cliente

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";

Ejecuciones

Veamos las salidas obtenidas sobre diversas entradas:
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

La Directiva no bypass

La cláusula 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

El Método YYBypassrule

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



Subsecciones
next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: La opción alias de Sup: Análisis Sintáctico con Parse::Eyapp Ant: Nombres para los Atributos Err: Si hallas una errata ...
Casiano Rodríguez León
2012-05-22