Анимация
JavaScript


Главная  Библионтека 

0 [ 1 ] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

Глава 1. ПРОЦЕСС КОМПИЛЯЦИИ

Программы, написанные на языках программирования высокого уровня (или проблемно-ориентированных языках), перед выполнением на ЭВМ должны транслироваться 1 в эквивалентные программы, написанные в машинном коде. Языки высокого уровня появились в середине 50-х годов, примерами первых таких языков могут служить Фортран и Кобол, а примерами недавно созданных - Алгол 68, Паскаль и Ада. Программа, которая транслирует любую программу, написанную на конкретном языке высокого уровня, в эквивалентную программу на другом языке (обычно это код машины), называется компилятором. Компилирование программы включает анализ - определение предусмотренного результата действия программы и последующий синтез - генерирование эквивалентной программы в машинном коде. В процессе анализа компилятор должен выяснить, является ли входная программа недействительной в каком-либо смысле (т. е. принадлежит ли она к языку, для которого написан данный компилятор), и если она окажется недействительной,- выдать соответствующее сообщение программисту. Этот аспект компиляции называется обнаружением ошибок.

В построении компиляторов за последние двадцать пять лет достигнуты значительные успехи. Первые компиляторы использовали специальные методы, были медленными и не носили структурного характера. (Краткая история написания компиляторов изложена в [7].) В современных компиляторах применяются более системные методы; эти компиляторы относительно быстрые и строятся таким образом, чтобы как можно более четко выделять отдельные аспекты компиляции.

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

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

Версия программы на промежуточном языке нередко оказывается компактнее, чем машинный код, выданный компилятором.

Изменение части программы не требует перекомпиляции всей программы.

Диалоговые языки, такие, как Бейсик, часто реализуются посредством интерпретатора. Промежуточным языком обычно служит некая форма обратной польской записи (см. разд. 10.1). Хорошим пособием по реализации диалоговтх языков может быть работа [13]. Основной недостаток интерпретаторов заключается в том, что эти программы обычно пропускаются относительно медленно, так как операторы промежуточного кода должны транслироваться всякий раз, когда они выполняются, хотя временные издержки не так уж велики (они зависят от конструкции промежуточного языка). Применяется метод смешанного кода [15], в котором наиболее часто выполняемая часть интерпретируется. При этом экономится память, поскольку часть программы, которая должна интерпретироваться, будет, по



всей вероятности, намного компактнее, чем скомпилированный код. Развитием этой идеи является «отбрасывающее» компилирование [12].

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

Исходная j

Иймпалятор

программа

Рис и

Объектный

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

1.1. СВЯЗЬ МЕЖДУ ЯЗЫКАМИ И МАШИНАМИ

Хотя с точки зрения пользователя (и разработчика) различия между языками высокого уровня могут быть весьма значительными, мы здесь хотим подчеркнуть сходные, черты этих языков, чтобы показать, какие задачи должен выполнять компилятор. Под типичными языками высокого уровня мы подразумеваем языки Бейсик, Фортран, ПЛ/1, Паскаль, Алгол 68 и (возможно, в несколько меньшей степени) Кобол. Фрагменты программ, которыми мы иллюстрируем основные положения, написаны на Алголе 68, но они в равной степени могли бы быть написаны на любом другом языке, поэтому у читателей, не знакомых с Алголом 68. не должно возникать в связи с этим никаких серьезных проблем.

Языки

Общими средствами большинства языков высокого уровня являются: 1. Выражения и присваивания

Значение выражения обтчно можно вычислить и (помимо прочего) присвоить какой-либо переменной, например

где В, С, Е и F имеют соответствующие значения.

2. Условные выражения

Значение выражения или результат действия оператора может зависеть от логического значения (истинности), например

М Л = В then X+Y else X-Y fi

значением которого будет X+Y, если А =В> и X-Y - в противном случае.



3. Циклы

Последовательность действий может выполняться несколько раз. Например

Последовательность

выполняется 10 раз.

4. Ввод-вывод



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

redd ((х, у, z))

для чтения значений х, у и z и

для печати значений a, b и с.

print ((а. Ь, с})

5. Процедуры, подпрограммы, функции

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

ргос average {{ \ real row) real:

begin real sum:=0;

for i from Iwb row to upb row

do sum plusab row \t\

5um/(upb row - Iwb row+ 1) end

и, допуская, что а объявлено как

[1:10] real a

можно вызвать процедуру average, например, следующим образом:



0 [ 1 ] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69