-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path5.42.scm
More file actions
665 lines (614 loc) · 28.9 KB
/
5.42.scm
File metadata and controls
665 lines (614 loc) · 28.9 KB
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
(load "./eval.scm")
;;; make-branchのための手続き
(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)))))
;;; make-compileに必要な機械演算
(define (make-compiled-procedure entry env)
(list 'compiled-procedure entry env))
(define (compiled-procedure? proc)
(tagged-list? proc 'compiled-procedure))
(define (compiled-procedure-entry c-proc) (cadr c-proc))
(define (compiled-procedure-env c-proc) (caddr c-proc))
(define all-regs '(env proc val argl continue))
;; (define (compile exp target linkage)
;; (cond ((self-evaluating? exp)
;; (compile-self-evaluating exp target linkage))
;; ((quoted? exp) (compile-quoted exp target linkage))
;; ((variable? exp)
;; (compile-variable exp target linkage))
;; ((assignment? exp)
;; (compile-assignment exp target linkage))
;; ((definition? exp)
;; (compile-definition exp target linkage))
;; ((if? exp) (compile-if exp target linkage))
;; ((lambda? exp) (compile-lambda exp target linkage))
;; ((begin? exp)
;; (compile-sequence (begin-actions exp)
;; target linkage))
;; ((cond? exp) (compile (cond->if exp) target linkage))
;; ((application? exp)
;; (compile-application exp target linkage))
;; (else
;; (error "Unknown expression type -- COMPILE" exp))))
(define (make-instruction-sequence needs modifies statements)
(list needs modifies statements))
(define (empty-instruction-sequence)
(make-instruction-sequence '() '() '()))
;;; 接続コードの翻訳
(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があるのでlinkageがreturnでinstruction-sequenceがcontinueを変更しても
;;; save, restoreされるので問題ない
(define (end-with-linkage linkage instruction-sequence)
(preserving '(continue)
instruction-sequence
(compile-linkage linkage)))
;;; 単純な式のコンパイル
;;; targetにexpを代入して次の計算への命令を作る
(define (compile-self-evaluating exp target linkage)
(end-with-linkage
linkage
(make-instruction-sequence '() (list target)
`((assign ,target (const ,exp))))))
;;; targetに(cadr exp)を代入して次の計算への命令を作る
(define (compile-quoted exp target linkage)
(end-with-linkage
linkage
(make-instruction-sequence '() (list target)
`((assign ,target (const ,(text-of-quotation exp)))))))
;;; variableを環境から探してきて,見つかった値をtargetに代入して,次の計算への命令を足して返す
;; (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))))))
;;; 代入
;; (define (compile-assignment exp target linkage ct-env)
;; (let ((var (assignment-variable exp))
;; (get-value-code ;valを求めるための命令.
;; (compile (assignment-value exp) 'val 'next ct-env)))
;; (end-with-linkage
;; linkage
;; (preserving '(env) ;valを求める間に環境が変わると困る
;; get-value-code ;代入する値を求め,valに代入される.seq1
;; ;; valに代入された値をvarに代入する.seq2
;; (make-instruction-sequence
;; ;; ;代入するので元々の環境と代入する値を必要とする.
;; '(env val)
;; ;; targetに'okを入れて返すのでtargetは変更する
;; (list target)
;; `((perform (op set-variable-value!)
;; (const ,var)
;; (reg val)
;; (reg env))
;; (assign ,target (const ok))))))))
;;; 定義
(define (compile-definition exp target linkage ct-env)
(let ((var (definition-variable exp)) ;糖衣構文(f x)の場合でもfがvarに束縛される
(get-value-code ;varに束縛する値を求める命令
(compile (definition-value exp) 'val 'next ct-env)))
(end-with-linkage
linkage
(preserving '(env) ;valを求める間に環境が変わると困る
get-value-code
(make-instruction-sequence
;;定義する元々の環境とget-value-codeで求めた値の入っているvalが必要
'(env val)
(list target) ;targetにokを入れて返す
`((perform (op define-variable!)
(const ,var)
(reg val)
(reg env))
(assign ,target (const ok))))))))
;;; 条件式
;;; ifはtestがtrueならfalseに飛ぶ.
;;; そのためlinkageがnextの場合,そのままだとtrueの後にfalseにいってしまう
;;; falseを飛ばすためにtrueの後はafter-ifに飛ぶように
;;; nextの場合はconsequenct-linkageにafter-ifを入れる.
(define (compile-if exp target linkage ct-env)
;; make-branchで書くラベルにIDをつける
(let ((t-branch (make-label 'true-branch))
(f-branch (make-label 'false-branch))
(after-if (make-label 'after-if)))
(let ((consequent-linkage ;nextならafter-ifが入る
(if (eq? linkage 'next) after-if linkage)))
(let ((p-code (compile (if-predicate exp) 'val 'next ct-env)) ;術後を生成する
(c-code
(compile
(if-consequent exp) target consequent-linkage ct-env)) ;consequenct節の命令の生成
(a-code
(compile (if-alternative exp) target linkage ct-env))) ;alterenative節の命令の生成
(preserving '(env continue) ;環境とcontinueは保護
p-code
(append-instruction-sequences ;任意の数の式をつながりのある式として連結する
(make-instruction-sequence '(val) '()
`((test (op false?) (reg val))
(branch (label ,f-branch))))
;; prallelで逐次実行でなくどちらかだけが実行される命令を作る
;; これはどちらが選ばれるか実行時までわからないので
;; neededとmodifiedの和集合をとる.
(parallel-instruction-sequences
(append-instruction-sequences t-branch c-code)
(append-instruction-sequences f-branch a-code))
after-if))))))
;;; 並び
;;; beginやlambdaのbodyで使う
(define (compile-sequence seq target linkage ct-env)
(if (last-exp? seq)
(compile (first-exp seq) target linkage ct-env)
(preserving
'(env continue) ;環境と継続は保護
(compile (first-exp seq) target 'next ct-env) ;そのまま次の命令を実行するのでnext
(compile-sequence (rest-exps seq) target linkage ct-env)))) ;再帰的に命令列を作る
;;; lambda式
;;; target(val)にコンパイルした式のラベルを束縛してlambda-linkageにジャンプ
;;; 実際に式を呼び出すときにcompile-lambda-bodyで作るラベルにジャンプし,処理をする
(define (compile-lambda exp target linkage ct-env)
(let ((proc-entry (make-label 'entry)) ;コンパイルされた式はこのentry-idのラベルで処理される
(after-lambda (make-label 'after-lambda)))
(let ((lambda-linkage
(if (eq? linkage 'next) after-lambda linkage)))
(append-instruction-sequences
;; tack-onでend-with-linkageにcompile-lambda-bodyを連結.
;; neededとmodifiedはend-with-linkageのほうを使う
(tack-on-instruction-sequence
(end-with-linkage
lambda-linkage
(make-instruction-sequence
'(env) (list target)
`((assign ,target
(op make-compiled-procedure)
(label ,proc-entry)
(reg env)))))
(compile-lambda-body exp proc-entry ct-env))
after-lambda))))
;;; コンパイルした手続きが実際に処理をするラベルの中身を作る
(define (compile-lambda-body exp proc-entry ct-env)
(let ((formals (lambda-parameters exp))) ;lambdaの引数はformalsに束縛
(append-instruction-sequences
(make-instruction-sequence
'(env proc argl) '(env)
;; 実際の処理をするラベル
`(,proc-entry
(assign env (op compiled-procedure-env) (reg proc))
(assign env ;ここで仮引数と実引数で環境を拡張
(op extend-environment)
(const ,formals)
(reg argl)
(reg env))))
;; lambdaのbodyは式が複数のことがあるのでcompile-sequence
;; 呼び出し元に値を返さないと行けないのでlinkageはreturn
(compile-sequence (lambda-body exp) 'val 'return (cons formals ct-env)))))
;;; apply
(define (compile-application exp target linkage ct-env)
(let (
;; operatorをコンパイルしたら次はoperandの評価をしなければいけないのでnext
(proc-code (compile (operator exp) 'proc 'next ct-env))
;; operandは複数なのでそれぞれcompileしてリストにして保持
(operand-codes
(map (lambda (operand) (compile operand 'val 'next ct-env))
(operands exp))))
(preserving
'(env continue)
proc-code ;最初にoperatorを確定させる
(preserving
'(proc continue)
(construct-arglist operand-codes) ;operandを評価してarglに代入するための命令の生成
(compile-procedure-call target linkage))))) ;
;;; compile-applicationでoperand-codesはコンパイル済みなのでそれをarglに入れるための命令を生成
(define (construct-arglist operand-codes)
;; reverseして連結していくので右から左に評価することになる
(let ((operand-codes (reverse operand-codes)))
(if (null? operand-codes)
;; 引数がない場合はarglに'()を代入
(make-instruction-sequence
'() '(argl)
`((assign argl (const ()))))
(let ((code-to-get-last-arg ;最後のoperandが生成する命令
(append-instruction-sequences
(car operand-codes)
(make-instruction-sequence
'(val) '(argl) ;arglの初期化が必要なのでこれだけ特別に処理
`((assign argl (op list) (reg val)))))))
(if (null? (cdr operand-codes))
code-to-get-last-arg ;cdrがnullなら最後のoperand
;; まだoperandが残っていればこちら
(preserving
'(env) ;環境は保持
code-to-get-last-arg ;引数の最後(reverseしているので先頭)からつなげる.
(code-to-get-rest-args
(cdr operand-codes))))))))
;;; last-arg以外はここで処理する
;;; operand-codesはコンパイル済み
;;; arglには既に最後の引数が代入されているのでそこに先頭(reverseしてるので後ろ)から代入していく
(define (code-to-get-rest-args operand-codes)
(let ((code-for-next-arg ;先頭
(preserving
'(argl)
(car operand-codes) ;valに先頭の要素のコンパイル結果を代入する命令
(make-instruction-sequence
'(val argl) '(argl)
'((assign argl ;valに入った(car operand)の値をarglに代入
(op cons) (reg val) (reg argl)))))))
(if (null? (cdr operand-codes))
code-for-next-arg
(preserving
'(env)
code-for-next-arg
(code-to-get-rest-args (cdr operand-cods))))))
;;; operator, operandsを評価する命令を作った後に呼ばれる
;;; この時点でprocにはoperatorのシンボル, arglにはoperandsが入っている
(define (compile-procedure-call target linkage)
(let ((primitive-branch (make-label 'primitive-branch))
(compiled-branch (make-label 'compiled-branch))
(after-call (make-label 'after-call)))
(let ((compiled-linkage
(if (eq? linkage 'next) after-call linkage)))
(append-instruction-sequences
(make-instruction-sequence
'(proc) '()
`((test (op primitive-procedure?) (reg proc))
(branch (label ,primitive-branch))))
;; compiled-branchかprimitive-branchのどちらかだけが実行されるのでparallel
(parallel-instruction-sequences
(append-instruction-sequences
compiled-branch
;; ここでtargetとlinkageに合わせた命令を生成
(compile-proc-appl target compiled-linkage))
(append-instruction-sequences
primitive-branch
(end-with-linkage
linkage
(make-instruction-sequence
'(proc argl) (list target)
`((assign ,target
(op apply-primitive-procedure)
(reg proc)
(reg argl)))))))
after-call))))
;;; 手続きの採用
(define (compile-proc-appl target linkage)
(cond (
;; linkageがreturnでなければlinkageにはいったlabelが値を返す場所
(and (eq? target 'val) (not (eq? linkage 'return)))
(make-instruction-sequence
'(proc) all-regs
`((assign continue (label ,linkage)) ;計算した値をvalに入れたらこのlinkageにジャンプ
(assign val (op compiled-procedure-entry)
(reg proc))
(goto (reg val)))))
;; targetがvalでないのでproc-returnでtargetにvalを代入しないといけない
((and (not (eq? target 'val))
(not (eq? linkage 'return)))
(let ((proc-return (make-label 'proc-return)))
(make-instruction-sequence
'(proc) all-regs
`((assign continue (label ,proc-return))
(assign val (op compiled-procedure-entry)
(reg proc))
(goto (reg val))
,proc-return
(assign ,target (reg val)) ;targetがvalでないので,ここでtargetにvalを代入
(goto (label ,linkage))))))
;; targetがvalでreturnなら計算の後,continueに行けばいいので余計な処理はない
((and (eq? target 'val) (eq? linkage 'return))
(make-instruction-sequence
'(proc continue) all-regs
`((assign val (op compiled-procedure-entry)
(reg proc))
(goto (reg val)))))
((and (not (eq? target 'val)) (eq? linkage 'return))
(error "return linkage, target not val -- COMPILE" target))))
;;; 命令列の組み合わせ
(define (registers-needed s)
(if (symbol? s) '() (car s)))
(define (registers-modified s)
(if (symbol? s) '() (cadr s)))
(define (statements s)
(if (symbol? s) (list s) (caddr s)))
(define (needs-register? seq reg)
(memq reg (registers-needed seq)))
(define (modifies-register? seq reg)
(memq reg (registers-modified seq)))
;;; neededとmodifiedをうまく合成して新しい命令列を作る
;;; これは人つながりの命令にする.
(define (append-instruction-sequences . seqs)
(define (append-2-sequences seq1 seq2)
(make-instruction-sequence
;; needed
(list-union (registers-needed seq1)
(list-difference (registers-needed seq2) ;seq1で変更してseq2がそれを必要とする
(registers-modified seq1))) ;ならseq1の時点では必要ない
(list-union (registers-modified seq1)
(registers-modified seq2))
(append (statements seq1) (statements seq2))))
(define (append-seq-list seqs)
(if (null? seqs)
(empty-instruction-sequence)
(append-2-sequences (car seqs) ;nullじゃなければこっち.
(append-seq-list (cdr seqs)))))
(append-seq-list seqs))
;;; 集合演算
(define (list-union s1 s2)
(cond ((null? s1) s2)
((memq (car s1) s2) (list-union (cdr s1) s2))
(else (cons (car s1) (list-union (cdr s1) s2)))))
(define (list-difference s1 s2)
(cond ((null? s1) '())
((memq (car s1) s2) (list-difference (cdr s1) s2))
(else (cons (car s1)
(list-difference (cdr s1) s2)))))
;;; regsの中にseq1で変更してseq2でしようするレジスタがあれば
;;; seq1の前後でsave, restoreする命令を作る.
(define (preserving regs seq1 seq2)
(if (null? regs)
(append-instruction-sequences seq1 seq2)
(let ((first-reg (car regs))) ;first-regが
(if (and (needs-register? seq2 first-reg) ;seq2に必要なレジスタで
(modifies-register? seq1 first-reg)) ;seq1が変更するレジスタなら
(preserving
(cdr regs)
(make-instruction-sequence
;; needs ここでsaveするのでfirst-regが必要になるのでlist-union
(list-union (list first-reg)
(registers-needed seq1))
;; modify saveしてのseq2の前にrestoreするのでseq2から見ればfirst-reg変更無し
(list-difference (registers-modified seq1)
(list first-reg))
;; statements 条件を満たすfirst-regの場合はseq1をsaveとrestoreで挟む
(append `((save ,first-reg))
(statements seq1)
`((restore ,first-reg))))
seq2)
(preserving (cdr regs) seq1 seq2)))))
;;; seqとbodyとbody-seqをつなげる.neededとmodifiedはseqのまま
(define (tack-on-instruction-sequence seq body-seq)
(make-instruction-sequence
(registers-needed seq)
(registers-modified seq)
(append (statements seq) (statements body-seq))))
;;; neededとmodifiedは和集合を取る.
;;; ifのconsequentとalternative, や
;;; 手続きのcompiled, primitiveの違いのようにどちらかだけが実行されるようなラベルを作るときに使う
(define (parallel-instruction-sequences seq1 seq2)
(make-instruction-sequence
(list-union (registers-needed seq1)
(registers-needed seq2))
(list-union (registers-modified seq1)
(registers-modified seq2))
(append (statements seq1) (statements seq2))))
;; ;;; 5.38
(define (open-code? exp)
(memq (car exp) '(= * - +)))
;; (define (compile exp target linkage)
;; (cond ((self-evaluating? exp)
;; (compile-self-evaluating exp target linkage))
;; ((quoted? exp) (compile-quoted exp target linkage))
;; ((variable? exp)
;; (compile-variable exp target linkage))
;; ((assignment? exp)
;; (compile-assignment exp target linkage))
;; ((definition? exp)
;; (compile-definition exp target linkage))
;; ((if? exp) (compile-if exp target linkage))
;; ((lambda? exp) (compile-lambda exp target linkage))
;; ((begin? exp)
;; (compile-sequence (begin-actions exp)
;; target linkage))
;; ((cond? exp) (compile (cond->if exp) target linkage))
;; ((open-code? exp) ;open-code?でdispatch
;; (compile-open-code exp target linkage))
;; ((application? exp)
;; (compile-application exp target linkage))
;; (else
;; (error "Unknown expression type -- COMPILE" exp))))
;; (define (spread-arguments operand) ;それぞれコンパイルしてリストにして返す
;; (let ((co-arg1 (compile (car operand) 'arg1 'next))
;; (co-arg2 (compile (cadr operand) 'arg2 'next)))
;; (list co-arg1 co-arg2)))
;; (define (compile-open-code exp target linkage)
;; (if (= (length exp) 3)
;; (let ((proc (operator exp))
;; (args (spread-arguments (operands exp))))
;; (end-with-linkage linkage
;; (append-instruction-sequences
;; (car args)
;; ;; co-arg2がopen-code式だった場合にarg1が上書きされるので退避させる.
;; (preserving
;; '(arg1)
;; (cadr args)
;; (make-instruction-sequence
;; '(arg1 arg2)
;; (list target)
;; `((assign ,target (op ,proc) (reg arg1) (reg arg2))))))))
;; (error "require 2 operand" exp)))
;;; 5.38-d
(define (compile-open-code exp target linkage ct-env)
(cond ((= (length exp) 3)
(compile-open-code-operand exp target linkage ct-env))
((or (tagged-list? exp '+)
(tagged-list? exp '*))
(compile-open-code-operand-2
(operator exp) (operands exp) target linkage ct-env))
(error "invalid application: " exp)))
(define (compile-open-code-operand exp target linkage ct-env)
(let ((proc (operator exp))
(args (spread-arguments (operands exp) ct-env)))
(end-with-linkage linkage
(append-instruction-sequences
(car args)
;; co-arg2がopen-code式だった場合にarg1が上書きされるので退避させる.
(preserving
'(arg1)
(cadr args)
(make-instruction-sequence
'(arg1 arg2)
(list target)
`((assign ,target (op ,proc) (reg arg1) (reg arg2)))))))))
;;; operandが無くてprocが+なら1を,*なら0をtargetに代入.
;;; operandが一つだけならそのままの値をtargetに入れる.
;;; operandが3つ以上なら
(define (compile-open-code-operand-2 proc operands target linkage ct-env)
(cond ((null? operands)
(if (eq? proc '+)
(compile-self-evaluating 0 target linkage) ;+なら0
(compile-self-evaluating 1 target linkage))) ;*なら1
((null? (cdr operands))
(end-with-linkage linkage
(compile (car operand) target 'next ct-env)))
(else ;引数が3つ以上ならこちらで処理
(let ((operand (spread-arguments operands ct-env)))
(end-with-linkage
linkage
(append-instruction-sequences
(car operand)
(compile-open-code-operand-3 proc (cdr operand) target)))))))
;;; ここに渡されるseqはコンパイルされた引数のリスト.
;;; last-seqだとarg1を保護しながら最後の引数をarg2に代入して
;;; 最後にarg1, arg2をprocした結果をvalに代入する.
;;; まだ残っているときはarg1を保護しながら引数をarg2に代入して
;;; arg1とarg2をprocした結果をarg1に代入する
(define (compile-open-code-operand-3 proc seq target)
(if (last-seq? seq)
(preserving
'(arg1)
(car seq)
(make-instruction-sequence
'(arg1 arg2)
(list target)
`((assin ,target (op ,proc) (reg arg1) (reg arg2)))))
(append-instruction-sequences
(preserving
'(arg1)
(car seq)
(make-instruction-sequence '(arg1 arg2) '(arg1)
`((assign arg1 (op ,proc) (reg arg1) (reg arg2)))))
(compile-open-code-operand-3 proc (cdr seq) target))))
;;; operandが0または1以外の時はここでcompileする.
;;; 一つ目だけarg1に代入し,残りはarg2に代入する.
(define (spread-arguments operand ct-env)
(let iter ((operand (cdr operand))
(result (list (compile (car operand) 'arg1 'next ct-env))))
(if (null? operand)
(reverse result)
(iter (cdr operand)
(cons (compile (car operand) 'arg2 'next ct-env) result)))))
(define (last-seq? seq)
(null? (cdr seq)))
;;; 5.39
;;; 文面アドレスを使って変数の値を探す
(define (lexical-address-lookup lex-add r-env)
(let ((frame (frame-values (list-ref r-env (car lex-add)))))
(let ((val (list-ref frame (cadr lex-add))))
(if (eq? val '*unassigned*)
(error "*Unassigned* variable")
val))))
;;; 文面アドレスにある値を変更する
(define (lexical-address-set! lex-add val r-env)
(let ((frame (frame-values (list-ref r-env (car lex-add)))))
(let ((target (list-ref frame (cadr lex-add))))
(set! target val)
'ok)))
;;; 5.41
(define (find-variable var ct-env)
(define (env-loop frame-address env)
(define (scan variable-address frame)
(cond ((null? frame)
(env-loop (+ frame-address 1) (enclosing-environment env)))
((eq? (car frame) var)
(list frame-address variable-address))
(else
(scan (+ variable-address 1) (cdr frame)))))
(if (null? env)
'not-found
(let ((frame (first-frame env)))
(scan 0 frame))))
(env-loop 0 ct-env))
;;; 5.42
(define (compile-variable exp target linkage ct-env)
(let ((address (find-variable exp ct-env)))
(end-with-linkage
linkage
(if (eq? address 'not-found)
(make-instruction-sequence
'() (list target)
;; targetなら変更しても問題ないので一時的に帯域環境を入れる
`((assign ,target (op get-global-environment))
(assign ,target
(op lookup-variable-value)
(const ,exp)
(reg ,target))))
(make-instruction-sequence
'() (list target)
`((assign ,target
(op lexical-address-lookup)
(const ,address)
(const ,ct-env))))))))
(define (compile-assignment exp target linkage ct-env)
(let ((var (assignment-variable exp))
(get-value-code ;valを求めるための命令.
(compile (assignment-value exp) 'val 'next ct-env)))
(let ((address (find-variable var ct-env)))
(end-with-linkage
linkage
(append-instruction-sequences
get-value-code ;代入する値を求め,valに代入される.seq1
;; valに代入された値をvarに代入する.seq2
(if (eq? address 'not-found)
(make-instruction-sequence
'(val)
`(env ,target)
;; 一度targetにglobal-environmentを代入してからsetする
`((assign env (op get-global-environment))
(perform (op set-variable-value!)
(const ,var)
(reg val)
(reg env))
(assign ,target (const ok))))
(make-instruction-sequence
'(val)
(list target)
`((perform (op lexical-address-set!)
(const ,address)
(reg val)
(const ,ct-env))
(assign ,target (const ok))))))))))
(define (compile exp target linkage ct-env)
(cond ((self-evaluating? exp)
(compile-self-evaluating exp target linkage))
((quoted? exp) (compile-quoted exp target linkage))
((variable? exp)
(compile-variable exp target linkage ct-env))
((assignment? exp)
(compile-assignment exp target linkage ct-env))
((definition? exp)
(compile-definition exp target linkage ct-env))
((if? exp) (compile-if exp target linkage ct-env))
((lambda? exp) (compile-lambda exp target linkage ct-env))
((begin? exp)
(compile-sequence (begin-actions exp)
target linkage ct-env))
((cond? exp) (compile (cond->if exp) target linkage ct-env))
((open-code? exp) ;open-code?でdispatch
(compile-open-code exp target linkage ct-env))
((application? exp)
(compile-application exp target linkage ct-env))
(else
(error "Unknown expression type -- COMPILE" exp))))