class SyntaxTree::YARV::Bf

Parses the given source code into a syntax tree, compiles that syntax tree into YARV bytecode.

Attributes

source[R]

Public Class Methods

new(source) click to toggle source
# File lib/syntax_tree/yarv/bf.rb, line 10
def initialize(source)
  @source = source
end

Public Instance Methods

compile() click to toggle source
# File lib/syntax_tree/yarv/bf.rb, line 14
def compile
  # Set up the top-level instruction sequence that will be returned.
  iseq = InstructionSequence.new("<compiled>", "<compiled>", 1, :top)

  # Set up the $tape global variable that will hold our state.
  iseq.duphash({ 0 => 0 })
  iseq.setglobal(:$tape)
  iseq.getglobal(:$tape)
  iseq.putobject(0)
  iseq.send(YARV.calldata(:default=, 1))

  # Set up the $cursor global variable that will hold the current position
  # in the tape.
  iseq.putobject(0)
  iseq.setglobal(:$cursor)

  stack = []
  source
    .each_char
    .chunk do |char|
      # For each character, we're going to assign a type to it. This
      # allows a couple of optimizations to be made by combining multiple
      # instructions into single instructions, e.g., +++ becomes a single
      # change_by(3) instruction.
      case char
      when "+", "-"
        :change
      when ">", "<"
        :shift
      when "."
        :output
      when ","
        :input
      when "[", "]"
        :loop
      else
        :ignored
      end
    end
    .each do |type, chunk|
      # For each chunk, we're going to emit the appropriate instruction.
      case type
      when :change
        change_by(iseq, chunk.count("+") - chunk.count("-"))
      when :shift
        shift_by(iseq, chunk.count(">") - chunk.count("<"))
      when :output
        chunk.length.times { output_char(iseq) }
      when :input
        chunk.length.times { input_char(iseq) }
      when :loop
        chunk.each do |char|
          case char
          when "["
            stack << loop_start(iseq)
          when "]"
            loop_end(iseq, *stack.pop)
          end
        end
      end
    end

  iseq.leave
  iseq.compile!
  iseq
end