Class: Janeway::Interpreters::BinaryOperatorInterpreter

Inherits:
Base
  • Object
show all
Defined in:
lib/janeway/interpreters/binary_operator_interpreter.rb

Overview

Interprets a binary operator within filter selector.

Constant Summary

Constants inherited from Base

Janeway::Interpreters::Base::NOTHING

Instance Attribute Summary

Attributes inherited from Base

#next, #node

Instance Method Summary collapse

Methods inherited from Base

#as_json, #selector, #to_s, #type

Constructor Details

#initialize(operator) ⇒ BinaryOperatorInterpreter

Set up the internal interpreter chain for the BinaryOperator.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 12

def initialize(operator)
  super
  @left = TreeConstructor.ast_node_to_interpreter(operator.left)
  @right = TreeConstructor.ast_node_to_interpreter(operator.right)

  # Bind the per-op behavior at construction time to avoid dispatching
  # on operator.name and allocating a symbol on every candidate value.
  case operator.name
  when :and, :or
    @extract_single_value = false
  when :equal, :not_equal, :less_than, :greater_than, :less_than_or_equal, :greater_than_or_equal
    @extract_single_value = true
  else
    raise "Don't know how to handle binary operator #{operator.name.inspect}"
  end
  @op = method(:"interpret_#{operator.name}")
end

Instance Method Details

#interpret(input, parent, root, _path = nil) ⇒ Object

The binary operators are all comparison operators that test equality.

  • boolean values specified in the query
  • JSONPath expressions which must be evaluated

After a JSONPath expression is evaluated, it results in a node list. This may contain literal values or nodes, whose value must be extracted before comparison.

Parameters:

  • input (Array, Hash)

    the results of processing so far

  • parent (Array, Hash)

    parent of the input object

  • root (Array, Hash)

    the entire input

  • _path (Array) (defaults to: nil)

    ignored



42
43
44
45
46
47
48
49
50
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 42

def interpret(input, parent, root, _path = nil)
  lhs = @left.interpret(input, parent, root, [])
  rhs = @right.interpret(input, parent, root, [])
  if @extract_single_value
    lhs = to_single_value(lhs)
    rhs = to_single_value(rhs)
  end
  @op.call(lhs, rhs)
end

#interpret_and(lhs, rhs) ⇒ Object

Interpret && operator May receive node lists, in which case empty node list == false



98
99
100
101
102
103
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 98

def interpret_and(lhs, rhs)
  # non-empty array is already truthy, so that works properly without conversion
  lhs = false if lhs == []
  rhs = false if rhs == []
  lhs && rhs
end

#interpret_boolean(boolean, _input) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


148
149
150
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 148

def interpret_boolean(boolean, _input)
  boolean.value
end

#interpret_equal(lhs, rhs) ⇒ Object

Parameters:

  • lhs (String, Numeric, Symbol, nil)

    string/number/null or NOTHING

  • rhs (String, Numeric, Symbol, nil)

    string/number/null or NOTHING



87
88
89
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 87

def interpret_equal(lhs, rhs)
  lhs == rhs
end

#interpret_greater_than(lhs, rhs) ⇒ Object



132
133
134
135
136
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 132

def interpret_greater_than(lhs, rhs)
  lhs > rhs
rescue StandardError
  false
end

#interpret_greater_than_or_equal(lhs, rhs) ⇒ Object



138
139
140
141
142
143
144
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 138

def interpret_greater_than_or_equal(lhs, rhs)
  return true if interpret_equal(lhs, rhs)

  lhs > rhs
rescue StandardError
  false
end

#interpret_less_than(lhs, rhs) ⇒ Object



114
115
116
117
118
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 114

def interpret_less_than(lhs, rhs)
  lhs < rhs
rescue StandardError
  false
end

#interpret_less_than_or_equal(lhs, rhs) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 120

def interpret_less_than_or_equal(lhs, rhs)
  # Must be done in 2 comparisons, because the equality comparison is
  # valid for many types that do not support the < operator.
  return true if interpret_equal(lhs, rhs)

  lhs < rhs
rescue StandardError
  # This catches type mismatches like {} <= 1
  # RFC says that both < and > return false for such comparisons
  false
end

#interpret_not_equal(lhs, rhs) ⇒ Object

Interpret != operator



92
93
94
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 92

def interpret_not_equal(lhs, rhs)
  !interpret_equal(lhs, rhs)
end

#interpret_null(_null, _input) ⇒ Object

Parameters:

  • _null (AST::Null)

    ignored

  • _input (Object)

    ignored



166
167
168
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 166

def interpret_null(_null, _input)
  nil
end

#interpret_number(number, _input) ⇒ Integer, Float

Parameters:

Returns:

  • (Integer, Float)


154
155
156
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 154

def interpret_number(number, _input)
  number.value
end

#interpret_or(lhs, rhs) ⇒ Object

Interpret || operator May receive node lists, in which case empty node list == false



107
108
109
110
111
112
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 107

def interpret_or(lhs, rhs)
  # non-empty array is already truthy, so that works properly without conversion
  lhs = false if lhs.is_a?(Array) && lhs.empty?
  rhs = false if rhs.is_a?(Array) && rhs.empty?
  lhs || rhs
end

#interpret_string_type(string, _input) ⇒ String

Parameters:

Returns:

  • (String)


160
161
162
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 160

def interpret_string_type(string, _input)
  string.value
end

#to_single_value(result) ⇒ String, ...

Convert an expression result into a single value suitable for use by a comparison operator. Expression result may already ba single value (ie. from a literal like AST::String) or it may be a node list from a singular query.

Any node list is guaranteed not to contain multiple values because the expression that produce it was already verified to be a singular query.

Parameters:

  • result (Object)

    node list, or single value of basic type

Returns:

  • (String, Integer, nil)


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/janeway/interpreters/binary_operator_interpreter.rb', line 67

def to_single_value(result)
  # Return basic types (ie. from AST::Number, AST::StringType, AST::Null)
  return result unless result.is_a?(Array)

  # Node lists are returned by Selectors, ChildSegment, DescendantSegment.
  #
  # For a comparison operator, an empty node list represents a missing element.
  # This must not match any literal value (including null/nil) but must match another missing value.
  return NOTHING if result.empty?

  # The parsing stage has already verified that both the left and right
  # expressions evaluate to a single value. Both are either a literal or a singular query.
  # So, this check will never raise an error.
  raise 'node list contains multiple elements but this is a comparison' unless result.size == 1

  result.first # Return the only node in the node list
end