0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マクロプロセッサm4を利用した汎用bfトランスパイラlgbtの応用

0
Last updated at Posted at 2026-03-13

マクロプロセッサの研究

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 として使います。

必要なファイルはここにあります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?