Sunday, June 26, 2011

Solution of SICP Exercise 5.17

I think the best solution is adding the label information for each instruction
at the time when the function "extract-labels" is called.
But too lazy to modify many basic functions and decide to add label information
when the machine is created and update/refer to the information in the function
"execute".
The approach to hand "GOTO (REG XXX)" is too dirty and hope to modify it
when have time.

比较好的解决方案应该是在EXTRACT-LABELS函数中给每个指令另外加上LABEL的信息。
但是对于懒得修改很多基本的函数。于是决定在make-machine中加上LABEL的信息,
之后在EXECUTE函数中参照并随时加以修改。
但是之前没有很好考虑处理GOTO (REG xxx)语句的情况,导致不得不根据指令反过来
判断LABEL。希望有时间再修改吧。

一番良い方法は多分、Extract-labels関数に各命令のラベル情報
も追加してあげて、Execute関数それらを出力する。
いくつかの基本関数を修正しなければならないので、気が遠くなったので、
代わりに、make-machineにラベル情報を追加し、Execute関数にそれらを
参照/更新するアプローチを取った。
但し、GOTO (REG xxx)のケースは考慮不足で、汚い処理になってしまった。
時間があるときに修正しようかな。

Execution image(実行イーメージ)
(set-register-contents! fm 'n 3)
;Value: done

(fm 'trace-on)
;Value: #f

(fm 'prepare-label-trace)
;Value: ()

(start fm)
(label: controller text: (assign continue (label fact-done)))
(label: controller text: (test (op =) (reg n) (const 1)))
(label: controller text: (branch (label base-case)))
(label: controller text: (save continue))
(label: controller text: (save n))
(label: controller text: (assign n (op -) (reg n) (const 1)))
(label: controller text: (assign continue (label after-fact)))
(label: controller text: (goto (label fact-loop)))
(label: fact-loop text: (test (op =) (reg n) (const 1)))
(label: fact-loop text: (branch (label base-case)))
(label: fact-loop text: (save continue))
(label: fact-loop text: (save n))
(label: fact-loop text: (assign n (op -) (reg n) (const 1)))
(label: fact-loop text: (assign continue (label after-fact)))
(label: fact-loop text: (goto (label fact-loop)))
(label: fact-loop text: (test (op =) (reg n) (const 1)))
(label: fact-loop text: (branch (label base-case)))
(label: base-case text: (assign val (const 1)))
(label: base-case text: (goto (reg continue)))
(label: after-fact text: (restore n))
(label: after-fact text: (restore continue))
(label: after-fact text: (assign val (op *) (reg n) (reg val)))
(label: after-fact text: (goto (reg continue)))
(label: after-fact text: (restore n))
(label: after-fact text: (restore continue))
(label: after-fact text: (assign val (op *) (reg n) (reg val)))
(label: after-fact text: (goto (reg continue)))
;Value: done

Simulator instructions used above(上記レジスタマシーンfmのコード):
(define fm
  (make-machine
  '(n val continue)
  (list (list '= =) (list '- -) (list '* *))
  '(controller
    (assign continue (label fact-done))
 fact-loop
    (test (op =) (reg n) (const 1))
    (branch (label base-case))
    (save continue)
    (save n)
    (assign n (op -) (reg n) (const 1))
    (assign continue (label after-fact))
    (goto (label fact-loop))
 after-fact
    (restore n)
    (restore continue)
    (assign val (op *) (reg n) (reg val))
    (goto (reg continue))
 base-case
    (assign val (const 1))
    (goto (reg continue))
 fact-done)))

Functions modified and added in the implementation of the simulator machine
(define (make-machine register-names ops controller-text)
  (let ((machine (make-new-machine)))
    (for-each (lambda (register-name)
                ((machine 'allocate-register) register-name))
              register-names)
    ((machine 'install-operations) ops)
    ((machine 'install-labels) controller-text)
    ((machine 'install-instruction-sequence)
     (assemble controller-text machine))
    machine))
(define (make-new-machine)
  (let ((pc (make-register 'pc))
        (flag (make-register 'flag))
        (stack (make-stack))
        (the-instruction-sequence '())
    (counter 0)
    (trace-flg #f)
    (the-labels '())
    (current-label 'initial-label)
    (current-label-counter 1)
    (current-label-info '()))
    (let ((the-ops
           (list (list 'initialize-stack
                       (lambda () (stack 'initialize)))
                 (list 'print-stack-statistics
                       (lambda () (stack 'print-statistics)))))
          (register-table
           (list (list 'pc pc) (list 'flag flag))))
      (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))))
   (define (initialize-program-counter)
    (set! counter 0))
     
   (define (print-program-counter)
    (newline)
    (display (list 'program-counter= counter)))
      (define (execute)
        (let ((insts (get-contents pc)))
          (if (null? insts)
              'done
        (let ((text (instruction-text (car insts))))
          (if (not (false? trace-flg))
          (begin
            (newline)
            (display
             (list 'label: (caar current-label-info) 'text: text))))
          ((instruction-execution-proc (car insts)))
          (set! counter (+ counter 1))
          (cond ((eq? (car text) 'goto)
             (let ((goto-dest (cadr text)))
               (if (eq? (car goto-dest) 'reg)
               (set-label-info-cmds!
                (map
                 (lambda (x) (car x))
                 (get-contents pc)))
             (set-label-info! (cadr goto-dest)))))
            ((eq? (car text) 'branch)
             (if (get-contents flag)
             (set-label-info! (cadadr text))))
            (else
               (update-label-info! (+ current-label-counter 1))))
          (execute)))))
  (define (install-labels controller-text)
    (define (iter seq symb counter seq-follow-symb)
      (if (null? seq)
          '()
        (if (symbol? (car seq))
        (cons (cons symb (cons counter seq-follow-symb))
              (iter (cdr seq) (car seq) 0 (cdr seq)))
        (iter (cdr seq) symb (+ counter 1) seq-follow-symb))))
   (let ((insts (cdr controller-text)))
      (iter insts (car controller-text) 0 insts)))

  (define (prepare-label-trace!)
    (set! current-label-info the-labels))
;    (set! current-label (caar current-label-info)))
     
   (define (update-label-info! counter)
    (let ((label-counter (car current-label-info)))
      (let ((counter-threshold (cadr label-counter)))
        (if (= counter counter-threshold)
        (begin
          (set! current-label-info (cdr current-label-info))
          (set! current-label-counter 1))
          (set! current-label-counter counter)))))
     
  (define (set-label-info! label)
    (define (iter labels-info label-text)
      (if (null? labels-info)
          (set! current-label-info '())
          (if (eq? (caar labels-info) label-text)
          (set! current-label-info labels-info)
          (iter (cdr labels-info) label-text))))
    (iter the-labels label))
      (define (set-label-info-cmds! seq)
    (define (iter labels-info cmds)
      (if (null? labels-info)
          (set! current-label-info '())
          (if (equal?
           (filter
            (lambda (x)
              (not (symbol? x)))
            (cddar labels-info))
           cmds)
          (set! current-label-info labels-info)
          (iter (cdr labels-info) cmds))))
    (iter the-labels seq))
         

      (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 'print-program-counter)
           (print-program-counter))
          ((eq? message 'initialize-program-counter)
           (initialize-program-counter))
          ((eq? message 'trace-on)
           (set! trace-flg #t))
          ((eq? message 'trace-off)
           (set! trace-flg #f))
          ((eq? message 'install-labels)
           (lambda (controller-text)
         (set! the-labels
               (install-labels controller-text))))
          ((eq? message 'prepare-label-trace)
           (prepare-label-trace!))

              (else (error "Unknown request -- MACHINE" message))))
      dispatch)))

No comments:

Post a Comment