class RegularExpression::Interpreter

An interpreter for our compiled bytecode. Maybe we could make this possible to enter at a given state and deoptimise to it from the compiled code?

Attributes

bytecode[R]

Public Class Methods

new(bytecode) click to toggle source
# File lib/regular_expression/interpreter.rb, line 9
def initialize(bytecode)
  @bytecode = bytecode
end

Public Instance Methods

match?(string) click to toggle source
# File lib/regular_expression/interpreter.rb, line 19
def match?(string)
  stack = []

  (0..string.size).any? do |start_n|
    string_n = start_n
    insn_n = 0

    loop do
      insn = bytecode.insns[insn_n]

      case insn
      when Bytecode::Insns::PushIndex
        stack << string_n
        insn_n += 1
      when Bytecode::Insns::PopIndex
        string_n = stack.pop
        insn_n += 1
      when Bytecode::Insns::GuardBegin
        return false if start_n != 0

        insn_n = bytecode.labels[insn.guarded]
      when Bytecode::Insns::GuardEnd
        break if string_n != string.size

        insn_n = bytecode.labels[insn.guarded]
      when Bytecode::Insns::JumpAny
        if string_n < string.size
          string_n += 1
          insn_n = bytecode.labels[insn.target]
        else
          insn_n += 1
        end
      when Bytecode::Insns::JumpValue
        if string_n < string.size && string[string_n] == insn.char
          string_n += 1
          insn_n = bytecode.labels[insn.target]
        else
          insn_n += 1
        end
      when Bytecode::Insns::JumpValuesInvert
        if string_n < string.size && !insn.chars.include?(string[string_n])
          string_n += 1
          insn_n = bytecode.labels[insn.target]
        else
          insn_n += 1
        end
      when Bytecode::Insns::JumpRange
        if string_n < string.size && string[string_n] >= insn.left && string[string_n] <= insn.right
          string_n += 1
          insn_n = bytecode.labels[insn.target]
        else
          insn_n += 1
        end
      when Bytecode::Insns::JumpRangeInvert
        if string_n < string.size && (string[string_n] < insn.left || string[string_n] > insn.right)
          string_n += 1
          insn_n = bytecode.labels[insn.target]
        else
          insn_n += 1
        end
      when Bytecode::Insns::Jump
        insn_n = bytecode.labels[insn.target]
      when Bytecode::Insns::Match
        return true
      when Bytecode::Insns::Fail
        break
      else
        raise
      end
    end
  end
end
to_proc() click to toggle source

This is just here for API parity with the compiled outputs.

# File lib/regular_expression/interpreter.rb, line 14
def to_proc
  interpreter = self
  ->(string) { interpreter.match?(string) }
end