Давайте создадим компилятор!
Добавить в закладки К обложке
- Введение - Страница 1
- Основа - Страница 3
- Синтаксический анализ выражений - Страница 4
- Одиночные цифры - Страница 5
- Выражения с двумя цифрами - Страница 6
- Общая форма выражения - Страница 8
- Использование стека - Страница 9
- Умножение и деление - Страница 10
- Круглые скобки - Страница 11
- Унарный минус - Страница 12
- Слово об оптимизации - Страница 13
- Снова выражения - Страница 15
- Переменные - Страница 16
- Функции - Страница 17
- Подробнее об обработке ошибок - Страница 18
- Присваивание - Страница 19
- Многосимвольные токены - Страница 20
- Пробелы - Страница 21
- Интерпретаторы - Страница 23
- Интерпретатор - Страница 25
- Немного философии - Страница 27
- Управляющие конструкции - Страница 30
- План - Страница 31
- Немного основ - Страница 32
- Оператор IF - Страница 34
- Оператор WHILE - Страница 35
- Оператор LOOP - Страница 36
- Цикл FOR - Страница 37
- Оператор DO - Страница 38
- Оператор BREAK - Страница 39
- Заключение - Страница 41
- Булевы выражения - Страница 43
- План - Страница 44
- Грамматика - Страница 45
- Операторы отношений - Страница 46
- Исправление грамматики - Страница 47
- Синтаксический анализатор - Страница 48
- Объединение с управляющими конструкциями - Страница 52
- Добавление присваиваний - Страница 53
- Лексический анализ - Страница 54
- Лексический анализ - Страница 55
- Конечные автоматы и альтернативы - Страница 56
- Эксперименты по сканированию - Страница 57
- Пробел - Страница 58
- Конечные автоматы - Страница 59
- Новые строки - Страница 60
- Операторы - Страница 61
- Списки, запятые и командные строки - Страница 62
- Становится интересней - Страница 63
- Возвращение символа - Страница 65
- Распределенные сканеры против централизованных - Страница 66
- Объединение сканера и парсера - Страница 67
- Заключение - Страница 72
- Немного философии - Страница 73
- Дорога домой - Страница 74
- Почему это так просто? - Страница 76
- Здесь нет ничего сложного! - Страница 77
- Заключение - Страница 80
- Вид сверху - Страница 81
- Верхний уровень - Страница 82
- Структура Паскаля - Страница 83
- Расширение - Страница 84
- Объявления - Страница 85
- Структура Си - Страница 87
- Представление «TINY» - Страница 90
- Подготовка - Страница 91
- Объявления - Страница 93
- Объявления и идентификаторы - Страница 94
- Инициализаторы - Страница 95
- Таблица идентификаторов - Страница 96
- Выполнимые утверждения - Страница 97
- Булева логика - Страница 99
- Управляющие структуры - Страница 101
- Лексический анализ - Страница 103
- Многосимвольные имена переменных - Страница 105
- Снова операторы отношений - Страница 106
- Ввод/Вывод - Страница 107
- Заключение - Страница 108
- Пересмотр лексического анализа - Страница 113
- Предпосылка - Страница 114
- Проблема - Страница 115
- Решение - Страница 116
- Исправление компилятора - Страница 118
- Заключение - Страница 119
- Разное - Страница 124
- Точки с запятой - Страница 125
- Синтаксический сахар - Страница 126
- Работа с точками с запятой - Страница 127
- Компромисс - Страница 128
- Комментарии - Страница 129
- Односимвольные разделители - Страница 130
- Многосимвольные разделители - Страница 132
- Односторонние комментарии - Страница 133
- Заключение - Страница 134
- Процедуры - Страница 135
- Последнее отклонение - Страница 136
- Основы - Страница 137
- Основа для экспериментов - Страница 138
- Объявление процедуры - Страница 140
- Вызов процедуры - Страница 142
- Передача параметров - Страница 143
- Семантика параметров - Страница 145
- Передача по значению - Страница 147
- Что неправильно? - Страница 149
- Передача по ссылке - Страница 151
- Локальные переменные - Страница 152
- Заключение - Страница 154
- Типы - Страница 155
- Что будет дальше? - Страница 156
- Таблица идентификаторов - Страница 157
- Добавление записей - Страница 159
- Распределение памяти - Страница 160
- Объявление типов - Страница 161
- Присваивания - Страница 162
- Трусливый выход - Страница 164
- Более приемлемое решение - Страница 165
- Литеральные аргументы - Страница 167
- Аддитивные выражения - Страница 168
- Почему так много процедур? - Страница 170
- Мультипликативные выражения - Страница 171
- Умножение - Страница 172
- Деление - Страница 173
- Завершение - Страница 174
- Приводить или не приводить - Страница 175
- Заключение - Страница 177
- Назад в будущее - Страница 178
- Новое начало, старое направление - Страница 179
- Начинаем заново? - Страница 181
- Модуль INPUT - Страница 182
- Модуль OUTPUT - Страница 184
- Модуль ERROR - Страница 185
- Лексический и синтаксический анализ - Страница 186
- Модуль SCANNER - Страница 188
- Решения, решения - Страница 189
- Синтаксический анализ - Страница 191
- Ссылки - Страница 193
- Конструирование модулей - Страница 194
- Совсем как классический? - Страница 196
- Расширение синтаксического анализатора - Страница 198
- Термы и выражения - Страница 200
- Присваивания - Страница 202
- Булева алгебра - Страница 203
- Булево «AND» - Страница 205
Лексический анализ
Лексический анализ – это процесс сканирования потока входных символов и разделения его на строки, называемые лексемами. Большинство книг по компиляторам начинаются с этого и посвящают несколько глав обсуждению различных методов построения сканеров. Такой подход имеет свое место, но, как вы уже видели, существуют множество вещей, которые вы можете сделать даже никогда не обращавшись к этому вопросу, и, фактически, сканер, который мы здесь закончим, не очень будет напоминать то, что эти тексты описывают. Причина? Теория компиляторов и, следовательно, программы следующие из нее, должны работать с большинством общих правил синтаксического анализа. Мы же не делаем этого. В реальном мире возможно определить синтаксис языка таким образом, что будет достаточно довольно простого сканера. И как всегда KISS – наш девиз.
Как правило, лексический анализатор создается как отдельная часть компилятора, так что синтаксический анализатор по существу видит только поток входных лексем. Теоретически нет необходимости отделять эту функцию от остальной части синтаксического анализатора. Имеется только один набор синтаксических уравнений, который определяет весь язык, поэтому теоретически мы могли бы написать весь анализатор в одном модуле.
Зачем необходимо разделение? Ответ имеет и теоретическую и практическую основы.
В 1956 Ноам Хомский определил «Иерархию Хомского» для грамматик. Вот они:
Тип 0. Неограниченные (например Английский язык)
Тип 1. Контекстно-зависимые
Тип 2. Контекстно-свободные
Тип 3. Регулярные.
Некоторые характеристики типичных языков программирования (особенно старых, таких как Фортран) относят их к Типу 1, но большая часть всех современных языков программирования может быть описана с использованием только двух последних типов и с ними мы и будем здесь работать.
Хорошая сторона этих двух типов в том, что существуют очень специфические пути для их анализа. Было показано, что любая регулярная грамматика может быть анализирована с использованием частной формы абстрактной машины, называемой конечным автоматом. Мы уже реализовывали конечные автоматы в некоторых их наших распознающих программ.
Аналогично грамматики Типа 2 (контекстно-свободные) всегда могут быть анализированы с использованием магазинного автомата (конечный автомат, дополненный стеком). Мы также реализовывали эти машины. Вместо реализации явного стека для выполнения работы мы положились на встроенный стек связанный с рекурсивным кодированием и это фактически является предочтительным способом для нисходящего синтаксического анализа.
Случается что в реальных, практических грамматиках части, которые квалифицируются как регулярные выражения, имеют склонность быть низкоуровневыми частями, как определение идентификатора:
<ident> ::= <letter> [ <letter> | <digit> ]*
Так как требуется различные виды абстрактных машин для анализа этих двух типов грамматик, есть смысл отделить эти низкоуровневые функции в отдельный модуль, лексический анализатор, который строится на идее конечного автомата. Идея состоит в том, чтобы использовать самый простой метод синтаксического анализа, необходимый для работы.
Имеется другая, более практическая причина для отделения сканера от синтаксического анализатора. Мы хотим думать о входном исходном файле как потоке символов, которые мы обрабатываем справа налево без возвратов. На практике это невозможно. Почти каждый язык имеет некоторые ключевые слова типа IF, WHILE и END. Как я упомянул ранее, в действительности мы не можем знать является ли данная строка ключевым словом до тех пор пока мы не достигнем ее конца, что определено пробелом или другим разделителем. Так что мы должны хранить строку достаточно долго для того, чтобы выяснить имеем мы ключевое слово или нет. Это ограниченная форма перебора с возвратом.
Поэтому структура стандартного компилятора включает разбиение функций низкоуровневого и высокоуровневого синтаксического анализа. Лексический анализатор работает на символьном уровне собирая символы в строки и т.п., и передавая их синтаксическому анализатору как неделимые лексемы. Также считается нормальным позволить сканеру выполнять работу по идентификации ключевых слов.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206