Skip to content

Instantly share code, notes, and snippets.

@mstahl
Created February 10, 2011 04:54
Show Gist options
  • Save mstahl/819971 to your computer and use it in GitHub Desktop.
Save mstahl/819971 to your computer and use it in GitHub Desktop.
A Befunge interpreter written in Ruby
#!/usr/bin/ruby
require 'pp'
@core = {}
@hello_world = <<__END_HELLO_WORLD__
> v
v ,,,,,"Hello"<
>48*, v
v,,,,,,"World!"<
>25*,@
__END_HELLO_WORLD__
@random = <<__END_RANDOM_PROGRAM__
vv < <
2
^ v<
v1<?>3v4
^ ^
> >?> ?>5^
v v
v9<?>7v6
v v<
8
. > > ^
^<
__END_RANDOM_PROGRAM__
# This one asks for a number, then outputs
# that many random digits before halting.
@random2 = <<__END_RANDOM2_PROGRAM__
vv < <
& 2
^
v 1<?>3v4
^ ^
> >?> ?>5^
v v
v 9<?>7v6
. v
1 8
-
:
^_@
> >> ^
__END_RANDOM2_PROGRAM__
class BefungeCore
attr_reader :raw_core
attr_reader :width, :height
def initialize(width = 64, height = 64)
@raw_core = Hash.new
@width = width
@height = height
end
def []=(coords, datum)
@raw_core["#{coords[:x]}_#{coords[:y]}"] = datum
end
def [](coords)
@raw_core["#{coords[:x]}_#{coords[:y]}"]
end
def to_s
core = ""
(0..@height).each do |y|
line = ""
(0..@width).each do |x|
char = self[:x => x, :y => y]
if char.nil? then
line += ' '
else
line += char
end
end
core += "#{line}$\n"
end
core
end
end
class BefungeProgram
attr_reader :core # Text/data section of code
attr_reader :pointer # Internal register pointing to x/y coordinates of currently executing instruction
attr_reader :direction # Direction in which the program counter moves
# Some convenient constants
DIRECTION_NORTH = 0
DIRECTION_EAST = 1
DIRECTION_SOUTH = 2
DIRECTION_WEST = 3
def initialize(program_text)
@core = BefungeCore.new
program_text.lines.each_with_index do |row, y|
row.chop.split(//).each_with_index do |c, x|
next if c == ' '
@core[:x => x, :y => y] = c
end
end
end
# advance
#
# Advances program counter in whichever direction it's supposed to go
def advance
case @direction
when DIRECTION_EAST
@pointer[:x] = (@pointer[:x] + 1) % @core.width
when DIRECTION_WEST
@pointer[:x] = (@pointer[:x] - 1) % @core.width
when DIRECTION_NORTH
@pointer[:y] = (@pointer[:y] - 1) % @core.height
when DIRECTION_SOUTH
@pointer[:y] = (@pointer[:y] + 1) % @core.height
end
end
def run
@pointer = {:x => 0, :y => 0}
@direction = DIRECTION_EAST
@mode = :execute
@stack = []
while true
instruction = @core[@pointer]
# puts "\nCurrent instruction: #{instruction.inspect}"
# puts "Position: #{@pointer}"
# puts "Direction: #{@direction}"
# puts "Mode: #{@mode}"
# puts "Stack contents: #{@stack.inspect}"
# gets
unless instruction.nil?
if @mode == :execute then
case instruction
when '+'
a = @stack.pop
b = @stack.pop
@stack.push(a + b)
when '-'
a = @stack.pop
b = @stack.pop
@stack.push(b - a)
when '*'
a = @stack.pop
b = @stack.pop
@stack.push(a * b)
when '/'
a = @stack.pop
b = @stack.pop
@stack.push((a/b).floor)
when '%'
a = @stack.pop
b = @stack.pop
@stack.push(a % b)
when '!'
a = @stack.pop
if a == 0 then
@stack.push 1
else
@stack.push 0
end
when '`'
a = @stack.pop
b = @stack.pop
@stack.push(a < b ? 1 : 0)
when '>'
@direction = DIRECTION_EAST
when '<'
@direction = DIRECTION_WEST
when '^'
@direction = DIRECTION_NORTH
when 'v'
@direction = DIRECTION_SOUTH
when '?'
@direction = rand(4)
when '_'
if @stack.pop == 0 then
@direction = DIRECTION_EAST
else
@direction = DIRECTION_WEST
end
when '|'
if @stack.pop == 0 then
@direction = DIRECTION_SOUTH
else
@direction = DIRECTION_NORTH
end
when '"'
@mode = :string
when ':'
a = @stack.pop
2.times {@stack.push a}
when '\\'
a = @stack.pop
b = @stack.pop
@stack.push a
@stack.push b
when '$'
@stack.pop
when '.'
print @stack.pop.to_i
when ','
print @stack.pop.chr
when '#'
self.advance
when 'p'
y = @stack.pop
x = @stack.pop
v = @stack.pop
@core[:x => x, :y => y] = v.chr
when 'g'
y = @stack.pop
x = @stack.pop
@stack.push @core[:x => x, :y => y].bytes.first
when '&'
@stack.push gets.to_i
when '~'
@stack.push gets.bytes.first
when '@'
return
else
@stack.push instruction.to_i
end
else
# String mode
if instruction == '"' then
@mode = :execute
elsif instruction.nil? then
pp @pointer
else
@stack.push instruction.bytes.first
end
end
end
self.advance
end
end
def to_s
@core.to_s
end
end
bp2 = BefungeProgram.new @random2
bp2.run
puts " "
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment