Анимация
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

5. Арифметическое устройство

Здесь выполняются различные арифметические операции, тесты и т. п.

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

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

Для представления компилятора можно воспользоваться Т-образной схемой. Например, компилятор для ПЛ/1, написанный в коде ICL 1900 с целью получения кода 1900, можно изобразить так, как показано на рис. 1.2. В верхнем левом углу схемы указывается исходный язык, в верхнем правом углу - объектный язык, а внизу - язык, на котором написан компилятор. Кросс-компилятор выдает код для какой-либо другой машины, а не основной машины. Например, на рис. 1.3 представлен компилятор, написанный в коде ЭВМ ICL 1900, для компиляции программ на Фортране в код машины PDP-11. С помощью такого компилятора программы, написанные на Фортране, могут компилироваться на ЭВМ 1900, а пропускаться на ЭВМ PDP-11.


В настоящее время предпринимаются попытки как можно больше обособлять зависимые от машины части компилятора. Это особенно важно в тех случаях, когда компилятор предполагается перемещать из одной машины на другую. Так, если бы у нас был написанный на Фортране компилятор Паскаля, который должен выработать код, предназначенный для машины А (рис. 1.4), то, чтобы пропустить компилятор на машине А, нам прежде всего потребовалось бы транслировать его в А-код с помощью компилятора Фортрана, написанного в А-коде и выдающего А-код (рис. 1.5).

Оаскадь

шртран

Фортран Й-код

й-код

Рис. 1.4

Рис. 1.5



Из этих двух компиляторов мы смогли бы получить третий, используя второй компилятор для компиляции первого (рис. 1.6). Это можно показать, соединив вместе две Т-образные схемы в соответствии с простыми правилами (рис. 1.7); согласно которым ветви среднего звена Т должны показывать те же языки, что и корни смежных с ними соседних звеньев, а в двух верх-


них звеньях в правых и левых углах должны быть записаны одинаковые языки. Можно также образовать более сложные Т-образные схемы (рис. 1.8). Идея создания Т - образной схемы принадлежит Брэтману [11].


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



1.2. НЕКОТОРЫЕ АСПЕКТЫ ПРОЦЕССА КОМПИЛЯЦИИ

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

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

Структуру программы удобно представлять в виде дерева. Так, для программы

begin int х;

х: = 3; print (Х)

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


Допустим, что конечными вершинами дерева разбора (вершинами, у которых нет ветвей) являются не отдельные литеры, а такие идентификаторы, как print, или такие слова языка, как begin. Конечную вершину с меткой print можно было бы заменить поддеревом, показанным на рис. 1.10, но читателю это помешает яснее представить себе структуру программы. Что касается компилятора, то в начальной фазе работы он объединяет литеры в так называемые символы, например




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