В чем разница между парсерами LR, SLR и LALR?

Какова фактическая разница между анализаторами LR, SLR и LALR? Я знаю, что SLR и LALR являются типами парсеров LR, но какова фактическая разница в их таблицах синтаксического анализа?

И как показать, является ли грамматика LR, SLR или LALR? Для грамматики LL нам просто нужно показать, что любая ячейка таблицы синтаксического анализа не должна содержать нескольких правил производства. Любые подобные правила для LALR, SLR и LR?

Например, как мы можем показать, что грамматика

S --> Aa | bAc | dc | bda
A --> d

- LALR (1), но не SLR (1)?


EDIT (ybungalobill). Я не получил удовлетворительного ответа о том, какая разница между LALR и LR. Таким образом, таблицы LALR имеют меньший размер, но могут распознавать только подмножество LR-грамматик. Может ли кто-нибудь более подробно рассказать о различии между LALR и LR, пожалуйста? LALR (1) и LR (1) будут достаточными для ответа. Оба из них используют 1 токен в обратном направлении, и оба они управляются таблицей! Как они отличаются?

Ответ 1

Синтаксические анализаторы SLR, LALR и LR могут быть реализованы с использованием одной и той же машины, управляемой таблицами.

По сути, алгоритм синтаксического анализа собирает следующий входной токен T и обращается к текущему состоянию S (и соответствующим таблицам предварительного просмотра, GOTO и сокращения), чтобы решить, что делать:

  • SHIFT: если текущая таблица говорит SHIFT на токене T, пара (S, T) помещается в стек анализа, состояние изменяется в соответствии с тем, что говорит таблица GOTO для текущего токена (например, GOTO (T)), выбирается другой входной токен T ', и процесс повторяется
  • СОКРАЩЕНИЕ: В каждом состоянии есть 0, 1 или много возможных сокращений, которые могут произойти в этом состоянии. Если синтаксическим анализатором является LR или LALR, токен проверяется на основе наборов предварительного просмотра для всех допустимых сокращений для состояния. Если токен совпадает с набором преднамеренного сокращения для правила грамматики G = R1 R2.. Rn, происходит уменьшение стека и сдвиг: вызывается семантическое действие для G, стек выталкивается n (из Rn) раз, пара ( S, G) помещается в стек, новое состояние S 'устанавливается в GOTO (G), и цикл повторяется с тем же маркером T. Если синтаксический анализатор является синтаксическим анализатором SLR, существует не более одного правила сокращения для состояние и поэтому действие сокращения может быть выполнено вслепую без поиска, чтобы увидеть, какое сокращение применяется. Для анализатора SLR полезно знать, есть ли уменьшение или нет; Легко сказать, что каждое состояние явно записывает количество сокращений, связанных с ним, и это число необходимо для версий L (AL) R на практике в любом случае.
  • ОШИБКА: если ни SHIFT, ни REDUCE невозможны, объявляется синтаксическая ошибка.

Итак, если они все используют один и тот же механизм, какой смысл?

Предполагаемая ценность в SLR - это простота реализации; вам не нужно сканировать возможные сокращения, проверяя наборы предварительного просмотра, потому что их самое большее, и это единственное жизнеспособное действие, если нет состояний SHIFT, выходящих из состояния. То, какое сокращение применяется, может быть привязано конкретно к государству, так что механизм синтаксического анализа SLR не должен охотиться за ним. На практике парсеры L (AL) R обрабатывают полезно больший набор языков, и для их реализации так мало дополнительной работы, что никто не реализует SLR, кроме как в качестве академического упражнения.

Разница между LALR и LR связана с генератором таблиц. Генераторы синтаксического анализатора LR отслеживают все возможные сокращения от определенных состояний и их точный предварительный набор; в итоге вы получите состояния, в которых каждое сокращение связано с его точным прогнозным набором из левого контекста. Это имеет тенденцию создавать довольно большие наборы состояний. Генераторы синтаксических анализаторов LALR готовы объединять состояния, если таблицы GOTO и наборы элементов поиска для сокращений совместимы и не конфликтуют; это приводит к значительно меньшему количеству состояний за счет невозможности различить определенные последовательности символов, которые может различать LR. Таким образом, парсеры LR могут анализировать больший набор языков, чем парсеры LALR, но имеют намного большие таблицы парсеров. На практике можно найти грамматики LALR, которые достаточно близки к целевым языкам, так что размер конечного автомата стоит оптимизировать; места, где парсер LR был бы лучше, обрабатываются специальной проверкой вне парсера.

Итак: все три используют один и тот же механизм. SLR "легок" в том смысле, что вы можете проигнорировать чуть-чуть машины, но это не стоит хлопот. LR анализирует более широкий набор языков, но таблицы состояний имеют тенденцию быть довольно большими. Это оставляет LALR в качестве практического выбора.

Сказав все это, стоит знать, что парсеры GLR могут анализировать любой контекстно-свободный язык, используя более сложные механизмы, но точно такие же таблицы (включая уменьшенную версию, используемую LALR). Это означает, что GLR строго более мощный, чем LR, LALR и SLR; в значительной степени, если вы можете написать стандартную грамматику BNF, GLR будет анализировать в соответствии с ней. Разница в механизме заключается в том, что GLR желает попробовать несколько разборов, когда есть конфликты между таблицей GOTO и/или наборами предвкушения. (То, как GLR делает это эффективно, является чистым гением [не моим], но не вписывается в этот пост SO).

Это для меня чрезвычайно полезный факт. Я строю анализаторы программ, и преобразователи кода и анализаторы необходимы, но "неинтересны"; интересная работа - это то, что вы делаете с проанализированным результатом, поэтому основное внимание уделяется выполнению работы после разбора. Использование GLR означает, что я могу относительно легко создавать рабочие грамматики по сравнению со взломом грамматики, чтобы получить LALR-форму. Это очень важно, когда вы пытаетесь работать с неакадемическими языками, такими как C++ или Fortran, где вам буквально нужны тысячи правил для правильной работы со всем языком, и вы не хотите тратить свою жизнь на попытки взломать грамматику правила для удовлетворения ограничений LALR (или даже LR).

В качестве своего рода известного примера, C++ считается чрезвычайно сложным для анализа... парнями, выполняющими анализ LALR. C++ легко разобрать, используя механизм GLR, используя в значительной степени правила, приведенные в конце справочного руководства C++. (У меня есть именно такой синтаксический анализатор, и он обрабатывает не только ванильный C++, но также и различные вендорные диалекты. Это возможно только на практике, потому что мы используем синтаксический анализатор GLR, IMHO).

[РЕДАКТИРОВАТЬ Ноябрь 2011: Мы расширили наш парсер для обработки всего C++ 11. GLR сделал это намного проще. РЕДАКТИРОВАТЬ Авг 2014: Теперь обрабатывает все C++ 17. Ничего не сломалось и не стало хуже, GLR по-прежнему кошачий мяу.]

Ответ 2

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

Кстати, я упоминаю об этом немного в моем алгоритме парсинга таблицы MLR (k) здесь.

Добавление

Короткий ответ заключается в том, что таблицы разбора LALR меньше, но машины парсеров одинаковы. Данная LALR-грамматика будет генерировать гораздо большие таблицы разбора, если генерируются все состояния LR с большим количеством избыточных (почти идентичных) состояний.

Таблицы LALR меньше, потому что аналогичные (избыточные) состояния объединяются вместе, эффективно отбрасывая информацию о контексте /lookahead, которую кодируют отдельные состояния. Преимущество состоит в том, что вы получаете гораздо меньшие таблицы разбора для одной и той же грамматики.

Недостатком является то, что не все LR-грамматики могут быть закодированы как таблицы LALR, потому что более сложные грамматики имеют более сложные взгляды, приводящие к двум или более состояниям вместо одного объединенного состояния.

Основное отличие состоит в том, что алгоритм для создания таблиц LR содержит больше информации между переходами из состояния в состояние, а алгоритм LALR - нет. Таким образом, алгоритм LALR не может определить, должно ли данное объединенное состояние быть оставлено как два или более отдельных состояний.

Ответ 3

Еще один ответ (ЯА).

Алгоритмы анализа для SLR (1), LALR (1) и LR (1) идентичны, как сказала Ира Бакстер,
однако таблицы синтаксического анализатора могут отличаться из-за алгоритма генерации синтаксического анализатора.

Генератор синтаксического анализатора SLR создает конечный автомат LR (0) и вычисляет прогнозные значения из грамматики (наборы FIRST и FOLLOW). Это упрощенный подход и может сообщать о конфликтах, которые на самом деле не существуют в конечном автомате LR (0).

Генератор синтаксического анализатора LALR создает конечный автомат LR (0) и вычисляет прогнозные значения из конечного автомата LR (0) (через терминальные переходы). Это правильный подход, но иногда он сообщает о конфликтах, которых не было бы в конечном автомате LR (1).

Генератор синтаксического анализатора Canonical LR вычисляет конечный автомат LR (1), и предварительные просмотры уже являются частью конечного автомата LR (1). Эти таблицы парсеров могут быть очень большими.

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

LRSTAR 10.0 может генерировать парсеры LALR (1), LR (1), CLR (1) или LR (*) в C++, что угодно для вашей грамматики. Смотрите эту диаграмму, которая показывает разницу между парсерами LR.

[Полное раскрытие: LRSTAR - мой продукт]

Ответ 4

Предположим, что синтаксический анализатор без lookghead счастливо разбирает строки для вашей грамматики.

Используя данный пример, он встречает строку dc, что она делает? Уменьшает ли это значение до S, потому что dc является допустимой строкой, созданной этой грамматикой? Или, может быть, мы пытались разобрать bdc, потому что даже это приемлемая строка?

Как люди, мы знаем, что ответ прост, нам просто нужно помнить, что мы только что разобрали b или нет. Но компьютеры глупы:)

Так как парсер SLR (1) имел дополнительную мощность над LR (0), чтобы выполнить просмотр, мы знаем, что любое количество просмотров не может сказать нам, что делать в этом случае; вместо этого нам нужно оглянуться назад в наше прошлое. Таким образом, канонический LR-парсер на помощь. Он помнит прошлый контекст.

То, как он помнит этот контекст, состоит в том, что он дисциплинирует себя, что всякий раз, когда он встретится с b, он начнет идти по пути к чтению bdc, как одна из возможностей. Поэтому, когда он видит d, он знает, идет ли он уже по пути. Таким образом, анализатор CLR (1) может делать то, что не может сделать парсер SLR (1)!

Но теперь, поскольку нам приходилось определять так много путей, состояния машины становятся очень большими!

Итак, мы объединяем одинаковые пути, но, как и ожидалось, это может вызвать проблемы с путаницей. Однако мы готовы пойти на риск за счет уменьшения размера.

Это ваш парсер LALR (1).


Теперь, как это сделать алгоритмически.

При рисовании наборов конфигурации для вышеуказанного языка вы увидите конфликт с уменьшением сдвига в двух состояниях. Чтобы удалить их, вы можете рассмотреть SLR (1), который принимает решения, смотрящие на следующее, но вы заметите, что он все равно не сможет. Таким образом, вы снова нарисуете настройки, но на этот раз с ограничением, которое, когда вы вычисляете закрытие, добавляете дополнительные производные, должны иметь строгие указания. Обратитесь к любому учебнику о том, как это должно быть.

Ответ 5

Основное различие между таблицами синтаксического анализатора, сгенерированными с помощью SLR vs LR, заключается в том, что действия по уменьшению основаны на соответствии требованиям, установленным для таблиц SLR. Это может быть чрезмерно ограничительным, что в конечном итоге вызывает конфликт с уменьшением смены.

Парсер LR, с другой стороны, основы уменьшают решения только на множестве терминалов, которые фактически могут быть уменьшены. Этот набор терминалов часто является правильным подмножеством набора Follows такого нетерминального и, следовательно, имеет меньше шансов противоречить действиям сдвига.

Анализаторы LR являются более мощными по этой причине. Однако таблицы разбора LR могут быть чрезвычайно большими.

Парсер LALR начинает с идеи построения таблицы разбора LR, но объединяет сгенерированные состояния таким образом, что приводит к значительно меньшему размеру таблицы. Недостатком является то, что для некоторых грамматик будет введена небольшая вероятность конфликтов, которую в противном случае избегала бы таблица LR.

Партизаны LALR немного менее мощны, чем парсы LR, но все же более мощные, чем парсеры SLR. По этой причине YACC и другие подобные генераторы парсеров используют LALR.

P.S. Для краткости SLR, LALR и LR выше действительно означают SLR (1), LALR (1) и LR (1), поэтому подразумевается один токен с лексем.

Ответ 6

Парсеры SLR распознают правильное подмножество грамматик, распознаваемых анализаторами LALR (1), которые, в свою очередь, распознают правильное подмножество грамматик, распознаваемых анализаторами LR (1).

Каждый из них сконструирован как конечный автомат, причем каждое состояние представляет собой некоторый набор правил генерации грамматики (и позиции в каждом) при анализе ввода.

Dragon Book пример грамматики LALR (1), которая не является SLR, такова:

S → L = R | R
L → * R | id
R → L

Вот одно из состояний для этой грамматики:

S → L•= R
R → L•

указывает позицию анализатора в каждой из возможных производств. Он не знает, какой из производств он фактически до тех пор, пока он не достигнет цели и не попытается уменьшить.

Здесь анализатор может либо сдвинуть =, либо уменьшить R → L.

Парсер SLR (aka LR (0)) определит, может ли он быть уменьшен, проверяя, находится ли следующий входной символ в следующем наборе R (т.е. множество всех терминалов в грамматике, которые могут следовать R). Поскольку = также находится в этом наборе, парсер SLR сталкивается с конфликтом с уменьшением сдвига.

Однако анализатор LALR (1) будет использовать набор всех терминалов, которые могут следовать за этой конкретной продукцией R, которая только $ (т.е. конец ввода). Таким образом, конфликта нет.

Как отмечали предыдущие комментаторы, партизаны LALR (1) имеют такое же количество состояний, что и парсер SLR. Алгоритм распространения прогноза используется для определения взглядов на государственные производства SLR из соответствующих состояний LR (1). Получающийся парсер LALR (1) может вводить конфликты уменьшения уменьшения, отсутствующие в парсере LR (1), но он не может создавать конфликты с уменьшением сдвига.

В вашем примере следующее состояние LALR (1) вызывает конфликт с уменьшением сдвига в реализации SLR:

S → b d•a / $
A → d• / c

Символ после / - это следующий набор для каждой постановки в парсере LALR (1). В SLR следовать (A) включает в себя A, который также можно сдвинуть.

Ответ 7

Один простой ответ заключается в том, что все грамматики LR (1) являются грамматиками LALR (1). По сравнению с LALR (1), LR (1) имеет больше состояний в связанном автомате конечных состояний (более чем в два раза больше состояний). И это главная причина, по которой грамматикам LALR (1) требуется больше кода для обнаружения синтаксических ошибок, чем грамматикам LR (1). И еще одна важная вещь, которую необходимо знать в отношении этих двух грамматик, заключается в том, что в грамматиках LR (1) у нас может быть меньше конфликтов уменьшения/уменьшения. Но в LALR (1) больше возможностей уменьшить/уменьшить конфликты.

Ответ 8

В дополнение к ответам выше, эта диаграмма демонстрирует, как соотносятся разные парсеры:

enter image description here