Синтаксические анализаторы 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, которые достаточно близки к целевым языкам, поэтому размер конечного автомата стоит оптимизировать;
Итак: все трое используют одно и то же оборудование. 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 все еще кошачье мяуканье.]