日記

日本語の勉強のためのブログ

C++でBrainfuckのインタプリタを作った

プログラミングの練習として。
単に動作するものを目標として作成したため、動作速度などは無視している。

追記)後継言語であるBrainCrushのインタプリタも作った。 everykalax.hateblo.jp

コード

標準入力からgetline()Brainfuckコードを受け取り、そのコードをfor文で1文字ずつswitch文を使いながら実行しているだけである。
また、[ ]の対応関係についてはスタックを使うことで解決した。 [の実行時に現在位置をスタックfrontParenthesisに入れ、]の実行時にスタックから取り出した位置に移動するという方法で実装した。

(2021/05/30 追記) ソースコードは以下に移動した。 github.com

動作方法

適当なファイルに以上のコードを張り付けて保存し、ターミナルから

$ g++ brainfuck.cpp
$ ./a.out

で実行。
入力受付け状態になるので、ここでbrainfuckコードを入力すると実行される。仕様上、一行しか受け付けないので注意(これについては後々修正するかも)。

製作過程

ここから先は個人的な話になるので、見る必要はありません

wikiで言語仕様を見て、大部分はすぐに書けた。

ja.wikipedia.org

スタックについてはわからなかったので、以下のサイトを参考にした。

qiita.com

最初に書いたコードは以下であった。

#define MEMORY_NUMBER 20
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

int main() {
  int ptr = 0;
  vector<char> memory(20, 0);
  
  string prog;
  getline(cin, prog);

  char c;
  int endParenthesis = -1;
  stack<int> frontParenthesis;
  
  for (int i = 0; i < prog.length(); i++) {
    c = prog.at(i);

    switch (c) {
      case '>':
        ptr++;
        break;
      case '<':
        ptr--;
        break;
      case '+':
        memory.at(ptr)++;
        break;
      case '-':
        memory.at(ptr)--;
        break;
      case '.':
        cout << memory.at(ptr);
        break;
      case ',':
        cin >> memory.at(ptr);
        break;
      case '[':
        if (memory.at(ptr) == 0) i = endParenthesis;
        frontParenthesis.push(i);
        break;
      case ']':
        endParenthesis = i;
        if (memory.at(ptr) != 0) i = frontParenthesis.top();
        frontParenthesis.pop();
        break;
      default:
        break;
    }
  }
 
  cout << endl << "[END OF RUNNING]" << endl;
}

試しにHello worldを出力するコードの冒頭部分(Hを出力するところまで)を実行させてみたら、Segmentation faultエラーが発生してしまった。

$ ./a.out
>+++++++++[<++++++++>-]<.
Segmentation fault (core dumped)
$ 

gdbデバッガを用いてステップ実行させながら動作を確認すると、2回目のループでエラーが起こることが分かった。frontParenthesisというスタックが空なのに、中身を確認しようとしたために起きたエラーであった。
(gdbについてはここを参照した)

Program received signal SIGSEGV, Segmentation fault.
0x0000000008002730 in main () at brainfuck.cpp:45
45              if (memory.at(ptr) != 0) i = frontParenthesis.top();

そのため該当箇所をif (memory.at(ptr) == 0) i = endParenthesis - 1;if (memory.at(ptr) != 0) i = frontParenthesis.top() - 1;のように修正した。 つまり]があったら対応する[の1つ前に飛ばすことで、[を実行させるように変更した。 そうしたらしっかり動いてくれた。

$ ./a.out
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.
Hello World!

[END OF RUNNING]
$ 

Hello worldのコードについてはこちらから拝借した。

www.kmonos.net