Для этого конкретного случая вы можете изменить правило для отрицательных выражений на
expr: '-' '(' expr ')'
и распознавать отрицания только в выражениях в скобках. Это, однако, не распознает двойные негативы (например, - - x
) и, что более важно, не будет масштабироваться, поскольку оно сломается, если вы попытаетесь добавить другие унарные операторы.
Теперь вы можете просто поместить правила num
ДО правил expr
и разрешить разрешение конфликтов уменьшения / уменьшения по умолчанию, чтобы справиться с ним (первое правило, появляющееся в файле, будет использоваться, если оба варианта возможны), но Это довольно уродливо, потому что вы получаете эти предупреждения о конфликтах каждый раз, когда запускаете зубров, и игнорировать их, когда вы точно не знаете, что происходит, - плохая идея.
Общий способ устранения такой неоднозначности заключается в разделении грамматики на разделение нарушающего правила на два правила и использовании соответствующей версии в каждом контексте, чтобы избежать конфликтов. В этом случае вы бы разбили expr
на num_expr
для выражений, которые начинаются с num
и non_num_expr
для других выражений:
expr: num_expr | non_num_expr ;
num_expr: num_expr '+' expr
| num_expr '-' expr
| num
;
non_num_expr: non_num_expr '+' expr
| non_num_expr '-' expr
| '-' non_num_expr
| '(' expr ')'
;
По сути, каждое правило для expr
, начинающееся с expr
в RHS, должно быть продублировано, а другие варианты использования expr
, возможно, придется изменить на один из вариантов, чтобы избежать конфликта.
К сожалению, в этом случае это не работает чисто, так как вы используете уровни приоритета, чтобы разрешить внутреннюю неоднозначность грамматики выражения, и факторные правила мешают этому - дополнительный один шаг правила вызывают проблемы. Таким образом, вам необходимо либо исключить эти правила (дублируя каждое правило с expr
на RHS - одно с версией num_expr
, а другое с non_num_version
ИЛИ вам нужно реорганизовать грамматику с дополнительными правилами для старшинство / ассоциативность
expr: expr '+' term
| expr '-' term
| term
;
term: non_num_term | num_term ;
non_num_term: '-' non_num_term
| '(' expr ')'
;
num_term: num ;
Обратите внимание, что в этом случае факторинг num / non_num был выполнен для term
, а не expr