* acl4のプログラムのページ0001
-(by [[K]], 2026.02.02)

** (1) #defineしかできない簡易プリプロセッサ
-[[a4_0007]]までの関数があれば、そこそこ面白いことができるはずだと思ったので、適当に書いてみました。

** (2) p0001a0.txt を使ってテスト
 #define Sqr(x)          (x)*(x)
 #define Diff(x, y)      (x)-(y)
 
 x = Sqr(Diff(9, 5));
 y = Diff(Sqr(5), Sqr(3));

 >p0001a p0001a0.txt
 
 x = ((9)-(5))*((9)-(5));
 y = ((5)*(5))-((3)*(3));
 p0001a.c(181): malloc_debugList()

-こんなふうにちゃんと動きます。もちろんいろいろ足りない部分はありますが、183行でこれくらいのことができるっていうのは、まあまあ頑張っているのではないかと私は思います。
-Windows上でa_DbgLv=0でコンパイルした場合、p0001a.exeは12.5KBになっています。

-これに #if や #include をつけて、さらに細かいところをちゃんとすれば、普通のプリプロセッサになります。

** (3) p0001a.c

 #define a_Version 1
 #include <acl4.c>
 
 class_(Def) { 
     SetElm elm[1];
     char *q;
     intptr_t n;
 };
 
 void define1(VecChr *tmp, Token0 *t0, VecChr *args, int c);
 
 void define0(Set0 *set, Token0 *t0, VecChr *linBuf, int c)
 // #defineの登録処理. 
 {
     VecChr args[1], tmp[1]; VecChr_ini4(args, tmp, 0, 0);
     char *k = Token1_get(t0);
     intptr_t kn = t0->len, argsN = 0xff;
     if (k[kn] == '(') {
         t0->s++; parseArgs(t0, args);
         if (t0->len == 0) goto fin;
         argsN = args->n / (2 * sizeof (char *));
     }
     char *t = Token1_get(t0);
     VecChr_resize(_arg_  tmp, linBuf->p + linBuf->n - t);
     memcpy(tmp->p, t, tmp->n);
     define1(tmp, t0, args, c);
     Def *d = malloc_(_arg_  sizeof (Def));
     d->elm->k = malloc_(_arg_  kn + 1);
     d->elm->n = kn + 1;
     ((unsigned char *) d->elm->k)[0] = argsN;
     ((unsigned char *) d->elm->k)[0] = (unsigned char) argsN;
     memcpy((char *) d->elm->k + 1, k, kn);
     d->q = malloc_(_arg_  tmp->n);
     d->n = tmp->n;
     memcpy(d->q, tmp->p, tmp->n);
     Set0_add(set, d->elm);
 fin:
     VecChr_din4(args, tmp, 0, 0);
 }
 
 void define1(VecChr *tmp, Token0 *t0, VecChr *args, int c)
 // #defineの引数を [01 01] とか [01 02] などに置換する.
 {
     int i, n = args->n / (2 * sizeof (char *));
     int i, n = (int) (args->n / (2 * sizeof (char *)));
     Set0 set[1]; Set0_ini(set); SetElm elm[256];
     char mark[2]; mark[0] = c;
     char **ap = (char **) args->p, *t;
     if (n + c > 256) errExit("define1: too many args (c=%d, n=%d)", c, n);
     for (i = 0; i < n; i++) {
         elm[i].k = ap[i * 2];
         elm[i].n = ap[i * 2 + 1] - ap[i * 2];
         Set0_add(set, &elm[i]);
     }
     t0->s = tmp->p; t0->s1 = tmp->p + tmp->n;
     for (;;) {
         t = Token1_get(t0);
         if (t0->len == 0) break;
         if (t0->len >= 2 && t[0] == '/' && t[1] == '/') {
             tmp->n = t - tmp->p; // コメントが後続しているならそこから打ち切る。
             break;
         }
     }
     while (tmp->n > 0 && ((unsigned char *) tmp->p)[tmp->n - 1] <= ' ') tmp->n--;
     t0->s = tmp->p; t0->s1 = tmp->p + tmp->n;
     for (;;) {
         t = Token1_get(t0);
         if (t0->len == 0) break;
         SetElm *e = Set0_findKn(set, t, t0->len);
         if (e != NULL) {
             mark[1] = c + (e - elm);
             mark[1] = (unsigned char) (c + (e - elm));
             intptr_t pos0 = t - tmp->p;
             VecChr_replace(tmp, pos0, t0->len, mark, 2); // 変数名を [01 xx] に置換.
             t0->s = tmp->p + pos0 + t0->len;
             t0->s1 = tmp->p + tmp->n;
         }
     }
     Set0_din(set);
 }
 
 void define2(Set0 *set, Token0 *t0, VecChr *linBuf, int c)
 // 一般行に対して#defineの登録内容を適用する.
 {
     VecChr args[1], tmp[1]; VecChr_ini4(args, tmp, 0, 0);
     char *p = linBuf->p;
     for (t0->s = p;;) {
         char *t = Token1_get(t0);
         if (t0->len == 0) break;
         if (t0->len == 2 && t[0] == '#' && t[1] == '#') { // ## があったら前後の空白類とともに削除する.
             intptr_t pos0 = t - p, pos1 = pos0 + 2;
             while (pos0 > 0 && p[pos0 - 1] <= ' ') pos0--;
             while (pos1 < linBuf->n && p[pos1] <= ' ') pos1++;
             VecChr_replace(linBuf, pos0, pos1 - pos0, NULL, 0); // 長さ0文字の文字列と置換することで削除を実現.
             t0->s = p;
             t0->s1 = p + linBuf->n;
             continue;
         }
         if (t0->cTyp != 3) continue;
         intptr_t kn = t0->len + 1;
         unsigned char *k = malloc_(_arg_  kn);
         memcpy(k + 1, t, kn); k[0] = 0xff;
         intptr_t pos0 = t - p;
         Def *d = Set0_findKn(set, k, kn); // 引数なしマクロで一致するものはあるか?.
         if (d != NULL) {
             VecChr_replace(linBuf, pos0, t0->len, d->q, d->n); // 置換する.
 nxt:
             p = linBuf->p;
             t0->s = p + pos0;
             t0->s1 = p + linBuf->n;
             free_(_arg_  k, kn);
             continue;
         }
         if (t[t0->len] == '(') {
             const char *t0s = t0->s++;
             t = parseArgs(t0, args);
             k[0] = args->n / (2 * sizeof (char *));
             k[0] = (unsigned char) (args->n / (2 * sizeof (char *)));
             d = Set0_findKn(set, k, kn); // 引数付きマクロで一致するものはあるか?.
             if (d != NULL) {
                 VecChr_resize(_arg_  tmp, d->n);
                 memcpy(tmp->p, d->q, d->n);
                 for (;;) {
                     p = memchr(tmp->p, c, tmp->n);
                     if (p == NULL) break;
                     int i = ((unsigned char *) p)[1] - c;
                     char **ap = (char **) args->p;
                     VecChr_replace(tmp, p - tmp->p, 2, ap[i * 2], ap[i * 2 + 1] - ap[i * 2]); // 引数を代入.
                 }
                 VecChr_replace(linBuf, pos0, t + 1 - (linBuf->p + pos0), tmp->p, tmp->n); // 置換する.
                 goto nxt;
             }
             t0->s = t0s;
         }
         free_(_arg_  k, kn);
     }
     VecChr_din4(args, tmp, 0, 0);
 }
 
 void define3(Set0 *set)
 {
     intptr_t i, n = set->tbl->n / sizeof (Def *);
     Def **tbl = (Def **) set->tbl->p;
     for (i = 0; i < n; i++) {
         free_(_arg_  (char *) tbl[i]->elm->k, tbl[i]->elm->n);
         free_(_arg_  tbl[i]->q, tbl[i]->n);
         free_(_arg_  tbl[i], sizeof (Def));
     }
     Set0_din(set);
 }
 
 int main(int argc, const char **argv)
 {
     VecChr src[1], linBuf[1], tmp[1]; VecChr_ini4(src, linBuf, tmp, 0);
     VecChr_readFileAll_errChk(src, argv[1]); VecChr_eraseCr(src);
     Set0 set[1]; Set0_ini(set);
     Token0Table t0t[1]; Token0Table_ini(t0t);
     Token0 t0[1]; t0->tbl = t0t;
 
     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 == 6 && memcmp(t, "define", 6) == 0) {
                 define0(set, t0, linBuf, 1); continue;
             }
             continue;
         } else { // #で始まらない行は一般行.
             define2(set, t0, linBuf, 1);
             printf("%.*s", linBuf->n, linBuf->p);
             printf("%.*s", (int) linBuf->n, linBuf->p);
         }
     }
     define3(set);
     VecChr_din4(src, linBuf, tmp, 0);
     a_malloc_debugList(_arg);
     return 0;
 }

** (4) 説明
-mainはまあ難しいことはないと思うので、説明を省略します。
--setは連想コンテナですが、この中にdefineの定義内容を貯めていくことになります。
--素早く辞書引きしたいので連想コンテナに入れています。

-define0は、#defineの登録処理をします。
--まず引数があれば、それはparseArgsで解析してしまいます。これで引数の変数名と引数の個数はすぐにわかるようになります。
--次に置換内容が書かれているわけですが、それをlinBufから切り出してtmpにいれます。
--そしてargsとともにdefine1に渡して整形します。
--整形が終わったら、(引数の数, define名)をkeyとして、整形後のtmpをvalueとして、define用のsetに登録します。

-define1は、上記で言及した整形処理をやります。
--整形処理というのは、こういうことです。
--[1]もしコメントが後続している場合は、邪魔なので切り取る。
--[2]引数部分を [01 xx] の2バイトに置き換える。
 (例) #define Diff(x, y)      (x)-(y)
        → ([01 01])-([01 02])
--なぜ2バイトなのかというと、1バイトでは普通の文字と衝突するかもしれないと思ったからです。
--01は「次が引数番号だよ」ということを表すマークで、xxが引数番号です。一応00を避けたほうがいいかなと思ったのでxxも01以上になるようにしています。
--最初にargsをsetに登録していきます。このsetは#define登録用で出てきたsetとはまた別のもので、あるトークンが引数なのか、そうではないのかをすぐに判定できるようにするために使っています。
--整形処理が終わったら、args早引き用setは不要なので処分しています。

-define2はマクロを適用していく処理ですが、コメントを多くしてあるので説明は省略します。

-define3はsetを開放するための処理をまとめたものです。


** (99) 更新履歴
-2026.02.02(月) 初版

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