Manejo en jison de Atributos Heredados

Supongamos que jison esta inmerso en la construcción de la antiderivación a derechas y que la forma sentencial derecha en ese momento es:

$ X_m \ldots X_1 X_0 Y_1 \ldots Y_n a_1 \ldots a_0$

y que el mango es $ B \rightarrow Y_1 \ldots Y_n$ y en la entrada quedan por procesar $ a_1 \ldots a_0$.

No es posible acceder en jison a los valores de los atributos de los estados en la pila del analizador que se encuentran ``por debajo'' o si se quiere ``a la izquierda'' de los estados asociados con la regla por la que se reduce.

Vamos a usar un pequeño hack para acceder a los atributos asociados con símbolos vistos en el pasado "remoto":

[~/jison/jison-inherited(grammar)]$ cat inherited.jison 
%lex
%%
\s+                           {}
(global|local|integer|float)  { return yytext; }
[a-zA-Z_]\w*                  { return 'id'; }
.                             { return yytext; }
/lex
%%
D 
  : C T L 
  ;

C
  : global
  | local
  ; 
  
T
  : integer
  | float
;

L 
  : L ',' id    { 
                  console.log("L -> L ',' id ("+yytext+")");
                  var s =  eval('$$');
                  console.log(s);
                }
  | id          {
                  console.log("L -> id ("+yytext+")");
                  var s =  eval('$$');
                  console.log(s);
                }
  ;
%%

Veamos un ejemplo de ejecución:

[~/jison/jison-inherited(grammar)]$ cat input.txt 
global integer a, b, c
[~/jison/jison-inherited(grammar)]$ node inherited.js input.txt 
L -> id (a)
[ null, 'global', 'integer', 'a' ]
L -> L ',' id (b)
[ null, 'global', 'integer', 'a', ',', 'b' ]
L -> L ',' id (c)
[ null, 'global', 'integer', 'a', ',', 'c' ]

Esta forma de acceder a los atributos es especialmente útil cuando se trabaja con atributos heredados. Esto es, cuando un atributo de un nodo del árbol sintáctico se computa en términos de valores de atributos de su padre y/o sus hermanos. Ejemplos de atributos heredados son la clase y tipo en la declaración de variables.

Es importante darse cuenta que en cualquier derivación a derechas desdeD, cuando se reduce por una de las reglas

L $ \rightarrow$ id $ \vert$ L$ _1$ ',' id

el símbolo a la izquierda de L es T y el que esta a la izquierda de T es C. Considere, por ejemplo la derivación a derechas:

D $ \Longrightarrow$ C T L $ \Longrightarrow$ C T L, id $ \Longrightarrow$ C T L, id, id $ \Longrightarrow$ C T id, id, id $ \Longrightarrow$
$ \Longrightarrow$ C float id, id, id $ \Longrightarrow$ local float id, id, id

Observe que el orden de recorrido de jison es:

local float id, id, id $ \Longleftarrow$ C float id, id $ \Longleftarrow$ C T id, id, id $ \Longleftarrow$
$ \Longleftarrow$ C T L, id, id $ \Longleftarrow$ C T L, id $ \Longleftarrow$ C T L $ \Longleftarrow$ D

en la antiderivación, cuando el mango es una de las dos reglas para listas de identificadores, L $ \rightarrow$ id y L $ \rightarrow$ L, id es decir durante las tres ultimas antiderivaciones:

C T L, id, id $ \Longleftarrow$ C T L, id $ \Longleftarrow$ C T L $ \Longleftarrow$ D

las variables a la izquierda del mango son T y C. Esto ocurre siempre. Estas observaciones nos conducen al siguiente programa jison:

[~/jison/jison-inherited(deepstack)]$ cat inherited.jison 
%lex
%%
\s+                           {}
(global|local|integer|float)  { return yytext; }
[a-zA-Z_]\w*                  { return 'id'; }
.                             { return yytext; }
/lex
%%
D 
  : C T L 
  ;

C
  : global
  | local
  ; 
  
T
  : integer
  | float
  ;

L 
  : L ',' id    { 
                  var s =  eval('$$');
                  var b0 = s.length - 3;

                  console.log("L -> L ',' id ("+yytext+")");
                  console.log($id + ' is of type ' + s[b0-1]);
                  console.log(s[b0] + ' is of class ' + s[b0-2]);
                }
  | id          {
                  var s =  eval('$$');
                  var b0 = s.length - 1;

                  console.log("L -> id ("+yytext+")");
                  console.log($id + ' is of type ' + s[b0-1]);
                  console.log(s[b0] + ' is of class ' + s[b0-2]);
                }
  ;
%%

A continuación sigue un ejemplo de ejecución:

[~/jison/jison-inherited(deepstack)]$ node inherited.js input.txt 
L -> id (a)
a is of type integer
a is of class global
L -> L ',' id (b)
b is of type integer
a is of class global
L -> L ',' id (c)
c is of type integer
a is of class global

En este caso, existen varias alternativas simples a esta solución:

Casiano Rodríguez León
2016-03-27