1
1
//! Automaton representations of a JSONPath query.
2
+ mod array_transition_set;
2
3
pub mod error;
3
4
mod minimizer;
4
5
mod nfa;
@@ -21,8 +22,23 @@ pub struct Automaton<'q> {
21
22
22
23
/// Transition when a JSON member name matches a [`JsonString`]i.
23
24
pub type MemberTransition < ' q > = ( & ' q JsonString , State ) ;
24
- /// Transition on the n-th element of an array, with n specified by a [`JsonUInt`].
25
- pub type ArrayTransition < ' q > = ( JsonUInt , State ) ;
25
+
26
+ /// Transition on elements of an array with indices specified by either a single index
27
+ /// or a simple slice expression.
28
+ #[ derive( Debug , PartialEq , Eq ) ]
29
+ pub struct ArrayTransition {
30
+ label : ArrayTransitionLabel ,
31
+ target : State ,
32
+ }
33
+
34
+ /// Represent the distinct methods of moving on a match between states.
35
+ #[ derive( Debug , Copy , PartialEq , Clone , Eq ) ]
36
+ pub ( super ) enum ArrayTransitionLabel {
37
+ /// Transition on the n-th element of an array, with n specified by a [`JsonUInt`].
38
+ Index ( JsonUInt ) ,
39
+ /// Transition on elements of array matched by a slice expression - bounds and a step.
40
+ Slice ( SimpleSlice ) ,
41
+ }
26
42
27
43
/// A transition table of a single [`State`] of an [`Automaton`].
28
44
///
@@ -32,10 +48,17 @@ pub type ArrayTransition<'q> = (JsonUInt, State);
32
48
pub struct StateTable < ' q > {
33
49
attributes : StateAttributes ,
34
50
member_transitions : SmallVec < [ MemberTransition < ' q > ; 2 ] > ,
35
- array_transitions : SmallVec < [ ArrayTransition < ' q > ; 2 ] > ,
51
+ array_transitions : SmallVec < [ ArrayTransition ; 2 ] > ,
36
52
fallback_state : State ,
37
53
}
38
54
55
+ #[ derive( Debug , Copy , PartialEq , Clone , Eq ) ]
56
+ pub ( crate ) struct SimpleSlice {
57
+ start : JsonUInt ,
58
+ end : Option < JsonUInt > ,
59
+ step : JsonUInt ,
60
+ }
61
+
39
62
impl < ' q > Default for StateTable < ' q > {
40
63
#[ inline]
41
64
fn default ( ) -> Self {
@@ -76,6 +99,47 @@ impl<'q> Index<State> for Automaton<'q> {
76
99
}
77
100
}
78
101
102
+ impl ArrayTransition {
103
+ pub ( crate ) fn new ( label : ArrayTransitionLabel , target : State ) -> Self {
104
+ Self { label, target }
105
+ }
106
+
107
+ #[ inline( always) ]
108
+ pub ( crate ) fn target_state ( & self ) -> State {
109
+ self . target
110
+ }
111
+
112
+ #[ inline( always) ]
113
+ pub ( crate ) fn matches ( & self , index : JsonUInt ) -> bool {
114
+ self . label . matches ( index)
115
+ }
116
+ }
117
+
118
+ impl ArrayTransitionLabel {
119
+ pub ( crate ) fn matches ( & self , index : JsonUInt ) -> bool {
120
+ match self {
121
+ Self :: Index ( i) => index. eq ( i) ,
122
+ Self :: Slice ( s) => s. contains ( index) ,
123
+ }
124
+ }
125
+ }
126
+
127
+ impl From < JsonUInt > for ArrayTransitionLabel {
128
+ #[ must_use]
129
+ #[ inline( always) ]
130
+ fn from ( index : JsonUInt ) -> Self {
131
+ Self :: Index ( index)
132
+ }
133
+ }
134
+
135
+ impl From < SimpleSlice > for ArrayTransitionLabel {
136
+ #[ must_use]
137
+ #[ inline( always) ]
138
+ fn from ( slice : SimpleSlice ) -> Self {
139
+ Self :: Slice ( slice)
140
+ }
141
+ }
142
+
79
143
impl < ' q > Automaton < ' q > {
80
144
/// Convert a [`JsonPathQuery`] into a minimal deterministic automaton.
81
145
///
@@ -172,7 +236,7 @@ impl<'q> Automaton<'q> {
172
236
#[ must_use]
173
237
#[ inline( always) ]
174
238
pub fn has_any_array_item_transition ( & self , state : State ) -> bool {
175
- self [ state] . attributes . has_array_index_transition ( )
239
+ self [ state] . attributes . has_array_transition ( )
176
240
}
177
241
178
242
/// Returns whether the given state is accepting the first item in a list.
@@ -219,11 +283,11 @@ impl<'q> Automaton<'q> {
219
283
#[ inline( always) ]
220
284
pub fn has_array_index_transition_to_accepting ( & self , state : State , match_index : & JsonUInt ) -> bool {
221
285
let state = & self [ state] ;
222
- state. attributes . has_array_index_transition_to_accepting ( )
286
+ state. attributes . has_array_transition_to_accepting ( )
223
287
&& state
224
288
. array_transitions ( )
225
289
. iter ( )
226
- . any ( |( i , s ) | i . eq ( match_index ) && self . is_accepting ( * s ) )
290
+ . any ( |trans| self . is_accepting ( trans . target ) && trans . matches ( * match_index ) )
227
291
}
228
292
229
293
/// Returns whether the given state has any transitions
@@ -303,7 +367,7 @@ impl<'q> StateTable<'q> {
303
367
/// to the contained [`State`].
304
368
#[ must_use]
305
369
#[ inline( always) ]
306
- pub fn array_transitions ( & self ) -> & [ ArrayTransition < ' q > ] {
370
+ pub fn array_transitions ( & self ) -> & [ ArrayTransition ] {
307
371
& self . array_transitions
308
372
}
309
373
@@ -318,6 +382,22 @@ impl<'q> StateTable<'q> {
318
382
}
319
383
}
320
384
385
+ impl Display for ArrayTransitionLabel {
386
+ #[ inline( always) ]
387
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
388
+ match self {
389
+ Self :: Index ( index) => write ! ( f, "{}" , index. as_u64( ) ) ,
390
+ Self :: Slice ( slice) => {
391
+ if let Some ( end) = slice. end {
392
+ write ! ( f, "[{}:{}:{}]" , slice. start, end, slice. step)
393
+ } else {
394
+ write ! ( f, "[{}::{}]" , slice. start, slice. step)
395
+ }
396
+ }
397
+ }
398
+ }
399
+ }
400
+
321
401
impl < ' q > Display for Automaton < ' q > {
322
402
#[ inline]
323
403
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
@@ -348,8 +428,35 @@ impl<'q> Display for Automaton<'q> {
348
428
}
349
429
350
430
for ( i, transitions) in self . states . iter ( ) . enumerate ( ) {
351
- for ( label, state) in & transitions. array_transitions {
352
- writeln ! ( f, " {i} -> {} [label=\" [{}]\" ]" , state. 0 , label. as_u64( ) ) ?
431
+ for array_transition in & transitions. array_transitions {
432
+ match array_transition. label {
433
+ ArrayTransitionLabel :: Index ( label) => writeln ! (
434
+ f,
435
+ " {i} -> {} [label=\" [{}]\" ]" ,
436
+ array_transition. target. 0 ,
437
+ label. as_u64( )
438
+ ) ?,
439
+ ArrayTransitionLabel :: Slice ( label) => {
440
+ if let Some ( end) = label. end {
441
+ writeln ! (
442
+ f,
443
+ " {i} -> {} [label=\" [{}:{}:{}]\" ]" ,
444
+ array_transition. target. 0 ,
445
+ label. start. as_u64( ) ,
446
+ end. as_u64( ) ,
447
+ label. step. as_u64( )
448
+ ) ?
449
+ } else {
450
+ writeln ! (
451
+ f,
452
+ " {i} -> {} [label=\" [{}::{}]\" ]" ,
453
+ array_transition. target. 0 ,
454
+ label. start. as_u64( ) ,
455
+ label. step. as_u64( )
456
+ ) ?
457
+ }
458
+ }
459
+ }
353
460
}
354
461
for ( label, state) in & transitions. member_transitions {
355
462
writeln ! ( f, " {i} -> {} [label=\" {}\" ]" , state. 0 , label. unquoted( ) ) ?
@@ -360,3 +467,42 @@ impl<'q> Display for Automaton<'q> {
360
467
Ok ( ( ) )
361
468
}
362
469
}
470
+
471
+ impl SimpleSlice {
472
+ fn new ( start : JsonUInt , end : Option < JsonUInt > , step : JsonUInt ) -> Self {
473
+ Self { start, end, step }
474
+ }
475
+
476
+ #[ inline( always) ]
477
+ #[ must_use]
478
+ fn contains ( & self , index : JsonUInt ) -> bool {
479
+ if index < self . start {
480
+ return false ;
481
+ }
482
+ let offset = index. as_u64 ( ) - self . start . as_u64 ( ) ;
483
+ if let Some ( end) = self . end {
484
+ index < end && offset % self . step . as_u64 ( ) == 0
485
+ } else {
486
+ offset % self . step . as_u64 ( ) == 0
487
+ }
488
+ }
489
+ }
490
+
491
+ #[ cfg( test) ]
492
+ mod tests {
493
+ use super :: SimpleSlice ;
494
+ use rsonpath_syntax:: num:: JsonUInt ;
495
+ use test_case:: test_case;
496
+
497
+ #[ test_case( 0 . into( ) , None , 1 . into( ) , 0 . into( ) => true ) ]
498
+ #[ test_case( 2 . into( ) , None , 1 . into( ) , 3 . into( ) => true ) ]
499
+ #[ test_case( 2 . into( ) , None , 2 . into( ) , 3 . into( ) => false ) ]
500
+ #[ test_case( 3 . into( ) , None , 2 . into( ) , 3 . into( ) => true ) ]
501
+ #[ test_case( 2 . into( ) , None , 2 . into( ) , 4 . into( ) => true ) ]
502
+ #[ test_case( 2 . into( ) , Some ( 6 . into( ) ) , 2 . into( ) , 2 . into( ) => true ) ]
503
+ #[ test_case( 2 . into( ) , Some ( 6 . into( ) ) , 2 . into( ) , 6 . into( ) => false ) ]
504
+ fn simple_slice_containment ( start : JsonUInt , end : Option < JsonUInt > , step : JsonUInt , idx : JsonUInt ) -> bool {
505
+ let slice = SimpleSlice :: new ( start, end, step) ;
506
+ slice. contains ( idx)
507
+ }
508
+ }
0 commit comments