- •Вопрос 1 Трансляторы , интерпретаторы и компиляторы
- •Вопрос 2
- •Вопрос3 Понятие о грамматике языка
- •Вопрос 4
- •Вопрос 4 Способы задания схем грамматик
- •Вопрос 5 Классификация грамматик и языков по Хомскому (грамматики классифицируются по виду их правил вывода)
- •Вопрос 6 *каша* думайте сами что и куда писать
- •Вопрос 6. Парт 2 Разбор цепочек
- •Преобразования грамматик
- •Вопрос 7
- •Вопрос 8 Простейшие способы организации таблицы идентификаторов
- •Вопрос 9 Автоматные грамматики и конечные автоматы
- •Вопрос 10 Регулярные выражения. Свойства регулярных выражений
- •Вопрос 11
- •Вопрос 12.1.
- •Вопрос 12.2
- •1. Для каждой упорядоченной пары терминальных символов выполняется не более чем одно из трех отношений предшествования:
- •Вопрос 13
- •Вопрос 14
- •Вопрос 15
- •Вопрос 16
- •Вопрос 17
- •Вопрос 18
- •Вопрос 19
- •Вопрос 20 Распознаватель на основе алгоритма «сдвиг-свертка»
- •Вопрос 21 Метод рекурсивного спуска
- •Вопрос 22
- •Вопрос 23 внутренние формы представления программы
- •Вопрос 24 Оптимизация объектного кода методом свертки
- •Оптимизация объектного кода методом исключения лишних операций
- •Общий алгоритм генерации и оптимизации объектного кода
Вопрос 16
Принципы построения распознавателей для LL(k)-грамматик
Для построения распознавателей LL(k)-грамматик используются два важных множества, определяемые следующим образом:
FIRST(k, ) — множество терминальных цепочек, выводимых из (VTVN)*, укороченных до k символов;
FOLLOW(k, A) — множество укороченных до k символов терминальных цепочек, которые могут следовать непосредственно за AVN в цепочках вывода
Формально эти два множества могут быть определены следующим образом:
FIRST(k, ) = {VT* | либо || k и * , либо ||>k и * х, x(VTVN)*}, (VTVN)*, k>0.
FOLLOW(k,A) = {VT* | S*A и FIRST(k, ), VT*}, AVN, k>0.
Очевидно, что если имеется цепочка терминальных символов VT*, то FIRST(k,) — это первые k символов цепочки .
Доказано, что грамматика G(VT,VN,P,S) является LL(k )-грамматикой тогда и только тогда, когда выполняется следующее условие:
А Р и А Р (): FIRST(k,)) FIRST(k,)) = для всех цепочек таких, что S * A.
Иначе говоря, если существуют две цепочки вывода:
S* A z *
S * A t *
то из условия FIRST(k, ) = FIRST(k, ) следует, что z = t.
На основе этих двух множеств строится алгоритм работы распознавателя для LL(k)-грамматик, который представляет собой k-предсказывающий алгоритм для МП-автомата, заданного так: R({q},VT,Z,,q,S,{q}), где Z = VTVN, a S - целевой символ грамматики G. Функция переходов автомата строится на основе управляющей таблицы М, которая отображает множество (Z{}) x VT* на множество, состоящее из следующих элементов:
пар вида (,i), где — цепочка символов, помещаемая автоматом на верхушку стека, a i — номер правила вида А, AVN, Z*;
«выброс»;
«допуск»;
«ошибка».
Конфигурацию распознавателя можно отобразить в виде конфигурации МП-автомата с дополнением цепочки , в которую помещаются номера примененных правил. Поскольку автомат имеет только одно состояние, его в конфигурации можно не указывать. Если считать, что Х — это символ на верхушке стека автомата, — непрочитанная автоматом часть входной цепочки символов, a = FIRST(k,), то работу алгоритма распознавателя можно представить следующим образом:
(, Х, ) (, , i), Z*, если М(Х,) = (,i);
(, Х, ) (', , ), если Х = a VT и = а', M(a,) = «выброс»;
(, , ) — завершение работы, при этом М(, ) - «допуск»;
иначе — «ошибка».
Цепочка = FIRST(k, ) носит в работе автомата название «аванцепочка».
Таким образом, для создания алгоритма распознавателя языка, заданного произвольной LL(k)-грамматикой, достаточно уметь построить управляющую таблицу М. Управляющую таблицу М, а также множества FIRST и FOLLOW можно получить на основе правил исходной грамматики.
Вопрос 17
Определение LR(k)-грамматики
Восходящие распознаватели выполняют построение дерева вывода снизу вверх. Результатом их работы является правосторонний вывод. Функционирование таких распознавателей основано на модификациях алгоритма «сдвиг-свертка» (или «перенос-свертка»), который был рассмотрен в разделе «Распознаватели КС-языков с возвратом».
Идея состоит в том, чтобы модифицировать этот алгоритм таким образом, чтобы на каждом шаге его работы можно было однозначно дать ответ на следующие вопросы:
что следует выполнять: сдвиг (перенос) или свертку;
какую цепочку символов а выбрать из стека для выполнения свертки;
какое правило выбрать для выполнения свертки (в том случае, если существует несколько правил вида A1, A2, ..., Аn).
Тогда восходящий алгоритм распознавания цепочек КС-языка не требовал бы выполнения возвратов, поскольку сам язык мог бы быть задан детерминированным расширенным МП-автоматом. Конечно, как уже было сказано, это нельзя сделать в общем случае, для всех КС-языков (поскольку даже сам класс детерминированных КС-языков более узкий, чем весь класс КС-языков). Но, вероятно, среди всех КС-языков можно выделить такой класс (или классы), для которых подобная реализация распознающего алгоритма станет возможной.
В первую очередь можно использовать тот же самый подход, который был положен в основу определения LL(k)-грамматик. Тогда мы получим другой класс КС-грамматик, который носит название LR(k)-грамматик.
КС-грамматика обладает свойством LR(k), k0, если на каждом шаге вывода для однозначного решения вопроса о выполняемом действии в алгоритме «сдвиг-свертка» («перенос-свертка») расширенному МП-автомату достаточно знать содержимое верхней части стека и рассмотреть первые k символов от текущего положения считывающей головки автомата во входной цепочке символов.
Грамматика называется LR(k)-грамматикой, если она обладает свойством LR(k) для некоторого k0.
Название «LR(k)», как и рассмотренное выше «LL(k)», также несет определенный смысл. Первая литера «L» также обозначает порядок чтения входной цепочки символов: слева— направо. Вторая литера «R» происходит от слова «right» и, по аналогии с LL(k), означает, что в результате работы распознавателя получается правосторонний вывод. Вместо «k» в названии грамматики стоит число, которое показывает, сколько символов входной цепочки надо рассмотреть, чтобы принять решение о действии на каждом шаге алгоритма «сдвиг-свертка». Так, существуют LR(0)-грамматики, LR(1)-грамматики и другие классы.
В совокупности все LR(k)-грамматики для всех kO образуют класс LR-грамматик.
Для LR(k)-грамматик известны следующие полезные свойства:
всякая LR(k)-грамматикa для любого k О является однозначной;
существует алгоритм, позволяющий проверить, является ли заданная грамматика LR(k)-грамматикой для строго определенного числа k.
Есть, однако, неразрешимые проблемы для произвольных КС-грамматик (они аналогичны таким же проблемам для других классов КС-грамматик):
не существует алгоритма, который бы мог проверить, является ли заданная КС-грамматика LR(k)-грамматикой для некоторого произвольного числа k;
не существует алгоритма, который бы мог преобразовать (или доказать, что преобразование невозможно) произвольную КС-грамматику к виду LR(k)-грамматики для некоторого k.
Кроме того, для LR-грамматик доказано еще одно очень интересное свойство — класс LR-грамматик полностью совпадает с классом детерминированных КС-языков. То есть, во-первых, любая LR(k)-грамматика задает детерминированный КС-язык (это очевидно следует из однозначности всех LR-грамматик), а во-вторых, для любого детерминированного КС-языка можно построить LR-грамматику, задающую этот язык. Второе утверждение уже не столь очевидно, но доказано в теории формальных языков [6, т.1, 65]. Более того, доказано даже, что любой детерминированный КС-язык может быть задан LR( 1 )-грамматикой.
В принципе класс LR-грамматик очень удобен для построения распознавателей детерминированных КС-языков (а все языки программирования, безусловно, относятся к этому классу). Но тот факт, что для каждого детерминированного КС-языка существует задающая его LR-грамматика, еще ни о чем не говорит, поскольку из-за неразрешимости проблемы преобразования отсутствует алгоритм, который позволил бы эту грамматику построить всегда. Данный детерминированный КС-язык может быть изначально задан грамматикой, которая не относится к классу LR-грамматик. В таком случае совсем не очевидно, что для этого языка удастся построить распознаватель на основе LR-грамматики, потому что в общем случае нет алгоритма, который бы позволил эту грамматику получить, хотя и известно, что она существует. То, что проблема не разрешима в общем случае, совсем не означает, что ее не удастся решить в конкретной ситуации. И здесь факт существования LR-грамматики для каждого детерминированного КС-языка играет важную роль — всегда есть смысл в каждом конкретном случае пытаться построить такую грамматику.