Когда вы генерируете лексер и парсер из вашей грамматики, вы видите следующую ошибку, напечатанную на вашей консоли:
ошибка (211): CoffeeScript.g: 52: 3: [фатальный] выражение правила имеет решение без LL (*) из-за рекурсивных вызовов правил, достижимых из alts 1 , 3. Решить с помощью левого факторинга или используя синтаксические предикаты или используя параметр backtrack = true.
предупреждение (200): CoffeeScript.g: 52: 3: Решение может соответствовать вводу, такому как "{NUMBER, STRING}", используя несколько альтернатив: 1, 3
В результате альтернативный вариант (ы) 3 были отключены для этого входа
(я подчеркнул важные биты)
Это только первая ошибка, но вы начинаете с первой и, если повезет, ошибки ниже первой также исчезнут, когда вы исправите первую.
Вышеприведенная ошибка означает, что когда вы пытаетесь проанализировать NUMBER
или STRING
с анализатором, сгенерированным из вашей грамматики, анализатор может пойти двумя путями, когда он окажется в правиле expression
:
expression
: value // choice 1
| assign // choice 2
| operation // choice 3
;
А именно, вариант 1 и вариант 3 могут анализировать NUMBER
или STRING
, как вы можете видеть по "путям", по которым анализатор может следовать, чтобы соответствовать этим двум вариантам:
выбор 1:
expression
value
literal
alphaNumeric : {NUMBER, STRING}
выбор 3:
expression
operation
logicOp
relationOp
shiftOp
additiveOp
mathOp
questionOp
term
value
literal
alphaNumeric : {NUMBER, STRING}
В последней части предупреждения ANTLR информирует вас о том, что он игнорирует вариант 3 всякий раз, когда NUMBER
или STRING
будет проанализирован, в результате чего выбор 1 будет соответствовать такому вводу (так как он определен до выбора 3) .
Итак, либо грамматика CoffeeScript неоднозначна в этом отношении (и каким-то образом обрабатывает эту неоднозначность), либо ваша реализация неверна (я предполагаю последнее :)). Вам необходимо исправить эту неоднозначность в вашей грамматике: не допускайте, чтобы варианты expression
1 и 3 совпадали с одним и тем же вводом.
Я заметил еще 3 вещи в вашей грамматике:
1
Примите следующие правила лексера:
NEW : 'new';
...
UNARY : '!' | '~' | NEW;
Имейте в виду, что токен UNARY
никогда не может соответствовать тексту 'new'
, поскольку токен NEW
определен перед ним. Если вы хотите, чтобы UNARY
сделал это, удалите правило NEW
и выполните:
UNARY : '!' | '~' | 'new';
2
В некоторых случаях вы собираете несколько типов токенов в один, например LOGIC
:
LOGIC : '&&' | '||';
и затем вы используете этот токен в правилах синтаксического анализа:
logicOp
: compareOp (LOGIC compareOp)*
;
Но если вы собираетесь оценить такое выражение на более позднем этапе, вы не знаете, что соответствует этому LOGIC
токену ('&&'
или '||'
), и вам придется проверить внутреннее состояние токена текст, чтобы узнать это. Вам лучше сделать что-то вроде этого (по крайней мере, если вы делаете какую-то оценку на более позднем этапе):
AND : '&&';
OR : '||';
...
logicOp
: compareOp ( AND compareOp // easier to evaluate, you know it's an AND expression
| OR compareOp // easier to evaluate, you know it's an OR expression
)*
;
3
Вы пропускаете пробелы (и без табуляции?) С:
WS : (' ')+ {skip();} ;
но разве CoffeeScript не отступает, это блок кода с пробелами (и табуляциями), как Python? Но, возможно, вы собираетесь сделать это на более позднем этапе?
Я только что увидел, что грамматика, которую вы смотрите - это грамматика jison (которая более или менее реализована в JavaScript). Но бизон и, следовательно, jison генерируют парсеры LR , а ANTLR генерирует парсеры LL . Поэтому попытка придерживаться правил исходной грамматики приведет только к большему количеству проблем.