brainfuckを吐く⾃作⾔語
bf-reusable
2021/9/23 @rood_ni
bf-reusableとは
• プログラミング⾔語
• brainfuckにコンパイルされる
• 開発途中
https://github.com/roodni/bf-reusable
brainfuckの簡単な説明
brainfuck C⾔語
+ a[p]++;
- a[p]--;
> p++;
< p--;
[ while (a[p]) {
] }
. putchar(a[p]);
, a[p] = getchar();
• 8つの命令がある
• 配列とポインタを操作する
char a[ARRAY_SIZE];
int p = 0;
配列の要素のことを
セルと呼びます
brainfuckの楽しさ
書いたコード⽚を再利⽤することで
作れるものを増やしていく
⾃作brainfuckライブラリの図→
brainfuckの楽しさ
⼀部のオンラインジャッジで動く
→ 問題が⼤量に⽤意されている
brainfuckのつらさ
例題
⼊⼒: 10進数の数値(終端は改⾏ ¥n)
出⼒: 対応するASCIIコードの⽂字
⼊出⼒例
⼊⼒: 48¥n
出⼒: 0
brainfuckのつらさ
a[0] a[1]
結果の数値が⼊る ⼀時的な計算領域
brainfuckのつらさ
brainfuckプログラミングの⼯程:
• セル配置(どのセルを何の⽬的で使うか)を決める
↑全部決めるのは難しい
実装途中で変えたくなる
• 実装する
↑セル配置を決めないと実装できない
セル配置が変わると修正が⼤変
a[0] a[1]
結果の数値が⼊る ⼀時的な計算領域
brainfuckのつらさ
より複雑なプログラムの例 →
ポインタ操作 < > がつらい
セル配置を変えるのは⼤変
作り直す⽅が早い
brainfuckのつらさ
←この状況では使えない
ポインタ操作 < > のせいで
コードの再利⽤がやりにくい
a[0] a[1] a[2] a[3], a[4], ...
空 使⽤済 空 使⽤済
bf-reusableのコンセプト
brainfuckで遊びたいが、つらい思いはしたくない
brainfuckのつらさを改善して楽しさを残す
• ポインタ操作 < > を取り払う
• コードを再利⽤しやすくする
bf-reusableのコンセプト
たとえば除算なら
❌除算のbrainfuckのコードをbf-reusableの処理系に組み込む
⭕bf-reusableで除算を実装して再利⽤する
bf-reusableの成果
bf-reusableでbrainfuckインタプリタを作ってコンパイル
→⾃分⾃⾝を実⾏できるbrainfuckインタプリタが出⼒される
最適化が未実装でコードが冗⻑です
bf-reusableの機能(1) セル確保とセレクタ
セルを確保して名前をつける
a[0] a[1]
名前: y 名前: x
セルの確保例
bf-reusableの機能(1) セル確保とセレクタ
• セルを指す式(と式の値)のことを セレクタ と呼ぶ
• + - . , は対象セルのセレクタを取る
ポインタ操作 < > を書かなくて良い
コンパイル
⼊出⼒例
⼊⼒: BB
出⼒: CA
bf-reusableの機能(1) セル確保とセレクタ
+ - は第2引数に増減数を取る
出⼒: AFf
+++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++.
+++++.
++++++++++++++++++++++++++++++++.
算術式 ('a' - 'A') = 32
bf-reusableの機能(1) セル確保とセレクタ
ループ命令 [ 〜 ] をどう扱うか
while(a[p]) { 〜 }
brainfuckではwhileの条件に⽤いるセルが変わりうる
ポインタがずれないループ
反復のたびにポインタが右に1つずれるループ
bf-reusableの機能(1) セル確保とセレクタ
bf-reusableの制御⽂その1 ! (while)⽂
! x [ 〜 ]
セレクタ x の指すセルの中⾝が0でない間
[ 〜 ] を繰り返す
条件に⽤いるセルは x に限定
bf-reusableの機能(1) セル確保とセレクタ
bf-reusableの制御⽂その1 ! (while)⽂
hogeの2倍をfugaに加算する
hogeはゼロになる
ポインタ位置を
hogeに合わせている
bf-reusableの機能(1) セル確保とセレクタ
bf-reusableの制御⽂その2 ? (if)⽂
? x [ A ] [ B ]
x のセルの中⾝が0でなければ A、0であれば B
を1度だけ実⾏
bf-reusableの機能(1) セル確保とセレクタ
bf-reusableの制御⽂その2 ? (if)⽂
Q. whileの組み合わせでifを作れるのに
ifを組み込んだのはなぜですか?
A. 「⾮破壊if」を⽤いたコードを⽣成するためです
• セルの中⾝を変更せずに分岐するbrainfuckのテクニック(詳細は割愛)
• whileの組み合わせで作るifより⾼速
• 条件に⽤いたセルに隣接するセル2つが追加で確保されることに注意
bf-reusableの機能(1) セル確保とセレクタ
10進数⼊⼒の例題を今までの機能で実装する
↓⽐較
bf-reusableの機能(2) コードの再利⽤
⽂
• brainfuckのコードに変換
式
• ⽂のパラメータとして記述
• コンパイル時 に評価され値に
⽂
式
bf-reusableの機能(2) コードの再利⽤
値 の種類
• 整数
• 真偽
• 関数
• ペア
• リスト
• セレクタ
• ブロック⽂
整数になる
セレクタになる
bf-reusableの機能(2) コードの再利⽤
ブロック⽂式
• ⽂の列を [ ] で囲うと式になる
• 展開⽂ (*) で⽂中に展開できる
ブロック⽂式
展開⽂
bf-reusableの機能(2) コードの再利⽤
ブロック⽂は静的スコープを持つ
bf-reusableの機能(2) コードの再利⽤
ブロック⽂と関数と組み合わせて使う
→再利⽤可能なプログラムに
例: gen_del
セルの中⾝をゼロにするブロック⽂
を返す関数
bf-reusableの機能(2) コードの再利⽤
複雑な例:
10進数⼊⼒のプログラムを再利⽤したい
⼊⼒の結果を格納するセル
計算のため⼀時的に使うセル
bf-reusableの機能(2) コードの再利⽤
複雑な例:
10進数⼊⼒のプログラムを再利⽤したい
⼀時的に使うセルを渡すのが⾯倒
gen_input の引数は dest だけにしたい
計算のため⼀時的に使うセル
bf-reusableの機能(2) コードの再利⽤
セル確保⽂ $alloc (別名 $var)
• ⽂中でセルを確保する
• セレクタは in の内側で有効
関数呼び出しが簡単になった!
bf-reusableの機能(2) コードの再利⽤
セル確保⽂ $alloc
• 確保したセルの中⾝はゼロ
bf-reusableの機能(2) コードの再利⽤
コード⽣成例1: ⽂字列出⼒(gen_puts)
• 任意の⽂字列を出⼒するブロック⽂を返す
• (⽂字列リテラルは整数のリストの糖⾐構⽂)
+++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++.-
--.+++++++..+++.-------------------------------------
------------------------------------------
.++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++.--------.+++.----
--.--------.-----------------------------------------
-------------------------------------------------.[-]
bf-reusableの機能(2) コードの再利⽤
コード⽣成例1: ⽂字列出⼒(gen_puts)
まず不動点コンビネータを定義
静的型検査は未実装です
bf-reusableの機能(2) コードの再利⽤
コード⽣成例1: ⽂字列出⼒(gen_puts)
セルを1つ確保
再帰関数でコード⽣成
※空のブロック⽂(リストではない)
bf-reusableの機能(2) コードの再利⽤
コード⽣成例2: セルの中⾝で分岐する(gen_switch)
bf-reusableの機能(2) コードの再利⽤
コード⽣成例2: セルの中⾝で分岐する(gen_switch)
実装: がんばる
• brainfuckを書くよりまし
• ⼀度作れば後で使える
bf-reusableの機能(2) コードの再利⽤
マクロ vs サブルーチン
「ブロック⽂を返す関数」はマクロのようなもの
brainfuckはプログラムカウンタを⾃由に動かせない
→ サブルーチン(callとreturn)の実装が難しい
bf-reusableの機能(2) コードの再利⽤
マクロ vs サブルーチン
参考 https://github.com/n4o847/nazuki-hs
• brainfuckにコンパイルされる⾔語
• VMを構築してbrainfuckでエミュレート
• VMのジャンプ命令でサブルーチンを実現
bf-reusableの機能(3) 配列
配列
• 連続した領域を確保する
0 1 2 3 4 5
a:(0) a:(1) a:(2)
x y x y x y
配列の確保例
bf-reusableの機能(3) 配列
配列の要素へのセレクタ
その1. 添字を直接指定
0 1 2 3 4 5
a:(0) a:(1) a:(2)
x y x y x y
'A' 0 'B' 0 0 'Y'
bf-reusableの機能(3) 配列
配列の要素へのセレクタ
その2. インデックスを経由
bf-reusableの機能(3) 配列
インデックス
• 配列の添字を記憶するセルの並び
• 経由して配列要素にアクセスできる
0 1 2 3 4 5 6
ヘッダ b:(0) b:(1) b:(2)
i x i x i x i
bf-reusableの機能(3) 配列
i = 0
i = 1
i = 2
ヘッダ b:(0) b:(1) b:(2)
0 x 0 x 0 x 0
ヘッダ b:(0) b:(1) b:(2)
0 x 1 x 0 x 0
ヘッダ b:(0) b:(1) b:(2)
0 x 1 x 1 x 0
ポインタをヘッダに合わせて
>>[>>] で配列要素に移動 <<[<<] で戻る
bf-reusableの機能(3) 配列
シフト⽂ (< >): インデックスを左右に動かす
右に1つ移動
0 1 2 3 4 5 6
ヘッダ b:(0) b:(1) b:(2)
i x i x i x i
0 1 1 2 0 3 0
bf-reusableの機能(3) 配列
インデックスと!⽂ (while)
• インデックスは ! の条件に指定できる
• インデックスが0になるまで繰り返す
bf-reusableの機能(3) 配列
無限配列: 要素数に制限のない配列
bf-reusableの機能(3) 配列
⭕ 無限配列は複数確保できる
code tape code:(0) tape:(0) code:(1) tape:(1) …
i i inst depth i v i … …
確保例
bf-reusableの機能(3) 配列
❌ 無限配列は配列の要素にできない
bf-reusableの機能(3) 配列
⽂ $dive
$dive (インデックスのセレクタ) (ブロック⽂)
$dive内部の$allocは
• インデックスの所属する配列下
• インデックスの指す添字
にセルを確保する
↑brainfuckインタプリタの⼀部
bf-reusableの機能(3) 配列
⽂ $dive
例: depth_cur
• インデックス code@i に付属
• code@i をシフトすると
同時にdepth_curも移動する
↑brainfuckインタプリタの⼀部
bf-reusableの機能(3) 配列
⽂ $dive
• $alloc を使うコード⽣成関数(例: gen_input)に
インデックスを含むセレクタを与えるとき便利
• 配列と配列外の往復が減る
• コード⻑の短縮
• 実⾏速度の向上
bf-reusableの機能(3) 配列
例題1: ⽂字列反転
⽂字列を1⾏受け取って
反転して出⼒する
⼊出⼒例
⼊⼒: rats¥n
出⼒: star
bf-reusableの機能(3) 配列
例題1: ⽂字列反転
⽂字列を1⾏受け取って
反転して出⼒する
無限配列 buf に⼊⼒を蓄える
bf-reusableの機能(3) 配列
例題2: 10進数出⼒
⼊⼒: 1⽂字
出⼒: 対応するASCIIコード(10進数)
⼊出⼒例
⼊⼒: A
出⼒: 65
bf-reusableの機能(3) 配列
例題2: 10進数出⼒ …… 多倍⻑10進カウンタを作る
• リトルエンディアン
• 10進数を1桁ずつ持つ
n 0 1 2 3 4 5 6 7 8 9 10
意味 未使⽤ 9 8 7 6 5 4 3 2 1 0
bf-reusableの機能(3) 配列
例題2: 10進数出⼒ …… 多倍⻑10進カウンタを作る
インクリメント→ 出⼒→
bf-reusableの機能(3) 配列
例題2: 10進数出⼒
⼊⼒
xの中⾝の数だけインクリメント
出⼒
bf-reusableの機能(3) 配列
例題3: FizzBuzz
⼊出⼒例
⼊⼒: 15¥n
出⼒: 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
bf-reusableの機能(3) 配列
例題3: FizzBuzz
• gen_input
• 10進カウンタ
• gen_puts
を再利⽤した
bf-reusableの機能(3) 配列
例題3: FizzBuzz
出⼒されたbrainfuckのコード
bf-reusableのコンパイルの流れ
bf-reusable
中間⾔語 bf-named
brainfuck
• 式の評価
• ブロック⽂の展開
• $alloc の削除
• セル配置の決定
• (ここに最適化を実装したい)
• コード⽣成
今後の課題
• 別ファイルのimport機能とライブラリ作成
• パターンマッチの実装 (今はリストしかない)
• データ構造の名前定義
• 静的型検査
• ⽣成するコードの最適化
参考資料
• Aminet - dev/lang/brainfuck-2.lha
https://aminet.net/package/dev/lang/brainfuck-2
• Brainfuck にコンパイルされる⾔語 Nazuki の 2020 年の進捗状況
https://n4o847.hatenablog.com/entry/2020/12/24/182051
• 実⽤Brainf*ckプログラミング
https://www.slideshare.net/KMC_JP/brainfck
• hsjoihs/camphorscript: A C-like language designed to help write brainf*ck
https://github.com/hsjoihs/camphorscript

brainfuckを吐く自作言語bf-reusable