jpt 0.0.5 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2556dc372f83257488713fad1a473be979587873c8cb31d0f4833c736d286533
4
- data.tar.gz: cff64906279a4fdff9e10be53807e29a074c9d0ae3890ebbc756b9fd5ee49d41
3
+ metadata.gz: 11f0008d935a2ce891b13af68fe35da1984c733c6d36cc03a3490749ac5deaa6
4
+ data.tar.gz: fbe7f467057066b07c70fb6f37628f0b105b08e8da560af4f99f3a94aa3336d8
5
5
  SHA512:
6
- metadata.gz: 964781ffc90ec14022559b5128a0cecbb50ac4f6ae4ce1e1fd1a5b57031c663efc6557b65626b187916eb6ce9ad0be169bc5a2a155f8c97c9652de8cc5355342
7
- data.tar.gz: c2e910750fb6180465cf673f33232f103e6d4dcf772351bafeb2c468c2579ba25a51bab15395dadf046e69003617e4842accdac33dd0fef72046bcabd239a00a
6
+ metadata.gz: ecc6e9084048e7d9253fd14c175acc83e932caad33817790579d1f53000bca8efc89cf350fead4255b27fa9b72ec1fd67a6b67c9f0ccfbee4b1c9a23ee40b11e
7
+ data.tar.gz: 4a7a95f87230e35d9c5437aba5da7593f9a08bdbedabc09d56d4143dd72254e1c224c06f9fbc725d64a75a68c286db44ae601bb358af4926c8ac1c56c8708e23
data/bin/jpt CHANGED
@@ -23,6 +23,9 @@ begin
23
23
  opts.on("-l", "--[no-]lines", "multi-line mode") do |v|
24
24
  $options.lines = v
25
25
  end
26
+ opts.on("-q", "--[no-]test", "test-file mode") do |v|
27
+ $options.test = v
28
+ end
26
29
  opts.on("-fFUNCSIG", "--[no-]f=FUNCSIG", "add function signature name=rppp") do |v|
27
30
  fail "funcsig format must be name=rppp" unless v =~ /\A([a-z][_a-z0-9]*)-([lnv]+)\z/
28
31
 
@@ -44,7 +47,30 @@ if ARGV == []
44
47
  end
45
48
  jp_file = ARGF.read
46
49
 
47
- if $options.lines
50
+ if $options.test
51
+ argument = query = output = nil
52
+ jp_file.scan(/((?:^(?:$|[^$=].*)\n)+)|([$].*)|=(.*)|#.*/) do |arg,qy,out|
53
+ begin
54
+ if arg
55
+ argument = JSON.parse(arg)
56
+ puts
57
+ puts JSON.dump(argument)
58
+ elsif qy
59
+ jpt = JPT.from_jp(qy)
60
+ output = jpt.apply(argument)
61
+ print jpt.tree.inspect << " "
62
+ puts "➔ #{JSON.dump(output)}"
63
+ elsif out
64
+ suggested = JSON.parse(out)
65
+ if output != suggested
66
+ p [:SUGGESTED, suggested]
67
+ end
68
+ end
69
+ rescue => e
70
+ warn "*** #{e.detailed_message} #{e.backtrace}"
71
+ end
72
+ end
73
+ elsif $options.lines
48
74
  lines = jp_file.lines(chomp: true)
49
75
  col = lines.map(&:length).max
50
76
  form = "%-#{col}s %s"
data/jpt.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "jpt"
3
- s.version = "0.0.5"
3
+ s.version = "0.1.0"
4
4
  s.summary = "JSONPath tools"
5
5
  s.description = %q{jpt implements converters and miscellaneous tools for JSONPath}
6
6
  s.author = "Carsten Bormann"
data/lib/jpt.rb CHANGED
@@ -89,4 +89,249 @@ class JPT
89
89
  Marshal.load(Marshal.dump(self))
90
90
  end
91
91
 
92
+ def apply(arg)
93
+ nodes = [arg]
94
+ select_query(tree, nodes, nodes, :error)
95
+ end
96
+
97
+ def select_query(tree, nodes, root_node, curr_node)
98
+ case tree
99
+ in ["$", *segments]
100
+ nodes = root_node
101
+ segments.each do |seg|
102
+ nodes = select_segment(seg, nodes, root_node, curr_node)
103
+ end
104
+ in ["@", *segments]
105
+ nodes = curr_node
106
+ segments.each do |seg|
107
+ nodes = select_segment(seg, nodes, root_node, curr_node)
108
+ end
109
+ end
110
+ nodes
111
+ end
112
+
113
+ def select_segment(seg, nodes, root_node, curr_node)
114
+ case seg
115
+ in Integer => ix
116
+ nodes = nodes.flat_map do |n|
117
+ if Array === n
118
+ [n.fetch(ix, :nothing)]
119
+ else []
120
+ end
121
+ end
122
+ in String => ky
123
+ nodes = nodes.flat_map do |n|
124
+ if Hash === n
125
+ [n.fetch(ky, :nothing)]
126
+ else []
127
+ end
128
+ end
129
+ in ["u", *sel]
130
+ # nodes = sel.flat_map{ |sel1| select_segment(sel1, nodes, root_node, curr_node)}
131
+ nodes = nodes.flat_map do |n|
132
+ sel.flat_map{ |sel1| select_segment(sel1, [n], root_node, curr_node)}
133
+ end
134
+ in ["wild"]
135
+ nodes = nodes.flat_map do |n|
136
+ enumerate(n)
137
+ end
138
+ in ["desc", sel]
139
+ nodes = nodes.flat_map do |n|
140
+ containers(n)
141
+ end
142
+ nodes = select_segment(sel, nodes, root_node, curr_node)
143
+ in ["slice", st, en, sp]
144
+ nodes = nodes.flat_map do |n|
145
+ if (Array === n) && sp != 0
146
+ len = n.length
147
+ ret = []
148
+ sp ||= 1
149
+ if sp > 0 # weird formulae copied from spec
150
+ st ||= 0
151
+ en ||= len
152
+ else
153
+ # warn "ARR #{st} #{en} #{sp}"
154
+ st ||= len - 1
155
+ en ||= -len - 1
156
+ # warn "ARR2 #{st} #{en} #{sp}"
157
+ end
158
+ st, en = [st, en].map{|i| i >= 0 ? i : len + i}
159
+ if sp > 0
160
+ lo = [[st, 0].max, len].min
161
+ up = [[en, 0].max, len].min
162
+ while lo < up
163
+ ret << n[lo]; lo += sp
164
+ end
165
+ else
166
+ up = [[st, -1].max, len-1].min
167
+ lo = [[en, -1].max, len-1].min
168
+ # warn "ARR3 #{st} #{en} #{sp} #{lo} #{up}"
169
+ while lo < up
170
+ ret << n[up]; up += sp
171
+ end
172
+ end
173
+ ret
174
+ else
175
+ []
176
+ end
177
+ end
178
+ in ["filt", logexp]
179
+ nodes = nodes.flat_map do |n|
180
+ enumerate(n).flat_map do |cand|
181
+ a = filt_apply(logexp, root_node, [cand])
182
+ # warn "***A #{a.inspect}"
183
+ if filt_to_logical(a)
184
+ [cand]
185
+ else
186
+ []
187
+ end
188
+ end
189
+ end
190
+ end
191
+ nodes.delete(:nothing)
192
+ nodes
193
+ end
194
+
195
+ def enumerate(n)
196
+ case n
197
+ in Array
198
+ n
199
+ in Hash
200
+ n.map{|k, v| v}
201
+ else
202
+ []
203
+ end
204
+ end
205
+
206
+ def containers(n)
207
+ case n
208
+ in Array
209
+ [n, *n.flat_map{containers(_1)}]
210
+ in Hash
211
+ [n, *n.flat_map{|k, v| containers(v)}]
212
+ else
213
+ []
214
+ end
215
+ end
216
+
217
+ def filt_to_logical(val)
218
+ case val
219
+ in [:nodes, v]
220
+ v != []
221
+ in [:logical, v]
222
+ v
223
+ end
224
+ end
225
+
226
+ def filt_to_value(val)
227
+ case val
228
+ in [:nodes, v]
229
+ if v.length == 1
230
+ v[0]
231
+ else
232
+ :nothing
233
+ end
234
+ in [:value, v]
235
+ v
236
+ end
237
+ end
238
+
239
+ COMPARE_SWAP = {">" => "<", ">=" => "<="}
240
+
241
+ def filt_apply(logexp, root_node, curr_node)
242
+ # warn "***B #{logexp.inspect} #{root_node.inspect} #{curr_node.inspect}"
243
+ case logexp
244
+ in ["@", *]
245
+ [:nodes, select_query(logexp, curr_node, root_node, curr_node)]
246
+ in ["$", *]
247
+ [:nodes, select_query(logexp, root_node, root_node, curr_node)]
248
+ in [("==" | "!=" | "<" | ">" | "<=" | ">="), a, b]
249
+ lhs = filt_to_value(filt_apply(a, root_node, curr_node)) rescue :nothing
250
+ rhs = filt_to_value(filt_apply(b, root_node, curr_node)) rescue :nothing
251
+ op = logexp[0]
252
+ # warn "***C #{op} #{lhs.inspect}, #{rhs.inspect}"
253
+ if sop = COMPARE_SWAP[op]
254
+ lhs, rhs = rhs, lhs
255
+ op = sop
256
+ end
257
+ # warn "***C1 #{op} #{lhs.inspect}, #{rhs.inspect}"
258
+ res = if op != "<" && (lhs == rhs rescue false)
259
+ op == "!=" ? false : true
260
+ else
261
+ if op[0] == "<" # "<" or "<="
262
+ case [lhs, rhs]
263
+ in Numeric, Numeric
264
+ lhs < rhs
265
+ in String, String
266
+ lhs < rhs
267
+ else
268
+ false
269
+ end
270
+ else op == "!="
271
+ end
272
+ end
273
+ # warn "***C9 #{res}"
274
+ [:logical, res]
275
+ in ["and" | "or", a, b]
276
+ lhs = filt_to_logical(filt_apply(a, root_node, curr_node))
277
+ rhs = filt_to_logical(filt_apply(b, root_node, curr_node))
278
+ op = logexp[0]
279
+ # warn "***C #{op} #{lhs.inspect}, #{rhs.inspect}"
280
+ [:logical, case op
281
+ in "or"
282
+ lhs || rhs
283
+ in "and"
284
+ lhs && rhs
285
+ end]
286
+ in ["not", a]
287
+ lhs = filt_to_logical(filt_apply(a, root_node, curr_node))
288
+ [:logical, !lhs]
289
+ in ["func", "length", value]
290
+ value = filt_to_value(filt_apply(value, root_node, curr_node))
291
+ [:value,
292
+ case value
293
+ in Hash | Array | String
294
+ value.length
295
+ else
296
+ :nothing
297
+ end
298
+ ]
299
+ in ["func", "count", nodes]
300
+ ty, nodes = filt_apply(nodes, root_node, curr_node)
301
+ [:value,
302
+ if ty != :nodes
303
+ warn "*** func count ty #{ty.inspect}"
304
+ 0
305
+ else
306
+ nodes.length
307
+ end]
308
+ in ["func", "match", str, re]
309
+ str = filt_to_value(filt_apply(str, root_node, curr_node))
310
+ re = filt_to_value(filt_apply(re, root_node, curr_node))
311
+ [:logical,
312
+ begin
313
+ /\A(?:#{re})\z/ === str
314
+ rescue => e
315
+ warn "*** #{e.detailed_message} #{e.backtrace}"
316
+ false
317
+ end
318
+ ]
319
+ in ["func", "search", str, re]
320
+ str = filt_to_value(filt_apply(str, root_node, curr_node))
321
+ re = filt_to_value(filt_apply(re, root_node, curr_node))
322
+ [:logical,
323
+ begin
324
+ /#{re}/ === str
325
+ rescue => e
326
+ warn "*** #{e.detailed_message} #{e.backtrace}"
327
+ false
328
+ end
329
+ ]
330
+ in ["func", name, *args]
331
+ warn "*** Unknown function extension #{name} with args #{args.inspect}"
332
+ [:logical, false]
333
+ in String | Numeric | false | true | nil
334
+ [:value, logexp]
335
+ end
336
+ end
92
337
  end
@@ -2974,7 +2974,7 @@ module JPTGRAMMAR
2974
2974
 
2975
2975
  module AbsSingularPath1
2976
2976
  def ast
2977
- ["@", *singular_path_segments.ast]
2977
+ ["$", *singular_path_segments.ast]
2978
2978
  end
2979
2979
  end
2980
2980
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carsten Bormann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-21 00:00:00.000000000 Z
11
+ date: 2023-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler