Я пытаюсь понять крайний левый вывод в контексте алгоритма синтаксического анализа LL. Эта ссылка объясняет это с генеративной точки зрения . то есть показывает, как следовать самому левому выводу, чтобы сгенерировать конкретную последовательность токенов из набора правил.
Но я думаю о противоположном направлении. Учитывая поток токенов и набор правил грамматики, как найти правильные шаги для применения набора правил с помощью самого левого вывода?
Давайте продолжим использовать следующую грамматику из вышеупомянутой ссылки:
И данная последовательность токенов: 1 2 3
Один из способов это:
1 2 3
-> D D D
-> N D D (rewrite the *left-most* D to N according to the rule N->D.)
-> N D (rewrite the *left-most* N D to N according to the rule N->N D.)
-> N (same as above.)
Но есть и другие способы применения правил грамматики:
1 2 3 -> D D D -> N D D -> N N D -> N N N
ИЛИ
1 2 3 -> D D D -> N D D -> N N D -> N N
Но только первая деривация заканчивается в единственном нетерминале.
По мере увеличения длины последовательности токенов может быть гораздо больше способов. Я думаю, чтобы сделать правильный вывод , необходимы 2 предварительных условия:
- стартовое / корневое правило
- последовательность токенов
После того, как вы зададите эти 2, каков алгоритм, чтобы найти производные шаги? Должны ли мы сделать конечный результат одиночным нетерминальным?
1 ответ
Общий процесс разбора LL состоит из повторения:
Спрогнозируйте результативность верхнего грамматического символа в стеке, если этот символ нетерминальный, и замените этот символ правой частью продуктивного символа.
Сопоставьте верхний символ грамматики в стеке со следующим входным символом, отбросив их оба.
Действие совпадения не вызывает проблем, но для предсказания может потребоваться оракул. Однако для целей этого объяснения механизм, с помощью которого делается прогноз, не имеет значения, если он работает. Например, может случиться так, что для некоторого небольшого целого числа k
каждая возможная последовательность входных символов k
согласуется только с одним возможным продуктом, и в этом случае вы можете использовать справочную таблицу. В этом случае мы говорим, что грамматика - это LL(k)
. Но вы можете использовать любой механизм, в том числе и магический. Нужно только, чтобы прогноз всегда был точным.
На любом этапе этого алгоритма частично производная строка является потребляемым вводом, добавленным к стеку. Изначально нет потребляемого ввода, и стек состоит только из начального символа, так что частично производная строка (к которой применено 0 производных). Поскольку потребляемый ввод состоит исключительно из терминалов, а алгоритм всегда изменяет только верхний (первый) элемент стека, ясно, что последовательность частично производных строк составляет крайнее левое производное.
Если синтаксический анализ успешен, весь ввод будет использован, а стек будет пуст, поэтому синтаксический анализ приведет к самому левому производному ввода от начального символа.
Вот полный анализ вашего примера:
Consumed Unconsumed Partial Production
Input Stack input derivation or other action
-------- ----- ---------- ---------- ---------------
N 1 2 3 N N → N D
N D 1 2 3 N D N → N D
N D D 1 2 3 N D D N → D
D D D 1 2 3 D D D D → 1
1 D D 1 2 3 1 D D -- match --
1 D D 2 3 1 D D D → 2
1 2 D 2 3 1 2 D -- match --
1 2 D 3 1 2 D D → 3
1 2 3 3 1 2 3 -- match --
1 2 3 -- -- 1 2 3 -- success --
Если вы прочитаете последние два столбца, вы увидите, как процесс вывода начинается с N
и заканчивается 1 2 3
. В этом примере прогноз можно сделать только с помощью магии, потому что правило N → N D
не является LL ( k ) для любого k ; использование вместо этого правила правой рекурсии N → D N
разрешило бы процедуру принятия решения LL (2) (например, "использовать N → D N
, если есть по крайней мере два неиспользованных входных токена; в противном случае N → D
". )
Диаграмма, которую вы пытаетесь создать, которая начинается с 1 2 3
и заканчивается N
, является синтаксическим анализом снизу вверх . Анализ снизу вверх с использованием алгоритма LR соответствует крайним правым производным, но производные нужно читать в обратном направлении, поскольку они заканчиваются начальным символом.
N → N D
не равно LL(k)
ни для какого k; использование вместо этого правила правой рекурсии N → D N
разрешило бы процедуру принятия решения LL(2)
. . На мой взгляд, с правилом N → N D
, если есть хотя бы два неиспользованных входных токена, мне все равно нужно применить N → N D
. Я не вижу здесь другого выбора.
N
на N D
никогда не потребляет входной токен. Вы должны сделать это ровно столько раз, сколько у вас есть входных данных, но поскольку он не потребляет входные данные, вы не можете знать, сколько раз, без проверки всего входного потока, неограниченного просмотра вперед. Напротив, после замены N
на D N
следующие два шага должны использовать входной токен, соответствующий D
, что позволит продолжить синтаксический анализ. Таким образом, стек всегда состоит только из D N
, и только тогда, когда следующий токен является последним оставшимся, вам нужно сделать что-то другое.
k
для некоторого фиксированного k
. Как это бывает, алгоритм LR будет работать как с леворекурсивными, так и с праворекурсивными правилами, но алгоритм LL не будет работать с леворекурсивными правилами, как показано на этом тривиальном примере. Но обычно можно конвертировать ...
Похожие вопросы
Связанные вопросы
Новые вопросы
algorithm
Алгоритм - это последовательность четко определенных шагов, которые определяют абстрактное решение проблемы. Используйте этот тег, если ваша проблема связана с дизайном алгоритма.