Skip to content

Instantly share code, notes, and snippets.

@expajp
Last active October 18, 2018 00:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save expajp/c443f14944d114e94fb5c82c4e4b13fb to your computer and use it in GitHub Desktop.
Save expajp/c443f14944d114e94fb5c82c4e4b13fb to your computer and use it in GitHub Desktop.
brainfxckのコンパイラ `$ ruby brain_fxck.rb <source_file`
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
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.
@expajp
Copy link
Author

expajp commented Oct 18, 2018

[の挙動がおかしい

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment