From e12093e8dfcf870b794b7725f738ea3fb026a6aa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 4 Mar 2021 13:50:27 -0800 Subject: [PATCH] Fix regression introduced in CharacterSet optimization --- cli/src/generate/nfa.rs | 91 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/cli/src/generate/nfa.rs b/cli/src/generate/nfa.rs index 233d5def..bcc05793 100644 --- a/cli/src/generate/nfa.rs +++ b/cli/src/generate/nfa.rs @@ -128,6 +128,13 @@ impl CharacterSet { if range.end >= start { range.end = range.end.max(end); range.start = range.start.min(start); + + // Join this range with the next range if needed. + while i + 1 < self.ranges.len() && self.ranges[i + 1].start <= self.ranges[i].end { + self.ranges[i].end = self.ranges[i].end.max(self.ranges[i + 1].end); + self.ranges.remove(i + 1); + } + return i; } i += 1; @@ -549,6 +556,65 @@ impl<'a> NfaCursor<'a> { mod tests { use super::*; + #[test] + fn test_adding_ranges() { + let mut set = CharacterSet::empty() + .add_range('c', 'm') + .add_range('q', 's'); + + // within existing range + set = set.add_char('d'); + assert_eq!( + set, + CharacterSet::empty() + .add_range('c', 'm') + .add_range('q', 's') + ); + + // at end of existing range + set = set.add_char('m'); + assert_eq!( + set, + CharacterSet::empty() + .add_range('c', 'm') + .add_range('q', 's') + ); + + // adjacent to end of existing range + set = set.add_char('n'); + assert_eq!( + set, + CharacterSet::empty() + .add_range('c', 'n') + .add_range('q', 's') + ); + + // filling gap between existing ranges + set = set.add_range('o', 'p'); + assert_eq!(set, CharacterSet::empty().add_range('c', 's')); + + set = CharacterSet::empty() + .add_range('c', 'f') + .add_range('i', 'l') + .add_range('n', 'r'); + set = set.add_range('d', 'o'); + assert_eq!(set, CharacterSet::empty().add_range('c', 'r')); + } + + #[test] + fn test_adding_sets() { + let set1 = CharacterSet::empty() + .add_range('c', 'f') + .add_range('i', 'l'); + let set2 = CharacterSet::empty().add_range('b', 'g').add_char('h'); + assert_eq!( + set1.add(&set2), + CharacterSet::empty() + .add_range('b', 'g') + .add_range('h', 'l') + ); + } + #[test] fn test_group_transitions() { let table = [ @@ -799,6 +865,24 @@ mod tests { right_only: CharacterSet::empty(), intersection: CharacterSet::from_range('d', 'f'), }, + // [ L ] + // [R] + Row { + left: CharacterSet::from_range(',', '/'), + right: CharacterSet::from_char('/'), + left_only: CharacterSet::from_range(',', '.'), + right_only: CharacterSet::empty(), + intersection: CharacterSet::from_char('/'), + }, + // [ L ] + // [R] + Row { + left: CharacterSet::from_range(',', '/'), + right: CharacterSet::from_char('/'), + left_only: CharacterSet::from_range(',', '.'), + right_only: CharacterSet::empty(), + intersection: CharacterSet::from_char('/'), + }, // [ L1 ] [ L2 ] // [ R ] Row { @@ -908,6 +992,13 @@ mod tests { ); assert!(a.does_intersect(&b)); assert!(b.does_intersect(&a)); + + let (a, b) = ( + CharacterSet::from_range('c', 'f'), + CharacterSet::from_char('f'), + ); + assert!(a.does_intersect(&b)); + assert!(b.does_intersect(&a)); } #[test]