class BrainFxck INSTRUCTIONS = ['>','<','+','-','.',',','[',']'] class ProgramError < StandardError; end class InstructionSequence def initialize(src) scanner = Regexp.new(INSTRUCTIONS.map{ |c| Regexp.escape(c) }.join('|')) @seq = src.scan(scanner).flatten @stack = Array.new @pc = 0 end # 次の命令を取り出す def fetch @seq[@pc] end # pcを進める def next_inst @pc += 1 end # 命令ポインタを次の閉じカッコの次に飛ばす def find_next_bracket stride = @seq[@pc..-1].index(']') raise ProgramError, '"["に対応する"]"が見つかりません' if stride.nil? @pc = @pc + stride + 1 end # 命令ポインタを指定した値に変更する def pc=(point) @pc = point end # 命令ポインタをスタックにpush def push @stack.push(@pc) end # 命令ポインタをスタックからpopする def pop raise ProgramError, 'スタックに値が入っていません' if @stack.empty? @stack.pop end # スタック末尾の値を取得 def last raise ProgramError, 'スタックに値が入っていません' if @stack.empty? @stack.last end # 命令ポインタが命令列の終端にあるか def eof? return true if @seq.length <= @pc false end def view p "stack: #{@stack}" p "pc: #{@pc}/#{@seq.length}" end end class ByteSequence def initialize @seq = [0] @stack = Array.new @p = 0 end # ポインタをインクリメントする def right @p += 1 end # ポインタをデクリメントする def left raise ProgramError, '0未満のポインタを指定することはできません' if @p <= 0 @p -= 1 end # 値をインクリメントする def increment @seq[@p] = 0 if @seq[@p].nil? @seq[@p] += 1 end # 値をデクリメントする def decrement @seq[@p] = 0 if @seq[@p].nil? raise ProgramError, '0未満のデータを持つことはできません' if @seq[@p] == 0 @seq[@p] -= 1 end # 現在位置の値を取得 def val @seq[@p] ||= 0 end # 現在位置の値を上書き def val=(v) @seq[@p] = v end # 現在位置の値を文字として取得 def val_by_char val.chr('UTF-8') end def view p "pointer: #{@p}" p "bytes: #{@seq}" end end def initialize(src) @inst_seq = InstructionSequence.new(src) @byte_seq = ByteSequence.new @output = '' end # TODO case文排除したい # 命令オブジェクトを定義し、バイト列とスタックとポインタをいちいち受け取って返す形にする def run loop do inst = @inst_seq.fetch # p "inst: #{inst}" # for debug case inst when '>' @byte_seq.right when '<' @byte_seq.left when '+' @byte_seq.increment when '-' @byte_seq.decrement when '.' @output += @byte_seq.val_by_char when ',' # TODO when '[' left_bracket when ']' right_bracket end # # for debug # @inst_seq.view # @byte_seq.view # print "\n" @inst_seq.next_inst break if @inst_seq.eof? end print @output end private def left_bracket if @byte_seq.val == 0 @inst_seq.find_next_bracket else @inst_seq.push end end def right_bracket if @byte_seq.val == 0 @inst_seq.pop return end @inst_seq.pc = @inst_seq.last end end begin BrainFxck.new($stdin.read).run rescue BrainFxck::ProgramError => e puts e.message puts "プログラムの実行に失敗しました" end