Методы синтаксического анализа КС-языков
Проблема синтаксического анализа для КС-языков состоит в построении алгоритма, который по любой КС-грамматике и цепочке в ее терминальном алфавите распознает, принадлежит ли языку , порождаемому грамматикой . В случае положительного ответа на вопрос алгоритм должен строить дерево вывода в грамматике .
Существование такого алгоритма следует из факта разрешимости проблемы принадлежности для КС-языков. Мы рассмотрим некоторые алгоритмы синтаксического анализа для определенных классов КС-языков.
Прототипом синтаксического анализатора является МП-автомат, который строится по данной КС-грамматике, но такой МП-автомат, как мы видели, является в общем случае недетерминированным и может даже для допустимой цепочки построить вывод, который заканчивается тупиковой конфигурацией. Чтобы на основе такого распознавателя построить алгоритм синтаксического анализа (и тем самым превратить распознаватель в анализатор), нужно разработать определенный механизм управления выводом на множестве конфигураций МП-автомата. Этот механизм должен обеспечить эффективный поиск допускающей последовательности конфигураций для допустимых цепочек. В частности, может быть поставлена задача разработки алгоритма беступикового, или однопроходного, анализа. Беступиковый анализ имеет место, если получение тупиковой конфигурации в процессе анализа означает "неправильность" анализируемой цепочки, т.е. тот факт, что она не принадлежит языку, порождаемому заданной грамматикой. Беступиковый анализ, как можно показать, невозможен в случае произвольного КС-языка, но для некоторых (достаточно широких) классов КС-языков он может быть реализован. Некоторые алгоритмы беступикового синтаксического анализа мы очень коротко рассмотрим в этом дополнении.
Существуют две основные стратегии синтаксического анализа:
1) нисходящий анализ (называемый также анализом "сверху вниз", или анализом "разверткой"); 2) восходящий анализ (анализ "снизу вверх", "сверткой").
В нисходящем анализе дерево вывода цепочки строится от корня к листьям, т.е. дерево вывода "реконструируется" в прямом порядке, и аксиома грамматики "развертывается" в цепочку. В восходящем анализе дерево вывода строится от листьев к корню и анализируемая цепочка "свертывается" в аксиому.
Рассмотрим две стратегии анализа по очереди.
Нисходящий анализ. LL(k)-грамматики
Мы видели, что МП-автомат, который при доказательстве теоремы 8.8 мы строили по данной КС-грамматике, "воспроизводит" левый вывод в грамматике. Основная "задача" данного автомата как распознавателя состоит в том, чтобы на каждом шаге "угадать" очередное применяемое правило вывода и "правильно выбрать" соответствующую команду при построении допускающей последовательности конфигураций. Механизм управления выводом, которым мы должны снабдить классический МП-автомат, должен обеспечить выбор команды (единственной в случае беступикового анализа) по определенной информации о состоянии процесса поиска дерева вывода (или, что равносильно, левого вывода) входной цепочки. Обычно механизм управления выводом реализуется в виде специальных таблиц, которые называют управляющими таблицами и которые можно рассматривать как дополнительное устройство памяти в МП-автомате.
Существует класс грамматик, называемых LL(k)-грамматиками, в которых применяемая команда однозначно определяется:
1) прочитанной частью входной цепочки; эту цепочку называют левым контекстом; 2) находящимся в данный момент в верхней ячейке магазина нетерминальным символом ; 3) началом (префиксом) непрочитанной части цепочки, состоящим не более чем из букв ; этот префикс называют аванцепочкой (рис. 8.37).
Эта информация, по которой беступиковый анализатор выбирает нужную команду на каждом шаге работы с входной цепочкой, организована в виде управляющей таблицы (конкретные формы таких таблиц будут рассмотрены ниже). При этом левый вывод цепочки , если , может быть представлен следующим образом: из аксиомы выводится цепочка , затем нетерминал заменяется в соответствии с правилом и из цепочки выводится терминальная цепочка , где
 (символ  означает левую выводимость).
Описанное выше представление левого вывода цепочки предполагает, что мы произвольно в этом выводе фиксировали некий шаг, состоящий в замене нетерминала посредством применения правила вывода . Так как в левом выводе на каждом шаге производится замена самого левого вхождения нетерминального символа, то слева от в цепочке , полученной перед рассматриваемым шагом, должны быть только терминальные символы. Следовательно, определенный выше левый контекст есть не что иное, как левое крыло вхождения нетерминала в цепочку .
Моделируя этот левый вывод, т.е. читая записанную на входной ленте цепочку , МП-автомат читает левый контекст , а затем с помощью управляющей таблицы, "видя" в магазине символ , учитывая левый контекст и зная аванцепочку , принимает единственно правильное решение, применяя команду, соответствующую правилу . Так на интуитивном уровне можно определить ключевое свойство LL(k)-грамматики.
Переходим теперь к построению формального определения LL(k)-грамматики.
Пусть дана КС-грамматика . Для цепочки а в объединенном алфавите грамматики и положительного натурального определим множество , состоящее из всех терминальных цепочек, которые либо выводятся из цепочки а (если их длина строго меньше ), либо являются k-буквенными префиксами терминальных цепочек, выводимых из (обратим внимание на то, что везде речь идет о левом выводе).
Таким образом, .
Нетрудно видеть, что для всякой терминальной цепочки получим , если , и , если . Множества (для разных цепочек ) иногда будем называть -множествами.
Пример 8.23. Зададим грамматику с терминальным алфавитом и нетерминальным алфавитом, состоящим из одной аксиомы , следующим множеством правил вывода:
Вычислим множество . Первая буква всех терминальных цепочек, выводимых из заданной, уже фиксирована — это буква . Нетерминал может быть заменен буквой , после чего возникнет трехбуквенный префикс . Но символ можно заменить и цепочками . В силу этого первые две буквы цепочки, выводимой из исходной, могут быть либо , либо . Продолжение вывода, как нетрудно понять, может дать третью букву — либо , либо , либо . Окончательно получаем
Определение 8.11. КС-грамматику называют LL(k)-грамматикой (для произвольно фиксированного ), если для любых , таких, что существуют левые выводы
Таким образом, из этого определения следует, что для LL(k)-грамматики левый контекст нетерминала в цепочке и не более чем символов, с которых начинается терминальная цепочка , выводимая из , однозначно определяют то правило, которое нужно применить к цепочке (заменяя в ней выделенное, т.е. первое, вхождение нетерминала ), чтобы сделать очередной шаг в левом выводе цепочки (и именно этой цепочки!) из аксиомы грамматики . При совпадении множеств и , как следует из сформулированного определения, указанные два вывода "сливаются" в один, т.е. тогда .
Определение LL(k)-грамматики является само по себе труднопроверяемым. Нужно какое-то условие, проверка которого позволила бы дать ответ на вопрос, является ли заданная КС-грамматика LL(k)-грамматикой. Доказывается, что дать ответ на этот вопрос можно, только фиксировав число . Построить же алгоритм, отвечающий на вопрос, является ли данная КС-грамматика LL(k)-грамматикой для некоторого (не задавая его заранее), невозможно.
Следующая теорема (формулируемая без доказательства) дает соответствующий критерий (необходимое и достаточное условие) при фиксированном .
Теорема 8.12. КС-грамматика является LL(k)-грамматикой (для фиксированного ) тогда и только тогда, когда для любой цепочки выполняется следующее условие: для любых , таких, что , и любых двух различных правил и грамматики имеет место .
Условие теоремы называют часто LL(k)-условием. Его следует проверять, фиксируя по очереди левые контексты (т.е. различные цепочки ) для всех нетерминалов грамматики.
Рассмотрим пример, в котором, используя теорему 8.12, мы убедимся в том, что заданная КС-грамматика является LL(k)-грамматикой (для фиксированного ).
Пример 8.24. Грамматика задается системой правил
Докажем, что данная грамматика является LL(2)-грамматикои. Чтобы проверить условие теоремы 8.12, достаточно для каждого нетерминала нашей грамматики проделать следующее:
1) вычислить все такие цепочки и , что имеет место левая выводимость
 (8.26)
2) фиксировав "левый контекст" , для каждой пары различных правил вывода и вычислить множества и . Для всех таких а, что при фиксированном w выполняется (8.26), и убедиться в том, что их пересечение пусто.
Если выполнение пункта 2 для всех возможных левых контекстов w и всех нетерминалов В подтвердит истинность условия теоремы 8.12, то тем самым и будет доказано, что перед нами LL(2)-грамматика.
Следует заметить, что в общем случае вычисление пар цепочек , удовлетворяющих условию (8.26) (для всех нетерминалов КС-грамматики), требует применения специального алгоритма. Но для конкретной грамматики нашего примера эти пары цепочек достаточно просто вычисляются из анализа выводов грамматики. По очереди проанализуем оба нетерминала, т.е. и , грамматики .
Нетерминал S
В этом случае имеем, во-первых, тривиальную выводимость за нуль шагов, т.е. в этом случае цепочки и обе являются пустыми: . Нетерминал S может быть заменен согласно правилу (1) с правой частью или согласно правилу (2) с пустой правой частью. Вычислим тогда множества и . Ясно, что , а и эти множества не пересекаются.
Проанализируем теперь, каким образом в заданной грамматике может быть выведена из аксиомы за число шагов, большее нуля, цепочка вида для некоторых терминальной цепочки и цепочки в объединенном алфавите .
Так как вхождение может возникнуть только после применения правила (3), а чтобы применить его, нужно сначала применить правило (1), то, чередуя применение этих правил раз, получим вывод:
 (8.27)
Это значит, что все возможные пары , такие, что (с учетом уже ранее рассмотренного случая, когда обе цепочки пусты), есть и .
Вычисляем множества и для различных . Первое множество, как нетрудно видеть, для всех будет равно , a второе при будет состоять из одной пустой цепочки, а при — из цепочки , т.е. можно утверждать, что для всех пересечение
Итак, для нетерминала (аксиомы грамматики ) условие теоремы 8.12 выполнено.
Нетерминал A
Рассматривая вывод (8.27), легко убедиться в том, что все пары , для которых имеет место левая выводимость , есть для всех .
Нетерминал является левой частью двух правил вывода (3) и (4) с правыми частями и соответственно. Вычисляем соответствующие F2-множества: , а , если , и , если . Как видно, при всех пересечение указанных множеств пусто. Тем самым доказано, что и для нетерминала А критерий теоремы 8.12 выполнен и заданная грамматика является LL(2)-грамматикой.
Полученный результат показывает также, что эта грамматика является LL(2)-грамматикой, не являясь LL(1)-грамматикой. Действительно, для нетерминала получим
Следовательно, при всех указанные множества совпадут, что означает невыполнение LL(1)-условия. Заметим, что для нетерминала LL(1)-условие выполняется.
Результаты анализа представлены в табл. 8.1.
Внутри таблицы указаны номера применяемых правил. Прочерк означает, что данная пара (нетерминал, аванцепочка) не определяет никакого правила, и возникновение такой пары в процессе анализа сигнализирует о синтаксической ошибке.
Мы видим, что в данном случае номер применяемого на каждом шаге правила вывода однозначно определяется только двумя факторами: нетерминалом в верхней ячейке магазина и аванцепочкой. LL(k)-грамматики, в которых выполняется такое условие, называют сильными LL(к)-грамматиками.
Таблица подобного вида для сильной LL(k)-грамматики и является примером управляющей таблицы. Именно она обеспечивает в данном случае тот механизм управления выводом, который позволяет МП-автомату, построенному по заданной сильной LL(k)-грамматике, на каждом шаге однозначно выбирать правило вывода и для "правильной" цепочки строить ее левый вывод, а для "неправильной" — сигнализировать об ошибке.
Следующий пример показывает, что существуют LL(k)-грамматики, не являющиеся сильными.
Пример 8.25. Зададим грамматику системой правил
Для этой грамматики легко построить все ее деревья вывода (рис. 8.38) и проверить LL(k)-условие.
В данном случае влечет и , а , то есть .
Из следует, что или . Если , то .
Если же , то и , и в этом случае также . Таким образом, рассматриваемая грамматика есть LL(2)-грамматика.
Сведем полученные результаты в табл. 8.2.
Из табл. 8.2 следует, что одна и та же пара не определяет однозначно применяемого правила и требуется информация о левом контексте.
Из этой же таблицы видно, что данная грамматика не является LL(1)-грамматикой, поскольку, например, однобуквенная аванцепочка вместе с нетерминалом и левым контекстом не определяет однозначно применямого правила.
Таким образом, для сильных LL(k)-грамматик МП-автомат, который строится по КС-грамматике, может быть преобразован в синтаксический анализатор путем добавления к нему управляющей таблицы и выходной ленты (первоначально пустой), на которой автомат пишет номера правил в порядке их применения, а также сигнал ошибки. В результате анализа для правильной цепочки, записанной на входной ленте, на выходной ленте появится ее левый вывод (точнее, "протокол" левого вывода в виде последовательности номеров применимых правил), а для неправильной цепочки на выходной ленте появится сообщение об ошибке (в виде какого-нибудь специального символа).
Представим теперь протоколы анализа некоторых цепочек в грамматике примера 8.24.
Пусть ; имеем последовательность конфигураций (четвертая компонента конфигурации — содержимое выходной ленты):
Обратим внимание на то, что на каждом шаге вывода наш автомат-анализатор в существенном отличии от обычного распознавателя, который может легко "заблудиться" (особенно при выборе альтернативы для нетерминала ), использует информацию управляющей таблицы.
Посмотрим, как отработает такой анализатор синтаксическую ошибку. Берем цепочку , имеем
так как пара не определяет никакого правила.
Для не сильной LL(k)-грамматики учет левых контекстов в общем случае весьма сложен, и мы его здесь не рассматриваем.
Приведем, наконец, простой пример грамматики с терминальным алфавитом , не являющейся LL(k)-грамматикой ни для какого 
Для любого имеем , .
Одинаковый префикс не дает возможности отождествить цепочки и , т.е. нет возможности обеспечить выбор альтернативы для нетерминала .
Если заметили ошибку, опечатку или есть предложения, напишите в комментариях.
|