acl4のページ0013
(1)
#if (a_Version >= 1)
#define Preprocessor_incl0 a_Preprocessor_incl0
#define Preprocessor_Put0 a_Preprocessor_Put0
#define Preprocessor_Put0_ini a_Preprocessor_Put0_ini
#define Preprocessor_Put0_din a_Preprocessor_Put0_din
#define Preprocessor_put0 a_Preprocessor_put0
#define Preprocessor a_Preprocessor
#define Preprocessor_ini a_Preprocessor_ini
#define Preprocessor_din a_Preprocessor_din
#define Preprocessor_line a_Preprocessor_line
#define Preprocessor_main a_Preprocessor_main
#endif
a_static int a_Preprocessor_incl0(void *w, char *s, char *s1, a_Preprocessor_SourceFiles *sfs)
{
(void) w;
while (s < s1 && *(unsigned char *) s <= ' ') s++;
if (*s == 0x22) {
char *t = strchr(s + 1, 0x22);
if (t == NULL) t = s1;
a_Preprocessor_SourceFiles_addFile(sfs, s + 1, t - (s + 1));
return 1;
}
if (*s == '<') {
char *t = strchr(s + 1, '>');
if (t == NULL) t = s1;
if (t[-2] == '.' && t[-1] == 'h') return 0; // through.
a_Preprocessor_SourceFiles_addFile(sfs, s + 1, t - (s + 1));
return 1;
}
return 0;
}
a_class(a_Preprocessor_Put0) {
a_Preprocessor_SourceFiles *sfs;
a_VecChr *dst, path[1];
intptr_t lin;
};
a_static void a_Preprocessor_Put0_ini(a_Preprocessor_Put0 *w) { a_VecChr_ini(w->path); w->lin = 0; }
a_static void a_Preprocessor_Put0_din(a_Preprocessor_Put0 *w) { a_VecChr_din(w->path); }
a_static void a_Preprocessor_put0(a_Preprocessor_Put0 *w, a_VecChr *lin)
{
a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
if (w->path->n != sf->path->n || (w->path->n > 0 && memcmp(w->path->p, sf->path->p, w->path->n) != 0)) {
char *s = a_malloc(_arg_ sf->path->n + 64);
sprintf(s, "#line %d \"%.*s\"\n", sf->lin, sf->path->n, sf->path->p);
a_VecChr_puts(w->dst, s, s + strlen(s));
a_free(_arg_ s, sf->path->n + 64);
a_VecChr_replace(w->path, 0, w->path->n, sf->path->p, sf->path->n);
} else if (w->lin + 1 < sf->lin && sf->lin < w->lin + 9) {
while (w->lin + 1 < sf->lin) {
char *s = "\n";
a_VecChr_puts(w->dst, s, s + 1);
w->lin++;
}
} else if (w->lin + 1 != sf->lin) {
char s[64];
sprintf(s, "#line %d\n", sf->lin);
a_VecChr_puts(w->dst, s, s + strlen(s));
}
a_VecChr_puts(w->dst, lin->p, lin->p + lin->n);
w->lin = sf->lin;
}
a_class(a_Preprocessor) {
a_Token0 t0[1];
a_Preprocessor_SourceFiles sfs[1];
a_Set0 set[1];
a_Preprocessor_If ppIf[1];
a_Preprocessor_Eval ppEv[1];
a_Preprocessor_Put0 put0[1];
int (*incl)(void *, const char *, const char *, a_Preprocessor_SourceFiles *);
void (*put)(void *, a_VecChr *, a_Preprocessor_SourceFiles *);
void *w_incl, *w_put;
};
a_static void a_Preprocessor_ini(a_Preprocessor *w)
{
a_Token0_ini1(w->t0);
a_Preprocessor_SourceFiles_ini(w->sfs);
a_Preprocessor_if0(w->ppIf);
a_Set0_ini(w->set);
// 以下はとりあえずのデフォルト設定. 不要なら上書きする.
w->ppIf->eval = (void *) a_Preprocessor_eval;
w->ppIf->w_eval = w->ppEv;
w->incl = (void *) a_Preprocessor_incl0;
w->w_incl = NULL;
a_Preprocessor_Put0_ini(w->put0);
w->put = (void *) a_Preprocessor_put0;
w->w_put = w->put0;
w->put0->sfs = w->sfs;
a_Preprocessor_define7(w->set, -1, "ifdef", "if defined");
a_Preprocessor_define7(w->set, -1, "ifndef", "if !defined");
a_Preprocessor_define7(w->set, -1, "elifdef", "elif defined");
a_Preprocessor_define7(w->set, -1, "elifndef", "elif !defined");
}
a_static void a_Preprocessor_din(a_Preprocessor *w)
{
a_Preprocessor_define3(w->set);
a_Preprocessor_if3(w->ppIf);
a_Preprocessor_SourceFiles_din(w->sfs);
a_Preprocessor_Put0_din(w->put0);
}
a_static void a_Preprocessor_line(a_Preprocessor *w)
{
a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
const char *t = a_Token1_get(w->t0);
sf->lin = strtol(t, NULL, 0) - 1;
t = a_Token1_get(w->t0);
if (w->t0->len >= 2 && *t == 0x22) {
sf->path->n = 0;
a_VecChr_puts(sf->path, t + 1, t + w->t0->len - 1);
}
}
a_static void a_Preprocessor_main(a_Preprocessor *w)
{
int defC = 1;
a_VecChr lin[1], tmp[1], stkDef[1]; a_VecChr_ini4(lin, tmp, stkDef, 0);
for (;;) {
a_Preprocessor_SourceFiles_gets(w->sfs, lin);
if (lin->n == 0) break;
for (;;) { // 末尾が "\\\n"であれば、後続行を接続.
char *p1 = lin->p + lin->n;
if (!(lin->n >= 2 && p1[-2] == '\\' && p1[-1] == '\n')) break;
a_Preprocessor_SourceFiles_gets(w->sfs, tmp);
a_VecChr_replace(lin, lin->n - 2, 2, tmp->p, tmp->n);
}
if (a_Preprocessor_isDirective(lin->p, lin->p + lin->n) == 0) {
a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
if (a_Preprocessor_define2(w->set, w->t0, lin, defC, sf->path, sf->lin, 0) != 0) {
retry:
a_Preprocessor_SourceFiles_addBuf(w->sfs, lin->p, lin->n);
continue;
}
if (w->ppIf->phase != 0)
w->put(w->w_put, lin, w->sfs);
continue;
}
const char *s = lin->p, *s1 = lin->p + lin->n;
while (s < s1 && *(const unsigned char *) s <= ' ') s++;
s++; // '#'.
if (strncmp(s, "if", 2) == 0 || strncmp(s, "elif", 4) == 0) {
a_Preprocessor_SourceFile *sf = a_Preprocessor_SourceFiles_getSf(w->sfs);
if (a_Preprocessor_define2(w->set, w->t0, lin, defC, sf->path, sf->lin, 0) != 0) goto retry;
}
w->t0->s = s + 1; w->t0->s1 = lin->p + lin->n; a_Token1_get(w->t0);
if (w->ppIf->phase != 0) {
if (strncmp(s, "include", 7) == 0) { if (w->incl(w->w_incl, s + 7, s1, w->sfs) > 0) continue; }
if (strncmp(s, "define", 6) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 0); continue; }
if (strncmp(s, ".def", 4) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 1); continue; }
if (strncmp(s, ".addTl", 6) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 2); continue; }
if (strncmp(s, ".addHd", 6) == 0) { a_Preprocessor_define5(w->set, w->t0, lin, defC, w->sfs, stkDef, 3); continue; }
if (strncmp(s, "undef", 5) == 0) { if (a_Preprocessor_define6(w->set, w->t0, lin, stkDef, 0) > 0) continue; }
if (strncmp(s, ".undef", 6) == 0) { if (a_Preprocessor_define6(w->set, w->t0, lin, stkDef, 1) > 0) continue; }
if (strncmp(s, "line", 4) == 0) { a_Preprocessor_line(w); continue; }
}
w->t0->s = s; w->t0->s1 = lin->p + lin->n;
char *t = a_Token1_get(w->t0);
if (a_Preprocessor_if2(w->ppIf, lin, w->t0, t) > 0) continue;
if (w->ppIf->phase != 0)
w->put(w->w_put, lin, w->sfs); // エラー検出のため.
continue;
}
while (stkDef->n > 0) {
a_Preprocessor_Define *sp = a_VecChr_stkRmv(stkDef, sizeof (a_Preprocessor_Define));
a_Preprocessor_Define_din(sp);
}
a_VecChr_din4(lin, tmp, stkDef, 0);
}
(2) 説明
- Preprocessor_incl0 → #include 処理をどうするかをプログラム側でカスタマイズできるようにしています。これはデフォルトで提供される関数です。
- Preprocessor_put0 → プリプロセッサからの出力処理をプログラム側でカスタマイズできるようにしています。これはデフォルトで提供される関数です。
- Preprocessor_line → #line の処理関数です。
- Preprocessor_main → プリプロセッサ処理のmainです。
(3) サンプルプログラム: t0013a.c [13行]
- acl4ライブラリは以下のようにして利用します。
- [1]まずa4_0001~a4_0013のプログラムをつなげて"acl4.c"として保存します。
- [2]次にacl4を使ったプログラムを書きます。
- [3]コンパイル時に、"acl4.c"のあるパスをインクルードパスの一つとして指定します。
#define a_Version 1
#include <acl4.c>
int main(int argc, const char **argv)
{
if (argc < 2) return 1;
Preprocessor pp[1]; Preprocessor_ini(pp); // プリプロセッサの初期化.
Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 最初に読むファイルを指定.
VecChr dst[1]; VecChr_ini(dst); pp->put0->dst = dst; // 出力先のオブジェクトを指定.
Preprocessor_main(pp); Preprocessor_din(pp); // プリプロセッサ処理. およびメモリ開放.
VecChr_reserve0(dst); puts(dst->p); VecChr_din(dst); // 処理結果を出力. およびメモリ開放.
a_malloc_debugList(_arg); // メモリリークがないか確認.
}
- これは、ちゃんと動く(と思われる)プリプロセッサです。acl4ライブラリを使うことで、13行で書けています。
- 動作テストとして、t0013a.exe を使って t0013a.c 自身をプリプロセスして、その結果をコンパイラにかけたところ問題なくビルドできていたので、たぶん大きなバグはないと思います。
- 手元の環境では t0013a.exe は 20.0KB になりました(まあ私の基準では小さくはないですが、大きくもないです)。
- ちなみに「 prompt>t0013a t0013a.c > t0013a.txt 」とすると、こんな感じのファイルができます。
#line 7 "a4_0001.c"
#line 19
#include <ctype.h>
#include <errno.h>
#include <float.h>
(中略)
#line 3 "t0013a.c"
int main(int argc, const char **argv)
{
if (argc < 2) return 1;
a_Preprocessor pp[1]; a_Preprocessor_ini(pp); // プリプロセッサの初期化.
a_Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 最初に読むファイルを指定.
a_VecChr dst[1]; a_VecChr_ini(dst); pp->put0->dst = dst; // 出力先のオブジェクトを指定.
a_Preprocessor_main(pp); a_Preprocessor_din(pp); // プリプロセッサ処理. およびメモリ開放.
a_VecChr_reserve0(dst); puts(dst->p); a_VecChr_din(dst); // 処理結果を出力. およびメモリ開放.
a_malloc_debugList("t0013a.c", 12); // メモリリークがないか確認.
}
- 目視でも、正しく変換できていると思います。
- なお、標準ライブラリの #include が処理されずにそのままになっているのは意図的なものです。
- Preprocessor_incl0 で < ... .h> な include については、インクルード処理をするなと明記しているからです。
- まだインクルードパスを探す処理を書いていないので、無理にやっても失敗してしまうのです。
(3) サンプルプログラム: t0013b.c [101行]
- a4_p0002 の p0002b.c の main だけを改造して、この言語にプリプロセッサ機能を加えるということをやってみました。
- acl4ライブラリは以下のようにして利用します。
- [1]まずa4_0001~a4_0013のプログラムをつなげて"acl4.c"として保存します。
- [2]次にacl4を使ったプログラムを書きます。
- [3]コンパイル時に、"acl4.c"のあるパスをインクルードパスの一つとして指定します。
#define a_Version 1
#include <acl4.c>
class_(Var) { SetElm elm[1]; double d; };
Set0 setVar[1];
double *getVarPtr(const char *s, intptr_t n) → p0002b.c のものをそのまま使います
int np = 0;
double eval(Token0 *t0, int pri) → p0002b.c のものをそのまま使います
void myPut(Preprocessor_Put0 *w, VecChr *lin)
{
// エラーが起きて未処理扱いになったプリプロセッサ制御行があれば、それはエラー出力して変換結果に含めない.
if (Preprocessor_isDirective(lin->p, lin->p + lin->n) != 0)
fprintf(stderr, "[err] %.*s", lin->n, lin->p);
else
VecChr_puts(w->dst, lin->p, lin->p + lin->n);
}
int main(int argc, const char **argv)
{
if (argc < 2) return 1;
Preprocessor pp[1]; Preprocessor_ini(pp); // プリプロセッサの初期化.
pp->put = (void *) myPut; // 出力関数の変更.
Preprocessor_SourceFiles_addFile(pp->sfs, argv[1], strlen(argv[1])); // 入力ファイルを指定.
VecChr vc[1]; VecChr_ini(vc); pp->put0->dst = vc; // 出力先のオブジェクトを指定.
Preprocessor_main(pp); Preprocessor_din(pp); // プリプロセッサ処理. およびメモリ開放.
Token0 t0[1]; Token0_ini1(t0); // ここから下は p0002b.c の main にあったものをそのまま使用.
Set0_ini(setVar);
t0->s = vc->p; t0->s1 = vc->p + vc->n;
double d = eval(t0, 99);
if (np == 0)
printf("%f\n", d);
VecChr_din(vc);
intptr_t i, n = setVar->tbl->n / sizeof (Var *);
for (i = 0; i < n; i++)
free_(_arg_ ((Var **) setVar->tbl->p)[i], sizeof (Var));
Set0_din(setVar);
a_malloc_debugList(_arg);
return 0;
}
- 元の p0002b.c は86行だったので、行数的には15行しか増えていません。
- でもこれだけで、 #include や #if や #define が使えるようになります。
- 増えた行数は15行ですが、では実行ファイルサイズとしてはどうでしょうか。
- p0002b.exe → 13.5KB
- t0013b.exe → 22.0KB
- ということで、8.5KBしか増えていません(これはいい!!)。
- acl4を使って作った適当な自作言語があったとして、それに15行を書き足して8.5KBの増加を許容すれば、プリプロセッサ機能が手に入るわけです。
(99) 更新履歴
- 2026.02.11(水) 初版
- 2026.02.12(木) t0013b.c を追記