Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[169]

5.5.2 Компиляция выражений

В этом и следующем разделе мы реализуем генераторы кода, на которые ссылается процедура compile.

Компиляция связующего кода

В общем случае результат работы каждого генератора кода будет заканчиваться командами - порожденными процедурой compile-linkage, - которые реализуют требуемый тип связи. Если это тип return, то нам надо породить команду (goto (reg continue)) . Она нуждается в регистре continue и никаких регистров не меняет. Если тип связи next, то никаких дополнительных команд порождать не надо. В остальных случаях тип связи - переход по метке, и мы порождаем команду goto на эту метку, команду, которая ни в чем не нуждается и не изменяет никакие регистры.36

(define (compile-linkage linkage) (cond ((eq? linkage return)

(make-instruction-sequence (continue) () ((goto (reg continue))))) ((eq? linkage next)

(empty-instruction-sequence)) (else

(make-instruction-sequence () () ((goto (label ,linkage)))))))

Связующий код добавляется к последовательности команд с сохранением через preserving регистра continue, поскольку связь return нуждается в этом регистре: если данная последовательность команд изменяет continue, а связующий код в нем нуждается, continue будет сохранен и восстановлен.

(define (end-with-linkage linkage instruction-sequence) (preserving (continue) instruction-sequence (compile-linkage linkage)))

Компиляция простых выражений

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

(define (compile-self-evaluating exp target linkage) (end-with-linkage linkage (make-instruction-sequence () (list target) ((assign ,target (const ,exp))))))

36 В этой процедуре используется конструкция Лиспа, называемая обратная кавычка (backquote) или квазикавычка (quasiquote), с помощью которой удобно строить списки. Обратная кавычка перед списком работает почти так же, как обычная, но при этом все выражения внутри списка, перед которыми стоит запятая, вычисляются.

Например, если значение linkage равно символу branch25, то результатом выражения ((goto (label ,linkage))) будет список ((goto (label branch25))). Подобным образом, если значением x является список (a b c), то (1 2 ,(car x)) дает при вычислении список (1 2 a).


(define (compile-quoted exp target linkage) (end-with-linkage linkage (make-instruction-sequence () (list target) ((assign ,target (const ,(text-of-quotation exp)))))))

(define (compile-variable exp target linkage) (end-with-linkage linkage (make-instruction-sequence (env) (list target) ((assign ,target

(op lookup-variable-value) (const ,exp)

(reg env))))))

Все эти последовательности команд изменяют целевой регистр, а для поиска значения переменной требуется регистр env.

Присваивания и определения обрабатываются во многом так же, как в интерпретаторе. Мы рекурсивно порождаем код, вычисляющий значение, которое следует присвоить переменной, и присоединяем его к последовательности из двух команд, которая собственно присваивает значение переменной или определяет ее, а затем заносит в целевой регистр значение всего выражения (символ ok). Рекурсивная компиляция вызывается с целевым регистром val и типом связи next, так что порождаемый код положит результат в регистр val, а затем продолжит выполнение с той последовательности, которая идет за ним. При объединении кода сохраняется env, поскольку для определения и присваивания переменной требуется окружение, а код, вычисляющий значение переменной, может оказаться сложным выражением, которое изменяет регистры произвольным образом.

(define (compile-assignment exp target linkage) (let ((var (assignment-variable exp)) (get-value-code (compile (assignment-value exp) val next))) (end-with-linkage linkage (preserving (env) get-value-code

(make-instruction-sequence (env val) (list target) ((perform (op set-variable-value!) (const ,var) (reg val) (reg env)) (assign ,target (const ok))))))))

(define (compile-definition exp target linkage) (let ((var (definition-variable exp)) (get-value-code (compile (definition-value exp) val next))) (end-with-linkage linkage (preserving (env) get-value-code

(make-instruction-sequence (env val) (list target) ((perform (op define-variable!) (const ,var)


(reg val) (reg env)) (assign ,target (const ok))))))))

Двухкомандная последовательность в конце нуждается в env и val и изменяет свой целевой регистр. Заметим, что мы сохраняем в последовательности env, но не сохраняем val, поскольку get-value-code для того и нужна, чтобы поместить в val результат, которым затем воспользуется эта последовательность. (На самом деле сохранение val было бы ошибкой, поскольку тогда сразу после выполнения get-value-code восстановилось бы старое значение

val.)

Компиляция условных выражений

Код для выражения if с указанными целевым регистром и типом связи имеет форму

(скомпилированный код для предиката с целевым регистром val и типом связи next) (test (op false?) (reg val)) (branch (label false-branch)) true-branch

(скомпилированный код для следствия с указанным целевым регистром и указанным типом связи либо after-if) false-branch

(скомпилированный код для альтернативы с указанными целевым регистром и типом связи) after-if

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

37Просто использовать метки true-branch, false-branch и after-if нельзя, потому что в программе может быть больше одного if. Компьютер порождает метки при помощи процедуры make-label. Она принимает символ в качестве аргумента и возвращает новый символ, имя которого начинается с данного. Например, последовательные вызовы (make-label a) будут возвращать a1 , a2 и так далее. Процедуру make-label можно написать аналогично тому, как порождаются новые имена переменных в языке запросов, а именно:

(define label-counter 0)

(define (new-label-number)

(set! label-counter (+ 1 label-counter)) label-counter)

(define (make-label name) (string->symbol

(string-append (symbol->string name)

(number->string (new-label-number)))))



[стр.Начало] [стр.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] [стр.70] [стр.71] [стр.72] [стр.73] [стр.74] [стр.75] [стр.76] [стр.77] [стр.78] [стр.79] [стр.80] [стр.81] [стр.82] [стр.83] [стр.84] [стр.85] [стр.86] [стр.87] [стр.88] [стр.89] [стр.90] [стр.91] [стр.92] [стр.93] [стр.94] [стр.95] [стр.96] [стр.97] [стр.98] [стр.99] [стр.100] [стр.101] [стр.102] [стр.103] [стр.104] [стр.105] [стр.106] [стр.107] [стр.108] [стр.109] [стр.110] [стр.111] [стр.112] [стр.113] [стр.114] [стр.115] [стр.116] [стр.117] [стр.118] [стр.119] [стр.120] [стр.121] [стр.122] [стр.123] [стр.124] [стр.125] [стр.126] [стр.127] [стр.128] [стр.129] [стр.130] [стр.131] [стр.132] [стр.133] [стр.134] [стр.135] [стр.136] [стр.137] [стр.138] [стр.139] [стр.140] [стр.141] [стр.142] [стр.143] [стр.144] [стр.145] [стр.146] [стр.147] [стр.148] [стр.149] [стр.150] [стр.151] [стр.152] [стр.153] [стр.154] [стр.155] [стр.156] [стр.157] [стр.158] [стр.159] [стр.160] [стр.161] [стр.162] [стр.163] [стр.164] [стр.165] [стр.166] [стр.167] [стр.168] [стр.169] [стр.170] [стр.171] [стр.172] [стр.173] [стр.174] [стр.175] [стр.176] [стр.177] [стр.178] [стр.179] [стр.180] [стр.181] [стр.182] [стр.183] [стр.184] [стр.185] [стр.186] [стр.187] [стр.188] [стр.189] [стр.190] [стр.191] [стр.192] [стр.193] [стр.194] [стр.195] [стр.196]