class JMESPath::Parser
@api private
Constants
- AFTER_DOT
- COLON_RBRACKET
- CURRENT_NODE
- NUM_COLON_RBRACKET
Public Class Methods
new(options = {})
click to toggle source
@option options [Lexer] :lexer
# File lib/jmespath/parser.rb, line 30 def initialize(options = {}) @lexer = options[:lexer] || Lexer.new end
Public Instance Methods
method_missing(method_name, *args)
click to toggle source
@api private
Calls superclass method
# File lib/jmespath/parser.rb, line 47 def method_missing(method_name, *args) if matches = method_name.match(/^(nud_|led_)(.*)/) raise Errors::SyntaxError, "unexpected token #{matches[2]}" else super end end
parse(expression)
click to toggle source
@param [String<JMESPath>] expression
# File lib/jmespath/parser.rb, line 35 def parse(expression) tokens = @lexer.tokenize(expression) stream = TokenStream.new(expression, tokens) result = expr(stream) if stream.token.type != Lexer::T_EOF raise Errors::SyntaxError, "expected :eof got #{stream.token.type}" else result end end
Private Instance Methods
expr(stream, rbp = 0)
click to toggle source
@param [TokenStream] stream @param [Integer] rbp Right binding power
# File lib/jmespath/parser.rb, line 59 def expr(stream, rbp = 0) left = send("nud_#{stream.token.type}", stream) while rbp < stream.token.binding_power left = send("led_#{stream.token.type}", stream, left) end left end
led_comparator(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 142 def led_comparator(stream, left) token = stream.token stream.next right = expr(stream) Nodes::Comparator.create(token.value, left, right) end
led_dot(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 149 def led_dot(stream, left) stream.next(match:AFTER_DOT) if stream.token.type == :star parse_wildcard_object(stream, left) else right = parse_dot(stream, Token::BINDING_POWER[:dot]) Nodes::Subexpression.new(left, right) end end
led_filter(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 159 def led_filter(stream, left) stream.next expression = expr(stream) if stream.token.type != Lexer::T_RBRACKET raise Errors::SyntaxError, 'expected a closing rbracket for the filter' end stream.next rhs = parse_projection(stream, Token::BINDING_POWER[Lexer::T_FILTER]) left ||= CURRENT_NODE right = Nodes::Condition.new(expression, rhs) Nodes::ArrayProjection.new(left, right) end
led_flatten(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 172 def led_flatten(stream, left) stream.next left = Nodes::Flatten.new(left) right = parse_projection(stream, Token::BINDING_POWER[:flatten]) Nodes::ArrayProjection.new(left, right) end
led_lbracket(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 179 def led_lbracket(stream, left) stream.next(match: Set.new([:number, :colon, :star])) type = stream.token.type if type == :number || type == :colon right = parse_array_index_expression(stream) Nodes::Subexpression.new(left, right) else parse_wildcard_array(stream, left) end end
led_lparen(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 190 def led_lparen(stream, left) args = [] name = left.name stream.next while stream.token.type != :rparen args << expr(stream, 0) if stream.token.type == :comma stream.next end end stream.next Nodes::Function.create(name, args) end
led_or(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 204 def led_or(stream, left) stream.next right = expr(stream, Token::BINDING_POWER[:or]) Nodes::Or.new(left, right) end
led_pipe(stream, left)
click to toggle source
# File lib/jmespath/parser.rb, line 210 def led_pipe(stream, left) stream.next right = expr(stream, Token::BINDING_POWER[:pipe]) Nodes::Pipe.new(left, right) end
nud_current(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 67 def nud_current(stream) stream.next CURRENT_NODE end
nud_expref(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 72 def nud_expref(stream) stream.next Nodes::Expression.new(expr(stream, 2)) end
nud_filter(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 77 def nud_filter(stream) led_filter(stream, CURRENT_NODE) end
nud_flatten(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 81 def nud_flatten(stream) led_flatten(stream, CURRENT_NODE) end
nud_identifier(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 85 def nud_identifier(stream) token = stream.token n = stream.next if n.type == :lparen Nodes::Function::FunctionName.new(token.value) else Nodes::Field.new(token.value) end end
nud_lbrace(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 95 def nud_lbrace(stream) valid_keys = Set.new([:quoted_identifier, :identifier]) stream.next(match:valid_keys) pairs = [] begin pairs << parse_key_value_pair(stream) if stream.token.type == :comma stream.next(match:valid_keys) end end while stream.token.type != :rbrace stream.next Nodes::MultiSelectHash.new(pairs) end
nud_lbracket(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 109 def nud_lbracket(stream) stream.next type = stream.token.type if type == :number || type == :colon parse_array_index_expression(stream) elsif type == :star && stream.lookahead(1).type == :rbracket parse_wildcard_array(stream) else parse_multi_select_list(stream) end end
nud_literal(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 121 def nud_literal(stream) value = stream.token.value stream.next Nodes::Literal.new(value) end
nud_quoted_identifier(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 127 def nud_quoted_identifier(stream) token = stream.token next_token = stream.next if next_token.type == :lparen msg = 'quoted identifiers are not allowed for function names' raise Errors::SyntaxError, msg else Nodes::Field.new(token[:value]) end end
nud_star(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 138 def nud_star(stream) parse_wildcard_object(stream, CURRENT_NODE) end
parse_array_index_expression(stream)
click to toggle source
parse array index expressions, for example [0], [1:2:3], etc.
# File lib/jmespath/parser.rb, line 217 def parse_array_index_expression(stream) pos = 0 parts = [nil, nil, nil] expected = NUM_COLON_RBRACKET begin if stream.token.type == Lexer::T_COLON pos += 1 expected = NUM_COLON_RBRACKET elsif stream.token.type == Lexer::T_NUMBER parts[pos] = stream.token.value expected = COLON_RBRACKET end stream.next(match: expected) end while stream.token.type != Lexer::T_RBRACKET stream.next # consume the closing bracket if pos == 0 # no colons found, this is a single index extraction Nodes::Index.new(parts[0]) elsif pos > 2 raise Errors::SyntaxError, 'invalid array slice syntax: too many colons' else Nodes::ArrayProjection.new( Nodes::Slice.new(*parts), parse_projection(stream, Token::BINDING_POWER[Lexer::T_STAR]) ) end end
parse_dot(stream, binding_power)
click to toggle source
# File lib/jmespath/parser.rb, line 248 def parse_dot(stream, binding_power) if stream.token.type == :lbracket stream.next parse_multi_select_list(stream) else expr(stream, binding_power) end end
parse_key_value_pair(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 257 def parse_key_value_pair(stream) key = stream.token.value stream.next(match:Set.new([:colon])) stream.next Nodes::MultiSelectHash::KeyValuePair.new(key, expr(stream)) end
parse_multi_select_list(stream)
click to toggle source
# File lib/jmespath/parser.rb, line 264 def parse_multi_select_list(stream) nodes = [] begin nodes << expr(stream) if stream.token.type == :comma stream.next if stream.token.type == :rbracket raise Errors::SyntaxError, 'expression epxected, found rbracket' end end end while stream.token.type != :rbracket stream.next Nodes::MultiSelectList.new(nodes) end
parse_projection(stream, binding_power)
click to toggle source
# File lib/jmespath/parser.rb, line 279 def parse_projection(stream, binding_power) type = stream.token.type if stream.token.binding_power < 10 CURRENT_NODE elsif type == :dot stream.next(match:AFTER_DOT) parse_dot(stream, binding_power) elsif type == :lbracket || type == :filter expr(stream, binding_power) else raise Errors::SyntaxError, 'syntax error after projection' end end
parse_wildcard_array(stream, left = nil)
click to toggle source
# File lib/jmespath/parser.rb, line 293 def parse_wildcard_array(stream, left = nil) stream.next(match:Set.new([:rbracket])) stream.next left ||= CURRENT_NODE right = parse_projection(stream, Token::BINDING_POWER[:star]) Nodes::ArrayProjection.new(left, right) end
parse_wildcard_object(stream, left = nil)
click to toggle source
# File lib/jmespath/parser.rb, line 301 def parse_wildcard_object(stream, left = nil) stream.next left ||= CURRENT_NODE right = parse_projection(stream, Token::BINDING_POWER[:star]) Nodes::ObjectProjection.new(left, right) end