SICP 読み (332) 5.5 翻訳系

昨晩のソレを前提に検討着手してみたのでログを残しておく。

問題 5.38

まず a.なんですが、被演算子のリストは基本的に (operands exp) だと見てて良いのかなぁ。あと_連続した引数レジスタ_は arg1 と arg2 とゆー事を前提にしてみます。
下書きとゆー事で以下。

(define (spread-arguments exp)
  (let ((exp1 (compile (car exp) 'arg1 'next))
	(exp2 (compile (cadr exp) 'arg2 'next)))
    (preserving '(env arg1)
		exp1
		exp2)))

すげー。ってかヒデぇ。動作させる事全然考えてません、みたいなコードです。よく見れば

  • exp2 で arg1 が上書きされる可能性が考慮されてない (ん??そうでも無い??
  • (cadr exp) が無い場合な可能性が考慮されてない (可能性はあるのかどうかも微妙

一つしかない、というのは無いと見たとしてもやっぱ_連続した引数レジスタ_のナニがやっぱ分からん。a.はスルーするか。微妙だなぁ。

b.

で、b.です。例えば + だと

(define (compile-plus exp target linkage)
  )

みたいな出だしで ... なのか。こんなしとけば良いのかなぁ。

(define (compile-plus exp target linkage)
  (let ((arg (spread-argument (operands exp))))
    (preserving '(continue)
		arg
		(make-instruction-sequence 
		 '(arg1 arg2)
		 '(val)
		 `((assign ,target (op +) (reg arg1) (reg arg2)))))))

むむ。なんか spread-argument が復活してるし。これ、compile が振り分けてくれる、とゆー事にしといて

(+ 1 2)

がどうなるかを机上ベースで検証してみるか。ちなみにデフォルト評価器だと

((assign proc (op lookup-variable-value) (const +) (reg env)) 
 (assign val (const 2)) 
 (assign argl (op list) (reg val)) 
 (assign val (const 1)) 
 (assign argl (op cons) (reg val) (reg argl)) 
 (test (op primitive-procedure?) (reg proc)) 
 (branch (label primitive-branch1)) 
 compiled-branch2 
 (assign val (op compiled-procedure-entry) (reg proc)) 
 (goto (reg val)) 
 primitive-branch1 
 (assign val (op apply-primitive-procedure) (reg proc) (reg argl)) 
 (goto (reg continue)) 
 after-call3)

なソレが出てきた。ぱっと見ですが、compile-plus だと

((assign arg1 (const 1))
 (assign arg2 (const 2))
 (assign val (op +) (reg arg1) (reg arg2)))

になるんかな。ただ、上記の compile-plus は

(+ (+ 1 2) (+ 3 4))

みたいな式だとどうなるのだろうか。あるいは fuctorial を翻訳したらどうなるのか。

つづき

ええと、とりあえず上記のソレで

(+ 1 (+ 2 3))

だとどうか、を見てみます。まず微妙な spread-argument で (1 (+ 2 3)) が渡されて exp1 には

(() (arg1)
 ((assign arg1 (const 1))))

exp2 はさらに compile-plus に渡されて

(() (arg1)
 ((assign arg1 (const 2))))

(() (arg2)
 ((assign arg2 (const 3))))

が preserving されて

(() (arg1 arg2)
 ((assign arg1 (const 2))
  (assign arg2 (const 3))))

さらに assign と preserving で

(() (arg1 arg2)
 ((assign arg1 (const 2))
  (assign arg2 (const 3))
  (assign arg2 (op +) (reg arg1) (reg arg2))))

で (って target が arg2 でいいんだよな ...)、これが最初のソレと (env arg1) で preserving されるんですが、よく考えると compile-plus って linkage な処理をしてないな。あ、でも compile-application は end-with-linkage なソレってスルー??
や、違う。やんなきゃ駄目そげ。最後は (goto (reg continue)) でシメないと駄目だ。とりあえずこのあたりの盛り込みは置いといて組み合わせてみよう。

ってダメだな。compile-plus で preserving する時に (continue) ではなくって (continue arg1) でヤッてあげれば

((arg1) (arg1 arg2)
 ((assign arg1 (const 2))
  (assign arg2 (const 3))
  (assign arg2 (op +) (reg arg1) (reg arg2))))

になるから

(() (arg1)
 ((assign arg1 (const 1))))

と preserving (順番逆ですが) と (env arg1) で preserving する時に直上の式が save/restore で囲まれる事にはなりますが ...
あ、違うぞ。最終到達点としては

((assign arg1 (const 1))
 (save arg1)
 (assign arg1 (const 2))
 (assign arg2 (const 3))
 (assign arg2 (op +) (reg arg1) (reg arg2))
 (restore arg1)
 (assign val (op +) (reg arg1) (reg arg2))
 (goto (reg continue))

にならないと駄目なんだな。これって preserving の機能というか目的とは微妙に違ってるように見えますが気のせいだろうか。preserving って

  • regs で指定されたレジスタ
    • 式1 の modified で
    • 式2 の needed の時に save/restore して式1 の needed と modified を修正

というナニ。うむむむ。ワケワカ。このあたりを何とかするのが spread-arguments の役割になるのかな。