* acl4のページ0013
-(by [[K]], 2026.02.11)

** (1)
 #if (a_Version >= 1)
     #define Preprocessor_incl0          a_Preprocessor_incl0
     #define Preprocessor_Put0           a_Preprocessor_Put0
     #define Preprocessor_Put0_ini       a_Preprocessor_Put0_ini
     #define Preprocessor_Put0_din       a_Preprocessor_Put0_din
     #define Preprocessor_put0           a_Preprocessor_put0
     #define Preprocessor                a_Preprocessor
     #define Preprocessor_ini            a_Preprocessor_ini
     #define Preprocessor_din            a_Preprocessor_din
     #define Preprocessor_line           a_Preprocessor_line
     #define Preprocessor_main           a_Preprocessor_main
 #endif
 
 a_static int a_Preprocessor_incl0(void *w, char *s, char *s1, a_Preprocessor_SourceFiles *sfs)
 {
     (void) w;
     while (s < s1 && *(unsigned char *) s <= ' ') s++;
     if (*s == 0x22) {
         char *t = strchr(s + 1, 0x22);
         if (t == NULL) t = s1;
         a_Preprocessor_SourceFiles_addFile(sfs, s + 1, t - (s + 1));
         return 1;
     }
     if (*s == '<') {
         char *t = strchr(s + 1, '>');
         if (t == NULL) t = s1;
         if (t[-2] == '.' && t[-1] == 'h') return 0; // through.
         a_Preprocessor_SourceFiles_addFile(sfs, s + 1, t - (s + 1));
         return 1;
     }
     return 0;
 }
 
 a_class(a_Preprocessor_Put0) {
     a_Preprocessor_SourceFiles *sfs;
     a_VecChr *dst, path[1];
     intptr_t lin;
 };
 
 a_static void a_Preprocessor_Put0_ini(a_Preprocessor_Put0 *w) { a_VecChr_ini(w->path); w->lin = 0; }
 a_static void a_Preprocessor_Put0_din(a_Preprocessor_Put0 *w) { a_VecChr_din(w->path); }
 
 a_static void a_Preprocessor_put0(a_Preprocessor_Put0 *w, a_VecChr *lin)
 {
     a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
     if (w->path->n != sf->path->n || (w->path->n > 0 && memcmp(w->path->p, sf->path->p, w->path->n) != 0)) {
         char *s = a_malloc(_arg_  sf->path->n + 64);
         sprintf(s, "#line %d \"%.*s\"\n", (int) sf->lin, (int) sf->path->n, sf->path->p);
         a_VecChr_puts(w->dst, s, s + strlen(s));
         a_free(_arg_  s, sf->path->n + 64);
         a_VecChr_replace(w->path, 0, w->path->n, sf->path->p, sf->path->n);
     } else if (w->lin + 1 < sf->lin && sf->lin < w->lin + 9) {
         while (w->lin + 1 < sf->lin) {
             char *s = "\n";
             a_VecChr_puts(w->dst, s, s + 1);
             w->lin++;
         }
     } else if (w->lin + 1 != sf->lin) {
         char s[64];
         sprintf(s, "#line %d\n", (int) sf->lin);
         a_VecChr_puts(w->dst, s, s + strlen(s));
     }
     a_VecChr_puts(w->dst, lin->p, lin->p + lin->n);
     w->lin = sf->lin;
 }
 
 a_class(a_Preprocessor) {
     a_Token0 t0[1];
     a_Preprocessor_SourceFiles sfs[1];
     a_Set0 set[1];
     a_Preprocessor_If ppIf[1];
     a_Preprocessor_Eval ppEv[1];
     a_Preprocessor_Put0 put0[1];
     int (*incl)(void *, const char *, const char *, a_Preprocessor_SourceFiles *);
     void (*put)(void *, a_VecChr *, a_Preprocessor_SourceFiles *);
     void *w_incl, *w_put;
 };
 
 a_static void a_Preprocessor_ini(a_Preprocessor *w)
 {
     a_Token0_ini1(w->t0);
     a_Preprocessor_SourceFiles_ini(w->sfs);
     a_Preprocessor_if0(w->ppIf);
     a_Set0_ini(w->set);
     // 以下はとりあえずのデフォルト設定. 不要なら上書きする.
     w->ppIf->eval = (void *) a_Preprocessor_eval;
     w->ppIf->w_eval = w->ppEv;
     w->incl = (void *) a_Preprocessor_incl0;
     w->w_incl = NULL;
     a_Preprocessor_Put0_ini(w->put0);
     w->put = (void *) a_Preprocessor_put0;
     w->w_put = w->put0;
     w->put0->sfs = w->sfs;
     a_Preprocessor_define7(w->set, -1, "ifdef",    "if    defined");
     a_Preprocessor_define7(w->set, -1, "ifndef",   "if   !defined");
     a_Preprocessor_define7(w->set, -1, "elifdef",  "elif  defined");
     a_Preprocessor_define7(w->set, -1, "elifndef", "elif !defined");
 }
 
 a_static void a_Preprocessor_din(a_Preprocessor *w)
 {
     a_Preprocessor_define3(w->set);
     a_Preprocessor_if3(w->ppIf);
     a_Preprocessor_SourceFiles_din(w->sfs);
     a_Preprocessor_Put0_din(w->put0);
 }
 
 a_static void a_Preprocessor_line(a_Preprocessor *w)
 {
     a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
     const char *t = a_Token1_get(w->t0);
     sf->lin = strtol(t, NULL, 0) - 1;
     t = a_Token1_get(w->t0);
     if (w->t0->len >= 2 && *t == 0x22) {
         sf->path->n = 0;
         a_VecChr_puts(sf->path, t + 1, t + w->t0->len - 1);
     }
 }
 
 a_static void a_Preprocessor_main(a_Preprocessor *w)
 {
     int defC = 1, retryCount = 0;
     a_VecChr lin[1], tmp[1], stkDef[1]; a_VecChr_ini4(lin, tmp, stkDef, 0);
     for (;;) {
         if (w->sfs->s >= w->sfs->buf->p + w->sfs->buf->n) retryCount = 0;
         a_Preprocessor_SourceFiles_gets(w->sfs, lin);
         if (lin->n == 0) break;
         for (;;) { // 末尾が "\\\n"であれば、後続行を接続.
             char *p1 = lin->p + lin->n;
             if (!(lin->n >= 2 && p1[-2] == '\\' && p1[-1] == '\n')) break;
             a_Preprocessor_SourceFiles_gets(w->sfs, tmp);
             a_VecChr_replace(lin, lin->n - 2, 2, tmp->p, tmp->n);
         }
         if (retryCount >= 64) {
             if (w->ppIf->phase != 0)
                 w->put(w->w_put, lin, w->sfs);
             continue;
         }
         if (a_Preprocessor_isDirective(lin->p, lin->p + lin->n) == 0) {
             a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
             if (a_Preprocessor_define2(w->set, w->t0, lin, defC, sf->path, sf->lin, 0) != 0) {
 retry:
                  a_Preprocessor_SourceFiles_addBuf(w->sfs, lin->p, lin->n);
                  retryCount++; continue;
             }
             if (w->ppIf->phase != 0)
                 w->put(w->w_put, lin, w->sfs);
             continue;
         }
         const char *s = lin->p, *s1 = lin->p + lin->n;
         while (s < s1 && *(const unsigned char *) s <= ' ') s++;
         s++; // '#'.
         if (strncmp(s, "if", 2) == 0 || strncmp(s, "elif", 4) == 0) {
             a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
             if (a_Preprocessor_define2(w->set, w->t0, lin, defC, sf->path, sf->lin, 0) != 0) goto retry;
         }
         w->t0->s = s + 1; w->t0->s1 = lin->p + lin->n; a_Token1_get(w->t0);
         if (w->ppIf->phase != 0) {
             if (strncmp(s, "include", 7) == 0) { if (w->incl(w->w_incl, s + 7, s1, w->sfs) > 0) continue; }
             if (strncmp(s, "define", 6) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 0); continue; }
             if (strncmp(s, ".def",   4) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 1); continue; }
             if (strncmp(s, ".addTl", 6) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 2); continue; }
             if (strncmp(s, ".addHd", 6) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 3); continue; } 
             if (strncmp(s, "undef",  5) == 0) { if (a_Preprocessor_define6(w->set, w->t0, lin, stkDef, 0) > 0) continue; }
             if (strncmp(s, ".undef", 6) == 0) { if (a_Preprocessor_define6(w->set, w->t0, lin, stkDef, 1) > 0) continue; }
             if (strncmp(s, "line",   4) == 0) { a_Preprocessor_line(w); continue; }
         }
         w->t0->s = s; w->t0->s1 = lin->p + lin->n;
         char *t = a_Token1_get(w->t0);
         if (a_Preprocessor_if2(w->ppIf, lin, w->t0, t) > 0) continue;
         if (w->ppIf->phase != 0)
             w->put(w->w_put, lin, w->sfs); // エラー検出のため.
         continue;
     }
     while (stkDef->n > 0) {
         a_Preprocessor_Define *sp = a_VecChr_stkRmv(stkDef, sizeof (a_Preprocessor_Define));
         a_Preprocessor_Define_din(sp);
     }
     a_VecChr_din4(lin, tmp, stkDef, 0);
 }

** (2) 説明
-Preprocessor_incl0 → #include 処理をどうするかをプログラム側でカスタマイズできるようにしています。これはデフォルトで提供される関数です。
-Preprocessor_put0 → プリプロセッサからの出力処理をプログラム側でカスタマイズできるようにしています。これはデフォルトで提供される関数です。
-Preprocessor_line → #line の処理関数です。
-Preprocessor_main → プリプロセッサ処理のmainです。

** (3) サンプルプログラム: t0013a.c [13行]
-acl4ライブラリは以下のようにして利用します。
-[1]まずa4_0001~a4_0013のプログラムをつなげて"acl4.c"として保存します。
-[2]次にacl4を使ったプログラムを書きます。
-[3]コンパイル時に、"acl4.c"のあるパスをインクルードパスの一つとして指定します。

 #define a_Version 1
 #include <acl4.c>
 
 int main(int argc, const char **argv)
 {
     if (argc < 2) return 1;
     Preprocessor pp[1]; Preprocessor_ini(pp);                            // プリプロセッサの初期化.
     Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 最初に読むファイルを指定.
     VecChr dst[1]; VecChr_ini(dst); pp->put0->dst = dst;                 // 出力先のオブジェクトを指定.
     Preprocessor_main(pp); Preprocessor_din(pp);                         // プリプロセッサ処理. およびメモリ開放.
     VecChr_reserve0(dst); puts(dst->p); VecChr_din(dst);                 // 処理結果を出力. およびメモリ開放.
     a_malloc_debugList(_arg);                                            // メモリリークがないか確認.
 }
-これは、ちゃんと動く(と思われる)プリプロセッサです。acl4ライブラリを使うことで、13行で書けています。
-動作テストとして、t0013a.exe を使って t0013a.c 自身をプリプロセスして、その結果をコンパイラにかけたところ問題なくビルドできていたので、たぶん大きなバグはないと思います。
-手元の環境では t0013a.exe は 20.0KB になりました(まあ私の基準では小さくはないですが、大きくもないです)。

-ちなみに「 prompt>t0013a t0013a.c > t0013a.txt 」とすると、こんな感じのファイルができます。
 #line 7 "a4_0001.c"
 
 #line 19
 
 #include <ctype.h>
 #include <errno.h>
 #include <float.h>
 (中略)
 #line 3 "t0013a.c"
 
 int main(int argc, const char **argv)
 {
     if (argc < 2) return 1;
     a_Preprocessor pp[1]; a_Preprocessor_ini(pp);                            // プリプロセッサの初期化.
     a_Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 最初に読むファイルを指定.
     a_VecChr dst[1]; a_VecChr_ini(dst); pp->put0->dst = dst;                 // 出力先のオブジェクトを指定.
     a_Preprocessor_main(pp); a_Preprocessor_din(pp);                         // プリプロセッサ処理. およびメモリ開放.
     a_VecChr_reserve0(dst); puts(dst->p); a_VecChr_din(dst);                 // 処理結果を出力. およびメモリ開放.
     a_malloc_debugList("t0013a.c", 12);                                              // メモリリークがないか確認.
 }
-目視でも、正しく変換できていると思います。
-なお、標準ライブラリの #include が処理されずにそのままになっているのは意図的なものです。
-Preprocessor_incl0 で < ... .h> な include については、インクルード処理をするなと明記しているからです。
--まだインクルードパスを探す処理を書いていないので、無理にやっても失敗してしまうのです。

** (3) サンプルプログラム: t0013b.c [101行]
-[[a4_p0002]] の p0002b.c の main だけを改造して、この言語にプリプロセッサ機能を加えるということをやってみました。

-acl4ライブラリは以下のようにして利用します。
-[1]まずa4_0001~a4_0013のプログラムをつなげて"acl4.c"として保存します。
-[2]次にacl4を使ったプログラムを書きます。
-[3]コンパイル時に、"acl4.c"のあるパスをインクルードパスの一つとして指定します。

 #define a_Version 1
 #include <acl4.c>
 
 class_(Var) { SetElm elm[1]; double d; };
 Set0 setVar[1];
 
 double *getVarPtr(const char *s, intptr_t n) → p0002b.c のものをそのまま使います
 
 int np = 0;
 
 double eval(Token0 *t0, int pri) → p0002b.c のものをそのまま使います
 
 void myPut(Preprocessor_Put0 *w, VecChr *lin)
 {
     // エラーが起きて未処理扱いになったプリプロセッサ制御行があれば、それはエラー出力して変換結果に含めない.
     if (Preprocessor_isDirective(lin->p, lin->p + lin->n) != 0)
         fprintf(stderr, "[err] %.*s", (int) lin->n, lin->p);
     else
         VecChr_puts(w->dst, lin->p, lin->p + lin->n);
 }
 
 int main(int argc, const char **argv)
 {
     if (argc < 2) return 1;
     Preprocessor pp[1]; Preprocessor_ini(pp);                            // プリプロセッサの初期化.
     pp->put = (void *) myPut;                                            // 出力関数の変更.
     Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 入力ファイルを指定.
     VecChr vc[1]; VecChr_ini(vc); pp->put0->dst = vc;                    // 出力先のオブジェクトを指定.
     Preprocessor_main(pp); Preprocessor_din(pp);                         // プリプロセッサ処理. およびメモリ開放.
 
     Token0 t0[1]; Token0_ini1(t0); // ここから下は p0002b.c の main にあったものをそのまま使用.
     Set0_ini(setVar);
     t0->s = vc->p; t0->s1 = vc->p + vc->n;
     double d = eval(t0, 99);
     if (np == 0)
          printf("%f\n", d);
     VecChr_din(vc);
     intptr_t i, n = setVar->tbl->n / sizeof (Var *);
     for (i = 0; i < n; i++)
         free_(_arg_  ((Var **) setVar->tbl->p)[i], sizeof (Var));
     Set0_din(setVar);
     a_malloc_debugList(_arg);
     return 0;
 }
-元の [[p0002b.c>a4_p0002]] は86行だったので、行数的には''15行''しか増えていません。
-でもこれだけで、 #include や #if や #define が使えるようになります。

~

-増えた行数は15行ですが、では実行ファイルサイズとしてはどうでしょうか。
--p0002b.exe → 13.5KB
--t0013b.exe → 22.0KB
-ということで、''8.5KB''しか増えていません(これはいい!!)。
-acl4を使って作った適当な自作言語があったとして、それに15行を書き足して8.5KBの増加を許容すれば、プリプロセッサ機能が手に入るわけです。

~

-もし「プログラム内でincludeを書かなくても、決まったファイルをインクルードした状態で始まってほしい」場合は、
     Preprocessor_SourceFiles_addFile(pp->sfs, "default.txt", 11);
-という1行を「入力ファイルを指定」の直後に書き足せば実現できます。
--default.txt に #define Pi 3.14159265358979323 とかを書き並べておけば、これらが最初から定義済みの状態で使えるわけです。便利!

~

-もし「いや、デフォルトで定義しておきたいのは Pi だけしかなくて、そのためだけに default.txt を用意するのはかえってみっともない」と思う場合は、
     Preprocessor_define7(pp->set, -1, "Pi", "3.14159265358979323");
-を「入力ファイルを指定」の直前に書き足せば実現できます。

** (99) 更新履歴
-2026.02.11(水) 初版
-2026.02.12(木) t0013b.c を追記
-2026.02.15(日) a_Preprocessor_main を修正。retry回数を最大で64回にした

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS