bobuhiro11's diary

rubyでbrainfuckインタプリタ

21 Mar 2013
[brainfuck] [ruby] [インタプリタ]

Brainfuckの言語仕様

これは,wikipediaからの引用です.

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

  • ポインタをインクリメントする。ポインタをptrとすると、C言語の「ptr++;」に相当する。

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

実装

rubyを使いました.”[“と”]”のところはやっぱり大変.

#!/usr/bin/env ruby

class BrainFuck
  def set(program)
    @pc = 0                         # プログラムポインタ   
    @pro = program                  # プログラム
    @mar = 0                        # メモリアドレスレジスタ
    @mem = Array.new(30000,0)       # メモリ(30000Bytes分)
  end

  def step()
    # オペコード読み出し
    op = @pro[@pc]
    @pc+=1

    # 実行
    case op
    when ">"
      @mar+=1
    when "<"
      @mar-=1
    when "+"
      @mem[@mar]+=1
    when "-"
      @mem[@mar]-=1
    when "."
      print @mem[@mar].chr
    when "["
      if @mem[@mar] == 0
        depth = 0  
        (@pc..@pro.length).each do |i|
          # 対応する括弧発見
          if depth == 0 && @pro[i] == "]"
            @pc = i+1
            break
          # 深さを調べる
          elsif @pro[i] == "["
            depth+=1
          elsif @pro[i] == "]"
            depth-=1
          end
        end
      end
    when "]"
      if @mem[@mar]!=0
        depth = 0
        (0..@pc-2).reverse_each do |i|
          # 対応する括弧発見
          if depth == 0 && @pro[i] == "["  
            @pc = i+1
            break
          # 深さを調べる
          elsif @pro[i] == "]"
            depth+=1
          elsif @pro[i] == "["
            depth-=1
          end
        end
      end
    end
  end

  def exec()
    while @pc!=@pro.length 
      step 
    end
    print "\n"
  end
end


brainfuck = BrainFuck.new

while line = gets
  brainfuck.set(line)
  brainfuck.exec
end

テスト

brainfuckプログラミングをしているところです.実行権限をつけてから遊んでください.それと,行ごとに読み込んでいるのでbrainfuck中に改行コードが入るとまずいです.

$ ./brainfuck.rb +++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+. Hello, world! ++++++++++[>++++++++++>+++++++++++>++++++++++++>++++++++++>++++++++++>+++++++++++<<<<<<-]>--.>+.<.>>---.>++++.>+++++.>++++.<<<<. bobuhiro

comments powered by Disqus < rubyまとめ ubuntuのインストールメモ >