- •Введение
- •Формальные грамматики. Выводы Основные понятия
- •Способы задания языков
- •Операции над языками
- •Формальные грамматики. Выводы
- •Преобразования кс-грамматик Основные понятия
- •Устранение лишних символов
- •Исключение лишних правил
- •Исключение -правил
- •Устранение цепных правил
- •Выделение нетерминала
- •Левая факторизация
- •Устранение левой рекурсии
- •Устранение правой рекурсии
- •Нормальная форма Хомского
- •Нормальная форма Грейбах
- •Операторная кс-грамматика
- •Регулярные языки и конечные распознаватели Правосторонние грамматики
- •Левосторонние грамматики
- •Связь правосторонних и левосторонних грамматик
- •Преобразование кс-грамматики произвольного вида в правостороннюю
- •Конечные распознаватели. Основные понятия
- •Преобразование недетерминированных конечных распознавателей с -переходами в детерминированные
- •Эквивалентность состояний конечных детерминированных распознавателей
- •Эквивалентность конечных детерминированных распознавателей
- •Минимизация конечных детерминированных распознавателей
- •Регулярные множества и регулярные выражения
- •Регулярные выражения и конечные распознаватели
- •Получение регулярного выражения из конечного распознавателя
- •Построение конечного распознавателя по регулярному выражению
- •Конечные распознаватели и регулярные грамматики
- •Получение грамматики из конечного распознавателя
- •Построение конечных распознавателей по регулярным грамматикам
- •Ещё один способ взаимного преобразования право- и левосторонних грамматик
- •Программная реализация конечных детерминированных распознавателей
- •Лексический анализ
- •Принципы построения лексических анализаторов
- •Определение границ лексем
- •Выполнение действий, связанных с лексемами
- •Выделение ключевых слов
- •Программная реализация лексических анализаторов
- •Глава 3. Синтаксический анализ
- •3.1. Автомат с магазинной памятью
- •3.2. Таблично-управляемый предсказывающий разбор
- •3.2.1. Алгоритм разбора сверху-вниз
- •3.2.2. Множества first и follow
- •3.2.3. Конструирование таблиц предсказывающего анализатора
- •3.2.4. Ll(1)-грамматики
- •3.2.7. Рекурсивный спуск
- •3.3. Разбор снизу-вверх типа сдвиг-свертка
- •3.3.1. Основа
- •3.3.2. Lr(k)-анализаторы
- •3.3.4. Конфликты разбора типа сдвиг-свертка
Программная реализация конечных детерминированных распознавателей
Рассмотрим два способа программной реализации конечных детерминированных распознавателей: компиляционный (программный) и интерпретационный (табличный).
1. Компиляционный способ.
Пусть задан граф конечного детерминированного распознавателя. Его можно преобразовать в программу распознавания цепочки языка, размер которой пропорционален количеству вершин (состояний) и дуг в графе. Каждому состоянию соответствует часть кода: считывание очередного символа и, если считан не концевой маркер, определение состояния перехода. Процесс распознавания заканчивается, если считан концевой маркер или если состоянием перехода является состояние ошибки. При переходе в состояние ошибки может быть выдано сообщение о типе ошибки. Результат распознавания определяется состоянием, в котором завершён процесс распознавания: если оно допускающее -–цепочка допускается, иначе – отвергается.
Рассмотрим два алгоритма реализации следующего конечного детерминированного распознавателя:
ц
ц
ц . ц
+
- .
ц
.
Алгоритм 1.
Код каждого состояния отметим соответствующей меткой. Переменную x будем использовать для хранения символа входной цепочки. Анализ переменной х выполним с помощью оператора множественного выбора. Переход в новое состояние закодируем оператором безусловного перехода на метку. Концевой маркер обозначим символом ‘┤’
Псевдокод алгоритма 1 представлен ниже:
s0: ввод (х);
выбор (х)
х{‘+’,’-‘} : переход на s1;
х{‘0’..’9‘} : переход на s2;
х = '.' : переход на s3;
x = ‘┤’ : вывод ( ‘Отвергнуть, цепочка пуста’), конец.
конец выбор;
s1: ввод (х);
выбор (х)
х{‘0’..’9‘} : переход на s2;
х = '.' : переход на s3;
х{‘+’,’-‘} : вывод ( ‘Отвергнуть, недопустимы два знака
подряд’), конец.
x = ‘┤’ : вывод ( ‘Отвергнуть, нет значения константы’), конец.
конец выбор;
s2: ввод (х);
выбор (х)
х{‘0’..’9‘} : переход на s2;
х = '.' : переход на s4;
иначе : вывод ( ‘Отвергнуть, в вещественной константе
должна быть точка ’), конец.
конец выбор;
s3: ввод (х);
выбор (х)
х{‘0’..’9‘} : переход на s4;
х = '.' : переход на s4;
иначе : вывод ( ‘Отвергнуть, после точки должна быть цифра’),
конец.
конец выбор;
s4: ввод (х);
выбор (х)
х{‘0’..’9‘} : переход на s4;
х{‘+’,’-‘,'.'} : вывод ( ‘Отвергнуть, последний символ
не может встречаться дважды’), конец.
x = ‘┤’ : вывод ( ‘Допустить’), конец.
конец выбор;
Алгоритм 2.
Номер текущего состояния будем хранить в переменной S , а для его определения будем использовать множественный выбор, каждая альтернатива которого соответствует состоянию. Символ входной цепочки будем хранить в переменной x , а для его анализа будем использовать множественный выбор. Переходу в новое состояние соответствует присваивание переменной S нового значения. Распознавание будем выполнять в цикле, пока не будет введён концевой маркер или пока в S не записано состояние ошибки. Состояние ошибки представим отрицательным числом, которое так же является кодом ошибки. После выхода из цикла выводится результат – ‘допустить’ или ‘отвергнуть’ и, если ‘отвергнуть’, то выводится сообщение об ошибке.
Псевдокод алгоритма 2 представлен ниже:
S := 0;
повторять
ввод (х);
выбор (S)
S = 0 : выбор (х)
х{‘+’,’-‘} : переход на s1;
х{‘0’..’9‘} : переход на s2;
х = '.' : переход на s3;
x = ‘┤’ : S := -1;
конец выбор;
S = 1 : выбор (х)
х{‘0’..’9‘} : переход на s2;
х = '.' : переход на s3;
х{‘+’,’-‘} : S := -2;
x = ‘┤’ : S = -3;
конец выбор;
S = 2 : выбор (х)
х{‘0’..’9‘} : переход на s2;
х = '.' : переход на s4;
иначе : S = -4;
конец выбор;
S = 3 : выбор (х)
х{‘0’..’9‘} : переход на s4;
х = '.' : переход на s4;
иначе : S = -5;
конец выбор;
S = 4 : выбор (х)
х{‘0’..’9‘} : переход на s4;
х{‘+’,’-‘,'.'} : S = -6;
конец выбор;
конец выбор;
закончить повторять если х = ‘┤’ или S < 0 ;
выбор (S)
S = 4 : вывод ( ‘Допустить’);
S = -1 : вывод ( ‘Отвергнуть, цепочка пуста’);
S = -2 : вывод ( ‘Отвергнуть, недопустимы два знака подряд’);
S = -3 : вывод ( ‘Отвергнуть, нет значения константы’);
S = -4 : вывод ( ‘Отвергнуть, в вещественной константе
должна быть точка ’);
S = -5 : вывод ( ‘Отвергнуть, после точки должна быть цифра’);
S = -6 : вывод ( ‘Отвергнуть, последний символ не может
встречаться дважды’);
конец выбор;
2. Интерпретационный способ.
В этом способе реализации используется табличный способ задания конечного детерминированного распознавателя. Таблица распознавателя сохраняется в памяти и обрабатывается по определённому, общему для всех распознавателей, алгоритму. Номер состояния распознавателя будем хранить в переменной S , символ входной цепочки - в переменной x . На каждом шаге распознавания, пока не введён концевой маркер, определяется состояние перехода путём обращения к элементу таблицы распознавателя, расположенному в строке, соответствующей входному символу х , и столбце, соответствующем состоянию S . С помощью функции L(x) поставим в соответствие каждому входному символу номер строки таблицы. Для быстрого вычисления L(x) представим эту функцию в табличной форме и сохраним в массиве L таким образом, что элемент L[x] содержит номер строки таблицы распознавателя, соответствующей входному символу х . В результате, для вычисления значения функции L(x) достаточно обратиться к элементу L[x] . Переходы в состояние ошибки закодируем отрицательными числами, представляющими собой код ошибки. Сообщения об ошибках будем хранить в таблице ошибок. Выход из процесса распознавания происходит при вводе концевого маркера или достижении состояния ошибки.
Итак, для реализации конечного детерминированного распознавателя интерпретационным способом, необходимо иметь:
1) таблицу переходов конечного детерминированного распознавателя;
2) множество допускающих состояний;
3) массив L для определения строки, таблицы, соответствующей входномусимволу;
4) таблицу ошибок.
Ниже представлен псевдокод алгоритма интерпретационного способа реализации конечного детерминированного распознавателя.
S := 0;
ввод (х);
пока S 0 и х ‘┤’ выполнять
S := таблица_переходов[L[x],S];
ввод (х);
конец пока;
если S множество_допускающих_сотояний
то вывод (‘Допустить’)
иначе вывод(‘Отвергнуть ’, таблица_ошибок[-S]);
Этот алгоритм является универсальным и позволяет получать программы реализации различных распознавателей путём изменения обрабатываемых данных.
