Лекция: Интерпретаторы. Особенности построения интерпретаторов

Интерпретатор – это программа, которая воспринимает входную программу на исходном языке и выполняет ее.

В отличие от трансляторов интерпретаторы не порождают результирующую программу (и вообще какого-либо результирующего кода) – и в этом принципиальная разница между ними. Интерпретатор, так же как и транслятор, анализирует текст исходной программы. Однако он не порождает результирующей программы, а сразу же выполняет исходную в соответствии с ее смыслом, заданным семантикой входного языка. Таким образом, результатом работы интерпретатора будет результат, заданный смыслом исходной программы, в том случае, если эта программа правильная, или сообщение об ошибке, если исходная программа неверна.

Конечно, чтобы исполнить исходную программу, интерпретатор так или иначе должен преобразовать ее в язык машинных кодов, поскольку иначе выполнение программ на компьютере невозможно. Он и делает это, однако полученные машинные коды не являются доступными для пользователя интерпретатора. Эти машинные коды порождаются интерпретатором, исполняются и уничтожаются по мере надобности. Пользователь же видит результат выполнения этих кодов – результат выполнения исходной программы.

Простейшим способом реализации интерпретатора можно было бы считать вариант, когда исходная программа сначала полностью транслируется в машинные команды, а затем сразу же выполняется. В такой реализации интерпретатор, по сути, мало чем бы отличался от компилятора с той лишь разницей, что результирующая программа в нем была бы недоступна пользователю.

Пользователь также должен был бы ждать компиляции всей исходной программы прежде, чем начнется ее выполнение, и по сути, в таком интерпретаторе не было бы никакого смысла – он не давал бы никаких преимуществ по сравнению с аналогичным компилятором.

Поэтому подавляющее большинство интерпретаторов исполняет исходную программу последовательно, по мере ее поступления на вход интерпретатора.

При таком порядке работы интерпретатора проявляется существенная особенность, которая отличает его от компилятора, – если интерпретатор исполняет команды по мере их поступления, то он не может выполнять оптимизацию исходной программы. Следовательно, фаза оптимизации в общей структуре интерпретатора будет отсутствовать. В остальном же она будет мало отличаться от структуры аналогичного компилятора, с той разницей, что на этапе генерации кода машинные команды не записываются в объектный файл, а выполняются по мере их порождения.

Далеко не все языки программирования допускают построение интерпретаторов, которые могли бы выполнять исходную программу по мере поступления команд. Для этого язык должен допускать возможность существования компилятора, выполняющего разбор исходной программы за один проход.

Кроме того, язык не может интерпретироваться по мере поступления команд, если он допускает появление обращений к функциям и структурам данных раньше их непосредственного описания. Поэтому данным методом не могут интерпретироваться такие языки, как С и Pascal.

Отсутствие шага оптимизации ведет к тому, что выполнение программы с помощью интерпретатора является менее эффективным, чем с помощью аналогичного компилятора. Кроме того, при интерпретации исходная программа должна заново разбираться всякий раз при ее выполнении, в то время как при компиляции она разбирается только один раз, а после этого всегда используется объектный файл. Таким образом, интерпретаторы всегда проигрывают компиляторам в производительности.

Преимуществом интерпретатора является независимость выполнения программы от архитектуры целевой вычислительной системы. В результате компиляции получается объектный код, который всегда ориентирован на определенную архитектуру. Для перехода на другую архитектуру целевой вычислительной системы программу требуется откомпилировать заново. А для интерпретации программы необходимо иметь только ее исходный текст и интерпретатор с соответствующего языка.

Далеко не все языки программирования допускают построение интерпретаторов, которые могли бы выполнять исходную программу по мере поступления команд. Для этого язык должен допускать возможность существования компилятора, выполняющего разбор исходной программы за один проход. Кроме того, язык не может интерпретироваться по мере поступления команд, если он допускает появление обращений к функциям и структурам данных раньше их непосредственного описания.

Недостаток: интерпретаторы всегда проигрывают компиляторам в производительности.

Достоинство: независимость выполнения программы от архитектуры компьютерной системы.

 

81. Трансляторы с языка ассемблера („ассемблеры“ )

Макроассемблер — транслятор с языка ассемблера, включающий средства определения и использования макрокоманд.

Транслятор с языка ассемблера является компилятором, т.к. язык результирующей программы — машинный код. Язык ассемблер содержит мнемонические коды машинных команд. При этом все команды можно разделить на две группы: обычные команды, которые преобразуются в машинные команды; специальные команды, которые используются компилятором для выполнения задач компиляции (распределение памяти). Каждая команда может быть разделена на три составляющие: поле метки;

код операции; поле операндов.

Компилятор с языка ассемблера обычно предусматривает возможность наличия комментариев, отделяющихся от команд заданным разделителем. Поле метки содержит идентификатор, представляющий собой метку, либо является пустым. Каждый идентификатор метки может встречаться в программе на языке ассемблер только один раз. Метка считается описанной там, где она непосредственно встретилась в программе (предварительное описание меток не требуется). Метка может быть использована для передачи управления на помеченную ею команду. Нередко метка отделяется от остальной части команды специальным разделителем (чаще всего — двоеточием «:»).

Код операции всегда представляет собой строго определенную мнемонику одной из возможных команд процессора или также строго определенную команду самого компилятора. Код операции описывается алфавитными символами входного языка. Чаще всего его длина составляет 3-4, реже — 5 или 6 символов. Поле операндов является пустым, либо представляет собой список из одного, двух, реже — трех операндов. Количество операндов строго определено и зависти от кода операции. Соответственно, каждому из этих вариантов соответствуют безадресные, одноадресные, двухадресные или трехадресные команды. В качестве операндов выступают идентификаторы или константы.

Особенностью языка ассемблера является то, что ряд идентификаторов в нем выделяется специально для обозначения регистров процессора. Такие идентификаторы, с одной стороны, не требуют предварительного описания, но, с другой, они не могут быть использованы пользователем для иных целей. Набор этих идентификаторов предопределен для каждого языка ассемблера. Синтаксис языка может быть написан с помощью регулярной грамматики. Семантика языка определяется компьютерной системой, на которую ориентирован данный язык. Основной задачей семантического анализа является проверить допустимость операндов для каждого кода операции, а также проверит, что все идентификаторы и метки, встречающиеся во входной программе, описаны и обозначающие их идентификаторы не совпадают с предопределенными идентификаторами, используемыми для обозначения кодов операций и регистров процессора.

Схемы синтаксического и семантического анализа в компиляторе с языка ассемблера могут быть реализованы на основе обычного конечного автомата. Существует ряд особенностей, присущих языкам ассемблера и упрощающих построение компиляторов для них:

в компиляторах не выполняется дополнительная идентификация переменных (все переменные языка сохраняют имена, присвоенные им пользователем);

в компиляторах предельно упрощено распределение памяти (работа со статической памятью; если используется динамическая память, то для работы с нею нужно использовать соответствующую библиотеку или функции операционной системы, а за ее распределение отвечает разработчик; разработчик должен организовать передачу данных, монитор памяти процедур и функций, а также отделить данные от кода программы);

на этапе генерации кода в компиляторе с языка ассемблера не производится оптимизация, поскольку разработчик сам отвечает за организацию вычислений, последовательность команд и распределение регистров процессора.

Компиляторы с языка ассемблера реализуются чаще всего по двухпроходной схеме. На первом проходе компилятор выполняет разбор исходной программы, ее преобразование в машинные коды и одновременно заполняет таблицу идентификаторов. Но на первом проходе в машинных командах остаются незаполненными адреса тех операндов, которые размещаются в оперативной памяти. На втором проходе компилятор заполняет эти адреса и одновременно обнаруживает неописанные идентификаторы. Это связано с тем, что операнд может быть описан в программе после того, как он первый раз был использован. Тогда его адрес еще не известен на момент построения машинной команды, а поэтому требуется второй проход. Типичным примером такого операнда является метка, предусматривающая переход вперед по ходу последовательности команд.

 

еще рефераты
Еще работы по информатике