SlideShare a Scribd company logo
1 of 22
Формальные языки и грамматики
План лекции:






Формальные языки и формальные грамматики
Способы задания формальных грамматик
Общий алгоритм КС-разбора (на примере)
LL(1)-грамматики и алгоритм рекурсивного спуска
Обратная польская запись и алгоритм Дейкстры

автор: С.Н. Дроздов
Формальные языки


Дан конечный алфавит T (терминальный алфавит).








Примеры: T = {a, b, c, … , z}; T = {0, 1}; T = {a}

Цепочка (строка) – любая конечная последовательность букв из T
(возможно, нулевой длины).




2

Примеры: "a", "aacb", "babababababababa", "0000011111",
ε (пустая цепочка).

Множество всех цепочек над T обозначим T*.
Язык над алфавитом T – любое подмножество цепочек L ⊆ T*.
Все цепочки α ∈ L называются предложениями этого языка.
Способы задания языка:





перечисление всех предложений (пример: язык Эллочки-людоедки);
любой алгоритм, принимающий на входе цепочку над T и
возвращающий "да"/"нет" (распознаватель языка);
формальная грамматика.
Формальные грамматики


Формальная грамматика определяется следующей четверкой:









терминальный алфавит T;
нетерминальный алфавит N (алфавит для записи правил);
правила вывода цепочек: αk → βk; αk, βk – цепочки над T ∪ N;
начальный символ (главный нетерминальный символ) P.

Предложениями языка являются все терминальные цепочки,
которые можно получить из P, конечное число раз применяя
правила вывода.
Н.Хомский (N.Chomsky) в 1957 г. предложил классификацию
формальных грамматик: класс 0 – любая грамматика, классы 1, 2 и
3 последовательно сужаются. Практически важны:





3

класс 2: контекстно-свободные (КС) грамматики;
класс 3: автоматные (регулярные) грамматики.

Существуют языки, которые не могут быть описаны грамматикой
определенного класса, требуя использования более общего класса.
Например:




"abcabcabc…abc" (N раз) – допускает автоматную грамматику;
"aaaa…ab…bbbb" (по N раз каждая буква) – требует КС-грамматики;
"aa…ab…bc…cc" (по N раз каждая) – требует грамматики класса 1.
4

Нотация Бэкуса-Наура (расширенная)


Классическая БН-нотация: правила вывода записываются в форме:




Нетерминальный алфавит N состоит из слов-понятий в угловых
скобках, а также символов "::=" и "|".





<нетерминал> ::= цепочка1 | цепочка2 | … цепочкаm

"::=" – то же самое, что "→";
"|" означает выбор одной из нескольких альтернатив, т.е. позволяет
объединить запись нескольких правил с одинаковой левой частью.

Современный расширенный вариант БН-нотации вводит
дополнительные обозначения, позволяющие в некоторых случаях
резко сократить количество и длину правил:



[ … ] – обозначает необязательные подцепочки в правой части правила;
{ … } – обозначает повторение подцепочки 0 или более раз.
5

Примеры задания языков в БН-нотации


<СЛОВО> ::= a<СЛОВО>b | ε




<СЛОВО> ::= <СЛОВО_A> <СЛОВО_B>
<СЛОВО_A> ::= a <СЛОВО_A> | ε
<СЛОВО_B> ::= b <СЛОВО_b> | ε




Задает язык вида "aaa…ab…bbb" (N раз буква 'a', M раз буква 'b', N,M ≥ 0).

<ИМЯ> ::= <БУКВА> {<БУКВА> | <ЦИФРА>}
<БУКВА> ::= a | b | с | d | e | f | … | x | y | z
<ЦИФРА> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9




Задает язык L, состоящий из всех цепочек вида "aaa…ab…bbb" (N раз
каждая буква, N ≥ 0).

Задает понятно что.

<ОПЕРАТОР_IF> ::= if <УСЛОВИЕ> then <ОПЕРАТОР> [else
<ОПЕРАТОР>]
(плюс правила для <УСЛОВИЕ> и <ОПЕРАТОР> )


Задает понятно что.
Диаграммы Вирта




Представляют собой наглядную графическую форму,
эквивалентную БН-нотации.
Примеры:

6
Структура транслятора









7

Парсер выполняет синтаксический разбор, т.е. ищет путь вывода от
главного нетерминала <ПРОГРАММА> до конкретного текста
программы. Распознав очередную конструкцию, вызывает
Генератор для создания выходного кода.
Сканер (необязательная часть) упрощает работу Парсера, разбивая
входной текст на лексемы – ключевые слова, имена, числа,
операции, разделители.
Генератор либо формирует двоичный код (компиляция), либо сразу
выполняет команды программы (интерпретация).
Каждый из трех в пределах своей компетенции обнаруживает
ошибки.
8

Синтаксис, лексика и семантика








Синтаксис языка – грамматическая структура предложения,
распознаваемая программой синтаксического анализа (парсером)
на основании сопоставления текста с формальной грамматикой
языка.
Лексика языка – часть синтаксиса, связанная с разбивкой исходного
текста на неделимые языковые единицы-лексемы: ключевые слова,
имена переменных, числовые константы, разделители, знаки
операций (в том числе составные, как "<=" или ":="). Лексический
анализ выполняется на основе регулярной грамматики лексем,
облегчая работу парсера.
Семантика языка – содержательный смысл синтаксических
конструкций: выполнение операторов, распределение памяти,
ведение таблицы имен переменных.
Практически на семантику сваливают также все ограничения языка,
которые трудно или невозможно описать на языке формальных
грамматик. Например, использование неописанных переменных
считается семантической ошибкой.
9

Методы разбора для КС-грамматик






Существует алгоритм разбора для КС-грамматики общего вида,
требующий небольшого предварительного «причесывания»
грамматики. Общая идея алгоритма: для каждого нетерминала
последовательно выясняется, какие подстроки разбираемой строки
могут быть из него выведены, а какие нет. (См. пример.)
Трудоемкость: T(N) = O(Nk), где k зависит от максимального числа
нетерминалов в правой части правила вывода. Это может быть
приемлемо для некоторых олимпиадных задач, но слишком
медленно для трансляции реальных языков программирования, где
N (длина программы) может быть большим. Требуются алгоритмы с
линейной оценкой O(N).
Линейные оценки могут быть получены для некоторых подклассов
КС-грамматик, таких как LL(k)-грамматики и LR(k)-грамматики.
Практически любой язык программирования может быть описан
грамматикой одного из этих классов, особенно если об этом
подумать заранее, при проектировании языка.
Задача «Словарь»

10

Гран-при Украины, сентябрь’2007



Дано:









алфавит: Q нетерминалов {A1, A2, … AQ}, R терминалов {t1, t2, … tR};
K правил вывода, двух разных типов:
 Ai → Aj Ak
(нетерминал → два нетерминала);
 Ai → tj
(нетерминал → терминал);
начальный символ A1;
цепочка X = "x1 x2 … xN".

Найти: выводима ли цепочка X в данной грамматике.
Ограничения: R ≤ 26, Q ≤ 26, K ≤ 20, N ≤ 50.
Решение задачи














11

Несмотря на простоту структуры грамматики, она может быть
достаточно неприятной, поскольку не относится к «хорошим»
классам LL(1), LR(0) и др. Грамматика допускает как левую, так и
правую рекурсию.
Применим общий алгоритм КС-разбора, но приспособленный к
данному типу грамматики.
Пусть F(k, i, r) = 1, если из нетерминала Ak выводима подцепочка
X[i, i+r–1].
Для подцепочек длины 1 имеем: F(k, i, 1) = 1 тогда и только тогда,
когда имеется правило: Ak → X[i].
Для подцепочек длины > 1: F(k, i, 1) = 1 тогда и только тогда, когда
существует такое правило Ak → Ap Aq и существует разбиение
подстроки X[i, i+r–1] = X[i, i+a–1] || X[i+a, i+r– 1] такие, что
Ap → X[i, i+a–1] и Aq → X[i, i+r–1].
Требуется найти F(1, 1, N).
Для расчета можно использовать рекурсию с запоминанием либо
рекуррентное заполнение таблицы, начиная с подцепочек длины 1.
Решение задачи (2)
var
ArrF: array[1..Q, 1..N, 1..N] of -1..1;
function F(k, i, r): Boolean;
var
r1, k1, k2: Integer;
begin
if ArrF[k, i, r] = -1 then begin {Рассчитать значение}
F := False;
for r1 := 1 to r-1 do begin
for k1 := 1 to Q do begin
if F(k1, i, r1) = 1 then
for k2 := 1 to Q do
if F(k2, i+r1, r-r1) = 1 then begin
ArrF[k, i, r] := 1;
F := True;
Exit;
end;
end;
end;
ArrF[k, i, r] := 0;
F := False;
end
else {Значение уже рассчитано}
F := (ArrF[k, i, r] = 1);
end;

12
Решение задачи (3)

13

В основной программе:
Инициализировать массив ArrF[k, i, r] := -1; {Не рассчитано}
Для всех правил вида A[k] -> X[i] присвоить ArrF[k, i, 1] := 1;
Answer := F(A[1], 1, N);


Трудоемкость: T(N, Q) = O(Q2N3)

ВНИМАНИЕ: алгоритм в бою не проверен!
LL(1)-грамматики



Смысл обозначения:












правые части всех правил начинаются с терминального символа;
для одного и того же нетерминала все правые части начинаются с разных
терминалов.

В случае LL(1)-грамматики общего вида для каждого правила с
нетерминалом A в левой части определяется множество выбора, которое
включает в себя:




входная цепочка просматривается слева (L) направо;
правые части грамматических правил тоже просматриваются слева (L) направо;
решение о применимости правила принимается по одной (1) текущей букве
входной цепочки.

Самый простой и понятный подкласс LL(1)-грамматик – S-грамматики,
которые удовлетворяют таким условиям:




14

все терминалы, которые могут быть первыми буквами цепочек, выводимых из A;
если правило имеет вид A →Bα и из B выводима пустая цепочка, то все
терминалы, которые могут быть первыми буквами цепочек, выводимых из α.

Грамматика относится к классу LL(1), если разные правила для одного и
того же нетерминала имеют непересекающиеся множества выбора.
Для грамматик LL(1) работает алгоритм рекурсивного спуска, который
выполняет построение вывода от начального символа до терминальной
цепочки, просматривая эту цепочку один раз слева направо.
15

Алгоритм рекурсивного спуска
для LL(1)-грамматики










Для каждого нетерминала пишется процедура, которая по
очередной входной букве выбирает правую часть правила,
проверяет соответствие входных терминалов правилу и вызывает
(возможно, рекурсивно) процедуры для нетерминалов в правой
части правила.
Весь разбор инициализируется вызовом процедуры для главного
нетерминала.
Написание процедур выполняется достаточно формальным
образом на основании формы Бэкуса-Наура или (еще лучше)
диаграмм Вирта.
Если требуется семантическая обработка (например, подсчет
выражения или генерация кода), то соответствующие операторы
или вызова семантических процедур вставляются (к сожалению,
неформально) в подходящие места процедур разбора
нетерминалов.
Трудоемкость разбора: T(N) = O(N).
16

Задача «Химические реакции»


Дано: правила записи химических реакций:












<уравнение> ::= <формула> "=" <формула>
<формула> ::= [<число>]<молекула> {"+" [<число>] <молекула>}
<молекула> ::= <элемент>[<число>]{<элемент>[<число>]}
<элемент> ::= <прописная>[<строчная>]
<прописная> ::= "A" … "Z"
<строчная> ::= "a" … "z"
<число> ::= "2"…"9"

Требуется: определить правильность записи реакции,
включая проверку равенства числа атомов справа и
слева.
Примеры:




H2SO4 + 2NaOH = Na2SO4 + 2H2O
– правильно
H2SO4 + 2NaOH = Na2SO4 – 2H2O
– нарушение синтаксиса
2PbO + 3Xx = 5Au
– алхимическое превращение,
семантическая ошибка
Решение задачи


Для каждого нетерминала определим множество First – множество
допустимых начальных букв терминального алфавита.














17

First(число) = "23456789";
First(строчная) = "abcde…xyz";
First(прописная) = "ABCDE…XYZ";
First(элемент) = First(молекула) = "ABCDE…XYZ";
First(формула) = First(уравнение) = "23456789ABCDE…XYZ";

Глобальная переменная curChar – текущая буква входного текста.
Глобальная переменная curNumb – текущее число, найденное в тексте.
Процедура GetNext: читает следующую букву в переменную curChar.
Процедура Check("символы") – проверяет принадлежность curChar к
заданному множеству букв, допустимых в данной позиции. В противном
случае прекращает разбор и выдает ответ "нет".
Для каждого нетерминала пишется процедура разбора. Предполагается,
что в момент вызова curChar = первой букве разбираемого нетерминала, а
после выхода из процедуры curChar = первой букве, идущей после
распознанного нетерминала.
Синим шрифтом в процедурах выделены семантические действия (все
остальное – чистый синтаксис).
Решение задачи (2)
procedure PEquation;
begin
Начать счет атомов слева;
Check(First(уравнение));
PFormula;
Check("=");
GetNext;
Начать счет атомов справа;
PFormula;
Сравнить число атомов справа и
слева, выдать "да" или "нет".;
end;
procedure PFormula;
begin
Check(First(формула));
repeat
numMolec := 1;
if curChar ∈ First(число)
then begin
PNumber;
numMolec := curNumb;
end;
PMolecula;
flag := (curChar = "+" );
if flag then
GetNext;

18

until not Flag;
end;
procedure PMolecula;
begin
Check(First(молекула));
repeat
PElement;
numElem := 1;
if curChar ∈ First(число) then
begin
PNumber;
numElem := curNumb;
end;
Увеличить число атомов данного
элемента на numMolec * numElem;
until curChar ∉ First(элемент);
end;
procedure PElement;
begin
Check(First(элемент));
PUpCase;
if curChar ∈ First(строчная) then
PLoCase;
Найти или добавить в таблицу имя
текущего элемента;
end;
Решение задачи (3)
procedure PUpCase;
begin
Check(First(прописная));
Запомнить первую букву имени;
GetNext;
end;
procedure PLoCase;
begin
Check(First(строчная));
Запомнить вторую букву имени;
GetNext;
end;
procedure PNumber;
begin
Check(First(число));
curNumb := значение цифры;
GetNext;
end;

В основной программе:
GetNext;
PEquation;

19
Обратная польская запись






Это постфиксная форма записи выражений, т.е. знак операции идет
после операндов.
Не содержит скобок и не использует старшинство операций.
Инфиксная форма: (a + b) * (x – 20*(a – 5) / Sin(d – 10 * a)) - 1
ОПЗ: a, b, +, x, 20, a, 5, –, *, d, 10, a, *, –, Sin, /, –, *, 1, –
Вычисление выражения по ОПЗ:








20

если очередное слово – операнд (константа или переменная), то
значение операнда помещается в стек;
если оператор, то из стека выбирается нужное число операндов (обычно
два или один), выполняется операция и результат кладется в стек.

Порядок следования операндов сохраняется при переходе от
инфиксной формы к ОПЗ, надо только в правильных местах
расставить знаки операций.
Используется как "полуфабрикат" при переходе от исходной
обычной (инфиксной) формы записи выражений к набору команд
для вычисления выражения. ОПЗ переводится в машинные
команды практически "один к одному".





Алгоритм служит для трансляции арифметических и других выражений в
ОПЗ.
Используется три массива данных: входная строка (инфиксная запись
выражения), выходная строка (запись в ОПЗ) и стек, используемый для
«перестроения» операций.
Операнды (переменные и константы) проходят напрямую из входа на
выход.
Для знаков операций и скобок обработка определяется рядом правил и
таблицей приоритетов.

Если стек пуст, операция
вход
выход
записывается в стек.

Опеpация выталкивает из стека все
опеpации с большим или pавным
пpиоpитетом, после чего
записывается в стек.

Открывающая скобка всегда
записывается в стек.

Закрывающая скобка выталкивает из
стека все до открывающей скобки и
операция
приоритет
«взаимно уничтожается» с ней.
(
0

Конец входной строки выталкивает
)
1
все операции из стека.
стек



Алгоритм Дейкстры

21

+, –
*, /
^

2
3
4
22

Пример на алгоритм Дейкстры
Вход

(

A +

B

/

C

(

(

+
(

/
+
(

/
+
(

+
(

Стек

Выход





A

B

)

C /+

*

(

(

D –

1

)

^

2

+

E

)

*

(
*

(
(
*

(
(
*

–
(
(
*

(
*

^
(
*

^
(
*

+
(
*

+
(
*

*

1

–

2

^

E

+

D

–
(
(
*

Путем некоторого усложнения системы правил и приоритетов
можно добавить обработку присваивания, обращения к элементам
массива, вызова функций с параметрами, даже IF-THEN-ELSE.
Трудоемкость: T(N) = O(N)

*

More Related Content

What's hot

03 классическая логика высказываний
03 классическая логика высказываний03 классическая логика высказываний
03 классическая логика высказыванийJulia Gorbatova
 
04 классическая логика предикатов
04 классическая логика предикатов04 классическая логика предикатов
04 классическая логика предикатовJulia Gorbatova
 
Ruby: работа с массивами
Ruby: работа с массивамиRuby: работа с массивами
Ruby: работа с массивамиEvgeny Smirnov
 
элементы языка и типы данных
элементы языка и типы данныхэлементы языка и типы данных
элементы языка и типы данныхЕлена Ключева
 
20100923 proof complexity_hirsch_lecture02
20100923 proof complexity_hirsch_lecture0220100923 proof complexity_hirsch_lecture02
20100923 proof complexity_hirsch_lecture02Computer Science Club
 
язык работы с КМАС (Yafoll сообщение 1)
язык работы с КМАС (Yafoll сообщение 1)язык работы с КМАС (Yafoll сообщение 1)
язык работы с КМАС (Yafoll сообщение 1)Alex Shkotin
 
20111015 inroduction to_combinatorics_on_words_frid_lecture01
20111015 inroduction to_combinatorics_on_words_frid_lecture0120111015 inroduction to_combinatorics_on_words_frid_lecture01
20111015 inroduction to_combinatorics_on_words_frid_lecture01Computer Science Club
 
20081012 structuralcomplexitytheory lecture03-04
20081012 structuralcomplexitytheory lecture03-0420081012 structuralcomplexitytheory lecture03-04
20081012 structuralcomplexitytheory lecture03-04Computer Science Club
 

What's hot (17)

лек7
лек7лек7
лек7
 
лек8
лек8лек8
лек8
 
03 классическая логика высказываний
03 классическая логика высказываний03 классическая логика высказываний
03 классическая логика высказываний
 
04 классическая логика предикатов
04 классическая логика предикатов04 классическая логика предикатов
04 классическая логика предикатов
 
Ruby строки
Ruby строкиRuby строки
Ruby строки
 
Java - основы языка
Java - основы языкаJava - основы языка
Java - основы языка
 
Алгоритмы сжатия данных
Алгоритмы сжатия данныхАлгоритмы сжатия данных
Алгоритмы сжатия данных
 
Модули автоматической обработки текстов в проекте aot.ru
Модули автоматической обработки текстов в проекте aot.ruМодули автоматической обработки текстов в проекте aot.ru
Модули автоматической обработки текстов в проекте aot.ru
 
Ruby: работа с массивами
Ruby: работа с массивамиRuby: работа с массивами
Ruby: работа с массивами
 
Logacheva
LogachevaLogacheva
Logacheva
 
элементы языка и типы данных
элементы языка и типы данныхэлементы языка и типы данных
элементы языка и типы данных
 
Slides2
Slides2Slides2
Slides2
 
20100923 proof complexity_hirsch_lecture02
20100923 proof complexity_hirsch_lecture0220100923 proof complexity_hirsch_lecture02
20100923 proof complexity_hirsch_lecture02
 
язык работы с КМАС (Yafoll сообщение 1)
язык работы с КМАС (Yafoll сообщение 1)язык работы с КМАС (Yafoll сообщение 1)
язык работы с КМАС (Yafoll сообщение 1)
 
20111015 inroduction to_combinatorics_on_words_frid_lecture01
20111015 inroduction to_combinatorics_on_words_frid_lecture0120111015 inroduction to_combinatorics_on_words_frid_lecture01
20111015 inroduction to_combinatorics_on_words_frid_lecture01
 
20081012 structuralcomplexitytheory lecture03-04
20081012 structuralcomplexitytheory lecture03-0420081012 structuralcomplexitytheory lecture03-04
20081012 structuralcomplexitytheory lecture03-04
 
лекция 3
лекция 3лекция 3
лекция 3
 

Similar to формальные языки и грамматики

2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...
2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...
2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...Омские ИТ-субботники
 
лекция 6 тема 1
лекция 6 тема 1лекция 6 тема 1
лекция 6 тема 1Noobie312
 
10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскальAnna_Malina
 
10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскальAnna_Malina
 
Semantic feature machine translation system
Semantic feature machine translation systemSemantic feature machine translation system
Semantic feature machine translation systemDmitry Kan
 

Similar to формальные языки и грамматики (9)

8 2-2
8 2-28 2-2
8 2-2
 
2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...
2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...
2017-02-04 01 Евгений Тюменцев. Выразительные возможности языков программиро...
 
лекция 6 тема 1
лекция 6 тема 1лекция 6 тема 1
лекция 6 тема 1
 
Тодуа. Методы разработки интерпретатора языка Рефал-2
Тодуа. Методы разработки интерпретатора языка Рефал-2Тодуа. Методы разработки интерпретатора языка Рефал-2
Тодуа. Методы разработки интерпретатора языка Рефал-2
 
Методы морфологического анализа текстов
Методы морфологического анализа текстовМетоды морфологического анализа текстов
Методы морфологического анализа текстов
 
10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль
 
10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль10кл общие сведения о языке программирования паскаль
10кл общие сведения о языке программирования паскаль
 
Semantic feature machine translation system
Semantic feature machine translation systemSemantic feature machine translation system
Semantic feature machine translation system
 
функции
функциифункции
функции
 

More from Екатерина Редько

More from Екатерина Редько (7)

Зорина "Исследования по истории ВОВ" - 1 место
Зорина "Исследования по истории ВОВ" - 1 местоЗорина "Исследования по истории ВОВ" - 1 место
Зорина "Исследования по истории ВОВ" - 1 место
 
Богданова, "Города герои"
Богданова, "Города герои"Богданова, "Города герои"
Богданова, "Города герои"
 
Аннина, "Дети-герои"
Аннина, "Дети-герои"Аннина, "Дети-герои"
Аннина, "Дети-герои"
 
Евсеева "Символы победы"
Евсеева "Символы победы"Евсеева "Символы победы"
Евсеева "Символы победы"
 
Ефимова, Буйнова, "9мая день победы !!!"
Ефимова, Буйнова,  "9мая день победы !!!"Ефимова, Буйнова,  "9мая день победы !!!"
Ефимова, Буйнова, "9мая день победы !!!"
 
публикация презентаций On line
публикация презентаций On lineпубликация презентаций On line
публикация презентаций On line
 
проба пера
проба перапроба пера
проба пера
 

формальные языки и грамматики

  • 1. Формальные языки и грамматики План лекции:      Формальные языки и формальные грамматики Способы задания формальных грамматик Общий алгоритм КС-разбора (на примере) LL(1)-грамматики и алгоритм рекурсивного спуска Обратная польская запись и алгоритм Дейкстры автор: С.Н. Дроздов
  • 2. Формальные языки  Дан конечный алфавит T (терминальный алфавит).      Примеры: T = {a, b, c, … , z}; T = {0, 1}; T = {a} Цепочка (строка) – любая конечная последовательность букв из T (возможно, нулевой длины).   2 Примеры: "a", "aacb", "babababababababa", "0000011111", ε (пустая цепочка). Множество всех цепочек над T обозначим T*. Язык над алфавитом T – любое подмножество цепочек L ⊆ T*. Все цепочки α ∈ L называются предложениями этого языка. Способы задания языка:    перечисление всех предложений (пример: язык Эллочки-людоедки); любой алгоритм, принимающий на входе цепочку над T и возвращающий "да"/"нет" (распознаватель языка); формальная грамматика.
  • 3. Формальные грамматики  Формальная грамматика определяется следующей четверкой:       терминальный алфавит T; нетерминальный алфавит N (алфавит для записи правил); правила вывода цепочек: αk → βk; αk, βk – цепочки над T ∪ N; начальный символ (главный нетерминальный символ) P. Предложениями языка являются все терминальные цепочки, которые можно получить из P, конечное число раз применяя правила вывода. Н.Хомский (N.Chomsky) в 1957 г. предложил классификацию формальных грамматик: класс 0 – любая грамматика, классы 1, 2 и 3 последовательно сужаются. Практически важны:    3 класс 2: контекстно-свободные (КС) грамматики; класс 3: автоматные (регулярные) грамматики. Существуют языки, которые не могут быть описаны грамматикой определенного класса, требуя использования более общего класса. Например:    "abcabcabc…abc" (N раз) – допускает автоматную грамматику; "aaaa…ab…bbbb" (по N раз каждая буква) – требует КС-грамматики; "aa…ab…bc…cc" (по N раз каждая) – требует грамматики класса 1.
  • 4. 4 Нотация Бэкуса-Наура (расширенная)  Классическая БН-нотация: правила вывода записываются в форме:   Нетерминальный алфавит N состоит из слов-понятий в угловых скобках, а также символов "::=" и "|".    <нетерминал> ::= цепочка1 | цепочка2 | … цепочкаm "::=" – то же самое, что "→"; "|" означает выбор одной из нескольких альтернатив, т.е. позволяет объединить запись нескольких правил с одинаковой левой частью. Современный расширенный вариант БН-нотации вводит дополнительные обозначения, позволяющие в некоторых случаях резко сократить количество и длину правил:   [ … ] – обозначает необязательные подцепочки в правой части правила; { … } – обозначает повторение подцепочки 0 или более раз.
  • 5. 5 Примеры задания языков в БН-нотации  <СЛОВО> ::= a<СЛОВО>b | ε   <СЛОВО> ::= <СЛОВО_A> <СЛОВО_B> <СЛОВО_A> ::= a <СЛОВО_A> | ε <СЛОВО_B> ::= b <СЛОВО_b> | ε   Задает язык вида "aaa…ab…bbb" (N раз буква 'a', M раз буква 'b', N,M ≥ 0). <ИМЯ> ::= <БУКВА> {<БУКВА> | <ЦИФРА>} <БУКВА> ::= a | b | с | d | e | f | … | x | y | z <ЦИФРА> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9   Задает язык L, состоящий из всех цепочек вида "aaa…ab…bbb" (N раз каждая буква, N ≥ 0). Задает понятно что. <ОПЕРАТОР_IF> ::= if <УСЛОВИЕ> then <ОПЕРАТОР> [else <ОПЕРАТОР>] (плюс правила для <УСЛОВИЕ> и <ОПЕРАТОР> )  Задает понятно что.
  • 6. Диаграммы Вирта   Представляют собой наглядную графическую форму, эквивалентную БН-нотации. Примеры: 6
  • 7. Структура транслятора     7 Парсер выполняет синтаксический разбор, т.е. ищет путь вывода от главного нетерминала <ПРОГРАММА> до конкретного текста программы. Распознав очередную конструкцию, вызывает Генератор для создания выходного кода. Сканер (необязательная часть) упрощает работу Парсера, разбивая входной текст на лексемы – ключевые слова, имена, числа, операции, разделители. Генератор либо формирует двоичный код (компиляция), либо сразу выполняет команды программы (интерпретация). Каждый из трех в пределах своей компетенции обнаруживает ошибки.
  • 8. 8 Синтаксис, лексика и семантика     Синтаксис языка – грамматическая структура предложения, распознаваемая программой синтаксического анализа (парсером) на основании сопоставления текста с формальной грамматикой языка. Лексика языка – часть синтаксиса, связанная с разбивкой исходного текста на неделимые языковые единицы-лексемы: ключевые слова, имена переменных, числовые константы, разделители, знаки операций (в том числе составные, как "<=" или ":="). Лексический анализ выполняется на основе регулярной грамматики лексем, облегчая работу парсера. Семантика языка – содержательный смысл синтаксических конструкций: выполнение операторов, распределение памяти, ведение таблицы имен переменных. Практически на семантику сваливают также все ограничения языка, которые трудно или невозможно описать на языке формальных грамматик. Например, использование неописанных переменных считается семантической ошибкой.
  • 9. 9 Методы разбора для КС-грамматик    Существует алгоритм разбора для КС-грамматики общего вида, требующий небольшого предварительного «причесывания» грамматики. Общая идея алгоритма: для каждого нетерминала последовательно выясняется, какие подстроки разбираемой строки могут быть из него выведены, а какие нет. (См. пример.) Трудоемкость: T(N) = O(Nk), где k зависит от максимального числа нетерминалов в правой части правила вывода. Это может быть приемлемо для некоторых олимпиадных задач, но слишком медленно для трансляции реальных языков программирования, где N (длина программы) может быть большим. Требуются алгоритмы с линейной оценкой O(N). Линейные оценки могут быть получены для некоторых подклассов КС-грамматик, таких как LL(k)-грамматики и LR(k)-грамматики. Практически любой язык программирования может быть описан грамматикой одного из этих классов, особенно если об этом подумать заранее, при проектировании языка.
  • 10. Задача «Словарь» 10 Гран-при Украины, сентябрь’2007  Дано:       алфавит: Q нетерминалов {A1, A2, … AQ}, R терминалов {t1, t2, … tR}; K правил вывода, двух разных типов:  Ai → Aj Ak (нетерминал → два нетерминала);  Ai → tj (нетерминал → терминал); начальный символ A1; цепочка X = "x1 x2 … xN". Найти: выводима ли цепочка X в данной грамматике. Ограничения: R ≤ 26, Q ≤ 26, K ≤ 20, N ≤ 50.
  • 11. Решение задачи        11 Несмотря на простоту структуры грамматики, она может быть достаточно неприятной, поскольку не относится к «хорошим» классам LL(1), LR(0) и др. Грамматика допускает как левую, так и правую рекурсию. Применим общий алгоритм КС-разбора, но приспособленный к данному типу грамматики. Пусть F(k, i, r) = 1, если из нетерминала Ak выводима подцепочка X[i, i+r–1]. Для подцепочек длины 1 имеем: F(k, i, 1) = 1 тогда и только тогда, когда имеется правило: Ak → X[i]. Для подцепочек длины > 1: F(k, i, 1) = 1 тогда и только тогда, когда существует такое правило Ak → Ap Aq и существует разбиение подстроки X[i, i+r–1] = X[i, i+a–1] || X[i+a, i+r– 1] такие, что Ap → X[i, i+a–1] и Aq → X[i, i+r–1]. Требуется найти F(1, 1, N). Для расчета можно использовать рекурсию с запоминанием либо рекуррентное заполнение таблицы, начиная с подцепочек длины 1.
  • 12. Решение задачи (2) var ArrF: array[1..Q, 1..N, 1..N] of -1..1; function F(k, i, r): Boolean; var r1, k1, k2: Integer; begin if ArrF[k, i, r] = -1 then begin {Рассчитать значение} F := False; for r1 := 1 to r-1 do begin for k1 := 1 to Q do begin if F(k1, i, r1) = 1 then for k2 := 1 to Q do if F(k2, i+r1, r-r1) = 1 then begin ArrF[k, i, r] := 1; F := True; Exit; end; end; end; ArrF[k, i, r] := 0; F := False; end else {Значение уже рассчитано} F := (ArrF[k, i, r] = 1); end; 12
  • 13. Решение задачи (3) 13 В основной программе: Инициализировать массив ArrF[k, i, r] := -1; {Не рассчитано} Для всех правил вида A[k] -> X[i] присвоить ArrF[k, i, 1] := 1; Answer := F(A[1], 1, N);  Трудоемкость: T(N, Q) = O(Q2N3) ВНИМАНИЕ: алгоритм в бою не проверен!
  • 14. LL(1)-грамматики  Смысл обозначения:        правые части всех правил начинаются с терминального символа; для одного и того же нетерминала все правые части начинаются с разных терминалов. В случае LL(1)-грамматики общего вида для каждого правила с нетерминалом A в левой части определяется множество выбора, которое включает в себя:   входная цепочка просматривается слева (L) направо; правые части грамматических правил тоже просматриваются слева (L) направо; решение о применимости правила принимается по одной (1) текущей букве входной цепочки. Самый простой и понятный подкласс LL(1)-грамматик – S-грамматики, которые удовлетворяют таким условиям:   14 все терминалы, которые могут быть первыми буквами цепочек, выводимых из A; если правило имеет вид A →Bα и из B выводима пустая цепочка, то все терминалы, которые могут быть первыми буквами цепочек, выводимых из α. Грамматика относится к классу LL(1), если разные правила для одного и того же нетерминала имеют непересекающиеся множества выбора. Для грамматик LL(1) работает алгоритм рекурсивного спуска, который выполняет построение вывода от начального символа до терминальной цепочки, просматривая эту цепочку один раз слева направо.
  • 15. 15 Алгоритм рекурсивного спуска для LL(1)-грамматики      Для каждого нетерминала пишется процедура, которая по очередной входной букве выбирает правую часть правила, проверяет соответствие входных терминалов правилу и вызывает (возможно, рекурсивно) процедуры для нетерминалов в правой части правила. Весь разбор инициализируется вызовом процедуры для главного нетерминала. Написание процедур выполняется достаточно формальным образом на основании формы Бэкуса-Наура или (еще лучше) диаграмм Вирта. Если требуется семантическая обработка (например, подсчет выражения или генерация кода), то соответствующие операторы или вызова семантических процедур вставляются (к сожалению, неформально) в подходящие места процедур разбора нетерминалов. Трудоемкость разбора: T(N) = O(N).
  • 16. 16 Задача «Химические реакции»  Дано: правила записи химических реакций:          <уравнение> ::= <формула> "=" <формула> <формула> ::= [<число>]<молекула> {"+" [<число>] <молекула>} <молекула> ::= <элемент>[<число>]{<элемент>[<число>]} <элемент> ::= <прописная>[<строчная>] <прописная> ::= "A" … "Z" <строчная> ::= "a" … "z" <число> ::= "2"…"9" Требуется: определить правильность записи реакции, включая проверку равенства числа атомов справа и слева. Примеры:    H2SO4 + 2NaOH = Na2SO4 + 2H2O – правильно H2SO4 + 2NaOH = Na2SO4 – 2H2O – нарушение синтаксиса 2PbO + 3Xx = 5Au – алхимическое превращение, семантическая ошибка
  • 17. Решение задачи  Для каждого нетерминала определим множество First – множество допустимых начальных букв терминального алфавита.           17 First(число) = "23456789"; First(строчная) = "abcde…xyz"; First(прописная) = "ABCDE…XYZ"; First(элемент) = First(молекула) = "ABCDE…XYZ"; First(формула) = First(уравнение) = "23456789ABCDE…XYZ"; Глобальная переменная curChar – текущая буква входного текста. Глобальная переменная curNumb – текущее число, найденное в тексте. Процедура GetNext: читает следующую букву в переменную curChar. Процедура Check("символы") – проверяет принадлежность curChar к заданному множеству букв, допустимых в данной позиции. В противном случае прекращает разбор и выдает ответ "нет". Для каждого нетерминала пишется процедура разбора. Предполагается, что в момент вызова curChar = первой букве разбираемого нетерминала, а после выхода из процедуры curChar = первой букве, идущей после распознанного нетерминала. Синим шрифтом в процедурах выделены семантические действия (все остальное – чистый синтаксис).
  • 18. Решение задачи (2) procedure PEquation; begin Начать счет атомов слева; Check(First(уравнение)); PFormula; Check("="); GetNext; Начать счет атомов справа; PFormula; Сравнить число атомов справа и слева, выдать "да" или "нет".; end; procedure PFormula; begin Check(First(формула)); repeat numMolec := 1; if curChar ∈ First(число) then begin PNumber; numMolec := curNumb; end; PMolecula; flag := (curChar = "+" ); if flag then GetNext; 18 until not Flag; end; procedure PMolecula; begin Check(First(молекула)); repeat PElement; numElem := 1; if curChar ∈ First(число) then begin PNumber; numElem := curNumb; end; Увеличить число атомов данного элемента на numMolec * numElem; until curChar ∉ First(элемент); end; procedure PElement; begin Check(First(элемент)); PUpCase; if curChar ∈ First(строчная) then PLoCase; Найти или добавить в таблицу имя текущего элемента; end;
  • 19. Решение задачи (3) procedure PUpCase; begin Check(First(прописная)); Запомнить первую букву имени; GetNext; end; procedure PLoCase; begin Check(First(строчная)); Запомнить вторую букву имени; GetNext; end; procedure PNumber; begin Check(First(число)); curNumb := значение цифры; GetNext; end; В основной программе: GetNext; PEquation; 19
  • 20. Обратная польская запись    Это постфиксная форма записи выражений, т.е. знак операции идет после операндов. Не содержит скобок и не использует старшинство операций. Инфиксная форма: (a + b) * (x – 20*(a – 5) / Sin(d – 10 * a)) - 1 ОПЗ: a, b, +, x, 20, a, 5, –, *, d, 10, a, *, –, Sin, /, –, *, 1, – Вычисление выражения по ОПЗ:     20 если очередное слово – операнд (константа или переменная), то значение операнда помещается в стек; если оператор, то из стека выбирается нужное число операндов (обычно два или один), выполняется операция и результат кладется в стек. Порядок следования операндов сохраняется при переходе от инфиксной формы к ОПЗ, надо только в правильных местах расставить знаки операций. Используется как "полуфабрикат" при переходе от исходной обычной (инфиксной) формы записи выражений к набору команд для вычисления выражения. ОПЗ переводится в машинные команды практически "один к одному".
  • 21.    Алгоритм служит для трансляции арифметических и других выражений в ОПЗ. Используется три массива данных: входная строка (инфиксная запись выражения), выходная строка (запись в ОПЗ) и стек, используемый для «перестроения» операций. Операнды (переменные и константы) проходят напрямую из входа на выход. Для знаков операций и скобок обработка определяется рядом правил и таблицей приоритетов.  Если стек пуст, операция вход выход записывается в стек.  Опеpация выталкивает из стека все опеpации с большим или pавным пpиоpитетом, после чего записывается в стек.  Открывающая скобка всегда записывается в стек.  Закрывающая скобка выталкивает из стека все до открывающей скобки и операция приоритет «взаимно уничтожается» с ней. ( 0  Конец входной строки выталкивает ) 1 все операции из стека. стек  Алгоритм Дейкстры 21 +, – *, / ^ 2 3 4
  • 22. 22 Пример на алгоритм Дейкстры Вход ( A + B / C ( ( + ( / + ( / + ( + ( Стек Выход   A B ) C /+ * ( ( D – 1 ) ^ 2 + E ) * ( * ( ( * ( ( * – ( ( * ( * ^ ( * ^ ( * + ( * + ( * * 1 – 2 ^ E + D – ( ( * Путем некоторого усложнения системы правил и приоритетов можно добавить обработку присваивания, обращения к элементам массива, вызова функций с параметрами, даже IF-THEN-ELSE. Трудоемкость: T(N) = O(N) *