@@ -12,10 +12,6 @@ use super::head_skipping::{CanHeadSkip, HeadSkip};
12
12
use super :: Compiler ;
13
13
#[ cfg( feature = "head-skip" ) ]
14
14
use crate :: classification:: ResumeClassifierState ;
15
- use crate :: classification:: {
16
- quotes:: { classify_quoted_sequences, QuoteClassifiedIterator } ,
17
- structural:: { classify_structural_characters, BracketType , Structural , StructuralIterator } ,
18
- } ;
19
15
use crate :: debug;
20
16
use crate :: engine:: depth:: Depth ;
21
17
use crate :: engine:: error:: EngineError ;
@@ -24,9 +20,16 @@ use crate::engine::tail_skipping::TailSkip;
24
20
use crate :: engine:: { Engine , Input } ;
25
21
use crate :: query:: automaton:: { Automaton , State } ;
26
22
use crate :: query:: error:: CompilerError ;
27
- use crate :: query:: { JsonPathQuery , Label } ;
23
+ use crate :: query:: { JsonPathQuery , Label , NonNegativeArrayIndex } ;
28
24
use crate :: result:: QueryResult ;
29
25
use crate :: BLOCK_SIZE ;
26
+ use crate :: {
27
+ classification:: {
28
+ quotes:: { classify_quoted_sequences, QuoteClassifiedIterator } ,
29
+ structural:: { classify_structural_characters, BracketType , Structural , StructuralIterator } ,
30
+ } ,
31
+ query:: automaton:: TransitionLabel ,
32
+ } ;
30
33
use smallvec:: { smallvec, SmallVec } ;
31
34
32
35
/// Main engine for a fixed JSONPath query.
@@ -102,6 +105,9 @@ struct Executor<'q, 'b, I: Input> {
102
105
bytes : & ' b I ,
103
106
next_event : Option < Structural > ,
104
107
is_list : bool ,
108
+ array_count : NonNegativeArrayIndex ,
109
+ has_any_array_item_transition : bool ,
110
+ has_any_array_item_transition_to_accepting : bool ,
105
111
}
106
112
107
113
fn query_executor < ' q , ' b , I : Input > (
@@ -116,6 +122,9 @@ fn query_executor<'q, 'b, I: Input>(
116
122
bytes,
117
123
next_event : None ,
118
124
is_list : false ,
125
+ array_count : NonNegativeArrayIndex :: ZERO ,
126
+ has_any_array_item_transition : false ,
127
+ has_any_array_item_transition_to_accepting : false ,
119
128
}
120
129
}
121
130
@@ -203,10 +212,15 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
203
212
let mut any_matched = false ;
204
213
205
214
for & ( label, target) in self . automaton [ self . state ] . transitions ( ) {
206
- if self . automaton . is_accepting ( target) && self . is_match ( idx, label) ? {
207
- result. report ( idx) ;
208
- any_matched = true ;
209
- break ;
215
+ match label {
216
+ TransitionLabel :: ArrayIndex ( _) => { }
217
+ TransitionLabel :: ObjectMember ( label) => {
218
+ if self . automaton . is_accepting ( target) && self . is_match ( idx, label) ? {
219
+ result. report ( idx) ;
220
+ any_matched = true ;
221
+ break ;
222
+ }
223
+ }
210
224
}
211
225
}
212
226
let fallback_state = self . automaton [ self . state ] . fallback_state ( ) ;
@@ -240,13 +254,32 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
240
254
R : QueryResult ,
241
255
{
242
256
self . next_event = classifier. next ( ) ;
257
+
243
258
let is_next_opening = self . next_event . map_or ( false , |s| s. is_opening ( ) ) ;
244
259
245
- if !is_next_opening {
246
- let fallback_state = self . automaton [ self . state ] . fallback_state ( ) ;
247
- if self . is_list && self . automaton . is_accepting ( fallback_state) {
248
- result. report ( idx) ;
249
- }
260
+ let is_fallback_accepting = self
261
+ . automaton
262
+ . is_accepting ( self . automaton [ self . state ] . fallback_state ( ) ) ;
263
+
264
+ if !is_next_opening && self . is_list && is_fallback_accepting {
265
+ debug ! ( "Accepting on comma." ) ;
266
+ result. report ( idx) ;
267
+ }
268
+
269
+ // After wildcard, check for a matching array index.
270
+ // If the index increment exceeds the field's limit, give up.
271
+ if self . is_list && self . array_count . try_increment ( ) . is_err ( ) {
272
+ return Ok ( ( ) ) ;
273
+ }
274
+ debug ! ( "Incremented array count to {}" , self . array_count) ;
275
+
276
+ let match_index = self
277
+ . automaton
278
+ . has_array_index_transition_to_accepting ( self . state , & self . array_count ) ;
279
+
280
+ if !is_next_opening && match_index {
281
+ debug ! ( "Accepting on list item." ) ;
282
+ result. report ( idx) ;
250
283
}
251
284
252
285
Ok ( ( ) )
@@ -267,15 +300,32 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
267
300
debug ! ( "Opening {bracket_type:?}, increasing depth and pushing stack." , ) ;
268
301
let mut any_matched = false ;
269
302
270
- if let Some ( colon_idx) = self . find_preceding_colon ( idx) {
271
- for & ( label, target) in self . automaton [ self . state ] . transitions ( ) {
272
- if self . is_match ( colon_idx, label) ? {
273
- any_matched = true ;
274
- self . transition_to ( target, bracket_type) ;
275
- if self . automaton . is_accepting ( target) {
276
- result. report ( colon_idx) ;
303
+ let colon_idx = self . find_preceding_colon ( idx) ;
304
+
305
+ for & ( label, target) in self . automaton [ self . state ] . transitions ( ) {
306
+ match label {
307
+ TransitionLabel :: ArrayIndex ( i) => {
308
+ if self . is_list && i. eq ( & self . array_count ) {
309
+ any_matched = true ;
310
+ self . transition_to ( target, bracket_type) ;
311
+ if self . automaton . is_accepting ( target) {
312
+ debug ! ( "Accept {idx}" ) ;
313
+ result. report ( idx) ;
314
+ }
315
+ break ;
316
+ }
317
+ }
318
+ TransitionLabel :: ObjectMember ( label) => {
319
+ if let Some ( colon_idx) = colon_idx {
320
+ if self . is_match ( colon_idx, label) ? {
321
+ any_matched = true ;
322
+ self . transition_to ( target, bracket_type) ;
323
+ if self . automaton . is_accepting ( target) {
324
+ result. report ( colon_idx) ;
325
+ }
326
+ break ;
327
+ }
277
328
}
278
- break ;
279
329
}
280
330
}
281
331
}
@@ -301,29 +351,51 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
301
351
302
352
if bracket_type == BracketType :: Square {
303
353
self . is_list = true ;
354
+ self . has_any_array_item_transition =
355
+ self . automaton . has_any_array_item_transition ( self . state ) ;
356
+ self . has_any_array_item_transition_to_accepting = self
357
+ . automaton
358
+ . has_any_array_item_transition_to_accepting ( self . state ) ;
304
359
305
360
let fallback = self . automaton [ self . state ] . fallback_state ( ) ;
306
- if self . automaton . is_accepting ( fallback) {
361
+ let is_fallback_accepting = self . automaton . is_accepting ( fallback) ;
362
+
363
+ let searching_list = is_fallback_accepting || self . has_any_array_item_transition ;
364
+
365
+ if searching_list {
307
366
classifier. turn_commas_on ( idx) ;
308
- self . next_event = classifier. next ( ) ;
309
- match self . next_event {
310
- Some ( Structural :: Closing ( _, close_idx) ) => {
311
- if let Some ( ( next_idx, _) ) = self . bytes . seek_non_whitespace_forward ( idx + 1 )
312
- {
313
- if next_idx < close_idx {
314
- result. report ( next_idx) ;
367
+ self . array_count = NonNegativeArrayIndex :: ZERO ;
368
+ debug ! ( "Initialized array count to {}" , self . array_count) ;
369
+
370
+ let wants_first_item = is_fallback_accepting
371
+ || self
372
+ . automaton
373
+ . has_first_array_index_transition_to_accepting ( self . state ) ;
374
+
375
+ if wants_first_item {
376
+ self . next_event = classifier. next ( ) ;
377
+
378
+ match self . next_event {
379
+ Some ( Structural :: Closing ( _, close_idx) ) => {
380
+ if let Some ( ( next_idx, _) ) =
381
+ self . bytes . seek_non_whitespace_forward ( idx + 1 )
382
+ {
383
+ if next_idx < close_idx {
384
+ result. report ( next_idx) ;
385
+ }
315
386
}
316
387
}
388
+ Some ( Structural :: Comma ( _) ) => {
389
+ result. report ( idx + 1 ) ;
390
+ }
391
+ _ => ( ) ,
317
392
}
318
- Some ( Structural :: Comma ( _) ) => {
319
- result. report ( idx + 1 ) ;
320
- }
321
- _ => ( ) ,
322
393
}
323
394
} else {
324
395
classifier. turn_commas_off ( ) ;
325
396
}
326
397
} else {
398
+ classifier. turn_commas_off ( ) ;
327
399
self . is_list = false ;
328
400
}
329
401
@@ -359,6 +431,12 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
359
431
if let Some ( stack_frame) = self . stack . pop_if_at_or_below ( * self . depth ) {
360
432
self . state = stack_frame. state ;
361
433
self . is_list = stack_frame. is_list ;
434
+ self . array_count = stack_frame. array_count ;
435
+ self . has_any_array_item_transition = stack_frame. has_any_array_item_transition ;
436
+ self . has_any_array_item_transition_to_accepting =
437
+ stack_frame. has_any_array_item_transition_to_accepting ;
438
+
439
+ debug ! ( "Restored array count to {}" , self . array_count) ;
362
440
363
441
if self . automaton . is_unitary ( self . state ) {
364
442
let bracket_type = self . current_node_bracket_type ( ) ;
@@ -369,6 +447,7 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
369
447
}
370
448
}
371
449
}
450
+
372
451
#[ cfg( not( feature = "unique-labels" ) ) ]
373
452
{
374
453
self . depth
@@ -378,13 +457,20 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
378
457
if let Some ( stack_frame) = self . stack . pop_if_at_or_below ( * self . depth ) {
379
458
self . state = stack_frame. state ;
380
459
self . is_list = stack_frame. is_list ;
460
+ self . array_count = stack_frame. array_count ;
461
+ self . has_any_array_item_transition = stack_frame. has_any_array_item_transition ;
462
+ self . has_any_array_item_transition_to_accepting =
463
+ stack_frame. has_any_array_item_transition_to_accepting ;
464
+
465
+ debug ! ( "Restored array count to {}" , self . array_count) ;
381
466
}
382
467
}
383
468
384
469
if self . is_list
385
- && self
470
+ && ( self
386
471
. automaton
387
472
. is_accepting ( self . automaton [ self . state ] . fallback_state ( ) )
473
+ || self . has_any_array_item_transition )
388
474
{
389
475
classifier. turn_commas_on ( idx) ;
390
476
} else {
@@ -402,15 +488,25 @@ impl<'q, 'b, I: Input> Executor<'q, 'b, I> {
402
488
403
489
fn transition_to ( & mut self , target : State , opening : BracketType ) {
404
490
let target_is_list = opening == BracketType :: Square ;
405
- if target != self . state || target_is_list != self . is_list {
491
+
492
+ let fallback = self . automaton [ self . state ] . fallback_state ( ) ;
493
+ let is_fallback_accepting = self . automaton . is_accepting ( fallback) ;
494
+ let searching_list = is_fallback_accepting || self . has_any_array_item_transition ;
495
+
496
+ if target != self . state || target_is_list != self . is_list || searching_list {
406
497
debug ! (
407
- "push {}, goto {target}, is_list = {target_is_list}" ,
408
- self . state
498
+ "push {}, goto {target}, is_list = {target_is_list}, array_count: {} " ,
499
+ self . state, self . array_count
409
500
) ;
501
+
410
502
self . stack . push ( StackFrame {
411
503
depth : * self . depth ,
412
504
state : self . state ,
413
505
is_list : self . is_list ,
506
+ array_count : self . array_count ,
507
+ has_any_array_item_transition : self . has_any_array_item_transition ,
508
+ has_any_array_item_transition_to_accepting : self
509
+ . has_any_array_item_transition_to_accepting ,
414
510
} ) ;
415
511
self . state = target;
416
512
}
@@ -467,6 +563,9 @@ struct StackFrame {
467
563
depth : u8 ,
468
564
state : State ,
469
565
is_list : bool ,
566
+ array_count : NonNegativeArrayIndex ,
567
+ has_any_array_item_transition : bool ,
568
+ has_any_array_item_transition_to_accepting : bool ,
470
569
}
471
570
472
571
#[ derive( Debug ) ]
0 commit comments