Common << {

Parser < BasicObject {
  State < BasicObject {
    var start_idx
    var end_idx
    var rule
    var result

    var error_idx

    raise_error: self.error_idx && raise(SyntaxError
      "Syntax error with parser: "self.rule" at index: "self.error_idx""
    )
  }

  var debug_mode: false

  var grammar: Grammar.new
  var builder: null

  new_worker: set_up_worker(prototype.new)
  new_processor: Processor.new(builder: builder)

  var prototype: compile_prototype

  refresh: self.prototype = compile_prototype

  compile_prototype: {
    proto = Component.new
    grammar.construct_all_rules
    grammar.rule_table.each |name, rule| {
      compile_rule_into(proto, name, rule)
    }
    proto
  }

  compile_rule_into: |proto, name, rule| {
    construct = rule.inner.construct

    Myco.add_dynamic_method(proto, name, "(compiled parser)") |g| {
      g.extend(BytecodeHelpers)
      g.debug_mode = self.debug_mode
      g.rule_start
      construct.bytecode(g)
      g.rule_finish
    }
  }

  compile_generator: |name, file, line=1, &block| {
    g = Myco::ToolSet::Generator.new
    g.name = name
    g.file = file
    g.set_line(line)

    block.call(g)

    g.ret
    g.close
    g.use_detected
    g
  }

  compile_code: |name, file, line=1, &block| {
    g = compile_generator(name, file, line, &block)
    g.encode
    g.package(Rubinius::CompiledCode)
  }

  save_prototype: |filename| {
    code = compile_code(:__script__, :"(snippet)") |g| {
      g.push_cpath_top; g.find_const(:Class)
      g.create_block(compile_generator(:__block__, :"(snippet)") |g| {
        prototype.instance_methods.each |name| {
          code = prototype.instance_method(name).executable
          if(code.is_a?(Rubinius::CompiledCode)) {
            g.push_rubinius
              g.push_literal(name)
              g.push_literal(code)
              g.push_scope
              g.push_variables; g.send(:method_visibility, 0)
            g.send(:add_defn_method, 4)
          }
        }
      })
      g.send_with_block(:new, 0)
    }

    Myco::ToolSet::CompiledFile.dump(code, filename, Rubinius::Signature, Rubinius::RUBY_LIB_VERSION)
  }

  load_prototype: |filename| {
    loader = Myco::CodeLoader::BytecodeLoader.new(filename)
    loader.bind_to(call_depth:1)
    self.prototype = loader.load
  }

  set_up_worker: |worker| {
    worker.__set_ivar__(:"@captures", [])
    worker.__set_ivar__(:"@highest_idx", 0)
    worker
  }

  captures_of_worker: |worker|
    worker.__get_ivar__(:"@captures")

  highest_idx_of_worker: |worker|
    worker.__get_ivar__(:"@highest_idx")

  parse: {
    raise("The parse method is not implemented for "self"")
  }
  # Match and process the given string using the current grammar
  parse: |string, rule:"root", start_idx:0| {
    worker    = new_worker
    processor = new_processor
    state     = State.new(
      string:    string.to_s
      rule:      rule.to_sym
      start_idx: start_idx.to_i
    )

    state.end_idx = worker.__send__(state.rule, state.string, state.start_idx)

    state.end_idx &? (
      processor.string = state.string
      processor.capture_items = captures_of_worker(worker)
      grammar.?tokenizer && (processor.tokenizer = grammar.tokenizer)
      state.result = processor.process
    ) ?? (
      state.error_idx = highest_idx_of_worker(worker)
      pos = processor.position_of(state.error_idx, state.string)
      state.error_row = pos.first
      state.error_col = pos.last
    )
    state
  }
}

Parser << {
  # Find the first occurrence of the pattern that can be matched,
  # (not necessarily starting at index zero).
  # Returns the Parser::State, or null if no occurrences were found.
  find: |subject, start_idx: 0, **kwargs| {
    result = null
    Loop.run {
      start_idx >= subject.size && Loop.break
      state = parse(subject, kwargs.merge(start_idx: start_idx))
      state.end_idx && (result = state; Loop.break)
      start_idx = start_idx + 1
    }
    result
  }

  # Find each non-overlapping occurrence of the pattern that can be matched,
  # starting with the first occurrence as found by the find method.
  # Returns the array of Parser::State objects that were found.
  scan: |subject, start_idx: 0, **kwargs| {
    [].tap |result| {
      Loop.run {
        state = find(subject, kwargs.merge(start_idx: start_idx))
        state || Loop.break
        start_idx = state.end_idx
        result.push(state)
      }
    }
  }

  # For each scan result, replace the matched part with the rule result.
  # Returns the copied subject with replacements performed.
  scan_replace: |subject, start_idx: 0, rule: "root", **kwargs| {
    subject.class.new.tap |result| {
      rule = rule.to_sym
      kwargs = kwargs.merge(start_idx: start_idx, rule: rule)
      start_idx = 0 # always return the entire array
      scan(subject, kwargs).each |state| {
        range = Range.new(start_idx, state.start_idx, true)
        result.concat(subject.slice(range))
        result.send(:"<<", state.result[rule])
        start_idx = state.end_idx
      }
      range = Range.new(start_idx, subject.size, true)
      result.concat(subject.slice(range))
    }
  }
}

}