acl4のページ0004
(1)
#if (a_Version >= 1)
#define VecChr_vprintf a_VecChr_vprintf
#define VecChr_printf a_VecChr_printf
#define VecChr_putc a_VecChr_putc
#define VecChr_readFileAll a_VecChr_readFileAll
#define VecChr_readFileAll_errChk a_VecChr_readFileAll_errChk
#define VecChr_eraseCr a_VecChr_eraseCr
#define VecChr_writeFileAll a_VecChr_writeFileAll
#define VecChr_writeFileAll_errChk a_VecChr_writeFileAll_errChk
#define VecChr_stkAdd a_VecChr_stkAdd
#define VecChr_stkRmv a_VecChr_stkRmv
#endif
a_static intptr_t a_VecChr_vprintf(a_VecChr *w, const char *f, va_list a)
{
intptr_t d = w->n1 - w->n;
intptr_t i = vsnprintf(w->p + w->n, d, f, a);
if (i <= 0) goto fin;
if (i + 1 > d) {
a_VecChr_reserveDiff(w, i + 1);
d = w->n1 - w->n;
i = vsnprintf(w->p + w->n, d, f, a);
if (i <= 0) goto fin;
}
w->n += i;
fin:
return i;
}
a_static intptr_t a_VecChr_printf(a_VecChr *w, const char *f, ...)
{
va_list ap;
va_start(ap, f);
intptr_t i = a_VecChr_vprintf(w, f, ap);
va_end(ap);
return i;
}
a_static int a_VecChr_putc(a_VecChr *w, int c)
{
a_VecChr_reserveDiff(w, 2);
w->p[w->n++] = c;
w->p[w->n] = '\0';
return c;
}
a_static intptr_t a_VecChr_readFileAll(a_VecChr *w, const char *path)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL) return -1;
intptr_t bufSz = 1024 * 1024, n0 = w->n;
for (;;) {
a_VecChr_reserveDiff(w, bufSz);
int i = fread(w->p + w->n, 1, bufSz, fp);
if (i <= 0) break;
w->n += i;
}
fclose(fp);
a_VecChr_reserve0(w);
a_VecChr_shrink(_arg_ w, 1);
return w->n - n0;
}
a_static intptr_t a_VecChr_readFileAll_errChk(a_VecChr *w, const char *path)
{
if (path == NULL) a_errExit("fopen error: path=NULL");
intptr_t i = a_VecChr_readFileAll(w, path);
if (i < 0) a_errExit("fopen error: %s", path);
return i;
}
a_static intptr_t a_VecChr_eraseCr(a_VecChr *w)
{
char *p, *q, *p1;
p = q = w->p;
p1 = p + w->n;
while (p < p1) {
if (*p++ != '\r')
*q++ = p[-1];
}
w->n -= p1 - q;
a_VecChr_reserve0(w);
return p1 - q;
}
a_static intptr_t a_VecChr_writeFileAll(a_VecChr *w, const char *path, const char *mod)
{
FILE *fp = fopen(path, mod);
if (fp == NULL) return -1;
intptr_t i = 0, j;
for (;;) {
j = fwrite(w->p + i, 1, w->n - i, fp);
if (j <= 0) break;
i += j;
}
fclose(fp);
return i;
}
a_static intptr_t a_VecChr_writeFileAll_errChk(a_VecChr *w, const char *path, const char *mod)
{
if (path == NULL) a_errExit("fopen error: path=NULL");
intptr_t i = a_VecChr_writeFileAll(w, path, mod);
if (i < 0)
a_errExit("fopen error: %s", path);
if (i < w->n)
a_errExit("fwrite error: %s", path);
return i;
}
a_static void *a_VecChr_stkAdd(a_VecChr *w, intptr_t sz)
{
a_VecChr_resizeDiff(_arg_ w, + sz);
return w->p + w->n - sz;
}
a_static void *a_VecChr_stkRmv(a_VecChr *w, intptr_t sz)
{
return w->p + (w->n -= sz);
}
(2) VecChr_printf など
- 雰囲気としては sprintf の拡張版みたいなものです。
- 違うのは、出力結果が準備した配列に収まるかどうかを気にする必要がないことと、コンソールやファイルのように、どんどん追記できるというところです。
- 私がコンパイラを作るとき、fprintfなどでアセンブラをファイルに出力したりはしません。VecChrを使って、メモリ上に出力します。
- こうしておけば、ファイルに出力することもしないこともできます。ファイルに出力せず、オンメモリで自作のアセンブラに渡すこともあります。こうすれば中間ファイルを生成せずに処理を進めることもできるのです。
- そうやってファイルを出力しないプログラミングをやっていると、入力もファイルではなくメモリでやりたくなるので、結局、メモリから入力してメモリへ出力するような作り方になります。
- すると、ファイルを全部読んでメモリに入れる関数や、メモリからファイルへ書き出す関数が欲しくなって、readFileAllやwriteFileを作っています。
- eraseCrは、メモリ上の'\r'の文字コードをすべて消去する関数です。
- 本来はfopenするときに"rt"でオープンすればいいだけなので、こんな関数は必要ないはずです。実際、Windows上ではこれでうまくいきます。
- しかしLinux上ではうまくいきません。Linuxではテキストモード・バイナリモードという区別がなく、"t"をつけても何もしません。・・・Linuxではテキストファイルに'\r'を含めずに出力するので、この仕様で問題ないのですが、しかしWindowsで作ったテキストファイルをLinux上で読ませると'\r'を含んだまま読み込んでしまいます。こうして'\r'の存在を想定していないプログラムを書いてしまうと、誤動作してしまうのです。
- これに悩まされたので、私はWindowsでもLinuxでも"rb"で読み込んで、自前の関数で'\r'を消去するようにしています。これならどんな組み合わせでも誤動作はありません。
(3) サンプルプログラム: t0004a.c
- acl4ライブラリは以下のようにして利用します。
- [1]まずa4_0001~a4_0004のプログラムをつなげて"acl4.c"として保存します。
- [2]次にacl4を使ったプログラムを書きます。
- [3]コンパイル時に、"acl4.c"のあるパスをインクルードパスの一つとして指定します。
#define a_Version 1
#include <acl4.c>
int main()
{
VecChr vc[1]; VecChr_ini(vc);
VecChr_printf(vc, "hello, world\n");
int i;
for (i = 0; i < 10; i++)
VecChr_printf(vc, "%d ", i);
VecChr_putc(vc, '\n');
printf("%s", vc->p); // メモリ内で作った文字列を一気に出力.
printf("vc.n=(%d/%d)\n", vc->n, vc->n1);
VecChr_din(vc);
a_malloc_debugList(_arg); // 開放忘れがないことを確認するために、これを書くくせがある.
return 0;
}
>t0004a
hello, world
0 1 2 3 4 5 6 7 8 9
vc.n=(34/64)
t0004a.c(15): malloc_debugList()
(4) VecChr_stkAdd, stkRmv
- VecChrでスタックを作れば、サイズの管理を気にしなくてよくなるのではないかと思いついて、作ってみたものです。
(5) サンプルプログラム: t0004b.c
- acl4ライブラリは以下のようにして利用します。
- [1]まずa4_0001~a4_0004のプログラムをつなげて"acl4.c"として保存します。
- [2]次にacl4を使ったプログラムを書きます。
- [3]コンパイル時に、"acl4.c"のあるパスをインクルードパスの一つとして指定します。
#define a_Version 1
#include "acl4.c"
class_(Elm) { int i; const char *s; };
int main()
{
VecChr stk[1]; VecChr_ini(stk); Elm *sp;
sp = VecChr_stkAdd(stk, sizeof (Elm)); sp->i = 123; sp->s = "abc";
sp = VecChr_stkAdd(stk, sizeof (Elm)); sp->i = 456; sp->s = "def";
sp = VecChr_stkAdd(stk, sizeof (Elm)); sp->i = 789; sp->s = "ghi";
printf("stack-top = [%d, %s]\n", sp->i, sp->s);
sp = VecChr_stkRmv(stk, sizeof (Elm));
printf("stack-top = [%d, %s]\n", sp->i, sp->s);
sp = VecChr_stkRmv(stk, sizeof (Elm));
printf("stack-top = [%d, %s]\n", sp->i, sp->s);
VecChr_din(stk);
a_malloc_debugList(_arg);
return 0;
}
>t0004b
stack-top = [789, ghi]
stack-top = [456, def]
stack-top = [123, abc]
t0004b.c(20): malloc_debugList()
(99) 更新履歴
- 2026.01.28(水) 初版。
- 2026.01.30(金) 関数名変更: stkPush → stkAdd, stkPull → stkRmv
- 2026.02.09(月) a_VecChr_stkRmv の仕様を変更