* acl3 : A C-lang Library ver.3 -(by [[K]], 2025.05.05) ** 基本方針 -mainは置き換えない。AComArgも使わない。・・・おかげで癖がなくなった。 -[1]まず開発の際にはメモリリークなどを早期に発見したいですし、メモリ管理周りが実行速度上のボトルネックになってしまった場合などは、malloc/freeのアルゴリズムを部分的に変更したくなります。そのためにmalloc/freeを関数ポインタ経由で呼び出すことを前提に全体を作ってあります(AMクラス)。 -[2]次に可変長型のバッファを簡単に扱えるクラスを用意しました(AXクラス)。これで「いつも大きめにバッファを取っておく」というアルゴリズムを使うことはなくなり、必要に応じて必要なだけメモリを使うようになっています。これのおかげで「バッファサイズが足りなくなったか?」というエラーチェック処理も必要なくなりました。 -[3]私は言語などを作る時に「ファイルからソースコードを読んで、結果をファイルに出力する」というスタイルが好きではありません。それは複数のツールを組み合わせようとしたときに、ファイルに対してしか入出力できないために、中間ファイルの生成が避けられなくなるからです。・・・私は基本的にツールを「メモリから入力して、メモリへ出力する」というスタイルで作ります。これなら複数のツールを組み合わせたときに、中間ファイルを生成する必要はなくなります(もちろんデバッグなどのために出力することもできますが)。 -[4]これを容易に実現するために、ファイルの内容をメモリに読み込むとか、メモリの内容をファイルへ書き出すみたいな処理が簡単にできるように準備してあります(AX_fread, AX_fwrite)。またファイルや標準出力に出力するのと同じくらいに簡単にメモリに足して出力できますし(AX_putc, AX_puts, AX_printf)、メモリ上のファイルイメージからのgetsなどもあります。 ** 各種短縮表記 |Ai|intptr_t| |Ap|void *| |As|static| |Av|void| |AStr|char *| |ACStr|const char *| ** AM (malloc, free, reallocの代用) -Ap ''AM_alc''(AM *w, AM_Vt *v, Ai n) --malloc(n)の代用。wやvはシステムが提供しているものをそのまま使う。 -Av ''AM_fre''(AM *w, AM_Vt *v, Ap p, Ai n) --free(p)の代用。nはAM_alc()したときと同じ値を渡す。直前にrlcしているのならそのn1を渡す。 -Ap ''AM_rlc''(AM *w, AM_Vt *v, Ap p, Ai n0, Ai n1) --realloc(p, n1)の代用。n0はAM_alc()したときと同じ値を渡す。直前にrlcしているのならそのn1をn0として渡す。 ** AX (サイズ可変メモリ) (旧名:AExpMem) -''AX_ini''(AX *w) --コンストラクタ。 --コンストラクタを呼んだ直後にw->mとw->mvを上書きすれば、利用するAMを切り替えられる。 -''AX_dst''(AX *w) --デストラクタ。 -Av ''AX_ini4''(AX *x0, AX *x1, AX *x2, AX *x3) --x0~x3をiniするだけ。x2やx3にNULLを指定すれば、それは無視してくれる。 -Av ''AX_dst4''(AX *x0, AX *x1, AX *x2, AX *x3) --x0~x3をdstするだけ。x2やx3にNULLを指定すれば、それは無視してくれる。 -Av ''AX_rsv''(AX *w, Ai n) --w->p + w->n からnバイトをリザーブして、自由に読み書きできるようにする。 --書き込みが済んだら w->n を更新するのを忘れずに。 -Av ''AX_putc''(AX *w, char c) --1バイト書き込み(AX_rsv(1)して、cを書いて、n++する)。 -Av ''AX_puts0''(AX *x, Ap s, Ai n) --nバイト書き込み。 -Av ''AX_puts''(AX *x, ACStr s) --AX_puts0(x, s, strlen(s));する。 -Ai ''AX_fread''(AX *x, FILE *fp) --ファイルをEOFまで読み込んでメモリに格納。読み込んだバイト数を返す。 -Av ''AX_fread1''(AX *x, ACStr path, Ai flg) --AOpen(path, "rb")してfread()してfclose()する。 --pathが"-"の場合は、stdinからfreadする。 --flg:4=リードした結果メモリ内にCR(0x0d)の文字がある場合はこれをすべて取り除く。 --flg:2=ファイル読み取り後に AX_putc('\n'); する。 --flg:1=ファイル読み取り後に AX_putc('\0'); する(色々な用途に使えるように16バイトの0x00を付与している)。 --flg:4=freadに先立って、AXの中身を空にする。この指定がないのなら、fread結果は今あるAXに付け足されることになる。 --flg:2=リードした結果メモリ内にCR(0x0d)の文字がある場合はこれをすべて取り除く。 --flg:1=ファイル読み取り後に AX_putc('\n'); する。 //--flg:1=ファイル読み取り後に AX_putc('\0'); する。 -Av ''AX_fwrite1''(AX *x, ACStr path, ACStr mod) --AOpen()してfwrite()してfclose()する。 --pathが"-"の場合は、stdoutへfwriteする。 --x->pからx->nバイトをファイルに出力。 -Av ''AX_gets0''(AX *x, Ap s, Ai n) --sからnバイトの内容からなる文字列をAX上に作る。末尾に'\0'は付与しない(必要なら自分でやる)。呼び出し前に入っていた内容は上書きで消えるので気にしなくてよい。 -AStr ''AX_gets''(AX *x, ACStr s) --s1 = Astrchr1(s, '\n', 3); AX_gets0(x, s, s1 - s); AX_putc(x, '\0'); return s1;する。 -Ai ''AX_printrf''(AX *x, ACStr fmt, ...) --printfの出力先がAXになったもの。 ** ATokenMgr (文字列にIDを割り当てる) -''ATokenMgr_ini''(ATokenMgr *w) --コンストラクタ。 --コンストラクタを呼んだ直後にw->mとw->mvを上書きすれば、利用するAMを切り替えられる。 -''ATokenMgr_dst''(ATokenMgr *w) --デストラクタ。 -Ai ''ATokenMgr_s2i''(ATokenMgr *w, ACStr s, Ai n) --sからのn文字の文字列に対応する整数(=トークン番号)を返す。新規登録の場合はsを内部にコピーするため、sを破棄しても問題なく動作し続ける。 --内部で二分探索しているので、比較的高速に結果を返すことができる。 -AStr ''ATokenMgr_i2s''(ATokenMgr *w, Ai i) --トークン番号を渡すと対応する文字列を返す。 ** string系拡張 -AStr ''Astrstr1''(ACStr s, ACStr t, Ai flg) --基本的にはstrstr()と同じ。 --flg:2=文字列が見つかった場合、strlen(t)を加算してから返す。 --flg:1=文字列が見つからない場合、s + strlen(s)を返す。 -AStr ''Astrchr1''(ACStr s, char c, Ai flg) --Astrstr1()のstrchr()版。 ** 言語処理系用 -AStr ''AskpSpc''(ACStr s) --スペース類を読み飛ばす。 -AStr ''AskpLiteral''(ACStr s, char t) --文字tが現れるまで読み飛ばす。返すポインタはtが見つかった場所を返す。バックスラッシュで文字tをエスケープしている場合は、止まらずに進む。 -AStr ''AskpParenthesis''(ACStr s, char p0, char p1, Ai flg) --sの位置にあるカッコに対応する閉じカッコを探す。p0に'('をp1に')'を指定する。別の組み合わせでもよい。 --返されるポインタはp1の次を指している。対応するカッコを見つける前にsが尽きた場合はNULLを返す。 --flg:1=処理中に文字列リテラルを見つけた場合、AskpLiteral()を使って読み飛ばす(だから文字列中にp0やp1が含まれていてもカウントしない)。 --flg:2=flg:1の処理でエラーがあった場合、これを無視して続行する(引用符を閉じないで改行しているとか)。 --flg:4=「//」のコメントを見つけた場合は、行末まで読み飛ばす。 -AStr ''AskpParenthesis1''(ACStr s, Ai flg) --sの位置にあるカッコの種類に応じて、p0とp1を自動設定したのちにAskpParenthesis()する。 -Ai ''AnamLen''(ACStr s) --sで始まる文字列が変数名か関数名であった場合、その名前の長さを返す。 -Ai ''AnamLen1''(ACStr s, ACStr t) --AnamLen()に、さらに変数名・関数名に許す文字を追加した状態で、名前の長さを返す。追加する文字tは "$@"のように指定する。 -int ''AnamCmp''(ACStr s, ACStr t, Ai l) --AnamLen()でlを獲得したら、AnamCmp("abc", s, l)で名前がabcと完全一致するか比較できる。 --strncmp(s, "abc", 3)==0で比較すると、s上でabcdってなっていても一致してしまう。だからAnamCmp()が必要になる。 ** バイナリ系 -Av ''AputBytes''(Auc *p, Ai i, Ai s0, Ai s1, Ai s2, Ai s3) --iの値をp[0], p[1], p[2], p[3]に書き込む。その際にs0~s3だけ右シフトしてAND 0xffする。 --対応するsの値が-1の場合は、書き込みを行わない。 -Ai ''AgetBytes''(Auc *p, Ai s0, Ai s1, Ai s2, Ai s3) --p[0]~p[3]を読み込んで、s0~s3の値だけシフトしてORして、AputBytesのiを復元して返す。 --対応するsの値が-1の場合は、読み込みを行わない。 ** そのほか -FILE *''AOpen''(ACStr path, ACStr mod) --fopen()の代わり。fopenがNULLを返した場合は適当なエラーメッセージをstderrに出力してexit(1)してくれる。