module ScopedSearch::QueryLanguage::Parser

The Parser module adss methods to the query language compiler that transform a string into an abstract syntax tree, which can be used for query generation.

This module depends on the tokeinzer module to transform the string into a stream of tokens, which is more appropriate for parsing. The parser itself is a LL(1) recursive descent parser.

Constants

ALL_INFIX_OPERATORS
ALL_PREFIX_OPERATORS
COMPARISON_OPERATORS
DEFAULT_SEQUENCE_OPERATOR
LOGICAL_INFIX_OPERATORS
LOGICAL_PREFIX_OPERATORS
NULL_PREFIX_OPERATORS

Public Instance Methods

parse() click to toggle source

Start the parsing process by parsing an expression sequence

   # File lib/scoped_search/query_language/parser.rb
19 def parse
20   @tokens = tokenize
21   while @tokens.last.is_a?(Symbol) do
22     @tokens.delete_at(@tokens.size - 1)
23   end
24   parse_expression_sequence(true).simplify
25 end
parse_comparison() click to toggle source

Parses a comparison

   # File lib/scoped_search/query_language/parser.rb
76 def parse_comparison
77   next_token if peek_token == :comma # skip comma
78   return (String === peek_token) ? parse_infix_comparison : parse_prefix_comparison
79 end
parse_expression_sequence(root_node = false) click to toggle source

Parses a sequence of expressions

   # File lib/scoped_search/query_language/parser.rb
28 def parse_expression_sequence(root_node = false)
29   expressions = []
30 
31   next_token if !root_node && peek_token == :lparen # skip starting :lparen
32   expressions << parse_logical_expression until peek_token.nil? || peek_token == :rparen
33   next_token if !root_node && peek_token == :rparen # skip final :rparen
34   
35   return ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(DEFAULT_SEQUENCE_OPERATOR, expressions, root_node)
36 end
parse_infix_comparison() click to toggle source

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

    # File lib/scoped_search/query_language/parser.rb
 87 def parse_infix_comparison
 88   lhs = parse_value
 89   return case peek_token
 90     when nil
 91       lhs
 92     when :comma
 93       next_token # skip comma
 94       lhs
 95     else
 96       if COMPARISON_OPERATORS.include?(peek_token)
 97         comparison_operator = next_token
 98         rhs = parse_value
 99         ScopedSearch::QueryLanguage::AST::OperatorNode.new(comparison_operator, [lhs, rhs])
100       else
101         lhs
102       end
103   end
104 end
parse_logical_expression() click to toggle source

Parses a logical expression.

   # File lib/scoped_search/query_language/parser.rb
39 def parse_logical_expression
40   lhs = case peek_token
41     when nil;             nil
42     when :lparen;         parse_expression_sequence
43     when :not;            parse_logical_not_expression
44     when :null, :notnull; parse_null_expression
45     else;                 parse_comparison
46   end
47 
48   if LOGICAL_INFIX_OPERATORS.include?(peek_token)
49     operator = next_token
50     rhs = parse_logical_expression
51     ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(operator, [lhs, rhs])
52   else
53     lhs
54   end
55 end
parse_logical_not_expression() click to toggle source

Parses a NOT expression

   # File lib/scoped_search/query_language/parser.rb
58 def parse_logical_not_expression
59   next_token # = skip NOT operator
60   negated_expression = case peek_token
61     when :not;    parse_logical_not_expression
62     when :lparen; parse_expression_sequence
63     else          parse_comparison
64   end
65 
66   raise ScopedSearch::QueryNotSupported, "No operands found" if negated_expression.empty?
67   return ScopedSearch::QueryLanguage::AST::OperatorNode.new(:not, [negated_expression])
68 end
parse_multiple_values() click to toggle source

Parse values in the format (val, val, val)

    # File lib/scoped_search/query_language/parser.rb
107 def parse_multiple_values
108   next_token if  peek_token == :lparen #skip :lparen
109   value = []
110   value << current_token if String === next_token until peek_token.nil? || peek_token == :rparen
111   next_token if peek_token == :rparen  # consume the :rparen
112   value.join(',')
113 end
parse_null_expression() click to toggle source

Parses a set? or null? expression

   # File lib/scoped_search/query_language/parser.rb
71 def parse_null_expression
72   return ScopedSearch::QueryLanguage::AST::OperatorNode.new(next_token, [parse_value])
73 end
parse_prefix_comparison() click to toggle source

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

   # File lib/scoped_search/query_language/parser.rb
82 def parse_prefix_comparison
83   return ScopedSearch::QueryLanguage::AST::OperatorNode.new(next_token, [parse_value])
84 end
parse_value() click to toggle source

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

    # File lib/scoped_search/query_language/parser.rb
116 def parse_value
117   if String === peek_token
118     ScopedSearch::QueryLanguage::AST::LeafNode.new(next_token)
119   elsif ([:in, :notin].include? current_token)
120     value = parse_multiple_values()
121     ScopedSearch::QueryLanguage::AST::LeafNode.new(value)
122   else
123     raise ScopedSearch::QueryNotSupported, "Value expected but found #{peek_token.inspect}"
124   end
125 end