Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended LIST Syntax #8

Open
greymd opened this issue Jun 11, 2020 · 2 comments
Open

Extended LIST Syntax #8

greymd opened this issue Jun 11, 2020 · 2 comments
Labels
enhancement New feature or request

Comments

@greymd
Copy link
Owner

greymd commented Jun 11, 2020

echo ABCDE | teip -c '1,3,$-1'
=> [A]B[C][D]E

$-1 supposed to be 4, because $ means the number of field.

Instead of -, .. is used for specifying the range.

1..3 => 1,2,3

@greymd
Copy link
Owner Author

greymd commented Jun 28, 2020

just a idea. I want to keep compatibility with cut.

1~3 => same chunk
1-3 => three individual chunks

@greymd
Copy link
Owner Author

greymd commented Mar 11, 2023

My new idea is this.

1-5 .. Just select from 1 to 5 (default)
1~5 .. from 1 to 5 are explicitly merged
1:5 .. from 1 to 5 are explicitly separated

Keep - expression to keep backword compatibility.
That means, - behavior changes depending on the other consolidated options, which is current behavior.
With -c, merged, with -f separated.

On the other hand, ~ can explicitly merge chunks.
Such like..

$ echo AAA BBB CCC | teip -f 1~2
[AAA BBB] CCC

: can explicitly separate chunks on the other hand.

$ echo 123456789 | teip -f 2:5
1[2][3][4][5]6789

Syntax parser can be implemented something like that.
They are patch for v2.2.0.

diff --git a/src/list/ranges.rs b/src/list/ranges.rs
index 85b20fe..6f71bf5 100644
--- a/src/list/ranges.rs
+++ b/src/list/ranges.rs
@@ -1,10 +1,8 @@
 /*
- * This file is part of the uutils coreutils package.
+ * This file is based on the uutils coreutils package.
  *
- * (c) Rolf Morel <[email protected]>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
+ * For the full copyright and license information about
+ * the original file, please view the LICENSE
  */

 use std::str::FromStr;
@@ -13,6 +11,17 @@ use std::str::FromStr;
 pub struct Range {
     pub low: usize,
     pub high: usize,
+    pub join: RangeJoin,
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub enum RangeJoin {
+    /// 1-5 .. Just select from 1 to 5 (default)
+    Normal,
+    /// 1~5 .. from 1 to 5 are explicitly merged
+    Merge,
+    /// 1:5 .. from 1 to 5 are explicitly split
+    Split,
 }

 impl FromStr for Range {
@@ -20,8 +29,20 @@ impl FromStr for Range {

     fn from_str(s: &str) -> Result<Range, &'static str> {
         use std::usize::MAX;
-
-        let mut parts = s.splitn(2, '-');
+        let join: RangeJoin;
+
+        // check if s includes a ~ or a - and split on that
+        // if not, assume it's a single number
+        let mut parts = if s.contains('~') {
+            join = RangeJoin::Merge;
+            s.splitn(2, '~')
+        } else if s.contains(':') {
+            join = RangeJoin::Split;
+            s.splitn(2, ':')
+        } else {
+            join = RangeJoin::Normal;
+            s.splitn(2, '-')
+        };

         let field = "fields and positions are numbered from 1";
         let order = "high end of range less than low end";
@@ -31,7 +52,7 @@ impl FromStr for Range {
             (Some(nm), None) => {
                 if let Ok(nm) = nm.parse::<usize>() {
                     if nm > 0 {
-                        Ok(Range { low: nm, high: nm })
+                        Ok(Range { low: nm, high: nm, join: RangeJoin::Normal })
                     } else {
                         Err(field)
                     }
@@ -42,7 +63,7 @@ impl FromStr for Range {
             (Some(n), Some(m)) if m.is_empty() => {
                 if let Ok(low) = n.parse::<usize>() {
                     if low > 0 {
-                        Ok(Range { low, high: MAX - 1 })
+                        Ok(Range { low, high: MAX - 1, join })
                     } else {
                         Err(field)
                     }
@@ -53,7 +74,7 @@ impl FromStr for Range {
             (Some(n), Some(m)) if n.is_empty() => {
                 if let Ok(high) = m.parse::<usize>() {
                     if high > 0 {
-                        Ok(Range { low: 1, high })
+                        Ok(Range { low: 1, high, join })
                     } else {
                         Err(field)
                     }
@@ -64,7 +85,7 @@ impl FromStr for Range {
             (Some(n), Some(m)) => match (n.parse::<usize>(), m.parse::<usize>()) {
                 (Ok(low), Ok(high)) => {
                     if low > 0 && low <= high {
-                        Ok(Range { low, high })
+                        Ok(Range { low, high, join })
                     } else if low == 0 {
                         Err(field)
                     } else {
@@ -111,11 +132,14 @@ pub fn complement(ranges: &[Range]) -> Vec<Range> {
     use std::usize;

     let mut complements = Vec::with_capacity(ranges.len() + 1);
+    // Use the default join type to keep back compatibility
+    const DEF_JOIN: RangeJoin = RangeJoin::Normal;

     if !ranges.is_empty() && ranges[0].low > 1 {
         complements.push(Range {
             low: 1,
             high: ranges[0].low - 1,
+            join: DEF_JOIN,
         });
     }

@@ -127,6 +151,7 @@ pub fn complement(ranges: &[Range]) -> Vec<Range> {
                     complements.push(Range {
                         low: left.high + 1,
                         high: right.low - 1,
+                        join: DEF_JOIN,
                     });
                 }
             }
@@ -135,6 +160,7 @@ pub fn complement(ranges: &[Range]) -> Vec<Range> {
                     complements.push(Range {
                         low: last.high + 1,
                         high: usize::MAX - 1,
+                        join: DEF_JOIN,
                     });
                 }
             }
diff --git a/src/list/converter.rs b/src/list/converter.rs
index b2841c1..5e17b1a 100644
--- a/src/list/converter.rs
+++ b/src/list/converter.rs
@@ -11,10 +11,80 @@ pub fn to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
 #[cfg(test)]
 mod test {
     use super::*;
+    use ranges::RangeJoin::{Merge, Normal, Split};
     #[test]
     fn test_to_ranges() {
         let range = to_ranges("2-5,1-8", false).unwrap();
         assert_eq!(range[0].low, 1);
         assert_eq!(range[0].high, 8);
     }
+    #[test]
+    fn test_to_ranges_merge_unmerge() {
+        let range = to_ranges("1-3,4~6", false).unwrap();
+        assert_eq!(range[0].low, 1);
+        assert_eq!(range[0].high, 3);
+        assert_eq!(range[0].join, Normal);
+        assert_eq!(range[1].low, 4);
+        assert_eq!(range[1].high, 6);
+        assert_eq!(range[1].join, Merge);
+    }
+    #[test]
+    fn test_to_ranges_unsort() {
+        let range = to_ranges("4,1-3,5~7", false).unwrap();
+        println!("{:?}", range);
+        assert_eq!(range[0].low, 1);
+        assert_eq!(range[0].high, 3);
+        assert_eq!(range[0].join, Normal);
+        assert_eq!(range[1].low, 4);
+        assert_eq!(range[1].high, 4);
+        assert_eq!(range[1].join, Normal);
+        assert_eq!(range[2].low, 5);
+        assert_eq!(range[2].high, 7);
+        assert_eq!(range[2].join, Merge);
+    }
+    #[test]
+    fn test_to_ranges_split() {
+        let range = to_ranges("1,2,3,4", false).unwrap();
+        println!("{:?}", range);
+        for i in 0..4 {
+            assert_eq!(range[i].low, i + 1);
+            assert_eq!(range[i].high, i + 1);
+            assert_eq!(range[i].join, Normal);
+        }
+    }
+    #[test]
+    fn test_to_ranges_split_unsort() {
+        let range = to_ranges("5,3,4,1,2", false).unwrap();
+        println!("{:?}", range);
+        for i in 0..5 {
+            assert_eq!(range[i].low, i + 1);
+            assert_eq!(range[i].high, i + 1);
+            assert_eq!(range[i].join, Normal);
+        }
+    }
+    #[test]
+    fn test_to_ranges_overwrap() {
+        let range = to_ranges("1-3,2~5", false).unwrap();
+        println!("{:?}", range);
+        assert_eq!(range[0].low, 1);
+        assert_eq!(range[0].high, 5);
+        assert_eq!(range[0].join, Normal);
+    }
+    #[test]
+    fn test_to_ranges_three_different_range() {
+        let range = to_ranges("1-3,5:10,12,13~15", false).unwrap();
+        println!("{:?}", range);
+        assert_eq!(range[0].low, 1);
+        assert_eq!(range[0].high, 3);
+        assert_eq!(range[0].join, Normal);
+        assert_eq!(range[1].low, 5);
+        assert_eq!(range[1].high, 10);
+        assert_eq!(range[1].join, Split);
+        assert_eq!(range[2].low, 12);
+        assert_eq!(range[2].high, 12);
+        assert_eq!(range[2].join, Normal);
+        assert_eq!(range[3].low, 13);
+        assert_eq!(range[3].high, 15);
+        assert_eq!(range[3].join, Merge);
+    }
 }

@greymd greymd changed the title LIST Syntax support relative location Extended LIST Syntax Mar 11, 2023
@greymd greymd added the enhancement New feature or request label Mar 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant