From 41ec1aa8d99bce8bf7c3b8e52ca0fe559a6a983c Mon Sep 17 00:00:00 2001
From: Xavier L'Heureux <xavier.lheureux@icloud.com>
Date: Fri, 5 Jul 2019 08:48:01 -0400
Subject: [PATCH] Expand lazily the indexes and separate the parser from
 expansion

---
 src/lib/expansion/methods/arrays.rs  | 131 ++++++----------
 src/lib/expansion/methods/strings.rs | 132 +++++++---------
 src/lib/expansion/mod.rs             | 187 +++++++++++++----------
 src/lib/expansion/words/mod.rs       | 215 ++++++++++++---------------
 src/lib/expansion/words/tests.rs     | 120 ++++++---------
 src/lib/shell/shell_expand.rs        |  16 +-
 src/lib/shell/variables.rs           |  12 +-
 7 files changed, 355 insertions(+), 458 deletions(-)

diff --git a/src/lib/expansion/methods/arrays.rs b/src/lib/expansion/methods/arrays.rs
index e78d8268..f95c451f 100644
--- a/src/lib/expansion/methods/arrays.rs
+++ b/src/lib/expansion/methods/arrays.rs
@@ -1,9 +1,5 @@
 use super::{
-    super::{
-        is_expression,
-        words::{Select, SelectWithSize},
-        Error, Expander, Index,
-    },
+    super::{is_expression, words::Select, Error, Expander, ExpanderInternal, Index},
     strings::unescape,
     MethodError, Pattern,
 };
@@ -16,7 +12,7 @@ pub struct ArrayMethod<'a> {
     method:    &'a str,
     variable:  &'a str,
     pattern:   Pattern<'a>,
-    selection: Select<types::Str>,
+    selection: Option<&'a str>,
 }
 
 impl<'a> ArrayMethod<'a> {
@@ -24,7 +20,7 @@ impl<'a> ArrayMethod<'a> {
         method: &'a str,
         variable: &'a str,
         pattern: Pattern<'a>,
-        selection: Select<types::Str>,
+        selection: Option<&'a str>,
     ) -> Self {
         Self { method, variable, pattern, selection }
     }
@@ -41,30 +37,28 @@ impl<'a> ArrayMethod<'a> {
 
     fn chars<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> {
         let variable = self.resolve_var(expand_func)?;
-        let len = variable.chars().count();
-        Ok(variable.chars().map(|c| types::Str::from(c.to_string())).select(&self.selection, len))
+        expand_func
+            .slice_array(variable.chars().map(|c| types::Str::from(c.to_string())), &self.selection)
     }
 
     fn bytes<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> {
         let variable = self.resolve_var(expand_func)?;
-        let len = variable.len();
-        Ok(variable.bytes().map(|b| types::Str::from(b.to_string())).select(&self.selection, len))
+        expand_func
+            .slice_array(variable.bytes().map(|b| types::Str::from(b.to_string())), &self.selection)
     }
 
     fn map_keys<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Args, Error<E::Error>> {
-        expand_func.map_keys(self.variable, &self.selection)
+        expand_func.slice_array(expand_func.map_keys(self.variable)?.into_iter(), &self.selection)
     }
 
     fn map_values<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Args, Error<E::Error>> {
-        expand_func.map_values(self.variable, &self.selection)
+        expand_func.slice_array(expand_func.map_values(self.variable)?.into_iter(), &self.selection)
     }
 
     fn graphemes<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> {
         let variable = self.resolve_var(expand_func)?;
-        let graphemes: Vec<types::Str> =
-            UnicodeSegmentation::graphemes(variable.as_str(), true).map(From::from).collect();
-        let len = graphemes.len();
-        Ok(graphemes.into_iter().select(&self.selection, len))
+        let graphemes = UnicodeSegmentation::graphemes(variable.as_str(), true);
+        expand_func.slice_array(graphemes, &self.selection)
     }
 
     fn split_at<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> {
@@ -98,19 +92,17 @@ impl<'a> ArrayMethod<'a> {
 
     fn split<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> {
         let variable = self.resolve_var(expand_func)?;
-        let data: Args = match self.pattern {
-            Pattern::Whitespace => variable
-                .split(char::is_whitespace)
-                .filter(|x| !x.is_empty())
-                .map(From::from)
-                .collect(),
-            Pattern::StringPattern(pattern) => variable
-                .split(unescape(&expand_func.expand_string(pattern)?.join(" ")).as_str())
-                .map(From::from)
-                .collect(),
-        };
-        let len = data.len();
-        Ok(data.into_iter().select(&self.selection, len))
+        match self.pattern {
+            Pattern::Whitespace => {
+                let data = variable.split(char::is_whitespace).filter(|x| !x.is_empty());
+                expand_func.slice_array(data, &self.selection)
+            }
+            Pattern::StringPattern(pattern) => {
+                let escape = unescape(&expand_func.expand_string(pattern)?.join(" "));
+                let data = variable.split(escape.as_str());
+                expand_func.slice_array(data, &self.selection)
+            }
+        }
     }
 
     #[inline]
@@ -168,16 +160,12 @@ impl<'a> ArrayMethod<'a> {
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::{
-        expansion::test::DummyExpander,
-        ranges::{Index, Range},
-        types,
-    };
+    use crate::{expansion::test::DummyExpander, types};
 
     #[test]
     fn test_split_string_all() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Select::All);
+        let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), None);
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FO AR");
     }
@@ -185,7 +173,7 @@ mod test {
     #[test]
     fn test_split_whitespace_all() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Select::All);
+        let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, None);
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOO BAR");
     }
@@ -193,12 +181,7 @@ mod test {
     #[test]
     fn test_split_string_index_forward() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new(
-            "split",
-            "$FOO",
-            Pattern::StringPattern("OB"),
-            Select::Index(Index::Forward(1)),
-        );
+        let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Some("1"));
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "AR");
     }
@@ -206,12 +189,7 @@ mod test {
     #[test]
     fn test_split_whitespace_index_forward() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new(
-            "split",
-            "$SPACEDFOO",
-            Pattern::Whitespace,
-            Select::Index(Index::Forward(1)),
-        );
+        let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("1"));
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "BAR");
     }
@@ -219,12 +197,7 @@ mod test {
     #[test]
     fn test_split_string_index_backward() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new(
-            "split",
-            "$FOO",
-            Pattern::StringPattern("OB"),
-            Select::Index(Index::Backward(1)),
-        );
+        let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Some("-2"));
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FO");
     }
@@ -232,12 +205,7 @@ mod test {
     #[test]
     fn test_split_whitespace_index_backward() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new(
-            "split",
-            "$SPACEDFOO",
-            Pattern::Whitespace,
-            Select::Index(Index::Backward(1)),
-        );
+        let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("-2"));
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOO");
     }
@@ -245,12 +213,7 @@ mod test {
     #[test]
     fn test_split_string_range() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new(
-            "split",
-            "$FOO",
-            Pattern::StringPattern("OB"),
-            Select::Range(Range::from(Index::Forward(0))),
-        );
+        let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Some("0.."));
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FO AR");
     }
@@ -258,12 +221,7 @@ mod test {
     #[test]
     fn test_split_whitespace_range() {
         let mut output = types::Str::new();
-        let method = ArrayMethod::new(
-            "split",
-            "$SPACEDFOO",
-            Pattern::Whitespace,
-            Select::Range(Range::from(Index::Forward(0))),
-        );
+        let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("0.."));
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOO BAR");
     }
@@ -271,42 +229,39 @@ mod test {
     #[test]
     fn test_split_key() {
         let mut output = types::Str::new();
-        let method =
-            ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Select::Key("1".into()));
+        let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("\"1\""));
         method.handle(&mut output, &DummyExpander).unwrap();
-        assert_eq!(&*output, "");
+        assert_eq!(&*output, "BAR");
     }
 
     #[test]
     fn test_split_at_failing_whitespace() {
-        let method = ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::Whitespace, Select::All);
+        let method = ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::Whitespace, None);
         assert!(method.handle_as_array(&DummyExpander).is_err());
     }
 
     #[test]
     fn test_split_at_failing_no_number() {
-        let method =
-            ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("a"), Select::All);
+        let method = ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("a"), None);
         assert!(method.handle_as_array(&DummyExpander).is_err());
     }
 
     #[test]
     fn test_split_at_failing_out_of_bound() {
         let method =
-            ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("100"), Select::All);
+            ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("100"), None);
         assert!(method.handle_as_array(&DummyExpander).is_err());
     }
 
     #[test]
     fn test_split_at_succeeding() {
-        let method = ArrayMethod::new("split_at", "$FOO", Pattern::StringPattern("3"), Select::All);
+        let method = ArrayMethod::new("split_at", "$FOO", Pattern::StringPattern("3"), None);
         assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), args!["FOO", "BAR"]);
     }
 
     #[test]
     fn test_graphemes() {
-        let method =
-            ArrayMethod::new("graphemes", "$FOO", Pattern::StringPattern("3"), Select::All);
+        let method = ArrayMethod::new("graphemes", "$FOO", Pattern::StringPattern("3"), None);
         assert_eq!(
             method.handle_as_array(&DummyExpander).unwrap(),
             args!["F", "O", "O", "B", "A", "R"]
@@ -315,7 +270,7 @@ mod test {
 
     #[test]
     fn test_bytes() {
-        let method = ArrayMethod::new("bytes", "$FOO", Pattern::StringPattern("3"), Select::All);
+        let method = ArrayMethod::new("bytes", "$FOO", Pattern::StringPattern("3"), None);
         assert_eq!(
             method.handle_as_array(&DummyExpander).unwrap(),
             args!["70", "79", "79", "66", "65", "82"]
@@ -324,7 +279,7 @@ mod test {
 
     #[test]
     fn test_chars() {
-        let method = ArrayMethod::new("chars", "$FOO", Pattern::StringPattern("3"), Select::All);
+        let method = ArrayMethod::new("chars", "$FOO", Pattern::StringPattern("3"), None);
         assert_eq!(
             method.handle_as_array(&DummyExpander).unwrap(),
             args!["F", "O", "O", "B", "A", "R"]
@@ -333,15 +288,13 @@ mod test {
 
     #[test]
     fn test_lines() {
-        let method =
-            ArrayMethod::new("lines", "$MULTILINE", Pattern::StringPattern("3"), Select::All);
+        let method = ArrayMethod::new("lines", "$MULTILINE", Pattern::StringPattern("3"), None);
         assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), args!["FOO", "BAR"]);
     }
 
     #[test]
     fn test_reverse() {
-        let method =
-            ArrayMethod::new("reverse", "@ARRAY", Pattern::StringPattern("3"), Select::All);
+        let method = ArrayMethod::new("reverse", "@ARRAY", Pattern::StringPattern("3"), None);
         assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), args!["c", "b", "a"]);
     }
 }
diff --git a/src/lib/expansion/methods/strings.rs b/src/lib/expansion/methods/strings.rs
index 0dc64b7b..806634a4 100644
--- a/src/lib/expansion/methods/strings.rs
+++ b/src/lib/expansion/methods/strings.rs
@@ -87,7 +87,7 @@ pub struct StringMethod<'a> {
     /// Pattern to use for certain methods
     pub pattern: &'a str,
     /// Selection to use to control the output of this method
-    pub selection: Select<types::Str>,
+    pub selection: Option<&'a str>,
 }
 
 impl<'a> StringMethod<'a> {
@@ -223,18 +223,12 @@ impl<'a> StringMethod<'a> {
             "join" => {
                 let pattern = pattern.join(" ")?;
                 match expand.array(variable, &Select::All) {
-                    Ok(array) => <E as ExpanderInternal>::slice(
+                    Ok(array) => expand.slice(output, array.join(&pattern), &self.selection)?,
+                    Err(Error::VarNotFound) if is_expression(variable) => expand.slice(
                         output,
-                        array.join(&pattern),
+                        expand.expand_string(variable)?.join(&pattern),
                         &self.selection,
-                    ),
-                    Err(Error::VarNotFound) if is_expression(variable) => {
-                        <E as ExpanderInternal>::slice(
-                            output,
-                            expand.expand_string(variable)?.join(&pattern),
-                            &self.selection,
-                        )
-                    }
+                    )?,
                     Err(why) => return Err(why),
                 }
             }
@@ -368,7 +362,7 @@ mod test {
             method:    "ends_with",
             variable:  "$FOO",
             pattern:   "\"BAR\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "1");
@@ -381,7 +375,7 @@ mod test {
             method:    "ends_with",
             variable:  "$FOO",
             pattern:   "\"BA\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "0");
@@ -394,7 +388,7 @@ mod test {
             method:    "contains",
             variable:  "$FOO",
             pattern:   "\"OBA\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "1");
@@ -407,7 +401,7 @@ mod test {
             method:    "contains",
             variable:  "$FOO",
             pattern:   "\"OBI\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "0");
@@ -420,7 +414,7 @@ mod test {
             method:    "starts_with",
             variable:  "$FOO",
             pattern:   "\"FOO\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "1");
@@ -433,7 +427,7 @@ mod test {
             method:    "starts_with",
             variable:  "$FOO",
             pattern:   "\"OO\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "0");
@@ -446,7 +440,7 @@ mod test {
             method:    "basename",
             variable:  "\"/home/redox/file.txt\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "file.txt");
@@ -459,7 +453,7 @@ mod test {
             method:    "extension",
             variable:  "\"/home/redox/file.txt\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "txt");
@@ -472,7 +466,7 @@ mod test {
             method:    "filename",
             variable:  "\"/home/redox/file.txt\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "file");
@@ -485,7 +479,7 @@ mod test {
             method:    "parent",
             variable:  "\"/home/redox/file.txt\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "/home/redox");
@@ -498,7 +492,7 @@ mod test {
             method:    "to_lowercase",
             variable:  "\"Ford Prefect\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "ford prefect");
@@ -511,7 +505,7 @@ mod test {
             method:    "to_uppercase",
             variable:  "\"Ford Prefect\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FORD PREFECT");
@@ -524,7 +518,7 @@ mod test {
             method:    "trim",
             variable:  "\"  Foo Bar \"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "Foo Bar");
@@ -533,12 +527,8 @@ mod test {
     #[test]
     fn test_trim_with_variable() {
         let mut output = types::Str::new();
-        let method = StringMethod {
-            method:    "trim",
-            variable:  "$BAZ",
-            pattern:   "",
-            selection: Select::All,
-        };
+        let method =
+            StringMethod { method: "trim", variable: "$BAZ", pattern: "", selection: None };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "BARBAZ");
     }
@@ -550,7 +540,7 @@ mod test {
             method:    "trim_right",
             variable:  "\"  Foo Bar \"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "  Foo Bar");
@@ -563,7 +553,7 @@ mod test {
             method:    "trim_right",
             variable:  "$BAZ",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "  BARBAZ");
@@ -576,7 +566,7 @@ mod test {
             method:    "trim_left",
             variable:  "\"  Foo Bar \"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "Foo Bar ");
@@ -589,7 +579,7 @@ mod test {
             method:    "trim_left",
             variable:  "$BAZ",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "BARBAZ   ");
@@ -602,7 +592,7 @@ mod test {
             method:    "repeat",
             variable:  "$FOO",
             pattern:   "2",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOOBARFOOBAR");
@@ -616,7 +606,7 @@ mod test {
             method:    "repeat",
             variable:  "$FOO",
             pattern:   "-2",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
     }
@@ -628,7 +618,7 @@ mod test {
             method:    "replace",
             variable:  "$FOO",
             pattern:   "[\"FOO\" \"BAR\"]",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "BARBAR");
@@ -642,7 +632,7 @@ mod test {
             method:    "replace",
             variable:  "$FOO",
             pattern:   "[]",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
     }
@@ -654,7 +644,7 @@ mod test {
             method:    "replacen",
             variable:  "\"FOO$FOO\"",
             pattern:   "[\"FOO\" \"BAR\" 1]",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "BARFOOBAR");
@@ -668,7 +658,7 @@ mod test {
             method:    "replacen",
             variable:  "$FOO",
             pattern:   "[]",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
     }
@@ -680,7 +670,7 @@ mod test {
             method:    "regex_replace",
             variable:  "$FOO",
             pattern:   "[\"^F\" \"f\"]",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "fOOBAR");
@@ -693,7 +683,7 @@ mod test {
             method:    "regex_replace",
             variable:  "$FOO",
             pattern:   "[\"^f\" \"F\"]",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOOBAR");
@@ -706,7 +696,7 @@ mod test {
             method:    "join",
             variable:  "[\"FOO\" \"BAR\"]",
             pattern:   "\" \"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOO BAR");
@@ -719,7 +709,7 @@ mod test {
             method:    "join",
             variable:  "[\"FOO\" \"BAR\"]",
             pattern:   "[\"-\" \"-\"]",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOO- -BAR");
@@ -728,12 +718,8 @@ mod test {
     #[test]
     fn test_len_with_array() {
         let mut output = types::Str::new();
-        let method = StringMethod {
-            method:    "len",
-            variable:  "[\"1\"]",
-            pattern:   "",
-            selection: Select::All,
-        };
+        let method =
+            StringMethod { method: "len", variable: "[\"1\"]", pattern: "", selection: None };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "1");
     }
@@ -741,12 +727,8 @@ mod test {
     #[test]
     fn test_len_with_string() {
         let mut output = types::Str::new();
-        let method = StringMethod {
-            method:    "len",
-            variable:  "\"FOO\"",
-            pattern:   "",
-            selection: Select::All,
-        };
+        let method =
+            StringMethod { method: "len", variable: "\"FOO\"", pattern: "", selection: None };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "3");
     }
@@ -754,12 +736,8 @@ mod test {
     #[test]
     fn test_len_with_variable() {
         let mut output = types::Str::new();
-        let method = StringMethod {
-            method:    "len",
-            variable:  "$FOO",
-            pattern:   "",
-            selection: Select::All,
-        };
+        let method =
+            StringMethod { method: "len", variable: "$FOO", pattern: "", selection: None };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "6");
     }
@@ -771,7 +749,7 @@ mod test {
             method:    "len_bytes",
             variable:  "$FOO",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "6");
@@ -784,7 +762,7 @@ mod test {
             method:    "len_bytes",
             variable:  "\"oh là là\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "10");
@@ -797,7 +775,7 @@ mod test {
             method:    "reverse",
             variable:  "$FOO",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "RABOOF");
@@ -810,7 +788,7 @@ mod test {
             method:    "reverse",
             variable:  "\"FOOBAR\"",
             pattern:   "",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "RABOOF");
@@ -823,7 +801,7 @@ mod test {
             method:    "find",
             variable:  "$FOO",
             pattern:   "\"O\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "1");
@@ -836,7 +814,7 @@ mod test {
             method:    "find",
             variable:  "$FOO",
             pattern:   "\"L\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "-1");
@@ -849,7 +827,7 @@ mod test {
             method:    "or",
             variable:  "$NDIUKFBINCF",
             pattern:   "\"baz\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "baz");
@@ -862,7 +840,7 @@ mod test {
             method:    "or",
             variable:  "$EMPTY",
             pattern:   "\"baz\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "baz");
@@ -875,7 +853,7 @@ mod test {
             method:    "or",
             variable:  "$FOO",
             pattern:   "\"baz\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOOBAR");
@@ -888,7 +866,7 @@ mod test {
             method:    "or",
             variable:  "$EMPTY",
             pattern:   "\"bar\", \"baz\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "bar");
@@ -901,7 +879,7 @@ mod test {
             method:    "or",
             variable:  "$EMPTY",
             pattern:   "\"\", \"baz\"",
-            selection: Select::All,
+            selection: None,
         };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "baz");
@@ -910,12 +888,8 @@ mod test {
     #[test]
     fn test_or_no_pattern() {
         let mut output = types::Str::new();
-        let method = StringMethod {
-            method:    "or",
-            variable:  "$FOO",
-            pattern:   "\"\"",
-            selection: Select::All,
-        };
+        let method =
+            StringMethod { method: "or", variable: "$FOO", pattern: "\"\"", selection: None };
         method.handle(&mut output, &DummyExpander).unwrap();
         assert_eq!(&*output, "FOOBAR");
     }
diff --git a/src/lib/expansion/mod.rs b/src/lib/expansion/mod.rs
index 8033b628..9bded892 100644
--- a/src/lib/expansion/mod.rs
+++ b/src/lib/expansion/mod.rs
@@ -129,28 +129,13 @@ pub trait Expander: Sized {
     /// Expand a subshell expression.
     fn command(&self, _command: &str) -> Result<types::Str, Self::Error>;
     /// Iterating upon key-value maps.
-    fn map_keys(&self, _name: &str, _select: &Select<types::Str>) -> Result<Args, Self::Error>;
+    fn map_keys(&self, _name: &str) -> Result<Args, Self::Error>;
     /// Iterating upon key-value maps.
-    fn map_values(&self, _name: &str, _select: &Select<types::Str>) -> Result<Args, Self::Error>;
+    fn map_values(&self, _name: &str) -> Result<Args, Self::Error>;
     /// Get a string that exists in the shell.
     fn get_string(&self, value: &str) -> Result<types::Str, Self::Error> {
         Ok(self.expand_string(value)?.join(" ").into())
     }
-    /// Select the proper values from an iterator
-    fn select<I: Iterator<Item = types::Str>>(
-        vals: I,
-        select: &Select<types::Str>,
-        n: usize,
-    ) -> Option<Args> {
-        match select {
-            Select::All => Some(vals.collect()),
-            Select::Range(range) => range
-                .bounds(n)
-                .filter(|&(start, _)| n > start)
-                .map(|(start, length)| vals.skip(start).take(length).collect()),
-            _ => None,
-        }
-    }
 
     /// Get an array that exists in the shell.
     fn get_array(&self, value: &str) -> Result<Args, Self::Error> { self.expand_string(value) }
@@ -166,8 +151,7 @@ pub trait Expander: Sized {
         let mut token_buffer = Vec::new();
         let mut contains_brace = false;
 
-        for word in WordIterator::new(original, self, true) {
-            let word = word?;
+        for word in WordIterator::new(original, true) {
             if let WordToken::Brace(_) = word {
                 contains_brace = true;
             }
@@ -181,14 +165,14 @@ pub trait Expander: Sized {
 impl<T: Expander> ExpanderInternal for T {}
 
 trait ExpanderInternal: Expander {
-    fn expand_process(
+    fn expand_process<'a>(
         &self,
         current: &mut types::Str,
         command: &str,
-        selection: &Select<types::Str>,
+        selection: &Option<&'a str>,
     ) -> Result<(), Self::Error> {
-        self.command(command)
-            .map(|result| Self::slice(current, result.trim_end_matches('\n'), selection))
+        let result = self.command(command)?;
+        self.slice(current, result.trim_end_matches('\n'), selection)
     }
 
     fn expand_brace(
@@ -224,8 +208,14 @@ trait ExpanderInternal: Expander {
     fn array_expand(
         &self,
         elements: &[&str],
-        selection: &Select<types::Str>,
+        selection: &Option<&str>,
     ) -> Result<Args, Self::Error> {
+        let selection = if let Some(selection) = selection {
+            let value = self.expand_string(selection)?.join(" ");
+            value.parse::<Select<types::Str>>().map_err(|_| Error::IndexParsingError(value))?
+        } else {
+            Select::All
+        };
         match selection {
             Select::All => {
                 let mut collected = Args::new();
@@ -234,8 +224,8 @@ trait ExpanderInternal: Expander {
                 }
                 Ok(collected)
             }
-            Select::Index(index) => self.array_nth(elements, *index).map(|el| args![el]),
-            Select::Range(range) => self.array_range(elements, *range),
+            Select::Index(index) => self.array_nth(elements, index).map(|el| args![el]),
+            Select::Range(range) => self.array_range(elements, range),
             Select::Key(_) => Err(Error::OutOfBound),
         }
     }
@@ -277,41 +267,70 @@ trait ExpanderInternal: Expander {
         }
     }
 
-    fn slice<S: AsRef<str>>(output: &mut types::Str, expanded: S, selection: &Select<types::Str>) {
-        match selection {
-            Select::All => output.push_str(expanded.as_ref()),
-            Select::Index(Index::Forward(id)) => {
-                if let Some(character) =
-                    UnicodeSegmentation::graphemes(expanded.as_ref(), true).nth(*id)
-                {
-                    output.push_str(character);
+    fn slice_array<'a, S: Into<types::Str>, T: Iterator<Item = S>>(
+        &self,
+        expanded: T,
+        selection: &Option<&'a str>,
+    ) -> Result<Args, Self::Error> {
+        if let Some(selection) = selection {
+            let value = self.expand_string(selection)?.join(" ");
+            let selection =
+                value.parse::<Select<types::Str>>().map_err(|_| Error::IndexParsingError(value))?;
+            let expanded: Vec<_> = expanded.collect();
+            let len = expanded.len();
+            Ok(expanded.into_iter().map(Into::into).select(&selection, len))
+        } else {
+            Ok(expanded.map(Into::into).collect())
+        }
+    }
+
+    fn slice<'a, S: AsRef<str>>(
+        &self,
+        output: &mut types::Str,
+        expanded: S,
+        selection: &Option<&'a str>,
+    ) -> Result<(), Self::Error> {
+        if let Some(selection) = selection {
+            let value = self.expand_string(selection)?.join(" ");
+            let selection =
+                value.parse::<Select<types::Str>>().map_err(|_| Error::IndexParsingError(value))?;
+            match selection {
+                Select::All => output.push_str(expanded.as_ref()),
+                Select::Index(Index::Forward(id)) => {
+                    if let Some(character) =
+                        UnicodeSegmentation::graphemes(expanded.as_ref(), true).nth(id)
+                    {
+                        output.push_str(character);
+                    }
                 }
-            }
-            Select::Index(Index::Backward(id)) => {
-                if let Some(character) =
-                    UnicodeSegmentation::graphemes(expanded.as_ref(), true).rev().nth(*id)
-                {
-                    output.push_str(character);
+                Select::Index(Index::Backward(id)) => {
+                    if let Some(character) =
+                        UnicodeSegmentation::graphemes(expanded.as_ref(), true).rev().nth(id)
+                    {
+                        output.push_str(character);
+                    }
                 }
-            }
-            Select::Range(range) => {
-                let graphemes = UnicodeSegmentation::graphemes(expanded.as_ref(), true);
-                if let Some((start, length)) = range.bounds(graphemes.clone().count()) {
-                    graphemes.skip(start).take(length).for_each(|str| {
-                        output.push_str(str.as_ref());
-                    });
+                Select::Range(range) => {
+                    let graphemes = UnicodeSegmentation::graphemes(expanded.as_ref(), true);
+                    if let Some((start, length)) = range.bounds(graphemes.clone().count()) {
+                        graphemes.skip(start).take(length).for_each(|str| {
+                            output.push_str(str.as_ref());
+                        });
+                    }
                 }
+                Select::Key(_) => (),
             }
-            Select::Key(_) => (),
-        }
+        } else {
+            output.push_str(expanded.as_ref())
+        };
+        Ok(())
     }
 
     fn expand_string_no_glob(&self, original: &str) -> Result<Args, Self::Error> {
         let mut token_buffer = Vec::new();
         let mut contains_brace = false;
 
-        for word in WordIterator::new(original, self, false) {
-            let word = word?;
+        for word in WordIterator::new(original, false) {
             if let WordToken::Brace(_) = word {
                 contains_brace = true;
             }
@@ -329,13 +348,14 @@ trait ExpanderInternal: Expander {
             WordToken::Array(ref elements, ref index) => {
                 self.array_expand(elements, index).map_err(Into::into)
             }
-            WordToken::ArrayVariable(array, quoted, Select::Key(ref key)) if key.contains(' ') => {
+            WordToken::ArrayVariable(array, quoted, Some(ref key)) if key.contains(' ') => {
                 if quoted {
                     let mut output = types::Str::new();
                     for index in key.split(' ') {
-                        let select = index
+                        let value = self.expand_string(index)?.join(" ");
+                        let select = value
                             .parse::<Select<types::Str>>()
-                            .map_err(|_| Error::IndexParsingError(index.into()))?;
+                            .map_err(|_| Error::IndexParsingError(value))?;
                         let _ = write!(
                             &mut output,
                             "{}",
@@ -348,16 +368,25 @@ trait ExpanderInternal: Expander {
                 } else {
                     let mut out = Args::with_capacity(10);
                     for index in key.split(' ') {
-                        let select = index
+                        let value = self.expand_string(index)?.join(" ");
+                        let select = value
                             .parse::<Select<types::Str>>()
-                            .map_err(|_| Error::IndexParsingError(index.into()))?;
+                            .map_err(|_| Error::IndexParsingError(value))?;
                         out.extend(self.array(array, &select)?);
                     }
                     Ok(out)
                 }
             }
             WordToken::ArrayVariable(array, quoted, ref index) => {
-                let array = self.array(array, index)?;
+                let index = if let Some(index) = index {
+                    let value = self.expand_string(index)?.join(" ");
+                    value
+                        .parse::<Select<types::Str>>()
+                        .map_err(|_| Error::IndexParsingError(value))?
+                } else {
+                    Select::All
+                };
+                let array = self.array(array, &index)?;
                 if quoted {
                     Ok(args![types::Str::from(array.join(" "))])
                 } else {
@@ -366,22 +395,17 @@ trait ExpanderInternal: Expander {
             }
             WordToken::ArrayProcess(command, quoted, ref index) => {
                 crate::IonPool::string(|output| {
-                    self.expand_process(output, command, &Select::All)?;
+                    self.expand_process(output, command, &None)?;
 
                     if quoted {
                         Ok(args!(format!(
                             "{}",
-                            output
-                                .split_whitespace()
-                                .select::<Vec<_>, _>(index, output.split_whitespace().count())
+                            self.slice_array(output.split_whitespace(), index)?
                                 .into_iter()
                                 .format(" ")
                         )))
                     } else {
-                        Ok(output
-                            .split_whitespace()
-                            .map(From::from)
-                            .select::<Args, _>(index, output.split_whitespace().count()))
+                        self.slice_array(output.split_whitespace(), index)
                     }
                 })
             }
@@ -411,7 +435,7 @@ trait ExpanderInternal: Expander {
                 self.expand_process(&mut output, command, index)?
             }
             WordToken::Variable(text, ref index) => {
-                Self::slice(&mut output, self.string(text)?, index);
+                self.slice(&mut output, self.string(text)?, index)?;
             }
             WordToken::Arithmetic(s) => self.expand_arithmetic(&mut output, s),
             _ => unreachable!(),
@@ -499,7 +523,7 @@ trait ExpanderInternal: Expander {
                         self.array_expand(elements, index)?.iter().format(" ")
                     );
                 }
-                WordToken::ArrayVariable(array, _, Select::Key(ref key)) if key.contains(' ') => {
+                WordToken::ArrayVariable(array, _, Some(ref key)) if key.contains(' ') => {
                     for index in key.split(' ') {
                         let select = index
                             .parse::<Select<types::Str>>()
@@ -514,7 +538,16 @@ trait ExpanderInternal: Expander {
                     output.pop(); // Pop out the last unneeded whitespace token
                 }
                 WordToken::ArrayVariable(array, _, ref index) => {
-                    let _ = write!(&mut output, "{}", self.array(array, index)?.iter().format(" "));
+                    let index = if let Some(index) = index {
+                        let value = self.expand_string(index)?.join(" ");
+                        value
+                            .parse::<Select<types::Str>>()
+                            .map_err(|_| Error::IndexParsingError(value))?
+                    } else {
+                        Select::All
+                    };
+                    let _ =
+                        write!(&mut output, "{}", self.array(array, &index)?.iter().format(" "));
                 }
                 WordToken::ArrayProcess(command, _, ref index)
                 | WordToken::Process(command, ref index) => {
@@ -542,7 +575,7 @@ trait ExpanderInternal: Expander {
                     output.push_str(text);
                 }
                 WordToken::Variable(text, ref index) => {
-                    Self::slice(&mut output, self.string(text)?, index);
+                    self.slice(&mut output, self.string(text)?, index)?;
                 }
                 WordToken::Arithmetic(s) => self.expand_arithmetic(&mut output, s),
             }
@@ -674,19 +707,11 @@ pub(crate) mod test {
 
         fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }
 
-        fn map_keys<'a>(
-            &'a self,
-            _name: &str,
-            _select: &Select<types::Str>,
-        ) -> Result<Args, Self::Error> {
+        fn map_keys<'a>(&'a self, _name: &str) -> Result<Args, Self::Error> {
             Err(Error::VarNotFound)
         }
 
-        fn map_values<'a>(
-            &'a self,
-            _name: &str,
-            _select: &Select<types::Str>,
-        ) -> Result<Args, Self::Error> {
+        fn map_values<'a>(&'a self, _name: &str) -> Result<Args, Self::Error> {
             Err(Error::VarNotFound)
         }
     }
@@ -696,12 +721,12 @@ pub(crate) mod test {
         let mut output = types::Str::new();
 
         let line = " Mary   had\ta little  \n\t lamb😉😉\t";
-        DummyExpander.expand_process(&mut output, line, &Select::All).unwrap();
+        DummyExpander.expand_process(&mut output, line, &None).unwrap();
         assert_eq!(output.as_str(), line);
 
         output.clear();
         let line = "foo not bar😉😉\n\n";
-        DummyExpander.expand_process(&mut output, line, &Select::All).unwrap();
+        DummyExpander.expand_process(&mut output, line, &None).unwrap();
         assert_eq!(output.as_str(), "foo not bar😉😉");
     }
 
diff --git a/src/lib/expansion/words/mod.rs b/src/lib/expansion/words/mod.rs
index 9409b5a3..da1e5453 100644
--- a/src/lib/expansion/words/mod.rs
+++ b/src/lib/expansion/words/mod.rs
@@ -1,12 +1,9 @@
 #[cfg(test)]
 mod tests;
 
-use super::{
-    methods::{ArrayMethod, Pattern, StringMethod},
-    Expander, Result,
-};
+use super::methods::{ArrayMethod, Pattern, StringMethod};
+use crate::parser::lexers::ArgumentSplitter;
 pub use crate::ranges::{Select, SelectWithSize};
-use crate::{parser::lexers::ArgumentSplitter, types};
 use std::borrow::Cow;
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
@@ -40,15 +37,15 @@ pub enum WordToken<'a> {
     /// Braced alternatives
     Brace(Vec<&'a str>),
     /// An array literal
-    Array(Vec<&'a str>, Select<types::Str>),
+    Array(Vec<&'a str>, Option<&'a str>),
     /// A scalar variable
-    Variable(&'a str, Select<types::Str>),
+    Variable(&'a str, Option<&'a str>),
     /// An array or map-like variable
-    ArrayVariable(&'a str, bool, Select<types::Str>),
+    ArrayVariable(&'a str, bool, Option<&'a str>),
     /// A process that should expand to an array
-    ArrayProcess(&'a str, bool, Select<types::Str>),
+    ArrayProcess(&'a str, bool, Option<&'a str>),
     /// A process that expands to a scalar value
-    Process(&'a str, Select<types::Str>),
+    Process(&'a str, Option<&'a str>),
     /// A method on a scalar value
     StringMethod(StringMethod<'a>),
     /// A method on a array value
@@ -59,16 +56,15 @@ pub enum WordToken<'a> {
 
 /// Iterate over the terminal tokens of the parsed text
 #[derive(Debug, PartialEq, Clone)]
-pub struct WordIterator<'a, E: Expander> {
-    data:      &'a str,
-    read:      usize,
-    quotes:    Quotes,
-    backsl:    bool,
-    expanders: &'a E,
-    do_glob:   bool,
+pub struct WordIterator<'a> {
+    data:    &'a str,
+    read:    usize,
+    quotes:  Quotes,
+    backsl:  bool,
+    do_glob: bool,
 }
 
-impl<'a, E: Expander + 'a> WordIterator<'a, E> {
+impl<'a> WordIterator<'a> {
     fn arithmetic_expression<I: Iterator<Item = u8>>(&mut self, iter: &mut I) -> WordToken<'a> {
         let mut paren: i8 = 0;
         let start = self.read;
@@ -138,7 +134,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
     }
 
     /// Contains the grammar for parsing array expression syntax
-    fn array<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error>
+    fn array<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
@@ -161,9 +157,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
 
                         return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
                             let _ = iterator.next();
-                            Ok(WordToken::Array(elements, self.read_selection(iterator)?))
+                            WordToken::Array(elements, Some(self.read_selection(iterator)))
                         } else {
-                            Ok(WordToken::Array(elements, Select::All))
+                            WordToken::Array(elements, None)
                         };
                     } else {
                         level -= 1;
@@ -218,7 +214,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
     }
 
     /// Contains the logic for parsing array subshell syntax.
-    fn array_process<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error>
+    fn array_process<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
@@ -243,17 +239,17 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
                         self.read += 1;
                         return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
                             let _ = iterator.next();
-                            Ok(WordToken::ArrayProcess(
+                            WordToken::ArrayProcess(
                                 array_process_contents,
                                 self.quotes == Quotes::Double,
-                                self.read_selection(iterator)?,
-                            ))
+                                Some(self.read_selection(iterator)),
+                            )
                         } else {
-                            Ok(WordToken::ArrayProcess(
+                            WordToken::ArrayProcess(
                                 array_process_contents,
                                 self.quotes == Quotes::Double,
-                                Select::All,
-                            ))
+                                None,
+                            )
                         };
                     } else {
                         level -= 1;
@@ -269,7 +265,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
     }
 
     /// Contains the logic for parsing subshell syntax.
-    fn process<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error>
+    fn process<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
@@ -305,9 +301,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
                         self.read += 1;
                         return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
                             let _ = iterator.next();
-                            Ok(WordToken::Process(output, self.read_selection(iterator)?))
+                            WordToken::Process(output, Some(self.read_selection(iterator)))
                         } else {
-                            Ok(WordToken::Process(output, Select::All))
+                            WordToken::Process(output, None)
                         };
                     } else {
                         level -= 1;
@@ -322,7 +318,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
         panic!("ion: fatal error with syntax validation: unterminated process");
     }
 
-    fn braced_array_variable<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error>
+    fn braced_array_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
@@ -331,11 +327,11 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
         while let Some(character) = iterator.next() {
             match character {
                 b'[' => {
-                    let result = Ok(WordToken::ArrayVariable(
+                    let result = WordToken::ArrayVariable(
                         &self.data[start..self.read],
                         self.quotes == Quotes::Double,
-                        self.read_selection(iterator)?,
-                    ));
+                        Some(self.read_selection(iterator)),
+                    );
                     self.read += 1;
                     if let Some(b'}') = iterator.next() {
                         return result;
@@ -348,33 +344,25 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
                 b'}' => {
                     let output = &self.data[start..self.read];
                     self.read += 1;
-                    return Ok(WordToken::ArrayVariable(
-                        output,
-                        self.quotes == Quotes::Double,
-                        Select::All,
-                    ));
+                    return WordToken::ArrayVariable(output, self.quotes == Quotes::Double, None);
                 }
                 // Only alphanumerical and underscores are allowed in variable names
                 0..=47 | 58..=64 | 91..=94 | 96 | 123..=127 => {
-                    return Ok(WordToken::ArrayVariable(
+                    return WordToken::ArrayVariable(
                         &self.data[start..self.read],
                         self.quotes == Quotes::Double,
-                        Select::All,
-                    ));
+                        None,
+                    );
                 }
                 _ => (),
             }
             self.read += 1;
         }
-        Ok(WordToken::ArrayVariable(
-            &self.data[start..],
-            self.quotes == Quotes::Double,
-            Select::All,
-        ))
+        WordToken::ArrayVariable(&self.data[start..], self.quotes == Quotes::Double, None)
     }
 
     /// Contains the logic for parsing array variable syntax
-    fn array_variable<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error>
+    fn array_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
@@ -408,25 +396,25 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
                                             self.data.as_bytes().get(self.read)
                                         {
                                             let _ = iterator.next();
-                                            Ok(WordToken::ArrayMethod(
+                                            WordToken::ArrayMethod(
                                                 ArrayMethod::new(
                                                     method,
                                                     variable.trim(),
                                                     Pattern::StringPattern(pattern),
-                                                    self.read_selection(iterator)?,
+                                                    Some(self.read_selection(iterator)),
                                                 ),
                                                 self.quotes == Quotes::Double,
-                                            ))
+                                            )
                                         } else {
-                                            Ok(WordToken::ArrayMethod(
+                                            WordToken::ArrayMethod(
                                                 ArrayMethod::new(
                                                     method,
                                                     variable.trim(),
                                                     Pattern::StringPattern(pattern),
-                                                    Select::All,
+                                                    None,
                                                 ),
                                                 self.quotes == Quotes::Double,
-                                            ))
+                                            )
                                         };
                                     }
                                     self.read += 1;
@@ -439,25 +427,25 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
 
                                 return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
                                     let _ = iterator.next();
-                                    Ok(WordToken::ArrayMethod(
+                                    WordToken::ArrayMethod(
                                         ArrayMethod::new(
                                             method,
                                             variable.trim(),
                                             Pattern::Whitespace,
-                                            self.read_selection(iterator)?,
+                                            Some(self.read_selection(iterator)),
                                         ),
                                         self.quotes == Quotes::Double,
-                                    ))
+                                    )
                                 } else {
-                                    Ok(WordToken::ArrayMethod(
+                                    WordToken::ArrayMethod(
                                         ArrayMethod::new(
                                             method,
                                             variable.trim(),
                                             Pattern::Whitespace,
-                                            Select::All,
+                                            None,
                                         ),
                                         self.quotes == Quotes::Double,
-                                    ))
+                                    )
                                 };
                             }
                             b')' => depth -= 1,
@@ -470,33 +458,29 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
                     panic!("ion: fatal error with syntax validation parsing: unterminated method");
                 }
                 b'[' => {
-                    return Ok(WordToken::ArrayVariable(
+                    return WordToken::ArrayVariable(
                         &self.data[start..self.read],
                         self.quotes == Quotes::Double,
-                        self.read_selection(iterator)?,
-                    ));
+                        Some(self.read_selection(iterator)),
+                    );
                 }
                 // Only alphanumerical and underscores are allowed in variable names
                 0..=47 | 58..=64 | 91..=94 | 96 | 123..=127 => {
-                    return Ok(WordToken::ArrayVariable(
+                    return WordToken::ArrayVariable(
                         &self.data[start..self.read],
                         self.quotes == Quotes::Double,
-                        Select::All,
-                    ));
+                        None,
+                    );
                 }
                 _ => (),
             }
             self.read += 1;
         }
 
-        Ok(WordToken::ArrayVariable(
-            &self.data[start..],
-            self.quotes == Quotes::Double,
-            Select::All,
-        ))
+        WordToken::ArrayVariable(&self.data[start..], self.quotes == Quotes::Double, None)
     }
 
-    fn read_selection<I>(&mut self, iterator: &mut I) -> Result<Select<types::Str>, E::Error>
+    fn read_selection<I>(&mut self, iterator: &mut I) -> &'a str
     where
         I: Iterator<Item = u8>,
     {
@@ -504,11 +488,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
         let start = self.read;
         for character in iterator {
             if let b']' = character {
-                let value = self.expanders.expand_string(&self.data[start..self.read])?.join(" ");
+                let value = &self.data[start..self.read];
                 self.read += 1;
-                return value
-                    .parse::<Select<types::Str>>()
-                    .map_err(|_| super::Error::IndexParsingError(value));
+                return value;
             }
             self.read += 1;
         }
@@ -517,7 +499,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
     }
 
     /// Contains the logic for parsing variable syntax
-    fn variable<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error>
+    fn variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
@@ -555,19 +537,19 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
                                             self.data.as_bytes().get(self.read)
                                         {
                                             let _ = iterator.next();
-                                            Ok(WordToken::StringMethod(StringMethod {
+                                            WordToken::StringMethod(StringMethod {
                                                 method,
                                                 variable: variable.trim(),
                                                 pattern,
-                                                selection: self.read_selection(iterator)?,
-                                            }))
+                                                selection: Some(self.read_selection(iterator)),
+                                            })
                                         } else {
-                                            Ok(WordToken::StringMethod(StringMethod {
+                                            WordToken::StringMethod(StringMethod {
                                                 method,
                                                 variable: variable.trim(),
                                                 pattern,
-                                                selection: Select::All,
-                                            }))
+                                                selection: None,
+                                            })
                                         };
                                     } else if character == b'(' {
                                         depth += 1;
@@ -585,19 +567,19 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
 
                                 return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
                                     let _ = iterator.next();
-                                    Ok(WordToken::StringMethod(StringMethod {
+                                    WordToken::StringMethod(StringMethod {
                                         method,
                                         variable: variable.trim(),
                                         pattern: " ",
-                                        selection: self.read_selection(iterator)?,
-                                    }))
+                                        selection: Some(self.read_selection(iterator)),
+                                    })
                                 } else {
-                                    Ok(WordToken::StringMethod(StringMethod {
+                                    WordToken::StringMethod(StringMethod {
                                         method,
                                         variable: variable.trim(),
                                         pattern: " ",
-                                        selection: Select::All,
-                                    }))
+                                        selection: None,
+                                    })
                                 };
                             }
                             b')' => depth -= 1,
@@ -614,9 +596,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
                     let variable = &self.data[start..self.read];
 
                     return if character == b'[' {
-                        Ok(WordToken::Variable(variable, self.read_selection(iterator)?))
+                        WordToken::Variable(variable, Some(self.read_selection(iterator)))
                     } else {
-                        Ok(WordToken::Variable(variable, Select::All))
+                        WordToken::Variable(variable, None)
                     };
                 }
                 _ => (),
@@ -624,7 +606,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
             self.read += 1;
         }
 
-        Ok(WordToken::Variable(&self.data[start..], Select::All))
+        WordToken::Variable(&self.data[start..], None)
     }
 
     // Contains the logic for parsing braced variables
@@ -637,7 +619,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
             if character == b'}' {
                 let output = &self.data[start..self.read];
                 self.read += 1;
-                return WordToken::Variable(output, Select::All);
+                return WordToken::Variable(output, None);
             }
             self.read += 1;
         }
@@ -664,16 +646,13 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
     }
 
     /// Creates a new iterator with a given expander
-    pub fn new(data: &'a str, expanders: &'a E, do_glob: bool) -> WordIterator<'a, E> {
-        WordIterator { data, backsl: false, read: 0, quotes: Quotes::None, expanders, do_glob }
+    pub fn new(data: &'a str, do_glob: bool) -> WordIterator<'a> {
+        WordIterator { data, backsl: false, read: 0, quotes: Quotes::None, do_glob }
     }
 }
 
-impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E>
-where
-    <E as Expander>::Error: 'static,
-{
-    type Item = Result<WordToken<'a>, E::Error>;
+impl<'a> Iterator for WordIterator<'a> {
+    type Item = WordToken<'a>;
 
     fn next(&mut self) -> Option<Self::Item> {
         if self.read == self.data.len() {
@@ -721,7 +700,7 @@ where
                     break;
                 }
                 b' ' if self.quotes == Quotes::None => {
-                    return Some(Ok(self.whitespaces(&mut iterator)));
+                    return Some(self.whitespaces(&mut iterator));
                 }
                 b'~' if self.quotes == Quotes::None => {
                     tilde = true;
@@ -730,7 +709,7 @@ where
                 }
                 b'{' if self.quotes == Quotes::None => {
                     self.read += 1;
-                    return Some(Ok(self.braces(&mut iterator)));
+                    return Some(self.braces(&mut iterator));
                 }
                 b'[' if self.quotes == Quotes::None => {
                     if self.glob_check(&mut iterator) {
@@ -752,7 +731,7 @@ where
                         Some(b' ') | None => {
                             self.read += 1;
                             let output = &self.data[start..self.read];
-                            Some(Ok(WordToken::Normal(output.into(), glob, tilde)))
+                            Some(WordToken::Normal(output.into(), glob, tilde))
                         }
                         _ => {
                             self.read += 1;
@@ -768,19 +747,19 @@ where
                                 // Pop the incoming left paren
                                 let _ = iterator.next();
                                 self.read += 1;
-                                Some(Ok(self.arithmetic_expression(&mut iterator)))
+                                Some(self.arithmetic_expression(&mut iterator))
                             } else {
                                 Some(self.process(&mut iterator))
                             }
                         }
                         Some(b'{') => {
                             self.read += 2;
-                            Some(Ok(self.braced_variable(&mut iterator)))
+                            Some(self.braced_variable(&mut iterator))
                         }
                         Some(b' ') | None => {
                             self.read += 1;
                             let output = &self.data[start..self.read];
-                            Some(Ok(WordToken::Normal(output.into(), glob, tilde)))
+                            Some(WordToken::Normal(output.into(), glob, tilde))
                         }
                         _ => {
                             self.read += 1;
@@ -809,7 +788,7 @@ where
                     if self.quotes == Quotes::Double {
                         let _ = iterator.next();
                         self.read += 1;
-                        return Some(Ok(WordToken::Normal(
+                        return Some(WordToken::Normal(
                             if next.map_or(true, |c| [b'$', b'@', b'\\', b'"'].contains(&c)) {
                                 unescape(&self.data[start..self.read])
                             } else {
@@ -817,7 +796,7 @@ where
                             },
                             glob,
                             tilde,
-                        )));
+                        ));
                     }
                 }
                 b'\'' if self.quotes != Quotes::Double => {
@@ -828,7 +807,7 @@ where
                     }
                     let output = &self.data[start..self.read];
                     self.read += 1;
-                    return Some(Ok(WordToken::Normal(output.into(), glob, tilde)));
+                    return Some(WordToken::Normal(output.into(), glob, tilde));
                 }
                 b'"' if self.quotes != Quotes::Single => {
                     if self.quotes == Quotes::Double {
@@ -838,39 +817,39 @@ where
                     }
                     let output = &self.data[start..self.read];
                     self.read += 1;
-                    return Some(Ok(WordToken::Normal(output.into(), glob, tilde)));
+                    return Some(WordToken::Normal(output.into(), glob, tilde));
                 }
                 b' ' | b'{' if self.quotes == Quotes::None => {
-                    return Some(Ok(WordToken::Normal(
+                    return Some(WordToken::Normal(
                         unescape(&self.data[start..self.read]),
                         glob,
                         tilde,
-                    )));
+                    ));
                 }
                 b'$' | b'@' if self.quotes != Quotes::Single => {
                     if let Some(&character) = self.data.as_bytes().get(self.read) {
                         if character == b' ' {
                             self.read += 1;
                             let output = &self.data[start..self.read];
-                            return Some(Ok(WordToken::Normal(output.into(), glob, tilde)));
+                            return Some(WordToken::Normal(output.into(), glob, tilde));
                         }
                     }
                     if self.read == start {
                         return self.next();
                     } else {
                         let output = &self.data[start..self.read];
-                        return Some(Ok(WordToken::Normal(unescape(output), glob, tilde)));
+                        return Some(WordToken::Normal(unescape(output), glob, tilde));
                     };
                 }
                 b'[' if self.quotes == Quotes::None => {
                     if self.glob_check(&mut iterator) {
                         glob = self.do_glob;
                     } else {
-                        return Some(Ok(WordToken::Normal(
+                        return Some(WordToken::Normal(
                             self.data[start..self.read].into(),
                             glob,
                             tilde,
-                        )));
+                        ));
                     }
                 }
                 b'*' | b'?' if self.quotes != Quotes::Single => {
@@ -884,7 +863,7 @@ where
         if start == self.read {
             None
         } else {
-            Some(Ok(WordToken::Normal(unescape(&self.data[start..]), glob, tilde)))
+            Some(WordToken::Normal(unescape(&self.data[start..]), glob, tilde))
         }
     }
 }
diff --git a/src/lib/expansion/words/tests.rs b/src/lib/expansion/words/tests.rs
index d4e43284..633c5190 100644
--- a/src/lib/expansion/words/tests.rs
+++ b/src/lib/expansion/words/tests.rs
@@ -1,13 +1,9 @@
 use super::*;
-use crate::{
-    expansion::test::DummyExpander,
-    ranges::{Index, Range},
-};
+use crate::expansion::test::DummyExpander;
 
-fn compare(input: &str, expected: Vec<WordToken<'_>>) {
+fn compare(input: &str, expected: &[WordToken<'_>]) {
     let mut correct = 0;
-    for (actual, expected) in WordIterator::new(input, &DummyExpander, true).zip(expected.iter()) {
-        let actual = actual.unwrap();
+    for (actual, expected) in WordIterator::new(input, true).zip(expected.iter()) {
         assert_eq!(actual, *expected, "{:?} != {:?}", actual, expected);
         correct += 1;
     }
@@ -17,19 +13,19 @@ fn compare(input: &str, expected: Vec<WordToken<'_>>) {
 #[test]
 fn string_method() {
     let input = "$join(array 'pattern') $join(array 'pattern')";
-    let expected = vec![
+    let expected = &[
         WordToken::StringMethod(StringMethod {
             method:    "join",
             variable:  "array",
             pattern:   "'pattern'",
-            selection: Select::All,
+            selection: None,
         }),
         WordToken::Whitespace(" "),
         WordToken::StringMethod(StringMethod {
             method:    "join",
             variable:  "array",
             pattern:   "'pattern'",
-            selection: Select::All,
+            selection: None,
         }),
     ];
     compare(input, expected);
@@ -38,7 +34,7 @@ fn string_method() {
 #[test]
 fn escape_with_backslash() {
     let input = r#"\$FOO\$BAR \$FOO"#;
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("$FOO$BAR".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Normal("$FOO".into(), false, false),
@@ -51,10 +47,10 @@ fn array_expressions() {
     let input = "[ one two [three four]] [[one two] three four][0]";
     let first = vec!["one", "two", "[three four]"];
     let second = vec!["[one two]", "three", "four"];
-    let expected = vec![
-        WordToken::Array(first, Select::All),
+    let expected = &[
+        WordToken::Array(first, None),
         WordToken::Whitespace(" "),
-        WordToken::Array(second, Select::Index(Index::new(0))),
+        WordToken::Array(second, Some("0")),
     ];
     compare(input, expected);
 }
@@ -62,12 +58,12 @@ fn array_expressions() {
 #[test]
 fn array_variables() {
     let input = "@array @array[0] @{array[1..]}";
-    let expected = vec![
-        WordToken::ArrayVariable("array", false, Select::All),
+    let expected = &[
+        WordToken::ArrayVariable("array", false, None),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable("array", false, Select::Index(Index::new(0))),
+        WordToken::ArrayVariable("array", false, Some("0")),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable("array", false, Select::Range(Range::from(Index::new(1)))),
+        WordToken::ArrayVariable("array", false, Some("1..")),
     ];
     compare(input, expected);
 }
@@ -75,10 +71,10 @@ fn array_variables() {
 #[test]
 fn array_processes() {
     let input = "@(echo one two three) @(echo one two three)[0]";
-    let expected = vec![
-        WordToken::ArrayProcess("echo one two three", false, Select::All),
+    let expected = &[
+        WordToken::ArrayProcess("echo one two three", false, None),
         WordToken::Whitespace(" "),
-        WordToken::ArrayProcess("echo one two three", false, Select::Index(Index::new(0))),
+        WordToken::ArrayProcess("echo one two three", false, Some("0")),
     ];
     compare(input, expected);
 }
@@ -87,13 +83,10 @@ fn array_processes() {
 fn array_process_within_string_process() {
     compare(
         "echo $(let free=[@(free -h)]; echo @free[6]@free[8]/@free[7])",
-        vec![
+        &[
             WordToken::Normal("echo".into(), false, false),
             WordToken::Whitespace(" "),
-            WordToken::Process(
-                "let free=[@(free -h)]; echo @free[6]@free[8]/@free[7]",
-                Select::All,
-            ),
+            WordToken::Process("let free=[@(free -h)]; echo @free[6]@free[8]/@free[7]", None),
         ],
     )
 }
@@ -101,24 +94,16 @@ fn array_process_within_string_process() {
 #[test]
 fn indexes() {
     let input = "@array[0..3] @array[0...3] @array[abc] @array[..3] @array[3..]";
-    let expected = vec![
-        WordToken::ArrayVariable(
-            "array",
-            false,
-            Select::Range(Range::exclusive(Index::new(0), Index::new(3))),
-        ),
+    let expected = &[
+        WordToken::ArrayVariable("array", false, Some("0..3")),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable(
-            "array",
-            false,
-            Select::Range(Range::inclusive(Index::new(0), Index::new(3))),
-        ),
+        WordToken::ArrayVariable("array", false, Some("0...3")),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable("array", false, Select::Key("abc".into())),
+        WordToken::ArrayVariable("array", false, Some("abc")),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable("array", false, Select::Range(Range::to(Index::new(3)))),
+        WordToken::ArrayVariable("array", false, Some("..3")),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable("array", false, Select::Range(Range::from(Index::new(3)))),
+        WordToken::ArrayVariable("array", false, Some("3..")),
     ];
     compare(input, expected);
 }
@@ -126,12 +111,12 @@ fn indexes() {
 #[test]
 fn string_keys() {
     let input = "@array['key'] @array[key] @array[]";
-    let expected = vec![
-        WordToken::ArrayVariable("array", false, Select::Key("key".into())),
+    let expected = &[
+        WordToken::ArrayVariable("array", false, Some("'key'")),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable("array", false, Select::Key("key".into())),
+        WordToken::ArrayVariable("array", false, Some("key")),
         WordToken::Whitespace(" "),
-        WordToken::ArrayVariable("array", false, Select::Key("".into())),
+        WordToken::ArrayVariable("array", false, Some("")),
     ];
     compare(input, expected);
 }
@@ -139,12 +124,12 @@ fn string_keys() {
 #[test]
 fn nested_processes() {
     let input = "echo $(echo $(echo one)) $(echo one $(echo two) three)";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Process("echo $(echo one)", Select::All),
+        WordToken::Process("echo $(echo one)", None),
         WordToken::Whitespace(" "),
-        WordToken::Process("echo one $(echo two) three", Select::All),
+        WordToken::Process("echo one $(echo two) three", None),
     ];
     compare(input, expected);
 }
@@ -152,18 +137,18 @@ fn nested_processes() {
 #[test]
 fn words_process_with_quotes() {
     let input = "echo $(git branch | rg '[*]' | awk '{print $2}')";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Process("git branch | rg '[*]' | awk '{print $2}'", Select::All),
+        WordToken::Process("git branch | rg '[*]' | awk '{print $2}'", None),
     ];
     compare(input, expected);
 
     let input = "echo $(git branch | rg \"[*]\" | awk '{print $2}')";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Process("git branch | rg \"[*]\" | awk '{print $2}'", Select::All),
+        WordToken::Process("git branch | rg \"[*]\" | awk '{print $2}'", None),
     ];
     compare(input, expected);
 }
@@ -171,21 +156,21 @@ fn words_process_with_quotes() {
 #[test]
 fn test_words() {
     let input = "echo $ABC \"${ABC}\" one{$ABC,$ABC} ~ $(echo foo) \"$(seq 1 100)\"";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Variable("ABC", Select::All),
+        WordToken::Variable("ABC", None),
         WordToken::Whitespace(" "),
-        WordToken::Variable("ABC", Select::All),
+        WordToken::Variable("ABC", None),
         WordToken::Whitespace(" "),
         WordToken::Normal("one".into(), false, false),
         WordToken::Brace(vec!["$ABC", "$ABC"]),
         WordToken::Whitespace(" "),
         WordToken::Normal("~".into(), false, true),
         WordToken::Whitespace(" "),
-        WordToken::Process("echo foo", Select::All),
+        WordToken::Process("echo foo", None),
         WordToken::Whitespace(" "),
-        WordToken::Process("seq 1 100", Select::All),
+        WordToken::Process("seq 1 100", None),
     ];
     compare(input, expected);
 }
@@ -193,7 +178,7 @@ fn test_words() {
 #[test]
 fn test_multiple_escapes() {
     let input = "foo\\(\\) bar\\(\\)";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("foo()".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Normal("bar()".into(), false, false),
@@ -204,7 +189,7 @@ fn test_multiple_escapes() {
 #[test]
 fn test_arithmetic() {
     let input = "echo $((foo bar baz bing 3 * 2))";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Arithmetic("foo bar baz bing 3 * 2"),
@@ -215,7 +200,7 @@ fn test_arithmetic() {
 #[test]
 fn test_globbing() {
     let input = "barbaz* bingcrosb*";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("barbaz*".into(), true, false),
         WordToken::Whitespace(" "),
         WordToken::Normal("bingcrosb*".into(), true, false),
@@ -226,7 +211,7 @@ fn test_globbing() {
 #[test]
 fn test_empty_strings() {
     let input = "rename '' 0 a \"\"";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("rename".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Normal("".into(), false, false),
@@ -243,7 +228,7 @@ fn test_empty_strings() {
 #[test]
 fn test_braces() {
     let input = "echo {c[a,b],d}";
-    let expected = vec![
+    let expected = &[
         WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Brace(vec!["c[a,b]", "d"]),
@@ -253,20 +238,13 @@ fn test_braces() {
 
 #[test]
 fn array_methods() {
-    let method = ArrayMethod::new(
-        "graphemes",
-        "pkmn1",
-        Pattern::Whitespace,
-        Select::Index(Index::Forward(3)),
-    );
+    let method = ArrayMethod::new("graphemes", "pkmn1", Pattern::Whitespace, Some("3"));
     let expected = args!["é"];
     assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), expected);
-    let method =
-        ArrayMethod::new("chars", "pkmn2", Pattern::Whitespace, Select::Index(Index::Forward(3)));
+    let method = ArrayMethod::new("chars", "pkmn2", Pattern::Whitespace, Some("3"));
     let expected = args!["e"];
     assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), expected);
-    let method =
-        ArrayMethod::new("bytes", "pkmn2", Pattern::Whitespace, Select::Index(Index::Forward(1)));
+    let method = ArrayMethod::new("bytes", "pkmn2", Pattern::Whitespace, Some("1"));
     let expected = args!["111"];
     assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), expected);
 }
diff --git a/src/lib/shell/shell_expand.rs b/src/lib/shell/shell_expand.rs
index b85b59f6..ad39f5b6 100644
--- a/src/lib/shell/shell_expand.rs
+++ b/src/lib/shell/shell_expand.rs
@@ -152,30 +152,26 @@ impl<'a, 'b> Expander for Shell<'b> {
         }
     }
 
-    fn map_keys(&self, name: &str, sel: &Select<types::Str>) -> Result<types::Args, Self::Error> {
+    fn map_keys(&self, name: &str) -> Result<types::Args, Self::Error> {
         match self.variables.get(name) {
             Some(&Value::HashMap(ref map)) => {
-                Self::select(map.keys().map(|x| format!("{}", x).into()), sel, map.len())
-                    .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
+                Ok(map.keys().map(|x| x.to_string().into()).collect())
             }
             Some(&Value::BTreeMap(ref map)) => {
-                Self::select(map.keys().map(|x| format!("{}", x).into()), sel, map.len())
-                    .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
+                Ok(map.keys().map(|x| x.to_string().into()).collect())
             }
             Some(_) => Err(Error::NotAMap(name.into())),
             None => Err(Error::VarNotFound),
         }
     }
 
-    fn map_values(&self, name: &str, sel: &Select<types::Str>) -> Result<types::Args, Self::Error> {
+    fn map_values(&self, name: &str) -> Result<types::Args, Self::Error> {
         match self.variables.get(name) {
             Some(&Value::HashMap(ref map)) => {
-                Self::select(map.values().map(|x| format!("{}", x).into()), sel, map.len())
-                    .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
+                Ok(map.values().map(|x| x.to_string().into()).collect())
             }
             Some(&Value::BTreeMap(ref map)) => {
-                Self::select(map.values().map(|x| format!("{}", x).into()), sel, map.len())
-                    .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
+                Ok(map.values().map(|x| x.to_string().into()).collect())
             }
             Some(_) => Err(Error::NotAMap(name.into())),
             None => Err(Error::VarNotFound),
diff --git a/src/lib/shell/variables.rs b/src/lib/shell/variables.rs
index 2c69cb70..fa5692c6 100644
--- a/src/lib/shell/variables.rs
+++ b/src/lib/shell/variables.rs
@@ -312,19 +312,11 @@ pub(crate) mod tests {
 
         fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }
 
-        fn map_keys(
-            &self,
-            _name: &str,
-            _select: &Select<types::Str>,
-        ) -> Result<types::Args, Self::Error> {
+        fn map_keys(&self, _name: &str) -> Result<types::Args, Self::Error> {
             Err(expansion::Error::VarNotFound)
         }
 
-        fn map_values(
-            &self,
-            _name: &str,
-            _select: &Select<types::Str>,
-        ) -> Result<types::Args, Self::Error> {
+        fn map_values(&self, _name: &str) -> Result<types::Args, Self::Error> {
             Err(expansion::Error::VarNotFound)
         }
     }
-- 
GitLab