2014年1月5日日曜日

開発環境

計算機プログラムの構造と解釈(Gerald Jay Sussman(原著)、Julie Sussman(原著)、Harold Abelson(原著)、和田 英一(翻訳)、ピアソンエデュケーション、原書: Structure and Interpretation of Computer Programs (MIT Electrical Engineering and Computer Science)(SICP))の5(レジスタ計算機での計算)、5.5(翻訳系)、翻訳系の概観、5.5.6(文面アドレス)、問題 5.42.を解いてみる。

その他参考書籍

問題 5.42.

コード(BBEdit)

compiler5.42.scm

(define (compile exp target linkage compile-time-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 compile-time-env))
        ((assignment? exp)
         (compile-assignment exp target linkage compile-time-env))
        ((definition? exp)
         (compile-definition exp target linkage compile-time-env))
        ((if? exp) (compile-if exp target linkage compile-time-env))
        ((lambda? exp) (compile-lambda exp target linkage compile-time-env))
        ((begin? exp)
         (compile-sequence (begin-actions exp)
                           target
                           linkage
                           compile-time-env))
        ((cond? exp) (compile (cond->if exp) target linkage compile-time-env))
        ((open-code? exp)
         (compile-open-code exp target linkage compile-time-env))
        ((application? exp)
         (compile-application exp target linkage compile-time-env))
        (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)))))))

(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))))))

(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 compile-time-env)
  (let ((lexical-address (find-variable exp compile-time-env)))
    (if (eq? lexical-address 'not-found)
        (end-with-linkage linkage
         (make-instruction-sequence '(env) (list target)
          `((assign ,target
                    (op lookup-variable-value)
                    (const ,exp)
                    (reg env)))))
        (end-with-linkage linkage
         (make-instruction-sequence '(env) (list target)
          `((assign ,target
                    (op lexical-address-lookup)
                    (const ,lexical-address)
                    (reg env))))))))

(define (compile-assignment exp target linkage compile-time-env)
  (let ((var (assignment-variable exp))
        (get-value-code
         (compile (assignment-value exp) 'val 'next compile-time-env)))
    (let ((lexical-address (find-variable var compile-time-env)))
      (if (eq? lexical-address 'not-found)
          (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))))))
          (end-with-linkage linkage
           (preserving '(env)
            get-value-code
            (make-instruction-sequence '(env val) (list target)
             `((perform (op lexical-address-set!)
                        (const ,lexical-address)
                        (reg val)
                        (reg env))
               (assign ,target (const ok))))))))))

(define (compile-definition exp target linkage compile-time-env)
  (let ((var (definition-variable exp))
        (get-value-code
         (compile (definition-value exp) 'val 'next compile-time-env)))
    (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))))))))

(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)))))

(define (compile-if exp target linkage compile-time-env)
  (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 compile-time-env))
            (c-code
             (compile
              (if-consequent exp) target consequent-linkage))
            (a-code
             (compile (if-alternative exp) target linkage compile-time-env)))
        (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))))))

(define (compile-sequence seq target linkage compile-time-env)
  (if (last-exp? seq)
      (compile (first-exp seq) target linkage compile-time-env)
      (preserving '(env continue)
       (compile (first-exp seq) target 'next compile-time-env)
       (compile-sequence (rest-exps seq) target linkage compile-time-env))))

(define (compile-lambda exp target linkage compile-time-env)
  (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 compile-time-env))
       after-lambda))))

(define (compile-lambda-body exp proc-entry compile-time-env)
  (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 (lambda-body exp)
                       'val
                       'return
                       (cons formals compile-time-env)))))

;; 5.38
(define (spread-arguments operand-codes compile-time-env)
  (if (null? operand-codes)
      (make-instruction-sequence
       '((assign arg1 (const ()))
         (assign arg2 (const ()))))
      (let ((op1 (compile (car operand-codes) 'arg1 'next compile-time-env))
            (op2 (compile (cadr operand-codes) 'arg2 'next compile-time-env)))
        (append-instruction-sequences
         op1
         (append-instruction-sequences
          (make-instruction-sequence '(arg1) '() '((save arg1)))
           (append-instruction-sequences
            op2
            (make-instruction-sequence '() '(arg1) '((restore arg1)))))))))

(define (open-code? exp) (memq (car exp) '(= * - +)))

(define (compile-open-code exp target linkage compile-time-env)
  (define (inner exp target linkage)
    (end-with-linkage linkage
     (preserving '(env continue)
      (spread-arguments (operands exp) compile-time-env)
      (make-instruction-sequence '(arg1 arg2) (list target)
       `((assign ,target (op ,(operator exp)) (reg arg1) (reg arg2)))))))
  (define (fold op operand-codes)
    (if (null? (cdr operand-codes))
        (car operand-codes)
        (list op (car operand-codes) (fold op (cdr operand-codes)))))
  (let ((op (operator exp)))
    (if (or (eq? op '+) (eq? op '*))
        (inner (fold op (operands exp)) target linkage)
        (inner exp target linkage))))

(define (compile-application exp target linkage compile-time-env)
  (let ((proc-code (compile (operator exp) 'proc 'next compile-time-env))
        (operand-codes
         (map (lambda (operand) (compile operand 'val 'next compile-time-env))
              (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))))))

(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))))

(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))))

(define all-regs '(env proc val argl continue))

(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))))

;;
(define (lexical-address-lookup lexical-address compile-time-env)
  (let ((val (list-ref (list-ref compile-time-env
                                 (car lexical-address))
                       (cadr lexical-address))))
    (if (eq? val '*unassigned*)
        (error
         "Unassigned variable -- LEXICAL-ADDRESS-LOOKUP" lexical-address)
        val)))

(define (lexical-address-set! exical-address val lcompile-time-env)
  (define (iter-frame frame-n var-n env)
    (cond ((null? env)
           (error
            "Unbound compile-time-env -- LEXICAL-ADDRESS-SET!"
            val lexical-address compile-time-env))
          ((= frame-n 0)
           (iter-variable var-n (car env)))
          (else (iter-frame (- frame-n 1) var-n (cdr env)))))
  (define (iter-variable var-n frame)
    (cond ((null? frame)
           (error
            "Unbound variable - LEXICAL-ADDRESS-SET!"
            val lexical-address compile-time-env))
          ((= var-n 0)
           (set-car! frame val))
          ((iter-variable val (- var-n (cdr frame))))))
  (iter-frame (car lexical-address)
              (cadr lexical-address)
              compile-time-env))

;; 5.41
(define (find-variable var compile-time-env)
  (define (iter frame-n disp-n frame frames)
    (if (null? frame)
        (if (null? frames)
            'not-found
            (iter (+ frame-n 1)
                  0
                  (car frames)
                  (cdr frames)))
        (if (eq? (car frame) var)
            (list frame-n disp-n)
            (iter frame-n
                  (+ disp-n 1)
                  (cdr frame)
                  frames))))
  (if (null? compile-time-env)
      'not-found
      (iter 0 0 (car compile-time-env) (cdr compile-time-env))))

;; 見やすく表示する為に追加
(define (print-compiled compiled)
  (define (iter result)
    (newline)
    (if (and (not (null? result)) (pair? result))
        (let ((first (car result))
              (rest (cdr result)))
          (if (symbol? first)
              (begin (display first)
                     (iter rest))
              (begin (display "  ")
                     (display first)
                     (iter rest))))
        'done))
  (newline)
  (display (car compiled))
  (newline)
  (display (cadr compiled))
  (iter (caddr compiled)))
      

入出力結果(Terminal, REPL(Read, Eval, Print, Loop))

$ scheme
MIT/GNU Scheme running under MacOSX
Type `^C' (control-C) followed by `H' to obtain information about interrupts.

Copyright (C) 2011 Massachusetts Institute of Technology
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Image saved on Saturday October 26, 2013 at 11:02:50 PM
  Release 9.1.1 || Microcode 15.3 || Runtime 15.7 || SF 4.41 || LIAR/C 4.118
  Edwin 3.116

1 ]=> (load "./eval.scm")

;Loading "./eval.scm"... done
;Value: the-global-environment

1 ]=> (load "./compiler5.42.scm")

;Loading "./compiler5.42.scm"... done
;Value: print-compiled

1 ]=> (print-compiled
(compile 
  '((lambda (x y)
      (lambda (a b c d e)
        ((lambda (y z) (* x y z))
         (* a b x)
         (+ c d x))))
    3
    4)
  'val
  'next
  '())
)

(env)
(env proc argl continue val)
  (assign proc (op make-compiled-procedure) (label entry2) (reg env))
  (goto (label after-lambda1))
entry2
  (assign env (op compiled-procedure-env) (reg proc))
  (assign env (op extend-environment) (const (x y)) (reg argl) (reg env))
  (assign val (op make-compiled-procedure) (label entry4) (reg env))
  (goto (reg continue))
entry4
  (assign env (op compiled-procedure-env) (reg proc))
  (assign env (op extend-environment) (const (a b c d e)) (reg argl) (reg env))
  (assign proc (op make-compiled-procedure) (label entry6) (reg env))
  (goto (label after-lambda5))
entry6
  (assign env (op compiled-procedure-env) (reg proc))
  (assign env (op extend-environment) (const (y z)) (reg argl) (reg env))
  (assign arg1 (op lexical-address-lookup) (const (2 0)) (reg env))
  (save arg1)
  (assign arg1 (op lexical-address-lookup) (const (0 0)) (reg env))
  (save arg1)
  (assign arg2 (op lexical-address-lookup) (const (0 1)) (reg env))
  (restore arg1)
  (assign arg2 (op *) (reg arg1) (reg arg2))
  (restore arg1)
  (assign val (op *) (reg arg1) (reg arg2))
  (goto (reg continue))
after-lambda5
  (assign arg1 (op lexical-address-lookup) (const (0 2)) (reg env))
  (save arg1)
  (assign arg1 (op lexical-address-lookup) (const (0 3)) (reg env))
  (save arg1)
  (assign arg2 (op lexical-address-lookup) (const (1 0)) (reg env))
  (restore arg1)
  (assign arg2 (op +) (reg arg1) (reg arg2))
  (restore arg1)
  (assign val (op +) (reg arg1) (reg arg2))
  (assign argl (op list) (reg val))
  (assign arg1 (op lexical-address-lookup) (const (0 0)) (reg env))
  (save arg1)
  (assign arg1 (op lexical-address-lookup) (const (0 1)) (reg env))
  (save arg1)
  (assign arg2 (op lexical-address-lookup) (const (1 0)) (reg env))
  (restore arg1)
  (assign arg2 (op *) (reg arg1) (reg arg2))
  (restore arg1)
  (assign val (op *) (reg arg1) (reg arg2))
  (assign argl (op cons) (reg val) (reg argl))
  (test (op primitive-procedure?) (reg proc))
  (branch (label primitive-branch9))
compiled-branch8
  (assign val (op compiled-procedure-entry) (reg proc))
  (goto (reg val))
primitive-branch9
  (assign val (op apply-primitive-procedure) (reg proc) (reg argl))
  (goto (reg continue))
after-call7
after-lambda3
after-lambda1
  (assign val (const 4))
  (assign argl (op list) (reg val))
  (assign val (const 3))
  (assign argl (op cons) (reg val) (reg argl))
  (test (op primitive-procedure?) (reg proc))
  (branch (label primitive-branch12))
compiled-branch11
  (assign continue (label after-call10))
  (assign val (op compiled-procedure-entry) (reg proc))
  (goto (reg val))
primitive-branch12
  (assign val (op apply-primitive-procedure) (reg proc) (reg argl))
after-call10
;Value: done

1 ]=> ^D
End of input stream reached.
Moriturus te saluto.
$

上から順に、(2 0), (0 0) (0 1)は(* x y z)のx、y、zのアドレス、次の(0 2)、(0 3)、(1 0)は(+ c d x)のc、d、xのアドレス、(0 0)、(0 1)、(1 0)は(* a b x)のa、b、xのアドレスとなっているので、テストが正しいのは確認できた。

テストしたのは上手くいった。

0 コメント:

コメントを投稿