@@ -188,10 +188,7 @@ impl<'q> Minimizer<'q> {
188188 debug ! ( "{id} is rejecting" ) ;
189189 attrs = attrs. rejecting ( ) ;
190190 }
191- if array_transitions. len ( ) + member_transitions. len ( ) == 1 && fallback == Self :: rejecting_state ( ) {
192- debug ! ( "{id} is unitary" ) ;
193- attrs = attrs. unitary ( ) ;
194- }
191+
195192 if self . accepting . contains ( fallback. 0 )
196193 || array_transitions
197194 . iter ( )
@@ -213,6 +210,23 @@ impl<'q> Minimizer<'q> {
213210 attrs = attrs. has_array_transition_to_accepting ( ) ;
214211 }
215212
213+ // Unitarity check:
214+ // 1. Fallback rejects.
215+ // 2. Only one transition that can match at most one element in a JSON, either:
216+ // a) member transition; or
217+ // b) array transition that matches only one index.
218+ let is_unitary = {
219+ fallback == Self :: rejecting_state ( )
220+ && ( ( member_transitions. len ( ) == 1 && array_transitions. is_empty ( ) )
221+ || ( array_transitions. len ( ) == 1
222+ && member_transitions. is_empty ( )
223+ && array_transitions[ 0 ] . label . matches_at_most_once ( ) ) )
224+ } ;
225+ if is_unitary {
226+ debug ! ( "{id} is unitary" ) ;
227+ attrs = attrs. unitary ( ) ;
228+ }
229+
216230 attrs. into ( )
217231 }
218232
@@ -1260,6 +1274,48 @@ mod tests {
12601274 assert_eq ! ( result, expected) ;
12611275 }
12621276
1277+ #[ test]
1278+ fn direct_slice ( ) {
1279+ // Query = $[1:3]
1280+ let label = SimpleSlice :: new ( 1 . into ( ) , Some ( 3 . into ( ) ) , 1 . into ( ) ) ;
1281+
1282+ let nfa = NondeterministicAutomaton {
1283+ ordered_states : vec ! [
1284+ NfaState :: Direct ( nfa:: Transition :: Array ( label. into( ) ) ) ,
1285+ NfaState :: Accepting ,
1286+ ] ,
1287+ } ;
1288+
1289+ let mut result = minimize ( nfa) . unwrap ( ) ;
1290+ make_canonical ( & mut result) ;
1291+ let expected = Automaton {
1292+ states : vec ! [
1293+ StateTable {
1294+ array_transitions: smallvec![ ] ,
1295+ member_transitions: smallvec![ ] ,
1296+ fallback_state: State ( 0 ) ,
1297+ attributes: StateAttributes :: REJECTING ,
1298+ } ,
1299+ StateTable {
1300+ array_transitions: smallvec![ ArrayTransition :: new( ArrayTransitionLabel :: Slice ( label) , State ( 2 ) ) , ] ,
1301+ member_transitions: smallvec![ ] ,
1302+ fallback_state: State ( 0 ) ,
1303+ attributes: StateAttributes :: TRANSITIONS_TO_ACCEPTING
1304+ | StateAttributes :: HAS_ARRAY_TRANSITION
1305+ | StateAttributes :: HAS_ARRAY_TRANSITION_TO_ACCEPTING ,
1306+ } ,
1307+ StateTable {
1308+ array_transitions: smallvec![ ] ,
1309+ member_transitions: smallvec![ ] ,
1310+ fallback_state: State ( 0 ) ,
1311+ attributes: StateAttributes :: ACCEPTING ,
1312+ } ,
1313+ ] ,
1314+ } ;
1315+
1316+ assert_eq ! ( result, expected) ;
1317+ }
1318+
12631319 /// DFA creation is unstable - it can result in many different isomorphic automaton structures.
12641320 /// This function relabels the states in a canonical way so that they can be compared for equality.
12651321 fn make_canonical ( dfa : & mut Automaton ) {
0 commit comments