Scm_ApplyRec
このエントリの実装書いて気がついたのですが、Scm_ApplyRec 手続きって apply した結果を戻します。
Scm_ApplyRec 手続きは引数を vm->vals に順にセットして apply_rec 手続きを呼び出します。
static ScmObj apply_rec(ScmVM *vm, ScmObj proc, int nargs) { ScmObj program; ScmWord code[2]; code[0] = SCM_WORD(SCM_VM_INSN1(SCM_VM_VALUES_APPLY, nargs)); code[1] = SCM_WORD(SCM_VM_INSN(SCM_VM_RET)); vm->val0 = proc; program = vm->base? SCM_OBJ(vm->base) : SCM_OBJ(&internal_apply_compiled_code); return user_eval_inner(program, code); }
手続きオブジェクトを val0 に置いて VALUES_APPLY して RET するナニ。む、VALUES_APPLY って 0.8.x 系には無い vm instruction なのかなぁ。0.9 な vminsn.scm にて以下な定義がある。
;; VALUES_APPLY(nargs) <args> ;; This instruction only appears in the code generated dynamically ;; by Scm_Apply(Rec). This is used to pass the application information ;; across the boundary frame (see user_eval_inner() in vm.c). ;; When the VM sees this instruciton, VAL0 contains the procedure to ;; call, and VAL1... contains the arguments. ;; If nargs >= SCM_VM_MAX_VALUES-1, args[SCM_VM_MAX_VALUES-1] through ;; args[nargs-1] are made into a list and stored in VALS[SCM_VM_MAX_VALUES-1] (define-insn VALUES-APPLY 0 none #f (let* ([nargs::int (SCM_VM_INSN_ARG code)]) (CHECK-STACK (ENV-SIZE nargs)) (dotimes [i nargs] (when (>= i (- SCM_VM_MAX_VALUES 1)) (for-each (lambda (vv) (PUSH-ARG vv)) (aref (-> vm vals) (- SCM_VM_MAX_VALUES 1))) (break)) (PUSH-ARG (aref (-> vm vals) i))) ($goto-insn TAIL-CALL)))
なんとなくではありますが、ぱっと見で
- CHECK_STACK して
- vm->vals にセットした引数を PUSH_ARG して
- TAIL_CALL な命令に jmp
なカンジである事が分かります。てか、vminsn.scm 読む、というのもなかなか面白げですねぇ。
話を元に
apply_rec 手続きは末端で user_eval_inner 手続きを呼び出してその戻りを戻す。実はあまり難しく考えてなかったりして、単純に Scm_ApplyRec から手続きをナニした場合には
- run_loop 手続き呼び出して
- vm->val0 を戻す
という形なんだろうな、と。これはなかなか凄い。ただ、Scm_Repl 手続きを経由して Scm_ApplyRec が呼ばれるケイスはちょっと事情が異なります。
簡単に言っちゃうと repl_proc 手続きの戻りが戻されるカンジ。詳細は ReadingGauche でそのあたりに到達してからメモを投入するかと思いますが、そこまで行きつくにはまだ結構時間がかかりそげ。