SICP 読み (347) 5.5 翻訳系
問題 5.44
と言いつつ 5.43 なんですが、昨晩の解はダウト。lexical-address-lookup なソレが出力されておりません。日曜囲碁を見つつ問題処理。
ちなみに 5.42 な解では
gosh> (compile '(lambda (x) x) 'val 'return '()) ((env continue) (val) ((assign val (op make-compiled-procedure) (label entry23) (reg env)) #0=(goto (reg continue)) entry23 (assign env (op compiled-procedure-env) (reg proc)) (assign env (op extend-environment) (const (x)) (reg argl) (reg env)) (assign val (op lexical-address-lookup) (const (0 0)) (reg env)) #0# after-lambda24)) gosh>
なんですが、昨晩な処理系だと
gosh> (compile '(lambda (x) x) 'val 'return '()) ((env continue) (val) ((assign val (op make-compiled-procedure) (label entry28) (reg env)) #0=(goto (reg continue)) entry28 (assign env (op compiled-procedure-env) (reg proc)) (assign env (op extend-environment) (const (x)) (reg argl) (reg env)) (assign val (op lookup-variable-value) (const x) (op get-global-environment)) #0# after-lambda29)) gosh>
で、確認してみたら compile-lambda-body の末端部分のナニが
(compile-sequence (scan-out-defines (lambda-body exp)) 'val 'return cenv))))
となっているのを発見。ここで cenv 拡張しないと駄目。これでは lexical address な命令は死んでも出てこない。
これで 5.43 の最終版が出来たのだろうか。
gosh> (compile '(define (length items)
(define (length-iter a count)
(if (null? a)
count
(length-iter (cdr a) (+ 1 count))))
(length-iter items 0)) 'val 'return '())
((env continue) (val)
((assign val #0=(op make-compiled-procedure) (label entry3) . #1=((reg env)))
(goto (label after-lambda4))
entry3
#2=(assign env (op compiled-procedure-env) (reg proc))
(assign env #3=(op extend-environment) (const (items)) . #4=((reg argl) (reg env)))
(assign proc #0# (label entry5) . #1#)
(goto (label after-lambda6))
entry5
#2#
(assign env #3# (const (length-iter)) . #4#)
(assign val #0# (label entry7) . #1#)
(goto (label after-lambda8))
entry7
#2#
(assign env #3# (const (a count)) . #4#)
(save continue)
(save env)
(assign proc #5=(op lookup-variable-value) (const null?) . #6=((op get-global-environment)))
(assign val #7=(op lexical-address-lookup) (const (0 0)) . #8=((reg env)))
#9=(assign argl (op list) (reg val))
#10=(test (op primitive-procedure?) (reg proc))
(branch (label primitive-branch12))
compiled-branch13
(assign continue (label after-call14))
#11=(assign val (op compiled-procedure-entry) (reg proc))
#12=(goto (reg val))
primitive-branch12
(assign val . #13=((op apply-primitive-procedure) (reg proc) (reg argl)))
after-call14
(restore env)
(restore continue)
(test (op false?) (reg val))
(branch (label false-branch10))
true-branch9
(assign val #7# (const (0 1)) . #8#)
#14=(goto (reg continue))
false-branch10
(assign proc #7# (const (1 0)) . #8#)
(save continue)
(save proc)
(save env)
(assign proc #5# (const +) . #6#)
(assign val #7# (const (0 1)) . #8#)
#9#
(assign val (const 1))
#15=(assign argl (op cons) (reg val) (reg argl))
#10#
(branch (label primitive-branch18))
compiled-branch19
(assign continue (label after-call20))
#11#
#12#
primitive-branch18
(assign val . #13#)
after-call20
#9#
(restore env)
(save argl)
(assign proc #5# (const cdr) . #6#)
(assign val #7# (const (0 0)) . #8#)
#9#
#10#
(branch (label primitive-branch15))
compiled-branch16
(assign continue (label after-call17))
#11#
#12#
primitive-branch15
(assign val . #13#)
after-call17
(restore argl)
#15#
(restore proc)
(restore continue)
#10#
(branch (label primitive-branch21))
compiled-branch22
#16=(assign val (op compiled-procedure-entry) (reg proc))
#17=(goto (reg val))
primitive-branch21
(assign val . #13#)
#14#
after-call23
after-if11
after-lambda8
(perform (op lexicall-address-set!) (const (0 0)) (reg val) (reg env))
(assign val (const ok))
(assign proc #7# (const (0 0)) . #8#)
(assign val (const 0))
#9#
(assign val #7# (const (1 0)) . #8#)
#15#
#10#
(branch (label primitive-branch24))
compiled-branch25
#16#
#17#
primitive-branch24
(assign val . #13#)
#14#
after-call26
after-lambda6
(assign val (const *unassigned*))
#9#
#10#
(branch (label primitive-branch27))
compiled-branch28
#16#
#17#
primitive-branch27
(assign val . #13#)
#14#
after-call29
after-lambda4
(perform (op define-variable!) (const length) (reg val) (reg env))
(assign val (const ok))
#14#))
gosh> 整形は略 (を
一応 5.43 の最終版とゆーコトで ch5-compiler.scm も以下に。
;;;;COMPILER FROM SECTION 5.5 OF
;;;; STRUCTURE AND INTERPRETATION OF COMPUTER PROGRAMS
;;;;Matches code in ch5.scm
;;;;This file can be loaded into Scheme as a whole.
;;;;**NOTE**This file loads the metacircular evaluator's syntax procedures
;;;; from section 4.1.2
;;;; You may need to change the (load ...) expression to work in your
;;;; version of Scheme.
;;;;Then you can compile Scheme programs as shown in section 5.5.5
;;**implementation-dependent loading of syntax procedures
(load "ch5-syntax.scm") ;section 4.1.2 syntax procedures
(define (scan-out-defines exp)
(define (make-set init set exp)
(if (null? exp)
(if (null? init)
'()
(append (list 'let init) set))
(if (definition? (car exp))
(make-set (append init (list (list (definition-variable (car exp))
''*unassigned*)))
(append set (list (list 'set!
(definition-variable (car exp))
(definition-value (car exp)))))
(cdr exp))
(make-set init set (cdr exp)))))
(define (remove-define l exp)
(cond ((null? exp) l)
((definition? (car exp))
(remove-define l (cdr exp)))
(else
(remove-define (append l (list (car exp))) (cdr exp)))))
(let ((result (make-set '() '() exp)))
(if (null? result)
exp
(list (append result (remove-define '() exp))))))
;;;SECTION 5.5.1
(define (lexical-address-lookup add env)
(let ((result (list-ref (cdr (list-ref env (car add))) (cadr add))))
(if (eq? '*unassigned* result)
(error "variable is unassigned -- " result)
result)))
(define (lexical-address-set! add val env)
(define (scan n l)
(if (= n 0)
(set-car! l val)
(scan (- n 1) (cdr l))))
(scan (cadr add) (cdr (list-ref env (car add)))))
(define (find-variable var env)
(define (f-search i env)
(define (lexical-add-out j l)
(cond ((null? l) (f-search (+ i 1) (cdr env)))
((eq? var (car l)) (list i j))
(else
(lexical-add-out (+ j 1) (cdr l)))))
(if (null? env)
'not-found
(lexical-add-out 0 (car env))))
(f-search 0 env))
(define (compile exp target linkage cenv)
(cond ((self-evaluating? exp)
(compile-self-evaluating exp target linkage))
((quoted? exp) (compile-quoted exp target linkage))
((variable? exp)
(compile-variable exp target linkage cenv))
((assignment? exp)
(compile-assignment exp target linkage cenv))
((definition? exp)
(compile-definition exp target linkage cenv))
((if? exp) (compile-if exp target linkage cenv))
((lambda? exp) (compile-lambda exp target linkage cenv))
((begin? exp)
(compile-sequence (begin-actions exp)
target
linkage
cenv))
((cond? exp) (compile (cond->if exp) target linkage cenv))
((let? exp) (compile (let->combination exp) target linkage cenv))
((application? exp)
(compile-application exp target linkage cenv))
(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 '() '() '()))
;;;SECTION 5.5.2
;;;linkage code
(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)))))))
(define (end-with-linkage linkage instruction-sequence)
(preserving '(continue)
instruction-sequence
(compile-linkage linkage)))
;;;simple expressions
(define (compile-self-evaluating exp target linkage)
(end-with-linkage linkage
(make-instruction-sequence '() (list target)
`((assign ,target (const ,exp))))))
(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))))))
(define (compile-variable exp target linkage cenv)
(let ((lexical-add (find-variable exp cenv)))
(end-with-linkage
linkage
(make-instruction-sequence
'(env)
(list target)
(if (eq? 'not-found lexical-add)
`((assign ,target
(op lookup-variable-value)
(const ,exp)
(op get-global-environment)))
`((assign ,target
(op lexical-address-lookup)
(const ,lexical-add)
(reg env))))))))
(define (compile-assignment exp target linkage cenv)
(let ((var (assignment-variable exp))
(get-value-code
(compile (assignment-value exp) 'val 'next cenv)))
(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-assignment exp target linkage cenv)
(let ((var (assignment-variable exp))
(get-value-code
(compile (assignment-value exp) 'val 'next cenv)))
(let ((lexical-add (find-variable var cenv)))
(end-with-linkage
linkage
(preserving
'(env)
get-value-code
(make-instruction-sequence
'(env val)
(list target)
(if (eq? 'not-found lexical-add)
`((perform (op set-variable-value!)
(const ,var)
(reg val)
(reg env))
(assign ,target (const ok)))
`((perform (op lexicall-address-set!)
(const ,lexical-add)
(reg val)
(reg env))
(assign ,target (const ok))))))))))
(define (compile-definition exp target linkage cenv)
(let ((var (definition-variable exp))
(get-value-code
(compile (definition-value exp) 'val 'next cenv)))
(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))))))))
;;;conditional expressions
;;;labels (from footnote)
(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)))))
;; end of footnote
(define (compile-if exp target linkage cenv)
(let ((t-branch (make-label 'true-branch))
(f-branch (make-label 'false-branch))
(after-if (make-label 'after-if)))
(let ((consequent-linkage
(if (eq? linkage 'next) after-if linkage)))
(let ((p-code (compile (if-predicate exp) 'val 'next cenv))
(c-code
(compile
(if-consequent exp) target consequent-linkage cenv))
(a-code
(compile (if-alternative exp) target linkage cenv)))
(preserving '(env continue)
p-code
(append-instruction-sequences
(make-instruction-sequence '(val) '()
`((test (op false?) (reg val))
(branch (label ,f-branch))))
(parallel-instruction-sequences
(append-instruction-sequences t-branch c-code)
(append-instruction-sequences f-branch a-code))
after-if))))))
;;; sequences
(define (compile-sequence seq target linkage cenv)
(if (last-exp? seq)
(compile (first-exp seq) target linkage cenv)
(preserving '(env continue)
(compile (first-exp seq) target 'next cenv)
(compile-sequence (rest-exps seq) target linkage cenv))))
;;;lambda expressions
(define (compile-lambda exp target linkage cenv)
(let ((proc-entry (make-label 'entry))
(after-lambda (make-label 'after-lambda)))
(let ((lambda-linkage
(if (eq? linkage 'next) after-lambda linkage)))
(append-instruction-sequences
(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 cenv))
after-lambda))))
(define (compile-lambda-body exp proc-entry cenv)
(let ((formals (lambda-parameters exp)))
(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))))
(compile-sequence (scan-out-defines (lambda-body exp)) 'val 'return (cons formals cenv)))))
;;;SECTION 5.5.3
;;;combinations
(define (compile-application exp target linkage cenv)
(let ((proc-code (compile (operator exp) 'proc 'next cenv))
(operand-codes
(map (lambda (operand) (compile operand 'val 'next cenv))
(operands exp))))
(preserving '(env continue)
proc-code
(preserving '(proc continue)
(construct-arglist operand-codes)
(compile-procedure-call target linkage)))))
(define (construct-arglist operand-codes)
(let ((operand-codes (reverse operand-codes)))
(if (null? operand-codes)
(make-instruction-sequence '() '(argl)
'((assign argl (const ()))))
(let ((code-to-get-last-arg
(append-instruction-sequences
(car operand-codes)
(make-instruction-sequence '(val) '(argl)
'((assign argl (op list) (reg val)))))))
(if (null? (cdr operand-codes))
code-to-get-last-arg
(preserving '(env)
code-to-get-last-arg
(code-to-get-rest-args
(cdr operand-codes))))))))
(define (code-to-get-rest-args operand-codes)
(let ((code-for-next-arg
(preserving '(argl)
(car operand-codes)
(make-instruction-sequence '(val argl) '(argl)
'((assign 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-codes))))))
;;;applying procedures
(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))))
(parallel-instruction-sequences
(append-instruction-sequences
compiled-branch
(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))))
;;;applying compiled procedures
(define (compile-proc-appl target linkage)
(cond ((and (eq? target 'val) (not (eq? linkage 'return)))
(make-instruction-sequence '(proc) all-regs
`((assign continue (label ,linkage))
(assign val (op compiled-procedure-entry)
(reg proc))
(goto (reg 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))
(goto (label ,linkage))))))
((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))))
;; footnote
(define all-regs '(env proc val argl continue))
;;;SECTION 5.5.4
(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)))
(define (append-instruction-sequences . seqs)
(define (append-2-sequences seq1 seq2)
(make-instruction-sequence
(list-union (registers-needed seq1)
(list-difference (registers-needed seq2)
(registers-modified 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)
(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)))))
(define (preserving regs seq1 seq2)
(if (null? regs)
(append-instruction-sequences seq1 seq2)
(let ((first-reg (car regs)))
(if (and (needs-register? seq2 first-reg)
(modifies-register? seq1 first-reg))
(preserving (cdr regs)
(make-instruction-sequence
(list-union (list first-reg)
(registers-needed seq1))
(list-difference (registers-modified seq1)
(list first-reg))
(append `((save ,first-reg))
(statements seq1)
`((restore ,first-reg))))
seq2)
(preserving (cdr regs) seq1 seq2)))))
(define (tack-on-instruction-sequence seq body-seq)
(make-instruction-sequence
(registers-needed seq)
(registers-modified seq)
(append (statements seq) (statements body-seq))))
(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))))
'(COMPILER LOADED)次の 5.38 な機能盛り込みは別途。