Class: Janeway::Parser

Inherits:
Object
  • Object
show all
Includes:
Functions
Defined in:
lib/janeway/parser.rb

Overview

Transform a list of tokens into an Abstract Syntax Tree

Constant Summary collapse

UNARY_OPERATORS =
%w[! -].freeze
BINARY_OPERATORS =
%w[== != > < >= <= ,].freeze
LOGICAL_OPERATORS =
%w[&& ||].freeze
UNARY_OPERATOR_SET =

O(1)-lookup Hash companions to the arrays above, plus the union used by determine_infix_function. Building these inline in the hot path allocated a fresh Array per token; hoisting eliminates it.

UNARY_OPERATORS.each_with_object({}) { |op, h| h[op] = true }.freeze
BINARY_OPERATOR_SET =
BINARY_OPERATORS.each_with_object({}) { |op, h| h[op] = true }.freeze
INFIX_OPERATOR_SET =
(BINARY_OPERATORS + LOGICAL_OPERATORS).each_with_object({}) { |op, h| h[op] = true }.freeze
PARSE_METHOD_TYPES =
%I[identifier number string true false nil function if while root current_node]
.each_with_object({}) { |t, h| h[t] = true }.freeze
TERMINATOR_TYPES =
%I[child_end union eof].each_with_object({}) { |t, h| h[t] = true }.freeze
PARSE_DISPATCH_BY_TYPE =

O(1) dispatch table for determine_parsing_function's type-driven cases. Sits next to the PARSE_METHOD_TYPES set (the other type branch) so both are visible when adding a new token type.

{
  group_start: :parse_grouped_expr,
  :"\n" => :parse_terminator,
  eof: :parse_terminator,
  child_start: :parse_child_segment,
  dot: :parse_dot_notation,
  descendants: :parse_descendant_segment,
  filter: :parse_filter_selector,
  null: :parse_null,
}.freeze
LOWEST_PRECEDENCE =
0
OPERATOR_PRECEDENCE =
{
  ',' => 0,
  '||' => 1,
  '&&' => 2,
  '==' => 3,
  '!=' => 3,
  '>' => 4,
  '<' => 4,
  '>=' => 4,
  '<=' => 4,
  '(' => 8,
}.freeze

Constants included from Functions

Functions::REGISTRY

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Functions

build_regex_body, #parse_function_count, #parse_function_length, #parse_function_match, #parse_function_parameter, #parse_function_search, #parse_function_value, translate_iregex_to_ruby_regex

Constructor Details

#initialize(tokens, jsonpath) ⇒ Parser

Returns a new instance of Parser.

Parameters:

  • tokens (Array<Token>)
  • jsonpath (String)

    original jsonpath query string



70
71
72
73
74
# File 'lib/janeway/parser.rb', line 70

def initialize(tokens, jsonpath)
  @tokens = tokens
  @next_p = 0
  @jsonpath = jsonpath
end

Instance Attribute Details

#tokensObject (readonly)

Returns the value of attribute tokens.



12
13
14
# File 'lib/janeway/parser.rb', line 12

def tokens
  @tokens
end

Class Method Details

.parse(jsonpath) ⇒ Query

Parameters:

  • jsonpath (String)

    jsonpath query to be lexed and parsed

Returns:

Raises:

  • (ArgumentError)


61
62
63
64
65
66
# File 'lib/janeway/parser.rb', line 61

def self.parse(jsonpath)
  raise ArgumentError, "expect jsonpath string, got #{jsonpath.inspect}" unless jsonpath.is_a?(String)

  tokens = Janeway::Lexer.lex(jsonpath)
  new(tokens, jsonpath).parse
end

Instance Method Details

#parseQuery

Parse the token list and create an Abstract Syntax Tree

Returns:



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/janeway/parser.rb', line 78

def parse
  consume
  raise err('JsonPath queries must start with root identifier "$"') unless current.type == :root

  root_node = parse_root
  consume
  unless current.type == :eof
    remaining = tokens[@next_p..].map(&:lexeme).join
    raise err("Unrecognized expressions after query: #{remaining}")
  end

  Query.new(root_node, @jsonpath)
end