Module: ScopedSearch::QueryLanguage::Parser

Included in:
Compiler
Defined in:
lib/scoped_search/query_language/parser.rb

Constant Summary collapse

DEFAULT_SEQUENCE_OPERATOR =
:and
LOGICAL_INFIX_OPERATORS =
[:and, :or]
LOGICAL_PREFIX_OPERATORS =
[:not]
NULL_PREFIX_OPERATORS =
[:null, :notnull]
COMPARISON_OPERATORS =
[:eq, :ne, :gt, :gte, :lt, :lte, :like, :unlike]
ALL_INFIX_OPERATORS =
LOGICAL_INFIX_OPERATORS + COMPARISON_OPERATORS
ALL_PREFIX_OPERATORS =
LOGICAL_PREFIX_OPERATORS + COMPARISON_OPERATORS + NULL_PREFIX_OPERATORS

Instance Method Summary collapse

Instance Method Details

#parseObject

Start the parsing process by parsing an expression sequence



13
14
15
16
# File 'lib/scoped_search/query_language/parser.rb', line 13

def parse
  @tokens = tokenize   
  parse_expression_sequence(true).simplify
end

#parse_comparisonObject

Parses a comparison



64
65
66
67
# File 'lib/scoped_search/query_language/parser.rb', line 64

def parse_comparison
  next_token if peek_token == :comma # skip comma      
  return (String === peek_token) ? parse_infix_comparison : parse_prefix_comparison
end

#parse_expression_sequence(initial = false) ⇒ Object

Parses a sequence of expressions



19
20
21
22
23
24
25
# File 'lib/scoped_search/query_language/parser.rb', line 19

def parse_expression_sequence(initial = false)
  expressions = []
  next_token if !initial && peek_token == :lparen # skip staring :lparen    
  expressions << parse_logical_expression until peek_token.nil? || peek_token == :rparen
  next_token if !initial && peek_token == :rparen # skip final :rparen
  return ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(DEFAULT_SEQUENCE_OPERATOR, expressions)
end

#parse_infix_comparisonObject

Parses an infix expression, i.e. <field> <operator> <value>



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/scoped_search/query_language/parser.rb', line 75

def parse_infix_comparison
  lhs = parse_value
  return case peek_token
    when nil
      lhs
    when :comma
      next_token # skip comma
      lhs
    else
      if COMPARISON_OPERATORS.include?(peek_token)
        comparison_operator = next_token
        rhs = parse_value
        ScopedSearch::QueryLanguage::AST::OperatorNode.new(comparison_operator, [lhs, rhs])
      else
        lhs
      end
  end
end

#parse_logical_expressionObject

Parses a logical expression.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/scoped_search/query_language/parser.rb', line 29

def parse_logical_expression
  lhs = case peek_token
    when nil;             nil
    when :lparen;         parse_expression_sequence
    when :not;            parse_logical_not_expression
    when :null, :notnull; parse_null_expression
    else;                 parse_comparison
  end

  if LOGICAL_INFIX_OPERATORS.include?(peek_token)
    operator = next_token
    rhs = parse_logical_expression
    ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(operator, [lhs, rhs])
  else
    lhs
  end
end

#parse_logical_not_expressionObject

Parses a NOT expression



48
49
50
51
52
53
54
55
56
# File 'lib/scoped_search/query_language/parser.rb', line 48

def parse_logical_not_expression
  next_token # = skip NOT operator
  negated_expression = case peek_token
    when :not;    parse_logical_not_expression 
    when :lparen; parse_expression_sequence
    else          parse_comparison
  end
  return ScopedSearch::QueryLanguage::AST::OperatorNode.new(:not, [negated_expression])
end

#parse_null_expressionObject

Parses a set? or null? expression



59
60
61
# File 'lib/scoped_search/query_language/parser.rb', line 59

def parse_null_expression
  return ScopedSearch::QueryLanguage::AST::OperatorNode.new(next_token, [parse_value])
end

#parse_prefix_comparisonObject

Parses a prefix comparison, i.e. without an explicit field: <operator> <value>



70
71
72
# File 'lib/scoped_search/query_language/parser.rb', line 70

def parse_prefix_comparison
  return ScopedSearch::QueryLanguage::AST::OperatorNode.new(next_token, [parse_value])
end

#parse_valueObject

Parses a single value. This can either be a constant value or a field name.



96
97
98
99
# File 'lib/scoped_search/query_language/parser.rb', line 96

def parse_value
  raise ScopedSearch::QueryNotSupported, "Value expected but found #{peek_token.inspect}" unless String === peek_token
  ScopedSearch::QueryLanguage::AST::LeafNode.new(next_token)
end