Промежуточная программа является, с одной стороны, линейной формой записи синтаксического дерева программы, а с другой, каждый оператор промежуточной программы должен относительно просто транслироваться в машинный код.
Основными терминальными символами, из которых состоит промежуточная программа, являются операторы и операнды. Различные формы представления промежуточной программы различаются, в основном, способом соединения операторов и операндов. В общем случае состав операторов промежуточного языка напоминает основные операторы машинного мнемокода, а операндами являются имена переменных, процедур, констант и т.д. из исходной программы и имена промежуточных переменных, меток и т.д., генерируемых самим компилятором. Внутри компилятора операторы представляются обычно числовыми кодами, а операнда — указателями на соответствующие элементы таблицы идентификаторов.
Общепринятыми формами представления промежуточной программы являются:
· постфиксная польская запись;
· тетрады;
· триады;
· связные списочные структуры и др.
Примеры таких представлений рассмотрим для программы, приведченной на рис. 4.6. В качестве языка, на котором будем записывать промежуточную программу, будем использовать язык, основные операторы которого приведены в табл. 4.1 [11].
Постфиксная форма записи (польская инверсная запись — ПОЛИЗ), впервые примененная польским логиком Я. Лукашевичем, в отличие от обычной (инфиксной) записи просто и точно указывает порядок выполнения операций. Так, например, арифметическое выражение а + b*с + d в ПОЛИЗ будет выглядеть a b с * + d +. В ПОЛИЗ операторы следуют непосредственно за своими операндами, поэтому не нужны скобки и не нужно учитывать приоритеты операций (конечно приоритеты учитываются, но на этапе построения ПОЛИЗ). Распространению ПОЛИЗ способствовало то, что для них существует очень простой алгоритм интерпретации выражений: выражение просматривается слева направо, и элементы его помещаются в стек до тех пор, пока рассматриваемый элемент не знак операции. Если же это знак операции, то два верхних элемента (в случае двухместной операции) выталкиваются из стека, над ними выполняется операция, после чего результат помещается в стек, операция удаляется из входной строки и просмотр продолжается.
Рис. 4.6. Учебный пример Паскаль — программы
Ниже приведен пример вычисления выражения 2 3 * 4 + с использованием данного алгоритма.
СОДЕРЖИМОЕ МАГАЗИНА |
ВХОДНАЯ СТРОКА |
# |
23*4 + |
#2 |
3*4 + |
#23 |
* 4 + |
#6 |
4 + |
#64 |
+ |
#10 |
В ранних компиляторах, использующих, так называемые «прямые методы трансляции», основные синтаксические единицы программы переводились в постфиксные выражения, а затем уже из ПОЛИЗ осуществлялся синтез объектной программы. С основными алгоритмами такого перевода можно ознакомиться в [16, 2].
ПОЛИЗ можно получить также непосредственно из бинарного синтаксического дерева программы, используя правило Кнута, для этого необходимо выполнить:
· обход левого поддерева снизу;
· обход правого поддерева снизу;
· посещение корня.
Таблица 4.1 Основные операторы промежуточного языка
Оператор |
Мнемокод |
Операнды |
Семантика оператора |
Начало блока |
BLOCK |
Инициализирует действие генератора кода, связанные с входом в блок |
|
Конец блока |
BLKEND |
Аналогично BLOCK, но при выходе из блока |
|
Переход по условию = > < <= >= |
BZ BP BM BMZ BPZ |
M, P |
Переход на М (М – метка, номер тетрады, или позиции символа в прграмме) при выполнении условия для значения из Р |
Безусловный переход |
BR |
M |
Безусловный переход на М (М – метка, номер тетрады, или позиции) |
Вычислить адрес элемента массива |
SUBS |
I, J, A, R |
Вычисляет адрес элемента массива А[I, J] и помещает в ячейку R, число аргументов может быть переменным |
Арифметические операторы |
+, -, *, / |
R1, R2, R3 |
Выполнение арифметических операций над операндами R1 и R2, результат – в R3 |
В ПОЛИЗ можно представить не только арифметические выражения, но и любые другие операторы исходной программы. Так, например, условный оператор
if <ВЫРАЖЕНИЕ> then <ОПЕРАТОР 1> else <ОПЕРАТОР 2>,
схема выполнения которого показана на рис. 4.7, в ПОЛИЗ будет иметь следующий вид:
<ВЫРАЖЕНИЕ> <МЕТКА 1> BZ <ОПЕРАТОР 1>
<МЕТКА 2> BR <МЕТКА 1> <ОПЕРАТОР 2> <МЕТКА 2>
Рис. 4.7. Схемы выполнения условного оператора и оператора цикла
Здесь предполагается, что для любого выражения 0 означает ложь, а не 0 – истину, BZ – бинарная операция, которая осуществляет переход на <МЕТКА 1>, если значение <ВЫРАЖЕНИЕ> – ложь и BR – унарная операция, осуществляет безусловный переход на <МЕТКА 2>. Оператор цикла
for i:=1 to10 do <OПEPATOP>
в ПОЛИЗ будет выглядеть:
i 1 := <МЕТКА 1 > i 10 — <МЕТКА 2> ВР <ОПЕРАТОР> i i 1
+ := <МЕТКА 1 > BR <METKA 2>
Схема выполнения этого оператора цикла показана на рис. 4.7.
С учетом приведенных представлений для операторов условного перехода и цикла, польская запись программы, приведенной на рис. 4.6, будет иметь вид:
(1) BLOCK S1 0:=S20:=
(8) i 1 :=i 10 — 46ВР
(16) i A SUBS 0 — 32BMZ
(23) S1 S1 i A SUBS + := 39 BR
(32)S2 S2 i ASUBS + :=
(39)i i 1 + :=11 BR
(46)BLKEND
Здесь слева в скобках для каждой строки указаны номера позиций крайних левых символов строки, номер позиции используется и в операторах перехода в качестве
значений соответствующих меток.
Многоадресный код (тетрады). Другой метод кодирования синтаксического дерева программы — применение многоадресного кода. Удобной формой представления является тетрада — код с явно именуемыми результатами операторов:
<операция>, <операнд1>, <операнд2>, <результат>.
Таким образом, выражение a+b*c+d можно представить в виде следующей последовательности тетрад:
*, b, c, t1
+, a, t1, t2
+, t2, d, t3
Здесь tl, t2 и t3 соответствуют промежуточным переменным, генерируемым компилятором. Представление в виде многоадресного кода, в данном случае трехадресного, удобно для фазы оптимизации, так как внести изменения здесь значительно проще, чем в польское представление промежуточной программы.
Представление программы, приведенной на рис. 4.6, в виде последовательности тетрад будет иметь вид:
(1) (1) BLOCK (9) +, t3, S1, S1
(2) :=, 0, , S1 (10) BR 13
(3) :=, 0, , S1 (11) SUBS, i, A, t4
(4) –, i, 10, t1 (12) +, t4, S2, S2
(5) BP, 15, t1 (13) +, i, 1, i
(6) SUBS, i, A, t2 (14) BR, 4
(7) BMZ, 11, t2 (15) BLKEND
(8) SUBS, i, A, t3
В качестве меток в операторах перехода здесь используются номера соответствующих тетрад.
С другими формами представления промежуточной программы можно познакомиться в работах [11, 9]. На практике обычно используется некоторое смешанное представление, зависящее, как отмечается в [11], от личных желаний и склонностей разработчика компилятора.