Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СПО юнита 2.doc
Скачиваний:
38
Добавлен:
17.11.2019
Размер:
5.82 Mб
Скачать

2 Основные концепции трансляторов

2.1 Введение в методы трансляции

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

Различают два вида транслятора: компилятор и интерпретатор.

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

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

На размер и количество модулей компилятора влияет "размер" исходного языка.

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

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

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

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

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

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

Препроцессоры

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

– обработка макросов. Макрос – средство замены одной последовательности символов другой;

– включение файлов;

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

– языковые расширения. Например, язык Equel – язык запросов к базе данных, встроенный в код языка С. Препроцессор получает инструкции, начинающиеся с ##, и переводит их в вызовы процедур, реализующих обращения к базе данных.

Обработчики макросов работают с двумя видами инструкций – определение макросов и их использование. Определения обычно указываются с помощью определенного символа или ключевого слова типа define или macro и состоят из имени определяемого макроса и его тела, формируя определение макроса. Макропроцессоры позволяют применять в определениях макросов формальные параметры, т.е. символы, заменяемые значениями при использовании макроса. Использование макроса представляет собой его имя с фактическими параметрами, т.е. значениями для подстановки вместо формальных параметров. Макропроцессор подставляет в тело макроса фактические значения вместо формальных параметров. Затем преобразованное тело макроса замещает его имя в программе.

Некоторые компиляторы создают ассемблерный код, который передается для дальнейшей об-работки ассемблеру. Другие компиляторы самостоятельно выполняют работу ассемблера, произ-водя перемещаемый машинный код, который непосредственно передается загрузчику/редактору связей.

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

MOV a, R1

ADD #2, R1

MOV Rl, b

Этот код перемещает содержимое памяти по адресу а в регистр 1, затем добавляет к нему константу 2, рассматривая содержимое регистра 1 как число с фиксированной точкой, и сохраняет результат в именованной ячейке памяти b. Таким образом вычисляется b := а + 2.

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

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

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

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

– исходным языком S, который он компилирует;

– целевым языком Т, для которого он генерирует код;

– языком реализации I, на котором он написан.

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