class JMESPath::Parser

@api private

Constants

AFTER_DOT

@api private

CURRENT_NODE

Public Class Methods

new(options = {}) click to toggle source

@option options [Lexer] :lexer

# File lib/jmespath/parser.rb, line 21
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 37
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 26
def parse(expression)
  stream = TokenStream.new(expression, @lexer.tokenize(expression))
  result = expr(stream)
  if stream.token.type != :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 49
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 137
def led_comparator(stream, left)
  token = stream.token
  stream.next
  {
    type: :comparator,
    relation: token.value,
    children: [
      left,
      expr(stream),
    ]
  }
end
led_dot(stream, left) click to toggle source
# File lib/jmespath/parser.rb, line 150
def led_dot(stream, left)
  stream.next(match:AFTER_DOT)
  if stream.token.type == :star
    parse_wildcard_object(stream, left)
  else
    {
      type: :subexpression,
      children: [
        left,
        parse_dot(stream, Token::BINDING_POWER[:dot])
      ]
    }
  end
end
led_filter(stream, left) click to toggle source
# File lib/jmespath/parser.rb, line 165
def led_filter(stream, left)
  stream.next
  expression = expr(stream)
  if stream.token.type != :rbracket
    raise Errors::SyntaxError, 'expected a closing rbracket for the filter'
  end
  stream.next
  rhs = parse_projection(stream, Token::BINDING_POWER[:filter])
  {
    type: :projection,
    from: :array,
    children: [
      left ? left : CURRENT_NODE,
      {
        type: :condition,
        children: [expression, rhs],
      }
    ]
  }
end
led_flatten(stream, left) click to toggle source
# File lib/jmespath/parser.rb, line 186
def led_flatten(stream, left)
  stream.next
  {
    type: :projection,
    from: :array,
    children: [
      { type: :flatten, children: [left] },
      parse_projection(stream, Token::BINDING_POWER[:flatten])
    ]
  }
end
led_lbracket(stream, left) click to toggle source
# File lib/jmespath/parser.rb, line 198
def led_lbracket(stream, left)
  stream.next(match: Set.new([:number, :colon, :star]))
  type = stream.token.type
  if type == :number || type == :colon
    {
      type: :subexpression,
      children: [
        left,
        parse_array_index_expression(stream)
      ]
    }
  else
    parse_wildcard_array(stream, left)
  end
end
led_lparen(stream, left) click to toggle source
# File lib/jmespath/parser.rb, line 214
def led_lparen(stream, left)
  args = []
  name = left[:key]
  stream.next
  while stream.token.type != :rparen
    args << expr(stream, 0)
    if stream.token.type == :comma
      stream.next
    end
  end
  stream.next
  {
    type: :function,
    fn: name,
    children: args,
  }
end
led_or(stream, left) click to toggle source
# File lib/jmespath/parser.rb, line 232
def led_or(stream, left)
  stream.next
  {
    type: :or,
    children: [left, expr(stream, Token::BINDING_POWER[:or])]
  }
end
led_pipe(stream, left) click to toggle source
# File lib/jmespath/parser.rb, line 240
def led_pipe(stream, left)
  stream.next
  {
    type: :pipe,
    children: [left, expr(stream, Token::BINDING_POWER[:pipe])],
  }
end
nud_current(stream) click to toggle source
# File lib/jmespath/parser.rb, line 57
def nud_current(stream)
  stream.next
  CURRENT_NODE
end
nud_expref(stream) click to toggle source
# File lib/jmespath/parser.rb, line 62
def nud_expref(stream)
  stream.next
  {
    type: :expression,
    children: [expr(stream, 2)]
  }
end
nud_filter(stream) click to toggle source
# File lib/jmespath/parser.rb, line 70
def nud_filter(stream)
  led_filter(stream, CURRENT_NODE)
end
nud_flatten(stream) click to toggle source
# File lib/jmespath/parser.rb, line 74
def nud_flatten(stream)
  led_flatten(stream, CURRENT_NODE)
end
nud_identifier(stream) click to toggle source
# File lib/jmespath/parser.rb, line 78
def nud_identifier(stream)
  token = stream.token
  stream.next
  { type: :field, key: token.value }
end
nud_lbrace(stream) click to toggle source
# File lib/jmespath/parser.rb, line 84
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
  {
    type: :multi_select_hash,
    children: pairs
  }
end
nud_lbracket(stream) click to toggle source
# File lib/jmespath/parser.rb, line 101
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 113
def nud_literal(stream)
  value = stream.token.value
  stream.next
  {
    type: :literal,
    value: value
  }
end
nud_quoted_identifier(stream) click to toggle source
# File lib/jmespath/parser.rb, line 122
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
    { type: :field, key: token[:value] }
  end
end
nud_star(stream) click to toggle source
# File lib/jmespath/parser.rb, line 133
def nud_star(stream)
  parse_wildcard_object(stream, CURRENT_NODE)
end
parse_array_index_expression(stream) click to toggle source
# File lib/jmespath/parser.rb, line 248
def parse_array_index_expression(stream)
  pos = 0
  parts = [nil, nil, nil]
  begin
    if stream.token.type == :colon
      pos += 1
    else
      parts[pos] = stream.token.value
    end
    stream.next(match:Set.new([:number, :colon, :rbracket]))
  end while stream.token.type != :rbracket
  stream.next
  if pos == 0
    { type: :index, index: parts[0] }
  elsif pos > 2
    raise Errors::SyntaxError, 'invalid array slice syntax: too many colons'
  else
    { type: :slice, args: parts }
  end
end
parse_dot(stream, binding_power) click to toggle source
# File lib/jmespath/parser.rb, line 269
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 278
def parse_key_value_pair(stream)
  key = stream.token.value
  stream.next(match:Set.new([:colon]))
  stream.next
  {
    type: :key_value_pair,
    key: key,
    children: [expr(stream)]
  }
end
parse_multi_select_list(stream) click to toggle source
# File lib/jmespath/parser.rb, line 289
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
  {
    type: :multi_select_list,
    children: nodes
  }
end
parse_projection(stream, binding_power) click to toggle source
# File lib/jmespath/parser.rb, line 307
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 321
def parse_wildcard_array(stream, left = nil)
  stream.next(match:Set.new([:rbracket]))
  stream.next
  {
    type: :projection,
    from: :array,
    children: [
      left ? left : CURRENT_NODE,
      parse_projection(stream, Token::BINDING_POWER[:star])
    ]
  }
end
parse_wildcard_object(stream, left = nil) click to toggle source
# File lib/jmespath/parser.rb, line 334
def parse_wildcard_object(stream, left = nil)
  stream.next
  {
    type: :projection,
    from: :object,
    children: [
      left ? left : CURRENT_NODE,
      parse_projection(stream, Token::BINDING_POWER[:star])
    ]
  }
end