class Brainfuck
options no_result_var
rule
exp:
| exp ope
ope: '>'
{
@ptr += 1
}
| '<'
{
@ptr -= 1
}
| '+'
{
@ary[@ptr] ||= 0
@ary[@ptr] += 1
}
| '-'
{
@ary[@ptr] ||= 0
@ary[@ptr] -= 1
}
| '.'
{
print (@ary[@ptr] || 0).chr
}
| ','
{
@ary[@ptr] = $stdin.getc XXX
}
| '['
{
if (@ary[@ptr] || 0).zero?
@ss.skip(/[^\]]*\]/)
else
@stack.push('')
end
}
| ']'
{
if (buf = @stack.pop)
@ss = StringScanner.new(buf + ']' + @ss.rest)
end
}
end
---- header
require 'strscan'
require 'readline'
---- inner
attr_reader :ptr
attr_reader :ary
attr_reader :stack
def initialize
@ptr = 0
@ary = []
@stack = []
end
def scan
until @ss.eos?
if tok = @ss.scan(/[><\+\-\.,\[\]]/)
yield tok, tok
@stack.last << tok if @stack.last
elsif tok = @ss.scan(/[^><\+\-\.,\[\]]/)
end
end
yield false, '$'
end
def parse(src)
@ss = StringScanner.new(src)
yyparse self, :scan
end
---- footer
parser = Brainfuck.new
if ARGV.empty?
while buf = Readline.readline('brainfuck> ', true)
parser.parse(buf).inspect
puts <<-EOS
---
ptr: #{parser.ptr}
ary: #{parser.ary.inspect}
stack: #{parser.stack.inspect}
EOS
end
else
ARGV.each do |fn|
parser.parse(open(fn) {|f| f.read }).inspect
end
end