# frozen_string_literal: true
# encoding: UTF-8
#--
# This file is automatically generated. Do not modify it.
# Generated by: oedipus_lex version 2.6.3.
# Source: lib/oedipus_lex.rex
#++


##
# The generated lexer OedipusLex

class OedipusLex
  require 'strscan'

  # :stopdoc:
  ST  = /(?:(:\S+|\w+\??))/
  RE  = /(\/(?:\\.|[^\/])*\/[ion]?)/
  ACT = /(\{.*|:?\w+)/
  # :startdoc:
  # :stopdoc:
  class LexerError < StandardError ; end
  class ScanError < LexerError ; end
  # :startdoc:

  ##
  # The current line number.

  attr_accessor :lineno
  ##
  # The file name / path

  attr_accessor :filename

  ##
  # The StringScanner for this lexer.

  attr_accessor :ss

  ##
  # The current lexical state.

  attr_accessor :state

  alias :match :ss

  ##
  # The match groups for the current scan.

  def matches
    m = (1..9).map { |i| ss[i] }
    m.pop until m[-1] or m.empty?
    m
  end

  ##
  # Yields on the current action.

  def action
    yield
  end

  ##
  # The previous position. Only available if the :column option is on.

  attr_accessor :old_pos

  ##
  # The position of the start of the current line. Only available if the
  # :column option is on.

  attr_accessor :start_of_current_line_pos

  ##
  # The current column, starting at 0. Only available if the
  # :column option is on.
  def column
    old_pos - start_of_current_line_pos
  end

  ##
  # Parse the file by getting all tokens and calling lex_+type+ on them.

  def do_parse
    while token = next_token do
      type, *vals = token

      send "lex_#{type}", *vals
    end
  end

  ##
  # The current scanner class. Must be overridden in subclasses.

  def scanner_class
    StringScanner
  end unless instance_methods(false).map(&:to_s).include?("scanner_class")

  ##
  # Parse the given string.

  def parse str
    self.ss     = scanner_class.new str
    self.lineno = 1
    self.start_of_current_line_pos = 0
    self.state  ||= nil

    do_parse
  end

  ##
  # Read in and parse the file at +path+.

  def parse_file path
    self.filename = path
    open path do |f|
      parse f.read
    end
  end

  ##
  # The current location in the parse.

  def location
    [
      (filename || "<input>"),
      lineno,
      column,
    ].compact.join(":")
  end

  ##
  # Lex the next token.

  def next_token

    token = nil

    until ss.eos? or token do
      if ss.check(/\n/) then
        self.lineno += 1
        # line starts 1 position after the newline
        self.start_of_current_line_pos = ss.pos + 1
      end
      self.old_pos = ss.pos
      token =
        case state
        when nil, :option, :inner, :start, :macro, :rule, :group then
          case
          when ss.skip(/options?.*/) then
            [:state, :option]
          when ss.skip(/inner.*/) then
            [:state, :inner]
          when ss.skip(/macros?.*/) then
            [:state, :macro]
          when ss.skip(/rules?.*/) then
            [:state, :rule]
          when ss.skip(/start.*/) then
            [:state, :start]
          when ss.skip(/end/) then
            [:state, :END]
          when ss.skip(/\A((?:.|\n)*)class ([\w:]+.*)/) then
            action { [:class, *matches] }
          when ss.skip(/\n+/) then
            # do nothing
          when text = ss.scan(/\s*(\#.*)/) then
            action { [:comment, text] }
          when (state == :option) && (ss.skip(/\s+/)) then
            # do nothing
          when (state == :option) && (text = ss.scan(/stub/i)) then
            action { [:option, text] }
          when (state == :option) && (text = ss.scan(/debug/i)) then
            action { [:option, text] }
          when (state == :option) && (text = ss.scan(/do_parse/i)) then
            action { [:option, text] }
          when (state == :option) && (text = ss.scan(/lineno/i)) then
            action { [:option, text] }
          when (state == :option) && (text = ss.scan(/column/i)) then
            action { [:option, text] }
          when (state == :inner) && (text = ss.scan(/.*/)) then
            action { [:inner, text] }
          when (state == :start) && (text = ss.scan(/.*/)) then
            action { [:start, text] }
          when (state == :macro) && (ss.skip(/\s+(\w+)\s+#{RE}/o)) then
            action { [:macro, *matches] }
          when (state == :rule) && (ss.skip(/\s*#{ST}?[\ \t]*#{RE}[\ \t]*#{ACT}?/o)) then
            action { [:rule, *matches] }
          when (state == :rule) && (ss.skip(/\s*:[\ \t]*#{RE}/o)) then
            action { [:grouphead, *matches] }
          when (state == :group) && (ss.skip(/\s*:[\ \t]*#{RE}/o)) then
            action { [:grouphead, *matches] }
          when (state == :group) && (ss.skip(/\s*\|\s*#{ST}?[\ \t]*#{RE}[\ \t]*#{ACT}?/o)) then
            action { [:group, *matches] }
          when (state == :group) && (ss.skip(/\s*#{ST}?[\ \t]*#{RE}[\ \t]*#{ACT}?/o)) then
            action { [:groupend, *matches] }
          else
            text = ss.string[ss.pos .. -1]
            raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
          end
        when :END then
          case
          when ss.skip(/\n+/) then
            # do nothing
          when text = ss.scan(/.*/) then
            action { [:end, text] }
          else
            text = ss.string[ss.pos .. -1]
            raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
          end
        else
          raise ScanError, "undefined state at #{location}: '#{state}'"
        end # token = case state

      next unless token # allow functions to trigger redo w/ nil
    end # while

    raise LexerError, "bad lexical result at #{location}: #{token.inspect}" unless
      token.nil? || (Array === token && token.size >= 2)

    # auto-switch state
    self.state = token.last if token && token.first == :state

    token
  end # def next_token
end # class
