Методы реализации языков программирования
Эта глава посвящена вопросам преобразования исходных кодов программы в исполняемое приложение. Можно выделить 2 основных подхода: компиляция и интерпретация. Однако, в настоящее время широко распространился подход, основанный на смешении этих двух крайних случаев. Управляемый код, который может быть выполнен на виртуальной машине, обладает достоинствами, а также некоторыми недостатками как компилируемых, так и интепретируемых языков.
Процесс компиляции, а также особенности языков, связанные с этим подходом реализации обсуждаются в разделе 2.1. Схема чистой интерпретации и последствия этого подхода рассмотрены в разделе 2.2. В разделе 2.3 обсуждается комбинированный подход компиляция + интерпретация, а также иерархия виртуальных машин.
Не последнюю роль в реализации языка также играет Операционная Система, на которой планируется исполнять программу. Роль ОС в реализации языков программирования обсуждается в разделе 2.4
Наконец, в процессе подготовки дистрибутива приложения иногда возникают сопутствующие задачи, связанные с нефункциональными требованиями и особенностями работы компиляторов и интерпретаторов. Эти задачи перечислены в разделе 2.5
Компилируемые языки программирования
Компиляция
a. Трансляция в машинные коды; b. Native-код. Код, выполняющийся непосредственно на процессоре; c. Программа зависит от ОС; d. Сложный механизм отладки.
Этапы компиляции:
a. Лексический анализ; b. Синтаксический анализ; c. Генерация промежуточного кода; d. Оптимизация; e. Генерация машинного кода; f. Линковка.
Единица Трансляции
a. Программы делят на несколько файлов с исходным кодом – единицу трансляции. Каждая единица задаёт свою область видимости переменных, функций, классов; i. Какие имена экспортировать для связи других модулей с данным? ii. Как перечислить все импортируемые зависимости? b. C++: header-file .h, .cpp. Объявление/Определение. Импорт на этапе линковки. Extern, static. c. C#, Java: import, using. Импорт на основе метаданных. Public, private, internal. d. Go: Директива import. PublicCase, privateCase.
Интерпретируемые языки
Интерпретация
a. Распространяется исходный код; b. Нужна программа-интерпретатор; c. Программа не зависит от ОС – Переносимость; d. Простота отладки;
Этапы интерпретации
a. Прочесть строку кода, b. Расшифровать команду, c. Выполнить команду – изменить состояние среды интерпретатора; d. Перейти к следующей строке.
REPL
a. Интерпретатору не нужен весь код целиком b. Возможность изучения ЯП c. Read – Eval – Print - Loop
Intermediate Language. Прекомпиляция + Интерпретация
a. Отладка не сложнее, чем в чистом интерпретаторе; b. Переносимость – как в интерпретаторе; c. Распространяются не исходники, а прекомпилированные сборки; d. Оптимизация под конкретную ОС, железо.
Этапы работы IL
a. Лексический, Синтаксический анализы, Генерация промежуточного кода; b. Just-In-Time компиляция; c. Оптимизация под компьютер-исполнитель; d. Исполнение в Виртуальной Машине.
Виртуальная машина
Сопутствующие задачи
Обфускация и Минимизация кода
a. Код отдаётся в исходном виде b. Как защититься от взлома? c. Как минимизировать размер дистрибутива?
Следует обратить внимание, что реализация языка программирования слабо связана с синтаксисом языка. То есть, можно реализовать чистый интерпретатор языка C++, или компилятор для нативного исполнения языка JavaScript. Однако, часто семантика некоторых языковых фич ограничивает возможности реализации.