Created
July 21, 2015 01:32
-
-
Save 5thWall/f6327b2a14d63d4f29a7 to your computer and use it in GitHub Desktop.
Implementation of a Befunge interpreter
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
require 'tape' | |
class BefungeMachine | |
attr_reader :tape, :stack, :output | |
def initialize(code, debug = false) | |
@tape = Tape.new(code) | |
@stack = [] | |
@output = [] | |
@debug = debug | |
end | |
def run | |
cmd = tape.cmd | |
loop do | |
if @debug | |
puts "Command: (#{tape.x}, #{tape.y}) '#{cmd}'" | |
end | |
return output.join('') if cmd == :EOP | |
send(cmd) | |
cmd = tape.next_cmd | |
end | |
end | |
private | |
def noop; end | |
[:up, :down, :left, :right].each do |dir| | |
define_method("move_#{dir}") do | |
tape.dir = dir | |
end | |
end | |
def move_random | |
tape.dir = Tape::DIRS.sample | |
end | |
(0..9).each do |n| | |
define_method("push_#{n}") do | |
stack.push(n) | |
end | |
end | |
def a_b | |
[stack.pop, stack.pop] | |
end | |
def addition | |
a, b = a_b | |
stack.push(a+b) | |
end | |
def subtraction | |
a, b = a_b | |
stack.push(b-a) | |
end | |
def multiplication | |
a, b = a_b | |
stack.push(a*b) | |
end | |
def division | |
a, b = a_b | |
stack.push(a.zero? ? 0 : b / a) | |
end | |
def modulo | |
a, b = a_b | |
stack.push(a.zero? ? 0 : b % a) | |
end | |
def l_not | |
stack.push(stack.pop.zero? ? 1 : 0) | |
end | |
def greater_than | |
a, b = a_b | |
stack.push(b > a ? 1 : 0) | |
end | |
def pop_lr | |
stack.pop.zero? ? move_right : move_left | |
end | |
def pop_ud | |
stack.pop.zero? ? move_down : move_up | |
end | |
def duplicate | |
top = stack.last | |
top.nil? ? stack.push(0) : stack.push(top) | |
end | |
def swap | |
a, b = a_b | |
stack << a << (b||0) | |
end | |
def discard | |
stack.pop | |
end | |
def pop_int | |
output << stack.pop.to_s | |
end | |
def pop_char | |
output << stack.pop.chr | |
end | |
def trampoline | |
tape.next_cmd | |
end | |
def string_mode | |
while((cmd = tape.next_raw) != '"') | |
stack.push(cmd.getbyte(0)) | |
end | |
end | |
def put | |
x, y = a_b | |
tape.put(x, y, stack.pop.chr) | |
end | |
def get | |
x, y = a_b | |
stack.push(tape.get(x, y).getbyte(0)) | |
end | |
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
module Parser | |
INSTRUCTIONS = { | |
'+' => :addition, | |
'-' => :subtraction, | |
'*' => :multiplication, | |
'/' => :division, | |
'%' => :modulo, | |
'!' => :l_not, | |
'`' => :greater_than, | |
'>' => :move_right, | |
'<' => :move_left, | |
'^' => :move_up, | |
'v' => :move_down, | |
'?' => :move_random, | |
'_' => :pop_lr, | |
'|' => :pop_ud, | |
'"' => :string_mode, | |
':' => :duplicate, | |
'\\' => :swap, | |
'$' => :discard, | |
'.' => :pop_int, | |
',' => :pop_char, | |
'#' => :trampoline, | |
'p' => :put, | |
'g' => :get, | |
'@' => :EOP, | |
' ' => :noop | |
} | |
(0..9).each do |n| | |
INSTRUCTIONS[n.to_s] = "push_#{n}".to_sym | |
end | |
INSTRUCTIONS.freeze | |
def self.parse(code) | |
code.split("\n").map(&:chars) | |
end | |
def self.parse_cmd(cmd, no_fail = false) | |
INSTRUCTIONS.fetch(cmd) do |_| | |
fail "Could not parse command #{cmd}" unless no_fail | |
end | |
end | |
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
require 'parser' | |
class Tape | |
DIRS = [:up, :down, :left, :right].freeze | |
attr_reader :tape, :ptr, :dir, :x, :y | |
def initialize(code) | |
@tape = Parser.parse(code) | |
@x, @y = 0, 0 | |
@dir = :right | |
end | |
def cmd | |
Parser.parse_cmd(raw) | |
rescue => e | |
throw "Failure at #{x}, #{y}: #{e.message}" | |
end | |
def raw | |
tape[x][y] | |
end | |
def next_cmd | |
send(dir) | |
cmd | |
end | |
def next_raw | |
send(dir) | |
raw | |
end | |
def dir=(dir) | |
fail ArgumentError, "#{dir} is not a valid direction" unless DIRS.include?(dir) | |
@dir = dir | |
end | |
def put(x, y, val) | |
tape[x][y] = val | |
end | |
def get(x, y) | |
tape[x][y] | |
end | |
private | |
def max_h | |
@max_h ||= (tape.length - 1) | |
end | |
def max_w | |
tape[x].length - 1 | |
end | |
def up | |
@x -= 1 | |
@x = max_h if x < 0 | |
@y = max_w if y > max_w | |
end | |
def down | |
@x = (x + 1) % (max_h+1) | |
@y = max_w if y > max_w | |
end | |
def right | |
@y = (y + 1) % (max_w+1) | |
end | |
def left | |
@y -= 1 | |
@y = max_w if y < 0 | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment