Last active
October 18, 2018 00:36
-
-
Save expajp/c443f14944d114e94fb5c82c4e4b13fb to your computer and use it in GitHub Desktop.
brainfxckのコンパイラ `$ ruby brain_fxck.rb <source_file`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++ | |
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]> | |
++++++++[<++++>-]<+.[-]++++++++++. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
[
の挙動がおかしい