secd machine 分解
SECD マシンの実行過程を分解して見る。
gosh> (run '((lambda (x) (+ x 3)) 4))
- s e (NIL LDC 4 CONS LDF (LDC 3 LD (1 . 1) + RTN) AP . c) d
c の先頭は NIL。 NIL は NIL をスタックs に積む。 - (NIL . s) e (LDC 4 CONS LDF (LDC 3 LD (1 . 1) + RTN) AP . c) d
c の先頭は LDC。LDC は次の数字をスタックsに積む。 - (4 NIL . s) e (CONS LDF (LDC 3 LD (1 . 1) + RTN) AP . c) d
c の先頭は CONS 。CONS はスタックs の二つの要素を cons してスタックs に積む。ここまでで lambda への引数 (4) ができた。 - ((4) . s) e (LDF (LDC 3 LD (1 . 1) + RTN) AP . c) d
c の先頭はLDF。LDF は関数本体(LDC 3 LD (1 . 1) + RTN) と環境とをペアにしてスタックに積む。 - (((LDC 3 LD (1 . 1) + RTN) . e) (4 . NIL) . s) e (AP . c) d
c の先頭はAP。AP は s から環境とパラメータを取り出し e に移した上で、関数本体を c に設定。さらに dump にこの後の処理を退避。 - NIL ((4 . NIL) . e) (LDC 3 LD (1 . 1) + RTN) (s e c . d)
関数本体を実行。c の先頭は LDC なので次の数字をスタック s に積む。 - (3 . NIL) ((4 . NIL) . e) (LD (1 . 1) + RTN) (s e c . d)
c の先頭はLD 。コンパイル時に分かっている x の具体的な値を環境から取り出す。 - (4 3 . NIL) ((4 . NIL) . e) (+ RTN) (s e c . d)
c の先頭は+。スタックsから二つ要素を取り出し、足して、結果をスタックに積む。 - (7 . NIL) ((4 . NIL) . e) (RTN) (s e c . d)
c の先頭は RTN。スタックの先頭要素はそのまま、のこりは退避していた s, e, c, d を戻す。 - (7 . s) e c d
終了。
追記。secd ((3 NIL) e (CONS . c) d) => ((3) e c d) になるように CONS の定義をいじった。
memo
見比べる。
gosh> (disasm (lambda () (+ (* 3 5) (* 6 8)))) main_code (name=#f, code=0xbad40, size=15, const=3, stack=13): args: #f 0 PRE-CALL(2) 6 2 CONSTI-PUSH(3) 3 CONSTI-PUSH(5) 4 GREF-CALL(2) #<identifier secd#*>; (* 3 5) 6 PUSH-PRE-CALL(2) 12 8 CONSTI-PUSH(6) 9 CONSTI-PUSH(8) 10 GREF-CALL(2) #<identifier secd#*>; (* 6 8) 12 PUSH-GREF-TAIL-CALL(2) #<identifier secd#+>; (+ (* 3 5) (* 6 8)) 14 RET #<undef> gosh> (run '(+ (* 3 5) (* 6 8))) ;1. s e (LDC 8 LDC 6 * LDC 5 LDC 3 * + . c) d ;2. (8 . s) e (LDC 6 * LDC 5 LDC 3 * + . c) d ;3. (6 8 . s) e (* LDC 5 LDC 3 * + . c) d ;4. (48 . s) e (LDC 5 LDC 3 * + . c) d ;5. (5 48 . s) e (LDC 3 * + . c) d ;6. (3 5 48 . s) e (* + . c) d ;7. (15 48 . s) e (+ . c) d ;8. (63 . s) e c d ((63 . s) e c d) #f