Class: JsonPath::Parser
Overview
Parser parses and evaluates an expression passed to @_current_node.
Constant Summary collapse
- REGEX =
/\A\/(.+)\/([imxnesu]*)\z|\A%r{(.+)}([imxnesu]*)\z/
Instance Method Summary collapse
-
#construct_expression_map(exps) ⇒ Object
Construct a map for which the keys are the expressions and the values are the corresponding parsed results.
-
#initialize(node, options) ⇒ Parser
constructor
A new instance of Parser.
-
#parse(exp) ⇒ Object
parse will parse an expression in the following way.
-
#parse_exp(exp) ⇒ Object
Using a scanner break down the individual expressions and determine if there is a match in the JSON for it or not.
Methods included from Dig
#dig, #dig_as_hash, #dig_one, #yield_if_diggable
Constructor Details
#initialize(node, options) ⇒ Parser
Returns a new instance of Parser.
12 13 14 15 16 |
# File 'lib/jsonpath/parser.rb', line 12 def initialize(node, ) @_current_node = node @_expr_map = {} @options = end |
Instance Method Details
#construct_expression_map(exps) ⇒ Object
Construct a map for which the keys are the expressions and the values are the corresponding parsed results. Exp.: =~ /herman|lukyanenko/i)”=>0 “@[‘isTrue’]”=>true
44 45 46 47 48 49 50 51 |
# File 'lib/jsonpath/parser.rb', line 44 def construct_expression_map(exps) exps.each_with_index do |item, _index| next if item == '&&' || item == '||' item = item.strip.gsub(/\)*$/, '').gsub(/^\(*/, '') @_expr_map[item] = parse_exp(item) end end |
#parse(exp) ⇒ Object
parse will parse an expression in the following way. Split the expression up into an array of legs for && and || operators. Parse this array into a map for which the keys are the parsed legs of the split. This map is then used to replace the expression with their corresponding boolean or numeric value. This might look something like this: ((false || false) && (false || true)) Once this string is assembled… we proceed to evaluate from left to right. The above string is broken down like this: (false && (false || true)) (false && true) false
29 30 31 32 33 34 35 36 37 |
# File 'lib/jsonpath/parser.rb', line 29 def parse(exp) exps = exp.split(/(&&)|(\|\|)/) construct_expression_map(exps) @_expr_map.each { |k, v| exp.sub!(k, v.to_s) } raise ArgumentError, "unmatched parenthesis in expression: #{exp}" unless check_parenthesis_count(exp) exp = parse_parentheses(exp) while exp.include?('(') bool_or_exp(exp) end |
#parse_exp(exp) ⇒ Object
Using a scanner break down the individual expressions and determine if there is a match in the JSON for it or not.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/jsonpath/parser.rb', line 55 def parse_exp(exp) exp = exp.sub(/@/, '').gsub(/^\(/, '').gsub(/\)$/, '').tr('"', '\'').strip exp.scan(/^\[(\d+)\]/) do |i| next if i.empty? index = Integer(i[0]) raise ArgumentError, 'Node does not appear to be an array.' unless @_current_node.is_a?(Array) raise ArgumentError, "Index out of bounds for nested array. Index: #{index}" if @_current_node.size < index @_current_node = @_current_node[index] # Remove the extra '' and the index. exp = exp.gsub(/^\[\d+\]|\[''\]/, '') end scanner = StringScanner.new(exp) elements = [] until scanner.eos? if (t = scanner.scan(/\['((?<!\\).)+'\]/)) elements << t.gsub(/^\['(.*)'\]$/, '\1') elsif (t = scanner.scan(/\.[a-zA-Z0-9_]+[?]?/)) elements << t.gsub(/[\[\]'.]|\s+/, '') elsif (t = scanner.scan(/(\s+)?[<>=!\-+][=~]?(\s+)?/)) operator = t elsif (t = scanner.scan(/(\s+)?'?.*'?(\s+)?/)) # If we encounter a node which does not contain `'` it means # that we are dealing with a boolean type. operand = if t == 'true' true elsif t == 'false' false elsif operator.to_s.strip == '=~' parse_regex(t) else t.gsub(%r{^'|'$}, '').strip end elsif (t = scanner.scan(/\/\w+\//)) elsif (t = scanner.scan(/.*/)) raise "Could not process symbol: #{t}" end end el = if elements.empty? @_current_node elsif @_current_node.is_a?(Hash) dig(@_current_node, *elements) else elements.inject(@_current_node, &:__send__) end return (el ? true : false) if el.nil? || operator.nil? el = Float(el) rescue el operand = Float(operand) rescue operand el.__send__(operator.strip, operand) end |