- •Лекции по построению компилятора на Pascal Автор неизвестен Оглавление
- •1. Введение введение
- •2. Синтаксический анализ выражений начало
- •Одиночные цифры
- •Выражения с двумя цифрами
- •Общая форма выражения
- •Использование стека
- •Умножение и деление
- •Круглые скобки
- •Унарный минус
- •Слово об оптимизации
- •3. Снова выражения введение
- •Переменные
- •Функции
- •Подробнее об обработке ошибок
- •Присваивание
- •Многосимвольные токены.
- •Пробелы
- •4. Интерпретаторы введение
- •Интерпретатор
- •Немного философии
- •5. Управляющие конструкции введение
- •Немного основ
- •Оператор if
- •Оператор while
- •Оператор loop
- •Цикл for
- •Оператор do
- •Оператор break
- •Заключение
- •6. Булевы выражения введение
- •Грамматика
- •Операторы отношений
- •Исправление грамматики
- •Синтаксический анализатор
- •Объединение с управляющими конструкциями
- •Добавление присваиваний
- •7. Лексический анализ введение
- •Лексический анализ
- •Конечные автоматы и альтернативы
- •Эксперименты по сканированию
- •Конечные автоматы
- •Новые строки
- •Операторы
- •Списки, запятые и командные строки.
- •Становится интересней
- •Возвращение символа
- •Распределенные сканеры против централизованных
- •Объединение сканера и парсера
- •Пара комментариев:
- •Заключение
- •8. Немного философии введение
- •Дорога домой
- •Почему это так просто?
- •Здесь нет ничего сложного!
- •Заключение
- •9. Вид сверху введение
- •Верхний уровень
- •Структура паскаля
- •Расширение
- •Объявления
- •Структура си
- •10. Представление "tiny" введение
- •Подготовка
- •Объявления
- •Объявления и идентификаторы
- •Инициализаторы
- •Выполнимые утверждения
- •Булева логика
- •Управляющие структуры
- •Лексический анализ
- •Многосимвольные имена переменных
- •Снова операторы отношений
- •Ввод/вывод
- •Заключение
- •11. Пересмотр лексического анализа введение
- •Предпосылка
- •Проблема
- •Решение
- •Исправление компилятора
- •Заключение
- •12. Разное введение
- •Точки с запятой
- •Синтаксический сахар
- •Работа с точками с запятой
- •Компромисс
- •Комментарии
- •Односимвольные разделители
- •Многосимвольные разделители
- •Односторонние комментарии
- •Заключение
- •13. Процедуры введение
- •Последнее отклонение
- •Основа для экспериментов
- •Объявление процедуры
- •Вызов процедуры
- •Передача параметров
- •Семантика параметров
- •Передача по значению
- •Что неправильно?
- •Передача по ссылке
- •Локальные переменные
- •Заключение
- •14. Типы введение
- •Что будет дальше?
- •Добавление записей
- •Распределение памяти
- •Объявление типов
- •Присваивания
- •Трусливый выход
- •Более приемлемое решение
- •Литеральные аргументы
- •Аддитивные выражения
- •Почему так много процедур?
- •Мультипликативные выражения
- •Умножение
- •Деление
- •Завершение
- •Приводить или не приводить
- •Заключение
- •15. Назад в будущее введение
- •Новое начало, старое направление
- •Начинаем заново?
- •Модуль input
- •Модуль output
- •Модуль error
- •Лексический и синтаксический анализ
- •Модуль scanner
- •Решения, решения
- •Синтаксический анализ
- •16. Конструирование модулей введение
- •Совсем как классический?
- •Расширение синтаксического анализатора
- •Термы и выражения
- •Присваивания
- •Булева алгебра
Лексический анализ
Лексический анализ - это процесс сканирования потока входных символов и разделения его на строки, называемые лексемами. Большинство книг по компиляторам начинаются с этого и посвящают несколько глав обсуждению различных методов построения сканеров. Такой подход имеет свое место, но, как вы уже видели, существуют множество вещей, которые вы можете сделать даже никогда не обращавшись к этому вопросу, и, фактически, сканер, который мы здесь закончим, не очень будет напоминать то, что эти тексты описывают. Причина? Теория компиляторов и, следовательно, программы, следующие из нее, должны работать с большинством общих правил синтаксического анализа. Мы же не делаем этого. В реальном мире возможно определить синтаксис языка таким образом, что будет достаточно довольно простого сканера. И как всегда KISS - наш девиз.
Как правило, лексический анализатор создается как отдельная часть компилятора, так что синтаксический анализатор по существу видит только поток входных лексем. Теоретически нет необходимости отделять эту функцию от остальной части синтаксического анализатора. Имеется только один набор синтаксических уравнений, который определяет весь язык, поэтому теоретически мы могли бы написать весь анализатор в одном модуле.
Зачем необходимо разделение? Ответ имеет и теоретическую и практическую основы.
В 1956 Ноам Хомский определил "Иерархию Хомского" для грамматик. Вот они:
-
Тип 0. Неограниченные (например Английский язык)
-
Тип 1. Контекстно-зависимые
-
Тип 2. Контекстно-свободные
-
Тип 3. Регулярные.
Некоторые характеристики типичных языков программирования (особенно старых, таких как Фортран) относят их к Типу 1, но большая часть всех современных языков программирования может быть описана с использованием только двух последних типов и с ними мы и будем здесь работать.
Хорошая сторона этих двух типов в том, что существуют очень специфические пути для их анализа. Было показано, что любая регулярная грамматика может быть анализирована с использованием частной формы абстрактной машины, называемой конечным автоматом. Мы уже реализовывали конечные автоматы в некоторых их наших распознающих программ.
Аналогично грамматики Типа 2 (контекстно-свободные) всегда могут быть анализированы с использованием магазинного автомата (конечный автомат, дополненный стеком). Мы также реализовывали эти машины. Вместо реализации явного стека для выполнения работы мы положились на встроенный стек связанный с рекурсивным кодированием и это фактически является предпочтительным способом для нисходящего синтаксического анализа.
Случается что в реальных, практических грамматиках части, которые квалифицируются как регулярные выражения, имеют склонность быть низкоуровневыми частями, как определение идентификатора:
<ident> ::= <letter> [ <letter> | <digit> ]*
Так как требуется различные виды абстрактных машин для анализа этих двух типов грамматик, есть смысл отделить эти низкоуровневые функции в отдельный модуль, лексический анализатор, который строится на идее конечного автомата. Идея состоит в том, чтобы использовать самый простой метод синтаксического анализа, необходимый для работы.
Имеется другая, более практическая причина для отделения сканера от синтаксического анализатора. Мы хотим думать о входном исходном файле как потоке символов, которые мы обрабатываем справа налево без возвратов. На практике это невозможно. Почти каждый язык имеет некоторые ключевые слова типа IF, WHILE и END. Как я упомянул ранее, в действительности мы не можем знать является ли данная строка ключевым словом до тех пор пока мы не достигнем ее конца, что определено пробелом или другим разделителем. Так что мы должны хранить строку достаточно долго для того, чтобы выяснить имеем мы ключевое слово или нет. Это ограниченная форма перебора с возвратом.
Поэтому структура стандартного компилятора включает разбиение функций низкоуровневого и высокоуровневого синтаксического анализа. Лексический анализатор работает на символьном уровне собирая символы в строки и т.п., и передавая их синтаксическому анализатору как неделимые лексемы. Также считается нормальным позволить сканеру выполнять работу по идентификации ключевых слов.