Почему эта простая грамматика имеет конфликт сдвига / уменьшения? - PullRequest
3 голосов
/ 15 марта 2012
%token <token> PLUS MINUS INT
%left PLUS MINUS

ЭТО РАБОТАЕТ:

exp : exp PLUS exp;
exp : exp MINUS exp;
exp : INT;

У ЭТОГО 2 КОНФЛИКТА СМЕНА / СНИЖЕНИЕ:

exp : exp binaryop exp;
exp : INT;
binaryop: PLUS | MINUS ;

Почему?

Ответы [ 4 ]

4 голосов
/ 16 марта 2012

Вам необходимо указать приоритет для правила exp binop exp, если вы хотите, чтобы правила приоритета разрешили неоднозначность:

exp : exp binaryop exp %prec PLUS;

С этим изменением все конфликты будут разрешены.

Редактировать

Кажется, что комментарии указывают на некоторую путаницу относительно того, что делают правила приоритета в yacc / bison.

Правила приоритета - это способ полуавтоматического разрешениясдвиг / уменьшение конфликтов в грамматике.Они являются лишь полуавтоматическими в том смысле, что вы должны знать, что вы делаете, когда задаете приоритеты.

По сути, всякий раз, когда возникает конфликт сдвига / уменьшения между токеном, подлежащим сдвигу, и правилом, которым нужно бытьпри уменьшении yacc сравнивает приоритет токена, который должен быть смещен, и правило, которое должно быть уменьшено, и - если оба имеют назначенные приоритеты - делает то, что имеет больший приоритет.Если токену или правилу не назначен приоритет, пользователю сообщается о конфликте.

%left / %right / %nonassoc появляется на рисунке, когда токен и правило имеют ОДНО ЖЕстаршинство.В этом случае %left означает уменьшение, %right означает сдвиг, а %nonassoc означает ни то, ни другое, что вызывает синтаксическую ошибку во время выполнения, если синтаксический анализатор сталкивается с этим случаем.

Уровни приоритетасами назначаются токенам с %left / %right / %nonassoc и правилам с %prec.Единственная странность состоит в том, что правила без %prec и, по крайней мере, один терминал в RHS получают приоритет последнего терминала в RHS.Иногда это может привести к назначению приоритетов правилам, которые вы на самом деле не хотите иметь, что может привести к сокрытию конфликтов из-за их неправильного разрешения.Вы можете избежать этих проблем, добавив дополнительный уровень косвенности в рассматриваемом правиле - замените проблемный терминал на RHS на новый нетерминал, который расширяется только до этого терминала.

3 голосов
/ 16 марта 2012

Это потому, что второе на самом деле неоднозначно. Это первая грамматика, но вы решили эту двусмысленность, добавив %left.

Это %left не работает во второй грамматике, потому что ассоциативность и приоритет не наследуются от правила к правилу. То есть нетерминал binaryop не наследует ничего подобного, даже если он производит PLUS и MINUS. Ассоциативность и предопределенность ограничены правилом и вращаются вокруг терминальных символов.

Мы не можем сделать %left binaryop, но мы можем немного изменить грамматику:

exp : exp binaryop term
exp : term;
term : INT;
binaryop: PLUS | MINUS ;

У этого сейчас нет конфликтов, потому что оно неявно левоассоциативно. То есть создание более длинного и длинного выражения может происходить только в левой части binaryop, потому что правая часть - это term, которая производит только INT.

0 голосов
/ 16 марта 2012

Выходной файл, описывающий противоречивую грамматику, созданную Bison (версия 2.3) для Linux, выглядит следующим образом. Ключевая информация вверху: «У государства 7 есть конфликты».

State 7 conflicts: 2 shift/reduce

Grammar

    0 $accept: exp $end

    1 exp: exp binaryop exp
    2    | INT

    3 binaryop: PLUS
    4         | MINUS

Terminals, with rules where they appear

$end (0) 0
error (256)
PLUS (258) 3
MINUS (259) 4
INT (260) 2

Nonterminals, with rules where they appear

$accept (6)
    on left: 0
exp (7)
    on left: 1 2, on right: 0 1
binaryop (8)
    on left: 3 4, on right: 1

state 0

    0 $accept: . exp $end

    INT  shift, and go to state 1

    exp  go to state 2

state 1

    2 exp: INT .

    $default  reduce using rule 2 (exp)

state 2

    0 $accept: exp . $end
    1 exp: exp . binaryop exp

    $end   shift, and go to state 3
    PLUS   shift, and go to state 4
    MINUS  shift, and go to state 5

    binaryop  go to state 6

state 3

    0 $accept: exp $end .

    $default  accept

state 4

    3 binaryop: PLUS .

    $default  reduce using rule 3 (binaryop)

state 5

    4 binaryop: MINUS .

    $default  reduce using rule 4 (binaryop)

state 6

    1 exp: exp binaryop . exp

    INT  shift, and go to state 1

    exp  go to state 7  

А вот информация о «Государстве 7»:

state 7

    1 exp: exp . binaryop exp
    1    | exp binaryop exp .

    PLUS   shift, and go to state 4
    MINUS  shift, and go to state 5

    PLUS      [reduce using rule 1 (exp)]
    MINUS     [reduce using rule 1 (exp)]
    $default  reduce using rule 1 (exp)

    binaryop  go to state 6

Проблема описана маркерами . в строках, помеченных 1. По какой-то причине %left не «вступает в силу», как вы ожидаете, поэтому Бизон обнаруживает конфликт, когда прочитал exp PLUS exp и находит PLUS или MINUS после него. В таких случаях Bison (и Yacc) делают сдвиг, а не уменьшают. В этом контексте мне кажется, что это равносильно тому, чтобы правила имели приоритет.

Изменение %left на %right и его пропуск не изменяют результат (с точки зрения предупреждений о конфликте). Я также попробовал Yacc на Solaris, и он вызвал по существу тот же конфликт.

Итак, почему первая грамматика работает? Вот вывод:

Grammar

    0 $accept: exp $end

    1 exp: exp PLUS exp
    2    | exp MINUS exp
    3    | INT

Terminals, with rules where they appear

$end (0) 0
error (256)
PLUS (258) 1
MINUS (259) 2
INT (260) 3

Nonterminals, with rules where they appear

$accept (6)
    on left: 0
exp (7)
    on left: 1 2 3, on right: 0 1 2

state 0

    0 $accept: . exp $end

    INT  shift, and go to state 1

    exp  go to state 2

state 1

    3 exp: INT .

    $default  reduce using rule 3 (exp)

state 2

    0 $accept: exp . $end
    1 exp: exp . PLUS exp
    2    | exp . MINUS exp

    $end   shift, and go to state 3
    PLUS   shift, and go to state 4
    MINUS  shift, and go to state 5

state 3

    0 $accept: exp $end .

    $default  accept

state 4

    1 exp: exp PLUS . exp

    INT  shift, and go to state 1

    exp  go to state 6

state 5

    2 exp: exp MINUS . exp

    INT  shift, and go to state 1

    exp  go to state 7

state 6

    1 exp: exp . PLUS exp
    1    | exp PLUS exp .
    2    | exp . MINUS exp

    $default  reduce using rule 1 (exp)

state 7

    1 exp: exp . PLUS exp
    2    | exp . MINUS exp
    2    | exp MINUS exp .

    $default  reduce using rule 2 (exp)

Разница, по-видимому, в том, что в состояниях 6 и 7 она может различать, что делать, исходя из того, что будет дальше.

Один из способов решения проблемы:

%token <token> PLUS MINUS INT
%left PLUS MINUS

%%

exp  : exp binaryop term;
exp  : term;
term : INT;
binaryop: PLUS | MINUS;
0 голосов
/ 15 марта 2012

Я предполагаю, что это подпадает под то, что руководство Bison называет " Mysterious Conflicts ". Вы можете повторить это с:

exp:   exp plus exp;
exp:   exp minus exp;
exp:   INT;
plus:  PLUS;
minus: MINUS;

, что дает мне четыре конфликта S / R.

...