Опубликован: 23.07.2006 | Доступ: свободный | Студентов: 2213 / 889 | Оценка: 4.28 / 4.17 | Длительность: 21:37:00
Специальности: Системный архитектор
Лекция 3:

Основы компиляторов

< Лекция 2 || Лекция 3: 123456 || Лекция 4 >

Методики создания компилятора

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

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

  • прямой, в котором целевым языком и языком реализации является язык ассемблера
  • метод раскрутки
  • использование кросс-трансляторов
  • использование виртуальных машин
  • компиляция "на лету"

Метод раскрутки


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

Пусть есть компилятор K_{A} : P \to A, где P - некоторый язык более высокого уровня, чем язык ассемблера. Тогда напишем K_{P} : L \to A, а затем применим компилятор K_{A} к компилятору K_{P}, т.е. получим K_{A}=K_{A}(K_{P}): L \to A. Такая схема проиллюстрирована с помощью Т-диаграмм на слайде и называется раскруткой ( bootstrapping2Название метода раскрутки произошло от фразы "to pull oneself up by one's bootstrap", т.е. "вытянуть себя за шнурки", аналогично легендарному методу барона Мюнхгаузена ); компилятор K_{P} как бы "раскручивается" с помощью компилятора K_{A}.

Описанная схема может быть использована при написании компилятора некоторого языка на нем самом. Пусть у нас есть компилятор некоторого подмножества S языка L в язык A, написанный на языке A, K_{A} : S \to A . Тогда мы можем написать K_{L} : L \to A и получим новый компилятор K_{A} = K_{A}(K_{L}). Мы используем это подмножество S для того, чтобы написать компилятор языка L в язык A, K_{S} : L \to  A . Если теперь мы применим компилятор K_{A} к программе K_{S}, то получим K_{A}=K_{A}(K_{S}): L \to A

Впервые такая схема была применена в 1960 году при реализации языка Neliac. В 1971 году Вирт написал с использованием раскрутки транслятор языка Pascal, причем самый первый компилятор был оттранслирован вручную. Количество шагов раскрутки было больше 1, т.е. была построена последовательность языков S_1 \subset S_2 \subset ...S_n = L и построена последовательность компиляторов: K_{A}: S_{1} \to A, K_{A}^{1}=K_{A}(K_{S1}: S_{2} \to A), ...

Раскрутку можно использовать и в следующей ситуации. Пусть у нас есть недостаточно эффективный компилятор K_{A} : L \to A . Можно написать более эффективный компилятор K_{L} : L \to A , а затем применить раскрутку.

Кросс-транслятор

Пусть у нас есть два компьютера: компьютер M с языком ассемблера A и компьютер M_{1} с языком ассемблера A_{1}. Кроме того, предположим, что имеется компилятор K_{A1} : P \to A_{1}, а сам компьютер M по каким-то причинам не доступен либо пока еще не существует компилятор K_{A} : P \to A. Нас интересует компилятор K_{A} : L \to A . В такой ситуации мы можем использовать M_{1} в качестве инструментальной машины и написать компилятор K_{P} : L \to A , который принято называть кросс-транслятором (cross-compiler) . Как только машина M станет доступной, мы сможем перенести K_{P} на M и "раскрутить" его с помощью K_{A}. Понятно, что это решение достаточно трудоемко, поскольку могут возникнуть проблемы при переносе, например, из-за различий операционных систем.

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

< Лекция 2 || Лекция 3: 123456 || Лекция 4 >