secd machine と util.match
gauche の util.match を使えばもっとずっと短く明快に、定義そのままに secd machine を書けることに今さら気づいた。やはりオブジェクトじゃなくリストで十分だった。うーん、パターンマッチは本当に強力だ。全体を見直したところ、完全に資料(http://www.cs.ualberta.ca/~you/courses/325/Mynotes/Fun/SECD-slides.html)どおりの表記になった。例外は LD に渡す要素で、1だけずれている。
(上記資料はとても分かりやすいので secd machine って何だ?という非情報科学系の人にお勧め。情報科学系の人には恐らく常識。)
gosh> (secd 's 'e '(LDC x . c) 'd) s e (LDC x . c) d (x . s) e c d ((x . s) e c d) #f gosh> (secd 's 'e '(NIL . c) 'd) s e (NIL . c) d (NIL . s) e c d ((NIL . s) e c d) #f gosh> (secd 's 'e '(LDC 3 LDC 2 LDC 6 + * . c) 'd) s e (LDC 3 LDC 2 LDC 6 + * . c) d (3 . s) e (LDC 2 LDC 6 + * . c) d (2 3 . s) e (LDC 6 + * . c) d (6 2 3 . s) e (+ * . c) d (8 3 . s) e (* . c) d (24 . s) e c d ((24 . s) e c d) #f gosh> (secd 's '((1 3) (4 (5 6))) '(LD (1 . 1) CAR LD (0 . 0) + . c) 'd) s ((1 3) (4 (5 6))) (LD (1 . 1) CAR LD (0 . 0) + . c) d ((5 6) . s) ((1 3) (4 (5 6))) (CAR LD (0 . 0) + . c) d (5 . s) ((1 3) (4 (5 6))) (LD (0 . 0) + . c) d (1 5 . s) ((1 3) (4 (5 6))) (+ . c) d (6 . s) ((1 3) (4 (5 6))) c d ((6 . s) ((1 3) (4 (5 6))) c d)
そして関数適用も。これはとりあえず動いた、という段階。もうすこし吟味して、何が起きているのか正確に把握しなくてはいけない。
gosh> (test* "((lambda (x y) (+ x y)) 2 3)" '((5 . s) e c d) (secd 's 'e '(NIL LDC 3 CONS LDC 2 CONS LDF (LD (0 . 1) LD (0 . 0) + RTN) AP . c) 'd)) test ((lambda (x y) (+ x y)) 2 3), expects ((5 . s) e c d) ==> s e (NIL LDC 3 CONS LDC 2 CONS LDF (LD (0 . 1) LD (0 . 0) + RTN) AP . c) d (NIL . s) e (LDC 3 CONS LDC 2 CONS LDF (LD (0 . 1) LD (0 . 0) + RTN) AP . c) d (3 NIL . s) e (CONS LDC 2 CONS LDF (LD (0 . 1) LD (0 . 0) + RTN) AP . c) d ((3 . NIL) . s) e (LDC 2 CONS LDF (LD (0 . 1) LD (0 . 0) + RTN) AP . c) d (2 (3 . NIL) . s) e (CONS LDF (LD (0 . 1) LD (0 . 0) + RTN) AP . c) d ((2 3 . NIL) . s) e (LDF (LD (0 . 1) LD (0 . 0) + RTN) AP . c) d (((LD (0 . 1) LD (0 . 0) + RTN) . e) (2 3 . NIL) . s) e (AP . c) d NIL ((2 3 . NIL) . e) (LD (0 . 1) LD (0 . 0) + RTN) (s e c . d) (3 . NIL) ((2 3 . NIL) . e) (LD (0 . 0) + RTN) (s e c . d) (2 3 . NIL) ((2 3 . NIL) . e) (+ RTN) (s e c . d) (5 . NIL) ((2 3 . NIL) . e) (RTN) (s e c . d) (5 . s) e c d ok #<undef>
OP | 説明 | 目的 | 状態 |
---|---|---|---|
NIL | push a nil | list 用 | done |
LD | load from the environment | 環境から値を取得 | done |
LDC | load constant | スタックに定数をプッシュ | done |
LDF | load function | done | |
AP | apply function | done | |
RTN | return | 環境を戻す | done |
SEL | select in if statement | if | done |
JOIN | rejoin main control | if の末尾で | done |
RAP | recursive apply | ||
DUM | create a dummy env | ||
CAR | built in unary op. | 引数一つの組み込み関数 | done |
+,-,*,>,< | built in binary op. | 引数二つの組み込み関数 | done |
- 今のところ secd machine (VM) のみで、コンパイラは作っていない。そんなに難しくはないはず。
- このシンプルな secd machine と gauche vm の関係、をこれから考える。
コードは以下。secd machine は4要素のリストで表す。処理ごとに現在の vm の状態を印字。
パターンマッチを行い、定義通りに新しい状態に移る。どのパターンにもマッチしない場合、 (s e c d) をそのまま返して終了する。
;; see http://www.cs.ualberta.ca/~you/courses/325/Mynotes/Fun/SECD-slides.html (define-module secd (use gauche.test) (use util.match) (use srfi-1)) (select-module secd) (debug-print-width #f) (use util.match) (define (secd s e c d) (format #t "~a ~a ~a ~a~%" s e c d) (match (list s e c d) ((s e ('LDC x . c) d) (secd (cons x s) e c d)) ((s e ('LD (i . j) . c) d) (secd (cons (list-ref (list-ref e i) j) s) e c d)) (((a b . s) e ('+ . c) d) (secd (cons (+ a b) s) e c d)) (((a b . s) e ('- . c) d) (secd (cons (- a b) s) e c d)) (((a b . s) e ('* . c) d) (secd (cons (* a b) s) e c d)) (((a b . s) e ('CONS . c) d) (secd (cons (cons a b) s) e c d)) ((s e ('NIL . c) d) (secd (cons 'NIL s) e c d)) (((a . s) e ('ATOM . c) d) (secd (cons (not (pair? a)) s) e c d)) (((a . s) e ('CAR . c) d) (secd (cons (car a) s) e c d)) (((x . s) e ('SEL ct cf . c) d) (secd s e (if x ct cf) (cons c d))) ((s e ('JOIN . c) (cr . d)) (secd s e cr d)) ((s e ('LDF f . c) d) (secd (cons (cons f e) s) e c d)) ((((f . e2) v . s) e ('AP . c) d) (secd 'NIL (cons v e2) f (append (list s e c) d))) (((x . z) e2 ('RTN . q) (s e c . d)) (secd (cons x s) e c d)) ((s e c d) (values (list s e c d) #f)))) (provide "secd")