問題 5.51

再開。一年少々前の仕掛りを発見。何故かディレクトリの名前が chroot.home だったのはどーゆー意味なのだろうか。

そりゃ良いとして

やりクサシ分の発想としては、基本的な scheme オブジェクトを C なレベルで操作できるようになっとけば op の実装も簡単にできるだろう、という事でした。
でも、その前段である scheme オブジェクトを C なレベルで云々な部分で挫折というか現職に就いてしまってそのままスルーになっているのか。(とほほ

えーと

よく考えたら REPL なラベルで以下な処理してて

  (perform (op nitialize-stack))
  (perform
    (op prompt-for-input) (const ";;; EC-Eval input:"))
  (assign exp (op read))

SICP の中では exp なレジスタに入ってるのはリストなんですが、Gauche ってどうなってるんだ、と言いつつ確認。バージョンは Gauche-0.8.11 ッス。まず main.c でエントリポイントは main() 手続きの末端部分の以下の

    } else {
        /* We're in interactive mode. (use gauche.interactive) */
        if (load_initfile) {
            if (Scm_Require(SCM_MAKE_STR("gauche/interactive"), 0, &lpak) < 0) {
                Scm_Warn("couldn't load gauche.interactive\n");
            } else {
                Scm_ImportModules(SCM_CURRENT_MODULE(),
                                  SCM_LIST1(SCM_INTERN("gauche.interactive")));
            }
        }

        if (batch_mode || (!isatty(0) && !interactive_mode)) {
            Scm_LoadFromPort(SCM_PORT(Scm_Stdin()), SCM_LOAD_PROPAGATE_ERROR,
                             NULL);
        } else {
            Scm_Repl(SCM_FALSE, SCM_FALSE, SCM_FALSE, SCM_FALSE);
        }

Scm_Repl() 手続きを掘削。repl.c にて定義。

void Scm_Repl(ScmObj reader, ScmObj evaluator, ScmObj printer,
              ScmObj prompter)
{
    Scm_ApplyRec(SCM_OBJ(&repl_STUB),
                 SCM_LIST4(reader, evaluator, printer, prompter));
}

引数全部 SCM_FALSE なんだよねー、と言いつつ repl_STUB は直上にて以下なマクロがナニ。

static SCM_DEFINE_SUBR(repl_STUB, 0, 1, SCM_OBJ(&repl_NAME), repl_proc, NULL, NULL);

ちなみに SCM_DEFINE_SUBR 関連情報が以下 (gauche.h)。

/* Subr - C defined procedure */
struct ScmSubrRec {
    ScmProcedure common;
    ScmSubrProc *func;
    void *data;
};

#define SCM_SUBRP(obj) \
    (SCM_PROCEDUREP(obj)&&(SCM_PROCEDURE_TYPE(obj)==SCM_PROC_SUBR))
#define SCM_SUBR(obj)              ((ScmSubr*)(obj))
#define SCM_SUBR_FUNC(obj)         SCM_SUBR(obj)->func
#define SCM_SUBR_DATA(obj)         SCM_SUBR(obj)->data

#define SCM_DEFINE_SUBR(cvar, req, opt, inf, func, inliner, data)           \
    ScmSubr cvar = {                                                        \
        SCM__PROCEDURE_INITIALIZER(SCM_CLASS2TAG(SCM_CLASS_PROCEDURE),      \
                                   req, opt, SCM_PROC_SUBR, inf, inliner),  \
        (func), (data)                                                      \
    }

repl_STUB という ScmSubr なオブジェクトの func 属性は repl_proc という事で手続きの実体が repl_proc と勝手読み。定義は Scm_Repl() 手続きの直上で以下。

static ScmObj repl_proc(ScmObj *args, int nargs, void *data)
{
    int argc = Scm_Length(args[0]);
    ScmObj reader =    (argc >= 1? SCM_CAR(args[0]) : SCM_FALSE);
    ScmObj evaluator = (argc >= 2? SCM_CADR(args[0]) : SCM_FALSE);
    ScmObj printer =   (argc >= 3? SCM_CAR(SCM_CDDR(args[0])) : SCM_FALSE);
    ScmObj prompter =  (argc >= 4? SCM_CADR(SCM_CDDR(args[0])) : SCM_FALSE);
    return Scm_VMRepl(reader, evaluator, printer, prompter);
}

えーと、末端の Scm_VMRepl() 手続きの定義も repl.c にて以下。

ScmObj Scm_VMRepl(ScmObj reader, ScmObj evaluator,
                  ScmObj printer, ScmObj prompter)
{
    ScmObj ehandler, reploop;
    ScmObj *packet = SCM_NEW_ARRAY(ScmObj, 4);
    packet[0] = reader;
    packet[1] = evaluator;
    packet[2] = printer;
    packet[3] = prompter;
    ehandler = Scm_MakeSubr(repl_error_handle, packet, 1, 0, SCM_FALSE);
    reploop = Scm_MakeSubr(repl_main, packet, 0, 0, SCM_FALSE);
    Scm_VMPushCC(repl_loop_cc, (void**)packet, 4);
    return Scm_VMWithErrorHandler(ehandler, reploop);
}

Scm_VMPushCC() ってどっかで見たな。ReadingGauche によれば_通常Scm_VMApply*関数が続く_とありますが続いてるのは若干違う?
とゆーコトで Scm_VMWithErrorHandler() 掘削。あ、違うな。このヒトは reploop を thunk にセットして

    return Scm_VMDynamicWind(before, thunk, after);

な模様。なので reploop に設定されてる repl_main ッスか。repl.c にて以下な定義。

static ScmObj repl_main(ScmObj *args, int nargs, void *data)
{
    ScmObj *closure = (ScmObj*)data;
    ScmObj prompter = closure[3];
    
    if (SCM_PROCEDUREP(prompter)) {
        Scm_VMPushCC(repl_prompt_cc, data, 4);
        return Scm_VMApply0(prompter);
    } else {
        Scm_Write(SCM_MAKE_STR("gosh> "),
                  SCM_OBJ(SCM_CUROUT), SCM_WRITE_DISPLAY);
        Scm_Flush(SCM_CUROUT);
        return repl_prompt_cc(SCM_UNDEFINED, (void**)data);
    }
}

とりあえず、引数 data にはなにも入ってないと見て repl_prompt_cc() 手続き掘削。repl.c にて以下。

static ScmObj repl_prompt_cc(ScmObj result, void **data)
{
    ScmObj *closure = (ScmObj*)data;
    ScmObj reader = closure[0];

    if (SCM_PROCEDUREP(reader)) {
        Scm_VMPushCC(repl_read_cc, data, 4);
        return Scm_VMApply0(reader);
    } else {
        ScmObj exp = Scm_Read(SCM_OBJ(SCM_CURIN));
        return repl_read_cc(exp, data);
    }
}

ようやく read が出た。なんとなく標準入力云々に見えます。Scm_Read() は read.c にて以下です。

ScmObj Scm_Read(ScmObj port)
{
    ScmReadContext ctx;
    read_context_init(Scm_VM(), &ctx);
    return Scm_ReadWithContext(port, &ctx);
}

Scm_ReadWithContext() を掘削。これも read.c ですね。ここでも実質 read_item() の呼び出しがキモと見て掘削。read.c にて以下。

static ScmObj read_item(ScmPort *port, ScmReadContext *ctx)
{
    for (;;) {
        ScmObj obj = read_internal(port, ctx);
        if (!SCM_UNDEFINEDP(obj)) return obj;
    }
}

次。read_internal() です。がしかし長い。ってかここが read の本体クサい。先頭部分のみ引用。

static ScmObj read_internal(ScmPort *port, ScmReadContext *ctx)
{
    int c = skipws(port, ctx);
    switch (c) {

skipws() 手続きで一文字づつ読みながら色々余計な情報を除去。そのまんまパクりたいカンジがしてたりするんですが (を

もう少し

repl.c の repl_read_cc() 手続きですが、末端の Scm_VMEval() 手続きもポイント高い。先頭のみ、ですが Scm_Compile() などという手続きが出てくる。

/* For now, we only supports a module as the evaluation environment */
ScmObj Scm_VMEval(ScmObj expr, ScmObj e)
{
    ScmObj v = SCM_NIL;
    int restore_module = SCM_MODULEP(e);
    ScmVM *vm = theVM;

    v = Scm_Compile(expr, e);

掘削してみると compaux.c にて以下な定義。

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

M-t で掘削不能。おそらくは compile.scm を起動して compile な手続きを呼び出しているのではないかと類推。たぶん正確には違うと思いますが。

掘削が

止まらなくなっちゃったんですが、継続あたりを色々確認してたお陰で以前よりもこのあたりがイメージしやすくなってるのにびっくり (掘削なプロセスで読み違いがあるかもしれませんが)。
とりあえず、read については read.c にて定義されている read_internal() を参考にすればなんとかなりそげ、とは言えこれを使うとなると C の手続きの中で使うのは scheme オブジェクトになりますな。
ただ、微妙なハードルと見ていた quote のナニも処理されているという意味でも実装は興味深いです。

しかし

問題の主旨としての_Scheme の C での初等的な実装_に沿っているのかどうなのか、は微妙な気がしています。カンニングしてるからこうなってしまうんだろうな、とは思いますが。