acl4のプログラムのページ0007

  • (by K, 2026.04.25)    (a4_i01: acl4 の目次へ戻る)(a4_log: acl4 の開発ログへ戻る)

(1) プリプロセッサは善か悪か?

  • 私はかつてC言語のプリプロセッサのマクロが嫌いでした。関数みたいな見た目だけど、実際は関数ではなく、ただのテキストの置き換えです。
    #define Square(x)  (x)*(x)
  • みたいなのがあると、xは二度評価されて、副作用があったりするとわかりにくいバグになるわけです。またマクロは型チェックもしてくれません。
  • こんなのやるくらいなら inline 関数を書いたほうがマシです。
  • そもそも x * x って書けないところからして、許せないんです。いや書いてもいいんですが、そしたら Square(2+3) がうまく計算できなくなるわけですよね。これが 11 になってうれしい状況が全く理解できません。
  • まあ最適化が強くないコンパイラでも、関数呼び出しコードを生成しないことが確実なところはちょっといいなと思っていましたが・・・。


  • 私は今はプリプロセッサが大好きです。プリプロセッサは世界を変えるパワーを持っていると思います。うまく使えばこれほど有用なものはないのです。今日はその話をしたいと思います。

(2) プログラム p0007a.c について #0 (プリプロセッサ)

  • acl4 ライブラリを使って、プリプロセッサを作りました。acl4 ライブラリにはプリプロセッサ関数があるので、それを呼んでいるだけです(a4_0022)。難しいことは何もありません。
  • [p0007b.txt]:
    #define Square(x)  (x)*(x)
    #define One     1
    #define Two     2
    #define Three   3
    #define Four    4
    #define Five    5
    #define Six     6
    #define Seven   7
    #define Eight   8
    #define Nine    9
    #define Ten    10
    #define Eleven 11
    #define Twelve 12
    
    #if (Square(2+3)!=11)
      Square(Three + Seven)
    #endif
  • これをプリプロセッサにかけるとこうなります。
    >p0007a prepro:1 src:p0007b.txt
    #line 14 "p0007b.txt"
    
    
    (3 + 7)*(3 + 7)
  • もし #line を出力しないでほしければ、こうします。
    >p0007a prepro:1 src:p0007b.txt mod:2
    
    (3 + 7)*(3 + 7)
  • このプリプロセッサの実行ファイル( p0007a.exe )は 35.0KB で(無圧縮)、これはWindowsのプリプロセッサのプログラムとしてはまあまあ小さいほうではないかと思います。

(3) プログラム p0007a.c について #1 (コマンドライン計算機)

  • プリプロセッサでは #if で式の値を計算する必要があります。そうしないと後続の行を出力するかしないかを決められません。ということで計算機能を持っています。
  • せっかくなので、それを使ってやろうと考えました。ファイルではなくコマンドラインに書くときは、ちょっと面倒ですが inline: を付けます。
    >p0007a calc:1 src:inline:1+2+3+4+5+6+7+8+9+10
    55
  • 結局中身はプリプロセッサなので、#defineとかが混ざっていても問題ありません。ということで、先の p0007b.txt を src: に指定することもできます。
    >p0007a calc:1 src:p0007b.txt
    100
  • だから計算機プログラムとしては、ちょっと多機能です(笑)。

(4) プログラム p0007a.c について #2 (x86-16のアセンブラ)

  • さてここからが本気です(笑)。
  • 実は自作プリプロセッサである p0007a は、普通のプリプロセッサの機能に加えて #typedDef などの拡張機能を持っています(型付きdefine)。
  • これは acl4 ライブラリのプリプロセッサ関数が標準で持っている機能なので、別にこの機能を使うためにあれこれと準備しなければいけないということはありません。
  • これを使って何ができるかと考えて、アセンブラが作れそうだと思いました。それでできたのがこれです。
  • [p0007c.txt]:
        MOV(AH,0x02);
        MOV(DL,0x20);
    LBL(lp);
        INT(0x21);
        INC(DX);
        CMP(DL,0x7E);
        JBE_SHORT(lp);
        RET();
  • これは MS-DOS 用の「 !"#$%&'()...」を表示するプログラムです。文字コード 0x20 から 0x7e までを出力します。
  • すべての命令をマクロとして書いて、それを変換していくことで目的の機械語に到達するので、ちょっと見た目は不格好ですが、しかしアセンブラなんてC言語とかのバックエンドで動けばいいのだから、見た目は別に気にしないということであれば、まあこれもアセンブラとして認めることはできます。
  • アセンブルしてみます。
    >p0007a x86asm:1 src:p0007c.txt
    [B4 02 B2 20 CD 21 42 80 FA 7E 76 F8 C3 ]
  • 今はとりあえずアセンブル結果を16進数で画面に出していますが、もちろんこれを好きなフォーマットに整えて出せば、きっときちんと動くと思います。
  • このアセンブラ結果を得るために用意したインクルードファイルはこんな内容です。 p0007c に出てこない命令はまだ全然書かれていません。
  • [x86asm.h]:
    #define AL  R8:0
    #define CL  R8:1
    #define DL  R8:2
    #define BL  R8:3
    #define AH  R8:4
    #define CH  R8:5
    #define DH  R8:6
    #define BH  R8:7
    #define AX  R16:0
    #define CX  R16:1
    #define DX  R16:2
    #define BX  R16:3
    #define SP  R16:4
    #define BP  R16:5
    #define SI  R16:6
    #define DI  R16:7
    
    #define RET()           DB(0xC3)
    #define INT(i)          DB(0xCD); DB(i)
    #define JBE_SHORT(l)    DB(0x76); DB_L(l, 1, -1)
    #define JBE_NEAR(l)     DB(0x0F); DB(0x86); DW_L(l, 1, -2)
    
    #typedDef MOV(R8:,r,   (0,1,2,3,4,5,6,7,8,9),i)  DB(0xB0+defRight(r,:)); DB(i)
    #typedDef MOV(R16:,r,  (0,1,2,3,4,5,6,7,8,9),i)  DB(0xB8+defRight(r,:)); DB(i)
    #typedDef INC(R16:,r)                            DB(0x40+defRight(r,:))
    #typedDef DEC(R16:,r)                            DB(0x48+defRight(r,:))
    #typedDef CMP(R8:,r,   (0,1,2,3,4,5,6,7,8,9),i)  DB(0x80); DB(0xF8+defRight(r,:)); DB(i)
    #typedDef CMP(R16:,r,  (0,1,2,3,4,5,6,7,8,9),i)  DB(0x81); DB(0xF8+defRight(r,:)); DW(i)
    #typedDef CMP(R8:0,r,  (0,1,2,3,4,5,6,7,8,9),i)  DB(0x3C); DB(i) // ALは特別.
    #typedDef CMP(R16:0,r, (0,1,2,3,4,5,6,7,8,9),i)  DB(0x3D); DW(i) // AXは特別.
  • こんなものを見せられても全くぴんと来ないと思いますが、これはつまりインクルードファイルさえ書き換えれば、x86に限らずに様々なCPU向けのアセンブラを作ることができるということです。
  • 中には見慣れない記述もありますが、でもなんか簡単に書けそうじゃないですか(普通にアセンブラを作るよりは)。
  • ちなみに p0007a にはデバッグモードというのもあって、それを使うと、内部でどういう処理が行われているのか、ちょっとわかります。
    >p0007a x86asm:1 src:p0007c.txt dbg:1
    
        DB(0xB0+4); DB(0x02);
        DB(0xB0+2); DB(0x20);
    LBL(2);
        DB(0xCD); DB(0x21);
        DB(0x40+2);
        DB(0x80); DB(0xF8+2); DB(0x7E);
        DB(0x76); DB_L(2, 1, -1);
        DB(0xC3);
    
    [B4 02 B2 20 CD 21 42 80 FA 7E 76 F8 C3 ]
  • このアセンブラは、もちろん純粋なプリプロセッサだけではできません。 p0007a 側もラベル計算機能を提供するために 50 行くらいは書いています。でもこれはCPUに依存せずに使えると思うので、別のCPU用に流用する時は多分そのまま使えるでしょう。
  • x86では、ALやAXレジスタを使うときのために、短い機械語を用意している場合があります。プリプロセッサで単純に置換していくだけでは、そういう処理はできないんじゃないかと思うかもしれません。でも p0007a はできます。
    >p0007a x86asm:1 src:inline:CMP(CL,123);
    [80 F9 7B ]
    
    >p0007a x86asm:1 src:inline:CMP(AL,123);
    [3C 7B ]

(5) プログラム p0007a.c について #3 (C言語インタプリタ)

  • アセンブラはバックエンドで活躍すればいいので不格好な構文でも許せる気はしますが、C言語ではどうでしょうか。やっぱりC言語はできるだけC言語の構文で書きたいです。
  • となると、C言語はプリプロセッサを使って簡単に作ることはできないのでしょうか。・・・うーん、それはくやしいです。
  • それで、まずC言語の「a=b+c;」を「Add(a,b,c);」へ変換してやろうと考えました。こんな感じでマクロっぽい書き方になってしまえば、後はプリプロセッサだけでどうにでもできそうです。
  • ということでそういう変換だけをするプログラムを「ミニコンパイラ」と呼ぶことにして、とりあえずやっつけで書きました。C言語のごく一部の構文しかサポートできてないですが、とにかくアイデアを試す第一歩としてこれでいいと思いました。150行くらいです。
  • p0007a.c は全部で 317 行なので、この半分をミニコンパイラだけで使っていることになります(笑)。いや、だって、やってみたかったんだもん!
  • いっぽうで、 acl4 ライブラリ側には a4vm_exec0() という170行程度の仮想マシンがすでにあるので(a4_0023)、ミニコンパイラの出力をプリプロセッサで変換して、この VM 用のバイトコードにコンパイルしてみることにしました。
  • 別にこれじゃなくてもいいのですが、これなら変換後に関数を呼ぶだけで実行ができるので面白いかなと思ったのです。
  • まずは hello, world からやってみます。
  • [p0007d.txt]:
    printf("hello, world\n");
  • 実行結果はこうなります。まあ当たり前ですね。
    >p0007a clang:1 src:p0007d.txt
    hello, world
  • ちなみにミニコンパイラの出力はこうなっています( dbg:1 で見えます)。
    Let(tmp0, CInt:"hello, world\n");
    printf(tmp0);
  • これがプリプロセッサによって何度も変換されて、最後にこうなります。これはVMのバイトコードです(これも dbg:1 で見えます)。
    0x003e, 256, 256, 0, 0x2, 0, 0, 0;
    0x0004, 128, "hello, world\n", 0;
    0x003d, printf, 3, 0, 0x2, 128, 0, 0;
    0x003f, 256, 256, 0, 0x2, 0, 0, 0;
  • これが a4vm_exec0() に送られて、上記のような出力結果になるわけです。


  • 素数を表示するプログラムを書きました。まだ配列変数が使えないので原始的な方法でやっています。 break もできないので goto で。
  • [p0007e.txt]:
    int i, j;
    for (i = 2; i < 100; i++) {
        for (j = 2; j * j <= i; j++) {
            if (i % j == 0) goto skip;
        }
        printf("%d ", i);
    skip: ;
    }
    printf("\n");
  • 実行結果はこうなります。もちろんあっています。
    >p0007a clang:1 src:p0007e.txt
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
  • ちなみにミニコンパイラの出力は以下の通りでした( dbg:1 で見えます)。
    #defineX skip  defEnum(1)
    #defineX i  Int:defEnum(0)
    #defineX j  Int:defEnum(0)
      Let(i, CInt:2);
      Jmp_T(LT0002);
    Lbl_T(LT0000);
      Let(j, CInt:2);
      Jmp_T(LT0006);
    Lbl_T(LT0004);
      Mod(tmp0, i, j);
      Ceq(tmp1, tmp0, CInt:0);
      Jne(tmp1, CInt:0, Txt:skip);
      Let(tmp2, j); Add(j, j, CInt:1);
    Lbl_T(LT0006);
      Mul(tmp0, j, j);
      Cle(tmp1, tmp0, i);
      Jne(tmp1, CInt:0, Txt:LT0004);
      Let(tmp0, CInt:"%d ");
      Let(tmp1, i);
      printf(tmp0,tmp1);
    Lbl_T(skip);
      Let(tmp1, i); Add(i, i, CInt:1);
    Lbl_T(LT0002);
      Clt(tmp0, i, CInt:100);
      Jne(tmp0, CInt:0, Txt:LT0000);
      Let(tmp0, CInt:"\n");
      printf(tmp0);


  • こんなに遊べるなら、 printf() だけではなくグラフィック関数も付けて、もっと遊ぼうと考えました。ということで p0007a にはすでに openWin() や setPix() が入っています。
  • [p0007f.txt]:
    int x, y, c; c = 0;
    openWin(256, 256);
    for (y = 0; y < 256; y++) {
        for (x = 0; x < 256; x++) {
            setPix(x, y, c);
            c = c + 0x100;
        }
    }
    waitInf(); // これがないとウィンドウが一瞬で閉じられてしまう.
  • 実行すると毎度のこれが出ます(最後は無限ループで止まっているだけなので、Ctrl-Cで終了させてください)。
    • https://essen.osask.jp/files/pic20260331a.png


  • 次はこれです。
  • [p0007g.txt]:
    int x, y;
    openWin(256, 256);
    for (y = 0; y < 256; y++)
        for (x = 0; x < 256; x++)
            setPix(x, y, (x ^ y) * 0x10101);
    waitInf(); // これがないとウィンドウが一瞬で閉じられてしまう.
  • 実行するとこれが出ます。大満足です(最後は無限ループで止まっているだけなので、Ctrl-Cで終了させてください)。
    • https://essen.osask.jp/files/pic20260331b.png


  • ここまで来たら、 mandel もやってみたくなるわけです。
  • [p0007h.txt]:
    int x, y, c, sx, sy, cx, cy, zx, zy, xx, yy, n, sn;
    openWin(512, 384);
    for (y = 0; y < 384; y++) {
        for (x = 0; x < 512; x++) {
            sn = 0;
            for (sx = 0; sx < 4; sx++) {
                cx = (x * 4 + sx) * 56 + 4673536;
                for (sy = 0; sy < 4; sy++) {
                    cy = (y * 4 + sy) * -56 - 124928;
                    zx = cx; zy = cy;
                    for (n = 1; n < 447; n++) {
                        Mul64Shr_RRRI(dR(xx), dR(zx), dR(zx), 24); // xx = (zx * zx) >> 24;
                        Mul64Shr_RRRI(dR(yy), dR(zy), dR(zy), 24);
                        if (xx + yy > 0x4000000) goto sk0;
                        Mul64Shr_RRRI(dR(zy), dR(zy), dR(zx), 23);
                        zx = xx + cx - yy;
                        zy = zy + cy;
                    }
    sk0:
                    sn = sn + n;
                }
            }
            sn = sn >> 4;
            c = sn << 8;
            if (sn < 256) goto sk1;
            c = 0;
            if (sn >= 447) goto sk1;
            c = sn + 0xfe01;
    sk1:
            setPix(x, y, c << 8);
        }
    }
    waitInf();
  • 実行結果はこうなります(最後は無限ループで止まっているだけなので、Ctrl-Cで終了させてください)。
    • https://essen.osask.jp/files/pic20260331c.png

(6) 主張のまとめ

  • プリプロセッサ関数があれば、プログラミング言語はこんなに簡単に作れるようになるんです。
  • 今回アセンブラについては不格好な例を出しましたが、C言語の時のようにミニコンパイラを作れば、もっとアセンブラらしい構文をそのまま使うこともきっと可能でしょう。
  • 今回はアセンブラもC言語も最後まで作り切ったわけではありません。だからもしかしたら何らかの限界がある可能性はあります。しかしとりあえず、可能性は見えたと思います。
  • C言語の標準のプリプロセッサは #typedDef に相当する機能がありません。これがないとマクロ機能が貧弱なので、 p0007a みたいなことはできません。だから #typedDef が重要かもしれません。
  • typedDef は以下のような仕様です。見た目はややこしそうですが、処理内容は単純です。
    #typedDef INC(R16:,r)   DB(0x40+defRight(r,:))
    こう書いたときは、INC(r)のrが、"R16:"という書き出しになっていないとこのマクロが適用されない。
    
    #typedDef MOV(R8:,r,   (0,1,2,3,4,5,6,7,8,9),i)  DB(0xB0+defRight(r,:)); DB(i)
    こう書いたときは、MOV(r, i)のrが"R8:"という書きだしになっていて、かつ、iの書き出しが0~9になっていないと、このマクロが適用されない。
    
    defRight(r,:)
    rの中にコロンがあれば、その右側だけを取り出す。
  • ということで、typedDefがプリプロセッサ活用の決め手かもしれないですが、しかしこんなのはプリプロセッサ処理全体から見れば些細なもので、つまりはC言語の標準のプリプロセッサは最初からかなり完成していたのです。わずかな不足分を補っただけです。
  • Macro(...)の形式を超えてマクロが適用できるようになったら、たぶん爆発的に応用範囲が広がると思うのですが、今のところいい方法は思いついていません。ミニコンパイラを作って変換するしかない状況です。
  • プログラムの実行ファイルサイズについてもう一度説明しておきますが、以上のすべての機能が入って、しかもWindowsの標準DLL以外は一切使わないで(つまり acl4 はスタティックなライブラリです)、それでも実行ファイルは 35.0KBです。
  • もちろんグラフィック機能も入った状態です。
  • [プリプロセッサ]+[計算機]+[簡易x86アセンブラ]+[簡易C言語インタプリタ(グラフィック機能あり)] = 35.0KB
    • それぞれがバラバラに入っているのではなく、それぞれがプリプロセッサ関数を使って作られているので、トータルサイズが小さいのです。

(7) ダウンロード

(8) p0007a.c のソースコード

  • 1日でやっつけで作ったのできれいではありません。
  • これは acl4v1 がないとビルドできないので、これから acl4v1 も wiki に書きます。
    #define UseGraph    1
    #define a_Version   1
    #include <acl4v1.c>
    
    #if (UseGraph != 0)
    
    #include <windows.h>
    #include "a4_t0002.c"
    
    a_Win *win;
    
    void openWin(intptr_t x, intptr_t y) { win = a_malloc(_a_  sizeof (a_Win)); a_Win_ini(_a_  win, (int) x, (int) y, "graph", 0x000000); }
    void setPix(intptr_t x, intptr_t y, intptr_t c) { win->buf[x + y * win->xsz] = (uint32_t) c; }
    void flushWin() { a_Win_flushAll0(win); }
    void waitInf() { if (win != NULL) { flushWin(); } for (;;) Sleep(1000); }
    
    #endif
    
    a_class(MiniCompiler_Sub) {
        int mod;
        int lb; VecChr vc[2];
        // mod=0: for文.
        //   lb+0:continue0, lb+1:continue, lb+2:continue2, lb+3:break.
        //   vc0:繰り返し条件, vc1:cotinue時のインクリメント処理など.
    };
    
    a_class(MiniCompiler) {
        a_Token0 *t0;
        a_BufFree bf[1];
        a_VecChr *vc;
        a_VecChr stk[1], lbl[1];
        int tmpNo, tmpLb, autoClose;
    };
    
    int eval_hook1(a_Preprocessor_Eval *w, a_Token0 *t0, int pri, const char *t, intptr_t *pi);
    a_static void MiniCompiler_ini(MiniCompiler *w);
    a_static void MiniCompiler_din(MiniCompiler *w);
    a_static char *MiniCompiler_compile(MiniCompiler *w, int pri);
    
    int main(int argc, const char **argv)
    {
        const char *src = getArg(argc, argv, "src:", ""); char *t, *tt;
        VecChr vc0[1], vc1[1], vc2[1], vc3[1]; VecChr_ini4(_a_ vc0, vc1, vc2, vc3);
        BufFree bf[1]; a_BufFree_ini(_a_ bf); Token0 t0[1]; Token0_ini1(t0);
        Preprocessor_Eval ev[1]; Preprocessor_Eval_ini(_a_ ev); ev->err = 0;
        ev->hook1 = eval_hook1; ev->v[0] = bf; ev->v[1] = vc3;
    
        if (strtol(getArg(argc, argv, "prepro:", "0"), NULL, 0) != 0) {
            int mod = strtol(getArg(argc, argv, "mod:", "1"), NULL, 0);
            a_Preprocessor_ini1(src, -1, mod, vc0);
            puts(vc0->p);
        }
    
        if (strtol(getArg(argc, argv, "calc:", "0"), NULL, 0) != 0) {
            a_Preprocessor_ini1(src, -1, 2, vc0);
            t0->s = vc0->p; t0->s1 = vc0->p + VecChr_N(vc0);
            for (;;) {
                t = Token1_get(t0);
                if (t0->c == ',' || t0->c == ';') { printf("%c ", t0->c); continue; }
                if (t0->c == 0) break;
                t0->s = t; intptr_t i = Preprocessor_eval(ev, t0, 0x7fff);
                if (ev->err != 0) break;
                printf("%d", (int) i);
            }
            printf("\n");
        }
    
        if (strtol(getArg(argc, argv, "x86asm:", "0"), NULL, 0) != 0) {
            a_VecChr_iniArg(_a_ vc0, src, 2+16);
            for (t = vc0->p;;) {
                tt = strstr(t, "LBL("); if (tt == NULL) break;
                t  = strchr(tt, ')');   if (t  == NULL) break;
                VecChr_printf(vc1, "#defineX %.*s  defEnum(0)\n", t - tt - 4, tt + 4);
            }
            VecChr_printf(vc2, "imm<sys>:#include \"x86asm.h\"\ndefEnum(0, 2)\n%s%s", vc1->p, vc0->p);
            VecChr_N(vc0) = 0; a_Preprocessor_ini1(vc2->p, -1, 2, vc0);
            if (strtol(getArg(argc, argv, "dbg:", "0"), NULL, 0) != 0) puts(vc0->p);
            intptr_t pass, lbl[999];
            for (pass = 0; pass < 2; pass++) {
                t0->s = vc0->p; t0->s1 = vc0->p + VecChr_N(vc0);
                VecChr_N(vc1) = 0;
                for (;;) {
                    t = Token1_get(t0); intptr_t n = t0->len, i, j, k, m;
                    if (t0->c == ',' || t0->c == ';') continue;
                    if (t0->c == 0) break;
                    if (n == 3 && memcmp(t, "LBL",  3) == 0) {
                        i = Preprocessor_eval(ev, t0, 0x7fff);
                        if (0 <= i && i < 999) lbl[i] = VecChr_N(vc1);
                    }
                    m = 0;
                    if (n == 4 && memcmp(t, "DB_L", 4) == 0) m = 1;
                    if (n == 4 && memcmp(t, "DW_L", 4) == 0) m = 2;
                    if (n == 4 && memcmp(t, "DD_L", 4) == 0) m = 4;
                    if (m > 0) {
                        Token1_get(t0); i = Preprocessor_eval(ev, t0, 0x7fff);
                        Token1_get(t0); j = Preprocessor_eval(ev, t0, 0x7fff);
                        Token1_get(t0); k = Preprocessor_eval(ev, t0, 0x7fff); Token1_get(t0);
                        if (i == 1) { i = VecChr_N(vc1); } else if (i > 1 && i < 999) { i = lbl[i]; }
                        if (j == 1) { j = VecChr_N(vc1); } else if (j > 1 && j < 999) { j = lbl[j]; }
                        i = i - j + k;
                        goto db_i;
                    }
                    m = 0;
                    if (n == 2 && memcmp(t, "DB",   2)  == 0) m = 1;
                    if (n == 2 && memcmp(t, "DW",   2)  == 0) m = 2;
                    if (n == 2 && memcmp(t, "DD",   2)  == 0) m = 4;
                    if (m > 0) {
                        i = Preprocessor_eval(ev, t0, 0x7fff);
    db_i:
                                   *(char *)VecChr_stkAdd(vc1, 1) =  i        & 0xff;
                        if (m > 1) *(char *)VecChr_stkAdd(vc1, 1) = (i >>  8) & 0xff;
                        if (m > 2) *(char *)VecChr_stkAdd(vc1, 1) = (i >> 16) & 0xff;
                        if (m > 3) *(char *)VecChr_stkAdd(vc1, 1) = (i >> 24) & 0xff;
                    }
                }
            }
            hexDump(vc1->p, VecChr_N(vc1), stdout);
        }
    
        if (strtol(getArg(argc, argv, "clang:", "0"), NULL, 0) != 0) {
            a_Preprocessor_ini1(src, -1, 2, vc0);
            t0->s = vc0->p; t0->s1 = vc0->p + a_VecChr_N(vc0);
            MiniCompiler mc[1]; MiniCompiler_ini(mc); mc->t0 = t0; mc->vc = vc1;
            MiniCompiler_compile(mc, 99); a_VecChr_replace(vc1, 0, 0, mc->lbl->p, a_VecChr_N(mc->lbl)); 
            MiniCompiler_din(mc); a_VecChr_reserve0(vc1);
            if (strtol(getArg(argc, argv, "dbg:", "0"), NULL, 0) != 0) puts(vc1->p);
            a_VecChr_printf(vc2, "imm<sys>:#include \"a4vm-clang-v0.h\"\nEnt_III(256,256,0);\n%s\nLev_III(256,256,0);\n", vc1->p);
            VecChr_N(vc0) = 0; a_Preprocessor_ini1(vc2->p, -1, 2, vc0);
            if (strtol(getArg(argc, argv, "dbg:", "0"), NULL, 0) != 0) puts(vc0->p);
            VecChr_N(vc1) = 0; t0->s = vc0->p; t0->s1 = vc0->p + VecChr_N(vc0);
            for (;;) {
                t = Token1_get(t0);
                if (t0->c == ',' || t0->c == ';') continue;
                if (t0->c == 0) break;
                t0->s = t;
                intptr_t i = Preprocessor_eval(ev, t0, 0x7fff);
                if (ev->err != 0) break;
                *(intptr_t *)VecChr_stkAdd(vc1, sizeof (intptr_t)) = i;
                if (strtol(getArg(argc, argv, "dbg:", "0"), NULL, 0) != 0) printf("%d ", (int) i);
            }
            if (strtol(getArg(argc, argv, "dbg:", "0"), NULL, 0) != 0) printf("\n\n");
            intptr_t *bp = (intptr_t *) vc1->p, bn = VecChr_N(vc1) / sizeof (intptr_t);
            intptr_t a4arg[10]; a4arg[0] = 0; A4vm_exec0(bp, bn, 10, a4arg);
        }
    
        Preprocessor_Eval_din(_a_ ev);
        VecChr_din4(_a_ vc0, vc1, vc2, vc3); BufFree_flush(_a_ bf); BufFree_din(_a_ bf);
        a_malloc_debugList(_a);
        a_DbgObjInfTbl_debugList(_a);
        return 0;
    }
    
    int eval_hook1(a_Preprocessor_Eval *w, a_Token0 *t0, int pri, const char *t, intptr_t *pi)
    {
        BufFree *bf = w->v[0];
        VecChr *vcTmp = w->v[1];
        intptr_t n = t0->len; (void) pri;
        if (n >= 2 && t[0] == 0x22) {
            char *s;
            VecChr_N(vcTmp) = 0; VecChr_puts(vcTmp, t + 1, n - 2);
            VecChr_convEsc(vcTmp);
            *pi = (intptr_t) (s = BufFree_malloc(_a_ bf, VecChr_N(vcTmp) + 1));
            memcpy(s, vcTmp->p, VecChr_N(vcTmp) + 1);
            return 1;
        }
        if (n == 6 && memcmp(t, "printf", 6)    == 0) { *pi = (intptr_t) printf;    return 1; }
    #if (UseGraph != 0)
        if (n == 7 && memcmp(t, "openWin", 7)   == 0) { *pi = (intptr_t) openWin;   return 1; }
        if (n == 6 && memcmp(t, "setPix", 6)    == 0) { *pi = (intptr_t) setPix;    return 1; }
        if (n == 8 && memcmp(t, "flushWin", 8)  == 0) { *pi = (intptr_t) flushWin;  return 1; }
        if (n == 7 && memcmp(t, "waitInf", 7)   == 0) { *pi = (intptr_t) waitInf;   return 1; }
    #endif
        return 0;
    }
    
    a_static void MiniCompiler_ini(MiniCompiler *w)
    {
        a_BufFree_ini(_a_ w->bf); a_VecChr_ini4(_a_ w->stk, w->lbl, 0, 0); w->tmpNo = 0; w->tmpLb = 0; w->autoClose = 0;
    }
    
    a_static void MiniCompiler_din(MiniCompiler *w)
    {
        a_BufFree_flush(_a_ w->bf); a_BufFree_din(_a_ w->bf); a_VecChr_din4(_a_ w->stk, w->lbl, 0, 0);
    }
    
    a_static char *MiniCompiler_newTmp(MiniCompiler *w)
    {
        char s[256];
        intptr_t n = sprintf(s, "tmp%d", w->tmpNo++);
        char *v = a_BufFree_malloc(_a_ w->bf, n + 1);
        memcpy(v, s, n + 1);
        return v;
    }
    
    #define MiniCompiler_compile_Macro0(pri, fmt)   vv = MiniCompiler_compile(w, pri); tv = MiniCompiler_newTmp(w); a_VecChr_printf(w->vc, fmt, tv, v, vv); v = tv; goto op2
    
    a_static char *MiniCompiler_compile(MiniCompiler *w, int pri)
    {
        char *v, *tv, *vv;
    op1:
        const char *tt, *t = a_Token1_get(w->t0); intptr_t n = w->t0->len; uint32_t c = w->t0->c;
        if (w->autoClose > 0) { w->t0->s = t; c = '}'; w->autoClose--; }
        if (c == '}') {
            MiniCompiler_Sub *sb = VecChr_stkTop(w->stk, sizeof (MiniCompiler_Sub));
            if (sb->mod == 0) {
                a_VecChr_printf(w->vc, "Lbl_T(LT%04d);\n%s%sLbl_T(LT%04d);\n", sb->lb + 1, sb->vc[1].p, sb->vc[0].p, sb->lb + 3);
                a_VecChr_din4(_a_ &sb->vc[0], &sb->vc[1], 0, 0); VecChr_stkRmv(w->stk, sizeof (MiniCompiler_Sub)); goto op1;
            }
            w->t0->s = t; return NULL;
        }
        if (n == 0) return NULL;
        if (c == '-') {
            tv = MiniCompiler_compile(w, 1); v = MiniCompiler_newTmp(w);
            a_VecChr_printf(w->vc, "Mul(%s, %s, CInt:-1);\n", v, tv); goto op2;
        }
        if (c ==  ';') { if (pri == 98) { w->t0->s = t; return NULL; } a_VecChr_printf(w->vc, "Semi();\n"); w->tmpNo = 0; w->autoClose *= -1; goto op1; }
        if (c == '(') { v = MiniCompiler_compile(w, 99); a_Token1_get(w->t0); goto op2; } // (...)
        tt = Token1_get(w->t0); if (w->t0->c == ':') { // コードラベル宣言.
            a_VecChr_printf(w->vc, "Lbl_T(%.*s);\n", n, t);
            a_VecChr_printf(w->lbl, "#defineX %.*s  defEnum(1)\n", n, t);
            goto op1;
        }
        if (n == 2 && memcmp(t, "if", 2) == 0 && w->t0->c == '(') {
            v = MiniCompiler_compile(w, 99); a_Token1_get(w->t0); // ')'.
            tt = a_Token1_get(w->t0);
            if (w->t0->len == 4 && memcmp(tt, "goto", 4) == 0) {
                tt = a_Token1_get(w->t0); a_VecChr_printf(w->vc, "Jne(%s, CInt:0, Txt:%.*s);\n", v, w->t0->len, tt); goto op1;
            }
            w->t0->s = t; return NULL;
        }
        if (n == 3 && memcmp(t, "for", 2) == 0 && w->t0->c == '(') {
            MiniCompiler_compile(w, 98); Token1_get(w->t0);
            MiniCompiler_Sub *sb = a_VecChr_stkAdd(w->stk, sizeof (MiniCompiler_Sub));
            sb->mod = 0;
            sb->lb = w->tmpLb; w->tmpLb += 4;
            a_VecChr_ini4(_a_ &sb->vc[0], &sb->vc[1], 0, 0);
            a_VecChr_printf(w->vc, "Jmp_T(LT%04d);\n", sb->lb + 2);
            a_VecChr_printf(w->vc, "Lbl_T(LT%04d);\n", sb->lb + 0);
            a_VecChr *vc0 = w->vc; w->vc = &sb->vc[0];
            a_VecChr_printf(w->vc, "Lbl_T(LT%04d);\n", sb->lb + 2);
            v = MiniCompiler_compile(w, 98); Token1_get(w->t0);
            if (v == NULL) { a_VecChr_printf(w->vc, "Jmp_T(LT%04d);\n", sb->lb + 0); }
            else { a_VecChr_printf(w->vc, "Jne(%s, CInt:0, Txt:LT%04d);\n", v, sb->lb + 0); }
            w->vc = &sb->vc[1];
            v = MiniCompiler_compile(w, 98); Token1_get(w->t0); // ')'
            w->vc = vc0; w->tmpNo = 0;
            tt = a_Token1_get(w->t0); if (w->t0->c != '{') { w->t0->s = tt; w->autoClose--; }
            goto op1;
        }
        if (n == 13 && memcmp(t, "Mul64Shr_RRRI", 13) == 0) {
            for (;;) {
                Token1_get(w->t0); if (w->t0->c == ';') break;
            }
            a_VecChr_printf(w->vc, "%.*s\n", w->t0->s - t, t);
            goto op1;
        }
        if (n == 7 && memcmp(t, "openWin",  7) == 0) goto cFunc;
        if (n == 6 && memcmp(t, "setPix",   6) == 0) goto cFunc;
        if (n == 8 && memcmp(t, "flushWin", 8) == 0) goto cFunc;
        if (n == 7 && memcmp(t, "waitInf",  7) == 0) goto cFunc;
        if (n == 6 && memcmp(t, "printf",   6) == 0) {
    cFunc:
            a_VecChr vc0[1]; a_VecChr_ini(_a_ vc0); a_VecChr_printf(vc0, "%.*s(", n, t);
            for (;;) {
                t = Token1_get(w->t0); if (w->t0->c == ')') break;
                if (w->t0->c == ',') continue;
                w->t0->s = t; v = MiniCompiler_compile(w, 98); tt = MiniCompiler_newTmp(w);
                a_VecChr_printf(w->vc, "Let(%s, %s);\n", tt, v);
                a_VecChr_printf(vc0, "%s,", tt);
            }
            n = a_VecChr_N(vc0); if (vc0->p[n - 1] == ',') n--;
            a_VecChr_printf(w->vc, "%.*s);\n", n, vc0->p); a_VecChr_din(_a_ vc0); v = "tmp0"; goto op2;
        }
        w->t0->s = tt; // 一度読み込んだ未解釈の演算子をt0に押し戻す.
        if (n == 3 && memcmp(t, "int", 3) == 0) {
            for (;;) {
                t = a_Token1_get(w->t0); if (*t == ';') break;
                if (*t == ',') continue;
                a_VecChr_printf(w->vc, "#defineX %.*s  Int:defEnum(0)\n", w->t0->len, t);
            }
            w->t0->s = t; goto op1;
        }
        if (('0' <= *t && *t <= '9') || *t == 0x22) {
            v = a_BufFree_malloc(_a_ w->bf, n + 1 + 5);
            memcpy(v, "CInt:", 5); memcpy(v + 5, t, n); v[n + 5] = '\0';
        } else {
            v = a_BufFree_malloc(_a_ w->bf, n + 1);
            memcpy(v, t, n); v[n] = '\0';
        }
    op2:
        t = Token1_get(w->t0); c = w->t0->c; n = w->t0->len;
        if (n == 0) return v;
        if (c == ('+' | '+' << 8) && pri >=  1)  {
            tv = MiniCompiler_newTmp(w);
            a_VecChr_printf(w->vc, "Let(%s, %s); Add(%s, %s, CInt:1);\n", tv, v, v, v);
            v = tv; goto op2;
        }
        if (c ==  '*'             && pri >=  4) { MiniCompiler_compile_Macro0( 3, "Mul(%s, %s, %s);\n"); }
        if (c ==  '/'             && pri >=  4) { MiniCompiler_compile_Macro0( 3, "Div(%s, %s, %s);\n"); }
        if (c ==  '%'             && pri >=  4) { MiniCompiler_compile_Macro0( 3, "Mod(%s, %s, %s);\n"); }
        if (c ==  '+'             && pri >=  5) { MiniCompiler_compile_Macro0( 4, "Add(%s, %s, %s);\n"); }
        if (c ==  '-'             && pri >=  5) { MiniCompiler_compile_Macro0( 4, "Sub(%s, %s, %s);\n"); }
        if (c == ('<' | '<' << 8) && pri >=  6) { MiniCompiler_compile_Macro0( 5, "Shl(%s, %s, %s);\n"); }
        if (c == ('>' | '>' << 8) && pri >=  6) { MiniCompiler_compile_Macro0( 5, "Shr(%s, %s, %s);\n"); }
        if (c == ('<' | '=' << 8) && pri >=  7) { MiniCompiler_compile_Macro0( 6, "Cle(%s, %s, %s);\n"); }
        if (c ==  '<'             && pri >=  7) { MiniCompiler_compile_Macro0( 6, "Clt(%s, %s, %s);\n"); }
        if (c == ('>' | '=' << 8) && pri >=  7) { MiniCompiler_compile_Macro0( 6, "Cge(%s, %s, %s);\n"); }
        if (c ==  '>'             && pri >=  7) { MiniCompiler_compile_Macro0( 6, "Cgt(%s, %s, %s);\n"); }
        if (c == ('=' | '=' << 8) && pri >=  8) { MiniCompiler_compile_Macro0( 7, "Ceq(%s, %s, %s);\n"); }
        if (c == ('!' | '=' << 8) && pri >=  8) { MiniCompiler_compile_Macro0( 7, "Cne(%s, %s, %s);\n"); }
        if (c ==  '&'             && pri >=  9) { MiniCompiler_compile_Macro0( 8, "And(%s, %s, %s);\n"); }
        if (c ==  '^'             && pri >= 10) { MiniCompiler_compile_Macro0( 9, "Xor(%s, %s, %s);\n"); }
        if (c ==  '|'             && pri >= 11) { MiniCompiler_compile_Macro0(10, "Or_(%s, %s, %s);\n"); }
        if (c == '='  && pri >= 15) { a_VecChr_printf(w->vc, "Let(%s, %s);\n", v, MiniCompiler_compile(w, 15)); goto op2; }
        if (c ==  ';' && pri >= 98) { if (pri == 98) { w->t0->s = t; return v; } a_VecChr_printf(w->vc, "Semi();\n"); w->tmpNo = 0; w->autoClose *= -1; goto op1; }
        w->t0->s = t; return v; // 一度読み込んだ未解釈の演算子をt0に押し戻してからreturn.
    }

(99) 更新履歴

  • 2026.04.25(土) 初版
  • 2026.04.27(月) 誤記修正

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2026-04-27 (月) 16:09:14 (49d)