Компиляция и интерпретация
| English | Русский |
|---|---|
| compiler/kəmˈpaɪlə/ | компилятор |
| interpreter/ɪnˈtɜːprɪtə/ | интерпретатор |
| lexical analysis/ˈleksɪkl əˈnæləsɪs/ | лексический анализ |
| tokens/ˈtəʊkənz/ | токены |
| syntax analysis/ˈsɪntæks əˈnæləsɪs/ | синтаксический анализ |
| abstract syntax tree/ˈæbstrækt ˈsɪntæks triː/ | абстрактное синтаксическое дерево |
| syntax error/ˈsɪntæks ˈerə/ | синтаксическая ошибка |
| semantic analysis/səˈmæntɪk əˈnæləsɪs/ | семантический анализ |
| code generation/kəʊd ˌdʒenəˈreɪʃn/ | генерация кода |
| code optimisation/kəʊd ˌɒptɪmaɪˈzeɪʃn/ | оптимизация кода |
Ошибка, стоившая космического аппарата, найденная запятой
- Маринер-1 НАСА был уничтожен через 293 секунды после запуска в 1962 году. Сбой часто приписывают одной неправильной букве в программном обеспечении навигации.
- Эту историю пересказывают так часто, потому что она отражает страх каждого программиста: компилятор радостно переводит программу, которая говорит о чем-то, чего вы не имели в виду.
- Компилятор — это не один процесс, а пять, и каждый ловит свой класс ошибок. Знание того, какой этап за что отвечает, помогает определить источник любой конкретной ошибки.
- Этот урок объясняет, как интерпретатор выполняет программу без создания исполняемого файла, и пять этапов, которые проходит компилятор.
Как интерпретатор выполняет программу
- Интерпретатор переводит исходный код и выполняет его одновременно, строку за строкой.
- Для каждой строки он считывает её, анализирует, проверяет типы, затем выполняет действие немедленно и переходит к следующей.
- Он не создает исполняемый файл: перевод существует только в памяти и удаляется сразу. Это ключевой момент учебной программы: интерпретатор может выполнять программу, не создавая переведенную версию.
- Он сообщает об ошибке в тот момент, когда достигает проблемной строки, и обычно останавливается, что обеспечивает быструю обратную связь при разработке. Каждый запуск требует повторного перевода, поэтому это медленнее, и для работы нужны и исходный код, и сам интерпретатор.

Переведи один раз и сохрани, или переводи и выполняй, а потом забудь
Интерпретатор:
Он работает построчно, сообщая об ошибках по мере их обнаружения; ничего не сохраняется в виде исполняемого файла, и обычно он медленнее.
Интерпретатор может выполнить программу, не создавая её переведенной версии.
Перевод существует только в памяти, по одной инструкции, и затем выбрасывается. Вот почему интерпретатор должен присутствовать каждый раз, когда программа запускается.
Этап 1: лексический анализ
- Лексический анализ объединяет отдельные символы исходного кода в токены: ключевые слова, идентификаторы, операторы и литералы.
- Пробельные символы и комментарии отбрасываются, так как они не имеют смысла для компилятора, а идентификаторы заносятся в таблицу символов.
- Так
total ← count * 2становится последовательностью токенов: идентификатор, присваивание, идентификатор, оператор, литерал. Лексер не знает и не заботится, имеет ли это смысл.
Лексический анализ (лексер) преобразует:
Лексер группирует символы в токены и исключает пробелы/комментарии; парсинг затем строит дерево.
Лексический анализ группирует исходные символы в ____ и исключает пробелы и комментарии.
Ключевые слова, идентификаторы, операторы и литералы. Лексер не оценивает, является ли последовательность допустимой программой.
Этап 2: синтаксический анализ
- Синтаксический анализ, или парсинг, проверяет, соответствует ли последовательность токенов грамматике языка, и строит абстрактное синтаксическое дерево, представляющее структуру.
- Пропущенная скобка, отсутствующий
ENDIFили ключевое слово не на своем месте здесь ловятся: это и есть синтаксическая ошибка, и именно поэтому компилятор может сообщить о ней, никогда не запуская программу.

Грамматика, по которой проверяет парсер
Этап 3: семантический анализ
- Семантический анализ проверяет, имеет ли синтактически корректная программа вообще смысл: каждая переменная должна быть объявлена до использования, типы по обе стороны присваивания должны быть совместимы, функция вызывается с правильным количеством аргументов.
total ← "seven" * 2— это абсолютно правильный синтаксис и полный бред. Только этот этап это ловит.- Именно это различие проверяют экзамены: синтаксис — это форма, семантика — это смысл.
Что из этого проверяется семантическим анализом, а не синтаксическим?
Скобки — это грамматика, значит синтаксис; пробелы — это лексер; регистры — это генерация кода. Объявления и типы — это смысл.
Этапы 4 и 5: генерация кода и оптимизация
- Генерация кода обходит дерево и порождает целевой машинный код, выбирая регистры и размещая данные.
- Оптимизация кода улучшает этот код, не меняя его поведения: устранение избыточных операций, вычисление константных выражений на этапе компиляции и переупорядочивание инструкций под конвейер.
- Результатом является самостоятельный исполняемый файл, который работает без наличия компилятора.

Пять этапов, и каждый ловит то, что предыдущий пропускал
Этапы компиляции
Продемонстрируйте, что делает компилятор с вашим исходным кодом. Каждый этап передает свой результат следующему — символы становятся токенами, токены превращаются в дерево, дерево становится оптимизированным машинным кодом.
Соотнесите каждый этап компилятора с тем, что он делает.
Каждый этап трансформирует программу на шаг дальше: токены, затем дерево, затем проверка, затем оптимизированный код.
Расставьте этапы компилятора по порядку.
Лексический → синтаксический → семантический → генерация кода → оптимизация.
Оптимизация кода направлена на то, чтобы:
Оптимизация улучшает сгенерированный код (сворачивание констант, удаление избыточности, перестановка для конвейера).
Разобранный пример: какой этап ловит какую ошибку
x ← 5 +: токены не соответствуют грамматике, оператор без правого операнда, поэтому синтаксический анализ.x ← y + 1, гдеyникогда не было объявлено: форма верна, но смысл нет, значит семантический анализ.IF a > b THEN OUTPUT aбезENDIF: снова синтаксический анализ.- Программа, которая запускается и выводит неверное среднее значение: ни один этап это не ловит. Это логическая ошибка, и найти её можно только тестированием. Назовите этап и объясните, почему предыдущие его пропустили.
Соотнесите каждую ошибочную строку с этапом компилятора, который её обнаружит.
Синтаксис — это форма, семантика — смысл, а логическая ошибка валидна и для того, и для другого, но ошибочна по намерению.
Разобранный пример: сравнение двух переводчиков
- Назовите два отличия в том, как компилятор и интерпретатор обрабатывают программу. [4]
- Компилятор переводит всю программу перед запуском и создает исполняемый файл, который затем работает без компилятора; интерпретатор переводит и выполняет по одной строке и не создает исполняемого файла, поэтому интерпретатор должен присутствовать при каждом запуске.
- Компилятор сообщается все ошибки вместе после перевода; интерпретатор сообщает о первой ошибке, достигнув этой строки, и затем останавливается.
- За каждый балл нужно указать пару действий: сказать, что делает одно, и что вместо него делает другое.
Потерянные баллы
- Интерпретатор не создает переведенной версии. Именно эту фразу использует учебная программа, и за нее дают балл.
- Лексер производит токены и отбрасывает пробелы и комментарии; он не проверяет, корректна ли программа.
- Синтаксис — это форма, семантика — это смысл. Необъявленная переменная — это семантическая ошибка, а не синтаксическая.
- Оптимизация не должна менять то, что делает программа, только скорость или компактность выполнения.
Вы поняли
- Интерпретатор переводит и выполняет по одной строке, не создавая исполняемого файла, сообщает о первой ошибке на своей строке и перечитывает код при каждом запуске
- лексический анализ формирует токены и отбрасывает пробелы и комментарии; синтаксический анализ проверяет грамматику и строит абстрактное синтаксическое дерево, выявляя синтаксические ошибки
- семантический анализ проверяет смысл: объявления, типы, количество аргументов
- генерация кода производит машинный код, а оптимизация улучшает его без изменения поведения, создавая готовый к запуску исполняемый файл