secd machine

分かりやすそうな http://www.cs.ualberta.ca/~you/courses/325/Mynotes/Fun/SECD-slides.html を元に SECD マシーンを作って見ている途中。ほとんど定義そのままの単純な構造。良くわかっていないが、まだ実装していない LDF あたりが楽しみ。

OP 説明 目的 状態
NIL push a nil list 用 done
LD 環境から値を取得 done
LDC スタックに定数をプッシュ done
LDF
AP
RTN
SEL
JOIN
RAP
DUM
CAR built in unary op. リスト操作 done
+,-,* built in binary op. 数値演算 done
gosh> (run-loop (make-secd '((LDC 3) (LDC 2) (LDC 6) (+) (*)) '()))
#<secd: s:() e:() c:((LDC 3) (LDC 2) (LDC 6) (+) (*)) ()>
#<secd: s:(3) e:() c:((LDC 2) (LDC 6) (+) (*)) ()>
#<secd: s:(2 3) e:() c:((LDC 6) (+) (*)) ()>
#<secd: s:(6 2 3) e:() c:((+) (*)) ()>
#<secd: s:(8 3) e:() c:((*)) ()>
#<secd: s:(24) e:() c:() ()>
#<secd: s:(24) e:() c:() ()>

unit test.

(test* "ldc"
       3
       (let1 secd
           (make-secd '((LDC 3)) '())
         (car (stack (run-loop secd)))))

(test* "ldc"
       '(3 5)
       (let1 secd
           (make-secd '((LDC 5) (LDC 3)) '())
         (stack (run-loop secd))))

(test* "ldc in SECD-slides.html"
       24
       (let1 secd
           (make-secd '((LDC 3) (LDC 2) (LDC 6) (+) (*)) '())
         (car (stack (run-loop secd)))))

SECD Machine のコード。

(define-module secd
  (use gauche.test)
  (use gauche.parameter)
  (use gauche.time)
  (use gauche.sequence)
  (use util.match)
  (use util.queue)
  (use srfi-1))

(select-module secd)
(debug-print-width #f)

;; http://www.cs.ualberta.ca/~you/courses/325/Mynotes/Fun/SECD-slides.html

(define-class <secd> ()
  (;; stack used ofr evaluation of expressions
   (stack :accessor stack :init-value ())
   ;; environment used to store the current value list
   (env :accessor env :init-value () :init-keyword :env)
   ;; control used to store the instructions
   (control :accessor control :init-value () :init-keyword :control)
   ;; dump used to store suspended invocation context.
   (dump :accessor dump :init-value ())))

(define-method write-object ((secd <secd>) port)
  (format port "#<secd: s:~a e:~a c:~a ~d>~%"
          (stack secd) (env secd) (control secd) (dump secd)))

(define (make-secd control env) (make <secd> :control control :env env))

(define-method run-loop ((secd <secd>))
  (format #t "~s" secd)
  (cond
   ((null? (control secd))
    secd)
   (else
    (let1 code (car (control secd))
      (match code
        (('NIL) ; push a nil pointer
         (push! (stack secd) 'nil)
         (pop! (control secd))
         (run-loop secd))
        (('LD i j) ; load from environment ; get a value from context
         (push! (stack secd) (list-ref (list-ref (env secd) i) j))
         (pop! (control secd))
         (run-loop secd))
        (('LDC x) ; load constant ;
         (push! (stack secd) x)
         (pop! (control secd))
         (run-loop secd))
        (('LDF) ; load function ; get a closure
         #t)
        (('AP) ; apply function
         #t)
        (('RTN) ; return ; restore calling env
         #t)
        (('SEL) ; select in if statement
         #t)
        (('JOIN) ; rejoin from control ; used with SEL
         #t)
        (('RAP) ; recursive apply
         #t)
        (('DUM) ; create a dummy env; used with RAP
         #t)
        (('CAR) ; unary operator car
         (let1 a (pop! (stack secd))
           (push! (stack secd) (car a))
           (pop! (control secd))
           (run-loop secd)))
        ((or ('+) ('-) ('*)) ; binary operator + - *
         (let* ((a (pop! (stack secd)))
                (b (pop! (stack secd)))
                (op (case (car code)
                      ((+) +)
                      ((-) -)
                      ((*) *))))
           (push! (stack secd) (op a b))
           (pop! (control secd))
           (run-loop secd)))
        (else
         (errorf "invalid code: ~a" code)))))))

(provide "secd")

追記。お、トラックバックを頂いた。多謝。これは最後まで頑張らなくては。
SECD in scheme -> Tail-Recursive SECD in scheme -> toy Gauche VM in scheme -> (Full Gauche VM in scheme)、と進めると素敵なのだけどまだ最初なのでかなり遠い道のり。