Счастливый контекстно-зависимый приоритет оператора

У меня есть два фрагмента кода Happy, один из которых использует обычные правила приоритета, а другой — контекстно-зависимые правила приоритета (оба описаны здесь).

Обычный:

%left '+'
%left '*'
%%

Exp :: { Exp }
    : Exp '+' Exp { Plus $1 $3 }
    | Exp '*' Exp { Times $1 $3 }
    | var         { Var $1 }

Зависит от контекста:

%left PLUS
%left TIMES
%%

Exp :: { Exp }
    : Exp '+' Exp %prec PLUS  { Plus $1 $3 }
    | Exp '*' Exp %prec TIMES { Times $1 $3 }
    | var                     { Var $1 }

Учитывая ввод:

a * b + c * d

Обычная версия дает:

Plus (Times (Var "a") (Var "b")) (Times (Var "c") (Var "d"))

тогда как контекстно-зависимая версия дает:

Times (Var "a") (Plus (Var "b") (Times (Var "c") (Var "c")))

Разве они оба не должны давать одинаковый результат? Что я здесь делаю неправильно, из-за чего они генерируют разные деревья синтаксического анализа?


person Kyle McCormick    schedule 24.01.2017    source источник


Ответы (1)


«Контекстно-зависимый приоритет» — это очень вводящий в заблуждение способ описания этой функции. Однако описание алгоритма предшествования в предыдущем разделе в значительной степени точное.

Как говорится, сравнение приоритета всегда выполняется между производством (которое можно уменьшить) и терминалом (которое можно сдвинуть). Этот простой факт часто омрачается решением разработать синтаксис объявления приоритета, как если бы приоритет был исключительно атрибутом терминала.

Приоритет производства устанавливается путем копирования приоритета последнего терминала в производстве, если нет явного объявления с %prec. Или, другими словами, приоритет производства устанавливается с помощью предложения %prec, по умолчанию равного приоритету последнего токена. В любом случае, вы можете определить приоритет производства, только сказав, что он такой же, как у некоторого терминала. Поскольку это не всегда удобно, генератор синтаксического анализа дает возможность использовать произвольное имя, которое не является именем грамматического символа. Реализация состоит в том, чтобы рассматривать имя как терминал и игнорировать тот факт, что оно фактически никогда не используется ни в одном правиле грамматики, но логически это имя уровня приоритета, который должен быть назначен этому конкретному произведению.

В вашем первом примере вы позволяете продуктам по умолчанию устанавливать приоритет последнего (фактически единственного) терминала в каждом продукте. Но во втором примере вы определили два именованных уровня приоритета, ПЛЮС и ВРЕМЯ, и вы используете их для установки приоритета двух производств. Но вы не объявляете приоритет какого-либо терминала. Поэтому, когда генератор синтаксического анализатора пытается проверить относительный приоритет производства, которое может быть уменьшено, и терминала, который может быть сдвинут, он обнаруживает, что только одна из этих вещей имеет объявленный приоритет. И в этом случае он всегда смещается.

person rici    schedule 24.01.2017
comment
Простите, если это глупый вопрос, так как я никогда раньше не использовал happy. Означает ли это, что %left '+'; %left '*' объявляет приоритеты для '+' и '*', но %left PLUS; %left TIMES не объявляет приоритеты для PLUS и TIMES? - person Daniel Wagner; 25.01.2017
comment
@DanielWagner: Что было непонятно в том, что я написал? %left '+'; %left '*' объявляет приоритеты для '+' и '*', а %left PLUS; %left TIMES объявляет приоритеты для PLUS и TIMES. Но сравнение приоритетов всегда происходит между производством и терминалом, и %left PLUS; %left TIMES не объявляет приоритеты для '+' и '*'. С чего бы это? И если '+' и '*' не имеют объявленных приоритетов, то не с чем сравнивать приоритеты PLUS и TIMES. - person rici; 25.01.2017
comment
Спасибо, это проясняет! Я не понял, что он будет пытаться сравнить приоритет '+' с PLUS во втором примере и не преуспеет (потому что только PLUS имеет там объявленный приоритет). - person Daniel Wagner; 25.01.2017
comment
@DanielWagner: сравнение приоритета всегда проводится между производством (которое можно уменьшить) и терминалом (которое можно сместить). Я не знаю, сколько раз я писал это предложение в ответе SO о том или ином генераторе парсеров; это не конкретное счастье. Если есть лучшая формулировка, пожалуйста, предложите ее. - person rici; 25.01.2017
comment
Кроме того, это не сравнение PLUS с +. Это сравнение TIMES (старшинство производства, которое можно уменьшить) с + (терминал, который можно сдвинуть). Позже он сравнивает PLUS (производство, которое можно сократить) с * (терминал, который можно переместить). В первый раз побеждает сокращение; во второй раз сдвиг делает. - person rici; 25.01.2017