* acl4のページ0010
-(by [[K]], 2026.02.06)

** (1)
 #if (a_Version >= 1)
     #define Preprocessor_Eval   a_Preprocessor_Eval
     #define Preprocessor_eval   a_Preprocessor_eval
     #define Preprocessor_If     a_Preprocessor_If
     #define Preprocessor_if0    a_Preprocessor_if0
     #define Preprocessor_if2    a_Preprocessor_if2
     #define Preprocessor_if3    a_Preprocessor_if3
 #endif
 
 a_class(a_Preprocessor_Eval) { char err; };
 
 a_static intptr_t a_Preprocessor_eval(a_Preprocessor_Eval *w, a_Token0 *t0, int pri)
 // この関数の利用者は、 pri=0x7fff として呼び出すことを想定.
 {
     const char *t = a_Token1_get(t0); intptr_t i = 0, j, n = t0->len; uint32_t c = t0->c;
     if (n == 0 || c == ('/' | '/' << 8)) { w->err = 1; return i; }
     if (c == '(') {
         i = a_Preprocessor_eval(w, t0, 99);
         a_Token1_get(t0);
         if (t0->c != ')') w->err = 1;
         goto op2;
     }
     if (c == '+' && pri >= 2) { i = + a_Preprocessor_eval(w, t0, 2); goto op2; }
     if (c == '-' && pri >= 2) { i = - a_Preprocessor_eval(w, t0, 2); goto op2; }
     if (c == '!' && pri >= 2) { i = ! a_Preprocessor_eval(w, t0, 2); goto op2; }
     if (c == '~' && pri >= 2) { i = ~ a_Preprocessor_eval(w, t0, 2); goto op2; }
     if ('0' <= *t && *t <= '9')
     if ('0' <= *t && *t <= '9') {
         i = strtol(t, NULL, 0);
     else {
         if (&t[n] < t0->s1 && t[n] == '(') {
             // 未定義マクロの引数部分を読み飛ばす. 値は0とする.
             a_Token1_get(t0);
             a_VecChr dmy[1]; a_VecChr_ini(dmy);
             a_parseArgs(t0, dmy);
             if (t0->c != ')') w->err = 1;
             a_VecChr_din(dmy);
         }
     } else if (n >= 2 && *t == 0x27) { // '...'
         a_VecChr vc[1]; a_VecChr_ini(vc); a_VecChr_puts(vc, t + 1, t + n - 1);
         a_VecChr_convEsc(vc); vc->p[vc->n] = '\0'; i = vc->p[0];
         a_VecChr_din(vc);
     } else if (&t[n] < t0->s1 && t[n] == '(') {
         // 未定義マクロの引数部分を読み飛ばす. 値は0とする.
         a_Token1_get(t0);
         a_VecChr dmy[1]; a_VecChr_ini(dmy);
         a_parseArgs(t0, dmy);
         if (t0->c != ')') w->err = 1;
         a_VecChr_din(dmy);
     }
     // それ以外の知らないトークンは引数なし未定義マクロだと解釈し、すべて値0とする.
 op2:
     t = a_Token1_get(t0); c = t0->c; n = t0->len;
 //  if (n == 0) { w->err = 1; return i; }
     if (c ==  '*'             && pri >=  4) { i = i *  a_Preprocessor_eval(w, t0,  3); goto op2; }
     if (c ==  '+'             && pri >=  5) { i = i +  a_Preprocessor_eval(w, t0,  4); goto op2; }
     if (c ==  '-'             && pri >=  5) { i = i -  a_Preprocessor_eval(w, t0,  4); goto op2; }
     if (c == ('<' | '<' << 8) && pri >=  6) { i = i << a_Preprocessor_eval(w, t0,  5); goto op2; }
     if (c == ('>' | '>' << 8) && pri >=  6) { i = i >> a_Preprocessor_eval(w, t0,  5); goto op2; }
     if (c == ('<' | '=' << 8) && pri >=  7) { i = i <= a_Preprocessor_eval(w, t0,  6); goto op2; }
     if (c ==  '<'             && pri >=  7) { i = i <  a_Preprocessor_eval(w, t0,  6); goto op2; }
     if (c == ('>' | '=' << 8) && pri >=  7) { i = i >= a_Preprocessor_eval(w, t0,  6); goto op2; }
     if (c ==  '>'             && pri >=  7) { i = i >  a_Preprocessor_eval(w, t0,  6); goto op2; }
     if (c == ('=' | '=' << 8) && pri >=  8) { i = i == a_Preprocessor_eval(w, t0,  7); goto op2; }
     if (c == ('!' | '=' << 8) && pri >=  8) { i = i != a_Preprocessor_eval(w, t0,  7); goto op2; }
     if (c ==  '&'             && pri >=  9) { i = i &  a_Preprocessor_eval(w, t0,  8); goto op2; }
     if (c ==  '^'             && pri >= 10) { i = i ^  a_Preprocessor_eval(w, t0,  9); goto op2; }
     if (c ==  '|'             && pri >= 11) { i = i |  a_Preprocessor_eval(w, t0, 10); goto op2; }
     if (c == ('&' | '&' << 8) && pri >= 12) { i = i && a_Preprocessor_eval(w, t0, 11); goto op2; }
     if (c == ('|' | '|' << 8) && pri >= 13) { i = i || a_Preprocessor_eval(w, t0, 12); goto op2; }
     if (c ==  '/'             && pri >=  4) {
         j = a_Preprocessor_eval(w, t0,  3);
         if (j != 0) { i = i / j; } else { w->err = 1; i = 0; }
         goto op2;
     }
     if (c ==  '%'             && pri >=  4) {
         j = a_Preprocessor_eval(w, t0,  3);
         if (j != 0) { i = i % j; } else { w->err = 1; i = 0; }
         goto op2;
     }
     t0->s = t; return i; // 一度読み込んだ未解釈の演算子をt0に押し戻してからreturn.
 }
 
 a_class(a_Preprocessor_If) {
     a_VecChr stk[1];
     char phase;
     int (*eval)(a_Preprocessor_Eval *, a_Token0 *, int);
     a_Preprocessor_Eval *w_eval;
 };
 
 a_static void a_Preprocessor_if0(a_Preprocessor_If *w)
 {
     a_VecChr_ini(w->stk); a_VecChr_reserve(w->stk, 16); w->stk->n = 1; w->stk->p[0] = 5;
     w->phase = 1;
 }
 
 a_static int a_Preprocessor_if2(a_Preprocessor_If *w, a_VecChr *linBuf, a_Token0 *t0, char *t)
 {
     intptr_t n = t0->len; (void) linBuf;
     w->w_eval->err = 0;
     if (n == 2 && memcmp(t, "if", 2) == 0) {
         a_VecChr_resizeDiff(_arg_  w->stk, 1); char *sp = &(w->stk->p[w->stk->n - 1]); *sp = 0;
         if ((sp[-1] & 1) != 0 && w->eval(w->w_eval, t0, 0x7fff) != 0) *sp = 5;
         a_Token1_get(t0);
         if ((t0->c != 0 && t0->c != ('/' | '/' << 8)) || w->w_eval->err != 0) {
             if ((sp[-1] & 1) != 0) *sp = 5;
             w->phase = *sp & 1; return 0;
         }
         w->phase = *sp & 1;
         return 1;
         // *spの値の説明: bit0-2に意味がある.
         //   bit0: phaseの値.
         //   bit1: 今はthen節の中にいるのか?それともelse節の中にいるのか?(0=then, 1=else).
         //   bit2: if~elif~elif~...~else~endif のかたまりにおいて、今までどこかで条件が成立したことがあるか?(1=ある).
         //     このbit2は意外に重要で、なぜならどこかで条件が成立してしまえば、以降はendifまで無視しなければならないから.
     }
     if (n == 4 && memcmp(t, "elif", 4) == 0 && w->stk->n > 1) {
         char *sp = &(w->stk->p[w->stk->n - 1]);
         if ((*sp & 2) != 0) return 0; // elseのあとにelifが来るのはおかしい. エラーに気づかせるために「未処理」とする.
         *sp &= 6;
         if ((sp[-1] & 1) != 0 && (*sp & 4) == 0 && w->eval(w->w_eval, t0, 0x7fff) != 0) *sp = 5;
         a_Token1_get(t0);
         if ((t0->c != 0 && t0->c != ('/' | '/' << 8)) || w->w_eval->err != 0) {
             if ((sp[-1] & 1) != 0) *sp = 5;
             w->phase = *sp & 1; return 0;
         }
         w->phase = *sp & 1;
         return 1;
     }
     if (n == 4 && memcmp(t, "else", 4) == 0 && w->stk->n > 1) {
         char *sp = &(w->stk->p[w->stk->n - 1]);
         if ((*sp & 2) != 0) return 0; // elseのあとにelseが来るのはおかしい.
         *sp |= 2; *sp &= 6;
         if ((sp[-1] & 1) != 0 && (*sp & 4) == 0) *sp = 7;
         w->phase = *sp & 1;
         return 1;
     }
     if (n == 5 && memcmp(t, "endif", 5) == 0 && w->stk->n > 1) {
         char *sp = &(w->stk->p[--(w->stk->n) - 1]);
         w->phase = *sp & 1;
         return 1;
     }
     return 0;
 }
 
 a_static void a_Preprocessor_if3(a_Preprocessor_If *w) { a_VecChr_din(w->stk); }
 

** (2) 説明
-Preprocessor_eval:
--プリプロセッサの #if のために作られた評価関数で、整数演算のみできます。
-Preprocessor_if0:
--Preprocessor_Ifオブジェクトを初期化します。
-Preprocessor_if1:
--この関数に1行と#以降のトークンをgetした値を渡せば、ifdef, ifndef, elifdef, elifndef のいずれかであるかどうかを判定して、その場合は #if defined(...) などの形に書き換えてくれます。
--書き換えを行った場合は 1 を返すので、この行の処理を最初からやり直してください。
--この関数は不要になったので削除しました。
-Preprocessor_if2:
--if, elif, else, endifの処理を行います。これらが該当した場合は 1 を返します。
--現在の処理が #if によって無効化されているかどうかは、オブジェクトの中の phase というメンバ変数を見ればわかります。ここが0なら無効化されているので、一般行や#defineなどを実行するべきではありません。
-Preprocessor_if3:
--Preprocessor_Ifオブジェクトの解放処理をします。

** (3) サンプルプログラム: t0010a.c (簡易的なプリプロセッサ)

 #define a_Version 1
 #include <acl4.c>
 
 void preprocessor_t0010a(VecChr *src, VecChr *dst, Token0 *t0)
 {
     VecChr linBuf[1], tmp[1]; VecChr_ini4(linBuf, tmp, 0, 0);
     Set0 set[1]; Set0_ini(set); dst->n = 0;
     Preprocessor_If ppIf[1]; Preprocessor_if0(ppIf);
     Preprocessor_Eval ppEv[1];
     ppIf->eval = (void *) Preprocessor_eval; ppIf->w_eval = ppEv;
     int defC = 1;
     char *s = src->p, *s1 = src->p + src->n, *t;
     for (;;) {
         s = VecChr_gets(linBuf, s, s1); // 1行をlinBufに格納.
         if (linBuf->n == 0) break;
         for (;;) { // 末尾が "\\\n"であれば、後続行を接続.
             char *p1 = linBuf->p + linBuf->n;
             if (!(linBuf->n >= 2 && p1[-2] == '\\' && p1[-1] == '\n')) break;
             s = VecChr_gets(tmp, s, s1);
             VecChr_replace(linBuf, linBuf->n - 2, 2, tmp->p, tmp->n);
         }
         t0->s = linBuf->p; t0->s1 = linBuf->p + linBuf->n;
         t = Token1_get(t0);
         if (t0->c == '#') {
             t = Token1_get(t0);
             if (t0->len < 16) {
                 char ttmp[16]; memcpy(ttmp, t, t0->len); ttmp[t0->len] = '\0';
                 if (strstr(ttmp, "def") == NULL && strstr(ttmp, "include") == NULL) {
                     while (Preprocessor_define2(set, t0, linBuf, defC, NULL, 0, 0) != 0);
                     t0->s = linBuf->p; t0->s1 = linBuf->p + linBuf->n;
                     Token1_get(t0);
                     t = Token1_get(t0);
                 }
             }
             if (Preprocessor_if2(ppIf, linBuf, t0, t) != 0) continue;
             if (ppIf->phase != 0) {
                 if (t0->len == 6 && memcmp(t, "define", 6) == 0) {
                     Preprocessor_define0(set, t0, linBuf, defC); continue;
                 }
             }
         } else {
             if (ppIf->phase != 0) {
                 while (Preprocessor_define2(set, t0, linBuf, defC, NULL, 0, 0) != 0);
                 VecChr_puts(dst, linBuf->p, linBuf->p + linBuf->n);
             }
         }
     }
     Preprocessor_define3(set);
     Preprocessor_if3(ppIf);
     VecChr_din4(linBuf, tmp, 0, 0);
 }
 
 int main(int argc, const char **argv)
 {
     VecChr src[1], dst[1]; VecChr_ini(dst);
     VecChr_iniArg(src, argc, argv, 1, 2);
     Token0 t0[1]; Token0_ini1(t0);
     preprocessor_t0010a(src, dst, t0);
     printf("%.*s\n", (int) dst->n, dst->p);
     VecChr_din4(src, dst, 0, 0);
     a_malloc_debugList(_arg);
 }
-include, undef は処理できませんが、それ以外は一通りできます。
--[註] 最初のバージョンでは ifdef 系もできたんですが、今のバージョンでは処理できなくなりました。
-なお、標準的なプリプロセッサとは異なり、同じマクロ名でも引数の数で区別され、異なる定義を与えられます。
-t0010a.exe は 17.5KB でした(初期バージョンでは 16.0KB でした)。

** (99) 更新履歴
-2026.02.06(金) 初版
-2026.02.11(水) エラーチェック機能を強化、 Preprocessor_if1 を削除
-2026.02.15(日) a_Preprocessor_eval に '...' の処理が抜けていたので追加

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