Scm_VMRepl 掘削メモ

現実トウヒがてら、ざくっと掘削したメモとか帰宅後に掘削したメモとか。
repl.c の Scm_Repl 手続き (L192 にて定義) を起点として、ここから apply されるのが直上で定義されている repl_proc 手続き (L179 にて定義)。これ、read-eval-print-loop という名前の特殊形式。
確認したらあった。

gosh> read-eval-print-loop
#<subr read-eval-print-loop>
gosh> 

で、この特殊形式には 1 〜 4 つの要素を持つリストを渡せば良い格好になっている模様。このリストの要素を引数に Scm_VMRepl 手続き (repl.c の L164 にて定義) が呼び出される。

ざっくり掘削

その前に Scm_VMRepl 手続きを精査。Scm_VMWithErrorHandler 手続きに渡す ScmObj 型の二つの変数ですが

ScmObj Scm_VMRepl(ScmObj reader, ScmObj evaluator,
                  ScmObj printer, ScmObj prompter)
{
    ScmObj ehandler, reploop;

Scm_MakeSubr 手続きな戻りを格納。

    ehandler = Scm_MakeSubr(repl_error_handle, packet, 1, 0, SCM_FALSE);
    reploop = Scm_MakeSubr(repl_main, packet, 0, 0, SCM_FALSE);

Scm_MakeSubr 手続きの定義は proc.c の L92 で以下。

ScmObj Scm_MakeSubr(ScmSubrProc *func,
                    void *data,
                    int required, int optional,
                    ScmObj info)
{
    ScmSubr *s = SCM_NEW(ScmSubr);
    SCM_SET_CLASS(s, SCM_CLASS_PROCEDURE);
    SCM_PROCEDURE_INIT(s, required, optional, SCM_PROC_SUBR, info);
    s->func = func;
    s->data = data;
    return SCM_OBJ(s);
}

ScmSubrProc って何? と言いつつ掘ると gauche.h の L474 にて以下な定義。

typedef ScmObj ScmSubrProc(ScmObj *, int, void*);

なるほど、と言いつつ見てみると repl_error_handle 手続きの定義が以下で

static ScmObj repl_error_handle(ScmObj *args, int nargs, void *data)

repl_main 手続きの定義が以下。

static ScmObj repl_main(ScmObj *args, int nargs, void *data)

呼び出される時にそれぞれの引数に何がセットされるのかは現時点では謎。ただ Scm_MakeSubr 手続きのナニが ReadingGauche にあります。

上記によれば required な引数は引数の数な模様。という事は repl_error_handle は引数を一つ取るけど、repl_main は thunk な手続きという事になる。
手続き定義を見るに確かにそんな実装になってるな。
で、次に C-continuation を push する Scm_VMPushCC 手続きを呼び出してます。

    Scm_VMPushCC(repl_loop_cc, (void**)packet, 4);

定義は vm.c の L1219 でてっぺんが以下。

void Scm_VMPushCC(ScmCContinuationProc *after,
                  void **data, int datasize)

ScmCContinuationProc 型を掘ってみたら gauche/vm.h の L601 にて定義されてて以下。

typedef ScmObj ScmCContinuationProc(ScmObj result, void **data);

repl_loop_cc 手続きのてっぺんの定義は以下。

static ScmObj repl_loop_cc(ScmObj result, void **data)

型としては整合してます。ここで push されてるこの手続きはスタックのいっちゃんてっぺんにある、って思ってて良いはず。
# てか push されるの初めてですわな
で、次が最後。

    return Scm_VMWithErrorHandler(ehandler, reploop);

Scm_VMWithErrorHandler 手続きは ReadingGauche に記述があって、概要欄には

thunk の実行。エラーはエラーハンドラ handler で受け付ける。
例外ハンドラが最初に呼ばれ、そのとき動的ハンドラを巻き戻す srfi-18 のモデル。

との記述がある。ので reploop (というか repl_main 手続き) が呼び出される、という事になるのか。とりあえず Scm_VMWithErrorHandler 手続きとか掘るの嫌だなぁ、と思いつつググッてみたら以下な過去ログを発見。

なんか同じあたりを穿りかえしてる模様。この時は read を何とか理解、というか実装しようとしてたのか。

ちょっとズルして compaux.c を見てみた。Scm_Compile あたりが以下。

/*
 * Compiler Entry
 */

static ScmGloc *compile_gloc = NULL;
static ScmGloc *compile_partial_gloc = NULL;
static ScmGloc *compile_finish_gloc = NULL;
static ScmGloc *init_compiler_gloc = NULL;

static ScmInternalMutex compile_finish_mutex;

ScmObj Scm_Compile(ScmObj program, ScmObj env)
{
    return Scm_ApplyRec2(SCM_GLOC_GET(compile_gloc), program, env);
}

compile_gloc で grep してみたら以下。

$ find|xargs grep compile_gloc
./compaux.c:static ScmGloc *compile_gloc = NULL;
./compaux.c:    return Scm_ApplyRec2(SCM_GLOC_GET(compile_gloc), program, env);
./compaux.c:    INIT_GLOC(compile_gloc,         "compile",       gi);
$

一番最後のソレは Scm__InitCompaux 手続きにてナニ。このあたり、0.8 な ReadingGauche を確認しつつ、0.9 に反映させつつ掘ってみる方向で今日も早めに休みます。