Правила ANTLR AST завершаются с ошибкой RewriteEmptyStreamException - PullRequest
8 голосов
/ 26 апреля 2010

У меня есть простая грамматика:

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

Теперь я хочу добавить несколько правил перезаписи для генерации AST. Из того, что я прочитал в Интернете и в книге «Языковые шаблоны», я смогу изменить грамматику следующим образом:

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

Но это не работает. Хотя он компилируется нормально, когда я запускаю парсер, я получаю ошибку RewriteEmptyStreamException. Вот где все становится странным.

Если я определю псевдотекены ADD и MULT и использую их вместо литералов узлов дерева, это будет работать без ошибок.

tokens { ADD; MULT; }

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

В качестве альтернативы, если я использую обозначение суффикса узла, оно также работает нормально:

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

Является ли это несоответствие в поведении ошибкой?

Ответы [ 2 ]

10 голосов
/ 26 апреля 2010

Нет, не ошибка, AFAIK. Возьмите правило expr, например:

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

, поскольку * может отсутствовать, его также не должно быть в вашем правиле перезаписи AST. Таким образом, вышеприведенное неверно и ANTLR жалуется на это является правильным.

Теперь, если вместо этого вы вставите воображаемый токен, такой как MULT:

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

все в порядке, так как ваше правило всегда будет производить один или несколько factor.

То, что вы, вероятно, хотели сделать, примерно так:

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

Также см. главу 7: Построение дерева из Полное руководство по ANTLR . Особенно параграфы Правила перезаписи в подправилах (стр. 173) и Ссылки на AST предыдущих правил в правилах перезаписи (стр. 174/175).

7 голосов
/ 28 мая 2010

Если вы хотите сгенерировать N-арное дерево для оператора '*' со всеми дочерними элементами на одном уровне, вы можете сделать это:

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

Вот несколько примеров того, что это вернет:

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

Третий пример Барта, приведенный выше, создаст вложенное дерево, поскольку результатом $ expr для каждой последующей итерации является узел с двумя дочерними элементами, например:

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

который вам, вероятно, не нужен, так как умножение коммутативно.

...