secd machine(2)
ちょこっと前進。if のサポート。そして初めて secd の d, dump スタックを使った。
元の secd だと、必ず s とか e とかが末尾残っているのだけど、わたしのは空リスト。そのままだと secd の d は結局空のままで分かり難いので、 if の結果に足し算をする例にしてある。既に微妙に表記が資料と違っていて混乱気味。基本的にわたしのは全てカッコが一階層多いようだ。
しかし secd って単純で楽しい。 haskell あたりならパターンマッチだけでものすごく奇麗に書き下せそう。
;; (+ (if (atom 5) 9 7) 11) => 20 gosh> (let1 secd (make-secd :c '((LDC 5) (ATOM) (SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN)) (LDC 11) (+))) (car (s (run-loop secd)))) #<s:() e:() c:((LDC 5) (ATOM) (SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN)) (LDC 11) (+)) d:()> -> #<s:(5) e:() c:((ATOM) (SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN)) (LDC 11) (+)) d:()> -> #<s:(#t) e:() c:((SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN)) (LDC 11) (+)) d:()> -> #<s:() e:() c:((LDC 9) (JOIN)) d:(((LDC 11) (+)))> -> #<s:(9) e:() c:((JOIN)) d:(((LDC 11) (+)))> -> #<s:(9) e:() c:((LDC 11) (+)) d:()> -> #<s:(11 9) e:() c:((+)) d:()> -> #<s:(20) e:() c:() d:()> 20 gosh> (let1 secd (make-secd :c '((LDC 5) (ATOM) (SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN)))) (car (s (run-loop secd)))) #<s:() e:() c:((LDC 5) (ATOM) (SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN))) d:()> -> #<s:(5) e:() c:((ATOM) (SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN))) d:()> -> #<s:(#t) e:() c:((SEL) ((LDC 9) (JOIN)) ((LDC 7) (JOIN))) d:()> -> #<s:() e:() c:((LDC 9) (JOIN)) d:(())> -> #<s:(9) e:() c:((JOIN)) d:(())> -> #<s:(9) e:() c:() d:()> 9 gosh>
コード。だんだんとオブジェクトじゃなく単なる list でも良かった気がしてきた、、
(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 of evaluation of expressions (stack :accessor s :init-value () :init-keyword :s) ;; environment used to store the current value list (env :accessor e :init-value () :init-keyword :e) ;; control used to store the instructions (control :accessor c :init-value () :init-keyword :c) ;; dump used to store suspended invocation context. (dump :accessor d :init-value () :init-keyword :d))) (define-method write-object ((secd <secd>) port) (format port "#<s:~a e:~a c:~a d:~a>" (s secd) (e secd) (c secd) (d secd))) (define (make-secd . options) (let-keywords options ((s ()) (e ()) (c ()) (d ())) (make <secd> :s s :e e :c c :d d ))) (define-method run-loop ((secd <secd>)) (format #t "~s" secd) (cond ((null? (c secd)) (format #t "~%") secd) (else (format #t " ->~%") (let1 code (car (c secd)) (match code (('NIL) ; push a nil pointer (push! (s secd) 'nil) (pop! (c secd)) (run-loop secd)) (('LD i j) ; load from environment ; get a value from context (push! (s secd) (list-ref (list-ref (e secd) i) j)) (pop! (c secd)) (run-loop secd)) (('LDC x) ; load constant ; (push! (s secd) x) (pop! (c 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 (pop! (c secd)) ;; sel (let* ((x (pop! (s secd))) (ct (pop! (c secd))) (cf (pop! (c secd)))) (push! (d secd) (c secd)) (set! (c secd) (if x ct cf)) (run-loop secd))) (('JOIN) ; rejoin from control ; used with SEL (pop! (c secd)) ;; rm join (set! (c secd) (pop! (d secd))) (run-loop secd)) (('RAP) ; recursive apply #t) (('DUM) ; create a dummy env; used with RAP #t) (('CAR) ; unary operator car (let1 a (pop! (s secd)) (push! (s secd) (car a)) (pop! (c secd)) (run-loop secd))) (('ATOM) (let* ((a (pop! (s secd)))) (push! (s secd) (not (pair? a))) (pop! (c secd)) (run-loop secd))) ((or ('+) ('-) ('*)) ; binary operator + - * (let* ((a (pop! (s secd))) (b (pop! (s secd))) (op (case (car code) ((+) +) ((-) -) ((*) *)))) (push! (s secd) (op a b)) (pop! (c secd)) (run-loop secd))) (else (errorf "invalid code: ~a" code))))))) (provide "secd")
関数のコンパイルは今日はもう時間切れ。まとめて3つ、RET, LDF, AP を実装できなきゃ駄目っぽい。