acl4の開発ログ #08
2026.03.14(土) #0
- ライブラリに加えるとなると、一応それなりの完成度は欲しくて、だからテスト用に雑に書いた関数はライブラリに含めらません。
- だからいろいろ書いているのに、ライブラリには全然追加できてなくて、なんかそこがもどかしいです。
2026.03.14(土) #1
- この開発ログの書き方もよくないよなあと反省中。
- ログはずっと同じページに書き続けて、たくさんになったら過去ログへ移動するスタイルにしたいので、後でそれをやろうと思います。
2026.03.15(日) #0
2026.03.15(日) #1
- ここ数年で最高のアイデアかもしれないものを思いつきました。
- [1](前提の説明)
- まず私は自作プリプロセッサを作りました。そしてプリプロセッサ処理関数を使えば、アセンブラ・仮想マシン・トランスパイラなどがとても簡単に作れることを示しました。
- [2](前提の説明)
- こんなにふうに何でも作れて本当に便利なのですが、でもこれには適用条件があります。プリプロセッサの置換機能が生きてくるのは、Macro(arg0, arg1, ...) みたいな形式で書かれているときだけなのです。
- だから、 a = b + c * d; みたいな構文があっても、プリプロセッサは何もできません。もしこれが何とかできるようになったら、言語の開発なんて革命的に楽ができそうなのに・・・。そんなふうにずっと前から(10年以上前から)なんとなく思っていました。
- そこまでは思っていても、そこから先を思いつくことはありませんでした。
- [3](今回のアイデア)
- それならば、そういう変換だけをするプログラムを作ればいいじゃないですか!
Mul(tmp0. c, d);
Add(tmp1, b, tmp0);
Let(a, tmp1);
- この形式に変換できさえすれば、あとはいくらでもプリプロセッサだけで加工できます。そしてこの形式に変換するだけなら、コンパイラを作るのはずっと楽になるし、これを一度作っておけば後段の置換処理を用意するだけで、実に様々な用途に使えるはずです。
- [4](今回のアイデア)
- この拡張プリプロセッサは、型情報を持たせようと思っていて、だから Mul(int,int)とMul(float,float)は別の定義を与えることができます。これができれば置換処理の記述は楽になると思っています。
- また前方参照も可能にしようと思っています。
2026.03.16(月) #0
- 新しいプリプロセッサ:
- C言語との互換性は気にしない
- #include は #incl にする
- #if 類はそのまま
- #undef はなくす
- #define は #def にする
- #ldef がある(ローカル宣言)
- #scop 命令があって、#endscop するとその中の #ldef は全部消える
- 前方参照OK
2026.03.17(火) #0
- 私が最初に覚えたアセンブラは MC6809 という CPU のものでした。これには COM という命令があって、ビット反転を意味していました。 x86 でいうところの NOT 命令です。
- 昨日この COM 命令が急に気になりました。なぜビット反転なのに NOT ではなく COM なのか? COM は何の省略形なのか?
- 苦労しましたが、ようやく分かりました。 complement の略のようです。補数ってことですね。ビット反転は「1の補数表現」に当たるので、これは納得がいきます。
- ついでに補数表現についても調べました。いや「1の補数表現」「2の補数表現」というのがあるのは35年以上前から知っていましたが、「3の補数表現」とか「4の補数表現」とかが何であるのか急に気になったためです。
- 結論からいうと、2進数で考えているときは「1の補数表現」と「2の補数表現」しかなくて、3や4はないということが分かりました。他の進法で考えれば、3や4も出てきます。
- (詳細に興味があれば「補数表現」を調べてみてください。)
2026.03.17(火) #1
// tt0003a.c (memo: p0002b.c を参考にして作った)
#define a_Version 1
#include <acl4.c>
a_class(a_BufFree) { a_VecChr stk[1]; };
a_static void a_BufFree_ini(a_BufFree *w) { a_VecChr_ini(w->stk); }
a_static void a_BufFree_din(a_BufFree *w) { a_VecChr_din(w->stk); }
a_static void a_BufFree_add(a_BufFree *w, void *p, intptr_t sz)
{
void **t = a_VecChr_stkAdd(w->stk, 2 * sizeof (void *));
t[0] = p; t[1] = (void *) sz;
}
a_static void *a_BufFree_malloc(a_BufFree *w, intptr_t sz)
{
void *p = a_malloc(_arg_ sz); a_BufFree_add(w, p, sz); return p;
}
a_static void a_BufFree_flush(a_BufFree *w)
{
while (w->stk->n > 0) {
w->stk->n -= 2 * sizeof (void *);
void **t = (void **) (w->stk->p + w->stk->n);
a_free(_arg_ t[0], (intptr_t) t[1]);
}
}
a_class(MiniCompiler) {
a_Token0 *t0;
a_BufFree bf[1];
a_VecChr *vc;
int tmpNo;
};
a_static void MiniCompiler_ini(MiniCompiler *w)
{
a_BufFree_ini(w->bf); w->tmpNo = 0;
}
a_static void MiniCompiler_din(MiniCompiler *w)
{
a_BufFree_flush(w->bf); a_BufFree_din(w->bf);
}
a_static char *MiniCompiler_newTmp(MiniCompiler *w)
{
char s[256];
intptr_t n = sprintf(s, "tmp%d", w->tmpNo++);
char *v = a_BufFree_malloc(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)
{
const char *t = Token1_get(w->t0); intptr_t n = w->t0->len; uint32_t c = w->t0->c;
char *v, *tv, *vv;
if (n == 0) return NULL;
if (c == '(') { v = MiniCompiler_compile(w, 99); Token1_get(w->t0); goto op2; }
v = a_BufFree_malloc(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 == '*' && 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 == '=' && pri >= 15) { a_VecChr_printf(w->vc, "Let(%s, %s);\n", v, MiniCompiler_compile(w, 15)); goto op2; }
if (c == ';' && pri >= 98) { a_VecChr_printf(w->vc, "Semi();\n"); v = MiniCompiler_compile(w, 97); goto op2; }
w->t0->s = t; return v; // 一度読み込んだ未解釈の演算子をt0に押し戻してからreturn.
}
int main(int argc, const char **argv)
{
VecChr src[1], dst[1]; VecChr_iniArg(src, argc, argv, 1, 3);
Token0 t0[1]; Token0_ini1(t0);
t0->s = src->p; t0->s1 = src->p + src->n;
MiniCompiler mc[1]; MiniCompiler_ini(mc); mc->t0 = t0;
VecChr_ini(dst); mc->vc = dst;
char *v = MiniCompiler_compile(mc, 99);
if (v == NULL) v = "(null)";
printf("%sans: %s\n", dst->p, v);
MiniCompiler_din(mc);
VecChr_din4(src, dst, 0, 0);
a_malloc_debugList(_arg);
return 0;
}
実行結果:
>tt0003a "x=y=1+2*3; (a+b)*c"
Mul(tmp0, 2, 3);
Add(tmp1, 1, tmp0);
Let(y, tmp1);
Let(x, y);
Semi();
Add(tmp2, a, b);
Mul(tmp3, tmp2, c);
ans: tmp3
- とてもよく動いています。 BufFree は有用だと思ったので、 a4_0014 を作るときに入れたいと思います。
- この構想がうまくいくと、「型によって処理が変わる部分」と「型に関係なく共通でできる部分」がうまく分離できて、言語が(コンパイラもインタプリタも)作りやすくなるのではないかという期待を持っています。
- 今日作った MiniCompiler は「型に関係ない共通の部分」の処理を担当しています。
こめんと欄