SICP 読み (252) 5.2 レジスタ計算機での計算

練習問題に着手。

問題 5.9

最初は make-assign らへんで、とか思っていたのですが op な式で label な要素を却下となると、make-operation-exp でチェックした方が良さげ。

(define (make-operation-exp exp machine labels operations)
  (let ((op (lookup-prim (operation-exp-op exp) operations))
        (aprocs
         (map (lambda (e)
		(if (label-exp? e)
		    (error "cannot place label element -- ASSEMBLE" e)
		    (make-primitive-exp e machine labels)))
              (operation-exp-operands exp))))
    (lambda ()
      (apply op (map (lambda (p) (p)) aprocs)))))

みたいな感じになるでしょうか。試験は make-opration-exp なテストに assert-error を入れただけなので略。って本来なら op 指定可能な命令は全部試験すべきなのでしょうか。UT とゆー意味では、上記手続きの試験ができてれば OK なんでしょうが ...

問題 5.10

branch の逆を作ってみる事に。flag レジスタが偽だったら jmp てのは楽ちんそう。(を
様式としては

(n-branch (label ))

みたいな感じ。解析な手続きは以下で良いはず。

(define (make-n-branch inst machine labels flag pc)
  (let ((dest (branch-dest inst)))
    (if (label-exp? dest)
        (let ((insts
               (lookup-label labels (label-exp-label dest))))
          (lambda ()
            (if (not (get-contents flag))
                (set-contents! pc insts)
                (advance-pc pc))))
        (error "Bad BRANCH instruction -- ASSEMBLE" inst))))

すごいテキトーだなぁ。他に修正が必要なのは

  • make-execution-procedure

くらい??
# ってコイツの試験書いてないな (とほほ

むむ、新たな構文の追加や修正は 5.2.3 なソレ達に閉じてるんですか。凄いな。試験も書くのが楽だな。

  ("make-n-branch"
   ("error"
    (assert-error (lambda () (make-n-branch '(branch (reg a)) '() '() '())))
    )

   ("flag is #f"
    (let ((m (make-new-machine))
	  (l '(test-b
	       (test (op =) (reg b) (const 0))
	       (branch (label gcd-done))
	       (assign t (op rem) (reg a) (reg b))
	       (assign a (reg b))
	       (assign b (reg t))
	       (goto (label test-b))
	       gcd-done)))
      (extract-labels 
       l
       (lambda (insts labels)
	 (set-contents! ((m 'get-register) 'flag) #f)
	 (let ((thunk (make-n-branch '(branch (label test-b))
				     m
				     labels
				     ((m 'get-register) 'flag)
				     ((m 'get-register) 'pc))))
	   (thunk)
	   (assert-equal '(test (op =) (reg b) (const 0))
			 (car (car (get-contents ((m 'get-register) 'pc)))))
	   )
	 ))
      )
    )


   ("flag is #t"
    (let ((m (make-new-machine))
	  (l '(test-b
	       (test (op =) (reg b) (const 0))
	       (branch (label gcd-done))
	       (assign t (op rem) (reg a) (reg b))
	       (assign a (reg b))
	       (assign b (reg t))
	       (goto (label test-b))
	       gcd-done)))
      (extract-labels 
       l
       (lambda (insts labels)
	 (set-contents! ((m 'get-register) 'flag) #t)
	 (set-contents! ((m 'get-register) 'pc) '(() (gcd-done ())))
	 (let ((thunk (make-n-branch '(branch (label test-b))
				     m
				     labels
				     ((m 'get-register) 'flag)
				     ((m 'get-register) 'pc))))
	   (thunk)
	   (assert-equal 'gcd-done
			 (car (car (get-contents ((m 'get-register) 'pc)))))
	   )
	 ))
      )
    )
   )

あと、make-execution-procedure の試験も書いとくか。と言いつつ test な試験を書いていた所、以下のようなメセジを出力して異常終了。

Error occurred in test
*** ERROR: invalid application: (#<<test> 0x83fb788>)

試験は以下。(一部のみ)

   ("test"
    (let ((m (make-new-machine)))
      (for-each (lambda (register-name)
                  ((m 'allocate-register) register-name))
                '(a b t))
      ((m 'install-operations) (list (list 'rem remainder) (list '= =)))
      (let ((thunk (make-execution-procedure
		    '(test (op =) (const 1) (const 1))
		    '()
		    m
		    ((m 'get-register) 'pc)
		    ((m 'get-register) 'flag)
		    (m 'stack)
		    (m 'operations))))
        (set-contents! ((m 'get-register) 'pc) '(() (gcd-done ())))
        (thunk)
        (assert-true (get-contents ((m 'get-register) 'flag)))
        )
      )
    )

thunk を動かすトコロで上記メセジが出ている模様。原因不明。ちなみに make-test を直接呼び出すと動きます。

   ("test"
    (let ((m (make-new-machine)))
      (for-each (lambda (register-name)
                  ((m 'allocate-register) register-name))
                '(a b t))
      ((m 'install-operations) (list (list 'rem remainder) (list '= =)))
      (let ((thunk (make-test '(test (op =) (const 1) (const 1))
                              m
                              '()
                              (m 'operations)
                              ((m 'get-register) 'flag)
                              ((m 'get-register) 'pc))))
;      (let ((thunk (make-execution-procedure
;		    '(test (op =) (const 1) (const 1))
;		    '()
;		    m
;		    ((m 'get-register) 'pc)
;		    ((m 'get-register) 'flag)
;		    (m 'stack)
;		    (m 'operations))))
        (set-contents! ((m 'get-register) 'pc) '(() (gcd-done ())))
;	(assert-equal 'a thunk)
        (thunk)
        (assert-true (get-contents ((m 'get-register) 'flag)))
        )
      )
    )

何故なんだ。問題解決してませんが、ログ投入。ちなみに make-assign な試験は以下の形なんですが、正常に動いてたりします。ワケワカんねぇ。

   ("assign"
    (let ((m (make-new-machine)))
      (for-each (lambda (register-name)
                  ((m 'allocate-register) register-name))
                '(a b t))
      ((m 'install-operations) (list (list 'rem remainder) (list '= =)))
      (set-contents! ((m 'get-register) 'a) 1)
      (assert-equal 1 (get-contents ((m 'get-register) 'a)))
      (let ((thunk (make-execution-procedure
		    '(assign b (reg a))
		    '()
		    m
		    ((m 'get-register) 'pc)
		    ((m 'get-register) 'flag)
		    (m 'stack)
		    (m 'operations))))
        (set-contents! ((m 'get-register) 'pc) '(() (gcd-done ())))
        (thunk)
        (assert-equal 1 (get-contents ((m 'get-register) 'b)))
        )
      )
    )