2010-04-26 9 views
8

tengo una gramática sencilla:reglas antlr AST fallar con RewriteEmptyStreamException

grammar sample; 
options { output = AST; } 
assignment 
    : IDENT ':=' expr ';' 
    ; 
expr  
    : factor ('*' factor)* 
    ; 
factor 
    : primary ('+' primary)* 
    ; 
primary 
    : NUM 
    | '(' expr ')' 
    ; 
IDENT : ('a'..'z')+ ; 
NUM : ('0'..'9')+ ; 
WS : (' '|'\n'|'\t'|'\r')+ {$channel=HIDDEN;} ; 

Ahora quiero añadir algunas reglas de reescritura para generar un AST. Por lo que he leído en línea y en el libro de patrones de lenguaje, yo debería ser capaz de modificar la gramática de esta manera:

assignment 
    : IDENT ':=' expr ';' -> ^(':=' IDENT expr) 
    ; 
expr  
    : factor ('*' factor)* -> ^('*' factor+) 
    ; 
factor 
    : primary ('+' primary)* -> ^('+' primary+) 
    ; 
primary 
    : NUM 
    | '(' expr ')' -> ^(expr) 
    ; 

Pero no funciona. Aunque compila bien, cuando ejecuto el analizador obtengo un error RewriteEmptyStreamException. Aquí es donde las cosas se ponen raras.

Si defino los pseudocódigos ADD y MULT y los uso en lugar de los literales del nodo de árbol, funciona sin error.

tokens { ADD; MULT; } 

expr  
    : factor ('*' factor)* -> ^(MULT factor+) 
    ; 
factor 
    : primary ('+' primary)* -> ^(ADD primary+) 
    ; 

Alternativamente, si utilizo la notación nodo sufijo, sino que también parece funcionar bien:

expr  
    : factor ('*'^ factor)* 
    ; 
factor 
    : primary ('+'^ primary)* 
    ; 

¿Es esta discrepancia en el comportamiento de un error?

Respuesta

10

No, no es un error, AFAIK. Tome su regla expr por ejemplo:

expr  
    : factor ('*' factor)* -> ^('*' factor+) 
    ; 

ya que el * podría no estar presente, tampoco debe estar en su regla de reescritura AST. Por lo tanto, lo anterior es incorrecto y ANTLR quejándose de él es correcto.

Ahora bien, si se inserta un token imaginaria como MULT lugar:

expr  
    : factor ('*' factor)* -> ^(MULT factor+) 
    ; 

todo está bien ya que su regla siempre producirá una o más factor 's.

Lo que probablemente significaba que hacer es algo como esto:

expr  
    : (factor -> factor) ('*' f=factor -> ^('*' $expr $f))* 
    ; 

ver también el capítulo 7 : Árbol de construcción de The Definitive ANTLR Reference. Especialmente los párrafos Reglas de reescritura en las Subrúbricas (página 173) y Hacer referencia a los AST de reglas anteriores en Reglas de reescritura (página 174/175).

7

Si desea generar un árbol de N-aria para el operador '*' con todos los niños, al mismo nivel que usted puede hacer esto:

expr 
    : (s=factor -> factor) (('*' factor)+ -> ^('*' $s factor+))? 
    ; 

Éstos son algunos ejemplos de lo que esto devolverá:

Tokens: AST 
factor: factor 
factor '*' factor: ^('*' factor factor) 
factor '*' factor '*' factor: ^('*' factor factor factor) 

tercer ejemplo de Bart anterior producirá un árbol anidado, ya que el resultado de $ expr para cada iteración sucesiva es un nodo con dos hijos, así:

factor * factor * factor: ^('*' factor ^('*' factor factor)) 

que probablemente no necesite ya que la multiplicación es conmutativa.

+0

Gracias a ton @JoelPM. Esto es exactamente lo que estaba buscando. Tuvimos un problema con los desbordamientos de pila y árboles anidados mientras evaluamos.Esto nos da la oportunidad de generar un árbol N-ary y reduce drásticamente la profundidad del árbol –