スタートアップ

nptのドキュメントです。
参照元:ANSI Common Lisp npt
前へ:脱出の操作
次へ:Paperオブジェクト

7.1 スタートアップ

本章ではC言語にて、nptをどのようにして開始するかを説明します。

C言語ではmain関数から実行されますが、Windowsの場合はWinMain関数ですので、 両者でどのようにnptを初期化するのかを説明します。

7.2 開始と終了

nptを開始するには、まずは次の関数を実行します。

void lisp_init(void);

この関数は、npt内のC言語で使用される静的変数を初期化し、 使用準備ができたことを宣言します。
対応する解放処理は下記の関数になります。

void lisp_free(void);

初期化処理を実行したあとは、関数ポインタの登録を実行することができます。
関数ポインタの登録とは、例えば下記の関数を用いた処理のことです。

void lisp_compiled_rest(int index, lisp_calltype_rest call);

関数ポインタの値は一度実行してしまえばlisp_freeで解放するまで持続します。

7.3 コマンド引数の読み込み

コマンド引数とは、main関数のargc, argv, envのことです。
読み込みに使用する関数は下記のとおりです。

struct lispargv *lispargv_main(int argc, char *argv[], char *env[]);

エラー時にはNULLが返却されます。
動的にメモリが確保されますので、終了時には下記の解放処理が必要です。

void lispargv_free(struct lispargv *ptr);

実行例を下記に示します。

int main(int argc, char *argv[], char *env[])
{
    struct lispargv *args;

    lisp_init();
    args = lispargv_main(argc, argv, env);
    if (args == NULL) {
        fprintf(stderr, "argv error\n");
        return 1;
    }

    /* ここにnptの処理を記載する */

    /* free */
    lispargv_free(args);
    lisp_free();

    return 0;
}

lispargv_main関数は、Windows環境で実行するとargc, argv, envは全て無視され、 代わりに下記の関数を強制的に実行します。

struct lispargv *lispargv_windows(void);

この関数はlispargv_mainと同等ですが、 引数と環境変数の情報をWIN32APIから取得します。
もしWinMain関数から起動する場合は、 argcのような引数は存在しないため、上記の関数を使用するしかないはずです。
しかしWindows環境でもmain関数を使用することはできます。
そのような場合でも、文字コードを正しく処理するために、 強制的にlispargv_windowsが使用されます。
もしlispargv_windowsではなく、 main関数の引数から実行したい場合は 下記の関数を使用してください。

struct lispargv *lispargv_main_force(int argc, char *argv[], char *env[]);

この関数は、Windows環境であっても強制的に引数を読み込みます。

もし、argc, argv, envが存在しないか、 あるいは指定したくない場合は下記のように実行をして下さい。

args = lispargv_main_force(0, NULL, NULL);

7.4 nptの設定

nptをC言語に組み込んで使用する場合には、 引数で変更されては困るものも存在します。
今回は次のように実行して、 standaloneモードでの実行を強制することにします。

args->mode_core = 0;
args->mode_degrade = 0;
args->mode_standalone = 1;
args->nocore = 1;
args->noinit = 1;
args->debugger = 1;
args->debuggerp = 0;
args->quit = 1;

もしメモリの大きさを変更したい場合は次のようにします。

args->heap = 1024UL * 1024UL*1024UL;  /* 1G */
args->local = 256UL * 1024UL*1024UL;  /* 256M */

heapは、heap領域の大きさです。
localは、stack領域の大きさです。
どちらもnptが実行される初期段階でmallocにより確保されます。

以上のことをまとめると、代表的なmain関数は下記のようになります。

int main(int argc, char *argv[], char *env[])
{
    int result;
    struct lispargv *args;

    /* initialize */
    lisp_init();
    args = lispargv_main(argc, argv, env);
    if (args == NULL) {
        fprintf(stderr, "argv error\n");
        return 1;
    }

    /* main_argv */
    args->mode_core = 0;
    args->mode_degrade = 0;
    args->mode_standalone = 1;
    args->nocore = 1;
    args->noinit = 1;
    args->debugger = 1;
    args->debuggerp = 0;
    args->quit = 1;
    result = main_argv(args);

    /* free */
    lispargv_free(args);
    lisp_free();

    return result;
}

以降はmain_argv関数を作成していきます。

7.5 ヘルプの表示

もし引数に--helpなどが指定されていた場合は、 この段階で情報の表示を行い、実行を終了させます。

static int main_argv(struct lispargv *args)
{
    if (args->mode_help)
        return lisp_main_help(stdout);
    if (args->mode_version)
        return lisp_main_version(args, stdout);
    if (args->mode_degrade)
        return lisp_main_degrade(args);
 
   /* 途中 */

    return 0
}

これで下記の引数に対応できました。

7.6 main_lispの実行

次に行うことは、npt環境の整備です。
下記の命令にて実行します。

int lisp_argv_init(struct lispargv *ptr);
int lisp_argv_run(struct lispargv *ptr);

lisp_argv_init関数で準備し、lisp_argv_runでLispを実行します。
これまでの例文のように、main_lisp関数を実行するには、 args->callを変更することで実現できます。

実行例を下記に示します。

 int main_lisp(void *call_ptr)
{
    return 0;
}

static int main_argv(struct lispargv *args)
{
    /* mode */
    if (args->mode_help)
        return lisp_main_help(stdout);
    if (args->mode_version)
        return lisp_main_version(args, stdout);
    if (args->mode_degrade)
        return lisp_main_degrade(args);

    /* execute */
    args->call = main_lisp;
    args->call_ptr = NULL;
    lisp_argv_init(args);
    lisp_argv_run(args);

    return lisp_code? 1: lisp_result;
}

本来であればlisp_argv_initlisp_argv_runの 戻り値をチェックする必要があるのですが、 もしエラーが生じていた場合はlisp_codeの値が1になり、 さらにlisp_argv_run関数が必ず失敗するのでチェックは無視します。

args->callに登録したmain_lisp関数は、 args->call_ptrを引数として呼び出されます。
なぜ関数ポインタで登録する必要があるかというと、 Common Lispとして正しく動作するさせるためには 様々な設定をしてから実行する必要があるためです。
一例をあげるなら、warn関数で使用するhandler-bindの処理です。

main_lisp関数は脱出関数ですが、 普通の脱出関数とは少々違っており、 もし非脱出時に0以外の値が返却された場合は 異常終了とみなして後の処理を全て無視して、 終了コード1で終了します。

7.7 まとめ

以上の内容をまとめますと、 最初にnptをC言語で使用するで説明した 例文そのものになります。

下記に例文を示します。

int main_lisp(void *call_ptr)
{
    return lisp_format8_(NULL, "Hello~%", NULL);
}

static int main_argv(struct lispargv *args)
{
    /* mode */
    if (args->mode_help)
        return lisp_main_help(stdout);
    if (args->mode_version)
        return lisp_main_version(args, stdout);
    if (args->mode_degrade)
        return lisp_main_degrade(args);

    /* execute */
    args->call = main_lisp;
    args->call_ptr = NULL;
    lisp_argv_init(args);
    lisp_argv_run(args);

    return lisp_code? 1: lisp_result;
}

int main(int argc, char *argv[], char *env[])
{
    int result;
    struct lispargv *args;

    /* initialize */
    lisp_init();
    args = lispargv_main(argc, argv, env);
    if (args == NULL) {
        fprintf(stderr, "argv error\n");
        return 1;
    }

    /* main_argv */
    args->mode_core = 0;
    args->mode_degrade = 0;
    args->mode_standalone = 1;
    args->nocore = 1;
    args->noinit = 1;
    args->debugger = 1;
    args->debuggerp = 0;
    args->quit = 1;
    result = main_argv(args);

    /* free */
    lispargv_free(args);
    lisp_free();

    return result;
}

実行結果は下記のとおりです。

Hello