Class: Befunger::Interpreter
- Inherits:
-
Object
- Object
- Befunger::Interpreter
- Defined in:
- lib/befunger/interpreter.rb
Overview
Interpreter is responsible for running Befunge code.
The only input to the Interpreter is the code to be executed,
passed in as a string. Lines are to be separated with the \n
character.
Example usage:
output = Befunger::Interpreter.new(code_string).run
Constant Summary collapse
- DIRECTIONS =
Human friendly constants for the direction to move a code_pointer in.
{ right: { x: 1, y: 0 }, down: { x: 0, y: 1 }, left: { x: -1, y: 0 }, up: { x: 0, y: -1 } }
- INITIAL_STATE =
Default interpreter state.
This gets copied and then modified and passed from instruction to instruction in the interpreter loop.
- ##
code_pointer
Denotes what the position of the current instruction is.
xandyare positive values starting at 0, calculated like this:.----------------------------------------------> x | | | | (code_array) --------> {x: 5, y: 0} | | | [ | | | [">", "9", "8", "7", "v", ">", ".", "v"], | | ["v", "4", "5", "6", "<", " ", " ", ":"], | | [">", "3", "2", "1", " ", "^", " ", "_", "@"] | | ] | | | | ------> {x: 8, y: 2} v ycode_directionThe next direction to move
code_pointerin.xandyare either 0, 1, or -1.- (x: 1, y: 0) means the next move is towards the right
- (x: 0, y: 1) means the next move is downwards
- ... so on and so forth.
Human-friendly constants available in DIRECTIONS.
stackA stack to store values, manipulated by the befunge code being run.
outputOutput returned from program, represented as an array of characters.
- ##
{ code_pointer: {x: 0, y: 0}, # Instructions are evaluated from the top-left code_direction: Interpreter::DIRECTIONS[:right], stack: [], output: [] }
Instance Attribute Summary collapse
-
#code_array ⇒ Object
readonly
The Befunge code to be run, stored as a 2D array of characters.
Instance Method Summary collapse
-
#get_instruction(code_pointer) ⇒ String
private
Returns the next instruction to be processed.
-
#handle_instruction(instruction, state) ⇒ Hash
private
Given an
instructionandstate- processes the instruction and returns the modifiedstate. -
#initialize(code) ⇒ Interpreter
constructor
A new instance of Interpreter.
-
#move_pointer(code_pointer, code_direction) ⇒ Hash
private
Moves the
code_pointerin the specifiedcode_direction, and returns the modifiedcode_pointer. -
#run ⇒ String
Output of program execution.
Constructor Details
#initialize(code) ⇒ Interpreter
106 107 108 |
# File 'lib/befunger/interpreter.rb', line 106 def initialize(code) @code_array = code.split("\n").map { |line| line.chars } end |
Instance Attribute Details
#code_array ⇒ Object (readonly)
The Befunge code to be run, stored as a 2D array of characters.
For a program like this:
>987v>.v
v456< :
>321 ^ _@
code_array will look like:
[
[">", "9", "8", "7", "v", ">", ".", "v"],
["v", "4", "5", "6", "<", " ", " ", ":"],
[">", "3", "2", "1", " ", "^", " ", "_", "@"]
]
34 35 36 |
# File 'lib/befunger/interpreter.rb', line 34 def code_array @code_array end |
Instance Method Details
#get_instruction(code_pointer) ⇒ String (private)
Returns the next instruction to be processed
131 132 133 |
# File 'lib/befunger/interpreter.rb', line 131 def get_instruction(code_pointer) @code_array[code_pointer[:y]][code_pointer[:x]] end |
#handle_instruction(instruction, state) ⇒ Hash (private)
Given an instruction and state - processes the instruction and returns
the modified state.
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/befunger/interpreter.rb', line 154 def handle_instruction(instruction, state) output = state[:output].dup stack = state[:stack].dup code_pointer = state[:code_pointer].dup code_direction = state[:code_direction].dup # Numbers if ('0'..'9').include? instruction stack.push(instruction.to_i) # Binary Stack Operations elsif ['+', '-', '*', '/', '%', '`'].include? instruction a = stack.pop b = stack.pop value = case instruction when '+' then b + a when '*' then b * a when '-' then b - a when '/' then b / a when '%' then b % a when '`' then b > a ? 1 : 0 end stack.push(value) # Setter, Getter elsif ['p', 'g'].include? instruction y = stack.pop x = stack.pop case instruction when 'p' then @code_array[y][x] = stack.pop.chr when 'g' then stack.push @code_array[y][x].ord end # Operations with one stack value elsif ['!', '$', '.', ','].include? instruction a = stack.pop case instruction when '!' then stack.push(a == 0 ? 1 : 0) when '.' then output.push(a) when ',' then output.push(a.chr) end # Direction changes elsif ['>', '<', 'v', '^', '?', '_', '|'].include? instruction code_direction = case instruction when '>' then DIRECTIONS[:right] when '<' then DIRECTIONS[:left] when 'v' then DIRECTIONS[:down] when '^' then DIRECTIONS[:up] when '?' then DIRECTIONS.values.sample when '_' then DIRECTIONS[stack.pop == 0 ? :right : :left] when '|' then DIRECTIONS[stack.pop == 0 ? :down : :up] end # String Mode elsif instruction == '"' code_pointer = move_pointer(code_pointer, code_direction) # Skip the first quote loop do instruction = get_instruction(code_pointer) break if instruction == '"' stack.push(instruction.ord) code_pointer = move_pointer(code_pointer, code_direction) end # Swap elsif instruction == '\\' if stack.size == 1 stack.push(0) else stack[-1], stack[-2] = stack[-2], stack[-1] end # Duplicate elsif instruction == ":" stack.push(stack.empty? ? 0 : stack.last) # Skip elsif instruction == '#' code_pointer = move_pointer(code_pointer, code_direction) elsif instruction != ' ' raise "Invalid Instruction '#{instruction}'" end { code_pointer: move_pointer(code_pointer, code_direction), code_direction: code_direction, stack: stack, output: output } end |
#move_pointer(code_pointer, code_direction) ⇒ Hash (private)
Moves the code_pointer in the specified code_direction, and returns the modified
code_pointer
141 142 143 144 145 146 |
# File 'lib/befunger/interpreter.rb', line 141 def move_pointer(code_pointer, code_direction) code_pointer[:x] += code_direction[:x] code_pointer[:y] += code_direction[:y] code_pointer end |
#run ⇒ String
111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/befunger/interpreter.rb', line 111 def run state = Interpreter::INITIAL_STATE # TODO: `deep_dup`? loop do instruction = get_instruction(state[:code_pointer]) break if instruction == '@' state = handle_instruction(instruction, state) end state[:output].join('') end |