Имеет ли значение, если мои правила лексера появятся раньше, чем правила синтаксического анализа в моей грамматике ...
Нет, это не имеет значения.
Проблема в том, что в вашем правиле atom
ANTLR не может сделать выбор между этими тремя вариантами:
ID ( ...
ID [ ...
ID
не прибегая к (возможно) возврату. Вы можете решить это, используя некоторые синтаксические предикаты (которые выглядят так: (...)=> ...
). Синтаксические предикаты - это не что иное, как «взгляд в будущее», и если этот «взгляд в будущее» успешен, он выбирает именно этот путь.
Ваше текущее atom
правило может быть переписано следующим образом:
atom
: OPENPAR expr CLOSEPAR
| ID OPENPAR ((expr (COMMA)* )+)? CLOSEPAR
| ID OPENBRACKET expr CLOSEBRACKET
| ID
| CALLOUT OPENPAR STRING (COMMA (calloutarg)+ COMMA)? CLOSEPAR
| constant
;
А с предикатами это будет выглядеть так:
atom
: OPENPAR expr CLOSEPAR
| (ID OPENPAR)=> ID OPENPAR ((expr (COMMA)* )+)? CLOSEPAR
| (ID OPENBRACKET)=> ID OPENBRACKET expr CLOSEBRACKET
| ID
| CALLOUT OPENPAR STRING (COMMA (calloutarg)+ COMMA)? CLOSEPAR
| constant
;
что должно сделать трюк.
Примечание: не используйте ANTLRWorks для генерации или тестирования парсера! Он не может обрабатывать предикаты (хорошо). Лучше всего делать это в командной строке.
Также см .: https://wincent.com/wiki/ANTLR_predicates
EDIT
Давайте обозначим шесть различных "ветвей" из вашего atom
правила от A
до F
:
atom // branch
: OPENPAR expr CLOSEPAR // A
| ID OPENBRACKET expr CLOSEBRACKET // B
| ID OPENPAR ((expr COMMA*)+)? CLOSEPAR // C
| ID // D
| CALLOUT OPENPAR STRING (COMMA calloutarg+ COMMA)? CLOSEPAR // E
| constant // F
;
Теперь, когда (будущий) парсер должен обрабатывать ввод следующим образом:
ID OPENPAR expr CLOSEPAR
ANTLR не знает, как парсер должен с этим справиться. Его можно проанализировать двумя разными способами:
- ветвь
D
, за которой следует ветвь A
- филиал
C
Который является источником неоднозначности, на которую жалуется ANTLR. Если вы закомментируете одну из веток A
, C
или D
, ошибка исчезнет.
Надеюсь, это поможет.