class PuppetDBQuery::Parser

parse a puppetdb query string into #PuppetDBQuery::Term s rubocop:disable Metrics/ClassLength

Constants

AND

these are the operators we understand rubocop:disable Style/ExtraSpacing

EQUAL
GREATER
GREATER_OR_EQUAL
IN
LESS
LESS_OR_EQUAL
MATCH
NOT
NOT_EQUAL
OPERATORS

map certain symbols (we get them from a tokenizer) to our operators

OR

Attributes

position[R]
symbols[R]

Public Class Methods

parse(puppetdb_query) click to toggle source
# File lib/puppetdb_query/parser.rb, line 12
def self.parse(puppetdb_query)
  Parser.new.parse(puppetdb_query)
end

Public Instance Methods

parse(query) click to toggle source

parse query and get resulting array of PuppetDBQuery::Term s

# File lib/puppetdb_query/parser.rb, line 51
def parse(query)
  @symbols = Tokenizer.symbols(query)
  @position = 0
  r = []
  r << read_maximal_term(0) until empty?
  r
end

Private Instance Methods

add_next_infix_terms(priority, first) click to toggle source

rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/BlockLength rubocop:disable Metrics/MethodLength,Metrics/PerceivedComplexity

# File lib/puppetdb_query/parser.rb, line 135
def add_next_infix_terms(priority, first)
  old_operator = nil
  term = first
  loop do
    # we expect an infix operator
    operator = get_operator
    logger.debug "we found operator '#{operator}'" unless operator.nil?
    if operator.nil? || operator.prefix? || operator.priority <= priority
      logger.debug "operator '#{operator}' is nil" if operator.nil?
      logger.debug "operator '#{operator}' is prefex" if !operator.nil? && operator.prefix?
      if !operator.nil? && operator.priority <= priority
        logger.debug "operator '#{operator}' has less priority #{operator.priority}" \
                     " than #{priority}"
      end
      logger.debug "get_next_infix_terms: #{term}"
      return term
    end
    if old_operator.nil? || old_operator.priority >= operator.priority
      # old operator has not less priority
      read_token
      new_term = read_maximal_term(operator.priority)
      error("to few arguments for operator '#{operator}'") if new_term.nil?
      logger.debug "is '#{old_operator}' == '#{operator}' : #{old_operator == operator}"
      if old_operator == operator
        if operator.maximum && term.args.size + 1 >= operator.maximum
          raise "to much arguments for operator '#{operator}'"
        end
        term.add(new_term)
      else
        also_new_term = Term.new(operator)
        also_new_term.add(term)
        also_new_term.add(new_term)
        term = also_new_term
      end
    else
      # old operator has less priority
      new_term = read_maximal_term(operator.priority)
      error("to few arguments for operator '#{operator}'") if new_term.nil?
      also_new_term = Term.new(operator)
      also_new_term.add(term)
      also_new_term.add(new_term)
      term = also_new_term
    end
    old_operator = operator
  end
end
empty?() click to toggle source
# File lib/puppetdb_query/parser.rb, line 197
def empty?
  position >= symbols.size
end
error(message) click to toggle source

rubocop:enable Style/AccessorMethodName

# File lib/puppetdb_query/parser.rb, line 208
def error(message)
  length = (position > 0 ? Tokenizer.query(symbols[0..(position - 1)]).size + 1 : 0)
  raise "parsing query failed\n#{message}\n\n#{Tokenizer.query(symbols)}\n#{' ' * length}^"
rescue
  logger.error $!
  raise $!
end
get_operator() click to toggle source

rubocop:disable Style/AccessorMethodName

# File lib/puppetdb_query/parser.rb, line 185
def get_operator
  OPERATORS[get_token]
end
get_token() click to toggle source

rubocop:disable Style/AccessorMethodName

# File lib/puppetdb_query/parser.rb, line 202
def get_token
  return nil if empty?
  symbols[position]
end
read_maximal_term(priority) click to toggle source

Reads next maximal term. The following input doesn't make the term more complete. Respects the priority of operators by comparing it to the given value.

# File lib/puppetdb_query/parser.rb, line 63
def read_maximal_term(priority)
  return nil if empty?
  logger.debug "read maximal term (#{priority})"
  first = read_minimal_term
  term = add_next_infix_terms(priority, first)
  logger.debug "read maximal term: #{term}"
  term
end
read_minimal_term() click to toggle source

Read next following term. This is a complete term but some infix operator or some terms for an infix operator might follow. rubocop:disable Metrics/PerceivedComplexity,Metrics/AbcSize,Metrics/CyclomaticComplexity rubocop:disable Metrics/MethodLength

# File lib/puppetdb_query/parser.rb, line 76
def read_minimal_term
  logger.debug "read minimal term"
  term = nil
  operator = get_operator
  if operator
    error("'#{operator}' is no prefix operator") unless operator.prefix?
    read_token
    term = Term.new(operator)
    if operator.maximum > 1
      error("'#{operator}' is prefix operator with more than one argument," \
            " that is not supported yet")
    end
    if operator.minimum > 0
      arg = read_maximal_term(operator.priority)
      error("prefix operator '#{operator}' got no argument") if arg.nil?
      term.add(arg)
    end
    logger.debug "read_minimal_term: #{term}"
    return term
  end
  # no prefix operator found
  token = get_token
  if token == :_begin
    logger.debug "read ()"
    read_token
    term = read_maximal_term(0)
    error "'#{Tokenizer.symbol_to_string(:_end)}' expected " unless get_token == :_end
    read_token
  elsif token == :_list_begin
    logger.debug "read []"
    read_token
    args = []
    loop do
      term = read_maximal_term(0)
      if get_token != :_list_end && get_token != :_comma
        error "'#{Tokenizer.symbol_to_string(:_list_end)}' or" \
              " '#{Tokenizer.symbol_to_string(:_comma)}' expected"
      end
      args << term if term
      break if read_token == :_list_end
    end
    term = args
  else
    error("no operator #{get_operator} expected here") if get_operator
    if Tokenizer::LANGUAGE_STRINGS.include?(token) && ![:true, :false, :null].include?(token)
      error("that was not expected here: '#{Tokenizer.symbol_to_string(token)}'")
    end
    token = read_token
    logger.debug "atom found: #{token}"
    term = token
  end
  logger.debug "read minimal term: #{term}"
  term
end
read_token() click to toggle source

rubocop:enable Style/AccessorMethodName

# File lib/puppetdb_query/parser.rb, line 190
def read_token
  return nil if empty?
  token = symbols[position]
  @position += 1
  token
end