У меня есть простая грамматика:
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)*
;
Является это несоответствие в поведении ошибкой?
Нет, не ошибка, 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).
Если вы хотите сгенерировать 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))
что вам, вероятно, не нужно, поскольку умножение коммутативно.