BrainF*ck実装してみた
先週「Rubyで作る奇妙なプログラミング言語」をかってきた。
Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~
- 作者: 原悠
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2008/12/20
- メディア: 単行本(ソフトカバー)
- 購入: 8人 クリック: 148回
- この商品を含むブログ (69件) を見る
方針として「本に載っているソースは見ない」「感性の赴くままに適当に作る」
1つめの「HQ9+」は簡単なので省略
というわけで見るも無惨なソースをさらしてみる
主に3つのソースから構成
- brainfuckproc.rb
- 実際の処理を行うBrainFuckProcクラス
- brainfuck.rb
- ソースを読み込んでBrainFuckProcインスタンスを呼び出す人 ジャンプ系の命令はこいつが担当
- main.rb
- 処理系のメイン
- dprint.rb
- デバッグ用のdprintメソッドだけがあるファイル(省略)
brainfuckproc.rb
require "../lib/dprint.rb" class BrainFuckProc def initialize @tape = Array.new @current_position = 0 end def inc_pointer dprint "up #{@current_position}->#{@current_position + 1}" @current_position += 1 end def dec_pointer dprint "down #{@current_position}->#{@current_position -1}" if @current_position == 0 then puts "ERROR - pointer lower limit" exit end @current_position -= 1 end def inc_current_value if @tape[@current_position] == nil then @tape[@current_position] = 0 end @tape[@current_position] += 1 dprint @tape end def dec_current_value if @tape[@current_position] == nil || @tape[@current_position] == 0 then puts "ERROR - value at the pointer is 0" exit end @tape[@current_position] -= 1 dprint @tape end def print_current_value_char print @tape[@current_position].chr end def get_char_to_current_pos data = gets @tape[@current_position] = data[0] end def is_zero? if @tape[@current_position] == 0 || @tape[@current_position] == nil then return true else return false end end def jump_forward? jump? end def jump_backward? jump? end def print_all_tape data = "" @tape.each do |val| if val then data.concat(val.chr) end end p "ALLDATA:#{data}" end end
brainfuck.rb
require "brainfuckproc" require "../lib/dprint.rb" class BrainFuck def initialize(src_name) @src_name = src_name @bfpProc = BrainFuckProc.new() end def run File.open(@src_name) do |file| run_bfp(file) end end def run_bfp io while ch = io.read(1) do case ch when ">": @bfpProc.inc_pointer when "<": @bfpProc.dec_pointer when "+": @bfpProc.inc_current_value when "-": @bfpProc.dec_current_value when ".": @bfpProc.print_current_value_char when ",": @bfpProc.get_char_to_current_pos when "[": jump_forward(io) when "]": jump_backward(io) when "*": @bfpProc.print_all_tape end end end def jump_forward(io) dprint "jump forward:" if !@bfpProc.is_zero? then dprint "no jump\n" return end dprint "jump\n" stack_count = 0; while true do inner_ch = io.read(1) if inner_ch == "" then # no matched clauses puts "ERROR - no matched clauses" exit end if inner_ch == "[" then stack_count += 1 end if inner_ch == "]" then if stack_count > 0 then stack_count -= 1 next else # stack_count == 0 -> matched found io.pos += 1 break # ends here end end end end def jump_backward(io) dprint "jump backward:" if @bfpProc.is_zero? then dprint "no jump\n" return end dprint "jump\n" stack_count = 0; if io.pos < 2 then puts "ERROR - guess only ] appears first?" exit end while io.pos > -1 do io.pos -= 2 inner_ch = io.read(1) if inner_ch == "" then # no more backwards puts "ERROR - no match clauses" exit end if inner_ch == "]" then stack_count += 1 end if inner_ch == "[" then if stack_count > 0 then stack_count -= 1 next else##matching #io.pos += 1 break end end end end end
main.rb
require "brainfuck" bfp = BrainFuck.new(ARGV.shift) bfp.run
ソース中にもあるように"*"という第9の命令が追加されています。
これはデバッグ&趣味の用途としてテープにどんな値が書き込まれているかを表示するために付け足した。
Hello, World on BrainF*ck
処理系(のようなもの?)は書いたけど、BrainF*ckはかけないので、WikipediaからBrainF*ckのソースコートを拝借してきた
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-. ------------.<++++++++.--------.+++.------.--------.>+.
素で読めたら神になれる。
このソースの末尾に追加機能である"*"を付加して実行
$ ruby main.rb helworld2.bfk Hello, world!"ALLDATA:\000Hd!"
結果としてこんな出力が得られる。
末尾の"ALLDATA:"以降がテープの内容。ASCIIコードに落とすと
[0, 72, 100, 33]
こんな状態で終わる。
なんと4つの領域しか使っていないとか。こんなの書けない