マクロプロセッサの研究
lgbt (m4 版)
Literal General Brainfuck Transpiler — m4 移植版
オリジナル:
概要
Brainfuck ソースをあらゆるターゲット言語に変換するトランスパイラです。
変換先の言語は m4 マップファイル (.m4) で指定します。
結果は標準出力に出力されるのでリダイレクトして使用してください。
lgbt map.c.m4 header.c hello.bf tailor.c > hello.c
必要なもの
- GNU m4
- bash
- awk (gawk / mawk / nawk いずれも可)
ファイル構成
lgbt シェルラッパー(エントリポイント)
lgbt_core.m4 コア m4 ライブラリ(インフラマクロ定義)
map.c.m4 C 用マップ
map.py.m4 Python 用マップ
map.ruby.m4 Ruby 用マップ
map.ook.m4 Ook! 用マップ
map.amd64.m4 x86_64 Linux AT&T アセンブリ用マップ(ラベルモード)
header.c / tailor.c C ヘッダ・テイラー
header.py Python ヘッダ
header.rb Ruby ヘッダ
header.amd64.s/tailor.amd64.s アセンブリ ヘッダ・テイラー
使い方
引数の数と解釈は Python 版と同じです。
引数数 解釈
1 <file.bf>
2 <mapfile.m4> <file.bf>
3 <mapfile.m4> <headerfile> <file.bf>
4 <mapfile.m4> <headerfile> <file.bf> <tailfile>
ラベルは常に生成され、マップファイル側が利用するかを決めます。
使用例
C
lgbt map.c.m4 header.c hello.bf tailor.c > hello.c
cc hello.c && ./a.out
Python
lgbt map.py.m4 header.py hello.bf > hello.py
python3 hello.py
Ruby
lgbt map.ruby.m4 header.rb hello.bf > hello.rb
ruby hello.rb
Ook!
lgbt map.ook.m4 hello.bf > hello.ook
x86_64 アセンブリ
lgbt map.amd64.m4 header.amd64.s hello.bf tailor.amd64.s > hello.s
as hello.s -o hello.o
ld hello.o -o hello
./hello
独自マップファイルの作り方
マップファイルは BF の各命令 (BF_GT, BF_LT, BF_PLUS, BF_MINUS,
BF_DOT, BF_COMMA, BF_OPEN, BF_CLOSE) を m4 マクロとして再定義します。
BF_OPEN(N) と BF_CLOSE(N) は常に対応するラベル番号 N を第 1 引数として受け取ります。
コア API(lgbt_core.m4 が提供):
マクロ 説明
`_EMIT(text)` 現在のインデント+テキスト+改行を出力
`_INC_INDENT()` インデントレベルを1増やす
`_DEC_INDENT()` インデントレベルを1減らす(0未満にはならない)
`S()` リテラルの`$`を展開(アセンブリ用\)
マップ例 (C)
divert(-1)dnl
define(`BF_GT', `_EMIT(`ptr++;')')
define(`BF_LT', `_EMIT(`ptr--;')')
define(`BF_PLUS', `_EMIT(`(*ptr)++;')')
define(`BF_MINUS', `_EMIT(`(*ptr)--;')')
define(`BF_DOT', `_EMIT(`putchar(*ptr);')')
define(`BF_COMMA', `_EMIT(`*ptr=getchar();')')
define(`BF_OPEN', `_EMIT(`while(*ptr){')_INC_INDENT()')
define(`BF_CLOSE', `_DEC_INDENT()_EMIT(`}')')
divert(0)dnl
マップ例(アセンブリ)
$1 でラベル番号を参照します。S() でリテラル $ を生成します。
divert(-1)dnl
define(`BF_OPEN', `_EMIT(`LB$1:')_EMIT(`cmpb S()0,(%rsi)')_EMIT(`jz LE$1')')
define(`BF_CLOSE', `_EMIT(`jmp LB$1')_EMIT(`LE$1:')')
divert(0)dnl
Python 版との相違点
項目 Python 版 m4 版
マップファイル形式 JSON m4 マクロファイル (`.m4`)
ラベル番号生成 Python の LoopLabelGenerator awk のカウンタ
インデント `[` / `]` のロジック マップファイルが `_INC_INDENT`/`_DEC_INDENT` を呼ぶかで決定
`$`文字 JSON にそのまま記述 `S()`マクロ(m4の`$N`参照と衝突するため)
S() について
m4 はマクロ本体内の $1, $2 … を引数に展開するため、
AT&T アセンブリの即値表現 $1, $0 などをそのまま書けません。
S() は $ 1 文字に展開されるため S()1 → $1 として使います。
必要なファイルはここにあります。