
- •10. Трансляторы, компиляторы и интерпретаторы
- •10.1. Общая схема работы
- •10.2. Этапы трансляции. Общая схема работы транслятора
- •10.3. Понятие прохода. Многопроходные и однопроходные компиляторы
- •10.4 Особенности построения интерпретаторов
- •10.5. Трансляторы с языка ассемблера («ассемблеры»)
- •10.5.1. Реализация компиляторов с языка ассемблера
- •10.5.2. Макроопределения и макрокоманды
10.3. Понятие прохода. Многопроходные и однопроходные компиляторы
Порядок выполнения фаз компиляции может меняться в разных вариантах компиляторов. В одних компиляторах просмотр текста исходной программы сопровождается выполнением всех фаз компиляции и получением результата − объектного кода. В других − над исходным текстом выполняются только некоторые фазы компиляции, и получается не конечный результат, а набор некоторых промежуточных данных, которые снова подвергаются обработке. Причем несколько раз. Реальные компиляторы транслируют текст исходной программы за несколько проходов. Проход − это процесс последовательного чтения компилятором данных из внешней памяти, их обработки и записи результата во внешнюю память. Чаще всего один проход включает в себя выполнение одной или нескольких фаз компиляции. В качестве внешней памяти могут выступать любые носители информации − ОП, накопители на магнитных дисках, лентах и т.д. При выполнении каждого прохода компилятору доступна информация, полученная в результате всех предыдущих проходов. Но, как правило, в первую очередь используется информация, полученная на проходе, непосредственно предшествующему текущему. Информация, получаемая компилятором при выполнении проходов, недоступна пользователю. Человек, компилирующий свою программу, видит только исходный текст программы и результирующую объектную программу. Очевидно, что цель разработчиков компиляторов − максимально сократить количество проходов. Это необходимо для увеличения скорости работы компилятора и уменьшения объема необходимой ему памяти. Идеал − однопроходный компилятор, получающий на вход исходную программу и сразу же генерирующий результирующую объектную программу.
Но сократить число проходов не всегда возможно. Количество проходов определяется, прежде всего, грамматикой и семантическими правилами исходного языка. Чем сложнее грамматика языка и чем больше вариантов предполагают семантические правила − тем больше проходов будет выполнять компилятор. Например, компиляторы с языка Pascal работают быстрее, чем компиляторы с языка С из-за того, что грамматика языка Pascal более проста, а семантические правила более жёсткие.
Однопроходные компиляторы − редкость, они возможны только для очень простых языков. Реальные компиляторы выполняют от двух до пяти проходов и являются многопроходными. Например, трехпроходный компилятор работает так:
− первый проход − лексический анализ;
− второй − синтаксический разбор и семантический анализ;
− третий − генерация и оптимизация кода.
10.4 Особенности построения интерпретаторов
Интерпретатор − это программа, которая воспринимает входную программу на исходном языке и выполняет её.
Термин «интерпретатор», как и «транслятор» означает «переводчик». Но с точки зрения формальных языков, отличаются они принципиально.
Большинство интерпретаторов последовательно исполняют исходную программу по мере поступления ее на вход интерпретатора. При этом пользователю нет смысла ждать завершения компиляции всей исходной программы. Исходя из этой особенности (исполнение команд по мере их поступления) в интерпретаторах отсутствует фаза оптимизации. А также на последнем этапе − этапе генерации кода − машинные команды не записываются в объектный файл, а выполняются.
Кроме того, далеко не все языки программирования допускают построение интерпретаторов, которые выполняли бы исходные программы по мере поступления команд.
Последнее требование предусматривает существование компилятора, разбирающего исходную программу за один проход.
Основное требование интерпретаторов − обработка программы по мере поступления − нарушается, если язык допускает обращения к функциям и структурам данных раньше их непосредственного описания. По этой причине не могут интерпретироваться такие языки, как Си и Паскаль.
Из-за отсутствия в интерпретаторах фазы оптимизации выполнение программы с помощью интерпретатора менее эффективно, чем с помощью аналогичного компилятора. В добавок, интерпретируемая программа должна разбираться каждый раз при выполнении, а при компиляции она разбирается единожды. Затем используется объектный код или файл. Поэтому интерпретаторы всегда уступают компиляторам в производительности.
Преимуществом интерпретаторов является независимость выполнения программы от архитектуры целевой вычислительной системы. При переходе на другую архитектуру в случае компилятора требуется откомпилировать программу заново, т.к. объектный код всегда ориентируется на определенную архитектуру.
Долгое время интерпретаторы были менее распространены, чем компиляторы, и существовали для относительно простых языков программирования (Basic). Профессиональные средства разработки ПО с высокими требованиями к производительности строились на базе компиляторов.
Такое положение существовало до момента широкого распространения глобальных вычислительных сетей. Как правило, такие сети представляют собой набор ЭВМ различной архитектуры. Из-за этого на первый план выходит требование единообразного выполнения на каждой ЭВМ сети исходной программы.
Многие языки программирования, которые используются в сети Интернет, предусматривают механизм интерпретации исходного текста программы вместо компиляции. В качестве примера интерпретируемого языка широкого распространения выступает HTML (Hypertext Markup Language) язык описания гипертекста. Он лежит в основе функционирования большинства структур сети Интернет. Языки Java и Java Script сочетают функции компиляции и интерпретации. На первом этапе исходная программа компилируется в некоторый двоичный код, который является промежуточным и не зависит от архитектуры целевого компьютера. Этот код передается по сети и выполняется принимающим компьютером в виде интерпретации.