
- •Сибирский государственный аэрокосмический университет
- •Красноярск 2011 г.
- •Глава 2. Базовые понятия теории формальных языков и грамматик
- •2.1. Алфавиты, цепочки и языки. Основные понятия и определения
- •2.2. Представление языков
- •2.3. Грамматики
- •2.3.1. Формальное определение грамматики
- •2.3.2. Классификация грамматик и языков по Хомскому
- •2.3.3. Разбор цепочек
- •2.3.4. Преобразования грамматик
- •2.4. Задачи
- •Глава 3. Лексический анализ
- •3.1. Описание модельного языка
- •3.2. Методы и средства лексического анализа
- •Int scan_g(){
- •3.3. Недетерминированный разбор
- •3.4. Задачи лексического анализа
- •3.5 Лексический анализатор для м-языка
- •3.6. Задачи
- •Глава 4. Синтаксический и семантический анализ
- •4.1. Задачи и методы синтаксического анализа
- •4.2 Сущность метода рекурсивного спуска
- •4.3. Условия применимости метода рекурсивного спуска
- •Void l()
- •Void s(void)
- •Void a(void)
- •4.4. Синтаксический анализатор для м-языка
- •4.5. Семантический анализ
- •Void a() {
- •Void check()
- •4.6. Семантический анализатор для м-языка
- •4.6.1. Обработка описаний
- •Int declare; / описан ? 1-"да", 0-"нет" /
- •Void decid (int I, char t)
- •Void ipush (int I); / значение I - в стек /
- •Int ipop (void); / из стека - целое /
- •Void dec (char t)
- •4.6.2. Контроль контекстных условий в выражении
- •Void spush (char s); / значение s - в стек /
- •4.6.3. Контроль контекстных условий в операторах
- •Void eqtype (void)
- •Void eqbool (void)
- •4.7. Задачи
- •Глава 5. Генерация внутреннего представления программ
- •5.1. Язык внутреннего представления программы
- •5.2 Синтаксически управляемый перевод
- •5.3 Генератор внутреннего представления программы на м-языке
- •Int value;}
- •Void put_lex (struct lex l)
- •Void put_lex5 (struct lex l)
- •Void checkop_p (void)
- •If (strcmp (res, "no"))
- •5.4 Интерпретатор полиЗа для модельного языка
- •Void interpreter(void) {
- •If (eq ("not")) {ipush (!ipop()); break;};
- •Ipush(ip); break;
- •Ipush(ip); break;
- •Ipush((int)ip); break;
- •5.5 Задачи
- •Литература
2.3. Грамматики
2.3.1. Формальное определение грамматики
Для нас наибольший интерес представляет одна из систем генерации языков – грамматики. Понятие грамматики изначально было формализовано лингвистами при изучении естественных языков. Предполагалось, что это может помочь при их автоматической трансляции. Однако наилучшие результаты в этом направлении достигнуты при описании не естественных языков, а языков программирования. Примером может служить способ описания синтаксиса языков программирования при помощи БНФ – формы БэкусаНаура, которая предполагает использование в качестве нетерминальных символов комбинаций слов естественного языка, заключенных в угловые скобки, а в качестве разделителя - специального знака, состоящего из двух двоеточий и равенства. Например, если правила <L><L> и <L><E> записаны в символической форме, и символ <L> соответствует синтаксическому понятию "список", а символ <E> - "элемент списка", то их можно представить в форме Наура-Бэкуса так:
<список>::= <элемент списка><список>, <список>::= <элемент списка>.
Чтобы сократить описание схемы грамматики, в БНФ разрешается объединять правила c одинаковой левой частью в одно правило, правая часть которого должна включать правые части объединяемых правил, разделенные вертикальной чертой. Используя объединение правил, для рассматриваемого примера получаем:
<список>::=<элемент списка><список>|<элемент списка>.
Декартовым произведением A B множеств A и B называется множество {(a,b) | a A, b B}.
Порождающая грамматика G - это четверка (VT, VN, P, S), где
VT - алфавит терминальных символов (терминалов), то есть множество таких символов, которые считаются известным и не требуют определения;
VN - алфавит нетерминальных символов (нетерминалов), не пересекающийся с VT, то есть множество таких символов, которые требуют определения в грамматике;
P - это конечное подмножество множества (VT VN)+ (VT VN)*;
Р является множеством правил или продукций (то есть способов определения нетерминальных символов) вида { } (из некоторой цепочки следует цепочка ), где образована из терминальных или нетерминальных символов, а также может быть пустой: ( VTVN )*, а - цепочка, которая в общем случае содержит как терминалы, так и нетерминалы, но в ней обязательно должен быть один нетерминал.
Элемент (, ) множества P называется правилом вывода и записывается в виде ,
S - начальный символ (цель) грамматики, нетерминал, S VN.
Мы будем использовать большие латинские буквы для обозначения нетерминальных символов, малые латинские буквы из начала алфавита для обозначения терминальных символов, малые латинские буквы из конца алфавита для обозначения цепочек из VT* и, наконец, малые греческие буквы для обозначения цепочек из ( VTVN )*.
Для записи правил вывода с одинаковыми левыми частями
1 2 ... n
будем пользоваться сокращенной записью
1 | 2 |...| n.
Каждое i , i= 1, 2, ... ,n , будем называть альтернативой правила вывода из цепочки .
Пример 2.3
1) Грамматика G1 = ({0,1}, {A,S}, P, S),
где P состоит из правил Р={ S 0A1, 0A 00A1, A }
Применяя последовательно правила (S 0A1 00A11 0011), получаем цепочку 0011.
Эта грамматика порождает язык L(G1) = {0n 1n |n > 0}.
2) Грамматика G2 = ({a, b, c}, {S, B, C}, P, S),
P = {S aSBC, S aBC, CB BC, aB ab, bB bb, bC bc, cC cc}.
Эта грамматика порождает язык L(G2) = {an bn cn |n > 0}.
Действительно, применяем n - 1 раз правило 1 и получаем an-1 S(BC)n-1 , затем один раз правило 2 и получаем an (BC)n , затем n(n - 1)/2 раз правило 3 и получаем anBnCn. Затем используем правило 4, получаем anbBn-1Cn . Затем применяем n – 1 раз правило 5 и получаем anbnCn. Затем применяем правило 6 и n - 1 раз правило 7 и получаем anbncn. Можно показать, что язык L(G2) состоит из цепочек только такого вида.
3) Грамматика G3 = ({0, 1},{S}, {S 0S1, S 01}, S).
Легко видеть, что цепочка 000111 L(G), так как существует вывод
S 0S1 00S11 00111
Нетрудно показать, что грамматика порождает язык L(G3) = {0n 1n |n > 0}.
Грамматика
G4 = ({0, 1},{S, A}, {S 0S, S 0A, A 1A, A 1}, S)
порождает язык L(G4) = {0n 1m |n,m > 0}, что нетрудно показать.
Цепочка (VT VN)* называется непосредственно выводимой из цепочки (VT VN)+ в грамматике G = (VT, VN, P, S) (обозначим ), если = 12, = 12, где 1, 2, (VT VN)*, (VT VN)+ и правило вывода содержится в P.
Например, цепочка 00A11 непосредственно выводима из 0A1 в грамматике G1.
Цепочка (VT VN)* называется выводимой из цепочки (VT VN)+ в грамматике G = (VT, VN, P, S) (обозначим ), если существуют цепочки 0, 1, ... , n (n0), такие, что = 0 1 ... n= .
Последовательность 0, 1, ... , n называется выводом длины n.
Например, S 000A111 в грамматике G1 (см. пример 2.3), так как существует вывод S 0A1 00A11 000A111. Длина вывода равна 3.
Языком, порождаемым грамматикой G = (VT, VN, P, S), называется множество L(G)={ VT* | S }.
Другими словами, L(G) - это все цепочки в алфавите VT, которые выводимы из S с помощью P.
Например, L(G1) = {0n1n | n>0}.
Цепочка (VT VN)*, для которой S , называется сентенциальной формой в грамматике G = (VT, VN, P, S).
Таким образом, язык, порождаемый грамматикой, можно определить как множество терминальных сентенциальных форм.
Грамматики G1 и G2 называются эквивалентными, если L(G1) = L(G2).
Пример 2.4
Грамматики G1 = ({0,1}, {A,S}, P1, S) и G2 = ({0,1}, {S}, P2, S), где
P1={ S 0A1, 0A 00A1, A P2={S 0S1 | 01}
эквивалентны, так как обе порождают язык
L(G1) = L(G2) = {0n1n | n>0}.
Грамматики G1 и G2 называются почти эквивалентными, если
L(G1) { = L(G2) {.
Другими словами, грамматики почти эквивалентны, если языки, ими порождаемые, отличаются не более чем на .
Пример 2.5
Грамматики G1 = ({0,1}, {A,S}, P1, S) и G2 = ({0,1}, {S}, P2, S), где
P1={ S 0A1, 0A 00A1, A P2={ S 0S1 |
являются почти эквивалентными, так как
L(G1)={0n1n | n>0}, а L(G2)={0n1n | n0}, то есть L(G2) состоит из всех цепочек языка L(G1) и пустой цепочки, которая в L(G1) не входит.