Class: LcpRuby::Search::QueryLanguageParser

Inherits:
Object
  • Object
show all
Defined in:
lib/lcp_ruby/search/query_language_parser.rb

Defined Under Namespace

Classes: ParseError

Constant Summary collapse

SYMBOL_OPERATORS =
{
  "!=" => :not_eq,
  ">=" => :gteq,
  "<=" => :lteq,
  ">" => :gt,
  "<" => :lt,
  "=" => :eq,
  "~" => :cont,
  "!~" => :not_cont,
  "!^" => :not_start,
  "!$" => :not_end,
  "^" => :start,
  "$" => :end
}.freeze
SORTED_SYMBOL_OPS =

Sorted by length desc so multi-char operators match first

SYMBOL_OPERATORS.keys.sort_by { |k| -k.length }.freeze
KEYWORD_OPERATORS =
{
  "in" => :in,
  "not in" => :not_in
}.freeze
IS_VALUES =
{
  "not null" => :not_null,
  "not true" => :not_true,
  "not false" => :not_false,
  "null" => :null,
  "present" => :present,
  "blank" => :blank,
  "true" => :true,
  "false" => :false,
  "this_week" => :this_week,
  "this_month" => :this_month,
  "this_quarter" => :this_quarter,
  "this_year" => :this_year
}.freeze
SORTED_IS_VALUES =

Sorted by length desc so “not null” matches before “not”

IS_VALUES.keys.sort_by { |k| -k.length }.freeze
WHITESPACE_RE =
/\s/
IDENTIFIER_CHAR_RE =
/[a-zA-Z0-9_]/
DIGIT_RE =
/[0-9]/
MAX_INPUT_LENGTH =
2000
MAX_PARSE_DEPTH =
50

Instance Method Summary collapse

Constructor Details

#initialize(input, max_nesting_depth: 10) ⇒ QueryLanguageParser

Returns a new instance of QueryLanguageParser.



62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/lcp_ruby/search/query_language_parser.rb', line 62

def initialize(input, max_nesting_depth: 10)
  @input = input.to_s
  if @input.length > MAX_INPUT_LENGTH
    raise ParseError.new(
      "Query too long (#{@input.length} characters, maximum is #{MAX_INPUT_LENGTH})",
      position: 0
    )
  end
  @pos = 0
  @parse_depth = 0
  @max_nesting_depth = max_nesting_depth
end

Instance Method Details

#parseObject

Parse the input and return a recursive condition tree. Returns: { “combinator” => “and”, “children” => […] } Each child is either a leaf condition { “field”, “operator”, “value” } or a group { “combinator”, “children” => […] }



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/lcp_ruby/search/query_language_parser.rb', line 79

def parse
  skip_whitespace
  return empty_tree if eof?

  tree = parse_or_expression
  skip_whitespace

  unless eof?
    error("Unexpected input at position #{@pos}")
  end

  result = normalize_ast(tree, depth: 1)

  # Ensure root is always a group node, not a bare leaf
  if result.is_a?(Hash) && result.key?("field")
    { "combinator" => "and", "children" => [ result ] }
  else
    result
  end
end