2. Теоретические сведения
Контекстно-свободные грамматики – это объекты теории формальных
грамматик, основы которой были заложены в фундаментальных
работах Н.Хомского.
Теория формальных грамматик занимается описанием, распознаванием и
переработкой языков. Под языком, определенном на алфавите
(множестве букв или символов) A, понимается множество слов L ⊆ A*,
где A* – множество всех возможных слов (цепочек), состоящих из букв
алфавита A. Для его описания вводится грамматика, как механизм,
порождающий слова, состоящие из букв его алфавита.
Для определения контекстно-свободной грамматики (КС-грамматики),
надо:
1) указать конечное множество A, называемое алфавитом; его элементы
называют символами; конечные последовательности символов
называют словами (в данном алфавите);
2) разделить все символы алфавита A на две группы: терминальные
("окончательные") и нетерминальные ("промежуточные");
3)
выбрать среди нетерминальных символов один, называемый
начальным;
4) указать конечное число правил грамматики, каждое из которых должно
иметь вид K → X, где K – некоторый нетерминальный символ, а X –
слово (в него могут входить и терминальные, и нетерминальные
символы).
3. Теоретические сведения
Пусть фиксирована КС-грамматика. Выводом в этой
грамматике называется последовательность слов
X[0], X[1], ..., X[n], в которой X[0] состоит из одного
символа, и этот символ – начальный, а X[i + 1]
получается
из
X[i]
заменой
некоторого
нетерминального символа K на слово X по одному из
правил грамматики.
Слово, составленное из терминальных символов,
называется выводимым, если существует вывод,
который им кончается. Множество всех выводимых
слов (из терминальных символов) называется языком,
порождаемым данной грамматикой.
4. Теоретические сведения
Пример 1. Пусть задана КС-грамматика:
1) алфавит состоит из четырех терминальных символов
(, ), [, ] и одного нетерминального символа E;
2) начальный символ – символ E.
3) правила: E → (E), E → [E], E → EE, E →.
Примеры выводимых с помощью заданной грамматики
слов: пустое слово, (), ([]), ()[([])], [()[]()[]]. Примеры
невыводимых с помощью заданной грамматики слов:
(, )(, (], ([)].
Пример 2. Грамматика, порождающая тот же язык, что и
в примере 1.
1) алфавит состоит из четырех терминальных символов
(, ), [, ] и двух нетерминальных символов T и E;
2) начальный символ – символ E.
3) правила: E → , E → TE, T → (E), T → [E].
5. Теоретические сведения
Для каждого нетерминального символа можно рассмотреть
множество всех слов из терминальных символов, которые из
него выводятся. Каждое правило грамматики можно
рассматривать как свойство этих множеств. Покажем это на
примере только что приведенной грамматики (пример 2). Пусть
T и E – множества слов, выводимых из нетерминалов T и E
соответственно. Тогда правилам грамматики соответствуют
такие свойства:
1) E → – E содержит пустое слово;
2) E → TE – если слово A принадлежит T, а слово B принадлежит
E, то слово AB принадлежит E;
3) T → (E) – если слово A принадлежит E, то слово (A)
принадлежит T;
4) T → [E] – если слово A принадлежит E, то слово [A]
принадлежит T.
Можно доказать, что множества, задаваемые грамматикой,
являются
минимальными
среди
всех
множеств,
удовлетворяющих соответствующим свойствам.
6. Теоретические сведения
Пример 3. Метод рекурсивного спуска. Он базируется на применении
следующей идеи. Для каждого нетерминального символа K строится
процедура ReadK, которая в применении к любому входному слову
1) находит наибольшее начало z слова x, которое может быть началом
выводимого из K слова,
2) сообщает, является ли найденное слово z выводимым из K.
Пусть буквы поступают по одной. Имеется функция Next, дающая первый
непрочитанный символ. Ее значениями могут быть терминальные
символы, а также символ EOI, означающий, что все слово уже
прочитано. Вызов функции Next не сдвигает границы между
прочитанной и непрочитанной частью – для этого есть процедура Move,
которая сдвигает границу на один символ (она применима в том
случае, если результат Next не равен EOI). Кроме этого, пусть имеется
булевская переменная b, значением которой является либо 1
(ИСТИНА) либо 0 (ЛОЖЬ). Теперь к процедуре ReadK можно
предъявить следующие требования:
1) ReadK прочитывает из оставшейся части слова максимальное начало
A, являющееся началом некоторого слова, выводимого из K,
2) значение b становится истинным или ложным в зависимости от того,
является ли A выводимым из K или лишь невыводимым началом
выводимого из K слова.
7. Теоретические сведения
Пример 4. Алгоритм разбора для LL(1)-грамматик. Его идея
заключается в применении левого вывода.
Левым выводом (слова в грамматике) называется вывод, в котором на
каждом шаге замене подвергается самый левый из нетерминалов.
Можно формально доказать, что для каждого выводимого слова (из
терминалов) существует левый вывод.
Для каждого слова X из терминалов и нетерминалов через Нач(X)
обозначим множество всех терминалов, с которых начинаются
непустые слова из терминалов, выводимые из X.
Для каждого нетерминала K через Послед(K) обозначим множество
терминалов, которые встречаются в выводимых (в грамматике)
словах сразу же за K. Кроме того, в Послед(K) включается символ
EOI, если существует выводимое слово, оканчивающееся на K.
Для каждого правила K → V (где K – нетерминал, V – слово,
содержащее терминалы и нетерминалы) определим множество
направляющих терминалов, обозначаемое Напр(K → V). По
определению оно равно Нач(V), к которому добавлено Послед(K),
если из V вводится пустое слово.
8. Теоретические сведения
Пример 4 (продолжение).
Грамматика называется
LL(1)грамматикой, если для любых правил
K → V и K → W с
одинаковыми левыми частями множества Напр(K → V) и Напр(K →
W) не пересекаются. Для LL(1)-грамматики существует не более
одного возможного продолжения левого вывода.
Грамматика называется леворекурсивной, если из некоторого
нетерминала K выводится слово, начинающееся с K, но не
совпадающее с ним. К леворекурсивным грамматикам LL(1)-метод
неприменим – их необходимо преобразовывать к эквивалентным
LL(1)-грамматикам или применять для них другие методы
распознавания.
С учетом всего сказанного можно построить следующий алгоритм
проверки выводимости слова из терминалов в LL(1)-грамматике.
Мы следуем описанному выше методу поиска левого вывода, храня
лишь часть слова, находящуюся правее уже прочитанной части
входного слова, т.е. храним слово S
из терминалов и
нетерминалов, обладающее таким свойством (прочитанную часть
входа обозначаем через A):
9. Теоретические сведения
Пример 4 (продолжение).
1) слово AS выводимо в грамматике;
2) любой левый вывод входного слова проходит через стадию AS.
Эти свойства вмести будем обозначать «(И)».
Вначале A пусто, а S состоит из единственного символа – начального
нетерминала.
Если в некоторый момент S начинается на терминал t и t = Next (Next
– функция такая же, как в примере 17.3), то можно выполнить
команду Move (Move – процедура такая же, как в примере 17.3) и
удалить символ t, являющийся начальным в S, поскольку при этом
AS не меняется.
Если S начинается на терминал t и t ≠ Next, то входное слово
невыводимо – ибо по условию любой его вывод должен проходить
через AS. (Это же справедливо и в случае Next = EOI.)
Если S пусто, то из условия (И) следует, что входное слово выводимо
тогда и только тогда, когда Next = EOI.
10. Теоретические сведения
Пример 4 (продолжение).
Остается случай, когда S начинается с некоторого нетерминала K. По
сказанному выше все левые выводы из S слов, начинающихся на
символ Next, начинаются с применения к S одного и того же
правила – того, для которого Next принадлежит направляющему
множеству. Если таких правил нет, то входное слово невыводимо.
Если такое правило есть, то нужно применить его к первому
символу слова S – при этом свойство (И) не нарушится.
Алгоритм заканчивает работу, поскольку при появлении нетерминала
в начале слова S происходит чтение со входа или остановка, а
бесконечный цикл сменяющих друг друга терминалов в начале S
означал бы, что грамматика леворекурсивна.
11. Контрольные вопросы
1. Формальная грамматика.
2. КС-грамматика.
3. Вывод в КС-грамматике.
4. Выводимое (в грамматике) слово.
5. Задание множеств КС-грамматиками.
6. Метод рекурсивного спуска.
7. Левый вывод.
8. LL(1)-грамматика.
9. Леворекурсивная грамматика.
10.Алгоритм разбора для LL(1)-грамматик.
12. Указания по выполнению заданий
1.
2.
3.
4.
Получить задание
Выполнить задание.
Подготовить отчет по выполнению задания. Отчет
должен включать задание и описание этапов
выполнения задания.
Ответить на вопросы по выполнению задания и
контрольные вопросы
13. Задания
1. Построить КС-грамматику, в которой выводимы слова
1) 00..0011..11 (число нулей равно числу единиц);
2) 00..0011..11 (число нулей вдвое больше числа единиц);
3) 00..0011..11 (число нулей больше числа единиц)
и только они.
2. Доказать, что не существует КС-грамматики, в которой были бы
выводимы слова вида 00..0011..1122..22, в которых числа
нулей, единиц и двоек равны, и только они.
3. Дана произвольная КС-грамматика. Построить алгоритм
проверки принадлежности задаваемому ей языку, работающий
полиномиальное время (т.е. число действий не превосходит
полинома от длины проверяемого слова; полином может
зависеть от грамматики).
14. Задания
4. Дана грамматика с
единственным
нетерминалом K,
нетерминалами 1, 2, 3 и правилами
1)
K→0
K→1K
K→2KK
K→3KKK
2)
K→0
K→K1
K→KK2
K→KKK3
Построить алгоритм проверки выводимости слова в этой
грамматике при чтении слово слева направо. (Число действий
при прочтении одной буквы должно быть ограничено.)
16. Задания
6. В грамматике из примера 2 найти левый вывод слова
A = [()([])] и доказать, что он единствен.
7. Написать LL(1)-грамматику языка, задаваемого следующей КСграмматикой:
1) алфавит: K (нетерминал), # (терминал);
2) правила:
K → K #.
8. Описать на псевдокоде (и/или языке высокого уровня, например,
на языке C) алгоритм проверки выводимости слова из
терминалов в LL(1)-грамматике. Оценить его сложность.