ANTLR AST управляет сбоем с 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;} ;

Теперь я хочу добавить, что некоторые переписывают правила генерировать 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)*
    ;

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

8
задан Michael Petrotta 26 April 2010 в 03:07
поделиться

2 ответа

Нет, не ошибка, 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).

10
ответ дан 5 December 2019 в 10:01
поделиться

Если вы хотите сгенерировать 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))

что вам, вероятно, не нужно, поскольку умножение коммутативно.

7
ответ дан 5 December 2019 в 10:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: