Brainfuckのインタープリタ作ってみた

今日受けていた講義があまりにも暇理解できなかったので,Brainfuckについて調べていた.

Brainfuck自体は大分前から知ってたが,ちゃんと仕様(?)とか確認したりしたことなかったので…

仕様も何も wikipedia に載っていることが恐らく全てで,

処理系は次の要素から成る: Brainfuckプログラム、インストラクションポインタ(プログラム中のある文字を指す)、少なくとも30000個の要素を持つバイトの配列(各要素はゼロで初期化される)、データポインタ(前述の配列のどれかの要素を指す。最も左の要素を指すよう初期化される)、入力と出力の2つのバイトストリーム。

Brainfuckプログラムは、以下の8個の実行可能な命令から成る(他の文字は無視され、読み飛ばされる)。

  1. > ポインタをインクリメントする。ポインタを ptr とすると、C言語の「ptr++;」に相当する。
  2. < ポインタをデクリメントする。C言語の「ptr--;」に相当。
  3. + ポインタが指す値をインクリメントする。C言語の「(*ptr)++;」に相当。
  4. - ポインタが指す値をデクリメントする。C言語の「(*ptr)--;」に相当。
  5. . ポインタが指す値を出力に書き出す。C言語の「putchar(*ptr);」に相当。
  6. , 入力から1バイト読み込んで、ポインタが指す先に代入する。C言語の「*ptr=getchar();」に相当。
  7. [ ポインタが指す値が 0 なら、対応する ] の直後にジャンプする。C言語の「while(*ptr){」に相当。
  8. ] ポインタが指す値が 0 でないなら、対応する [ にジャンプする。C言語の「}」に相当。

とある.

とりあえずインタープリタC言語で実装してみようと思って書いたら,上手く動かなかったので(] のときの条件分岐を間違えていた)下記の記事をちら見して書いてみた.

masahirosuzuka.hatenablog.com

#include <stdio.h>
#include <stdlib.h>

typedef char byte;

int main(int argc, char** argv)
{
  char code[60000] = { 0 };
  int code_size = 0;
      
  {
    FILE *fp = fopen(argv[1], "r");
    if(fp == NULL) {
      printf("cannot read file: %s\n", argv[1]);
      exit(1);
    }  
  
    char c;
    while((c = fgetc(fp)) != EOF) {
      code[code_size++] = c;
    }
    fclose(fp);
  }
  
  byte buff[30000] = { 0 };
  int dptr = 0;
  int loop = 0;
  
  for(int iptr = 0; iptr < code_size; iptr++) {
    switch(code[iptr]) {
    case '>':
      dptr++;
      break;
    case '<':
      dptr--;
      break;
    case '+':
      buff[dptr]++;
      break;
    case '-':
      buff[dptr]--;
      break;
    case '.':
      putchar(buff[dptr]);
      break;
    case ',':
      buff[dptr] = getchar();
      break;
    case '[':
      if(buff[dptr] == 0) {
        while(code[++iptr] != ']' || loop > 0) {
          if(code[iptr] == '[') {
            loop++;
          } else if(code[iptr] == ']') {
            loop--;
          }
        }
      }
      break;
    case ']':
      if(buff[dptr] != 0) {
        while(code[--iptr] != '[' || loop > 0) {
          if(code[iptr] == ']') {
            loop++;
          } else if(code[iptr] == '[') {
            loop--;
          }
        }
      }
      break;
    }
  }
}

あってるかどうかわからないが,一応 Hello, world! と fib と hanoi は正しく動くみたい.
ただ,コードサイズを固定にしてるのは宜しくなので,暇なときに書き変えよ…
一応,動作確認に使った hanoi は読み込めるサイズにはなっている.

Brainfuckインタープリタ書くだけなら凄い簡単なんだなぁ…
これを少し変更すると,一昔前に流行ってた Brainfuck 系言語のインタープリタになるわけだ.
ただ,インタープリタは作れても,まだ Brainfuck のコードを読むことができないよ…
動作はわかるけど,これは脳内でちゃんと追えるのだろうか…

Brainfuckインタープリタを実装してみたけど,手続き型プログラミング言語で書いても正直何の面白いところもないし,Haskell とかでどう工夫して書くかを考える方が楽しそう.

itchyny.hatenablog.com

qiita.com

暇潰しには丁度良かったかな?