def scan(source) source .chars .filter { |c| "+-<>[].,".include?(c) } .map(&:to_sym) end def parse(tokens) position = 0 tree = Array.new parse_node = Proc.new do |token| case token when :+, :-, :>, :<, :'.', :'.', :',' position += 1 token when :"[" position += 1 body = Array.new while position < tokens.size && tokens[position] != :"]" body << parse_node.call(tokens[position]) end raise "Unexpected EOF; expected ]" if position == tokens.size position += 1 if tokens[position] == :"]" body else raise "Unrecognized token #{token}" end end while position < tokens.size tree << parse_node.call(tokens[position]) end tree end def interpret(tree) stack = Array.new(100) { 0 } position = 0 pointer = 0 interpret_node = Proc.new do |node| case node when :+ stack[pointer] += 1 when :- stack[pointer] -= 1 when :> raise "Out of bounds" if pointer >= 30_000 pointer += 1 when :< raise "Below zero" if pointer <= 0 pointer -= 1 when :'.' STDOUT.write stack[pointer].chr when :',' char = STDIN.getc stack[pointer] = char.ord when Array while stack[pointer] > 0 node.each { |subnode| interpret_node.call(subnode) } end else raise "Unexpected node #{node}" end end while position < tree.length interpret_node.call(tree[position]) position += 1 end STDOUT.print("\n") end interpret(parse(scan(ARGV.first))).inspect