lib/rubocop/ast/node_pattern/lexer.rex.rb



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

# The only difficulty is to distinguish: `fn(argument)` from `fn (sequence)`.
# The presence of the whitespace determines if it is an _argument_ to the
# function call `fn` or if a _sequence_ follows the function call.
#
# If there is the potential for an argument list, the lexer enters the state `:ARG`.
# The rest of the times, the state is `nil`.
#
# In case of an argument list, :tARG_LIST is emitted instead of a '('.
# Therefore, the token '(' always signals the beginning of a sequence.


##
# The generated lexer RuboCop::AST::NodePattern::LexerRex

class RuboCop::AST::NodePattern::LexerRex
  require 'strscan'

  # :stopdoc:
  CONST_NAME  = /[A-Z:][a-zA-Z_:]+/
  SYMBOL_NAME = /[\w+@*\/?!<>=~|%^&-]+|\[\]=?/
  IDENTIFIER  = /[a-z][a-zA-Z0-9_]*/
  NODE_TYPE   = /[a-z][a-zA-Z0-9_-]*/
  CALL        = /(?:#{CONST_NAME}\.)?#{IDENTIFIER}[!?]?/
  REGEXP_BODY = /(?:[^\/]|\\\/)*/
  REGEXP      = /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
  # :startdoc:
  # :stopdoc:
  class LexerError < StandardError ; end
  class ScanError < LexerError ; end
  # :startdoc:

  ##
  # 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 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.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>"),
    ].compact.join(":")
  end

  ##
  # Lex the next token.

  def next_token

    token = nil

    until ss.eos? or token do
      token =
        case state
        when nil then
          case
          when ss.skip(/\s+/) then
            # do nothing
          when ss.skip(/:(#{SYMBOL_NAME})/o) then
            action { emit :tSYMBOL, &:to_sym }
          when ss.skip(/"(.+?)"/) then
            action { emit :tSTRING }
          when ss.skip(/[-+]?\d+\.\d+/) then
            action { emit :tNUMBER, &:to_f }
          when ss.skip(/[-+]?\d+/) then
            action { emit :tNUMBER, &:to_i }
          when ss.skip(/#{Regexp.union(
                    %w"( ) { | } [ ] < > $ ! ^ ` ... + * ? ,"
                  )}/o) then
            action { emit ss.matched, &:to_sym }
          when ss.skip(/#{REGEXP}/o) then
            action { emit_regexp }
          when ss.skip(/%?(#{CONST_NAME})/o) then
            action { emit :tPARAM_CONST }
          when ss.skip(/%([a-z_]+)/) then
            action { emit :tPARAM_NAMED }
          when ss.skip(/%(\d*)/) then
            action { emit(:tPARAM_NUMBER) { |s| s.empty? ? 1 : s.to_i } } # Map `%` to `%1`
          when ss.skip(/_(#{IDENTIFIER})/o) then
            action { emit :tUNIFY }
          when ss.skip(/_/o) then
            action { emit :tWILDCARD }
          when ss.skip(/\#(#{CALL})/o) then
            action { @state = :ARG; emit :tFUNCTION_CALL, &:to_sym }
          when ss.skip(/#{IDENTIFIER}\?/o) then
            action { @state = :ARG; emit :tPREDICATE, &:to_sym }
          when ss.skip(/#{NODE_TYPE}/o) then
            action { emit :tNODE_TYPE, &:to_sym }
          when ss.skip(/\#.*/) then
            action { emit_comment }
          else
            text = ss.string[ss.pos .. -1]
            raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
          end
        when :ARG then
          case
          when ss.skip(/\(/) then
            action { @state = nil; emit :tARG_LIST }
          when ss.skip(//) then
            action { @state = nil }
          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