Class: Befunger::Interpreter

Inherits:
Object
  • Object
show all
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.

x and y are 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

y

code_direction

The next direction to move code_pointer in.

x and y are 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.

stack

A stack to store values, manipulated by the befunge code being run.

output

Output 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

Instance Method Summary collapse

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_arrayObject (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

#runString



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