json_p3 0.4.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.rubocop.yml +26 -9
- data/.ruby-version +1 -1
- data/CHANGELOG.md +54 -0
- data/README.md +125 -123
- data/Rakefile +3 -3
- data/certs/jgrp.pem +21 -21
- data/lib/json_p3/errors.rb +51 -43
- data/lib/json_p3/patch/op.rb +23 -0
- data/lib/json_p3/patch/op_add.rb +51 -0
- data/lib/json_p3/patch/op_copy.rb +64 -0
- data/lib/json_p3/patch/op_move.rb +74 -0
- data/lib/json_p3/patch/op_remove.rb +56 -0
- data/lib/json_p3/patch/op_replace.rb +54 -0
- data/lib/json_p3/patch/op_test.rb +31 -0
- data/lib/json_p3/patch.rb +15 -330
- data/lib/json_p3/path/environment.rb +113 -0
- data/lib/json_p3/path/filter.rb +463 -0
- data/lib/json_p3/path/function.rb +12 -0
- data/lib/json_p3/path/function_extensions/count.rb +15 -0
- data/lib/json_p3/path/function_extensions/length.rb +17 -0
- data/lib/json_p3/path/function_extensions/match.rb +62 -0
- data/lib/json_p3/path/function_extensions/pattern.rb +42 -0
- data/lib/json_p3/path/function_extensions/search.rb +44 -0
- data/lib/json_p3/path/function_extensions/value.rb +15 -0
- data/lib/json_p3/path/lexer.rb +220 -0
- data/lib/json_p3/path/node.rb +48 -0
- data/lib/json_p3/path/parser.rb +676 -0
- data/lib/json_p3/path/query.rb +74 -0
- data/lib/json_p3/path/segment.rb +172 -0
- data/lib/json_p3/path/selector.rb +304 -0
- data/lib/json_p3/path/serialize.rb +16 -0
- data/lib/json_p3/path/unescape.rb +134 -0
- data/lib/json_p3/pointer.rb +15 -76
- data/lib/json_p3/relative_pointer.rb +69 -0
- data/lib/json_p3/version.rb +1 -1
- data/lib/json_p3.rb +50 -13
- data/sig/json_p3/cache.rbs +21 -0
- data/sig/json_p3/errors.rbs +55 -0
- data/sig/json_p3/patch.rbs +145 -0
- data/sig/json_p3/path/environment.rbs +81 -0
- data/sig/json_p3/path/filter.rbs +196 -0
- data/sig/json_p3/path/function.rbs +94 -0
- data/sig/json_p3/path/lexer.rbs +62 -0
- data/sig/json_p3/path/node.rbs +46 -0
- data/sig/json_p3/path/parser.rbs +92 -0
- data/sig/json_p3/path/query.rbs +47 -0
- data/sig/json_p3/path/segment.rbs +54 -0
- data/sig/json_p3/path/selector.rbs +100 -0
- data/sig/json_p3/path/serialize.rbs +9 -0
- data/sig/json_p3/path/unescape.rbs +12 -0
- data/sig/json_p3/pointer.rbs +64 -0
- data/sig/json_p3/relative_pointer.rbs +30 -0
- data/sig/json_p3.rbs +24 -1318
- data.tar.gz.sig +0 -0
- metadata +66 -46
- metadata.gz.sig +0 -0
- data/lib/json_p3/environment.rb +0 -111
- data/lib/json_p3/filter.rb +0 -459
- data/lib/json_p3/function.rb +0 -10
- data/lib/json_p3/function_extensions/count.rb +0 -15
- data/lib/json_p3/function_extensions/length.rb +0 -17
- data/lib/json_p3/function_extensions/match.rb +0 -62
- data/lib/json_p3/function_extensions/pattern.rb +0 -39
- data/lib/json_p3/function_extensions/search.rb +0 -44
- data/lib/json_p3/function_extensions/value.rb +0 -15
- data/lib/json_p3/lexer.rb +0 -440
- data/lib/json_p3/node.rb +0 -44
- data/lib/json_p3/parser.rb +0 -553
- data/lib/json_p3/path.rb +0 -72
- data/lib/json_p3/segment.rb +0 -158
- data/lib/json_p3/selector.rb +0 -306
- data/lib/json_p3/serialize.rb +0 -13
- data/lib/json_p3/token.rb +0 -36
- data/lib/json_p3/unescape.rb +0 -112
data/lib/json_p3/segment.rb
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSONP3
|
|
4
|
-
# Base class for all JSONPath segments.
|
|
5
|
-
class Segment
|
|
6
|
-
# @dynamic token, selectors
|
|
7
|
-
attr_reader :token, :selectors
|
|
8
|
-
|
|
9
|
-
def initialize(env, token, selectors)
|
|
10
|
-
@env = env
|
|
11
|
-
@token = token
|
|
12
|
-
@selectors = selectors
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Select the children of each node in _nodes_.
|
|
16
|
-
# @return [Array<JSONPathNode>]
|
|
17
|
-
def resolve(_nodes)
|
|
18
|
-
raise "segments must implement resolve(nodes)"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Select the children of each node in _nodes_.
|
|
22
|
-
# @return [Enumerable<JSONPathNode>]
|
|
23
|
-
def resolve_enum(_nodes)
|
|
24
|
-
raise "segments must implement resolve_enum(nodes)"
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# The child selection segment.
|
|
29
|
-
class ChildSegment < Segment
|
|
30
|
-
def resolve(nodes)
|
|
31
|
-
rv = [] # : Array[JSONPathNode]
|
|
32
|
-
nodes.each do |node|
|
|
33
|
-
@selectors.each do |selector|
|
|
34
|
-
rv.concat selector.resolve(node)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
rv
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def resolve_enum(nodes)
|
|
41
|
-
Enumerator.new do |yielder|
|
|
42
|
-
nodes.each do |node|
|
|
43
|
-
@selectors.each do |selector|
|
|
44
|
-
selector.resolve(node).each do |item|
|
|
45
|
-
yielder << item
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def to_s
|
|
53
|
-
"[#{@selectors.map(&:to_s).join(", ")}]"
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def ==(other)
|
|
57
|
-
self.class == other.class &&
|
|
58
|
-
@selectors == other.selectors &&
|
|
59
|
-
@token == other.token
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
alias eql? ==
|
|
63
|
-
|
|
64
|
-
def hash
|
|
65
|
-
[@selectors, @token].hash
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# The recursive descent segment
|
|
70
|
-
class RecursiveDescentSegment < Segment
|
|
71
|
-
def resolve(nodes)
|
|
72
|
-
rv = [] # : Array[JSONPathNode]
|
|
73
|
-
nodes.each do |node|
|
|
74
|
-
visit(node).each do |descendant|
|
|
75
|
-
@selectors.each do |selector|
|
|
76
|
-
rv.concat selector.resolve(descendant)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
rv
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def resolve_enum(nodes)
|
|
84
|
-
Enumerator.new do |yielder|
|
|
85
|
-
nodes.each do |node|
|
|
86
|
-
visit_enum(node).each do |descendant|
|
|
87
|
-
@selectors.each do |selector|
|
|
88
|
-
selector.resolve(descendant).each do |item|
|
|
89
|
-
yielder << item
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def to_s
|
|
98
|
-
"..[#{@selectors.map(&:to_s).join(", ")}]"
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def ==(other)
|
|
102
|
-
self.class == other.class &&
|
|
103
|
-
@selectors == other.selectors &&
|
|
104
|
-
@token == other.token
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
alias eql? ==
|
|
108
|
-
|
|
109
|
-
def hash
|
|
110
|
-
["..", @selectors, @token].hash
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
protected
|
|
114
|
-
|
|
115
|
-
def visit(node, depth = 1)
|
|
116
|
-
raise JSONPathRecursionError.new("recursion limit exceeded", @token) if depth > @env.class::MAX_RECURSION_DEPTH
|
|
117
|
-
|
|
118
|
-
rv = [node]
|
|
119
|
-
|
|
120
|
-
if node.value.is_a? Array
|
|
121
|
-
node.value.each_with_index do |value, i|
|
|
122
|
-
child = JSONPathNode.new(value, [node.location, i], node.root)
|
|
123
|
-
rv.concat visit(child, depth + 1)
|
|
124
|
-
end
|
|
125
|
-
elsif node.value.is_a? Hash
|
|
126
|
-
node.value.each do |key, value|
|
|
127
|
-
child = JSONPathNode.new(value, [node.location, key], node.root)
|
|
128
|
-
rv.concat visit(child, depth + 1)
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
rv
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def visit_enum(node, depth = 1)
|
|
136
|
-
raise JSONPathRecursionError.new("recursion limit exceeded", @token) if depth > @env.class::MAX_RECURSION_DEPTH
|
|
137
|
-
|
|
138
|
-
Enumerator.new do |yielder|
|
|
139
|
-
yielder << node
|
|
140
|
-
if node.value.is_a? Array
|
|
141
|
-
node.value.each_with_index do |value, i|
|
|
142
|
-
child = JSONPathNode.new(value, [node.location, i], node.root)
|
|
143
|
-
visit_enum(child, depth + 1).each do |item|
|
|
144
|
-
yielder << item
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
elsif node.value.is_a? Hash
|
|
148
|
-
node.value.each do |key, value|
|
|
149
|
-
child = JSONPathNode.new(value, [node.location, key], node.root)
|
|
150
|
-
visit_enum(child, depth + 1).each do |item|
|
|
151
|
-
yielder << item
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
data/lib/json_p3/selector.rb
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSONP3
|
|
4
|
-
# Base class for all JSONPath selectors
|
|
5
|
-
class Selector
|
|
6
|
-
# @dynamic token
|
|
7
|
-
attr_reader :token
|
|
8
|
-
|
|
9
|
-
def initialize(env, token)
|
|
10
|
-
@env = env
|
|
11
|
-
@token = token
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Apply this selector to _node_.
|
|
15
|
-
# @return [Array<JSONPathNode>]
|
|
16
|
-
def resolve(_node)
|
|
17
|
-
raise "selectors must implement resolve(node)"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Apply this selector to _node_.
|
|
21
|
-
# @return [Enumerable<JSONPathNode>]
|
|
22
|
-
def resolve_enum(node)
|
|
23
|
-
resolve(node)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Return true if this selector is a singular selector.
|
|
27
|
-
def singular?
|
|
28
|
-
false
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# The name selector select values from hashes given a key.
|
|
33
|
-
class NameSelector < Selector
|
|
34
|
-
# @dynamic name
|
|
35
|
-
attr_reader :name
|
|
36
|
-
|
|
37
|
-
def initialize(env, token, name)
|
|
38
|
-
super(env, token)
|
|
39
|
-
@name = name
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def resolve(node)
|
|
43
|
-
if node.value.is_a?(Hash) && node.value.key?(@name)
|
|
44
|
-
[node.new_child(node.value[@name], @name)]
|
|
45
|
-
else
|
|
46
|
-
[]
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def singular?
|
|
51
|
-
true
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def to_s
|
|
55
|
-
JSONP3.canonical_string(@name)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def ==(other)
|
|
59
|
-
self.class == other.class &&
|
|
60
|
-
@name == other.name &&
|
|
61
|
-
@token == other.token
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
alias eql? ==
|
|
65
|
-
|
|
66
|
-
def hash
|
|
67
|
-
[@name, @token].hash
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# This non-standard name selector selects values from hashes given a string or
|
|
72
|
-
# symbol key.
|
|
73
|
-
class SymbolNameSelector < NameSelector
|
|
74
|
-
def initialize(env, token, name)
|
|
75
|
-
super
|
|
76
|
-
@sym = @name.to_sym
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def resolve(node)
|
|
80
|
-
if node.value.is_a?(Hash)
|
|
81
|
-
if node.value.key?(@name)
|
|
82
|
-
[node.new_child(node.value[@name], @name)]
|
|
83
|
-
elsif node.value.key?(@sym)
|
|
84
|
-
[node.new_child(node.value[@sym], @name)]
|
|
85
|
-
else
|
|
86
|
-
[]
|
|
87
|
-
end
|
|
88
|
-
else
|
|
89
|
-
[]
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# The index selector selects values from arrays given an index.
|
|
95
|
-
class IndexSelector < Selector
|
|
96
|
-
# @dynamic index
|
|
97
|
-
attr_reader :index
|
|
98
|
-
|
|
99
|
-
def initialize(env, token, index)
|
|
100
|
-
super(env, token)
|
|
101
|
-
@index = index
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def resolve(node)
|
|
105
|
-
if node.value.is_a?(Array)
|
|
106
|
-
norm_index = normalize(@index, node.value.length)
|
|
107
|
-
return [] if norm_index.negative? || norm_index >= node.value.length
|
|
108
|
-
|
|
109
|
-
[node.new_child(node.value[@index], norm_index)]
|
|
110
|
-
else
|
|
111
|
-
[]
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def singular?
|
|
116
|
-
true
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def to_s
|
|
120
|
-
@index.to_s
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def ==(other)
|
|
124
|
-
self.class == other.class &&
|
|
125
|
-
@index == other.index &&
|
|
126
|
-
@token == other.token
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
alias eql? ==
|
|
130
|
-
|
|
131
|
-
def hash
|
|
132
|
-
[@index, @token].hash
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
private
|
|
136
|
-
|
|
137
|
-
def normalize(index, length)
|
|
138
|
-
index.negative? && length >= index.abs ? length + index : index
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# The wildcard selector selects all elements from an array or values from a hash.
|
|
143
|
-
class WildcardSelector < Selector
|
|
144
|
-
def resolve(node)
|
|
145
|
-
if node.value.is_a? Hash
|
|
146
|
-
node.value.map { |k, v| node.new_child(v, k) }
|
|
147
|
-
elsif node.value.is_a? Array
|
|
148
|
-
node.value.map.with_index { |e, i| node.new_child(e, i) }
|
|
149
|
-
else
|
|
150
|
-
[]
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def resolve_enum(node)
|
|
155
|
-
if node.value.is_a? Hash
|
|
156
|
-
Enumerator.new do |yielder|
|
|
157
|
-
node.value.each do |k, v|
|
|
158
|
-
yielder << node.new_child(v, k)
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
elsif node.value.is_a? Array
|
|
162
|
-
Enumerator.new do |yielder|
|
|
163
|
-
node.value.each.with_index do |e, i|
|
|
164
|
-
yielder << node.new_child(e, i)
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
else
|
|
168
|
-
[]
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def to_s
|
|
173
|
-
"*"
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def ==(other)
|
|
177
|
-
self.class == other.class && @token == other.token
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
alias eql? ==
|
|
181
|
-
|
|
182
|
-
def hash
|
|
183
|
-
@token.hash
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# The slice selector selects a range of elements from an array.
|
|
188
|
-
class SliceSelector < Selector
|
|
189
|
-
# @dynamic start, stop, step
|
|
190
|
-
attr_reader :start, :stop, :step
|
|
191
|
-
|
|
192
|
-
def initialize(env, token, start, stop, step)
|
|
193
|
-
super(env, token)
|
|
194
|
-
@start = start
|
|
195
|
-
@stop = stop
|
|
196
|
-
@step = step || 1
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def resolve(node)
|
|
200
|
-
return [] unless node.value.is_a?(Array)
|
|
201
|
-
|
|
202
|
-
length = node.value.length
|
|
203
|
-
return [] if length.zero? || @step.zero?
|
|
204
|
-
|
|
205
|
-
normalized_start = if @start.nil?
|
|
206
|
-
@step.negative? ? length - 1 : 0
|
|
207
|
-
elsif @start&.negative?
|
|
208
|
-
[length + (@start || raise), 0].max
|
|
209
|
-
else
|
|
210
|
-
[@start || raise, length - 1].min
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
normalized_stop = if @stop.nil?
|
|
214
|
-
@step.negative? ? -1 : length
|
|
215
|
-
elsif @stop&.negative?
|
|
216
|
-
[length + (@stop || raise), -1].max
|
|
217
|
-
else
|
|
218
|
-
[@stop || raise, length].min
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
(normalized_start...normalized_stop).step(@step).map { |i| node.new_child(node.value[i], i) }
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def to_s
|
|
225
|
-
start = @start || ""
|
|
226
|
-
stop = @stop || ""
|
|
227
|
-
step = @step || 1
|
|
228
|
-
"#{start}:#{stop}:#{step}"
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
def ==(other)
|
|
232
|
-
self.class == other.class &&
|
|
233
|
-
@start == other.start &&
|
|
234
|
-
@stop == other.stop &&
|
|
235
|
-
@step == other.step &&
|
|
236
|
-
@token == other.token
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
alias eql? ==
|
|
240
|
-
|
|
241
|
-
def hash
|
|
242
|
-
[@start, @stop, @step, @token].hash
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
# Select array elements or hash values according to a filter expression.
|
|
247
|
-
class FilterSelector < Selector
|
|
248
|
-
# @dynamic expression
|
|
249
|
-
attr_reader :expression
|
|
250
|
-
|
|
251
|
-
def initialize(env, token, expression)
|
|
252
|
-
super(env, token)
|
|
253
|
-
@expression = expression
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
def resolve(node)
|
|
257
|
-
nodes = [] # : Array[JSONPathNode]
|
|
258
|
-
|
|
259
|
-
if node.value.is_a?(Array)
|
|
260
|
-
node.value.each_with_index do |e, i|
|
|
261
|
-
context = FilterContext.new(@env, e, node.root)
|
|
262
|
-
nodes << node.new_child(e, i) if @expression.evaluate(context)
|
|
263
|
-
end
|
|
264
|
-
elsif node.value.is_a?(Hash)
|
|
265
|
-
node.value.each_pair do |k, v|
|
|
266
|
-
context = FilterContext.new(@env, v, node.root)
|
|
267
|
-
nodes << node.new_child(v, k) if @expression.evaluate(context)
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
nodes
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def resolve_enum(node)
|
|
275
|
-
Enumerator.new do |yielder|
|
|
276
|
-
if node.value.is_a?(Array)
|
|
277
|
-
node.value.each_with_index do |e, i|
|
|
278
|
-
context = FilterContext.new(@env, e, node.root)
|
|
279
|
-
yielder << node.new_child(e, i) if @expression.evaluate(context)
|
|
280
|
-
end
|
|
281
|
-
elsif node.value.is_a?(Hash)
|
|
282
|
-
node.value.each_pair do |k, v|
|
|
283
|
-
context = FilterContext.new(@env, v, node.root)
|
|
284
|
-
yielder << node.new_child(v, k) if @expression.evaluate(context)
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def to_s
|
|
291
|
-
"?#{@expression}"
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def ==(other)
|
|
295
|
-
self.class == other.class &&
|
|
296
|
-
@expression == other.start &&
|
|
297
|
-
@token == other.token
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
alias eql? ==
|
|
301
|
-
|
|
302
|
-
def hash
|
|
303
|
-
[@expression, @token].hash
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
end
|
data/lib/json_p3/serialize.rb
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module JSONP3 # rubocop:disable Style/Documentation
|
|
6
|
-
TRANS = { "\\\"" => "\"", "'" => "\\'" }.freeze
|
|
7
|
-
|
|
8
|
-
# Return _value_ formatted as a canonical string literal.
|
|
9
|
-
# @param value [String]
|
|
10
|
-
def self.canonical_string(value)
|
|
11
|
-
"'#{(JSON.dump(value)[1..-2] || raise).gsub(/('|\\")/, TRANS)}'"
|
|
12
|
-
end
|
|
13
|
-
end
|
data/lib/json_p3/token.rb
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "errors"
|
|
4
|
-
|
|
5
|
-
module JSONP3
|
|
6
|
-
# Tokens are produced by the lexer and consumed by the parser. Each token contains sub
|
|
7
|
-
# string from a JSONPath expression, its location within the JSONPath expression and a
|
|
8
|
-
# symbol indicating what type of token it is.
|
|
9
|
-
class Token
|
|
10
|
-
# @dynamic type, value, start, query, message
|
|
11
|
-
attr_reader :type, :value, :start, :query, :message
|
|
12
|
-
|
|
13
|
-
def initialize(type, value, start, query, message: nil)
|
|
14
|
-
@type = type
|
|
15
|
-
@value = value
|
|
16
|
-
@start = start
|
|
17
|
-
@query = query
|
|
18
|
-
@message = message
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def ==(other)
|
|
22
|
-
self.class == other.class &&
|
|
23
|
-
@type == other.type &&
|
|
24
|
-
@value == other.value &&
|
|
25
|
-
@start == other.start &&
|
|
26
|
-
@query == other.query &&
|
|
27
|
-
@message == other.message
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
alias eql? ==
|
|
31
|
-
|
|
32
|
-
def hash
|
|
33
|
-
[@type, @value].hash
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
data/lib/json_p3/unescape.rb
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSONP3 # rubocop:disable Style/Documentation
|
|
4
|
-
# Replace escape sequences with their equivalent Unicode code point.
|
|
5
|
-
# @param value [String]
|
|
6
|
-
# @param quote [String] one of '"' or "'".
|
|
7
|
-
# @param token [Token]
|
|
8
|
-
# @return [String] A new string without escape sequences.
|
|
9
|
-
def self.unescape_string(value, quote, token)
|
|
10
|
-
unescaped = String.new(encoding: "UTF-8")
|
|
11
|
-
index = 0
|
|
12
|
-
length = value.length
|
|
13
|
-
|
|
14
|
-
while index < length
|
|
15
|
-
ch = value[index] || raise
|
|
16
|
-
if ch == "\\"
|
|
17
|
-
index += 1
|
|
18
|
-
case value[index]
|
|
19
|
-
when quote
|
|
20
|
-
unescaped << quote
|
|
21
|
-
when "\\"
|
|
22
|
-
unescaped << "\\"
|
|
23
|
-
when "/"
|
|
24
|
-
unescaped << "/"
|
|
25
|
-
when "b"
|
|
26
|
-
unescaped << "\x08"
|
|
27
|
-
when "f"
|
|
28
|
-
unescaped << "\x0C"
|
|
29
|
-
when "n"
|
|
30
|
-
unescaped << "\n"
|
|
31
|
-
when "r"
|
|
32
|
-
unescaped << "\r"
|
|
33
|
-
when "t"
|
|
34
|
-
unescaped << "\t"
|
|
35
|
-
when "u"
|
|
36
|
-
code_point, index = JSONP3.decode_hex_char(value, index, token)
|
|
37
|
-
unescaped << JSONP3.code_point_to_string(code_point, token)
|
|
38
|
-
else
|
|
39
|
-
raise JSONPathSyntaxError.new("unknown escape sequence", token)
|
|
40
|
-
end
|
|
41
|
-
else
|
|
42
|
-
raise JSONPathSyntaxError.new("invalid character", token) if ch.ord <= 0x1F
|
|
43
|
-
|
|
44
|
-
unescaped << ch
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
index += 1
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
unescaped
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def self.decode_hex_char(value, index, token)
|
|
55
|
-
length = value.length
|
|
56
|
-
|
|
57
|
-
raise JSONPathSyntaxError.new("incomplete escape sequence", token) if index + 4 >= length
|
|
58
|
-
|
|
59
|
-
index += 1 # move past 'u'
|
|
60
|
-
code_point = parse_hex_digits(value[index, 4], token)
|
|
61
|
-
|
|
62
|
-
raise JSONPathSyntaxError.new("unexpected low surrogate", token) if low_surrogate?(code_point)
|
|
63
|
-
|
|
64
|
-
return [code_point, index + 3] unless high_surrogate?(code_point)
|
|
65
|
-
|
|
66
|
-
unless index + 9 < length && value[index + 4] == "\\" && value[index + 5] == "u"
|
|
67
|
-
raise JSONPathSyntaxError.new("incomplete escape sequence", token)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
low_surrogate = parse_hex_digits(value[index + 6, 10], token)
|
|
71
|
-
|
|
72
|
-
raise JSONPathSyntaxError.new("unexpected low surrogate", token) unless low_surrogate?(low_surrogate)
|
|
73
|
-
|
|
74
|
-
code_point = 0x10000 + (
|
|
75
|
-
((code_point & 0x03FF) << 10) | (low_surrogate & 0x03FF)
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
[code_point, index + 9]
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def self.parse_hex_digits(digits, token)
|
|
82
|
-
code_point = 0
|
|
83
|
-
digits.each_byte do |b|
|
|
84
|
-
code_point <<= 4
|
|
85
|
-
case b
|
|
86
|
-
when 48..57
|
|
87
|
-
code_point |= b - 48
|
|
88
|
-
when 65..70
|
|
89
|
-
code_point |= b - 65 + 10
|
|
90
|
-
when 97..102
|
|
91
|
-
code_point |= b - 97 + 10
|
|
92
|
-
else
|
|
93
|
-
raise JSONPathSyntaxError.new("invalid escape sequence", token)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
code_point
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def self.high_surrogate?(code_point)
|
|
100
|
-
code_point >= 0xD800 && code_point <= 0xDBFF
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def self.low_surrogate?(code_point)
|
|
104
|
-
code_point >= 0xDC00 && code_point <= 0xDFFF
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def self.code_point_to_string(code_point, token)
|
|
108
|
-
raise JSONPathSyntaxError.new("invalid character", token) if code_point <= 0x1F
|
|
109
|
-
|
|
110
|
-
code_point.chr(Encoding::UTF_8)
|
|
111
|
-
end
|
|
112
|
-
end
|