プログラム
"\0" code
0 $ ptr
512 @ mem
0 $ index
: execute makeJumptable {
index @g code , 3 = ( [ ) $ inst
inst '>' = ( incp [ )
inst '<' = ( decp [ )
inst '+' = ( incb [ )
inst '-' = ( decb [ )
inst '.' = ( out [ )
inst ',' = ( in [ )
inst '[' = ( if [ )
inst ']' = ( loop [ )
] ] ] ] ] ] ] ]
} ] ;
: next index 1 + $ index ;
: incp ptr 1 + $ ptr next ;
: decp ptr 1 - $ ptr next ;
: incb ptr @g mem 1 + ptr @s mem next ;
: decb ptr @g mem 1 - ptr @s mem next ;
: out ptr @g mem . next ;
: in ? ptr @s mem next ;
: if ptr @g mem ! ( index getJumpIf $ index ) next ;
: loop index getJumpLoop $ index ;
: makeJumptable
256 @ _stack
0 $ _sp
countOpenBracket 2 * @ jmptable
0 $ _jmpindex
0 $ _lpindex
{
_lpindex @g code
, 3 = ( ` [ )
_ '[' = ( _lpindex _sp 1 + $ _sp _sp @s _stack )
_ ']' = ( _sp _sp @g _stack _sp 1 - $ _sp
_jmpindex @s jmptable
_lpindex _jmpindex 1 + @s jmptable
_jmpindex 2 + $ _jmpindex
)
_lpindex 1 + $ _lpindex
}
] ;
: countOpenBracket 0 $ _index 0 $ _count {
_index @g code , 3 = ( [ )
'[' = ( _count 1 + $ _count )
_index 1 + $ _index
} ] _count ;
: getJumpIf $ ifindex 0 $ _j {
_j @g jmptable ifindex = (
_j 1 + @g jmptable [
) _j 2 + $ _j
} ] ;
: getJumpLoop $ lpindex 1 $ _j {
_j @g jmptable lpindex = (
_j 1 - @g jmptable [
) _j 2 + $ _j
} ] ;
実行例
上記のブログラムをbrainf.nzlという名前を付けて保存、以下のプログラムを記述しhellobf.nzと名前を付け同じディレクトリに入れて実行する。
##> brainf.nzl
"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."
code execute
##>でbrainf.nzlをimportします。
ダブルコーテーションでBrainf*ckソースコードを文字列として埋め込み、codeという変数に格納し、executeサブルーチンを呼び出すことで実行できます。
> python nouzen.py hellobf.nz
Hello World!
解説
変数とサブルーチンの解説です。
変数
変数として以下のものが実装されています。
| 変数名 | 意味 | 記述 |
|---|---|---|
| code | ソースコードを格納する文字配列。 | "\0" code |
| ptr | メモリ上を指すポインタ。 | 0 $ ptr |
| mem | データを格納するメモリ配列。 | 512 @ mem |
| index | ソースコードの次の命令を指し示すための変数。 | 0 $ index |
これらの値は変更可能なので、例えばメモリをもっと増やしたいと思えば1024 @ memとすれば良いです。executeを実行する前なら、importした先で書き換えることもできます。
サブルーチン
| サブルーチン名 | 意味 |
|---|---|
| incp | ポインタをインクリメントします。 |
| decp | ポインタをディクリメントします。 |
| incb | ポインタが指し示しているメモリの値をインクリメントします。 |
| decb | ポインタが指し示しているメモリの値をディクリメントします。 |
| out | ポインタが指し示しているメモリの値を文字として標準出力します。 |
| in | 標準入力から1文字を取得し、その値をポインタが指し示しているメモリへ格納します。 |
| if | ポインタが指すメモリの値が0だったら対応する]までジャンプします。 |
| loop | 対応する[までジャンプします |
| execute | 実行サブルーチンです。 |
| next |
indexをインクリメントして次の命令の実行をします。 |
| makeJumpTable |
ifやloopで使うジャンプ表を作ります。 |
| countOpenBracket |
[の数を数えてジャンプ表のサイズを確認します。 |
| getJumpIf | ジャンプ表を使ってifのジャンプ先を返します。 |
| getJumpLoop | ジャンプ表を使ってloopのジャンプ先を返します。 |
参考文献
こちらのサイトを参考にさせていただきました。
Brainf*ck
The Brainfuck Programming Language
nouzenについては私の過去の記事をどうぞ。
おわりに
これでついに私のプログラミング言語もひとつの壁を突破したわけですが(本当か?)、前々からやりたいと思っていた先祖返り的なことができて良かったです。
それはそれとして、暗号みたいなソースコードからHello World!と出力されるのは特別なことをしているような気分になるので皆さんもご自身の手でBrainf*ckを実装してみてはいかがでしょうか?
最後まで読んでくださってありがとうございました。 MLFEでnouzenを実装するのはいつになるんだ…