Skip to content

Instantly share code, notes, and snippets.

@xaviershay
Created July 17, 2013 03:59
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xaviershay/6017598 to your computer and use it in GitHub Desktop.
Save xaviershay/6017598 to your computer and use it in GitHub Desktop.
A very quick sketch of a befunge compiler.
class ValueObject < Struct
def self.[](*args)
if args.length > 0
new(*args)
else
new(:null)
end
end
def [](*args)
new(*args)
end
def inspect
if values == [nil]
"#{self.class.name}"
else
"#{self.class.name} #{values}"
end
end
end
Source = ValueObject.new(:code) do
def [](x, y)
grid[y][x]
end
def set(x, y, value)
grid[y][x] = value
end
def grid
@grid ||= code.lines.map(&:chars)
end
end
If = ValueObject[:left, :right]
Rand = ValueObject[:left, :up, :right, :down]
Push = ValueObject[:n]
PushString = ValueObject[:str]
Goto = ValueObject[:address]
Exit = ValueObject[:code]
Compiled = ValueObject[:address, :instruction]
OutputStack = ValueObject[]
MultiplyStack = ValueObject[]
Left = [-1, 0]
Right = [1, 0]
Up = [0, -1]
Down = [0, 1]
def compile(program, input, ip, delta)
done = false
add_instruction = ->(op) {
x = Compiled[program.length, op]
input.set(*ip, x)
program << x.instruction
x.instruction
}
while done == false
instruction = input[*ip]
case instruction
when Compiled then
done = true
program << Goto[instruction.address]
when '0'..'9' then
add_instruction.(Push[instruction.to_i])
when '*' then
add_instruction.(MultiplyStack[])
when '@' then
program << Exit[0]
done = true
when ' ' then
nil
when 'v' then
delta = [0, 1]; nil
when '<' then
delta = [-1, 0]; nil
when '>' then
delta = [1, 0]; nil
when '^' then
delta = [0, -1]; nil
when '|' then
done = true
x = add_instruction.(If[])
left_address = program.length
compile(program, input, [ip[0], ip[1] - 1], [0, -1])
right_address = program.length
compile(program, input, [ip[0], ip[1] + 1], [0, 1])
x.left = Goto[left_address]
x.right = Goto[right_address]
when '?' then
done = true
x = add_instruction.(Rand[])
left_address = program.length
compile(program, input, advance(ip, Left), Left)
right_address = program.length
compile(program, input, advance(ip, Right), Right)
up_address = program.length
compile(program, input, advance(ip, Up), Up)
down_address = program.length
compile(program, input, advance(ip, Down), Down)
x.left = Goto[left_address]
x.right = Goto[right_address]
x.up = Goto[up_address]
x.down = Goto[down_address]
when ','
add_instruction.(OutputStack[])
when '"'
start = ip
finish = false
buffer = ""
while !finish
ip = advance(ip, delta)
char = input[*ip]
if char == '"'
finish = true
else
buffer << char
end
end
add_instruction.(PushString[buffer])
else
raise "Unknown: #{instruction}"
end
ip = advance(ip, delta)
end
program
end
def advance(ip, delta)
ip.zip(delta).map {|x, y| x + y }
end
inputs = [
<<-EOS,
9 v>9v
>|0<
@
EOS
<<-EOS,
> v
v ,,,,,"Hello"<
>48*, v
v,,,,,,"World!"<
>25*,@
EOS
<<-EOS,
>v8"aaaab"<
^?7 ^
4
,
@
EOS
]
inputs.each do |input|
input = Source.new(input)
ip = [0, 0]
delta = [1, 0]
program = []
compile(program, input, ip, delta)
program.each_with_index do |x, i|
puts "%i: %s" % [i, x.inspect]
end
puts
end
0: Push [9]
1: If [Goto [2], Goto [5]]
2: Push [9]
3: Push [0]
4: Goto [1]
5: Exit [0]
0: PushString ["olleH"]
1: OutputStack
2: OutputStack
3: OutputStack
4: OutputStack
5: OutputStack
6: Push [4]
7: Push [8]
8: MultiplyStack
9: OutputStack
10: PushString ["!dlroW"]
11: OutputStack
12: OutputStack
13: OutputStack
14: OutputStack
15: OutputStack
16: OutputStack
17: Push [2]
18: Push [5]
19: MultiplyStack
20: OutputStack
21: Exit [0]
0: Rand [Goto [1], Goto [6], Goto [2], Goto [7]]
1: Goto [0]
2: Push [7]
3: PushString ["baaaa"]
4: Push [8]
5: Goto [0]
6: Goto [0]
7: Push [4]
8: OutputStack
9: Exit [0]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment