Перед вашей следующей промысловой экспедицией вам следует внимательно прочитать раздел в руководстве по зубрам на , используя декларации предшествующих действий и, возможно, некоторые из связанных ответов SO .В руководстве Bison также есть очень полезная информация о понимании конфликтов и инструментах, которые Bison предоставляет вам.Здесь я в основном следую процедуре, описанной в последней ссылке.
Первый шаг - попросить Bison сгенерировать отчет о состояниях синтаксического анализатора, для чего просто нужно указать параметр -v
(или --report=all
, если вы хотите больше информации, что иногда полезно).Первая строка в результирующем файле .output
сообщает вам, в каких состояниях есть конфликты сдвига / уменьшения:
State 12 conflicts: 4 shift/reduce
Итак, следующий шаг - взглянуть на состояние 12. Конфликты указываются действиями парсера вскобки;Я выделил их, чтобы сделать их более заметными.(Действия, заключенные в квадратные скобки, - это те, которые устранены зубрами с использованием алгоритма разрешения по умолчанию, также описанного в руководстве.)
State 12
5 Expression: Expression . '+' Expression
7 | TOKEN_ADDROF Expression .
13 ExpressionDot: Expression . '.' Expression
14 ExpressionIndexable: Expression . '[' Expression ']'
15 ExpressionFunctionCall: Expression . '(' OptionalExpressions ')'
'.' shift, and go to state 15
'+' shift, and go to state 16
'(' shift, and go to state 17
'[' shift, and go to state 18
<b> '.' [reduce using rule 7 (Expression)]</b>
<b> '+' [reduce using rule 7 (Expression)]</b>
<b> '(' [reduce using rule 7 (Expression)]</b>
<b> '[' [reduce using rule 7 (Expression)]</b>
$default reduce using rule 7 (Expression)
Таким образом, в этом состоянии зубр не смог применить какое-либо правило приоритета, чтобы решитьв случае, если применяется правило 7.Правило 7 удобно воспроизводится в отчете:
7 | TOKEN_ADDROF Expression .
Приоритетом этого правила будет приоритет терминала TOKEN_ADDROF
.Но этот приоритет не определен, потому что TOKEN_ADDROF
не появляется ни на одном уровне приоритета.
Мы можем попытаться добавить его:
%left '.' '+'
%precedence TOKEN_ADDROF
%precedence '(' '['
И, эй presto!
Было бы справедливо спросить, почему я положил его туда, куда я его положил, и почему я использовал %precedence
вместо %left
или %right
, как для него, так и для других унарных операторов.
Для началасо вторым вопросом %precedence
означает «этот уровень приоритета включает в себя операторы, для которых конфликт не может быть разрешен с помощью ассоциативности, поэтому я не буду объявлять какую-либо конкретную ассоциативность».
И это верно в этом случае: унарные операторы не имеют ассоциативности. - в 3-4-7
может ассоциироваться слева ((3-4)-7)
) или справа (3-(4-7)
).Разрешение будет принято на основе Expression
продукции с приоритетом -
(Expression: Expression '-' Expression
) и маркера предварительного просмотра с приоритетом -
( - ).Это, очевидно, может случиться.В качестве ограничения рассмотрим оператор TOKEN_ADDROF
(кстати, это не & ? Если так, просто запишите его как символьный токен.) Здесь соответствующая продукция, как мы уже видели,равно
Expression: TOKEN_ADDROF Expression
Теперь, что, если жетон предвкушения равен TOKEN_ADDROF
?Ответ: это синтаксическая ошибка, потому что TOKEN_ADDROF
не является двоичным оператором, поэтому он не может следовать за выражением.(Может случиться так, что у вас есть бинарный оператор с таким же написанием. Но в этом случае вы бы положили %prec UNOP
на вышеупомянутую продукцию, и тогда не было бы никакой возможности, чтобы маркер предпросмотра мог быть UNOP
, потому что этотокен никогда не создается лексическим сканером.) Таким образом, не существует производства, которое допускает сдвиг и, следовательно, никакого конфликта.
Аналогичная аргументация применима к постфиксным операторам, таким как приложение функций и подписка.(И после увеличения и после уменьшения, если применимо.) В этих случаях возможен следующий постфиксный оператор, но он не может быть сдвинут до тех пор, пока производство Expression POSTFIX
не будет уменьшено.Опять же, никакого возможного конфликта нет.
Так что в случае постфиксных операторов единственное сравнение приоритетов будет между уровнями, а не с уровнем, и ассоциативность не применяется.Если вы не укажете ассоциативность, бизон выдаст предупреждение о конфликте, если вы случайно ошибочно наберете грамматику таким образом, чтобы разрешить ассоциативность (например, не вставив объявление %prec UNOP
там, где это необходимо), вместо того, чтобы молча игнорировать ошибку.
В этой конкретной грамматике указание '['
и '('
на уровне приоритета не требуется, так как эти токены не используются напрямую в любом Expression
производстве.Это означает, что грамматика обеспечивает явный приоритет для этих операторов.Использование как деклараций предшествования, так и явного приоритета в одной и той же грамматике часто является признаком того, что различные части грамматики были скопированы и вставлены из разных источников.(Просто говорю.) Обычно это не считается хорошим стилем, хотя иногда это оправдано.В этом случае я бы предложил использовать явный или объявленный приоритет явно.
Итак, давайте предположим, что уровни приоритета должны быть объявлены.В таком случае, почему я поставил постфиксные операторы в конце?Ответ: потому что это общее правило (хотя и не абсолютное правило), что постфиксные операции связываются более тесно, чем префиксные операции.Например, -arr[i]
означает , а не означает (-arr)[i]
.Это так очевидно, что большинство людей не думают об этом, хотя иногда им не удастся применить правило к *arr[i]
и *arr++
, которые имеют точно такие же отношения предшествования.
Надеюсь, это поможет.