Class: Janeway::AST::ArraySliceSelector

Inherits:
Selector show all
Defined in:
lib/janeway/ast/array_slice_selector.rb

Overview

An array slice selects a series of elements from an array.

It accepts a start and end positions, and a step value that define the range to select. All of these are optional.

ArraySliceSelector needs to store “default” arguments differently from “explicit” arguments, since they’re interpreted differently.

Examples:

$[1:3]
$[5:]
$[1:5:2]
$[5:1:-2]
$[::-1]
$[:]

Instance Attribute Summary

Attributes inherited from Selector

#next

Attributes inherited from Expression

#next, #value

Instance Method Summary collapse

Methods inherited from Selector

#==

Methods inherited from Expression

#indented, #literal?, #singular_query?, #type

Constructor Details

#initialize(start = nil, end_ = nil, step = nil) ⇒ ArraySliceSelector

Returns a new instance of ArraySliceSelector.

Parameters:

  • start (Integer, nil) (defaults to: nil)
  • end_ (Integer, nil) (defaults to: nil)
  • step (Integer, nil) (defaults to: nil)


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/janeway/ast/array_slice_selector.rb', line 28

def initialize(start = nil, end_ = nil, step = nil)
  super(nil)

  # Check arguments
  [start, end_, step].each do |arg|
    unless [NilClass, Integer].include?(arg.class)
      raise Error, "Array slice selector index must be integer or nothing, got #{arg.inspect}"
    end
    next unless arg

    # Check integer size limits
    raise Error, "Array slice selector value too small: #{arg.inspect}" if arg < INTEGER_MIN
    raise Error, "Array slice selector value too large: #{arg.inspect}" if arg > INTEGER_MAX
  end

  # Nil values are kept to indicate that the "default" values is to be used.
  # The interpreter selects the actual values.
  @start = start
  @end = end_
  @step = step
end

Instance Method Details

#bounds(start, end_, step, len) ⇒ Object

IETF: Slice expression parameters start and end are used to derive slice bounds lower and upper. The direction of the iteration, defined by the sign of step, determines which of the parameters is the lower bound and which is the upper bound:¶

Parameters:

  • start (Integer)

    start index, normalized

  • end_ (Integer)

    end index, normalized

  • step (Integer)

    step value

  • len (Integer)

    length of input array

See Also:



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/janeway/ast/array_slice_selector.rb', line 124

def bounds(start, end_, step, len)
  n_start = normalize(start, len)
  n_end = normalize(end_, len)

  if step >= 0
    lower = n_start.clamp(0, len)
    upper = n_end.clamp(0, len)
  else
    upper = n_start.clamp(-1, len - 1)
    lower = n_end.clamp(-1, len - 1)
  end

  [lower, upper]
end

#calculate_index_values(input_size) ⇒ Object

Assign lower and upper bounds to instance variables, based on the input array size.

Parameters:

  • input_size (Integer)

See Also:



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/janeway/ast/array_slice_selector.rb', line 86

def calculate_index_values(input_size)
  if step >= 0
    start = @start || 0
    end_ = @end || input_size
  else
    start = @start || (input_size - 1)
    end_ = @end || ((-1 * input_size) - 1)
  end

  bounds(start, end_, step, input_size)
end

#lower_index(input_size) ⇒ Integer

Return the end index to use for stepping through the array, based on a specified array size End index is calculated to omit the final index value, as per the RFC.

Parameters:

  • input_size (Integer)

Returns:

  • (Integer)


78
79
80
# File 'lib/janeway/ast/array_slice_selector.rb', line 78

def lower_index(input_size)
  calculate_index_values(input_size).first
end

#normalize(index, len) ⇒ Object

IETF: Slice expression parameters start and end are not directly usable as slice bounds and must first be normalized.

Parameters:

  • index (Integer)
  • len (Integer)


109
110
111
112
113
# File 'lib/janeway/ast/array_slice_selector.rb', line 109

def normalize(index, len)
  return index if index >= 0

  len + index
end

#stepInteger

Return the step size to use for stepping through the array. Defaults to 1 if it was not given to the constructor.

Returns:

  • (Integer)


54
55
56
57
58
59
60
61
62
63
# File 'lib/janeway/ast/array_slice_selector.rb', line 54

def step
  # The iteration behavior of jsonpath does not match that of ruby Integer#step.
  # Support the behavior of Integer#step, which needs this:
  #   1. for stepping forward between positive numbers, use a positive number
  #   2. for stepping backward between positive numbers, use a negative number
  #   3. for stepping backward from positive to negative, use a negative number
  #   4. for stepping backward from negative to negative, use a positive number
  # Case #4 has to be detected and the sign of step inverted
  @step || 1
end

#to_s(brackets: true, **_ignored) ⇒ String

Parameters:

  • brackets (Boolean) (defaults to: true)

    add surrounding brackets if true

Returns:

  • (String)


141
142
143
144
145
146
147
148
149
# File 'lib/janeway/ast/array_slice_selector.rb', line 141

def to_s(brackets: true, **_ignored)
  index_str =
    if @step
      "#{@start}:#{@end}:#{@step}"
    else
      "#{@start}:#{@end}"
    end
  brackets ? "[#{index_str}]" : index_str
end

#tree(level) ⇒ Array

Parameters:

  • level (Integer)

Returns:

  • (Array)


153
154
155
# File 'lib/janeway/ast/array_slice_selector.rb', line 153

def tree(level)
  [indented(level, to_s)]
end

#upper_index(input_size) ⇒ Integer

Return the start index to use for stepping through the array, based on a specified array size

Parameters:

  • input_size (Integer)

Returns:

  • (Integer)


69
70
71
# File 'lib/janeway/ast/array_slice_selector.rb', line 69

def upper_index(input_size)
  calculate_index_values(input_size).last
end