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

問題 5.19

昨晩の修正を盛り込んで pc をトレイスしたら以下のような出力。(一部のみ)

(pc 
 ((((after-fact 3) assign val (op *) (reg n) (reg val)) . #<closure (make-assign make-assign)>)
  ... 続く))

ブレイクポイントは (label n) のリストになっているとして execute は以下な感じで良いと見てるんですが ...

      (define (isBreakpoint l)
	(let f ((breakpoints (lookup-register 'breakpoints)))
	  (cond ((null? breakpoints) #f)
		((equal? l (car breakpoints)) #t)
		(else
		 (f (cdr breakpoints))))))
      (define (execute)
        (let ((insts (get-contents pc)))
          (if (null? insts)
              'done
              (begin
                (if trace                                      ;; 5.16
                    (begin                                     ;; 5.16
                      (display (car (car insts)))              ;; 5.17
                      (newline)))                              ;; 5.16
		(cond ((isBreakpoint (car (car (car insts))))
		       (display (append (list 'break) (car (car (car insts)))))
		       (newline))
		      (else
		       ((instruction-execution-proc (car insts)))
		       (set! inst-ctr (+ inst-ctr 1)) ;; 5.15
		       (execute)))))))

他も含めて盛り込んでみたのが以下。

(define (make-new-machine)
  (let ((pc (make-register 'pc))
        (flag (make-register 'flag))
        (stack (make-stack))
	(inst-ctr 0) ;; 5.15
	(trace #f)   ;; 5.16
        (the-instruction-sequence '())
	(breakpoints (make-register 'breakpoints)))
    (let ((the-ops
           (list (list 'initialize-stack
                       (lambda () (stack 'initialize)))
                 ;;**next for monitored stack (as in section 5.2.4)
                 ;;  -- comment out if not wanted
                 (list 'print-stack-statistics
                       (lambda () (stack 'print-statistics)))))
          (register-table
           (list (list 'pc pc) (list 'flag flag))))
      (define (init-inst-ctr) ;; 5.15
	(set! inst-ctr 0))    ;; 5.15
      (define (get-inst-ctr)  ;; 5.15
	inst-ctr)             ;; 5.15
      (define (trace-on) (set! trace #t))   ;; 5.16
      (define (trace-off) (set! trace #f))  ;; 5.16
      (define (reg-trace-on)                        ;; 5.18
	(lambda (reg-name)                          ;; 5.18
	  ((lookup-register reg-name) 'trace-on)))  ;; 5.18
      (define (reg-trace-off)                       ;; 5.18
	(lambda (reg-name)                          ;; 5.18
	  ((lookup-register reg-name) 'trace-off))) ;; 5.18
      (define (allocate-register name)
        (if (assoc name register-table)
            (error "Multiply defined register: " name)
            (set! register-table
                  (cons (list name (make-register name))
                        register-table)))
        'register-allocated)
      (define (lookup-register name)
        (let ((val (assoc name register-table)))
          (if val
              (cadr val)
              (error "Unknown register:" name))))

;; 5.19
      (define (isBreakpoint l)
;	(let f ((breakpoints (lookup-register 'breakpoints)))
	(let f ((b (breakpoints 'get)))
	  (cond ((null? b) #f)
		((equal? l (car b)) #t)
		(else
		 (f (cdr b))))))
;; 5.19

      (define (execute)
        (let ((insts (get-contents pc)))
          (if (null? insts)
              'done
;; 5.19
;;              (begin
	      (cond ((isBreakpoint (car (car (car insts))))
		     (display (append (list 'break) (car (car (car insts)))))
		     (newline))
		    (else
		     (proceed))))))

      (define (proceed)
	(if trace                                      ;; 5.16
	    (begin                                     ;; 5.16
	      (display (car (car insts)))              ;; 5.17
	      (newline)))                              ;; 5.16
	((instruction-execution-proc (car insts)))
	(set! inst-ctr (+ inst-ctr 1)) ;; 5.15
	(execute))

      (define (set-breakpoint)
	(lambda (label n)
	  ((breakpoints 'set) (cons (list label n)
				    (breakpoints 'get))))

      (define (reset-breakpoint)
	(lambda (label n)
	  (let f ((b (breakpoints 'get))
		  (result '()))
	    (cond ((null? b) result)
		  ((equal? (list label n) (car b))
		   (f (cdr b) result))
		  (else
		   (f (cdr b) (cons (car b) result)))))))		

      (define (reset-breakpoint-all)
	((breakpoints 'set) '()))
;; 5.19

      (define (dispatch message)
        (cond ((eq? message 'start)
               (set-contents! pc the-instruction-sequence)
               (execute))
              ((eq? message 'install-instruction-sequence)
               (lambda (seq) (set! the-instruction-sequence seq)))
              ((eq? message 'allocate-register) allocate-register)
              ((eq? message 'get-register) lookup-register)
              ((eq? message 'install-operations)
               (lambda (ops) (set! the-ops (append the-ops ops))))
              ((eq? message 'stack) stack)
              ((eq? message 'operations) the-ops)
	      ((eq? message 'init-inst-ctr) (init-inst-ctr))   ;; 5.15
	      ((eq? message 'get-inst-ctr) (get-inst-ctr))     ;; 5.15
	      ((eq? message 'trace-on) (trace-on))             ;; 5.16
	      ((eq? message 'trace-off) (trace-off))           ;; 5.16
	      ((eq? message 'reg-trace-on) (reg-trace-on))             ;; 5.18
	      ((eq? message 'reg-trace-off) (reg-trace-off))           ;; 5.18
	      ((eq? message 'set-break) (set-breakpoint))
	      ((eq? message 'proceed) (proceed))
	      ((eq? message 'cancel-breakpoint) (reset-breakpoint))
	      ((eq? message 'calcel-all) (reset-breakpoint-all))
              (else (error "Unknown request -- MACHINE" message))))
      dispatch)))

;; 5.19
(define (set-breakpoint machine label n)
  ((m 'set-break) label n))
(define (proceed-machine machine)
  (m 'proceed))
(define (cancel-breakpoint machine label n)
  ((m 'cancel-breakpoint) label n))
(define (calcel-all-breakpoints machine)
  (m 'cancel-all))
;; 5.19

バグ入ってる可能性大。今から試験をしてみます。

不具合

  • breakpoints レジスタが *unassigned とか '() の場合の考慮ナシ
  • proceed 手続きに pc レジスタの中身を渡していない

という不具合あり。まだ breakpoint なソレは試していません。で、セットしてみたら以下の返答が。

gosh> (set-breakpoint m 'fact-loop 2)
((fact-loop 2) . *unassigned*)
gosh> 

これはヒドい。しかもよく見りゃ register-table にセットしてません。breakpoints にデフォルト値 '() を設定して走らせてみると以下。

gosh> (set-breakpoint m 'fact-loop 2)
((fact-loop 2))
gosh> (m 'start)
(break fact-loop 2)
#<undef>

これ、display するんじゃなくってリストを戻せば良いだけ??

      (define (execute)
        (let ((insts (get-contents pc))
	      (b (breakpoints 'get)))
          (if (null? insts)
              'done
;; 5.19
;;              (begin
	      (cond ((and (not (equal? '*unassigned* b))
			  (not (equal? '() b))
			  (isBreakpoint (car (car (car insts)))))
;		     (display (append (list 'break) (car (car (car insts)))))
;		     (newline))
		     (list 'break
			   (car (car (car (car insts))))
			   (cadr (car (car (car insts))))))
		    (else
		     (proceed))))))

なんか馬鹿丸出しだなぁ (とほほ
で、次は cancel だな、という事で以下。

gosh> (set-register-contents! m 'n 3)
done
gosh> (set-breakpoint m 'fact-loop 2)
((fact-loop 2))
gosh> (m 'start)
(break fact-loop 2)
gosh> (proceed-machine m)
(break fact-loop 2)
gosh> (get-register-contents m 'n)
2
gosh> (get-register-contents m 'val)
*unassigned*
gosh> (cancel-breakpoint m 'fact-loop 2)
()
gosh> (proceed-machine m)
(break fact-loop 2)
gosh> (get-register-contents m 'breakpoints)
((fact-loop 2))
gosh> 

cancel されてないし。ってこれは分かったぞ!!

      (define (reset-breakpoint)
	(lambda (label n)
	  (let f ((b (breakpoints 'get))
		  (result '()))
	    (cond ((null? b) result)
		  ((equal? (list label n) (car b))
		   (f (cdr b) result))
		  (else
		   (f (cdr b) (cons (car b) result)))))))	

直したリストを戻してるだけじゃん。(泣

      (define (reset-breakpoint)
	(define (update-breakp l)
	  (let f ((b (breakpoints 'get))
		  (result '()))
	    (cond ((null? b) result)
		  ((equal? l (car b))
		   (f (cdr b) result))
		  (else
		   (f (cdr b) (cons (car b) result))))))
	(lambda (label n)
	  (set-contents! breakpoints (update-breakp (list label n)))))

これでどうか。

gosh> (set-breakpoint m 'fact-loop 2)
((fact-loop 2))
gosh> (cancel-breakpoint m 'fact-loop 2)
()
gosh> (get-register-contents m 'breakpoints)
()
gosh> 

オーゲー。次は cancel-all ですがここでもボケをカマしてます。

gosh> (set-breakpoint m 'fact-loop 2)
((fact-loop 2))
gosh> (set-breakpoint m 'after-fact 1)
((after-fact 1) (fact-loop 2))
gosh> (get-register-contents m 'breakpoints)
((after-fact 1) (fact-loop 2))
gosh> (cancel-all-breakpoints m)
*** ERROR: unbound variable: cancel-all-breakpoints
Stack Trace:
_______________________________________
gosh> (calcel-all-breakpoints m)
*** ERROR: Unknown request -- MACHINE cancel-all
Stack Trace:
_______________________________________
gosh> 

ソースの一部を以下に。

	      ((eq? message 'calcel-all) (reset-breakpoint-all))
              (else (error "Unknown request -- MACHINE" message))))
      dispatch)))

;; 5.19
(define (set-breakpoint machine label n)
  ((m 'set-break) label n))
(define (proceed-machine machine)
  (m 'proceed))
(define (cancel-breakpoint machine label n)
  ((m 'cancel-breakpoint) label n))
(define (calcel-all-breakpoints machine)
  (m 'cancel-all))

段々嫌になって参りました。修正後は上手く動作している模様。

gosh> (set-breakpoint m 'fact-loop 2)
((fact-loop 2))
gosh> (set-breakpoint m 'after-fact 1)
((after-fact 1) (fact-loop 2))
gosh> (cancel-all-breakpoints m)
()
gosh> (get-register-contents m 'breakpoints)
()
gosh> 

やれやれ、と言いつつスタートしたらオチました。よく見たら n なレジスタに値を設定してなかった。相当パニクってるな。一応これでデキ上がりなのだろうか。色々手を入れてみた挙句なソレを以下に。(手が入った部分のみ)

(define (make-new-machine)
  (let ((pc (make-register 'pc))
        (flag (make-register 'flag))
        (stack (make-stack))
	(inst-ctr 0) ;; 5.15
	(trace #f)   ;; 5.16
        (the-instruction-sequence '())
	(breakpoints (make-register 'breakpoints)))
    (let ((the-ops
           (list (list 'initialize-stack
                       (lambda () (stack 'initialize)))
                 ;;**next for monitored stack (as in section 5.2.4)
                 ;;  -- comment out if not wanted
                 (list 'print-stack-statistics
                       (lambda () (stack 'print-statistics)))))
          (register-table
           (list (list 'pc pc) (list 'flag flag) (list 'breakpoints breakpoints)))
	  (dmy (set-contents! breakpoints '())))
      (define (init-inst-ctr) ;; 5.15
	(set! inst-ctr 0))    ;; 5.15
      (define (get-inst-ctr)  ;; 5.15
	inst-ctr)             ;; 5.15
      (define (trace-on) (set! trace #t))   ;; 5.16
      (define (trace-off) (set! trace #f))  ;; 5.16
      (define (reg-trace-on)                        ;; 5.18
	(lambda (reg-name)                          ;; 5.18
	  ((lookup-register reg-name) 'trace-on)))  ;; 5.18
      (define (reg-trace-off)                       ;; 5.18
	(lambda (reg-name)                          ;; 5.18
	  ((lookup-register reg-name) 'trace-off))) ;; 5.18
      (define (allocate-register name)
        (if (assoc name register-table)
            (error "Multiply defined register: " name)
            (set! register-table
                  (cons (list name (make-register name))
                        register-table)))
        'register-allocated)
      (define (lookup-register name)
        (let ((val (assoc name register-table)))
          (if val
              (cadr val)
              (error "Unknown register:" name))))

;; 5.19
      (define (isBreakpoint l)
	(let f ((b (get-contents breakpoints)))
	  (cond ((null? b) #f)
		((equal? l (car b)) #t)
		(else
		 (f (cdr b))))))
;; 5.19

      (define (execute)
        (let ((insts (get-contents pc))
	      (b (breakpoints 'get)))
          (if (null? insts)
              'done
;; 5.19
	      (let ((current-inst-label (car (car (car insts)))))
		(cond ((and (not (equal? '*unassigned* b))
			    (not (equal? '() b))
			    (isBreakpoint current-inst-label))
		       (list 'break 
			     (car current-inst-label)
			     (cadr current-inst-label)))
		      (else
		       (proceed)))))))

      (define (proceed)
	(let ((insts (get-contents pc)))
	  (if trace
	      (begin
		(display (car (car insts)))
		(newline)))
	  ((instruction-execution-proc (car insts)))
	  (set! inst-ctr (+ inst-ctr 1))
	  (execute)))

      (define (set-breakpoint)
	(lambda (label n)
	  (set-contents! breakpoints (cons (list label n)
					   (get-contents breakpoints)))))

      (define (reset-breakpoint)
	(define (update-breakp l)
	  (let f ((b (get-contents breakpoints))
		  (result '()))
	    (cond ((null? b) result)
		  ((equal? l (car b))
		   (f (cdr b) result))
		  (else
		   (f (cdr b) (cons (car b) result))))))
	(lambda (label n)
	  (set-contents! breakpoints (update-breakp (list label n)))))

      (define (reset-breakpoint-all)
	(set-contents! breakpoints '()))
;; 5.19

      (define (dispatch message)
        (cond ((eq? message 'start)
               (set-contents! pc the-instruction-sequence)
               (execute))
              ((eq? message 'install-instruction-sequence)
               (lambda (seq) (set! the-instruction-sequence seq)))
              ((eq? message 'allocate-register) allocate-register)
              ((eq? message 'get-register) lookup-register)
              ((eq? message 'install-operations)
               (lambda (ops) (set! the-ops (append the-ops ops))))
              ((eq? message 'stack) stack)
              ((eq? message 'operations) the-ops)
	      ((eq? message 'init-inst-ctr) (init-inst-ctr))   ;; 5.15
	      ((eq? message 'get-inst-ctr) (get-inst-ctr))     ;; 5.15
	      ((eq? message 'trace-on) (trace-on))             ;; 5.16
	      ((eq? message 'trace-off) (trace-off))           ;; 5.16
	      ((eq? message 'reg-trace-on) (reg-trace-on))             ;; 5.18
	      ((eq? message 'reg-trace-off) (reg-trace-off))           ;; 5.18
	      ((eq? message 'set-break) (set-breakpoint))           ;; 5.19
	      ((eq? message 'proceed) (proceed))                    ;; 5.19
	      ((eq? message 'cancel-breakpoint) (reset-breakpoint)) ;; 5.19
	      ((eq? message 'cancel-all) (reset-breakpoint-all))    ;; 5.19
              (else (error "Unknown request -- MACHINE" message))))
      dispatch)))

;; 5.19
(define (set-breakpoint machine label n)
  ((m 'set-break) label n))
(define (proceed-machine machine)
  (m 'proceed))
(define (cancel-breakpoint machine label n)
  ((m 'cancel-breakpoint) label n))
(define (cancel-all-breakpoints machine)
  (m 'cancel-all))
;; 5.19

これはアソビがいのあるオモチャかも。