class Befunger::Interpreter

{Befunger::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 “`

Constants

DIRECTIONS

Human friendly constants for the direction to move a code_pointer in.

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 {Interpreter::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.

Attributes

code_array[R]

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", " ", "^", " ", "_", "@"]

] “‘

Public Class Methods

new(code) click to toggle source

@param code [String] the befunge code to be run. Lines must be separated with newline characters

# File lib/befunger/interpreter.rb, line 106
def initialize(code)
  @code_array = code.split("\n").map { |line| line.chars }
end

Public Instance Methods

run() click to toggle source

@return [String] output of program execution

# 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

Private Instance Methods

get_instruction(code_pointer) click to toggle source

Returns the next instruction to be processed

@param code_pointer [Hash] the current code_pointer, in the form: ‘{x: 0, y: 3}` @return [String] the next instruction to be processed

# 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) click to toggle source

Given an ‘instruction` and `state` - processes the instruction and returns the modified `state`.

@param instruction [String] the instruction to be executed @param state [Hash] the current interpreter state @return [Hash] the modified ‘state`

# 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) click to toggle source

Moves the ‘code_pointer` in the specified `code_direction`, and returns the modified `code_pointer`

@param code_pointer [Hash] the current code_pointer, in the form: ‘{x: 0, y: 3}` @param code_direction [Hash] the current code_direction, in the form: `{x: 0, y: 1}` @return [Hash] the modified `code_pointer`

# 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