Javaなんか目じゃないポータブル言語Brainf*ckを楽しもう!

なんかおもいつきでBrainf*ckインタプリタを作ってみた。過去に何度か作ろうとしたけど、ちゃんと動かなかったりした。んで「ああ俺やっぱりプログラミングとか向いてねーなー」と思ったものですが。今回作ってみたら特につまることもなくスムーズにできた。
俺はK.INABAさんの紹介で知りました。
http://www.kmonos.net/alang/etc/brainfuck.php

動作はこんなかんじ。
hello.bf

>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.

として、このhello.bfを

% ./bf hello.bf

のようにbfに与えて実行してやると、

Hello World!

Hello World!が表示されます。素敵だ!
また、

% ./bf

引数を与えずに実行すると、

+[,.] # ここまでがbfコード
hello! <- 入力行
hello!
こんにちはこんにちは! <- 入力行
こんにちはこんにちは!

標準入力からbfスクリプトを読み込みます。

downchecho.bf

>>++++++++[->++++++++<]>>>>+++++++++[->++++++++++<]>[<<,[->+<<+<<+>>>]<<<[
->>>+<<<]>>>>>[->+>>+<<<]>[<<[->+>>+<<<]>>>[-<<<+>>>]<<[[-]<->]>-]>>[-<<<+
>>>]<<<<<<<[-<+<<+>>>]<[>>[-<+<<+>>>]<<<[->>>+<<<]>>[[-]>-<]<-]<<[->>>+<<<
]>>>>><[[-]>++++++++++++++++++++++++++++++++>[[-]<------------------------
-------->]<<]>>[-]<.>>]
% ./bf downchecho.bf
hogehoge <- 入力行
hogehoge
HoGeHHHOe <- 入力行
hogehhhoe

あーちゃんと動いてるな。

今回bfインタプリタ書いてて、俺のどうしようもなく駄目な所がわかった。うまく書かなきゃきれいに書かなきゃとか、そんなことばかり意識していて、手が動かない。実態はHello Worldぐらいしか書けない奴のくせに。前にbfインタプリタを書いたりしてみた時はまさにドツボにはまって、さっぱり動くものが作れなかった。
変数名をどうするかとかで無駄に悩んだりすること、かなり多いしなあ。

ひげぽんさんはすげーなあと思いました。関数型言語の勉強に「計算機プログラムの構造と解釈(SICP)」を読もうの、1章 - 手続きによる抽象の構築(1-30ページ)にある反復が分からない。という発言を読んで。「うわあこの人あっさりわからないってことを認めちゃったよスゲエ!」、と。

ソース

#include <stdio.h>
#define MAXCODE 10000
#define MAXMEM 10000

int bf(char[], unsigned char[]);
int isbfcode(char);

int main(int argc, char *argv[]){
  int c, i;
  FILE *fp;
  char bfcode[MAXCODE];
  unsigned char mem[MAXMEM];
  if(argc>1){
    if((fp = fopen(argv[argc-1], "r")) == NULL){
      printf("cannot open file\n");
      return 1;
    }
  }
  else
    fp = stdin;

  i = 0;
  while((c=fgetc(fp)) != EOF){
    if(isbfcode(c)){
      bfcode[i] = c;
      ++i;
      if(i>=MAXCODE){
        printf("bfcode too long\n");
        return 1;
      }
    }
  }
  for(i=0;i<MAXMEM;++i) mem[i]=0;
  bfcode[i] = '\0';
  bf(bfcode, mem);
  return 0;
}
int bf(char bfcode[], unsigned char mem[]){
  char c;
  int nest;
  while((c = *bfcode) != '\0'){
    switch(c){
      case '+':
        ++*mem; break;
      case '-':
        --*mem; break;
      case '>':
        ++mem; break;
      case '<':
        --mem; break;
      case '.':
        putchar(*mem); break;
      case ',':
        *mem = getchar(); break;
      case '[':
        if(*mem == 0){
          nest = 1;
          while(nest != 0){
            c = *(++bfcode);
            switch(c){
              case '[':
                ++nest;
                break;
              case ']':
                --nest;
                break;
            }
          }
        }
        break;
      case ']':
        nest = 1;
        while(nest != 0){
          c = *(--bfcode);
          switch(c){
            case '[':
              --nest;
              break;
            case ']':
              ++nest;
              break;
          }
        }
        --bfcode;
        break;
    }
    ++bfcode;
  }
  return 1;
}
int isbfcode(char c){
  switch(c){
    case '+':
    case '-':
    case '>':
    case '<':
    case '[':
    case ']':
    case '.':
    case ',':
      return 1;
      break;
    default:
      return -1;
  }
}

寝呆けた頭で書いたもんで、今読んだら

#define MAXCODE 10000
#define MAXMEM 10000

とか、いったい俺はどんなbrainfuckスクリプトを処理させようとしてるんだか。無えよ。

  for(i=0;i<MAXMEM;++i) mem[i]=0;

とか間抜けだよなあと思ったら、memsetとかいう関数があるようだ。
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/memset.3.html

  memset(mem, 0, sizeof(mem)*sizeof(unsigned char));

かな。ってか配列のサイズをsizeofで取れることを知らなんだ。

test