Парсер LR

редактировать
тип парсера в информатике

В информатике, парсеры LR - это тип восходящего синтаксического анализатора, который анализирует детерминированные контекстно-свободные языки за линейное время. Есть несколько вариантов парсеров LR: SLR парсеры, LALR парсеры, Канонические LR (1) парсеры, Минимальные LR (1) парсеры, GLR парсеры. Синтаксические анализаторы LR могут быть сгенерированы генератором синтаксического анализатора из формальной грамматики, определяющей синтаксис анализируемого языка. Они широко используются для обработки компьютерных языков.

синтаксического анализатора LR (L слева направо, R самое прямое происхождение в обратном порядке) считывает входной текст слева направо без резервного копирования (это верно для большинства парсеров) и производит крайнее правое производное в обратном порядке: он выполняет восходящий синтаксический анализ, а не нисходящий синтаксический анализ LL или специальный синтаксический анализ. За именем LR часто следует числовой квалификатор, например, LR (1) или иногда LR (k) . Чтобы избежать обратного отслеживания или угадывания, синтаксическому анализатору LR разрешено заглядывать вперед на k lookahead input символов, прежде чем решать, как анализировать более ранние символы. Обычно k равно 1 и не упоминается. Имя LR часто предшествует другим квалификаторам, например, SLR и LALR . Условие LR (k) для грамматики было предложено Кнутом как «переводимый слева направо с ограничением k».

Парсеры LR детерминированы; они производят единственный правильный синтаксический анализ без догадок или отката в линейное время. Это идеально подходит для компьютерных языков, но парсеры LR не подходят для человеческих языков, которым нужны более гибкие, но неизбежно более медленные методы. Некоторые методы, которые могут анализировать произвольные контекстно-свободные языки (например, Кок – Янгер – Касами, Эрли, GLR ), имеют производительность O (n) раз. Другие методы, которые возвращают или дают несколько синтаксических анализов, могут даже занять экспоненциальное время, если они плохо угадывают.

Вышеупомянутые свойства L, Rи k фактически используются всеми shift- уменьшить парсеры, включая парсеры приоритета . Но по соглашению имя LR обозначает форму синтаксического анализа, изобретенную Дональдом Кнутом, и исключает более ранние, менее мощные методы приоритета (например, синтаксический анализатор приоритета оператора ). Парсеры LR могут обрабатывать больший диапазон языков и грамматик, чем парсеры приоритета или нисходящий анализ LL. Это связано с тем, что синтаксический анализатор LR ожидает, пока он не увидит полный экземпляр некоторого грамматического шаблона, прежде чем совершить то, что он нашел. Анализатор LL должен решить или угадать, что он видит, гораздо раньше, когда он видел только крайний левый входной символ этого шаблона.

Содержание
  • 1 Обзор
    • 1.1 Дерево синтаксического анализа снизу вверх, например A * 2 + 1
    • 1.2 Действия сдвига и уменьшения
    • 1.3 Стек синтаксического анализа снизу вверх
    • 1.4 Анализ снизу вверх шаги, например, A * 2 + 1
    • 1.5 LR, шаги синтаксического анализа, например, A * 2 + 1
    • 1.6 Грамматика для примера A * 2 + 1
    • 1.7 Таблица синтаксического анализа для примера грамматики
    • 1.8 LR цикл синтаксического анализатора
  • 2 Анализ генератора LR
    • 2.1 Состояния LR
    • 2.2 Конечный автомат
    • 2.3 Наборы упреждающего просмотра
    • 2.4 Восстановление синтаксической ошибки
    • 2.5 Варианты синтаксических анализаторов LR
    • 2.6 Теория
  • 3 Дополнительный пример 1 + 1
    • 3.1 Таблицы действий и перехода
    • 3.2 Шаги анализа
    • 3.3 Пошаговое руководство
  • 4 Создание таблиц анализа LR (0)
    • 4.1 Элементы
    • 4.2 Наборы элементов
    • 4.3 Расширение набора элементов путем расширения нетерминальных элементов
    • 4.4 Закрытие наборов элементов
    • 4.5 Расширенная грамматика
  • 5 Создание таблиц
    • 5.1 Поиск доступных наборов элементов и переходов между ними
    • 5.2 Создание таблиц действий и перехода
      • 5.2.1 Замечание о LR (0) по сравнению с SLR и LALR парсинг
    • 5.3 Конфликты в построенных таблицах
  • 6 См. также
  • 7 Ссылки
  • 8 Дополнительная литература
  • 9 Внешние ссылки
Обзор

Например, дерево синтаксического анализа снизу вверх A * 2 + 1

Снизу вверх дерево синтаксического анализа, построенное с пронумерованными шагами

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

На шаге 6 в примере синтаксического анализа только "A * 2" был проанализирован не полностью. Существует только затененный нижний левый угол дерева синтаксического анализа. Ни один из узлов дерева синтаксического анализа с номерами 7 и выше еще не существует. Узлы 3, 4 и 6 являются корнями изолированных поддеревьев для переменной A, оператора * и числа 2 соответственно. Эти три корневых узла временно хранятся в стеке синтаксического анализа. Оставшаяся неанализируемая часть входного потока равна «+ 1».

Действия сдвига и уменьшения

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

  • A Shift шаг продвигается во входном потоке на один символ. Этот смещенный символ становится новым деревом синтаксического анализа с одним узлом.
  • A Шаг уменьшения применяет законченное правило грамматики к некоторым из недавних деревьев синтаксического анализа, объединяя их вместе в одно дерево с новым корневым символом.

Если input не имеет синтаксических ошибок, синтаксический анализатор продолжает эти шаги до тех пор, пока весь ввод не будет использован, и все деревья синтаксического анализа не будут сведены к одному дереву, представляющему весь допустимый ввод.

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

Эффективность синтаксического анализатора LR во многом обусловлена ​​детерминированностью. Чтобы избежать догадок, анализатор LR часто смотрит вперед (вправо) на следующий отсканированный символ, прежде чем решить, что делать с ранее отсканированными символами. Лексический сканер опережает синтаксический анализатор на один или несколько символов. Символы опережающего просмотра являются «правым контекстом» для решения о синтаксическом анализе.

Стек синтаксического анализа снизу вверх

Анализатор снизу вверх на шаге 6

Как и другие сдвиги- reduce parsers, парсер LR лениво ждет, пока он не просканирует и не проанализирует все части некоторой конструкции, прежде чем зафиксировать, что представляет собой комбинированная конструкция. Затем синтаксический анализатор немедленно воздействует на комбинацию, а не ждет дальнейшего. В примере с деревом синтаксического анализа фраза A сокращается до Value, а затем до Products на шагах 1-3, как только просматривается опережающий *, вместо того, чтобы ждать позже, чтобы организовать эти части дерева синтаксического анализа. Решения о том, как обрабатывать A, основаны только на том, что синтаксический анализатор и сканер уже видели, без учета вещей, которые появляются намного позже справа.

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

Шаги анализа снизу вверх, например A * 2 + 1

ШагРазбор стекаБез анализаСдвиг / Уменьшение
0пустойA * 2 + 1shift
1id* 2 + 1Значение → id
2Значение* 2 + 1Продукты → Значение
3Продукты* 2 + 1сдвиг
4Продукты *2 + 1shift
5Продукты * int+ 1Значение → int
6Продукты * Значение+ 1Продукты → Продукты * Стоимость
7Продукты+ 1Суммы → Продукты
8Суммы+ 1shift
9Sums +1shift
10Sums + inteofValue → int
11Sums + ValueeofProducts → Value
12Sums + ProductseofSums → Sums + Products
13Sumseofdone

На шаге 6 применяется правило грамматики, состоящее из нескольких частей:

Products → Products * Value

Это соответствует верхнему удержанию стека. ng проанализированных фраз "... Товары * Стоимость". Шаг уменьшения заменяет этот экземпляр правой части правила, «Products * Value», на левый символ правила, в данном случае на более крупный Products. Если синтаксический анализатор строит полные деревья синтаксического анализа, три дерева для внутренних продуктов, * и Value объединяются новым корнем дерева для продуктов. В противном случае семантические детали из внутренних продуктов и значения выводятся на более поздний этап компилятора или объединяются и сохраняются в новом символе продуктов.

Шаги анализа LR, например A * 2 + 1

В синтаксических анализаторах LR решения о сдвиге и сокращении потенциально основаны на всем стеке всего, что было предварительно проанализировано, а не только на одном, самом верхнем символе стека. Если сделать это неумелым образом, это может привести к очень медленным анализаторам, которые становятся все медленнее и медленнее для более длинных входных данных. Парсеры LR делают это с постоянной скоростью, суммируя всю релевантную информацию левого контекста в одно число, называемое состоянием парсера LR (0) . Для каждой грамматики и метода анализа LR существует фиксированное (конечное) количество таких состояний. Помимо хранения уже проанализированных символов, стек синтаксического анализа также запоминает номера состояний, достигнутые всем до этих точек.

На каждом шаге синтаксического анализа весь входной текст делится на стопку ранее проанализированных фраз, текущий предварительный символ и оставшийся неотсканированный текст. Следующее действие парсера определяется его текущим номером состояния LR (0) (крайний правый в стеке) и опережающим символом. В приведенных ниже шагах все черные детали точно такие же, как и в других синтаксических анализаторах, не относящихся к LR. Стек синтаксического анализатора LR добавляет информацию о состоянии фиолетовым цветом, суммируя черные фразы слева от них в стеке и то, какие возможности синтаксиса следует ожидать дальше. Пользователи парсера LR обычно могут игнорировать информацию о состоянии. Эти состояния объясняются в следующем разделе.

. ШагАнализ стека., состояние [Символ состояние ] *Смотреть. Впереди. Не просканированоАнализатор. Действие. Правило грамматикиСледующее. Состояние
0

0

id* 2 + 1shift9
1

0id9

*2 + 1reduceValue → id7
2

0Значение 7

*2 + 1уменьшитьПродукты → Значение4
3

0Продукты 4

*2 + 1shift5
4

0Продукты 4*5

int+ 1shift8
5

0Продукты 4*5int 8

+1reduceValue → int6
6

0Products 4*5Value 6

+1reduceПродукты → Продукты * Стоимость4
7

0Продукты 4

+1уменьшитьСуммы → Продукты1
8

0Суммы 1

+1сдвиг2
9

0Суммы 1+2

inteofshift8
10

0Суммирует 1+2int 8

eofreduceValue → int7
11

0Суммирует 1+2Value 7

eofreduceПродукты → Значение3
12

0Суммы 1+2Продукты 3

eofуменьшитьSums → Sums + Products1
13

0Sums 1

eofdone

Первоначально на шаге 0 входной поток «A * 2 + 1» делится на

  • пустой раздел в стеке синтаксического анализа,
  • предварительный текст «A», сканируемый как символ идентификатора, и
  • в оставшийся не просканированный текст «* 2 + 1».

Стек синтаксического анализа начинается с сохранения только начального состояния 0. Когда состояние 0 видит идентификатор опережающего просмотра, оно знает, что нужно сдвинуть этот идентификатор в стек и сканировать следующий входной символ * и переходите к состоянию 9.


На шаге 4 общий входной поток «A * 2 + 1» в настоящее время делится на

  • проанализированный раздел «A *» с 2 составными фразами «Продукты» и *,
  • прогнозируемый текст "2" отсканирован как символ int, а
  • оставшийся неотсканированный текст "+ 1".

Состояния, соответствующие сложенным фразам: 0, 4 и 5. Текущие, крайнее правое состояние в стеке - это состояние 5. Когда состояние 5 видит опережающий int, оно знает, что нужно переместить это int в стек как свою собственную фразу, сканировать следующий входной символ + и перейти к состоянию 8.


На шаге 12 весь входной поток израсходован, но организован лишь частично. Текущее состояние - 3. Когда состояние 3 видит опережающий eof, оно знает, что нужно применить завершенное правило грамматики

Sums → Sums + Products

, объединив три крайние правые фразы стека для Sums, +, и продукты в одно целое. Само состояние 3 не знает, каким должно быть следующее состояние. Это можно найти, вернувшись в состояние 0, слева от сокращаемой фразы. Когда состояние 0 видит этот новый завершенный экземпляр Sums, он переходит в состояние 1 (снова). Консультации со старыми состояниями - вот почему они хранятся в стеке, а не только в текущем состоянии.

Грамматика для примера A * 2 + 1

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

Используемый здесь пример грамматики представляет собой крошечное подмножество языка Java или C:

r0: Goal → Sums eof
r1: Sums → Sums + Products
r2: Суммы → Продукты
r3: Продукты → Продукты * Значение
r4: Продукты → Значение
r5: Значение → int
r6: Значение → id

терминальные символы грамматики - это многосимвольные символы или «токены», обнаруженные во входном потоке с помощью лексического сканера. Здесь они включают +*и int для любой целочисленной константы, id для любого имени идентификатора и eof для конца входного файла. Грамматика не заботится о значениях int или написании id, а также о пробелах или переносах строк. Грамматика использует эти терминальные символы, но не определяет их. Они всегда являются листовыми узлами (в нижнем густом конце) дерева синтаксического анализа.

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

Любой компьютерный язык может быть описан несколькими разными грамматиками. Парсер LR (1) может обрабатывать многие, но не все общие грамматики. Обычно можно вручную изменить грамматику, чтобы она соответствовала ограничениям синтаксического анализа LR (1) и инструмента генератора.

Грамматика для синтаксического анализатора LR должна быть однозначной сама по себе или должна быть дополнена правилами приоритета разрешения конфликтов. Это означает, что существует только один правильный способ применения грамматики к данному легальному примеру языка, в результате чего получается уникальное дерево синтаксического анализа с одним значением и уникальная последовательность действий сдвига / уменьшения для этого примера. Анализ LR не является полезным методом для человеческих языков с неоднозначной грамматикой, зависящей от взаимодействия слов. Человеческие языки лучше обрабатываются синтаксическими анализаторами, такими как обобщенный синтаксический анализатор LR, синтаксический анализатор Эрли или алгоритм CYK, который может одновременно вычислять все возможные деревья синтаксического анализа за один проход.

Таблица синтаксического анализа для примера грамматики

Большинство синтаксических анализаторов LR управляются таблицами. Программный код парсера представляет собой простой универсальный цикл, одинаковый для всех грамматик и языков. Знание грамматики и ее синтаксические последствия кодируются в неизменяемые таблицы данных, называемые таблицы синтаксического анализа (или таблицы синтаксического анализа ). Записи в таблице показывают, следует ли сдвигать или уменьшать (и по какому правилу грамматики) для каждой допустимой комбинации состояния синтаксического анализатора и символа опережающего просмотра. Таблицы синтаксического анализа также сообщают, как вычислить следующее состояние, учитывая только текущее состояние и следующий символ.

Таблицы синтаксического анализа намного больше грамматики. Таблицы LR трудно точно вычислить вручную для больших грамматик. Таким образом, они механически выводятся из грамматики с помощью некоторого инструмента генератора синтаксического анализатора, такого как Bison.

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

Таблицы синтаксического анализа LR двумерны. Каждое текущее состояние парсера LR (0) имеет свою собственную строку. Каждый возможный следующий символ имеет свой столбец. Некоторые комбинации состояния и следующего символа невозможны для допустимых входных потоков. Эти пустые ячейки вызывают сообщения об ошибках синтаксиса.

В левой половине таблицы Action есть столбцы для упреждающих оконечных символов. Эти ячейки определяют, будет ли следующим действием синтаксического анализатора сдвиг (в состояние n) или уменьшение (по правилу грамматики rn).

В правой половине таблицы Перейти к есть столбцы для нетерминальных символов. Эти ячейки показывают, к какому состоянию перейти после того, как левая сторона некоторого сокращения создала ожидаемый новый экземпляр этого символа. Это похоже на действие сдвига, но для нетерминалов; символ упреждающего терминала не изменился.

Столбец таблицы «Текущие правила» документирует значение и возможности синтаксиса для каждого состояния, разработанные генератором синтаксического анализатора. Он не входит в фактические таблицы, используемые во время анализа. Маркер • (розовая точка) показывает, где сейчас находится синтаксический анализатор в пределах некоторых частично распознанных грамматических правил. Объекты слева от • были проанализированы, и объекты справа ожидаются в ближайшее время. Состояние имеет несколько таких текущих правил, если синтаксический анализатор еще не сузил возможности до одного правила.

CurrLookaheadLHS Goto
StateCurrent Rulesintid*+eofSumsProductsЗначение
0Цель → • Сумма89147
1Цель → Суммы • eof. Суммы → Суммы • + Продукты. 2выполнено.
2Суммы → Суммы + • Продукты8937
3Суммы → Суммы + Продукты •. Продукты → Продукты • * Значение. 5r1.r1.
4Суммы → Продукты •. Продукты → Продукты • * Значение. 5r2.r2.
5Продукты → Продукты * • Значение896
6Продукты → Продукты * Значение •r3r3r3
7Продукты → Значение •r4r4r4
8Значение → int •r5r5r5
9Значение → id •r6r6r6

В состоянии 2 выше синтаксический анализатор только что нашел и сдвинул + правила грамматики

r1: Sums → Sums + • Products

Следующая ожидаемая фраза - Products. Товары начинаются с терминальных символов int или id. Если предварительный просмотр является одним из тех, синтаксический анализатор сдвигает их и переходит в состояние 8 или 9 соответственно. Когда продукт найден, анализатор переходит в состояние 3, чтобы собрать полный список слагаемых и найти конец правила r0. Товары также могут начинаться с нетерминального значения. Для любого другого опережающего или нетерминального сообщения синтаксический анализатор сообщает о синтаксической ошибке.


В состоянии 3 синтаксический анализатор только что нашел фразу Products, которая могла быть основана на двух возможных грамматических правилах:

r1: Sums → Sums + Products •
r3: Products → Products • * Значение

Выбор между r1 и r3 не может быть решен, просто взглянув назад на предыдущие фразы. Синтаксический анализатор должен проверить символ опережающего просмотра, чтобы сказать, что делать. Если опережающий просмотр *, он находится в правиле 3, поэтому синтаксический анализатор переходит в * и переходит к состоянию 5. Если опережающий просмотр - eof, он находится в конце правила. 1 и правило 0, так что синтаксический анализатор готов.


В состоянии 9, приведенном выше, все непустые, не содержащие ошибок ячейки относятся к одному и тому же сокращению r6. Некоторые парсеры экономят время и табличное пространство, не проверяя в этих простых случаях опережающий символ. Затем синтаксические ошибки обнаруживаются несколько позже, после некоторых безвредных сокращений, но еще до следующего действия сдвига или решения синтаксического анализатора.

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

Цикл синтаксического анализатора LR

Анализатор LR начинается с почти пустого стека синтаксического анализа, содержащего только начальное состояние 0, и с опережающего просмотра, содержащего первый отсканированный символ входного потока. Затем синтаксический анализатор повторяет следующий шаг цикла до тех пор, пока не будет выполнен или не остановится на синтаксической ошибке:

Самым верхним состоянием в стеке синтаксического анализа является некоторое состояние s, а текущим опережающим просмотром является некоторый конечный символ t. Найдите следующее действие парсера в строке s и столбце t таблицы Lookahead Action. Это действие - Shift, Уменьшение, Готово или Ошибка:

  • Shift n:
Переместить согласованный терминал t в стек синтаксического анализа и сканировать следующий входной символ в предварительный буфер.
Нажать следующий состояние n в стек синтаксического анализа как новое текущее состояние.
  • Уменьшить r m : применить правило грамматики r m : Lhs → S 1S2... S L
Удалите совпавшие самые верхние символы L (и деревья синтаксического анализа и связанные номера состояний) из стека синтаксического анализа.
Это показывает предыдущее состояние p, которое ожидало экземпляр символа Lhs.
Присоединитесь к L анализирует деревья вместе как одно дерево синтаксического анализа с новым корневым символом Lhs.
Ищет следующее состояние n из строки p и столбца Lhs таблицы LHS Goto.
Нажимает символ и дерево для Lhs. в стек синтаксического анализа.
Поместить следующее состояние n в стек синтаксического анализа как новое текущее состояние.
Предварительный просмотр и входной поток остаются неизменными.
  • Готово: Lookahead t - маркер eof. Конец разбора. Если стек состояний содержит только начальное состояние, сообщите об успехе. В противном случае сообщите о синтаксической ошибке.
  • Ошибка: сообщите о синтаксической ошибке. Синтаксический анализатор завершает работу или пытается какое-то восстановление.

Стек парсера LR обычно хранит только состояния LR (0) автомата, поскольку символы грамматики могут быть получены из них (в автомате все входные переходы в какое-либо состояние помечены значком тот же символ, который является символом, связанным с этим состоянием). Более того, эти символы почти никогда не нужны, поскольку состояние - это все, что имеет значение при принятии решения о синтаксическом анализе.

Анализ генератора LR

Этот раздел статьи может быть пропущен большинством пользователей парсера LR генераторы.

Состояния LR

Состояние 2 в примере таблицы синтаксического анализа относится к правилу частичного синтаксического анализа

r1: Sums → Sums + • Products

Это показывает, как синтаксический анализатор попал сюда, увидев Sums, затем + при поиске больших Sums. Маркер • переместился за пределы начала правила. Он также показывает, как синтаксический анализатор ожидает в конечном итоге завершить правило, найдя в следующий раз полные продукты. Но необходимы дополнительные сведения о том, как анализировать все части этих продуктов.

Частично проанализированные правила для состояния называются его "основными элементами LR (0)". Генератор синтаксического анализатора добавляет дополнительные правила или элементы для всех возможных следующих шагов в создании ожидаемых продуктов:

r3: Products → • Products * Value
r4: Products → • Значение
r5: Значение → •int
r6: Значение → •id

Маркер • находится в начале каждого из этих добавленных правил; парсер еще не подтвердил и не проанализировал какую-либо их часть. Эти дополнительные элементы называются «закрытием» основных элементов. Для каждого нетерминального символа, следующего сразу за •, генератор добавляет правила, определяющие этот символ. Это добавляет дополнительные маркеры • и, возможно, другие символы-последователи. Этот процесс закрытия продолжается до тех пор, пока все символы-последователи не будут развернуты. Последовательные нетерминалы для состояния 2 начинаются с Products. Затем стоимость добавляется закрытием. Терминалы последователя - это int и id.

Ядро и элементы закрытия вместе показывают все возможные законные способы перехода от текущего состояния к будущим состояниям и полным фразам. Если символ последователя появляется только в одном элементе, он приводит к следующему состоянию, содержащему только один базовый элемент с выдвинутым маркером •. Таким образом, int приводит к следующему состоянию 8 с ядром

r5: Value → int •

Если один и тот же символ последователя появляется в нескольких элементах, синтаксический анализатор еще не может сказать, какое правило здесь применяется. Таким образом, этот символ ведет к следующему состоянию, в котором показаны все оставшиеся возможности, опять же с продвинутым маркером •. Продукты появляются как в r1, так и в r3. Таким образом, Products приводит к следующему состоянию 3 с ядром

r1: Sums → Sums + Products •
r3: Products → Products • * Value

На словах это означает, что синтаксический анализатор обнаружил единичные продукты, это может быть сделано, или у него может быть еще больше вещей для умножения вместе. Все основные элементы имеют один и тот же символ перед маркером • ; все переходы в это состояние всегда имеют один и тот же символ.

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

Эти состояния называются состояниями «LR (0)», потому что они используют опережающий просмотр k = 0, то есть без опережения. Единственная проверка входных символов происходит, когда символ сдвигается внутрь. Проверка опережающих просмотров для сокращений выполняется отдельно таблицей синтаксического анализа, а не самими перечисленными состояниями.

Конечный автомат

Таблица синтаксического анализа описывает все возможные состояния LR (0) и их переходы. Они образуют конечный автомат (FSM). FSM - это простой механизм для разбора простых невложенных языков без использования стека. В этом приложении LR модифицированный «язык ввода» конечного автомата имеет как терминальные, так и нетерминальные символы и охватывает любой частично проанализированный снимок стека полного анализа LR.

Вызов шага 5 примера шагов синтаксического анализа:

. шагсинтаксический анализ стека. состояние символ состояние...Посмотрите. Впереди. Не просканированные
5

0Продукты 4*5int 8

+1

Стек синтаксического анализа показывает серию переходов состояний от начального состояния 0 до состояния 4, а затем до 5 и текущего состояния 8. Символы на стек синтаксического анализа - это символы сдвига или перехода для этих переходов. Другой способ увидеть это - то, что конечный автомат может сканировать поток «Products * int + 1» (без использования еще одного стека) и находить крайнюю левую полную фразу, которая должна быть сокращена следующей. И это действительно его работа!

Как может простой автомат сделать это, если исходный неанализируемый язык имеет вложенность и рекурсию и определенно требует анализатора со стеком? Хитрость в том, что все, что находится слева от вершины стека, уже полностью уменьшено. Это устраняет все петли и вложенность этих фраз. FSM может игнорировать все старые начала фраз и отслеживать только самые новые фразы, которые могут быть завершены следующей. В теории LR это неясное название - «жизнеспособный префикс».

Наборы опережающего просмотра

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

В синтаксических анализаторах SLR эти опережающие наборы определяются непосредственно из грамматики, без учета отдельных состояний и переходов. Для каждого нетерминального S генератор SLR вырабатывает Follows (S), набор всех терминальных символов, которые могут сразу следовать за некоторым вхождением S. В таблице синтаксического анализа каждое сокращение до S использует Follow (S) в качестве своего LR (1) опережающий набор. Такие последующие наборы также используются генераторами нисходящих синтаксических анализаторов LL. Грамматика, которая не имеет конфликтов сдвига / уменьшения или уменьшения / уменьшения при использовании наборов Follow, называется грамматикой SLR.

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

Анализатор SLR или LALR избегает дублирования состояний. Но в такой минимизации нет необходимости, и иногда она может создавать ненужные конфликты опережающего просмотра. Канонические парсеры LR используют дублированные (или «разделенные») состояния, чтобы лучше запоминать левый и правый контекст использования нетерминала. Каждое вхождение символа S в грамматике можно обрабатывать независимо с его собственным набором опережающего просмотра, чтобы помочь разрешить конфликты сокращения. Это обрабатывает еще несколько грамматик. К сожалению, это значительно увеличивает размер таблиц синтаксического анализа, если выполняется для всех частей грамматики. Это разделение состояний также может быть выполнено вручную и выборочно с помощью любого анализатора SLR или LALR путем создания двух или более именованных копий некоторых нетерминалов. Грамматика, которая является бесконфликтной для канонического генератора LR, но имеет конфликты в генераторе LALR, называется LR (1), но не LALR (1) и не SLR.

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

Восстановление синтаксической

Парсеры LR могут генерировать несколько полезных сообщений об ошибках для первой синтаксической ошибки в программе, просто перечисляя все ошибки терминальные символы, которые могли появиться рядом, вместо неожиданного плохого просмотра вперед символ. Но это не помогает синтаксическому анализатору понять, как анализировать оставшуюся часть входной программы для поиска дальнейших ошибок независимых. Если синтаксический анализатор плохо восстанавливается после первой ошибки, он, скорее всего, неправильно проанализирует все остальное и создаст каскад бесполезных ложных сообщений об ошибках.

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

Многие синтаксические ошибки кодирования - это простые опечатки или пропуски тривиального символа. Некоторые парсеры LR пытаются обнаружить и автоматически исправить эти распространенные случаи. Анализатор перечисляет все возможные вставки, удаления или замены одного символа в точке ошибки. Компилятор выполняет пробный синтаксический анализ каждого изменения, чтобы убедиться, что все работает нормально. (Для этого требуется возврат к снимкам стека синтаксического анализа и входного потока, которые обычно не требуются синтаксическому анализатору.) Выбрано лучшее восстановление. Это дает очень полезное сообщение об ошибке и хорошо синхронизирует синтаксический анализ. Однако исправление недостаточно надежно, чтобы навсегда изменить входной файл. Исправление синтаксических ошибок проще всего выполнять последовательно в парсерах (например, LR), которые имеют таблицы синтаксического анализа и явный стек данных.

Варианты парсеров LR

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

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

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

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

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

LCАнализаторы левого угла используют восходящие методы LR для распознавания левого конца альтернативных правил грамматики. Когда альтернативы были сужены до единственного возможного правила, анализатор затем переключается на нисходящие методы LL (1) для синтаксического анализа остальной части этого правила. Анализаторы LC имеют меньшие таблицы синтаксического анализа, чем анализаторы LALR, и лучшую диагностику ошибок. Широко используемых генераторов для детерминированных LC-анализаторов не существует. Анализаторы LC с множественным синтаксическим анализом полезны для человеческих языков с очень большими грамматиками.

Теория

Парсеры LR были изобретены Дональдом Кнутом в 1965 году как эффективное обобщение парсеров приоритета. Кнут доказал, что синтаксические анализаторы LR были наиболее универсальными из возможных, которые все равно были бы эффективны в худших случаях.

«LR (k) грамматики могут быть эффективно проанализированы со временем выполнения, по существу пропорциональным длине строки».
Для каждого k≥1 «язык может быть сгенерирован LR (k) грамматикой тогда и только тогда, когда он детерминирован [и контекстно-свободный], тогда и только тогда, когда он может быть сгенерирован LR (1) грамматика. "

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

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

Для получения полной информации о теории LR и о том, как парсеры LR выводятся из грамматик, см. Теорию синтаксического анализа, перевода и компиляции, том 1 (Ахо и Уллман).

Парсеры Эрли применять методы и нотацию • LR-анализаторов к задаче генерации всех синтаксических анализов для неоднозначных грамматик, таких как человеческие языки.

В то время как грамматики LR (k) имеют одинаковую порождающую способность для всех k≥1, случай грамматик LR (0) немного отличается. Говорят, что язык L обладает своим префикса, если ни одно в L не является слово собственное префиксом другое слово в L.Язык L имеет LR (0) грамматику тогда и только тогда, когда L детерминированный контекстно -свободный язык со своим префикса. Как следствие, язык L является детерминированным языком контекстно-свободным, когда L $ имеет LR (0) грамматику, где «$» не является символом L в пример алфавита.

Дополнительные 1 + 1
Анализ 1 + 1 снизу вверх

В этом анализе LR используется следующая небольшая грамматика с символом цели E:

(1) E → E * B
(2) E → E + B
(3) E → B
(4) B → 0
(5) B → 1

для анализа следующий ввод:

1 + 1

Таблицы действий и перехода

Две таблицы синтаксического анализа LR (0) для этой грамматики выглядят следующим образом:

состояниедействиеgoto
*+01$EB
0s1s234
1r4r4r4r4r4
2r5r5r5r5r5
3s5s6acc
4r3r3r3r3r3
5s1s27
6s1s28
7r1r1r1r1r1
8r2r2r2r2r2

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

  • shift, который записывается как 'sn' и указывает, что следующее состояние - n
  • reduce, которое записывается как 'rm' и указывает, что сок ращение с правилами грамматики m выполнено
  • accept, который пишется как согласно и указывает, что синтаксический анализатор принимает во входном потоке.

Таблица goto индекс состоянием синтаксического анализатора и нетерминалом и просто указывает, каким будет следующее состояние синтаксического анализатора, если он признал некий нетерминал. Эта таблица важна для определения следующего состояния после сокращения. После следующего сокращения путем поиска записи таблицы перехода для вершины стека (т. Е. Текущего состояния) и LHS сокращенного (т. Е. Нетерминального).

Этапы синтаксического анализа

В таблице ниже показан каждый этап процесса. Здесь состояние относится к элементу наверху стека (самый правый элемент), следующее действие указано на приведенную выше таблицу действий. К входной строке добавляется символ $, обозначающий конец потока.

СостояниеВходной потокВыходной потокСтекСледующее действие
01 + 1 $[0]Shift 2
2+ 1 $[0,2]Уменьшить 5
4+ 1 $5[0,4]Уменьшить 3
3+ 1 $5,3[0,3]Сдвиг 6
61 $5,3[0,3,6]Сдвиг 2
2$5,3[0,3,6,2]Уменьшить 5
8$5,3,5[0,3,6,8]Уменьшить 2
3$5,3,5,2[0,3]Принять

Пошаговое руководство

Анализатор начинает со стека, содержащего только начальное состояние ('0'):

[0]

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

[0'1' 2]

, где верх стека равен 2. Для пояснения показан символ (например, '1', B), который вызвал переход в следующее состояние, хотя строго, это не часть стека.

В состоянии 2 таблицы действий говорит о сокращении с помощью правил грамматики 5 (независимо от того, какой терминал анализатор видит во входном потоке), что означает, что анализатор только что распознал правую часть правил 5 В этом случае синтаксический анализатор записывает 5 в выходной поток, извлекает одно состояние из стека (поскольку часть правил имеет один символ) и помещает в стек состояние из состояния в результате перехода для состояния 0 и B, то есть 4. В результате получается стек:

[0B 4]

в состоянии 4 таблица действий говорит, что синтаксический анализатор должен выполнить сокращение согласно правилу 3. Таким образом, он записывает 3 в выходной поток, выталкивает одно состояние из стек, и находит новое состояние в совместном переходе для состояния 0 и E, которое является состоянием 3. Результирующий стек:

[0E 3]

Следующий терминал, который видит синтаксический анализатор, - это '+', и в соответствии с таблицами действий он должен перейди те в состоянии 6:

[0E 3'+' 6]

Полученный стек можно интерпретировать как историю конечного автомата, который только что прочитал нетерминал E с последующим знаком "+". Таблица переходов этого автомата определяет действия сдвига в таблице действий и действиями перехода в таблицу перехода.

Следующий терминал теперь равенство «1», и это означает, что синтаксический анализатор сдвиг и переходит в состояние 2:

[0E 3'+' 6 '1' 2]

Так же, как предыдущая '1' эта сокращается до B, давая следующий стек:

[0E 3'+' 6B 8]

соответствует списку состояний конечного автомата, который прочитал нетерминал E, за которым следует '+' а нетерминальный B. В состоянии 8 синтаксический анализатор всегда сокращение по правилам 2. Три верхних состояния в стеке соответствуют 3 символам в правой части правила 2. На этот раз мы выталкиваем 3 символа из стек (поскольку правая часть правила имеет 3 символа) и ищет состояние перехода для E и 0, таким образом, помещая состояние 3 обратно в стек

[0E 3]

Наконец, синтаксический анализатор считывает '$' (конец ввода символа) из входного потока, что означает, что в соответствии с таблицами действий (текущее состояние - 3) синтаксический анализатор принимает входную строку. Правила, которые будут записаны в выходной поток, будут [5, 3, 5, 2], что действительно является крайним правым производным «1 + 1» в обратном порядке.

Построение таблиц синтаксического анализа LR (0)

Элементы

Создание этих таблиц анализа основано на понятии элементов LR (0) (здесь просто называемых элементов), которые - это правила грамматики, в где-то справа добавляется специальная точка. Например, правилу E → E + B соответствуют следующие четыре элемента:

E → • E + B
E → E • + B
E → E + •B
E → E + B •

Правила формы A → ε имеют только один элемент A → •. Элемент E → E • + B, например, указывает, что синтаксический анализатор распознал строки, соответствующий E во входном потоке, и теперь ожидает прочитать '+', за соответствующую другую строку, соответствующую B.

Наборы элементов

Обычно невозможно охарактеризовать состояние анализатора с помощью элемента, потому что он может заранее не знать, какое правило будет использовать для сокращения. Например, если существует также правило E → E * B, то элементы E → E • + B и E → E • * B будут работать после строки, формы E прочитано. Поэтому состояние парсера удобно описать набором элементов, в данном случае набором {E → E • + B, E → E • * B}.

Расширение элементов пути расширения нетерлов

Элемент с точкой перед нетерминалом, например E → E + • B, указывает, что синтаксический анализатор ожидает разобрать нетерминал B. Чтобы обеспечить, что набор элементов содержит все возможные правила, которые синтаксический анализатор может выполнять в процессе синтаксического анализа, он должен включать все элементы, описывающие, как будет анализироваться сам B. Это означает, что если существуют такие правила, как B → 1 и B → 0, то набор элементов также должен включенные элементы B → • 1 и B → • 0. В целом это можно сформулировать следующим образом:

Если есть элемент вида A → v • Bw в наборе элементов, а в грамматике есть правило вида B → w 'тогда элемент B → • w 'также должен быть в наборе элементов.

Закрытие наборов элементов

Таким образом, любой набор элементов может быть расширен путем рекурсивного добавления всех соответствующих элементов, пока не будут учтены все нетерминалы, соответствующие предшествующие точки. Минимальное расширение называется закрытием набора и записывается как clos (I), где I - набор элементов. Именно эти закрытые наборы элементов принимаются в качестве состояния анализатора, хотя в таблицах включены только те, которые достижимы из начального состояния.

Расширенная грамматика

Перед определением перехода между различными состояниями грамматика дополнительных дополнительных правилом

(0) S → E eof

, где S - новый символ запуска и E старый символ запуска. Парсер будет использовать это правило для сокращения тогда, когда он принял всю входную инструкцию.

В этом примере та же грамматика, что и выше, дополнена следующим образом:

(0) S → E eof
(1) E → E * B
(2) E → E + B
(3) E → B
(4) B → 0
(5) B → 1

Это для этого будет определена расширенная грамматика, которую задают элементы и переходы между ними.

Построение таблицы

Поиск доступных наборов и переходов между ними

Первый шаг построения таблиц в переходах между закрытыми наборами элементов. Эти переходы будут определяться так, как если бы мы рассматривали конечный автомат, который может читать как терминалы, так и нетерминалы. Начальным состоянием этого автомата всегда является закрытие первого элемента добавленного правила: S → • E:

Набор элементов 0
S → • E eof
+E → • E * B
+E → • E + B
+E → •B
+B → •0
+B → •1

Значок жирный шрифт "+"Перед элементами указывают элементы, которые были добавлены для закрытия (не путать с математическим оператором '+', который является терминалом). Исходные элементы" + "называются ядром набора элементов.

начало с начального состояния (S0), теперь все состояния, которые могут быть достигнуты из этого состояния. Возможные переходы для набора могут найти следующие элементы за точками; в случае набора элементов 0 этими символами являются терминалы '0' и '1' и нетерминалы E и B. символ x ∈ {0, 1, E, B} {\ textstyle x \ in \ {0,1, E, B \}}{\ textstyle x \ in \ {0,1, E, B \}} приводит к, следующая процедура выполняется для каждого из символов:

  1. Возьмите п. одмножество S всех элементов в текущем наборе элементов, где есть точка перед интересующим символом, x.
  2. Для каждого элемента в S переместите точку вправо x.
  3. Закройте получившийся набор элементов.

Для терминала '0' (т.е. где x = '0') это приводит к:

Набор элементов 1
B → 0 •

и для терминала '1' (т.е. где x = '1') это приводит к:

Набор элементов 2
B → 1 •

и для нетерминала E (т.е. где x = E) это приводит к:

Набор элементов 3
S → E • eof
E → E • * B
E → E • + B

и для нетерминала B (т.е. где x = B) это приводит в:

Набор элементов 4
E → B •

Замыкание не пере новые элементы во всех случаях - в новых наборах выше, например, нет нетерминалов после точки.

Вышеуказанная процедура продолжается до тех пор, пока не будут найдены новые наборы элементов. Для набора элементов 1, 2 и 4 не будет никаких переходов, поскольку точка не находится перед каким-либо символом. Однако для набора элементов 3 у нас есть точки перед клеммами «*» и «+». Для символов x = * {\ textstyle x = {\ texttt {*}}}{\ textstyle x = {\ texttt {*}}} переход идет к:

Набор элементов 5
E → E * •B
+B → •0
+B → •1

и для x = + {\ textstyle x = {\ texttt {+}}}{\ textstyle x = {\ texttt {+ }}} переход идет к:

Набор элементов 6
E → E + •B
+B → •0
+B → •1

Теперь начинается третья итерация.

Для набора элементов 5 необходимо учитывать терминалы «0» и «1» и нетерминал B, но результирующие закрытые наборы элементов равны уже найденным наборам элементов 1 и 2, соответственно. Для нетерминала B переход идет к:

Набор элементов 7
E → E * B •

Для набора элементов 6 необходимо клеммы '0' и '1' и нетерминал B, но как и раньше, результирующие наборы элементов для терминалов равны уже найденным наборам элементов 1 и 2. Для нетерминала B переход идет к:

Набор элементов 8
E → E + B •

Эти последние наборы элементов 7 и 8 не имеют символов, кроме точек, поэтому новые наборы элементов больше не добавляются, процедура создания элементов завершена. Конечный автомат с наборами элементов в качестве состояния показано ниже.

Таблица переходов для автомата теперь выглядит следующим образом:

Набор элементов*+01EB
01234
1
2
356
4
5127
6128
7
8

Создание таблиц действий и переходов

Из этой таблицы и найденных наборов элементов, действие и переход таблицы строятся следующим образом:

  1. Столбцы для нетерминалов копируются в таблицу перехода.
  2. Столбцы для терминалов копируются в таблицу действий как действия сдвига.
  3. Дополнительный столбец для '$' (конец ввода) добавляется в таблицу действий, которая содержит элемент S → w • eof.
  4. Если элемент набор i содержит элемент вида A → w • и A → w является правилом m с m>0, тогда строка для состояния i в таблице действий полностью заполнена сокращения rm.

Может убедиться, что это действительно приводит к действию и таблице перехода, которые были представлены ранее.

Замечание о сравнении LR (0) с SLR и LALR-синтаксическим анализом

Только шаг 4 вышеупомянутой процедуры действий сокращения, поэтому все действия сокращения должны занимать всю строку таблицы, вызывая сокращение происходить независимо от следующего символа во входном потоке. Вот почему это таблицы синтаксического анализа LR (0): они не выполняют никакого предварительного просмотра (то есть они просматривают нулевые символы), прежде чем решить, какое сокращение выполнить. Грамматика, которая требует упреждения для устранения неоднозначности сокращений, потребует строки таблицы синтаксического анализа, содержащей различные действия сокращения в разных столбцах, а описанная выше процедура не способна создавать такие строки.

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

Конфликты в построенных таблицах

Автомат сконструирован таким образом, что гарантированно будет детерминированным. Однако, когда действия сокращения добавляются в таблицу действий, может случиться так, что одна и та же ячейка будет заполнена действием уменьшения и действием сдвига (конфликт сдвига-уменьшения) или двумя разными действиями уменьшения (конфликт уменьшения-уменьшения). Однако можно показать, что когда это происходит, грамматика не является грамматикой LR (0). Классическим реальным примером конфликта сдвиг-уменьшение является проблема dangling else.

Небольшой пример грамматики, отличной от LR (0), с конфликтом сдвиг-сокращение:

(1) E → 1 E
(2) E → 1

Один из найденных наборов элементов:

Набор элементов 1
E → 1 •E
E → 1 •
+E → • 1 E
+E → •1

Там представляет собой конфликт сдвига-уменьшения в этом наборе элементов: при построении таблицы действий в соответствии с приведенными выше правилами ячейка для [набор элементов 1, терминал '1'] содержит s1 (переход в состояние 1) и r2 (сокращение с помощью правила грамматики 2).

Небольшой пример грамматики, отличной от LR (0), с конфликтом сокращения-уменьшения:

(1) E → A 1
(2) E → B 2
(3) A → 1
(4) B → 1

В этом случае получается следующий набор элементов:

Набор элементов 1
A → 1 •
B → 1 •

В этом наборе элементов существует конфликт уменьшения-уменьшения, поскольку в ячейках в таблице действий для этого набора элементов будет действие уменьшения для правила 3 ​​и одно для правила 4.

Оба приведенных выше примера могут быть решены путем разрешения синтаксическому анализатору использовать следующий набор (см. LL-синтаксический анализатор ) нетерминала A, чтобы решить, будет ли он использовать одно из правил As для сокращения; он будет использовать правило A → w для сокращения, только если следующий символ во входном потоке находится в следующем наборе A. Это решение приводит к так называемым Простым анализаторам LR.

См. также
Ссылки
Дополнительная литература
  • Chapman, Nigel P., LR Parsing: Theory and Practice, Cambridge University Press, 1987. ISBN 0-521-30413-X
  • Пейджер, Д., Практический общий метод построения LR (k) Парсеры. Acta Informatica 7, 249 - 268 (1977)
  • «Конструирование компилятора: принципы и практика» Кеннета К. Лаудена. ISBN 0-534-939724
Внешние ссылки
Последняя правка сделана 2021-05-26 09:00:31
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте