From 74bbb9ed4a08a3459343f434af9f633f3111adc3 Mon Sep 17 00:00:00 2001
From: Michael Murphy <mmstickman@gmail.com>
Date: Sat, 28 Apr 2018 14:01:02 -0600
Subject: [PATCH] Update rustfmt (#722)

---
 README.md                                     |   19 +-
 build.rs                                      |   22 +-
 rustfmt.toml                                  |   18 +-
 src/lib/ascii_helpers.rs                      |    3 +-
 src/lib/builtins/command_info.rs              |   37 +-
 src/lib/builtins/conditionals.rs              |   19 +-
 src/lib/builtins/exec.rs                      |    4 +-
 src/lib/builtins/exists.rs                    |    9 +-
 src/lib/builtins/functions.rs                 |    3 +-
 src/lib/builtins/ion.rs                       |    3 +-
 src/lib/builtins/job_control.rs               |   10 +-
 src/lib/builtins/man_pages.rs                 |    6 +-
 src/lib/builtins/mod.rs                       |   95 +-
 src/lib/builtins/set.rs                       |    3 +-
 src/lib/builtins/source.rs                    |    3 +-
 src/lib/builtins/test.rs                      |   12 +-
 src/lib/builtins/variables.rs                 |    3 +-
 src/lib/lib.rs                                |   12 +-
 src/lib/parser/arguments.rs                   |   52 +-
 src/lib/parser/assignments/actions.rs         |    5 +-
 src/lib/parser/assignments/checker.rs         |   24 +-
 src/lib/parser/assignments/keys.rs            |   43 +-
 src/lib/parser/assignments/mod.rs             |   14 +-
 src/lib/parser/mod.rs                         |   14 +-
 src/lib/parser/pipelines/collector.rs         |  500 ++++---
 src/lib/parser/pipelines/mod.rs               |   28 +-
 src/lib/parser/quotes.rs                      |   50 +-
 src/lib/parser/shell_expand/mod.rs            |    6 +-
 src/lib/parser/shell_expand/ranges.rs         |    9 +-
 src/lib/parser/shell_expand/words/index.rs    |   22 +-
 .../shell_expand/words/methods/arrays.rs      |  230 +--
 .../parser/shell_expand/words/methods/mod.rs  |   21 +-
 .../shell_expand/words/methods/strings.rs     |   38 +-
 src/lib/parser/shell_expand/words/mod.rs      |  717 +++++-----
 src/lib/parser/shell_expand/words/range.rs    |   64 +-
 src/lib/parser/shell_expand/words/select.rs   |   12 +-
 src/lib/parser/statement/functions.rs         |   12 +-
 src/lib/parser/statement/mod.rs               |    6 +-
 src/lib/parser/statement/parse.rs             |   51 +-
 src/lib/parser/statement/splitter.rs          |   89 +-
 src/lib/shell/assignments.rs                  |  122 +-
 src/lib/shell/binary/designators.rs           |   15 +-
 src/lib/shell/binary/mod.rs                   |  131 +-
 src/lib/shell/binary/prompt.rs                |    3 +-
 src/lib/shell/binary/readln.rs                |   13 +-
 src/lib/shell/binary/terminate.rs             |    3 +-
 src/lib/shell/colors.rs                       |  138 +-
 src/lib/shell/completer.rs                    |   12 +-
 src/lib/shell/directory_stack.rs              |  654 ++++-----
 src/lib/shell/flow.rs                         | 1254 +++++++++--------
 src/lib/shell/flow_control.rs                 |   58 +-
 src/lib/shell/fork.rs                         |   18 +-
 src/lib/shell/history.rs                      |   95 +-
 src/lib/shell/job.rs                          |  176 +--
 src/lib/shell/mod.rs                          |  458 +++---
 src/lib/shell/pipe_exec/foreground.rs         |   42 +-
 src/lib/shell/pipe_exec/fork.rs               |   14 +-
 src/lib/shell/pipe_exec/job_control.rs        |  182 +--
 src/lib/shell/pipe_exec/mod.rs                |  777 +++++-----
 src/lib/shell/pipe_exec/streams.rs            |    8 +-
 src/lib/shell/plugins/methods/redox.rs        |    4 +-
 src/lib/shell/plugins/methods/unix.rs         |   63 +-
 src/lib/shell/plugins/mod.rs                  |    2 +-
 src/lib/shell/plugins/namespaces/redox.rs     |    4 +-
 src/lib/shell/plugins/namespaces/unix.rs      |   51 +-
 src/lib/shell/plugins/string.rs               |    6 +-
 src/lib/shell/variables/mod.rs                |  389 ++---
 src/lib/sys/redox/job_control.rs              |   44 +-
 src/lib/sys/redox/mod.rs                      |   22 +-
 src/lib/sys/unix/job_control.rs               |   46 +-
 src/lib/sys/unix/mod.rs                       |   20 +-
 src/lib/sys/unix/signals.rs                   |    3 +-
 src/main.rs                                   |   15 +-
 73 files changed, 3659 insertions(+), 3471 deletions(-)

diff --git a/README.md b/README.md
index 140b6144..e52ed313 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,9 @@
 # Introduction
 
 Ion is a modern system shell that features a simple, yet powerful, syntax. It is written entirely
-in Rust, which greatly increases the overall quality and security of the shell, eliminating the
-possibilities of a [ShellShock](http://www.wikiwand.com/en/Shellshock_(software_bug))-like vulnerability
-, and making development easier. It also offers a level of performance that exceeds that of Dash,
-when taking advantage of Ion's features. While it is developed alongside, and primarily for, RedoxOS,
-it is a fully capable on other \*nix platforms.
+in Rust, which greatly increases the overall quality and security of the shell. It also offers a
+level of performance that exceeds that of Dash, when taking advantage of Ion's features. While it
+is developed alongside, and primarily for, RedoxOS, it is a fully capable on other \*nix platforms.
 
 # Ion Shell
 
@@ -37,11 +35,12 @@ We are providing our manual for Ion in the form of a markdown-based book, which
 
 ### Code Formatting
 
-When submitting a pull request, be sure to run
-`env CFG_RELEASE_CHANNEL=nightly cargo +nightly fmt` on your project with a
-nightly version of **rustfmt**. This will prevent me from having to push PR's specifically
-to format the code base from time to time. To install **rustfmt-nightly**, simply run
-`cargo install rustfmt-nightly --force`.
+When submitting a pull request, be sure to run `rustfmt`:
+
+```
+rustup component add rustfmt-preview
+cargo +nightly fmt
+```
 
 ### On Unit & Integration Tests
 
diff --git a/build.rs b/build.rs
index d1821aed..37f39854 100644
--- a/build.rs
+++ b/build.rs
@@ -9,11 +9,13 @@ use version_check::is_nightly;
 // `loop` (RFC 1624, rust-lang/rust GitHub issue #37339).
 // const MIN_VERSION: &'static str = "1.19.0";
 
-use std::env;
-use std::fs::File;
-use std::io::{self, Read, Write};
-use std::path::Path;
-use std::process::Command;
+use std::{
+    env,
+    fs::File,
+    io::{self, Read, Write},
+    path::Path,
+    process::Command,
+};
 
 fn main() {
     match is_nightly() {
@@ -34,8 +36,14 @@ fn main() {
             panic!("Aborting compilation due to incompatible compiler.")
         }
         _ => {
-            eprintln!("cargo:warning={}", "Ion was unable to check rustc compatibility.");
-            eprintln!("cargo:warning={}", "Build may fail due to incompatible rustc version.");
+            eprintln!(
+                "cargo:warning={}",
+                "Ion was unable to check rustc compatibility."
+            );
+            eprintln!(
+                "cargo:warning={}",
+                "Build may fail due to incompatible rustc version."
+            );
         }
     }
     match write_version_file() {
diff --git a/rustfmt.toml b/rustfmt.toml
index 1f9ec585..9e639d7c 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,26 +1,16 @@
 unstable_features = true
-array_horizontal_layout_threshold = 100
-array_width = 100
-attributes_on_same_line_as_field = false
-attributes_on_same_line_as_variant = false
-chain_one_line_max = 100
-chain_width = 100
 comment_width = 100
 condense_wildcard_suffixes = true
-error_on_line_overflow_comments = false
-fn_call_width = 100
 fn_single_line = true
 format_strings = true
 imports_indent = "Block"
-match_pattern_separator_break_point = "Front"
 max_width = 100
 normalize_comments = true
-reorder_imported_names = true
 reorder_imports = true
-reorder_imports_in_group = true
-single_line_if_else_max_width = 100
+reorder_modules = true
+reorder_impl_items = true
 struct_field_align_threshold = 30
-struct_lit_width = 100
-where_style = "legacy"
+use_field_init_shorthand = true
 wrap_comments = true
 write_mode = "overwrite"
+merge_imports = true
diff --git a/src/lib/ascii_helpers.rs b/src/lib/ascii_helpers.rs
index 8ca4fafc..bf8c33e1 100644
--- a/src/lib/ascii_helpers.rs
+++ b/src/lib/ascii_helpers.rs
@@ -1,5 +1,4 @@
-use std::mem::transmute;
-use std::ops::DerefMut;
+use std::{mem::transmute, ops::DerefMut};
 
 // TODO: These could be generalised to work on non-ASCII characters (and even
 //       strings!) as long as the byte size of the needle and haystack match.
diff --git a/src/lib/builtins/command_info.rs b/src/lib/builtins/command_info.rs
index f18699cb..dd11439e 100644
--- a/src/lib/builtins/command_info.rs
+++ b/src/lib/builtins/command_info.rs
@@ -1,20 +1,17 @@
-use sys;
-use shell::Shell;
-use shell::status::*;
 use builtins::man_pages::*;
+use shell::{status::*, Shell};
+use sys;
 
-use std::env;
-use std::path::Path;
-use std::borrow::Cow;
+use std::{borrow::Cow, env, path::Path};
 
 pub(crate) fn which(args: &[&str], shell: &mut Shell) -> Result<i32, ()> {
     if check_help(args, MAN_WHICH) {
-        return Ok(SUCCESS)
+        return Ok(SUCCESS);
     }
 
     if args.len() == 1 {
         eprintln!("which: Expected at least 1 args, got only 0");
-        return Err(())
+        return Err(());
     }
 
     let mut result = SUCCESS;
@@ -24,10 +21,10 @@ pub(crate) fn which(args: &[&str], shell: &mut Shell) -> Result<i32, ()> {
                 "alias" => {
                     let alias = shell.variables.aliases.get(command).unwrap();
                     println!("{}: alias to {}", command, alias);
-                },
+                }
                 "function" => println!("{}: function", command),
                 "builtin" => println!("{}: built-in shell command", command),
-                _path => println!("{}", _path)
+                _path => println!("{}", _path),
             }
         } else {
             result = FAILURE;
@@ -40,7 +37,7 @@ pub(crate) fn find_type(args: &[&str], shell: &mut Shell) -> Result<i32, ()> {
     // Type does not accept help flags, aka "--help".
     if args.len() == 1 {
         eprintln!("type: Expected at least 1 args, got only 0");
-        return Err(())
+        return Err(());
     }
 
     let mut result = FAILURE;
@@ -50,11 +47,11 @@ pub(crate) fn find_type(args: &[&str], shell: &mut Shell) -> Result<i32, ()> {
                 "alias" => {
                     let alias = shell.variables.aliases.get(command).unwrap();
                     println!("{} is aliased to `{}`", command, alias);
-                },
+                }
                 // TODO Make it print the function.
-                "function" => println!("{} is a function", command), 
+                "function" => println!("{} is a function", command),
                 "builtin" => println!("{} is a shell builtin", command),
-                _path => println!("{} is {}", command, _path)
+                _path => println!("{} is {}", command, _path),
             }
             result = SUCCESS;
         } else {
@@ -66,21 +63,21 @@ pub(crate) fn find_type(args: &[&str], shell: &mut Shell) -> Result<i32, ()> {
 
 pub(crate) fn get_command_info<'a>(command: &str, shell: &mut Shell) -> Result<Cow<'a, str>, ()> {
     if shell.variables.aliases.get(command).is_some() {
-        return Ok("alias".into())
+        return Ok("alias".into());
     } else if shell.functions.contains_key(command) {
-        return Ok("function".into())
+        return Ok("function".into());
     } else if shell.builtins.contains_key(command) {
-        return Ok("builtin".into())
+        return Ok("builtin".into());
     } else {
         for path in env::var("PATH")
             .unwrap_or("/bin".to_string())
-            .split(sys::PATH_SEPARATOR) 
+            .split(sys::PATH_SEPARATOR)
         {
             let executable = Path::new(path).join(command);
             if executable.is_file() {
-                return Ok(executable.display().to_string().into())
+                return Ok(executable.display().to_string().into());
             }
         }
     }
     Err(())
-}
\ No newline at end of file
+}
diff --git a/src/lib/builtins/conditionals.rs b/src/lib/builtins/conditionals.rs
index bb723670..763fb6c9 100644
--- a/src/lib/builtins/conditionals.rs
+++ b/src/lib/builtins/conditionals.rs
@@ -1,24 +1,29 @@
-use shell::Shell;
-use shell::status::*;
+use shell::{status::*, Shell};
 
 macro_rules! string_function {
-    ($method:tt) => (
+    ($method:tt) => {
         pub(crate) fn $method(args: &[&str], _: &mut Shell) -> i32 {
             match args.len() {
                 0...2 => {
                     eprintln!("ion: {}: two arguments must be supplied", args[0]);
-                    return BAD_ARG
+                    return BAD_ARG;
+                }
+                3 => if args[1].$method(&args[2]) {
+                    SUCCESS
+                } else {
+                    FAILURE
                 },
-                3 => if args[1].$method(&args[2]) { SUCCESS } else { FAILURE },
                 _ => {
                     for arg in args[2..].iter() {
-                        if args[1].$method(arg) { return SUCCESS }
+                        if args[1].$method(arg) {
+                            return SUCCESS;
+                        }
                     }
                     FAILURE
                 }
             }
         }
-    )
+    };
 }
 
 string_function!(starts_with);
diff --git a/src/lib/builtins/exec.rs b/src/lib/builtins/exec.rs
index 749e92d0..81b20459 100644
--- a/src/lib/builtins/exec.rs
+++ b/src/lib/builtins/exec.rs
@@ -28,7 +28,9 @@ pub(crate) fn exec(shell: &mut Shell, args: &[&str]) -> Result<(), String> {
                 &[]
             };
             shell.prep_for_exit();
-            Err(execve(argument, args, (flags & CLEAR_ENV) == 1).description().to_owned())
+            Err(execve(argument, args, (flags & CLEAR_ENV) == 1)
+                .description()
+                .to_owned())
         }
         None => Err("no command provided".to_owned()),
     }
diff --git a/src/lib/builtins/exists.rs b/src/lib/builtins/exists.rs
index 82ba1a90..9ba6ede7 100644
--- a/src/lib/builtins/exists.rs
+++ b/src/lib/builtins/exists.rs
@@ -2,14 +2,13 @@
 use smallstring::SmallString;
 #[cfg(test)]
 use smallvec::SmallVec;
-use std::fs;
-use std::os::unix::fs::PermissionsExt;
+use std::{fs, os::unix::fs::PermissionsExt};
 
-use shell::Shell;
-#[cfg(test)]
-use shell::flow_control::{Function, Statement};
 #[cfg(test)]
 use shell;
+#[cfg(test)]
+use shell::flow_control::{Function, Statement};
+use shell::Shell;
 
 pub(crate) fn exists(args: &[&str], shell: &Shell) -> Result<bool, String> {
     let arguments = &args[1..];
diff --git a/src/lib/builtins/functions.rs b/src/lib/builtins/functions.rs
index 3efda681..ab663e2e 100644
--- a/src/lib/builtins/functions.rs
+++ b/src/lib/builtins/functions.rs
@@ -1,6 +1,5 @@
 use fnv::FnvHashMap;
-use shell::flow_control::Function;
-use shell::status::*;
+use shell::{flow_control::Function, status::*};
 use std::io::{self, Write};
 use types::Identifier;
 
diff --git a/src/lib/builtins/ion.rs b/src/lib/builtins/ion.rs
index bc52c853..74f7f0f7 100644
--- a/src/lib/builtins/ion.rs
+++ b/src/lib/builtins/ion.rs
@@ -1,5 +1,4 @@
-use shell::Shell;
-use shell::status::*;
+use shell::{status::*, Shell};
 use std::path::Path;
 
 use std::process::Command;
diff --git a/src/lib/builtins/job_control.rs b/src/lib/builtins/job_control.rs
index 9d409e34..56dc7864 100644
--- a/src/lib/builtins/job_control.rs
+++ b/src/lib/builtins/job_control.rs
@@ -1,10 +1,12 @@
 //! Contains the `jobs`, `disown`, `bg`, and `fg` commands that manage job
 //! control in the shell.
 
-use shell::Shell;
-use shell::job_control::{JobControl, ProcessState};
-use shell::signals;
-use shell::status::*;
+use shell::{
+    job_control::{JobControl, ProcessState},
+    signals,
+    status::*,
+    Shell,
+};
 
 /// Disowns given process job IDs, and optionally marks jobs to not receive SIGHUP signals.
 /// The `-a` flag selects all jobs, `-r` selects all running jobs, and `-h` specifies to mark
diff --git a/src/lib/builtins/man_pages.rs b/src/lib/builtins/man_pages.rs
index 514b3813..95c9afd2 100644
--- a/src/lib/builtins/man_pages.rs
+++ b/src/lib/builtins/man_pages.rs
@@ -1,5 +1,7 @@
-use std::error::Error;
-use std::io::{stdout, Write};
+use std::{
+    error::Error,
+    io::{stdout, Write},
+};
 
 pub(crate) fn print_man(man_page: &'static str) {
     let stdout = stdout();
diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs
index 7dbd6738..aca916cd 100644
--- a/src/lib/builtins/mod.rs
+++ b/src/lib/builtins/mod.rs
@@ -1,45 +1,54 @@
-pub mod source;
-pub mod variables;
-pub mod functions;
 pub mod calc;
+pub mod functions;
 pub mod random;
+pub mod source;
+pub mod variables;
 
 mod command_info;
 mod conditionals;
-mod job_control;
-mod man_pages;
-mod test;
 mod echo;
-mod set;
-mod status;
-mod exists;
 mod exec;
+mod exists;
 mod ion;
 mod is;
+mod job_control;
+mod man_pages;
+mod set;
+mod status;
+mod test;
 
-use self::command_info::*;
-use self::conditionals::{contains, ends_with, starts_with};
-use self::echo::echo;
-use self::exec::exec;
-use self::exists::exists;
-use self::functions::fn_;
-use self::ion::ion_docs;
-use self::is::is;
-use self::man_pages::*;
-use self::source::source;
-use self::status::status;
-use self::test::test;
-use self::variables::{alias, drop_alias, drop_array, drop_variable};
-
-use std::env;
-use std::error::Error;
-use std::io::{self, Write};
+use self::{
+    command_info::*,
+    conditionals::{contains, ends_with, starts_with},
+    echo::echo,
+    exec::exec,
+    exists::exists,
+    functions::fn_,
+    ion::ion_docs,
+    is::is,
+    man_pages::*,
+    source::source,
+    status::status,
+    test::test,
+    variables::{alias, drop_alias, drop_array, drop_variable},
+};
+
+use std::{
+    env,
+    error::Error,
+    io::{self, Write},
+};
 
 use parser::Terminator;
-use shell::job_control::{JobControl, ProcessState};
-use shell::fork_function::fork_function;
-use shell::status::*;
-use shell::{self, FlowLogic, Shell, ShellHistory};
+use shell::{
+    self,
+    fork_function::fork_function,
+    job_control::{JobControl, ProcessState},
+    status::*,
+    FlowLogic,
+    Shell,
+    ShellHistory,
+};
 use sys;
 
 const HELP_DESC: &str = "Display helpful information about a given command or list commands if \
@@ -128,6 +137,10 @@ pub struct BuiltinMap {
 }
 
 impl BuiltinMap {
+    pub fn contains_key(&self, func: &str) -> bool { self.name.iter().any(|&name| name == func) }
+
+    pub fn keys(&self) -> &'static [&'static str] { self.name }
+
     pub fn get(&self, func: &str) -> Option<Builtin> {
         self.name.binary_search(&func).ok().map(|pos| unsafe {
             Builtin {
@@ -137,10 +150,6 @@ impl BuiltinMap {
             }
         })
     }
-
-    pub fn keys(&self) -> &'static [&'static str] { self.name }
-
-    pub fn contains_key(&self, func: &str) -> bool { self.name.iter().any(|&name| name == func) }
 }
 
 // Definitions of simple builtins go here
@@ -174,7 +183,7 @@ pub fn builtin_cd(args: &[&str], shell: &mut Shell) -> i32 {
                         shell.set_var("PWD", current_dir);
                     }
                     fork_function(shell, "CD_CHANGE", &["ion"]);
-                },
+                }
                 Err(_) => env::set_var("PWD", "?"),
             };
             SUCCESS
@@ -557,20 +566,20 @@ fn builtin_exists(args: &[&str], shell: &mut Shell) -> i32 {
 fn builtin_which(args: &[&str], shell: &mut Shell) -> i32 {
     match which(args, shell) {
         Ok(result) => result,
-        Err(()) => FAILURE
+        Err(()) => FAILURE,
     }
 }
 
 fn builtin_type(args: &[&str], shell: &mut Shell) -> i32 {
     match find_type(args, shell) {
         Ok(result) => result,
-        Err(()) => FAILURE
+        Err(()) => FAILURE,
     }
 }
 
 fn builtin_isatty(args: &[&str], _: &mut Shell) -> i32 {
     if check_help(args, MAN_ISATTY) {
-        return SUCCESS
+        return SUCCESS;
     }
 
     if args.len() > 1 {
@@ -578,20 +587,20 @@ fn builtin_isatty(args: &[&str], _: &mut Shell) -> i32 {
         #[cfg(target_os = "redox")]
         match args[1].parse::<usize>() {
             Ok(r) => if sys::isatty(r) {
-                return SUCCESS
+                return SUCCESS;
             },
-            Err(_) => eprintln!("ion: isatty given bad number")
+            Err(_) => eprintln!("ion: isatty given bad number"),
         }
 
         #[cfg(not(target_os = "redox"))]
         match args[1].parse::<i32>() {
             Ok(r) => if sys::isatty(r) {
-                return SUCCESS
+                return SUCCESS;
             },
-            Err(_) => eprintln!("ion: isatty given bad number")
+            Err(_) => eprintln!("ion: isatty given bad number"),
         }
     } else {
-        return SUCCESS
+        return SUCCESS;
     }
 
     FAILURE
diff --git a/src/lib/builtins/set.rs b/src/lib/builtins/set.rs
index e0405f3e..36b810a4 100644
--- a/src/lib/builtins/set.rs
+++ b/src/lib/builtins/set.rs
@@ -1,6 +1,5 @@
 use liner::KeyBindings;
-use shell::Shell;
-use shell::flags::*;
+use shell::{flags::*, Shell};
 use std::iter;
 
 enum PositionalArgs {
diff --git a/src/lib/builtins/source.rs b/src/lib/builtins/source.rs
index 8bf58147..bb121ad9 100644
--- a/src/lib/builtins/source.rs
+++ b/src/lib/builtins/source.rs
@@ -1,6 +1,5 @@
 use shell::{FlowLogic, Shell};
-use std::fs::File;
-use std::io::Read;
+use std::{fs::File, io::Read};
 
 /// Evaluates the given file and returns 'SUCCESS' if it succeeds.
 pub(crate) fn source(shell: &mut Shell, arguments: &[&str]) -> Result<(), String> {
diff --git a/src/lib/builtins/test.rs b/src/lib/builtins/test.rs
index d3bbd987..f1eccb85 100644
--- a/src/lib/builtins/test.rs
+++ b/src/lib/builtins/test.rs
@@ -1,9 +1,11 @@
-use smallstring::SmallString;
-use std::fs;
-use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
-use std::path::Path;
-use std::time::SystemTime;
 use super::man_pages::{print_man, MAN_TEST};
+use smallstring::SmallString;
+use std::{
+    fs,
+    os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt},
+    path::Path,
+    time::SystemTime,
+};
 
 pub(crate) fn test(args: &[&str]) -> Result<bool, String> {
     let arguments = &args[1..];
diff --git a/src/lib/builtins/variables.rs b/src/lib/builtins/variables.rs
index 2f223df1..7002b6e4 100644
--- a/src/lib/builtins/variables.rs
+++ b/src/lib/builtins/variables.rs
@@ -2,8 +2,7 @@
 
 use std::io::{self, Write};
 
-use shell::status::*;
-use shell::variables::Variables;
+use shell::{status::*, variables::Variables};
 use types::*;
 
 fn print_list(list: &VariableContext) {
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index 733190b7..f79b63a1 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -1,6 +1,5 @@
 #![allow(unknown_lints)]
 #![allow(while_let_on_iterator)]
-#![feature(conservative_impl_trait)]
 #![feature(integer_atomics)]
 #![feature(pointer_methods)]
 #![feature(getpid)]
@@ -43,12 +42,11 @@ mod sys;
 mod types;
 #[macro_use]
 pub mod parser;
+mod ascii_helpers;
 mod builtins;
 mod shell;
-mod ascii_helpers;
 
-pub use shell::binary::MAN_ION;
-pub use shell::flags;
-pub use shell::status;
-pub use shell::{Binary, Capture, Fork, IonError, IonResult, Shell, ShellBuilder};
-pub use shell::pipe_exec::job_control::JobControl;
+pub use shell::{
+    binary::MAN_ION, flags, pipe_exec::job_control::JobControl, status, Binary, Capture, Fork,
+    IonError, IonResult, Shell, ShellBuilder,
+};
diff --git a/src/lib/parser/arguments.rs b/src/lib/parser/arguments.rs
index 3d0b2c26..dcd47d6c 100644
--- a/src/lib/parser/arguments.rs
+++ b/src/lib/parser/arguments.rs
@@ -1,31 +1,31 @@
 bitflags! {
     struct ArgumentFlags: u8 {
-        /// Double quotes 
+        /// Double quotes
         const DOUBLE = 0b00000001;
         /// Command flags
         const COMM_1 = 0b00000010; // found $
         const COMM_2 = 0b00000100; // found ( after $
-        /// String variable 
+        /// String variable
         const VARIAB = 0b00001000;
-        /// Array variable 
+        /// Array variable
         const ARRAY  = 0b00010000;
-        const METHOD = 0b00100000;       
+        const METHOD = 0b00100000;
     }
 }
 
 /// An efficient `Iterator` structure for splitting arguments
 pub struct ArgumentSplitter<'a> {
-    data:  &'a str,
+    data: &'a str,
     /// Number of bytes read
-    read:  usize,
+    read: usize,
     bitflags: ArgumentFlags,
 }
 
 impl<'a> ArgumentSplitter<'a> {
     pub fn new(data: &'a str) -> ArgumentSplitter<'a> {
         ArgumentSplitter {
-            data:  data,
-            read:  0,
+            data,
+            read:     0,
             bitflags: ArgumentFlags::empty(),
         }
     }
@@ -61,7 +61,6 @@ impl<'a> Iterator for ArgumentSplitter<'a> {
         let (mut level, mut alevel) = (0, 0);
         let mut bytes = data.iter().cloned().skip(self.read);
         while let Some(character) = bytes.next() {
-
             match character {
                 // Skip the next byte.
                 b'\\' => {
@@ -72,18 +71,16 @@ impl<'a> Iterator for ArgumentSplitter<'a> {
                 // Disable COMM_1 and enable COMM_2 + ARRAY.
                 b'@' => {
                     self.bitflags.remove(ArgumentFlags::COMM_1);
-                    self.bitflags.insert(
-                        ArgumentFlags::COMM_2 | ArgumentFlags::ARRAY
-                    );
+                    self.bitflags
+                        .insert(ArgumentFlags::COMM_2 | ArgumentFlags::ARRAY);
                     self.read += 1;
                     continue;
                 }
                 // Disable COMM_2 and enable COMM_1 + VARIAB.
                 b'$' => {
                     self.bitflags.remove(ArgumentFlags::COMM_2);
-                    self.bitflags.insert(
-                        ArgumentFlags::COMM_1 | ArgumentFlags::VARIAB
-                    );
+                    self.bitflags
+                        .insert(ArgumentFlags::COMM_1 | ArgumentFlags::VARIAB);
                     self.read += 1;
                     continue;
                 }
@@ -95,13 +92,12 @@ impl<'a> Iterator for ArgumentSplitter<'a> {
                 b'(' => {
                     // Disable VARIAB + ARRAY and enable METHOD.
                     // if variab or array are set
-                    if self.bitflags.intersects(
-                        ArgumentFlags::VARIAB | ArgumentFlags::ARRAY
-                    ) {
-                        self.bitflags.remove(
-                            ArgumentFlags::VARIAB | ArgumentFlags::ARRAY
-                        );
-                        self.bitflags.insert(ArgumentFlags::METHOD);                       
+                    if self.bitflags
+                        .intersects(ArgumentFlags::VARIAB | ArgumentFlags::ARRAY)
+                    {
+                        self.bitflags
+                            .remove(ArgumentFlags::VARIAB | ArgumentFlags::ARRAY);
+                        self.bitflags.insert(ArgumentFlags::METHOD);
                     }
                     level += 1
                 }
@@ -127,9 +123,10 @@ impl<'a> Iterator for ArgumentSplitter<'a> {
                 // ) && level + alevel == 0) => break,
                 // Break from the loop once a root-level space is found.
                 b' ' => {
-                    if !self.bitflags.intersects(
-                        ArgumentFlags::DOUBLE | ArgumentFlags::METHOD
-                    ) && level == 0 && alevel == 0 {
+                    if !self.bitflags
+                        .intersects(ArgumentFlags::DOUBLE | ArgumentFlags::METHOD)
+                        && level == 0 && alevel == 0
+                    {
                         break;
                     }
                 }
@@ -138,9 +135,8 @@ impl<'a> Iterator for ArgumentSplitter<'a> {
 
             self.read += 1;
             // disable COMM_1 and COMM_2
-            self.bitflags.remove(
-                ArgumentFlags::COMM_1 | ArgumentFlags::COMM_2
-            );
+            self.bitflags
+                .remove(ArgumentFlags::COMM_1 | ArgumentFlags::COMM_2);
         }
 
         if start == self.read {
diff --git a/src/lib/parser/assignments/actions.rs b/src/lib/parser/assignments/actions.rs
index b2f75503..cc1e043b 100644
--- a/src/lib/parser/assignments/actions.rs
+++ b/src/lib/parser/assignments/actions.rs
@@ -1,6 +1,4 @@
-use super::*;
-use super::checker::*;
-use super::super::ArgumentSplitter;
+use super::{super::ArgumentSplitter, checker::*, *};
 use std::fmt::{self, Display, Formatter};
 
 #[derive(Debug, PartialEq)]
@@ -49,6 +47,7 @@ impl<'a> AssignmentActions<'a> {
 
 impl<'a> Iterator for AssignmentActions<'a> {
     type Item = Result<Action<'a>, AssignmentError<'a>>;
+
     fn next(&mut self) -> Option<Result<Action<'a>, AssignmentError<'a>>> {
         if let Some(key) = self.keys.next() {
             match key {
diff --git a/src/lib/parser/assignments/checker.rs b/src/lib/parser/assignments/checker.rs
index fffacc42..f2010dda 100644
--- a/src/lib/parser/assignments/checker.rs
+++ b/src/lib/parser/assignments/checker.rs
@@ -1,6 +1,9 @@
-use super::{Primitive, ReturnValue, TypeError};
-use super::super::Expander;
-use super::super::expand_string;
+use super::{
+    super::{expand_string, Expander},
+    Primitive,
+    ReturnValue,
+    TypeError,
+};
 
 use std::iter::Iterator;
 
@@ -155,8 +158,16 @@ pub(crate) fn value_check<'a, E: Expander>(
     value: &'a str,
     expected: Primitive,
 ) -> Result<ReturnValue, TypeError<'a>> {
-    macro_rules! string { () => { get_string(shell, value) } }
-    macro_rules! array { () => { get_array(shell, value) } }
+    macro_rules! string {
+        () => {
+            get_string(shell, value)
+        };
+    }
+    macro_rules! array {
+        () => {
+            get_array(shell, value)
+        };
+    }
     let is_array = is_array(value);
     match expected {
         Primitive::Any if is_array => Ok(array!()),
@@ -195,8 +206,7 @@ pub(crate) fn value_check<'a, E: Expander>(
 
 #[cfg(test)]
 mod test {
-    use super::*;
-    use super::super::*;
+    use super::{super::*, *};
 
     #[test]
     fn is_array_() {
diff --git a/src/lib/parser/assignments/keys.rs b/src/lib/parser/assignments/keys.rs
index 20116dfc..8da6ae3d 100644
--- a/src/lib/parser/assignments/keys.rs
+++ b/src/lib/parser/assignments/keys.rs
@@ -106,27 +106,6 @@ pub(crate) struct KeyIterator<'a> {
 }
 
 impl<'a> KeyIterator<'a> {
-    pub(crate) fn new(data: &'a str) -> KeyIterator<'a> { KeyIterator { data, read: 0 } }
-
-    // Parameters are values that follow the semicolon (':').
-    fn parse_parameter(&mut self, name: &'a str) -> Result<Key<'a>, TypeError<'a>> {
-        let mut start = self.read;
-        for byte in self.data.bytes().skip(self.read) {
-            self.read += 1;
-            match byte {
-                b' ' if start + 1 == self.read => start += 1,
-                b' ' => return Key::new(name, &self.data[start..self.read].trim()),
-                _ => (),
-            }
-        }
-
-        if start == self.read {
-            Err(TypeError::Invalid(""))
-        } else {
-            Key::new(name, &self.data[start..self.read].trim())
-        }
-    }
-
     // Executes when a semicolon was not found, but an array character was.
     fn parse_array(&mut self, name: &'a str) -> Result<Key<'a>, TypeError<'a>> {
         let start = self.read;
@@ -155,10 +134,32 @@ impl<'a> KeyIterator<'a> {
             data @ _ => return Err(TypeError::Invalid(data)),
         }
     }
+
+    // Parameters are values that follow the semicolon (':').
+    fn parse_parameter(&mut self, name: &'a str) -> Result<Key<'a>, TypeError<'a>> {
+        let mut start = self.read;
+        for byte in self.data.bytes().skip(self.read) {
+            self.read += 1;
+            match byte {
+                b' ' if start + 1 == self.read => start += 1,
+                b' ' => return Key::new(name, &self.data[start..self.read].trim()),
+                _ => (),
+            }
+        }
+
+        if start == self.read {
+            Err(TypeError::Invalid(""))
+        } else {
+            Key::new(name, &self.data[start..self.read].trim())
+        }
+    }
+
+    pub(crate) fn new(data: &'a str) -> KeyIterator<'a> { KeyIterator { data, read: 0 } }
 }
 
 impl<'a> Iterator for KeyIterator<'a> {
     type Item = Result<Key<'a>, TypeError<'a>>;
+
     fn next(&mut self) -> Option<Result<Key<'a>, TypeError<'a>>> {
         let mut start = self.read;
         for byte in self.data.bytes().skip(self.read) {
diff --git a/src/lib/parser/assignments/mod.rs b/src/lib/parser/assignments/mod.rs
index b732fc15..fd3a81b7 100644
--- a/src/lib/parser/assignments/mod.rs
+++ b/src/lib/parser/assignments/mod.rs
@@ -1,15 +1,17 @@
 mod actions;
 mod checker;
-mod splitter;
 mod keys;
 mod operator;
+mod splitter;
 
-pub(crate) use self::actions::{Action, AssignmentActions, AssignmentError};
-pub(crate) use self::checker::{is_array, value_check};
-pub(crate) use self::keys::{Key, KeyBuf, KeyIterator, TypeError};
 pub use self::keys::Primitive;
-pub(crate) use self::operator::Operator;
-pub(crate) use self::splitter::split_assignment;
+pub(crate) use self::{
+    actions::{Action, AssignmentActions, AssignmentError},
+    checker::{is_array, value_check},
+    keys::{Key, KeyBuf, KeyIterator, TypeError},
+    operator::Operator,
+    splitter::split_assignment,
+};
 
 use types::{Array, Value};
 
diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs
index 30b7af7c..66926e29 100644
--- a/src/lib/parser/mod.rs
+++ b/src/lib/parser/mod.rs
@@ -2,13 +2,13 @@ mod arguments;
 pub(crate) mod assignments;
 mod loops;
 pub(crate) mod pipelines;
+mod quotes;
 pub(crate) mod shell_expand;
 mod statement;
-mod quotes;
 
-pub use self::arguments::ArgumentSplitter;
-pub use self::assignments::Primitive;
-pub(crate) use self::loops::for_grammar::ForExpression;
-pub use self::quotes::Terminator;
-pub(crate) use self::shell_expand::{expand_string, Expander, Select};
-pub(crate) use self::statement::{parse_and_validate, StatementSplitter};
+pub use self::{arguments::ArgumentSplitter, assignments::Primitive, quotes::Terminator};
+pub(crate) use self::{
+    loops::for_grammar::ForExpression,
+    shell_expand::{expand_string, Expander, Select},
+    statement::{parse_and_validate, StatementSplitter},
+};
diff --git a/src/lib/parser/pipelines/collector.rs b/src/lib/parser/pipelines/collector.rs
index 0f3631f7..b172ccf5 100644
--- a/src/lib/parser/pipelines/collector.rs
+++ b/src/lib/parser/pipelines/collector.rs
@@ -1,7 +1,6 @@
 #![allow(eq_op)] // Required as a macro sets this clippy warning off.
 
-use std::collections::HashSet;
-use std::iter::Peekable;
+use std::{collections::HashSet, iter::Peekable};
 
 use super::{Input, PipeItem, Pipeline, RedirectFrom, Redirection};
 use shell::{Job, JobKind};
@@ -17,189 +16,6 @@ lazy_static! {
 }
 
 impl<'a> Collector<'a> {
-    pub(crate) fn new(data: &'a str) -> Self { Collector { data } }
-
-    pub(crate) fn run(data: &'a str) -> Result<Pipeline, &'static str> {
-        Collector::new(data).parse()
-    }
-
-    fn peek(&self, index: usize) -> Option<u8> {
-        if index < self.data.len() {
-            Some(self.data.as_bytes()[index])
-        } else {
-            None
-        }
-    }
-
-    fn single_quoted<I>(
-        &self,
-        bytes: &mut Peekable<I>,
-        start: usize,
-    ) -> Result<&'a str, &'static str>
-    where
-        I: Iterator<Item = (usize, u8)>,
-    {
-        while let Some(&(i, b)) = bytes.peek() {
-            match b {
-                // We return an inclusive range to keep the quote type intact
-                b'\'' => {
-                    bytes.next();
-                    return Ok(&self.data[start..i + 1]);
-                }
-                _ => (),
-            }
-            bytes.next();
-        }
-        Err("ion: syntax error: unterminated single quote")
-    }
-
-    fn double_quoted<I>(
-        &self,
-        bytes: &mut Peekable<I>,
-        start: usize,
-    ) -> Result<&'a str, &'static str>
-    where
-        I: Iterator<Item = (usize, u8)>,
-    {
-        while let Some(&(i, b)) = bytes.peek() {
-            match b {
-                b'\\' => {
-                    bytes.next();
-                }
-                // We return an inclusive range to keep the quote type intact
-                b'"' => {
-                    bytes.next();
-                    return Ok(&self.data[start..i + 1]);
-                }
-                _ => (),
-            }
-            bytes.next();
-        }
-        Err("ion: syntax error: unterminated quote")
-    }
-
-    fn arg<I>(&self, bytes: &mut Peekable<I>) -> Result<Option<&'a str>, &'static str>
-    where
-        I: Iterator<Item = (usize, u8)>,
-    {
-        // XXX: I don't think its the responsibility of the pipeline parser to do this
-        // but I'm not sure of a better solution
-        let mut array_level = 0;
-        let mut proc_level = 0;
-        let mut brace_level = 0;
-        let mut start = None;
-        let mut end = None;
-
-        macro_rules! is_toplevel { () => (array_level + proc_level + brace_level == 0) }
-
-        // Skip over any leading whitespace
-        while let Some(&(_, b)) = bytes.peek() {
-            match b {
-                b' ' | b'\t' => {
-                    bytes.next();
-                }
-                _ => break,
-            }
-        }
-
-        while let Some(&(i, b)) = bytes.peek() {
-            if start.is_none() {
-                start = Some(i)
-            }
-            match b {
-                b'(' => {
-                    proc_level += 1;
-                    bytes.next();
-                }
-                b')' => {
-                    proc_level -= 1;
-                    bytes.next();
-                }
-                b'[' => {
-                    array_level += 1;
-                    bytes.next();
-                }
-                b']' => {
-                    array_level -= 1;
-                    bytes.next();
-                }
-                b'{' => {
-                    brace_level += 1;
-                    bytes.next();
-                }
-                b'}' => {
-                    brace_level -= 1;
-                    bytes.next();
-                }
-                // This is a tricky one: we only end the argment if `^` is followed by a
-                // redirection character
-                b'^' => {
-                    if is_toplevel!() {
-                        if let Some(next_byte) = self.peek(i + 1) {
-                            // If the next byte is for stderr to file or next process, end this
-                            // argument
-                            if next_byte == b'>' || next_byte == b'|' {
-                                end = Some(i);
-                                break;
-                            }
-                        }
-                        // Reaching this block means that either there is no next byte, or the next
-                        // byte is none of '>' or '|', indicating that this is not the beginning of
-                        // a redirection for stderr
-                        bytes.next();
-                    }
-                }
-                // Evaluate a quoted string but do not return it
-                // We pass in i, the index of a quote, but start a character later. This ensures
-                // the production rules will produce strings with the quotes intact
-                b'"' => {
-                    bytes.next();
-                    self.double_quoted(bytes, i)?;
-                }
-                b'\'' => {
-                    bytes.next();
-                    self.single_quoted(bytes, i)?;
-                }
-                // If we see a backslash, assume that it is leading up to an escaped character
-                // and skip the next character
-                b'\\' => {
-                    bytes.next();
-                    bytes.next();
-                }
-                // If we see a byte from the follow set, we've definitely reached the end of
-                // the arguments
-                c if FOLLOW_ARGS.contains(&c) && is_toplevel!() => {
-                    end = Some(i);
-                    break;
-                }
-                // By default just pop the next byte: it will be part of the argument
-                _ => {
-                    bytes.next();
-                }
-            }
-        }
-        if proc_level > 0 {
-            return Err("ion: syntax error: unmatched left paren");
-        }
-        if array_level > 0 {
-            return Err("ion: syntax error: unmatched left bracket");
-        }
-        if brace_level > 0 {
-            return Err("ion: syntax error: unmatched left brace");
-        }
-        if proc_level < 0 {
-            return Err("ion: syntax error: extra right paren(s)");
-        }
-        if array_level < 0 {
-            return Err("ion: syntax error: extra right bracket(s)");
-        }
-        match (start, end) {
-            (Some(i), Some(j)) if i < j => Ok(Some(&self.data[i..j])),
-            (Some(i), None) => Ok(Some(&self.data[i..])),
-            _ => Ok(None),
-        }
-    }
-
     pub(crate) fn parse(&self) -> Result<Pipeline, &'static str> {
         let mut bytes = self.data.bytes().enumerate().peekable();
         let mut args = Array::new();
@@ -213,13 +29,15 @@ impl<'a> Collector<'a> {
                 if let Some(v) = self.arg(&mut bytes)? {
                     args.push(v.into());
                 }
-            }}
+            }};
         }
 
         /// Attempt to add a redirection
         macro_rules! try_redir_out {
             ($from:expr) => {{
-                if let None = outputs { outputs = Some(Vec::new()); }
+                if let None = outputs {
+                    outputs = Some(Vec::new());
+                }
                 let append = if let Some(&(_, b'>')) = bytes.peek() {
                     bytes.next();
                     true
@@ -227,21 +45,23 @@ impl<'a> Collector<'a> {
                     false
                 };
                 if let Some(file) = self.arg(&mut bytes)? {
-                    outputs.as_mut().map(|o| o.push(Redirection {
-                        from: $from,
-                        file: file.into(),
-                        append
-                    }));
+                    outputs.as_mut().map(|o| {
+                        o.push(Redirection {
+                            from: $from,
+                            file: file.into(),
+                            append,
+                        })
+                    });
                 } else {
                     return Err("expected file argument after redirection for output");
                 }
-            }}
+            }};
         };
 
         /// Attempt to create a pipeitem and append it to the pipeline
         macro_rules! try_add_item {
             ($job_kind:expr) => {{
-                if ! args.is_empty() {
+                if !args.is_empty() {
                     let job = Job::new(args.clone(), $job_kind);
                     args.clear();
                     let item_out = if let Some(out_tmp) = outputs.take() {
@@ -256,7 +76,7 @@ impl<'a> Collector<'a> {
                     };
                     pipeline.items.push(PipeItem::new(job, item_out, item_in));
                 }
-            }}
+            }};
         }
 
         while let Some(&(i, b)) = bytes.peek() {
@@ -381,14 +201,202 @@ impl<'a> Collector<'a> {
 
         Ok(pipeline)
     }
+
+    fn arg<I>(&self, bytes: &mut Peekable<I>) -> Result<Option<&'a str>, &'static str>
+    where
+        I: Iterator<Item = (usize, u8)>,
+    {
+        // XXX: I don't think its the responsibility of the pipeline parser to do this
+        // but I'm not sure of a better solution
+        let mut array_level = 0;
+        let mut proc_level = 0;
+        let mut brace_level = 0;
+        let mut start = None;
+        let mut end = None;
+
+        macro_rules! is_toplevel {
+            () => {
+                array_level + proc_level + brace_level == 0
+            };
+        }
+
+        // Skip over any leading whitespace
+        while let Some(&(_, b)) = bytes.peek() {
+            match b {
+                b' ' | b'\t' => {
+                    bytes.next();
+                }
+                _ => break,
+            }
+        }
+
+        while let Some(&(i, b)) = bytes.peek() {
+            if start.is_none() {
+                start = Some(i)
+            }
+            match b {
+                b'(' => {
+                    proc_level += 1;
+                    bytes.next();
+                }
+                b')' => {
+                    proc_level -= 1;
+                    bytes.next();
+                }
+                b'[' => {
+                    array_level += 1;
+                    bytes.next();
+                }
+                b']' => {
+                    array_level -= 1;
+                    bytes.next();
+                }
+                b'{' => {
+                    brace_level += 1;
+                    bytes.next();
+                }
+                b'}' => {
+                    brace_level -= 1;
+                    bytes.next();
+                }
+                // This is a tricky one: we only end the argment if `^` is followed by a
+                // redirection character
+                b'^' => {
+                    if is_toplevel!() {
+                        if let Some(next_byte) = self.peek(i + 1) {
+                            // If the next byte is for stderr to file or next process, end this
+                            // argument
+                            if next_byte == b'>' || next_byte == b'|' {
+                                end = Some(i);
+                                break;
+                            }
+                        }
+                        // Reaching this block means that either there is no next byte, or the next
+                        // byte is none of '>' or '|', indicating that this is not the beginning of
+                        // a redirection for stderr
+                        bytes.next();
+                    }
+                }
+                // Evaluate a quoted string but do not return it
+                // We pass in i, the index of a quote, but start a character later. This ensures
+                // the production rules will produce strings with the quotes intact
+                b'"' => {
+                    bytes.next();
+                    self.double_quoted(bytes, i)?;
+                }
+                b'\'' => {
+                    bytes.next();
+                    self.single_quoted(bytes, i)?;
+                }
+                // If we see a backslash, assume that it is leading up to an escaped character
+                // and skip the next character
+                b'\\' => {
+                    bytes.next();
+                    bytes.next();
+                }
+                // If we see a byte from the follow set, we've definitely reached the end of
+                // the arguments
+                c if FOLLOW_ARGS.contains(&c) && is_toplevel!() => {
+                    end = Some(i);
+                    break;
+                }
+                // By default just pop the next byte: it will be part of the argument
+                _ => {
+                    bytes.next();
+                }
+            }
+        }
+        if proc_level > 0 {
+            return Err("ion: syntax error: unmatched left paren");
+        }
+        if array_level > 0 {
+            return Err("ion: syntax error: unmatched left bracket");
+        }
+        if brace_level > 0 {
+            return Err("ion: syntax error: unmatched left brace");
+        }
+        if proc_level < 0 {
+            return Err("ion: syntax error: extra right paren(s)");
+        }
+        if array_level < 0 {
+            return Err("ion: syntax error: extra right bracket(s)");
+        }
+        match (start, end) {
+            (Some(i), Some(j)) if i < j => Ok(Some(&self.data[i..j])),
+            (Some(i), None) => Ok(Some(&self.data[i..])),
+            _ => Ok(None),
+        }
+    }
+
+    fn double_quoted<I>(
+        &self,
+        bytes: &mut Peekable<I>,
+        start: usize,
+    ) -> Result<&'a str, &'static str>
+    where
+        I: Iterator<Item = (usize, u8)>,
+    {
+        while let Some(&(i, b)) = bytes.peek() {
+            match b {
+                b'\\' => {
+                    bytes.next();
+                }
+                // We return an inclusive range to keep the quote type intact
+                b'"' => {
+                    bytes.next();
+                    return Ok(&self.data[start..i + 1]);
+                }
+                _ => (),
+            }
+            bytes.next();
+        }
+        Err("ion: syntax error: unterminated quote")
+    }
+
+    fn single_quoted<I>(
+        &self,
+        bytes: &mut Peekable<I>,
+        start: usize,
+    ) -> Result<&'a str, &'static str>
+    where
+        I: Iterator<Item = (usize, u8)>,
+    {
+        while let Some(&(i, b)) = bytes.peek() {
+            match b {
+                // We return an inclusive range to keep the quote type intact
+                b'\'' => {
+                    bytes.next();
+                    return Ok(&self.data[start..i + 1]);
+                }
+                _ => (),
+            }
+            bytes.next();
+        }
+        Err("ion: syntax error: unterminated single quote")
+    }
+
+    fn peek(&self, index: usize) -> Option<u8> {
+        if index < self.data.len() {
+            Some(self.data.as_bytes()[index])
+        } else {
+            None
+        }
+    }
+
+    pub(crate) fn run(data: &'a str) -> Result<Pipeline, &'static str> {
+        Collector::new(data).parse()
+    }
+
+    pub(crate) fn new(data: &'a str) -> Self { Collector { data } }
 }
 
 #[cfg(test)]
 mod tests {
-    use parser::pipelines::{Input, PipeItem, Pipeline, RedirectFrom, Redirection};
-    use parser::statement::parse;
-    use shell::{Job, JobKind};
-    use shell::flow_control::Statement;
+    use parser::{
+        pipelines::{Input, PipeItem, Pipeline, RedirectFrom, Redirection},
+        statement::parse,
+    };
+    use shell::{flow_control::Statement, Job, JobKind};
     use types::Array;
 
     #[test]
@@ -400,13 +408,11 @@ mod tests {
             assert_eq!("--abbrev-ref", pipeline.items[0].job.args[2]);
             assert_eq!("HEAD", pipeline.items[0].job.args[3]);
 
-            let expected = vec![
-                Redirection {
-                    from:   RedirectFrom::Stderr,
-                    file:   "/dev/null".to_owned(),
-                    append: false,
-                },
-            ];
+            let expected = vec![Redirection {
+                from:   RedirectFrom::Stderr,
+                file:   "/dev/null".to_owned(),
+                append: false,
+            }];
 
             assert_eq!(expected, pipeline.items[0].outputs);
         } else {
@@ -830,13 +836,11 @@ mod tests {
                 PipeItem {
                     job:     Job::new(array!["cat"], JobKind::Last),
                     inputs:  vec![Input::File("stuff".into())],
-                    outputs: vec![
-                        Redirection {
-                            from:   RedirectFrom::Stderr,
-                            file:   "other".into(),
-                            append: true,
-                        },
-                    ],
+                    outputs: vec![Redirection {
+                        from:   RedirectFrom::Stderr,
+                        file:   "other".into(),
+                        append: true,
+                    }],
                 },
             ],
         };
@@ -863,13 +867,11 @@ mod tests {
                 PipeItem {
                     job:     Job::new(array!["cat"], JobKind::Last),
                     inputs:  vec![Input::File("stuff".into())],
-                    outputs: vec![
-                        Redirection {
-                            from:   RedirectFrom::Both,
-                            file:   "other".into(),
-                            append: true,
-                        },
-                    ],
+                    outputs: vec![Redirection {
+                        from:   RedirectFrom::Both,
+                        file:   "other".into(),
+                        append: true,
+                    }],
                 },
             ],
         };
@@ -915,13 +917,11 @@ mod tests {
     fn herestring() {
         let input = "calc <<< $(cat math.txt)";
         let expected = Pipeline {
-            items: vec![
-                PipeItem {
-                    job:     Job::new(array!["calc"], JobKind::Last),
-                    inputs:  vec![Input::HereString("$(cat math.txt)".into())],
-                    outputs: vec![],
-                },
-            ],
+            items: vec![PipeItem {
+                job:     Job::new(array!["calc"], JobKind::Last),
+                inputs:  vec![Input::HereString("$(cat math.txt)".into())],
+                outputs: vec![],
+            }],
         };
         assert_eq!(Statement::Pipeline(expected), parse(input));
     }
@@ -930,13 +930,11 @@ mod tests {
     fn heredoc() {
         let input = "calc << EOF\n1 + 2\n3 + 4\nEOF";
         let expected = Pipeline {
-            items: vec![
-                PipeItem {
-                    job:     Job::new(array!["calc"], JobKind::Last),
-                    inputs:  vec![Input::HereString("1 + 2\n3 + 4".into())],
-                    outputs: vec![],
-                },
-            ],
+            items: vec![PipeItem {
+                job:     Job::new(array!["calc"], JobKind::Last),
+                inputs:  vec![Input::HereString("1 + 2\n3 + 4".into())],
+                outputs: vec![],
+            }],
         };
         assert_eq!(Statement::Pipeline(expected), parse(input));
     }
@@ -956,13 +954,11 @@ mod tests {
                 PipeItem {
                     job:     Job::new(array!["tr", "'o'", "'x'"], JobKind::Last),
                     inputs:  vec![Input::HereString("$VAR".into())],
-                    outputs: vec![
-                        Redirection {
-                            from:   RedirectFrom::Stdout,
-                            file:   "out.log".into(),
-                            append: false,
-                        },
-                    ],
+                    outputs: vec![Redirection {
+                        from:   RedirectFrom::Stdout,
+                        file:   "out.log".into(),
+                        append: false,
+                    }],
                 },
             ],
         };
@@ -990,19 +986,15 @@ mod tests {
     fn escaped_filenames() {
         let input = "echo zardoz >> foo\\'bar";
         let expected = Pipeline {
-            items: vec![
-                PipeItem {
-                    job:     Job::new(array!["echo", "zardoz"], JobKind::Last),
-                    inputs:  Vec::new(),
-                    outputs: vec![
-                        Redirection {
-                            from:   RedirectFrom::Stdout,
-                            file:   "foo\\'bar".into(),
-                            append: true,
-                        },
-                    ],
-                },
-            ],
+            items: vec![PipeItem {
+                job:     Job::new(array!["echo", "zardoz"], JobKind::Last),
+                inputs:  Vec::new(),
+                outputs: vec![Redirection {
+                    from:   RedirectFrom::Stdout,
+                    file:   "foo\\'bar".into(),
+                    append: true,
+                }],
+            }],
         };
         assert_eq!(parse(input), Statement::Pipeline(expected));
     }
diff --git a/src/lib/parser/pipelines/mod.rs b/src/lib/parser/pipelines/mod.rs
index 301281e8..6aee2137 100644
--- a/src/lib/parser/pipelines/mod.rs
+++ b/src/lib/parser/pipelines/mod.rs
@@ -44,14 +44,6 @@ pub(crate) struct PipeItem {
 }
 
 impl PipeItem {
-    pub(crate) fn new(job: Job, outputs: Vec<Redirection>, inputs: Vec<Input>) -> Self {
-        PipeItem {
-            job,
-            outputs,
-            inputs,
-        }
-    }
-
     pub(crate) fn expand(&mut self, shell: &Shell) {
         self.job.expand(shell);
 
@@ -68,21 +60,29 @@ impl PipeItem {
             output.file = expand_string(output.file.as_str(), shell, false).join(" ");
         }
     }
-}
 
-impl Pipeline {
-    pub(crate) fn new() -> Self { Pipeline { items: Vec::new() } }
-
-    pub(crate) fn expand(&mut self, shell: &Shell) {
-        self.items.iter_mut().for_each(|i| i.expand(shell));
+    pub(crate) fn new(job: Job, outputs: Vec<Redirection>, inputs: Vec<Input>) -> Self {
+        PipeItem {
+            job,
+            outputs,
+            inputs,
+        }
     }
+}
 
+impl Pipeline {
     pub(crate) fn requires_piping(&self) -> bool {
         self.items.len() > 1 || self.items.iter().any(|it| it.outputs.len() > 0)
             || self.items.iter().any(|it| it.inputs.len() > 0)
             || self.items.last().unwrap().job.kind == JobKind::Background
             || self.items.last().unwrap().job.kind == JobKind::Disown
     }
+
+    pub(crate) fn expand(&mut self, shell: &Shell) {
+        self.items.iter_mut().for_each(|i| i.expand(shell));
+    }
+
+    pub(crate) fn new() -> Self { Pipeline { items: Vec::new() } }
 }
 
 impl fmt::Display for Pipeline {
diff --git a/src/lib/parser/quotes.rs b/src/lib/parser/quotes.rs
index fe914953..10ea43c1 100644
--- a/src/lib/parser/quotes.rs
+++ b/src/lib/parser/quotes.rs
@@ -35,29 +35,8 @@ impl From<String> for Terminator {
 }
 
 impl Terminator {
-    pub fn new(input: String) -> Terminator {
-        Terminator {
-            buffer:     input,
-            eof:        None,
-            eof_buffer: String::new(),
-            array:      0,
-            read:       0,
-            flags:      Flags::empty(),
-        }
-    }
-
-    /// Appends a string to the internal buffer.
-    pub fn append(&mut self, input: &str) {
-        if self.eof.is_none() {
-            self.buffer.push_str(if self.flags.contains(Flags::TRIM) {
-                input.trim()
-            } else {
-                input
-            });
-        } else {
-            self.eof_buffer.push_str(input);
-        }
-    }
+    /// Consumes the `Terminator`, and returns the underlying `String`.
+    pub fn consume(self) -> String { self.buffer }
 
     pub fn is_terminated(&mut self) -> bool {
         let mut eof_line = None;
@@ -181,6 +160,27 @@ impl Terminator {
         status
     }
 
-    /// Consumes the `Terminator`, and returns the underlying `String`.
-    pub fn consume(self) -> String { self.buffer }
+    /// Appends a string to the internal buffer.
+    pub fn append(&mut self, input: &str) {
+        if self.eof.is_none() {
+            self.buffer.push_str(if self.flags.contains(Flags::TRIM) {
+                input.trim()
+            } else {
+                input
+            });
+        } else {
+            self.eof_buffer.push_str(input);
+        }
+    }
+
+    pub fn new(input: String) -> Terminator {
+        Terminator {
+            buffer:     input,
+            eof:        None,
+            eof_buffer: String::new(),
+            array:      0,
+            read:       0,
+            flags:      Flags::empty(),
+        }
+    }
 }
diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs
index 71e3880b..687a0be7 100644
--- a/src/lib/parser/shell_expand/mod.rs
+++ b/src/lib/parser/shell_expand/mod.rs
@@ -6,12 +6,10 @@ mod braces;
 mod ranges;
 mod words;
 
-use self::braces::BraceToken;
-use self::ranges::parse_range;
 pub(crate) use self::words::{Index, Range, Select, WordIterator, WordToken};
+use self::{braces::BraceToken, ranges::parse_range};
 use glob::glob;
-use std::ptr;
-use std::str;
+use std::{ptr, str};
 use types::*;
 use unicode_segmentation::UnicodeSegmentation;
 
diff --git a/src/lib/parser/shell_expand/ranges.rs b/src/lib/parser/shell_expand/ranges.rs
index 114bc93a..923b5f45 100644
--- a/src/lib/parser/shell_expand/ranges.rs
+++ b/src/lib/parser/shell_expand/ranges.rs
@@ -142,14 +142,19 @@ pub(crate) fn parse_range(input: &str) -> Option<Vec<String>> {
                         } else {
                             return None;
                         }
-                    }
+                    };
                 }
 
                 macro_rules! finish {
                     ($inclusive:expr, $read:expr) => {
                         let end_str = &input[$read..];
                         if let Some((start, end)) = strings_to_isizes(first, end_str) {
-                            return numeric_range(start, end, if start < end { 1 } else { -1 }, $inclusive);
+                            return numeric_range(
+                                start,
+                                end,
+                                if start < end { 1 } else { -1 },
+                                $inclusive,
+                            );
                         } else {
                             finish_char!($inclusive, end_str, 1);
                         }
diff --git a/src/lib/parser/shell_expand/words/index.rs b/src/lib/parser/shell_expand/words/index.rs
index 4e25c65d..dcd9a7b6 100644
--- a/src/lib/parser/shell_expand/words/index.rs
+++ b/src/lib/parser/shell_expand/words/index.rs
@@ -10,6 +10,17 @@ pub(crate) enum Index {
 }
 
 impl Index {
+    pub(crate) fn resolve(&self, vector_length: usize) -> Option<usize> {
+        match *self {
+            Index::Forward(n) => Some(n),
+            Index::Backward(n) => if n >= vector_length {
+                None
+            } else {
+                Some(vector_length - (n + 1))
+            },
+        }
+    }
+
     /// Construct an index using the following convetions:
     /// - A positive value `n` represents `Forward(n)`
     /// - A negative value `-n` reprents `Backwards(n - 1)` such that:
@@ -23,15 +34,4 @@ impl Index {
             Index::Forward(input.abs() as usize)
         }
     }
-
-    pub(crate) fn resolve(&self, vector_length: usize) -> Option<usize> {
-        match *self {
-            Index::Forward(n) => Some(n),
-            Index::Backward(n) => if n >= vector_length {
-                None
-            } else {
-                Some(vector_length - (n + 1))
-            },
-        }
-    }
 }
diff --git a/src/lib/parser/shell_expand/words/methods/arrays.rs b/src/lib/parser/shell_expand/words/methods/arrays.rs
index 87b632df..3c08de92 100644
--- a/src/lib/parser/shell_expand/words/methods/arrays.rs
+++ b/src/lib/parser/shell_expand/words/methods/arrays.rs
@@ -1,7 +1,13 @@
-use super::Pattern;
-use super::strings::unescape;
-use super::super::{Index, Select, SelectWithSize};
-use super::super::super::{expand_string, is_expression, Expander};
+use super::{
+    super::{
+        super::{expand_string, is_expression, Expander},
+        Index,
+        Select,
+        SelectWithSize,
+    },
+    strings::unescape,
+    Pattern,
+};
 use smallstring::SmallString;
 use std::char;
 use types::Array;
@@ -16,54 +22,66 @@ pub(crate) struct ArrayMethod<'a> {
 }
 
 impl<'a> ArrayMethod<'a> {
-    pub(crate) fn handle<E: Expander>(&self, current: &mut String, expand_func: &E) {
-        let res = match self.method {
-            "split" => self.split(expand_func).map(|r| r.join(" ")),
-            _ => Err("invalid array method"),
-        };
-        match res {
-            Ok(output) => current.push_str(&output),
-            Err(msg) => eprintln!("ion: {}: {}", self.method, msg),
-        }
+    fn reverse<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let mut result = self.resolve_array(expand_func);
+        result.reverse();
+        Ok(result)
     }
 
-    pub(crate) fn handle_as_array<E: Expander>(&self, expand_func: &E) -> Array {
-        let res = match self.method {
-            "split" => self.split(expand_func),
-            "split_at" => self.split_at(expand_func),
-            "graphemes" => self.graphemes(expand_func),
-            "bytes" => self.bytes(expand_func),
-            "chars" => self.chars(expand_func),
-            "lines" => self.lines(expand_func),
-            "reverse" => self.reverse(expand_func),
-            _ => Err("invalid array method"),
-        };
+    fn lines<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        Ok(variable
+            .lines()
+            .into_iter()
+            .map(|line| line.to_string())
+            .collect())
+    }
 
-        res.unwrap_or_else(|m| {
-            eprintln!("ion: {}: {}", self.method, m);
-            array![]
-        })
+    fn chars<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        let len = variable.chars().count();
+        Ok(variable
+            .chars()
+            .map(|c| c.to_string())
+            .select(self.selection.clone(), len))
     }
 
-    #[inline]
-    fn resolve_var<E: Expander>(&self, expand_func: &E) -> String {
-        if let Some(variable) = expand_func.variable(self.variable, false) {
-            variable
-        } else if is_expression(self.variable) {
-            expand_string(self.variable, expand_func, false).join(" ")
-        } else {
-            "".into()
-        }
+    fn bytes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        let len = variable.as_bytes().len();
+        Ok(variable
+            .bytes()
+            .map(|b| b.to_string())
+            .select(self.selection.clone(), len))
     }
 
-    #[inline]
-    fn resolve_array<E: Expander>(&self, expand_func: &E) -> Array {
-        if let Some(array) = expand_func.array(self.variable, Select::All) {
-            array.clone()
-        } else if is_expression(self.variable) {
-            expand_string(self.variable, expand_func, false)
-        } else {
-            array![]
+    fn graphemes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        let graphemes: Vec<String> = UnicodeSegmentation::graphemes(variable.as_str(), true)
+            .map(From::from)
+            .collect();
+        let len = graphemes.len();
+        Ok(graphemes.into_iter().select(self.selection.clone(), len))
+    }
+
+    fn split_at<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        match self.pattern {
+            Pattern::StringPattern(string) => if let Ok(value) =
+                expand_string(string, expand_func, false)
+                    .join(" ")
+                    .parse::<usize>()
+            {
+                if value < variable.len() {
+                    let (l, r) = variable.split_at(value);
+                    Ok(array![SmallString::from(l), SmallString::from(r)])
+                } else {
+                    Err("value is out of bounds")
+                }
+            } else {
+                Err("requires a valid number as an argument")
+            },
+            Pattern::Whitespace => Err("requires an argument"),
         }
     }
 
@@ -72,8 +90,7 @@ impl<'a> ArrayMethod<'a> {
         let res = match (&self.pattern, self.selection.clone()) {
             (_, Select::None) => Some("".into()).into_iter().collect(),
             (&Pattern::StringPattern(pattern), Select::All) => variable
-                .split(&unescape(&expand_string(pattern, expand_func, false)
-                    .join(" "))?)
+                .split(&unescape(&expand_string(pattern, expand_func, false).join(" "))?)
                 .map(From::from)
                 .collect(),
             (&Pattern::Whitespace, Select::All) => variable
@@ -82,8 +99,7 @@ impl<'a> ArrayMethod<'a> {
                 .map(From::from)
                 .collect(),
             (&Pattern::StringPattern(pattern), Select::Index(Index::Forward(id))) => variable
-                .split(&unescape(&expand_string(pattern, expand_func, false)
-                    .join(" "))?)
+                .split(&unescape(&expand_string(pattern, expand_func, false).join(" "))?)
                 .nth(id)
                 .map(From::from)
                 .into_iter()
@@ -96,8 +112,7 @@ impl<'a> ArrayMethod<'a> {
                 .into_iter()
                 .collect(),
             (&Pattern::StringPattern(pattern), Select::Index(Index::Backward(id))) => variable
-                .rsplit(&unescape(&expand_string(pattern, expand_func, false)
-                    .join(" "))?)
+                .rsplit(&unescape(&expand_string(pattern, expand_func, false).join(" "))?)
                 .nth(id)
                 .map(From::from)
                 .into_iter()
@@ -140,92 +155,81 @@ impl<'a> ArrayMethod<'a> {
         Ok(res)
     }
 
-    fn split_at<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
-        let variable = self.resolve_var(expand_func);
-        match self.pattern {
-            Pattern::StringPattern(string) => if let Ok(value) =
-                expand_string(string, expand_func, false)
-                    .join(" ")
-                    .parse::<usize>()
-            {
-                if value < variable.len() {
-                    let (l, r) = variable.split_at(value);
-                    Ok(array![SmallString::from(l), SmallString::from(r)])
-                } else {
-                    Err("value is out of bounds")
-                }
-            } else {
-                Err("requires a valid number as an argument")
-            },
-            Pattern::Whitespace => Err("requires an argument"),
+    #[inline]
+    fn resolve_array<E: Expander>(&self, expand_func: &E) -> Array {
+        if let Some(array) = expand_func.array(self.variable, Select::All) {
+            array.clone()
+        } else if is_expression(self.variable) {
+            expand_string(self.variable, expand_func, false)
+        } else {
+            array![]
         }
     }
 
-    fn graphemes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
-        let variable = self.resolve_var(expand_func);
-        let graphemes: Vec<String> = UnicodeSegmentation::graphemes(variable.as_str(), true)
-            .map(From::from)
-            .collect();
-        let len = graphemes.len();
-        Ok(graphemes.into_iter().select(self.selection.clone(), len))
-    }
-
-    fn bytes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
-        let variable = self.resolve_var(expand_func);
-        let len = variable.as_bytes().len();
-        Ok(variable
-            .bytes()
-            .map(|b| b.to_string())
-            .select(self.selection.clone(), len))
+    #[inline]
+    fn resolve_var<E: Expander>(&self, expand_func: &E) -> String {
+        if let Some(variable) = expand_func.variable(self.variable, false) {
+            variable
+        } else if is_expression(self.variable) {
+            expand_string(self.variable, expand_func, false).join(" ")
+        } else {
+            "".into()
+        }
     }
 
-    fn chars<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
-        let variable = self.resolve_var(expand_func);
-        let len = variable.chars().count();
-        Ok(variable
-            .chars()
-            .map(|c| c.to_string())
-            .select(self.selection.clone(), len))
-    }
+    pub(crate) fn handle_as_array<E: Expander>(&self, expand_func: &E) -> Array {
+        let res = match self.method {
+            "split" => self.split(expand_func),
+            "split_at" => self.split_at(expand_func),
+            "graphemes" => self.graphemes(expand_func),
+            "bytes" => self.bytes(expand_func),
+            "chars" => self.chars(expand_func),
+            "lines" => self.lines(expand_func),
+            "reverse" => self.reverse(expand_func),
+            _ => Err("invalid array method"),
+        };
 
-    fn lines<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
-        let variable = self.resolve_var(expand_func);
-        Ok(variable
-            .lines()
-            .into_iter()
-            .map(|line| line.to_string())
-            .collect())
+        res.unwrap_or_else(|m| {
+            eprintln!("ion: {}: {}", self.method, m);
+            array![]
+        })
     }
 
-    fn reverse<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
-        let mut result = self.resolve_array(expand_func);
-        result.reverse();
-        Ok(result)
+    pub(crate) fn handle<E: Expander>(&self, current: &mut String, expand_func: &E) {
+        let res = match self.method {
+            "split" => self.split(expand_func).map(|r| r.join(" ")),
+            _ => Err("invalid array method"),
+        };
+        match res {
+            Ok(output) => current.push_str(&output),
+            Err(msg) => eprintln!("ion: {}: {}", self.method, msg),
+        }
     }
 }
 
 #[cfg(test)]
 mod test {
-    use super::*;
-    use super::super::Key;
-    use super::super::super::Range;
+    use super::{
+        super::{super::Range, Key},
+        *,
+    };
     use types::Value;
 
     struct VariableExpander;
 
     impl Expander for VariableExpander {
-        fn variable(&self, variable: &str, _: bool) -> Option<Value> {
+        fn array(&self, variable: &str, _: Select) -> Option<Array> {
             match variable {
-                "FOO" => Some("FOOBAR".to_owned()),
-                "SPACEDFOO" => Some("FOO BAR".to_owned()),
-                "MULTILINE" => Some("FOO\nBAR".to_owned()),
+                "ARRAY" => Some(array!["a", "b", "c"].to_owned()),
                 _ => None,
             }
         }
 
-        fn array(&self, variable: &str, _: Select) -> Option<Array> {
+        fn variable(&self, variable: &str, _: bool) -> Option<Value> {
             match variable {
-                "ARRAY" => Some(array!["a", "b", "c"].to_owned()),
+                "FOO" => Some("FOOBAR".to_owned()),
+                "SPACEDFOO" => Some("FOO BAR".to_owned()),
+                "MULTILINE" => Some("FOO\nBAR".to_owned()),
                 _ => None,
             }
         }
diff --git a/src/lib/parser/shell_expand/words/methods/mod.rs b/src/lib/parser/shell_expand/words/methods/mod.rs
index 34d85533..f27ee7f1 100644
--- a/src/lib/parser/shell_expand/words/methods/mod.rs
+++ b/src/lib/parser/shell_expand/words/methods/mod.rs
@@ -1,12 +1,10 @@
 mod arrays;
 mod strings;
 
-pub(crate) use self::arrays::ArrayMethod;
-pub(crate) use self::strings::StringMethod;
+pub(crate) use self::{arrays::ArrayMethod, strings::StringMethod};
 
 use self::strings::unescape;
-use super::{expand_string, Expander};
-use super::super::super::ArgumentSplitter;
+use super::{super::super::ArgumentSplitter, expand_string, Expander};
 
 #[derive(Debug, PartialEq, Clone)]
 pub(crate) enum Pattern<'a> {
@@ -20,9 +18,10 @@ pub(crate) struct Key {
 }
 
 impl Key {
+    pub(crate) fn get(&self) -> &::types::Key { return &self.key; }
+
     #[cfg(test)]
     pub(crate) fn new<K: Into<::types::Key>>(key: K) -> Key { Key { key: key.into() } }
-    pub(crate) fn get(&self) -> &::types::Key { return &self.key; }
 }
 
 pub(crate) struct MethodArgs<'a, 'b, E: 'b + Expander> {
@@ -31,8 +30,10 @@ pub(crate) struct MethodArgs<'a, 'b, E: 'b + Expander> {
 }
 
 impl<'a, 'b, E: 'b + Expander> MethodArgs<'a, 'b, E> {
-    pub(crate) fn new(args: &'a str, expand: &'b E) -> MethodArgs<'a, 'b, E> {
-        MethodArgs { args, expand }
+    pub(crate) fn array<'c>(&'c self) -> impl Iterator<Item = String> + 'c {
+        ArgumentSplitter::new(self.args)
+            .flat_map(move |x| expand_string(x, self.expand, false).into_iter())
+            .map(|s| unescape(&s).unwrap_or(String::from("")))
     }
 
     pub(crate) fn join(self, pattern: &str) -> String {
@@ -40,9 +41,7 @@ impl<'a, 'b, E: 'b + Expander> MethodArgs<'a, 'b, E> {
             .unwrap_or(String::from(""))
     }
 
-    pub(crate) fn array<'c>(&'c self) -> impl Iterator<Item = String> + 'c {
-        ArgumentSplitter::new(self.args)
-            .flat_map(move |x| expand_string(x, self.expand, false).into_iter())
-            .map(|s| unescape(&s).unwrap_or(String::from("")))
+    pub(crate) fn new(args: &'a str, expand: &'b E) -> MethodArgs<'a, 'b, E> {
+        MethodArgs { args, expand }
     }
 }
diff --git a/src/lib/parser/shell_expand/words/methods/strings.rs b/src/lib/parser/shell_expand/words/methods/strings.rs
index 1da1f928..0f37e167 100644
--- a/src/lib/parser/shell_expand/words/methods/strings.rs
+++ b/src/lib/parser/shell_expand/words/methods/strings.rs
@@ -1,6 +1,10 @@
-use super::MethodArgs;
-use super::super::Select;
-use super::super::super::{expand_string, is_expression, slice, Expander};
+use super::{
+    super::{
+        super::{expand_string, is_expression, slice, Expander},
+        Select,
+    },
+    MethodArgs,
+};
 use parser::assignments::is_array;
 use regex::Regex;
 use shell::plugins::methods::{self, MethodArguments, StringMethodPlugins};
@@ -102,25 +106,35 @@ impl<'a> StringMethod<'a> {
                 let is_true = if let Some(value) = expand.variable($variable, false) {
                     value.$method(&pattern)
                 } else if is_expression($variable) {
-                    expand_string($variable, expand, false).join(" ").$method(&pattern)
+                    expand_string($variable, expand, false)
+                        .join(" ")
+                        .$method(&pattern)
                 } else {
                     false
                 };
                 output.push_str(if is_true { "1" } else { "0" });
-            }}
+            }};
         }
 
         macro_rules! path_eval {
             ($method:tt) => {{
                 if let Some(value) = expand.variable(variable, false) {
-                    output.push_str(Path::new(&value).$method()
-                        .and_then(|os_str| os_str.to_str()).unwrap_or(value.as_str()));
+                    output.push_str(
+                        Path::new(&value)
+                            .$method()
+                            .and_then(|os_str| os_str.to_str())
+                            .unwrap_or(value.as_str()),
+                    );
                 } else if is_expression(variable) {
                     let word = expand_string(variable, expand, false).join(" ");
-                    output.push_str(Path::new(&word).$method()
-                        .and_then(|os_str| os_str.to_str()).unwrap_or(word.as_str()));
+                    output.push_str(
+                        Path::new(&word)
+                            .$method()
+                            .and_then(|os_str| os_str.to_str())
+                            .unwrap_or(word.as_str()),
+                    );
                 }
-            }}
+            }};
         }
 
         macro_rules! string_case {
@@ -131,7 +145,7 @@ impl<'a> StringMethod<'a> {
                     let word = expand_string(variable, expand, false).join(" ");
                     output.push_str(word.$method().as_str());
                 }
-            }}
+            }};
         }
 
         macro_rules! get_var {
@@ -141,7 +155,7 @@ impl<'a> StringMethod<'a> {
                 } else {
                     expand_string(variable, expand, false).join(" ")
                 }
-            }}
+            }};
         }
 
         match self.method {
diff --git a/src/lib/parser/shell_expand/words/mod.rs b/src/lib/parser/shell_expand/words/mod.rs
index 6eb968af..42302538 100644
--- a/src/lib/parser/shell_expand/words/mod.rs
+++ b/src/lib/parser/shell_expand/words/mod.rs
@@ -1,18 +1,19 @@
-#[cfg(test)]
-mod tests;
 mod index;
 mod methods;
 mod range;
 mod select;
+#[cfg(test)]
+mod tests;
 
-pub(crate) use self::index::Index;
-pub(crate) use self::methods::{ArrayMethod, Pattern, StringMethod};
 #[cfg(test)]
 pub(crate) use self::methods::Key;
-pub(crate) use self::range::Range;
-pub(crate) use self::select::{Select, SelectWithSize};
-use super::{expand_string, Expander};
-use super::super::ArgumentSplitter;
+pub(crate) use self::{
+    index::Index,
+    methods::{ArrayMethod, Pattern, StringMethod},
+    range::Range,
+    select::{Select, SelectWithSize},
+};
+use super::{super::ArgumentSplitter, expand_string, Expander};
 
 // Bit Twiddling Guide:
 // var & FLAG != 0 checks if FLAG is enabled
@@ -55,189 +56,283 @@ pub(crate) struct WordIterator<'a, E: Expander + 'a> {
 }
 
 impl<'a, E: Expander + 'a> WordIterator<'a, E> {
-    pub(crate) fn new(data: &'a str, expanders: &'a E) -> WordIterator<'a, E> {
-        WordIterator {
-            data,
-            read: 0,
-            flags: Flags::empty(),
-            expanders,
+    fn arithmetic_expression<I: Iterator<Item = u8>>(&mut self, iter: &mut I) -> WordToken<'a> {
+        let mut paren: i8 = 0;
+        let start = self.read;
+        while let Some(character) = iter.next() {
+            match character {
+                b'(' => paren += 1,
+                b')' => {
+                    if paren == 0 {
+                        // Skip the incoming ); we have validated this syntax so it should be OK
+                        let _ = iter.next();
+                        let output = &self.data[start..self.read];
+                        self.read += 2;
+                        return WordToken::Arithmetic(output);
+                    } else {
+                        paren -= 1;
+                    }
+                }
+                _ => (),
+            }
+            self.read += 1;
         }
+        panic!("ion: fatal syntax error: unterminated arithmetic expression");
     }
 
-    // Contains the grammar for collecting whitespace characters
-    fn whitespaces<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    fn glob_check<I>(&mut self, iterator: &mut I) -> bool
     where
-        I: Iterator<Item = u8>,
+        I: Iterator<Item = u8> + Clone,
     {
-        let start = self.read;
-        self.read += 1;
-        while let Some(character) = iterator.next() {
-            if character == b' ' {
-                self.read += 1;
-            } else {
-                return WordToken::Whitespace(&self.data[start..self.read]);
+        // Clone the iterator and scan for illegal characters until the corresponding ]
+        // is discovered. If none are found, then it's a valid glob signature.
+        let mut moves = 0;
+        let mut glob = false;
+        let mut square_bracket = 0;
+        let mut iter = iterator.clone();
+        while let Some(character) = iter.next() {
+            moves += 1;
+            match character {
+                b'[' => {
+                    square_bracket += 1;
+                }
+                b' ' | b'"' | b'\'' | b'$' | b'{' | b'}' => break,
+                b']' => {
+                    // If the glob is less than three bytes in width, then it's empty and thus
+                    // invalid. If it's not adjacent to text, it's not a glob.
+                    let next_char = iter.clone().next();
+                    if !(moves <= 3 && square_bracket == 1)
+                        && (next_char != None && next_char != Some(b' '))
+                    {
+                        glob = true;
+                        break;
+                    }
+                }
+                _ => (),
             }
         }
 
-        WordToken::Whitespace(&self.data[start..self.read])
+        if glob {
+            for _ in 0..moves {
+                iterator.next();
+            }
+            self.read += moves + 1;
+            true
+        } else {
+            self.read += 1;
+            false
+        }
     }
 
-    // Contains the logic for parsing braced variables
-    fn braced_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    /// Contains the grammar for parsing array expression syntax
+    fn array<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
         let start = self.read;
+        let mut level = 0;
         while let Some(character) = iterator.next() {
-            if character == b'}' {
-                let output = &self.data[start..self.read];
-                self.read += 1;
-                return WordToken::Variable(output, self.flags.contains(Flags::DQUOTE), Select::All);
+            match character {
+                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
+                b'\\' => self.flags ^= Flags::BACKSL,
+                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
+                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
+                b'[' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => level += 1,
+                b']' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => if level == 0 {
+                    let elements =
+                        ArgumentSplitter::new(&self.data[start..self.read]).collect::<Vec<&str>>();
+                    self.read += 1;
+
+                    return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
+                        let _ = iterator.next();
+                        WordToken::Array(elements, self.read_selection(iterator))
+                    } else {
+                        WordToken::Array(elements, Select::All)
+                    };
+                } else {
+                    level -= 1;
+                },
+                _ => (),
             }
             self.read += 1;
         }
 
-        // The validator at the frontend should catch unterminated braced variables.
-        panic!("ion: fatal error with syntax validation parsing: unterminated braced variable");
+        panic!("ion: fatal error with syntax validation: unterminated array expression")
     }
 
-    /// Contains the logic for parsing variable syntax
-    fn variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    /// Contains the grammar for parsing brace expansion syntax
+    fn braces<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
         let mut start = self.read;
-        self.read += 1;
+        let mut level = 0;
+        let mut elements = Vec::new();
         while let Some(character) = iterator.next() {
             match character {
-                b'(' => {
-                    let method = &self.data[start..self.read];
+                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
+                b'\\' => self.flags ^= Flags::BACKSL,
+                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
+                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
+                b',' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) && level == 0 => {
+                    elements.push(&self.data[start..self.read]);
+                    start = self.read + 1;
+                }
+                b'{' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => level += 1,
+                b'}' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => if level == 0 {
+                    elements.push(&self.data[start..self.read]);
                     self.read += 1;
-                    start = self.read;
-                    let mut depth = 0;
-                    while let Some(character) = iterator.next() {
-                        match character {
-                            b',' if depth == 0 => {
-                                let variable = &self.data[start..self.read];
-                                self.read += 1;
-                                start = self.read;
-                                while let Some(character) = iterator.next() {
-                                    if character == b')' {
-                                        self.read += 1;
-                                        if depth != 0 {
-                                            depth -= 1;
-                                            continue;
-                                        }
-                                        let pattern = &self.data[start..self.read - 1].trim();
-                                        return if let Some(&b'[') =
-                                            self.data.as_bytes().get(self.read)
-                                        {
-                                            let _ = iterator.next();
-                                            WordToken::StringMethod(StringMethod {
-                                                method,
-                                                variable: variable.trim(),
-                                                pattern,
-                                                selection: self.read_selection(iterator),
-                                            })
-                                        } else {
-                                            WordToken::StringMethod(StringMethod {
-                                                method,
-                                                variable: variable.trim(),
-                                                pattern,
-                                                selection: Select::All,
-                                            })
-                                        };
-                                    } else if character == b'(' {
-                                        depth += 1;
-                                    } else if character == b'\\' {
-                                        self.read += 1;
-                                        let _ = iterator.next();
-                                    }
-                                    self.read += 1;
-                                }
-                            }
-                            b')' if depth == 0 => {
-                                // If no pattern is supplied, the default is a space.
-                                let variable = &self.data[start..self.read];
-                                self.read += 1;
+                    return WordToken::Brace(elements);
+                } else {
+                    level -= 1;
+                },
+                _ => (),
+            }
+            self.read += 1;
+        }
 
-                                return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
-                                    let _ = iterator.next();
-                                    WordToken::StringMethod(StringMethod {
-                                        method,
-                                        variable: variable.trim(),
-                                        pattern: " ",
-                                        selection: self.read_selection(iterator),
-                                    })
-                                } else {
-                                    WordToken::StringMethod(StringMethod {
-                                        method,
-                                        variable: variable.trim(),
-                                        pattern: " ",
-                                        selection: Select::All,
-                                    })
-                                };
-                            }
-                            b')' => depth -= 1,
-                            b'(' => depth += 1,
-                            _ => (),
-                        }
-                        self.read += 1;
-                    }
+        panic!("ion: fatal error with syntax validation: unterminated brace")
+    }
 
-                    panic!("ion: fatal error with syntax validation parsing: unterminated method");
+    /// Contains the logic for parsing array subshell syntax.
+    fn array_process<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    where
+        I: Iterator<Item = u8>,
+    {
+        let start = self.read;
+        let mut level = 0;
+        while let Some(character) = iterator.next() {
+            match character {
+                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
+                b'\\' => self.flags ^= Flags::BACKSL,
+                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
+                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
+                b'@' if !self.flags.contains(Flags::SQUOTE) => {
+                    if self.data.as_bytes()[self.read + 1] == b'(' {
+                        level += 1;
+                    }
                 }
-                // Only alphanumerical and underscores are allowed in variable names
-                0...47 | 58...64 | 91...94 | 96 | 123...127 => {
-                    let variable = &self.data[start..self.read];
-
-                    return if character == b'[' {
-                        WordToken::Variable(
-                            variable,
+                b')' if !self.flags.contains(Flags::SQUOTE) => if level == 0 {
+                    let array_process_contents = &self.data[start..self.read];
+                    self.read += 1;
+                    return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
+                        let _ = iterator.next();
+                        WordToken::ArrayProcess(
+                            array_process_contents,
                             self.flags.contains(Flags::DQUOTE),
                             self.read_selection(iterator),
                         )
                     } else {
-                        WordToken::Variable(
-                            variable,
+                        WordToken::ArrayProcess(
+                            array_process_contents,
                             self.flags.contains(Flags::DQUOTE),
                             Select::All,
                         )
                     };
-                }
+                } else {
+                    level -= 1;
+                },
                 _ => (),
             }
             self.read += 1;
         }
 
-        WordToken::Variable(
-            &self.data[start..],
-            self.flags.contains(Flags::DQUOTE),
-            Select::All,
-        )
+        // The validator at the frontend should catch unterminated processes.
+        panic!("ion: fatal error with syntax validation: unterminated array process");
     }
 
-    fn read_selection<I>(&mut self, iterator: &mut I) -> Select
+    /// Contains the logic for parsing subshell syntax.
+    fn process<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
-        self.read += 1;
         let start = self.read;
+        let mut level = 0;
         while let Some(character) = iterator.next() {
-            if let b']' = character {
-                let value =
-                    expand_string(&self.data[start..self.read], self.expanders, false).join(" ");
-                let selection = match value.parse::<Select>() {
-                    Ok(selection) => selection,
-                    Err(_) => Select::None,
-                };
-                self.read += 1;
-                return selection;
+            match character {
+                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
+                b'\\' => self.flags ^= Flags::BACKSL,
+                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
+                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
+                b'$' if !self.flags.contains(Flags::SQUOTE) => {
+                    if self.data.as_bytes()[self.read + 1] == b'(' {
+                        level += 1;
+                    }
+                }
+                b')' if !self.flags.contains(Flags::SQUOTE) => if level == 0 {
+                    let output = &self.data[start..self.read];
+                    self.read += 1;
+                    return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
+                        let _ = iterator.next();
+                        WordToken::Process(
+                            output,
+                            self.flags.contains(Flags::DQUOTE),
+                            self.read_selection(iterator),
+                        )
+                    } else {
+                        WordToken::Process(output, self.flags.contains(Flags::DQUOTE), Select::All)
+                    };
+                } else {
+                    level -= 1;
+                },
+                _ => (),
             }
             self.read += 1;
         }
 
-        panic!()
+        // The validator at the frontend should catch unterminated processes.
+        panic!("ion: fatal error with syntax validation: unterminated process");
+    }
+
+    fn braced_array_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    where
+        I: Iterator<Item = u8>,
+    {
+        let start = self.read;
+        // self.read += 1;
+        while let Some(character) = iterator.next() {
+            match character {
+                b'[' => {
+                    let result = WordToken::ArrayVariable(
+                        &self.data[start..self.read],
+                        self.flags.contains(Flags::DQUOTE),
+                        self.read_selection(iterator),
+                    );
+                    self.read += 1;
+                    if let Some(b'}') = iterator.next() {
+                        return result;
+                    }
+                    panic!(
+                        "ion: fatal with syntax validation error: unterminated braced array \
+                         expression"
+                    );
+                }
+                b'}' => {
+                    let output = &self.data[start..self.read];
+                    self.read += 1;
+                    return WordToken::ArrayVariable(
+                        output,
+                        self.flags.contains(Flags::DQUOTE),
+                        Select::All,
+                    );
+                }
+                // Only alphanumerical and underscores are allowed in variable names
+                0...47 | 58...64 | 91...94 | 96 | 123...127 => {
+                    return WordToken::ArrayVariable(
+                        &self.data[start..self.read],
+                        self.flags.contains(Flags::DQUOTE),
+                        Select::All,
+                    )
+                }
+                _ => (),
+            }
+            self.read += 1;
+        }
+        WordToken::ArrayVariable(
+            &self.data[start..],
+            self.flags.contains(Flags::DQUOTE),
+            Select::All,
+        )
     }
 
     /// Contains the logic for parsing array variable syntax
@@ -344,283 +439,189 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> {
         )
     }
 
-    fn braced_array_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    fn read_selection<I>(&mut self, iterator: &mut I) -> Select
     where
         I: Iterator<Item = u8>,
     {
+        self.read += 1;
         let start = self.read;
-        // self.read += 1;
         while let Some(character) = iterator.next() {
-            match character {
-                b'[' => {
-                    let result = WordToken::ArrayVariable(
-                        &self.data[start..self.read],
-                        self.flags.contains(Flags::DQUOTE),
-                        self.read_selection(iterator),
-                    );
-                    self.read += 1;
-                    if let Some(b'}') = iterator.next() {
-                        return result;
-                    }
-                    panic!(
-                        "ion: fatal with syntax validation error: unterminated braced array \
-                         expression"
-                    );
-                }
-                b'}' => {
-                    let output = &self.data[start..self.read];
-                    self.read += 1;
-                    return WordToken::ArrayVariable(
-                        output,
-                        self.flags.contains(Flags::DQUOTE),
-                        Select::All,
-                    );
-                }
-                // Only alphanumerical and underscores are allowed in variable names
-                0...47 | 58...64 | 91...94 | 96 | 123...127 => {
-                    return WordToken::ArrayVariable(
-                        &self.data[start..self.read],
-                        self.flags.contains(Flags::DQUOTE),
-                        Select::All,
-                    )
-                }
-                _ => (),
+            if let b']' = character {
+                let value =
+                    expand_string(&self.data[start..self.read], self.expanders, false).join(" ");
+                let selection = match value.parse::<Select>() {
+                    Ok(selection) => selection,
+                    Err(_) => Select::None,
+                };
+                self.read += 1;
+                return selection;
             }
             self.read += 1;
         }
-        WordToken::ArrayVariable(
-            &self.data[start..],
-            self.flags.contains(Flags::DQUOTE),
-            Select::All,
-        )
+
+        panic!()
     }
 
-    /// Contains the logic for parsing subshell syntax.
-    fn process<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    /// Contains the logic for parsing variable syntax
+    fn variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
-        let start = self.read;
-        let mut level = 0;
+        let mut start = self.read;
+        self.read += 1;
         while let Some(character) = iterator.next() {
             match character {
-                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
-                b'\\' => self.flags ^= Flags::BACKSL,
-                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
-                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
-                b'$' if !self.flags.contains(Flags::SQUOTE) => {
-                    if self.data.as_bytes()[self.read + 1] == b'(' {
-                        level += 1;
-                    }
-                }
-                b')' if !self.flags.contains(Flags::SQUOTE) => if level == 0 {
-                    let output = &self.data[start..self.read];
+                b'(' => {
+                    let method = &self.data[start..self.read];
                     self.read += 1;
-                    return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
-                        let _ = iterator.next();
-                        WordToken::Process(
-                            output,
-                            self.flags.contains(Flags::DQUOTE),
-                            self.read_selection(iterator),
-                        )
-                    } else {
-                        WordToken::Process(output, self.flags.contains(Flags::DQUOTE), Select::All)
-                    };
-                } else {
-                    level -= 1;
-                },
-                _ => (),
-            }
-            self.read += 1;
-        }
-
-        // The validator at the frontend should catch unterminated processes.
-        panic!("ion: fatal error with syntax validation: unterminated process");
-    }
+                    start = self.read;
+                    let mut depth = 0;
+                    while let Some(character) = iterator.next() {
+                        match character {
+                            b',' if depth == 0 => {
+                                let variable = &self.data[start..self.read];
+                                self.read += 1;
+                                start = self.read;
+                                while let Some(character) = iterator.next() {
+                                    if character == b')' {
+                                        self.read += 1;
+                                        if depth != 0 {
+                                            depth -= 1;
+                                            continue;
+                                        }
+                                        let pattern = &self.data[start..self.read - 1].trim();
+                                        return if let Some(&b'[') =
+                                            self.data.as_bytes().get(self.read)
+                                        {
+                                            let _ = iterator.next();
+                                            WordToken::StringMethod(StringMethod {
+                                                method,
+                                                variable: variable.trim(),
+                                                pattern,
+                                                selection: self.read_selection(iterator),
+                                            })
+                                        } else {
+                                            WordToken::StringMethod(StringMethod {
+                                                method,
+                                                variable: variable.trim(),
+                                                pattern,
+                                                selection: Select::All,
+                                            })
+                                        };
+                                    } else if character == b'(' {
+                                        depth += 1;
+                                    } else if character == b'\\' {
+                                        self.read += 1;
+                                        let _ = iterator.next();
+                                    }
+                                    self.read += 1;
+                                }
+                            }
+                            b')' if depth == 0 => {
+                                // If no pattern is supplied, the default is a space.
+                                let variable = &self.data[start..self.read];
+                                self.read += 1;
 
-    /// Contains the logic for parsing array subshell syntax.
-    fn array_process<I>(&mut self, iterator: &mut I) -> WordToken<'a>
-    where
-        I: Iterator<Item = u8>,
-    {
-        let start = self.read;
-        let mut level = 0;
-        while let Some(character) = iterator.next() {
-            match character {
-                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
-                b'\\' => self.flags ^= Flags::BACKSL,
-                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
-                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
-                b'@' if !self.flags.contains(Flags::SQUOTE) => {
-                    if self.data.as_bytes()[self.read + 1] == b'(' {
-                        level += 1;
+                                return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
+                                    let _ = iterator.next();
+                                    WordToken::StringMethod(StringMethod {
+                                        method,
+                                        variable: variable.trim(),
+                                        pattern: " ",
+                                        selection: self.read_selection(iterator),
+                                    })
+                                } else {
+                                    WordToken::StringMethod(StringMethod {
+                                        method,
+                                        variable: variable.trim(),
+                                        pattern: " ",
+                                        selection: Select::All,
+                                    })
+                                };
+                            }
+                            b')' => depth -= 1,
+                            b'(' => depth += 1,
+                            _ => (),
+                        }
+                        self.read += 1;
                     }
+
+                    panic!("ion: fatal error with syntax validation parsing: unterminated method");
                 }
-                b')' if !self.flags.contains(Flags::SQUOTE) => if level == 0 {
-                    let array_process_contents = &self.data[start..self.read];
-                    self.read += 1;
-                    return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
-                        let _ = iterator.next();
-                        WordToken::ArrayProcess(
-                            array_process_contents,
+                // Only alphanumerical and underscores are allowed in variable names
+                0...47 | 58...64 | 91...94 | 96 | 123...127 => {
+                    let variable = &self.data[start..self.read];
+
+                    return if character == b'[' {
+                        WordToken::Variable(
+                            variable,
                             self.flags.contains(Flags::DQUOTE),
                             self.read_selection(iterator),
                         )
                     } else {
-                        WordToken::ArrayProcess(
-                            array_process_contents,
+                        WordToken::Variable(
+                            variable,
                             self.flags.contains(Flags::DQUOTE),
                             Select::All,
                         )
                     };
-                } else {
-                    level -= 1;
-                },
+                }
                 _ => (),
             }
             self.read += 1;
         }
 
-        // The validator at the frontend should catch unterminated processes.
-        panic!("ion: fatal error with syntax validation: unterminated array process");
+        WordToken::Variable(
+            &self.data[start..],
+            self.flags.contains(Flags::DQUOTE),
+            Select::All,
+        )
     }
 
-    /// Contains the grammar for parsing brace expansion syntax
-    fn braces<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    // Contains the logic for parsing braced variables
+    fn braced_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
-        let mut start = self.read;
-        let mut level = 0;
-        let mut elements = Vec::new();
+        let start = self.read;
         while let Some(character) = iterator.next() {
-            match character {
-                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
-                b'\\' => self.flags ^= Flags::BACKSL,
-                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
-                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
-                b',' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) && level == 0 => {
-                    elements.push(&self.data[start..self.read]);
-                    start = self.read + 1;
-                }
-                b'{' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => level += 1,
-                b'}' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => if level == 0 {
-                    elements.push(&self.data[start..self.read]);
-                    self.read += 1;
-                    return WordToken::Brace(elements);
-                } else {
-                    level -= 1;
-                },
-                _ => (),
+            if character == b'}' {
+                let output = &self.data[start..self.read];
+                self.read += 1;
+                return WordToken::Variable(output, self.flags.contains(Flags::DQUOTE), Select::All);
             }
             self.read += 1;
         }
 
-        panic!("ion: fatal error with syntax validation: unterminated brace")
+        // The validator at the frontend should catch unterminated braced variables.
+        panic!("ion: fatal error with syntax validation parsing: unterminated braced variable");
     }
 
-    /// Contains the grammar for parsing array expression syntax
-    fn array<I>(&mut self, iterator: &mut I) -> WordToken<'a>
+    // Contains the grammar for collecting whitespace characters
+    fn whitespaces<I>(&mut self, iterator: &mut I) -> WordToken<'a>
     where
         I: Iterator<Item = u8>,
     {
         let start = self.read;
-        let mut level = 0;
+        self.read += 1;
         while let Some(character) = iterator.next() {
-            match character {
-                _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
-                b'\\' => self.flags ^= Flags::BACKSL,
-                b'\'' if !self.flags.contains(Flags::DQUOTE) => self.flags ^= Flags::SQUOTE,
-                b'"' if !self.flags.contains(Flags::SQUOTE) => self.flags ^= Flags::DQUOTE,
-                b'[' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => level += 1,
-                b']' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => if level == 0 {
-                    let elements =
-                        ArgumentSplitter::new(&self.data[start..self.read]).collect::<Vec<&str>>();
-                    self.read += 1;
-
-                    return if let Some(&b'[') = self.data.as_bytes().get(self.read) {
-                        let _ = iterator.next();
-                        WordToken::Array(elements, self.read_selection(iterator))
-                    } else {
-                        WordToken::Array(elements, Select::All)
-                    };
-                } else {
-                    level -= 1;
-                },
-                _ => (),
-            }
-            self.read += 1;
-        }
-
-        panic!("ion: fatal error with syntax validation: unterminated array expression")
-    }
-
-    fn glob_check<I>(&mut self, iterator: &mut I) -> bool
-    where
-        I: Iterator<Item = u8> + Clone,
-    {
-        // Clone the iterator and scan for illegal characters until the corresponding ]
-        // is discovered. If none are found, then it's a valid glob signature.
-        let mut moves = 0;
-        let mut glob = false;
-        let mut square_bracket = 0;
-        let mut iter = iterator.clone();
-        while let Some(character) = iter.next() {
-            moves += 1;
-            match character {
-                b'[' => {
-                    square_bracket += 1;
-                }
-                b' ' | b'"' | b'\'' | b'$' | b'{' | b'}' => break,
-                b']' => {
-                    // If the glob is less than three bytes in width, then it's empty and thus
-                    // invalid. If it's not adjacent to text, it's not a glob.
-                    let next_char = iter.clone().next();
-                    if !(moves <= 3 && square_bracket == 1)
-                        && (next_char != None && next_char != Some(b' '))
-                    {
-                        glob = true;
-                        break;
-                    }
-                }
-                _ => (),
+            if character == b' ' {
+                self.read += 1;
+            } else {
+                return WordToken::Whitespace(&self.data[start..self.read]);
             }
         }
 
-        if glob {
-            for _ in 0..moves {
-                iterator.next();
-            }
-            self.read += moves + 1;
-            true
-        } else {
-            self.read += 1;
-            false
-        }
+        WordToken::Whitespace(&self.data[start..self.read])
     }
 
-    fn arithmetic_expression<I: Iterator<Item = u8>>(&mut self, iter: &mut I) -> WordToken<'a> {
-        let mut paren: i8 = 0;
-        let start = self.read;
-        while let Some(character) = iter.next() {
-            match character {
-                b'(' => paren += 1,
-                b')' => {
-                    if paren == 0 {
-                        // Skip the incoming ); we have validated this syntax so it should be OK
-                        let _ = iter.next();
-                        let output = &self.data[start..self.read];
-                        self.read += 2;
-                        return WordToken::Arithmetic(output);
-                    } else {
-                        paren -= 1;
-                    }
-                }
-                _ => (),
-            }
-            self.read += 1;
+    pub(crate) fn new(data: &'a str, expanders: &'a E) -> WordIterator<'a, E> {
+        WordIterator {
+            data,
+            read: 0,
+            flags: Flags::empty(),
+            expanders,
         }
-        panic!("ion: fatal syntax error: unterminated arithmetic expression");
     }
 }
 
diff --git a/src/lib/parser/shell_expand/words/range.rs b/src/lib/parser/shell_expand/words/range.rs
index 6b1a40c8..d3c779e5 100644
--- a/src/lib/parser/shell_expand/words/range.rs
+++ b/src/lib/parser/shell_expand/words/range.rs
@@ -13,38 +13,6 @@ pub(crate) struct Range {
 }
 
 impl Range {
-    pub(crate) fn to(end: Index) -> Range {
-        Range {
-            start: Index::new(0),
-            end,
-            inclusive: false,
-        }
-    }
-
-    pub(crate) fn from(start: Index) -> Range {
-        Range {
-            start,
-            end: Index::new(-1),
-            inclusive: true,
-        }
-    }
-
-    pub(crate) fn inclusive(start: Index, end: Index) -> Range {
-        Range {
-            start,
-            end,
-            inclusive: true,
-        }
-    }
-
-    pub(crate) fn exclusive(start: Index, end: Index) -> Range {
-        Range {
-            start,
-            end,
-            inclusive: false,
-        }
-    }
-
     /// Returns the bounds of this range as a tuple containing:
     /// - The starting point of the range
     /// - The length of the range
@@ -73,4 +41,36 @@ impl Range {
             None
         }
     }
+
+    pub(crate) fn exclusive(start: Index, end: Index) -> Range {
+        Range {
+            start,
+            end,
+            inclusive: false,
+        }
+    }
+
+    pub(crate) fn inclusive(start: Index, end: Index) -> Range {
+        Range {
+            start,
+            end,
+            inclusive: true,
+        }
+    }
+
+    pub(crate) fn from(start: Index) -> Range {
+        Range {
+            start,
+            end: Index::new(-1),
+            inclusive: true,
+        }
+    }
+
+    pub(crate) fn to(end: Index) -> Range {
+        Range {
+            start: Index::new(0),
+            end,
+            inclusive: false,
+        }
+    }
 }
diff --git a/src/lib/parser/shell_expand/words/select.rs b/src/lib/parser/shell_expand/words/select.rs
index 0bc07bed..7cf44711 100644
--- a/src/lib/parser/shell_expand/words/select.rs
+++ b/src/lib/parser/shell_expand/words/select.rs
@@ -1,8 +1,8 @@
-use super::{Index, Range};
-use super::methods::Key;
-use super::super::ranges::parse_index_range;
-use std::iter::{empty, FromIterator};
-use std::str::FromStr;
+use super::{super::ranges::parse_index_range, methods::Key, Index, Range};
+use std::{
+    iter::{empty, FromIterator},
+    str::FromStr,
+};
 
 /// Represents a filter on a vector-like object
 #[derive(Debug, PartialEq, Clone)]
@@ -31,6 +31,7 @@ where
     I: Iterator<Item = T>,
 {
     type Item = T;
+
     fn select<O>(&mut self, s: Select, size: usize) -> O
     where
         O: FromIterator<Self::Item>,
@@ -54,6 +55,7 @@ where
 
 impl FromStr for Select {
     type Err = ();
+
     fn from_str(data: &str) -> Result<Select, ()> {
         if ".." == data {
             return Ok(Select::All);
diff --git a/src/lib/parser/statement/functions.rs b/src/lib/parser/statement/functions.rs
index 3bd6a01c..52a1623e 100644
--- a/src/lib/parser/statement/functions.rs
+++ b/src/lib/parser/statement/functions.rs
@@ -1,5 +1,7 @@
-use super::split_pattern;
-use super::super::assignments::{KeyBuf, KeyIterator, TypeError};
+use super::{
+    super::assignments::{KeyBuf, KeyIterator, TypeError},
+    split_pattern,
+};
 
 /// The arguments expression given to a function declaration goes into here, which will be
 /// converted into a tuple consisting of a `KeyIterator` iterator, which will collect type
@@ -19,8 +21,10 @@ pub(crate) fn collect_arguments<'a>(args: KeyIterator<'a>) -> Result<Vec<KeyBuf>
 
 #[cfg(test)]
 mod tests {
-    use super::*;
-    use super::super::super::assignments::{KeyBuf, Primitive};
+    use super::{
+        super::super::assignments::{KeyBuf, Primitive},
+        *,
+    };
 
     #[test]
     fn function_parsing() {
diff --git a/src/lib/parser/statement/mod.rs b/src/lib/parser/statement/mod.rs
index 476ee5aa..35611320 100644
--- a/src/lib/parser/statement/mod.rs
+++ b/src/lib/parser/statement/mod.rs
@@ -3,8 +3,10 @@ mod functions;
 mod parse;
 mod splitter;
 
-pub(crate) use self::parse::parse;
-pub(crate) use self::splitter::{StatementError, StatementSplitter};
+pub(crate) use self::{
+    parse::parse,
+    splitter::{StatementError, StatementSplitter},
+};
 use shell::flow_control::Statement;
 
 /// Parses a given statement string and return's the corresponding mapped
diff --git a/src/lib/parser/statement/parse.rs b/src/lib/parser/statement/parse.rs
index 5e7c9da6..4a28c826 100644
--- a/src/lib/parser/statement/parse.rs
+++ b/src/lib/parser/statement/parse.rs
@@ -1,8 +1,12 @@
-use super::case;
-use super::functions::{collect_arguments, parse_function};
-use super::super::{pipelines, ArgumentSplitter};
-use super::super::assignments::{split_assignment, Operator};
-use super::super::pipelines::Pipeline;
+use super::{
+    super::{
+        assignments::{split_assignment, Operator},
+        pipelines::{self, Pipeline},
+        ArgumentSplitter,
+    },
+    case,
+    functions::{collect_arguments, parse_function},
+};
 use shell::flow_control::{Case, ElseIf, ExportAction, LocalAction, Statement};
 use std::char;
 
@@ -223,9 +227,7 @@ pub(crate) fn parse(code: &str) -> Statement {
             return Statement::And(Box::new(parse(cmd[3..].trim_left())))
         }
         _ if cmd.eq("and") => return Statement::And(Box::new(Statement::Default)),
-        _ if cmd.starts_with("or ") => {
-            return Statement::Or(Box::new(parse(cmd[2..].trim_left())))
-        }
+        _ if cmd.starts_with("or ") => return Statement::Or(Box::new(parse(cmd[2..].trim_left()))),
         _ if cmd.eq("or") => return Statement::Or(Box::new(Statement::Default)),
         _ if cmd.starts_with("not ") => {
             return Statement::Not(Box::new(parse(cmd[3..].trim_left())))
@@ -246,8 +248,7 @@ mod tests {
     use self::pipelines::PipeItem;
     use super::*;
     use parser::assignments::{KeyBuf, Primitive};
-    use shell::{Job, JobKind};
-    use shell::flow_control::Statement;
+    use shell::{flow_control::Statement, Job, JobKind};
 
     #[test]
     fn parsing_ifs() {
@@ -255,22 +256,20 @@ mod tests {
         let parsed_if = parse("if test 1 -eq 2");
         let correct_parse = Statement::If {
             expression: Pipeline {
-                items: vec![
-                    PipeItem {
-                        job:     Job::new(
-                            vec![
-                                "test".to_owned(),
-                                "1".to_owned(),
-                                "-eq".to_owned(),
-                                "2".to_owned(),
-                            ].into_iter()
-                                .collect(),
-                            JobKind::Last,
-                        ),
-                        outputs: Vec::new(),
-                        inputs:  Vec::new(),
-                    },
-                ],
+                items: vec![PipeItem {
+                    job:     Job::new(
+                        vec![
+                            "test".to_owned(),
+                            "1".to_owned(),
+                            "-eq".to_owned(),
+                            "2".to_owned(),
+                        ].into_iter()
+                            .collect(),
+                        JobKind::Last,
+                    ),
+                    outputs: Vec::new(),
+                    inputs:  Vec::new(),
+                }],
             },
             success:    vec![],
             else_if:    vec![],
diff --git a/src/lib/parser/statement/splitter.rs b/src/lib/parser/statement/splitter.rs
index c59c3ff6..2651ccb3 100644
--- a/src/lib/parser/statement/splitter.rs
+++ b/src/lib/parser/statement/splitter.rs
@@ -2,9 +2,11 @@
 // - Rewrite this in the same style as shell_expand::words.
 // - Validate syntax in methods
 
-use std::fmt::{self, Display, Formatter};
-use std::u16;
-use std::cmp::max;
+use std::{
+    cmp::max,
+    fmt::{self, Display, Formatter},
+    u16,
+};
 
 bitflags! {
     pub struct Flags : u16 {
@@ -80,19 +82,6 @@ pub(crate) struct StatementSplitter {
 }
 
 impl<'a> StatementSplitter {
-    pub(crate) fn new(data: String) -> StatementSplitter {
-        StatementSplitter {
-            data:             data,
-            read:             0,
-            flags:            Flags::empty(),
-            a_level:          0,
-            ap_level:         0,
-            p_level:          0,
-            brace_level:      0,
-            math_paren_level: 0,
-        }
-    }
-
     fn single_quote<B: Iterator<Item = u8>>(&mut self, bytes: &mut B) -> usize {
         let mut read = 0;
         while let Some(character) = bytes.next() {
@@ -106,10 +95,24 @@ impl<'a> StatementSplitter {
         }
         read
     }
+
+    pub(crate) fn new(data: String) -> StatementSplitter {
+        StatementSplitter {
+            data,
+            read:             0,
+            flags:            Flags::empty(),
+            a_level:          0,
+            ap_level:         0,
+            p_level:          0,
+            brace_level:      0,
+            math_paren_level: 0,
+        }
+    }
 }
 
 impl Iterator for StatementSplitter {
     type Item = Result<String, StatementError>;
+
     fn next(&mut self) -> Option<Result<String, StatementError>> {
         let start = self.read;
         let mut first_arg_found = false;
@@ -251,10 +254,12 @@ impl Iterator for StatementSplitter {
                                 self.read = 0;
                                 return match error {
                                     Some(error) => Some(Err(error)),
-                                    None => Some(Ok(String::from(statement[..max(statement.len()-2,0)].trim()))),
-                                }
+                                    None => Some(Ok(String::from(
+                                        statement[..max(statement.len() - 2, 0)].trim(),
+                                    ))),
+                                };
                             }
-                            _ => ()
+                            _ => (),
                         }
                     }
                 }
@@ -272,10 +277,12 @@ impl Iterator for StatementSplitter {
                                 self.read = 0;
                                 return match error {
                                     Some(error) => Some(Err(error)),
-                                    None => Some(Ok(String::from(statement[..max(statement.len()-2,0)].trim()))),
-                                }
+                                    None => Some(Ok(String::from(
+                                        statement[..max(statement.len() - 2, 0)].trim(),
+                                    ))),
+                                };
                             }
-                            _ => ()
+                            _ => (),
                         }
                     }
                 }
@@ -358,9 +365,9 @@ impl Iterator for StatementSplitter {
                         }
                         b'|' => Some(Err(StatementError::ExpectedCommandButFound("pipe"))),
                         b'&' => Some(Err(StatementError::ExpectedCommandButFound("&"))),
-                        b'*' | b'%' | b'?' | b'{' | b'}' => {
-                            Some(Err(StatementError::IllegalCommandName(String::from(output))))
-                        }
+                        b'*' | b'%' | b'?' | b'{' | b'}' => Some(Err(
+                            StatementError::IllegalCommandName(String::from(output)),
+                        )),
                         _ => Some(Ok(String::from(output))),
                     }
                 }
@@ -372,7 +379,8 @@ impl Iterator for StatementSplitter {
 #[test]
 fn syntax_errors() {
     let command = "echo (echo one); echo $( (echo one); echo ) two; echo $(echo one";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results[0], Err(StatementError::InvalidCharacter('(', 6)));
     assert_eq!(results[1], Err(StatementError::InvalidCharacter('(', 26)));
     assert_eq!(results[2], Err(StatementError::InvalidCharacter(')', 43)));
@@ -380,7 +388,8 @@ fn syntax_errors() {
     assert_eq!(results.len(), 4);
 
     let command = ">echo";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(
         results[0],
         Err(StatementError::ExpectedCommandButFound("redirection"))
@@ -429,16 +438,21 @@ fn process_with_statements() {
 #[test]
 fn quotes() {
     let command = "echo \"This ;'is a test\"; echo 'This ;\" is also a test'";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results.len(), 2);
     assert_eq!(results[0], Ok(String::from("echo \"This ;'is a test\"")));
-    assert_eq!(results[1], Ok(String::from("echo 'This ;\" is also a test'")));
+    assert_eq!(
+        results[1],
+        Ok(String::from("echo 'This ;\" is also a test'"))
+    );
 }
 
 #[test]
 fn comments() {
     let command = "echo $(echo one # two); echo three # four";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results.len(), 2);
     assert_eq!(results[0], Ok(String::from("echo $(echo one # two)")));
     assert_eq!(results[1], Ok(String::from("echo three")));
@@ -447,12 +461,14 @@ fn comments() {
 #[test]
 fn nested_process() {
     let command = "echo $(echo one $(echo two) three)";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results.len(), 1);
     assert_eq!(results[0], Ok(String::from(command)));
 
     let command = "echo $(echo $(echo one; echo two); echo two)";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results.len(), 1);
     assert_eq!(results[0], Ok(String::from(command)));
 }
@@ -460,12 +476,14 @@ fn nested_process() {
 #[test]
 fn nested_array_process() {
     let command = "echo @(echo one @(echo two) three)";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results.len(), 1);
     assert_eq!(results[0], Ok(String::from(command)));
 
     let command = "echo @(echo @(echo one; echo two); echo two)";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results.len(), 1);
     assert_eq!(results[0], Ok(String::from(command)));
 }
@@ -473,7 +491,8 @@ fn nested_array_process() {
 #[test]
 fn braced_variables() {
     let command = "echo ${foo}bar ${bar}baz ${baz}quux @{zardoz}wibble";
-    let results = StatementSplitter::new(String::from(command)).collect::<Vec<Result<String, StatementError>>>();
+    let results = StatementSplitter::new(String::from(command))
+        .collect::<Vec<Result<String, StatementError>>>();
     assert_eq!(results.len(), 1);
     assert_eq!(results, vec![Ok(String::from(command))]);
 }
diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs
index a021d78d..faf5707b 100644
--- a/src/lib/shell/assignments.rs
+++ b/src/lib/shell/assignments.rs
@@ -1,16 +1,20 @@
-use super::Shell;
-use super::flow_control::{ExportAction, LocalAction};
-use super::status::*;
+use super::{
+    flow_control::{ExportAction, LocalAction},
+    status::*,
+    Shell,
+};
 use itoa;
 use parser::assignments::*;
 use shell::history::ShellHistory;
-use std::env;
-use std::ffi::OsStr;
-use std::fmt::{self, Display};
-use std::io::{self, BufWriter, Write};
-use std::mem;
-use std::os::unix::ffi::OsStrExt;
-use std::str;
+use std::{
+    env,
+    ffi::OsStr,
+    fmt::{self, Display},
+    io::{self, BufWriter, Write},
+    mem,
+    os::unix::ffi::OsStrExt,
+    str,
+};
 
 fn list_vars(shell: &Shell) {
     let stdout = io::stdout();
@@ -58,27 +62,34 @@ pub(crate) trait VariableStore {
 }
 
 impl VariableStore for Shell {
-    fn local(&mut self, action: LocalAction) -> i32 {
+    fn export(&mut self, action: ExportAction) -> i32 {
         let actions = match action {
-            LocalAction::List => {
-                list_vars(&self);
+            ExportAction::Assign(ref keys, op, ref vals) => AssignmentActions::new(keys, op, vals),
+            ExportAction::LocalExport(ref key) => match self.get_var(key) {
+                Some(var) => {
+                    env::set_var(key, &var);
+                    return SUCCESS;
+                }
+                None => {
+                    eprintln!("ion: cannot export {} because it does not exist.", key);
+                    return FAILURE;
+                }
+            },
+            ExportAction::List => {
+                let stdout = io::stdout();
+                let mut stdout = stdout.lock();
+                for (key, val) in env::vars() {
+                    let _ = writeln!(stdout, "{} =\"{}\"", key, val);
+                }
                 return SUCCESS;
             }
-            LocalAction::Assign(ref keys, op, ref vals) => AssignmentActions::new(keys, op, vals),
         };
+
         for action in actions {
             match action {
                 Ok(Action::UpdateArray(key, Operator::Equal, expression)) => {
                     match value_check(self, &expression, key.kind) {
-                        Ok(ReturnValue::Vector(values)) => {
-                            // When we changed the HISTORY_IGNORE variable, update the
-                            // ignore patterns. This happens first because `set_array`
-                            // consumes 'values'
-                            if key.name == "HISTORY_IGNORE" {
-                                self.update_ignore_patterns(&values);
-                            }
-                            self.variables.set_array(key.name, values)
-                        }
+                        Ok(ReturnValue::Vector(values)) => env::set_var(key.name, values.join(" ")),
                         Err(why) => {
                             eprintln!("ion: assignment error: {}: {}", key.name, why);
                             return FAILURE;
@@ -93,11 +104,6 @@ impl VariableStore for Shell {
                     return FAILURE;
                 }
                 Ok(Action::UpdateString(key, operator, expression)) => {
-                    if ["HOME", "PWD", "MWD", "SWD", "?"].contains(&key.name) {
-                        eprintln!("ion: not allowed to set {}", key.name);
-                        return FAILURE;
-                    }
-
                     match value_check(self, &expression, key.kind) {
                         Ok(ReturnValue::Str(value)) => {
                             let key_name: &str = &key.name;
@@ -105,14 +111,11 @@ impl VariableStore for Shell {
                                 .variables
                                 .get(key_name)
                                 .map(|x| x.as_str())
-                                .unwrap_or("0") as *const str;
+                                .unwrap_or("0");
 
-                            let result =
-                                math(unsafe { &*lhs }, key.kind, operator, &value, |value| {
-                                    self.set_var(key_name, unsafe {
-                                        str::from_utf8_unchecked(value)
-                                    })
-                                });
+                            let result = math(&lhs, key.kind, operator, &value, |value| {
+                                env::set_var(key_name, &OsStr::from_bytes(value))
+                            });
 
                             if let Err(why) = result {
                                 eprintln!("ion: assignment error: {}", why);
@@ -136,34 +139,27 @@ impl VariableStore for Shell {
         SUCCESS
     }
 
-    fn export(&mut self, action: ExportAction) -> i32 {
+    fn local(&mut self, action: LocalAction) -> i32 {
         let actions = match action {
-            ExportAction::Assign(ref keys, op, ref vals) => AssignmentActions::new(keys, op, vals),
-            ExportAction::LocalExport(ref key) => match self.get_var(key) {
-                Some(var) => {
-                    env::set_var(key, &var);
-                    return SUCCESS;
-                }
-                None => {
-                    eprintln!("ion: cannot export {} because it does not exist.", key);
-                    return FAILURE;
-                }
-            },
-            ExportAction::List => {
-                let stdout = io::stdout();
-                let mut stdout = stdout.lock();
-                for (key, val) in env::vars() {
-                    let _ = writeln!(stdout, "{} =\"{}\"", key, val);
-                }
+            LocalAction::List => {
+                list_vars(&self);
                 return SUCCESS;
             }
+            LocalAction::Assign(ref keys, op, ref vals) => AssignmentActions::new(keys, op, vals),
         };
-
         for action in actions {
             match action {
                 Ok(Action::UpdateArray(key, Operator::Equal, expression)) => {
                     match value_check(self, &expression, key.kind) {
-                        Ok(ReturnValue::Vector(values)) => env::set_var(key.name, values.join(" ")),
+                        Ok(ReturnValue::Vector(values)) => {
+                            // When we changed the HISTORY_IGNORE variable, update the
+                            // ignore patterns. This happens first because `set_array`
+                            // consumes 'values'
+                            if key.name == "HISTORY_IGNORE" {
+                                self.update_ignore_patterns(&values);
+                            }
+                            self.variables.set_array(key.name, values)
+                        }
                         Err(why) => {
                             eprintln!("ion: assignment error: {}: {}", key.name, why);
                             return FAILURE;
@@ -178,6 +174,11 @@ impl VariableStore for Shell {
                     return FAILURE;
                 }
                 Ok(Action::UpdateString(key, operator, expression)) => {
+                    if ["HOME", "PWD", "MWD", "SWD", "?"].contains(&key.name) {
+                        eprintln!("ion: not allowed to set {}", key.name);
+                        return FAILURE;
+                    }
+
                     match value_check(self, &expression, key.kind) {
                         Ok(ReturnValue::Str(value)) => {
                             let key_name: &str = &key.name;
@@ -185,11 +186,14 @@ impl VariableStore for Shell {
                                 .variables
                                 .get(key_name)
                                 .map(|x| x.as_str())
-                                .unwrap_or("0");
+                                .unwrap_or("0") as *const str;
 
-                            let result = math(&lhs, key.kind, operator, &value, |value| {
-                                env::set_var(key_name, &OsStr::from_bytes(value))
-                            });
+                            let result =
+                                math(unsafe { &*lhs }, key.kind, operator, &value, |value| {
+                                    self.set_var(key_name, unsafe {
+                                        str::from_utf8_unchecked(value)
+                                    })
+                                });
 
                             if let Err(why) = result {
                                 eprintln!("ion: assignment error: {}", why);
diff --git a/src/lib/shell/binary/designators.rs b/src/lib/shell/binary/designators.rs
index 7ef640d6..fddf0cad 100644
--- a/src/lib/shell/binary/designators.rs
+++ b/src/lib/shell/binary/designators.rs
@@ -1,7 +1,6 @@
 use parser::ArgumentSplitter;
 use shell::Shell;
-use std::borrow::Cow;
-use std::str;
+use std::{borrow::Cow, str};
 
 bitflags! {
     struct Flags: u8 {
@@ -23,18 +22,18 @@ struct DesignatorSearcher<'a> {
 }
 
 impl<'a> DesignatorSearcher<'a> {
+    fn grab_and_shorten(&mut self, id: usize) -> &'a str {
+        let output = unsafe { str::from_utf8_unchecked(&self.data[..id]) };
+        self.data = &self.data[id..];
+        output
+    }
+
     fn new(data: &'a [u8]) -> DesignatorSearcher {
         DesignatorSearcher {
             data,
             flags: Flags::empty(),
         }
     }
-
-    fn grab_and_shorten(&mut self, id: usize) -> &'a str {
-        let output = unsafe { str::from_utf8_unchecked(&self.data[..id]) };
-        self.data = &self.data[id..];
-        output
-    }
 }
 
 impl<'a> Iterator for DesignatorSearcher<'a> {
diff --git a/src/lib/shell/binary/mod.rs b/src/lib/shell/binary/mod.rs
index e7c5eaf5..785bc2b1 100644
--- a/src/lib/shell/binary/mod.rs
+++ b/src/lib/shell/binary/mod.rs
@@ -4,19 +4,14 @@ mod prompt;
 mod readln;
 mod terminate;
 
-use self::prompt::{prompt, prompt_fn};
-use self::readln::readln;
-use self::terminate::{terminate_quotes, terminate_script_quotes};
-use super::{FlowLogic, Shell, ShellHistory};
-use super::flow_control::Statement;
-use super::status::*;
+use self::{
+    prompt::{prompt, prompt_fn},
+    readln::readln,
+    terminate::{terminate_quotes, terminate_script_quotes},
+};
+use super::{flow_control::Statement, status::*, FlowLogic, Shell, ShellHistory};
 use liner::{Buffer, Context};
-use std::env;
-use std::fs::File;
-use std::io::ErrorKind;
-use std::iter;
-use std::path::Path;
-use std::process;
+use std::{env, fs::File, io::ErrorKind, iter, path::Path, process};
 
 pub const MAN_ION: &'static str = r#"NAME
     ion - ion shell
@@ -65,45 +60,38 @@ pub trait Binary {
 }
 
 impl Binary for Shell {
-    fn prompt(&mut self) -> String { prompt(self) }
-
-    fn prompt_fn(&mut self) -> Option<String> { prompt_fn(self) }
-
-    fn readln(&mut self) -> Option<String> { readln(self) }
-
-    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32 {
-        terminate_script_quotes(self, lines)
-    }
-
-    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
-        terminate_quotes(self, command)
+    fn display_version(&self) {
+        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
+        process::exit(0);
     }
 
-    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
-        if let Some(mut arg) = args.next() {
-            for argument in args {
-                arg.push(' ');
-                if argument == "" {
-                    arg.push_str("''");
-                } else {
-                    arg.push_str(&argument);
-                }
+    fn save_command(&mut self, cmd: &str) {
+        if cmd.starts_with('~') {
+            if !cmd.ends_with('/')
+                && self.variables
+                    .tilde_expansion(cmd, &self.directory_stack)
+                    .map_or(false, |ref path| Path::new(path).is_dir())
+            {
+                self.save_command_in_history(&[cmd, "/"].concat());
+            } else {
+                self.save_command_in_history(cmd);
             }
-            self.on_command(&arg);
-        } else {
-            eprintln!("ion: -c requires an argument");
-            self.exit(FAILURE);
+            return;
         }
 
-        if self.flow_control.level != 0 {
-            eprintln!(
-                "ion: unexpected end of arguments: expected end block for `{}`",
-                self.flow_control.current_statement.short()
-            );
-            self.exit(FAILURE);
+        if Path::new(cmd).is_dir() & !cmd.ends_with('/') {
+            self.save_command_in_history(&[cmd, "/"].concat());
+        } else {
+            self.save_command_in_history(cmd);
         }
     }
 
+    fn reset_flow(&mut self) {
+        self.flow_control.level = 0;
+        self.flow_control.current_if_mode = 0;
+        self.flow_control.current_statement = Statement::Default;
+    }
+
     fn execute_interactive(mut self) {
         self.context = Some({
             let mut context = Context::new();
@@ -158,37 +146,44 @@ impl Binary for Shell {
         }
     }
 
-    fn reset_flow(&mut self) {
-        self.flow_control.level = 0;
-        self.flow_control.current_if_mode = 0;
-        self.flow_control.current_statement = Statement::Default;
-    }
-
-    fn save_command(&mut self, cmd: &str) {
-        if cmd.starts_with('~') {
-            if !cmd.ends_with('/')
-                && self.variables
-                    .tilde_expansion(cmd, &self.directory_stack)
-                    .map_or(false, |ref path| Path::new(path).is_dir())
-            {
-                self.save_command_in_history(&[cmd, "/"].concat());
-            } else {
-                self.save_command_in_history(cmd);
+    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
+        if let Some(mut arg) = args.next() {
+            for argument in args {
+                arg.push(' ');
+                if argument == "" {
+                    arg.push_str("''");
+                } else {
+                    arg.push_str(&argument);
+                }
             }
-            return;
+            self.on_command(&arg);
+        } else {
+            eprintln!("ion: -c requires an argument");
+            self.exit(FAILURE);
         }
 
-        if Path::new(cmd).is_dir() & !cmd.ends_with('/') {
-            self.save_command_in_history(&[cmd, "/"].concat());
-        } else {
-            self.save_command_in_history(cmd);
+        if self.flow_control.level != 0 {
+            eprintln!(
+                "ion: unexpected end of arguments: expected end block for `{}`",
+                self.flow_control.current_statement.short()
+            );
+            self.exit(FAILURE);
         }
     }
 
-    fn display_version(&self) {
-        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
-        process::exit(0);
+    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
+        terminate_quotes(self, command)
+    }
+
+    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32 {
+        terminate_script_quotes(self, lines)
     }
+
+    fn readln(&mut self) -> Option<String> { readln(self) }
+
+    fn prompt_fn(&mut self) -> Option<String> { prompt_fn(self) }
+
+    fn prompt(&mut self) -> String { prompt(self) }
 }
 
 // TODO: Convert this into an iterator to eliminate heap allocations.
@@ -208,7 +203,7 @@ fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
                     word_start = Some($index);
                 }
             }
-        }}
+        }};
     }
 
     let mut iter = buf.chars().enumerate();
diff --git a/src/lib/shell/binary/prompt.rs b/src/lib/shell/binary/prompt.rs
index 65a36841..3f7288e7 100644
--- a/src/lib/shell/binary/prompt.rs
+++ b/src/lib/shell/binary/prompt.rs
@@ -1,7 +1,6 @@
 use super::super::{Capture, Function, Shell};
 use parser::shell_expand::expand_string;
-use std::io::Read;
-use std::process;
+use std::{io::Read, process};
 use sys;
 
 pub(crate) fn prompt(shell: &mut Shell) -> String {
diff --git a/src/lib/shell/binary/readln.rs b/src/lib/shell/binary/readln.rs
index 8f6f2899..9ac6f078 100644
--- a/src/lib/shell/binary/readln.rs
+++ b/src/lib/shell/binary/readln.rs
@@ -1,11 +1,12 @@
-use super::super::{Binary, DirectoryStack, Shell, Variables};
-use super::super::completer::*;
+use super::super::{completer::*, Binary, DirectoryStack, Shell, Variables};
 use liner::{BasicCompleter, CursorPosition, Event, EventKind};
 use smallstring::SmallString;
-use std::env;
-use std::io::{self, ErrorKind, Write};
-use std::mem;
-use std::path::PathBuf;
+use std::{
+    env,
+    io::{self, ErrorKind, Write},
+    mem,
+    path::PathBuf,
+};
 use sys;
 use types::*;
 
diff --git a/src/lib/shell/binary/terminate.rs b/src/lib/shell/binary/terminate.rs
index 3ef4658c..40916288 100644
--- a/src/lib/shell/binary/terminate.rs
+++ b/src/lib/shell/binary/terminate.rs
@@ -1,5 +1,4 @@
-use super::super::{Binary, FlowLogic, Shell};
-use super::super::status::*;
+use super::super::{status::*, Binary, FlowLogic, Shell};
 use parser::Terminator;
 
 pub(crate) fn terminate_script_quotes<I: Iterator<Item = String>>(
diff --git a/src/lib/shell/colors.rs b/src/lib/shell/colors.rs
index 91e790cf..88a542a9 100644
--- a/src/lib/shell/colors.rs
+++ b/src/lib/shell/colors.rs
@@ -87,48 +87,43 @@ pub(crate) struct Colors {
 }
 
 impl Colors {
-    /// Parses the given input and returns a structure obtaining the text data needed for proper
-    /// transformation into ANSI code parameters, which may be obtained by calling the
-    /// `into_string()` method on the newly-created `Colors` structure.
-    pub(crate) fn collect(input: &str) -> Colors {
-        let mut colors = Colors {
-            foreground: None,
-            background: None,
-            attributes: None,
-        };
-        for variable in input.split(",") {
-            if variable == "reset" {
-                return Colors {
-                    foreground: None,
-                    background: None,
-                    attributes: Some(vec!["0"]),
-                };
-            } else if let Some(attribute) = ATTRIBUTES.get(&variable) {
-                colors.append_attribute(attribute);
-            } else if let Some(color) = COLORS.get(&variable) {
-                colors.foreground = Some(Mode::Name(color));
-            } else if let Some(color) = BG_COLORS.get(&variable) {
-                colors.background = Some(Mode::Name(color));
-            } else if !colors.parse_colors(variable) {
-                eprintln!("ion: {} is not a valid color", variable)
+    /// Attempts to transform the data in the structure into the corresponding ANSI code
+    /// representation. It would very ugly to require shell scripters to have to interface
+    /// with these codes directly.
+    pub(crate) fn into_string(self) -> Option<String> {
+        let mut output = String::from("\x1b[");
+
+        let foreground = match self.foreground {
+            Some(Mode::Name(string)) => Some(string.to_owned()),
+            Some(Mode::Range256(value)) => Some(format!("38;5;{}", value)),
+            Some(Mode::TrueColor(red, green, blue)) => {
+                Some(format!("38;2;{};{};{}", red, green, blue))
             }
-        }
-        colors
-    }
+            None => None,
+        };
 
-    /// Attributes can be stacked, so this function serves to enable that
-    /// stacking.
-    fn append_attribute(&mut self, attribute: &'static str) {
-        let vec_exists = match self.attributes.as_mut() {
-            Some(vec) => {
-                vec.push(attribute);
-                true
+        let background = match self.background {
+            Some(Mode::Name(string)) => Some(string.to_owned()),
+            Some(Mode::Range256(value)) => Some(format!("48;5;{}", value)),
+            Some(Mode::TrueColor(red, green, blue)) => {
+                Some(format!("48;2;{};{};{}", red, green, blue))
             }
-            None => false,
+            None => None,
         };
 
-        if !vec_exists {
-            self.attributes = Some(vec![attribute]);
+        if let Some(attr) = self.attributes {
+            output.push_str(&attr.join(";"));
+            match (foreground, background) {
+                (Some(c), None) | (None, Some(c)) => Some([&output, ";", &c, "m"].concat()),
+                (None, None) => Some([&output, "m"].concat()),
+                (Some(fg), Some(bg)) => Some([&output, ";", &fg, ";", &bg, "m"].concat()),
+            }
+        } else {
+            match (foreground, background) {
+                (Some(c), None) | (None, Some(c)) => Some([&output, &c, "m"].concat()),
+                (None, None) => None,
+                (Some(fg), Some(bg)) => Some([&output, &fg, ";", &bg, "m"].concat()),
+            }
         }
     }
 
@@ -186,44 +181,49 @@ impl Colors {
         false
     }
 
-    /// Attempts to transform the data in the structure into the corresponding ANSI code
-    /// representation. It would very ugly to require shell scripters to have to interface
-    /// with these codes directly.
-    pub(crate) fn into_string(self) -> Option<String> {
-        let mut output = String::from("\x1b[");
-
-        let foreground = match self.foreground {
-            Some(Mode::Name(string)) => Some(string.to_owned()),
-            Some(Mode::Range256(value)) => Some(format!("38;5;{}", value)),
-            Some(Mode::TrueColor(red, green, blue)) => {
-                Some(format!("38;2;{};{};{}", red, green, blue))
+    /// Attributes can be stacked, so this function serves to enable that
+    /// stacking.
+    fn append_attribute(&mut self, attribute: &'static str) {
+        let vec_exists = match self.attributes.as_mut() {
+            Some(vec) => {
+                vec.push(attribute);
+                true
             }
-            None => None,
+            None => false,
         };
 
-        let background = match self.background {
-            Some(Mode::Name(string)) => Some(string.to_owned()),
-            Some(Mode::Range256(value)) => Some(format!("48;5;{}", value)),
-            Some(Mode::TrueColor(red, green, blue)) => {
-                Some(format!("48;2;{};{};{}", red, green, blue))
-            }
-            None => None,
-        };
+        if !vec_exists {
+            self.attributes = Some(vec![attribute]);
+        }
+    }
 
-        if let Some(attr) = self.attributes {
-            output.push_str(&attr.join(";"));
-            match (foreground, background) {
-                (Some(c), None) | (None, Some(c)) => Some([&output, ";", &c, "m"].concat()),
-                (None, None) => Some([&output, "m"].concat()),
-                (Some(fg), Some(bg)) => Some([&output, ";", &fg, ";", &bg, "m"].concat()),
-            }
-        } else {
-            match (foreground, background) {
-                (Some(c), None) | (None, Some(c)) => Some([&output, &c, "m"].concat()),
-                (None, None) => None,
-                (Some(fg), Some(bg)) => Some([&output, &fg, ";", &bg, "m"].concat()),
+    /// Parses the given input and returns a structure obtaining the text data needed for proper
+    /// transformation into ANSI code parameters, which may be obtained by calling the
+    /// `into_string()` method on the newly-created `Colors` structure.
+    pub(crate) fn collect(input: &str) -> Colors {
+        let mut colors = Colors {
+            foreground: None,
+            background: None,
+            attributes: None,
+        };
+        for variable in input.split(",") {
+            if variable == "reset" {
+                return Colors {
+                    foreground: None,
+                    background: None,
+                    attributes: Some(vec!["0"]),
+                };
+            } else if let Some(attribute) = ATTRIBUTES.get(&variable) {
+                colors.append_attribute(attribute);
+            } else if let Some(color) = COLORS.get(&variable) {
+                colors.foreground = Some(Mode::Name(color));
+            } else if let Some(color) = BG_COLORS.get(&variable) {
+                colors.background = Some(Mode::Name(color));
+            } else if !colors.parse_colors(variable) {
+                eprintln!("ion: {} is not a valid color", variable)
             }
         }
+        colors
     }
 }
 
diff --git a/src/lib/shell/completer.rs b/src/lib/shell/completer.rs
index 8df962e3..5b0ec081 100644
--- a/src/lib/shell/completer.rs
+++ b/src/lib/shell/completer.rs
@@ -1,5 +1,4 @@
-use super::directory_stack::DirectoryStack;
-use super::variables::Variables;
+use super::{directory_stack::DirectoryStack, variables::Variables};
 use liner::{Completer, FilenameCompleter};
 
 /// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
@@ -22,8 +21,8 @@ impl IonFileCompleter {
     ) -> IonFileCompleter {
         IonFileCompleter {
             inner:     FilenameCompleter::new(path),
-            dir_stack: dir_stack,
-            vars:      vars,
+            dir_stack,
+            vars,
         }
     }
 }
@@ -45,8 +44,7 @@ impl Completer for IonFileCompleter {
             // because no changes will occur to either of the underlying references in the
             // duration between creation of the completers and execution of their
             // completions.
-            if let Some(expanded) =
-                unsafe { (*self.vars).tilde_expansion(start, &*self.dir_stack) }
+            if let Some(expanded) = unsafe { (*self.vars).tilde_expansion(start, &*self.dir_stack) }
             {
                 // Now we obtain completions for the `expanded` form of the `start` value.
                 let completions = self.inner.completions(&expanded);
@@ -151,7 +149,7 @@ where
     A: Completer,
     B: Completer,
 {
-    pub(crate) fn new(a: Vec<A>, b: B) -> MultiCompleter<A, B> { MultiCompleter { a: a, b: b } }
+    pub(crate) fn new(a: Vec<A>, b: B) -> MultiCompleter<A, B> { MultiCompleter { a, b } }
 }
 
 impl<A, B> Completer for MultiCompleter<A, B>
diff --git a/src/lib/shell/directory_stack.rs b/src/lib/shell/directory_stack.rs
index 9628de29..2eea62f3 100644
--- a/src/lib/shell/directory_stack.rs
+++ b/src/lib/shell/directory_stack.rs
@@ -1,175 +1,243 @@
-use std::borrow::Cow;
-use std::collections::VecDeque;
-use std::env::{current_dir, home_dir, set_current_dir};
-use std::path::PathBuf;
-use super::status::{FAILURE, SUCCESS};
-use super::variables::Variables;
+use super::{
+    status::{FAILURE, SUCCESS},
+    variables::Variables,
+};
+use std::{
+    borrow::Cow,
+    collections::VecDeque,
+    env::{current_dir, home_dir, set_current_dir},
+    path::PathBuf,
+};
 
 pub struct DirectoryStack {
     dirs: VecDeque<PathBuf>, // The top is always the current directory
 }
 
 impl DirectoryStack {
-    /// Create a new `DirectoryStack` containing the current working directory,
-    /// if available.
-    pub(crate) fn new() -> DirectoryStack {
-        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
-        match current_dir() {
-            Ok(curr_dir) => {
-                dirs.push_front(curr_dir);
-                DirectoryStack { dirs: dirs }
-            }
-            Err(_) => {
-                eprintln!("ion: failed to get current directory when building directory stack");
-                DirectoryStack { dirs: dirs }
-            }
+    fn normalize_path(&mut self, dir: &str) -> PathBuf {
+        use std::path::{Component, Path};
+        // Create a clone of the current directory.
+        let mut new_dir = match self.dirs.front() {
+            Some(cur_dir) => cur_dir.clone(),
+            None => PathBuf::new(),
+        };
+
+        // Iterate through components of the specified directory
+        // and calculate the new path based on them.
+        for component in Path::new(dir).components() {
+            match component {
+                Component::CurDir => {}
+                Component::ParentDir => {
+                    new_dir.pop();
+                }
+                _ => {
+                    new_dir.push(component);
+                }
+            };
         }
+
+        return new_dir;
     }
 
-    /// This function will take a map of variables as input and attempt to parse the value of
-    /// the
-    /// directory stack size variable. If it succeeds, it will return the value of that
-    /// variable,
-    /// else it will return a default value of 1000.
-    fn get_size(variables: &Variables) -> usize {
-        variables
-            .get_var_or_empty("DIRECTORY_STACK_SIZE")
-            .parse::<usize>()
-            .unwrap_or(1000)
+    // pushd -<num>
+    fn rotate_right(&mut self, num: usize) {
+        let len = self.dirs.len();
+        self.rotate_left(len - (num % len));
     }
 
-    /// Attempts to set the current directory to the directory stack's previous directory,
-    /// and then removes the front directory from the stack.
-    pub(crate) fn popd<I: IntoIterator>(&mut self, args: I, variables: &mut Variables) -> Result<(), Cow<'static, str>>
+    // pushd +<num>
+    fn rotate_left(&mut self, num: usize) {
+        let cloned = self.dirs.clone();
+        for (dest, src) in self.dirs.iter_mut().zip(cloned.iter().cycle().skip(num)) {
+            *dest = src.clone();
+        }
+    }
+
+    // sets current_dir to the element referred by index
+    fn set_current_dir_by_index(
+        &self,
+        index: usize,
+        caller: &str,
+    ) -> Result<(), Cow<'static, str>> {
+        let dir = self.dirs.iter().nth(index).ok_or_else(|| {
+            Cow::Owned(format!(
+                "ion: {}: {}: directory stack out of range\n",
+                caller, index
+            ))
+        })?;
+
+        set_current_dir(dir)
+            .map_err(|_| Cow::Owned(format!("ion: {}: Failed setting current dir\n", caller)))
+    }
+
+    fn print_dirs(&self) {
+        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
+            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
+        });
+        println!("{}", dir.trim_left());
+    }
+
+    pub(crate) fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
+        self.dirs.iter().rev().nth(num)
+    }
+
+    pub(crate) fn dir_from_top(&self, num: usize) -> Option<&PathBuf> { self.dirs.get(num) }
+
+    pub(crate) fn dirs<I: IntoIterator>(&mut self, args: I) -> i32
     where
         I::Item: AsRef<str>,
     {
-        let mut keep_front = false; // whether the -n option is present
-        let mut count_from_front = true; // <=> input number is positive
-        let mut num: usize = 0;
+        const CLEAR: u8 = 1; // -c
+        const ABS_PATHNAMES: u8 = 2; // -l
+        const MULTILINE: u8 = 4; // -p | -v
+        const INDEX: u8 = 8; // -v
+
+        let mut dirs_args: u8 = 0;
+        let mut num_arg: Option<usize> = None;
 
         for arg in args.into_iter().skip(1) {
             let arg = arg.as_ref();
-            if arg == "-n" {
-                keep_front = true;
-            } else {
-                match parse_numeric_arg(arg) {
-                    Some((x, y)) => {
-                        count_from_front = x;
-                        num = y;
-                    }
-                    None => {
-                        return Err(Cow::Owned(format!(
-                            "ion: popd: {}: invalid argument\n",
-                            arg
-                        )))
-                    }
-                };
+            match arg {
+                "-c" => dirs_args |= CLEAR,
+                "-l" => dirs_args |= ABS_PATHNAMES,
+                "-p" => dirs_args |= MULTILINE,
+                "-v" => dirs_args |= INDEX | MULTILINE,
+                arg => {
+                    num_arg = match parse_numeric_arg(arg) {
+                        Some((true, num)) => Some(num),
+                        Some((false, num)) if self.dirs.len() > num => {
+                            Some(self.dirs.len() - num - 1)
+                        }
+                        _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid
+                                              * argument\n", arg))) */
+                    };
+                }
             }
         }
 
-        let len: usize = self.dirs.len();
-        if len <= 1 {
-            return Err(Cow::Borrowed("ion: popd: directory stack empty\n"));
+        if dirs_args & CLEAR > 0 {
+            self.dirs.truncate(1);
         }
 
-        let mut index: usize = if count_from_front {
-            num
-        } else {
-            (len - 1).checked_sub(num).ok_or_else(|| {
-                Cow::Owned(format!(
-                    "ion: popd: negative directory stack index out of range\n"
-                ))
-            })?
+        let mapper: fn((usize, &PathBuf)) -> Cow<str> = match (
+            dirs_args & ABS_PATHNAMES > 0,
+            dirs_args & INDEX > 0,
+        ) {
+            // ABS, INDEX
+            (true, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, try_abs_path(x))),
+            (true, false) => |(_, x)| try_abs_path(x),
+            (false, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, x.to_string_lossy())),
+            (false, false) => |(_, x)| x.to_string_lossy(),
         };
 
-        // apply -n
-        if index == 0 && keep_front {
-            index = 1;
-        }
+        let mut iter = self.dirs.iter().enumerate().map(mapper);
 
-        // change to new directory, return if not possible
-        if index == 0 {
-            self.set_current_dir_by_index(1, "popd")?;
-        }
+        if let Some(num) = num_arg {
+            match iter.nth(num) {
+                Some(x) => println!("{}", x),
+                None => return FAILURE,
+            };
+        } else {
+            let folder: fn(String, Cow<str>) -> String = match dirs_args & MULTILINE > 0 {
+                true => |x, y| x + "\n" + &y,
+                false => |x, y| x + " " + &y,
+            };
 
-        // pop element
-        if self.dirs.remove(index).is_none() {
-            return Err(Cow::Owned(format!(
-                "ion: popd: {}: directory stack index out of range\n",
-                index
-            )));
+            let first = match iter.next() {
+                Some(x) => x.to_string(),
+                None => return SUCCESS,
+            };
+
+            println!("{}", iter.fold(first, folder));
         }
+        SUCCESS
+    }
 
-        self.update_env_variables(variables);
-        self.print_dirs();
-        Ok(())
+    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) {
+        self.dirs.insert(index, path);
+        self.dirs.truncate(DirectoryStack::get_size(variables));
     }
 
-    pub(crate) fn pushd<I: IntoIterator>(
+    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
+        self.dirs.push_front(path);
+
+        self.dirs.truncate(DirectoryStack::get_size(variables));
+    }
+
+    pub(crate) fn change_and_push_dir(
         &mut self,
-        args: I,
-        variables: &mut Variables,
-    ) -> Result<(), Cow<'static, str>>
-    where
-        I::Item: AsRef<str>,
-    {
-        enum Action {
-            Switch,          // <no arguments>
-            RotLeft(usize),  // +[num]
-            RotRight(usize), // -[num]
-            Push(PathBuf),   // [dir]
+        dir: &str,
+        variables: &Variables,
+    ) -> Result<(), Cow<'static, str>> {
+        let new_dir = self.normalize_path(dir);
+
+        // Try to change into the new directory
+        match set_current_dir(&new_dir) {
+            Ok(()) => {
+                // Push the new current directory onto the directory stack.
+                self.push_dir(new_dir, variables);
+                Ok(())
+            }
+            Err(err) => Err(Cow::Owned(format!(
+                "ion: failed to set current dir to {}: {}\n",
+                new_dir.to_string_lossy(),
+                err
+            ))),
         }
+    }
 
-        let mut keep_front = false; // whether the -n option is present
-        let mut action: Action = Action::Switch;
+    fn get_previous_dir(&self, variables: &Variables) -> Option<String> {
+        let previous_pwd = variables.get_var_or_empty("OLDPWD");
+        if previous_pwd == "?" || previous_pwd == "" {
+            None
+        } else {
+            Some(previous_pwd)
+        }
+    }
 
-        for arg in args.into_iter().skip(1) {
-            let arg = arg.as_ref();
-            if arg == "-n" {
-                keep_front = true;
-            } else if let Action::Switch = action {
-                // if action is not yet defined
-                action = match parse_numeric_arg(arg) {
-                    Some((true, num)) => Action::RotLeft(num),
-                    Some((false, num)) => Action::RotRight(num),
-                    None => Action::Push(PathBuf::from(arg)), // no numeric arg => `dir`-parameter
-                };
-            } else {
-                return Err(Cow::Borrowed("ion: pushd: too many arguments\n"));
+    fn switch_to_previous_directory(
+        &mut self,
+        variables: &Variables,
+    ) -> Result<(), Cow<'static, str>> {
+        match self.get_previous_dir(variables) {
+            Some(prev) => {
+                self.dirs.remove(0);
+                let prev = prev.to_string();
+                println!("{}", prev);
+                self.change_and_push_dir(&prev, variables)
             }
+            None => Err(Cow::Borrowed("ion: no previous directory to switch to")),
         }
+    }
 
-        let len = self.dirs.len();
-        match action {
-            Action::Switch => {
-                if len < 2 {
-                    return Err(Cow::Borrowed("ion: pushd: no other directory\n"));
-                }
-                if !keep_front {
-                    self.set_current_dir_by_index(1, "pushd")?;
-                    self.dirs.swap(0, 1);
-                }
-            }
-            Action::RotLeft(num) => if !keep_front {
-                self.set_current_dir_by_index(num, "pushd")?;
-                self.rotate_left(num);
-            },
-            Action::RotRight(num) => if !keep_front {
-                self.set_current_dir_by_index(len - (num % len), "pushd")?;
-                self.rotate_right(num);
+    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
+        home_dir().map_or(
+            Err(Cow::Borrowed("ion: failed to get home directory")),
+            |home| {
+                home.to_str().map_or(
+                    Err(Cow::Borrowed(
+                        "ion: failed to convert home directory to str",
+                    )),
+                    |home| self.change_and_push_dir(home, variables),
+                )
             },
-            Action::Push(dir) => {
-                let index = if keep_front { 1 } else { 0 };
-                let new_dir = self.normalize_path(dir.to_str().unwrap());
-                self.insert_dir(index, new_dir, variables);
-                self.set_current_dir_by_index(index, "pushd")?;
-            }
-        };
+        )
+    }
 
-        self.update_env_variables(variables);
-        self.print_dirs();
-        Ok(())
+    fn update_env_variables(&mut self, variables: &mut Variables) {
+        // Update $OLDPWD
+        let old_pwd = variables.get_var_or_empty("PWD");
+        if old_pwd.is_empty() {
+            variables.set_var("OLDPWD", "?");
+        } else {
+            variables.set_var("OLDPWD", &old_pwd);
+        }
+
+        // Update $PWD
+        match current_dir() {
+            Ok(current_dir) => variables.set_var("PWD", current_dir.to_str().unwrap_or("?")),
+            Err(_) => variables.set_var("PWD", "?"),
+        }
     }
 
     pub(crate) fn cd<I: IntoIterator>(
@@ -195,226 +263,170 @@ impl DirectoryStack {
         }
     }
 
-    fn update_env_variables(&mut self, variables: &mut Variables) {
-        // Update $OLDPWD
-        let old_pwd = variables.get_var_or_empty("PWD");
-        if old_pwd.is_empty() {
-            variables.set_var("OLDPWD", "?");
-        } else {
-            variables.set_var("OLDPWD", &old_pwd);
-        }
-
-        // Update $PWD
-        match current_dir() {
-            Ok(current_dir) => variables.set_var("PWD", current_dir.to_str().unwrap_or("?")),
-            Err(_) => variables.set_var("PWD", "?"),
-        }
-    }
-
-    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
-        home_dir().map_or(
-            Err(Cow::Borrowed("ion: failed to get home directory")),
-            |home| {
-                home.to_str().map_or(
-                    Err(Cow::Borrowed(
-                        "ion: failed to convert home directory to str",
-                    )),
-                    |home| self.change_and_push_dir(home, variables),
-                )
-            },
-        )
-    }
-
-    fn switch_to_previous_directory(
+    pub(crate) fn pushd<I: IntoIterator>(
         &mut self,
-        variables: &Variables,
-    ) -> Result<(), Cow<'static, str>> {
-        match self.get_previous_dir(variables) {
-            Some(prev) => {
-                self.dirs.remove(0);
-                let prev = prev.to_string();
-                println!("{}", prev);
-                self.change_and_push_dir(&prev, variables)
-            },
-            None => Err(Cow::Borrowed("ion: no previous directory to switch to")),
-        }
-    }
-
-    fn get_previous_dir(&self, variables: &Variables) -> Option<String> {
-        let previous_pwd = variables.get_var_or_empty("OLDPWD");
-        if previous_pwd == "?" || previous_pwd == "" {
-            None
-        } else {
-            Some(previous_pwd)
+        args: I,
+        variables: &mut Variables,
+    ) -> Result<(), Cow<'static, str>>
+    where
+        I::Item: AsRef<str>,
+    {
+        enum Action {
+            Switch,          // <no arguments>
+            RotLeft(usize),  // +[num]
+            RotRight(usize), // -[num]
+            Push(PathBuf),   // [dir]
         }
-    }
 
-    pub(crate) fn change_and_push_dir(
-        &mut self,
-        dir: &str,
-        variables: &Variables,
-    ) -> Result<(), Cow<'static, str>> {
-
-        let new_dir = self.normalize_path(dir);
+        let mut keep_front = false; // whether the -n option is present
+        let mut action: Action = Action::Switch;
 
-        // Try to change into the new directory
-        match set_current_dir(&new_dir) {
-            Ok(()) => {
-                // Push the new current directory onto the directory stack.
-                self.push_dir(new_dir, variables);
-                Ok(())
+        for arg in args.into_iter().skip(1) {
+            let arg = arg.as_ref();
+            if arg == "-n" {
+                keep_front = true;
+            } else if let Action::Switch = action {
+                // if action is not yet defined
+                action = match parse_numeric_arg(arg) {
+                    Some((true, num)) => Action::RotLeft(num),
+                    Some((false, num)) => Action::RotRight(num),
+                    None => Action::Push(PathBuf::from(arg)), // no numeric arg => `dir`-parameter
+                };
+            } else {
+                return Err(Cow::Borrowed("ion: pushd: too many arguments\n"));
             }
-            Err(err) => Err(Cow::Owned(format!(
-                "ion: failed to set current dir to {}: {}\n",
-                new_dir.to_string_lossy(), err
-            )))
         }
-    }
-
-    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
-        self.dirs.push_front(path);
 
-        self.dirs.truncate(DirectoryStack::get_size(variables));
-    }
+        let len = self.dirs.len();
+        match action {
+            Action::Switch => {
+                if len < 2 {
+                    return Err(Cow::Borrowed("ion: pushd: no other directory\n"));
+                }
+                if !keep_front {
+                    self.set_current_dir_by_index(1, "pushd")?;
+                    self.dirs.swap(0, 1);
+                }
+            }
+            Action::RotLeft(num) => if !keep_front {
+                self.set_current_dir_by_index(num, "pushd")?;
+                self.rotate_left(num);
+            },
+            Action::RotRight(num) => if !keep_front {
+                self.set_current_dir_by_index(len - (num % len), "pushd")?;
+                self.rotate_right(num);
+            },
+            Action::Push(dir) => {
+                let index = if keep_front { 1 } else { 0 };
+                let new_dir = self.normalize_path(dir.to_str().unwrap());
+                self.insert_dir(index, new_dir, variables);
+                self.set_current_dir_by_index(index, "pushd")?;
+            }
+        };
 
-    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) {
-        self.dirs.insert(index, path);
-        self.dirs.truncate(DirectoryStack::get_size(variables));
+        self.update_env_variables(variables);
+        self.print_dirs();
+        Ok(())
     }
 
-    pub(crate) fn dirs<I: IntoIterator>(&mut self, args: I) -> i32
+    /// Attempts to set the current directory to the directory stack's previous directory,
+    /// and then removes the front directory from the stack.
+    pub(crate) fn popd<I: IntoIterator>(
+        &mut self,
+        args: I,
+        variables: &mut Variables,
+    ) -> Result<(), Cow<'static, str>>
     where
         I::Item: AsRef<str>,
     {
-        const CLEAR: u8 = 1; // -c
-        const ABS_PATHNAMES: u8 = 2; // -l
-        const MULTILINE: u8 = 4; // -p | -v
-        const INDEX: u8 = 8; // -v
-
-        let mut dirs_args: u8 = 0;
-        let mut num_arg: Option<usize> = None;
+        let mut keep_front = false; // whether the -n option is present
+        let mut count_from_front = true; // <=> input number is positive
+        let mut num: usize = 0;
 
         for arg in args.into_iter().skip(1) {
             let arg = arg.as_ref();
-            match arg {
-                "-c" => dirs_args |= CLEAR,
-                "-l" => dirs_args |= ABS_PATHNAMES,
-                "-p" => dirs_args |= MULTILINE,
-                "-v" => dirs_args |= INDEX | MULTILINE,
-                arg => {
-                    num_arg = match parse_numeric_arg(arg) {
-                        Some((true, num)) => Some(num),
-                        Some((false, num)) if self.dirs.len() > num => {
-                            Some(self.dirs.len() - num - 1)
-                        }
-                        _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid
-                                              * argument\n", arg))) */
-                    };
-                }
+            if arg == "-n" {
+                keep_front = true;
+            } else {
+                match parse_numeric_arg(arg) {
+                    Some((x, y)) => {
+                        count_from_front = x;
+                        num = y;
+                    }
+                    None => {
+                        return Err(Cow::Owned(format!(
+                            "ion: popd: {}: invalid argument\n",
+                            arg
+                        )))
+                    }
+                };
             }
         }
 
-        if dirs_args & CLEAR > 0 {
-            self.dirs.truncate(1);
+        let len: usize = self.dirs.len();
+        if len <= 1 {
+            return Err(Cow::Borrowed("ion: popd: directory stack empty\n"));
         }
 
-        let mapper: fn((usize, &PathBuf)) -> Cow<str> = match (
-            dirs_args & ABS_PATHNAMES > 0,
-            dirs_args & INDEX > 0,
-        ) {
-            // ABS, INDEX
-            (true, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, try_abs_path(x))),
-            (true, false) => |(_, x)| try_abs_path(x),
-            (false, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, x.to_string_lossy())),
-            (false, false) => |(_, x)| x.to_string_lossy(),
-        };
-
-        let mut iter = self.dirs.iter().enumerate().map(mapper);
-
-        if let Some(num) = num_arg {
-            match iter.nth(num) {
-                Some(x) => println!("{}", x),
-                None => return FAILURE,
-            };
+        let mut index: usize = if count_from_front {
+            num
         } else {
-            let folder: fn(String, Cow<str>) -> String = match dirs_args & MULTILINE > 0 {
-                true => |x, y| x + "\n" + &y,
-                false => |x, y| x + " " + &y,
-            };
-
-            let first = match iter.next() {
-                Some(x) => x.to_string(),
-                None => return SUCCESS,
-            };
+            (len - 1).checked_sub(num).ok_or_else(|| {
+                Cow::Owned(format!(
+                    "ion: popd: negative directory stack index out of range\n"
+                ))
+            })?
+        };
 
-            println!("{}", iter.fold(first, folder));
+        // apply -n
+        if index == 0 && keep_front {
+            index = 1;
         }
-        SUCCESS
-    }
-
-    pub(crate) fn dir_from_top(&self, num: usize) -> Option<&PathBuf> { self.dirs.get(num) }
-
-    pub(crate) fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
-        self.dirs.iter().rev().nth(num)
-    }
-
-    fn print_dirs(&self) {
-        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
-            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
-        });
-        println!("{}", dir.trim_left());
-    }
-
-    // sets current_dir to the element referred by index
-    fn set_current_dir_by_index(
-        &self,
-        index: usize,
-        caller: &str,
-    ) -> Result<(), Cow<'static, str>> {
-        let dir = self.dirs.iter().nth(index).ok_or_else(|| {
-            Cow::Owned(format!(
-                "ion: {}: {}: directory stack out of range\n",
-                caller, index
-            ))
-        })?;
 
-        set_current_dir(dir)
-            .map_err(|_| Cow::Owned(format!("ion: {}: Failed setting current dir\n", caller)))
-    }
+        // change to new directory, return if not possible
+        if index == 0 {
+            self.set_current_dir_by_index(1, "popd")?;
+        }
 
-    // pushd +<num>
-    fn rotate_left(&mut self, num: usize) {
-        let cloned = self.dirs.clone();
-        for (dest, src) in self.dirs.iter_mut().zip(cloned.iter().cycle().skip(num)) {
-            *dest = src.clone();
+        // pop element
+        if self.dirs.remove(index).is_none() {
+            return Err(Cow::Owned(format!(
+                "ion: popd: {}: directory stack index out of range\n",
+                index
+            )));
         }
-    }
 
-    // pushd -<num>
-    fn rotate_right(&mut self, num: usize) {
-        let len = self.dirs.len();
-        self.rotate_left(len - (num % len));
+        self.update_env_variables(variables);
+        self.print_dirs();
+        Ok(())
     }
 
-    fn normalize_path(&mut self, dir: &str) -> PathBuf {
-        use std::path::{Component, Path};
-        // Create a clone of the current directory.
-        let mut new_dir = match self.dirs.front() {
-            Some(cur_dir) => cur_dir.clone(),
-            None => PathBuf::new()
-        };
+    /// This function will take a map of variables as input and attempt to parse the value of
+    /// the
+    /// directory stack size variable. If it succeeds, it will return the value of that
+    /// variable,
+    /// else it will return a default value of 1000.
+    fn get_size(variables: &Variables) -> usize {
+        variables
+            .get_var_or_empty("DIRECTORY_STACK_SIZE")
+            .parse::<usize>()
+            .unwrap_or(1000)
+    }
 
-        // Iterate through components of the specified directory
-        // and calculate the new path based on them.
-        for component in Path::new(dir).components() {
-            match component {
-                Component::CurDir => { },
-                Component::ParentDir => { new_dir.pop(); },
-                _ => { new_dir.push(component); }
-            };
+    /// Create a new `DirectoryStack` containing the current working directory,
+    /// if available.
+    pub(crate) fn new() -> DirectoryStack {
+        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
+        match current_dir() {
+            Ok(curr_dir) => {
+                dirs.push_front(curr_dir);
+                DirectoryStack { dirs }
+            }
+            Err(_) => {
+                eprintln!("ion: failed to get current directory when building directory stack");
+                DirectoryStack { dirs }
+            }
         }
-
-        return new_dir;
     }
 }
 
diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs
index a57ae9fa..d35ea7dd 100644
--- a/src/lib/shell/flow.rs
+++ b/src/lib/shell/flow.rs
@@ -1,15 +1,24 @@
-use super::Shell;
-use super::flags::*;
-use super::flow_control::{collect_cases, collect_if, collect_loops, Case, ElseIf, Function, Statement};
-use super::job_control::JobControl;
-use super::status::*;
-use parser::{expand_string, parse_and_validate, ForExpression, StatementSplitter};
-use parser::assignments::{is_array, ReturnValue};
-use parser::pipelines::Pipeline;
+use super::{
+    flags::*,
+    flow_control::{collect_cases, collect_if, collect_loops, Case, ElseIf, Function, Statement},
+    job_control::JobControl,
+    status::*,
+    Shell,
+};
+use parser::{
+    assignments::{is_array, ReturnValue},
+    expand_string,
+    parse_and_validate,
+    pipelines::Pipeline,
+    ForExpression,
+    StatementSplitter,
+};
 use shell::assignments::VariableStore;
-use std::io::{stdout, Write};
-use std::iter;
-use std::mem;
+use std::{
+    io::{stdout, Write},
+    iter,
+    mem,
+};
 use types::Array;
 
 pub(crate) enum Condition {
@@ -70,338 +79,352 @@ pub(crate) trait FlowLogic {
 }
 
 impl FlowLogic for Shell {
-    fn on_command(&mut self, command_string: &str) {
-        self.break_flow = false;
-        let mut iterator = StatementSplitter::new(String::from(command_string)).map(parse_and_validate);
+    fn execute_toplevel<I>(
+        &mut self,
+        iterator: &mut I,
+        statement: Statement,
+    ) -> Result<(), &'static str>
+    where
+        I: Iterator<Item = Statement>,
+    {
+        match statement {
+            Statement::Error(number) => self.previous_status = number,
+            // Execute a Let Statement
+            Statement::Let(action) => {
+                self.previous_status = self.local(action);
+            }
+            Statement::Export(action) => {
+                self.previous_status = self.export(action);
+            }
+            // Collect the statements for the while loop, and if the loop is complete,
+            // execute the while loop with the provided expression.
+            Statement::While {
+                expression,
+                mut statements,
+            } => {
+                self.flow_control.level += 1;
 
-        // If the value is set to `0`, this means that we don't need to append to an
-        // existing partial statement block in memory, but can read and execute
-        // new statements.
-        if self.flow_control.level == 0 {
-            while let Some(statement) = iterator.next() {
-                // Executes all statements that it can, and stores the last remaining partial
-                // statement in memory if needed. We can tell if there is a partial statement
-                // later if the value of `level` is not set to `0`.
-                if let Err(why) = self.execute_toplevel(&mut iterator, statement) {
-                    eprintln!("{}", why);
-                    self.flow_control.level = 0;
-                    self.flow_control.current_if_mode = 0;
-                    return;
+                // Collect all of the statements contained within the while block.
+                collect_loops(iterator, &mut statements, &mut self.flow_control.level);
+
+                if self.flow_control.level == 0 {
+                    // All blocks were read, thus we can immediately execute now
+                    self.execute_while(expression, statements);
+                } else {
+                    // Store the partial `Statement::While` to memory
+                    self.flow_control.current_statement = Statement::While {
+                        expression,
+                        statements,
+                    }
                 }
             }
-        } else {
-            fn append_new_commands<I: Iterator<Item = Statement>>(
-                mut iterator: &mut I,
-                current_statement: &mut Statement,
-                level: &mut usize,
-                current_if_mode: &mut u8,
-            ) {
-                match current_statement {
-                    &mut Statement::While {
-                        ref mut statements, ..
-                    }
-                    | &mut Statement::For {
-                        ref mut statements, ..
-                    }
-                    | &mut Statement::Function {
-                        ref mut statements, ..
-                    } => {
-                        collect_loops(&mut iterator, statements, level);
-                    }
-                    &mut Statement::If {
-                        ref mut success,
-                        ref mut else_if,
-                        ref mut failure,
-                        ..
-                    } => {
-                        *current_if_mode = match collect_if(
-                            &mut iterator,
-                            success,
-                            else_if,
-                            failure,
-                            level,
-                            *current_if_mode,
-                        ) {
-                            Ok(mode) => mode,
-                            Err(why) => {
-                                eprintln!("{}", why);
-                                4
-                            }
-                        };
-                    }
-                    &mut Statement::Match { ref mut cases, .. } => {
-                        if let Err(why) = collect_cases(&mut iterator, cases, level) {
-                            eprintln!("{}", why);
-                        }
-                    }
-                    &mut Statement::Time(ref mut box_stmt) => {
-                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
-                    }
-                    &mut Statement::And(ref mut box_stmt) => {
-                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
-                    }
-                    &mut Statement::Or(ref mut box_stmt) => {
-                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
-                    }
-                    &mut Statement::Not(ref mut box_stmt) => {
-                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
+            // Collect the statements for the for loop, and if the loop is complete,
+            // execute the for loop with the provided expression.
+            Statement::For {
+                variable,
+                values,
+                mut statements,
+            } => {
+                self.flow_control.level += 1;
+
+                // Collect all of the statements contained within the for block.
+                collect_loops(iterator, &mut statements, &mut self.flow_control.level);
+
+                if self.flow_control.level == 0 {
+                    // All blocks were read, thus we can immediately execute now
+                    self.execute_for(&variable, &values, statements);
+                } else {
+                    // Store the partial `Statement::For` to memory
+                    self.flow_control.current_statement = Statement::For {
+                        variable,
+                        values,
+                        statements,
                     }
-                    _ => (),
                 }
             }
+            // Collect the statements needed for the `success`, `else_if`, and `failure`
+            // conditions; then execute the if statement if it is complete.
+            Statement::If {
+                expression,
+                mut success,
+                mut else_if,
+                mut failure,
+            } => {
+                self.flow_control.level += 1;
 
-            append_new_commands(
-                &mut iterator,
-                &mut self.flow_control.current_statement,
-                &mut self.flow_control.level,
-                &mut self.flow_control.current_if_mode,
-            );
+                // Collect all of the success and failure statements within the if condition.
+                // The `mode` value will let us know whether the collector ended while
+                // collecting the success block or the failure block.
+                let mode = collect_if(
+                    iterator,
+                    &mut success,
+                    &mut else_if,
+                    &mut failure,
+                    &mut self.flow_control.level,
+                    0,
+                )?;
 
-            // If this is true, an error occurred during the if statement
-            if self.flow_control.current_if_mode == 4 {
-                self.flow_control.level = 0;
-                self.flow_control.current_if_mode = 0;
-                self.flow_control.current_statement = Statement::Default;
-                return;
+                if self.flow_control.level == 0 {
+                    // All blocks were read, thus we can immediately execute now
+                    self.execute_if(expression, success, else_if, failure);
+                } else {
+                    // Set the mode and partial if statement in memory.
+                    self.flow_control.current_if_mode = mode;
+                    self.flow_control.current_statement = Statement::If {
+                        expression,
+                        success,
+                        else_if,
+                        failure,
+                    };
+                }
             }
+            // Collect the statements needed by the function and add the function to the
+            // list of functions if it is complete.
+            Statement::Function {
+                name,
+                args,
+                mut statements,
+                description,
+            } => {
+                self.flow_control.level += 1;
 
-            // If the level is set to 0, it means that the statement in memory is finished
-            // and thus is ready for execution.
-            if self.flow_control.level == 0 {
-                // Replaces the `current_statement` with a `Default` value to avoid the
-                // need to clone the value, and clearing it at the same time.
-                let mut replacement = Statement::Default;
-                mem::swap(&mut self.flow_control.current_statement, &mut replacement);
-
-                fn execute_final(shell: &mut Shell, statement: Statement) -> Condition {
-                    match statement {
-                        Statement::Error(number) => shell.previous_status = number,
-                        Statement::Let(action) => {
-                            shell.previous_status = shell.local(action);
-                        }
-                        Statement::Export(action) => {
-                            shell.previous_status = shell.export(action);
-                        }
-                        Statement::While {
-                            expression,
-                            statements,
-                        } => {
-                            if let Condition::SigInt = shell.execute_while(expression, statements) {
-                                return Condition::SigInt;
-                            }
-                        }
-                        Statement::For {
-                            variable,
-                            values,
-                            statements,
-                        } => {
-                            if let Condition::SigInt =
-                                shell.execute_for(&variable, &values, statements)
-                            {
-                                return Condition::SigInt;
-                            }
-                        }
-                        Statement::Function {
-                            name,
-                            args,
-                            statements,
-                            description,
-                        } => {
-                            shell.functions.insert(
-                                name.clone(),
-                                Function::new(description, name, args, statements),
-                            );
-                        }
-                        Statement::If {
-                            expression,
-                            success,
-                            else_if,
-                            failure,
-                        } => {
-                            shell.execute_if(expression, success, else_if, failure);
-                        }
-                        Statement::Match { expression, cases } => {
-                            shell.execute_match(expression, cases);
-                        }
-                        Statement::Time(box_stmt) => {
-                            let time = ::std::time::Instant::now();
+                // The same logic that applies to loops, also applies here.
+                collect_loops(iterator, &mut statements, &mut self.flow_control.level);
 
-                            let condition = execute_final(shell, *box_stmt);
+                if self.flow_control.level == 0 {
+                    // All blocks were read, thus we can add it to the list
+                    self.functions.insert(
+                        name.clone(),
+                        Function::new(description, name, args, statements),
+                    );
+                } else {
+                    // Store the partial function declaration in memory.
+                    self.flow_control.current_statement = Statement::Function {
+                        description,
+                        name,
+                        args,
+                        statements,
+                    }
+                }
+            }
+            // Simply executes a provided pipeline, immediately.
+            Statement::Pipeline(mut pipeline) => {
+                self.run_pipeline(&mut pipeline);
+                if self.flags & ERR_EXIT != 0 && self.previous_status != SUCCESS {
+                    let status = self.previous_status;
+                    self.exit(status);
+                }
+            }
+            Statement::Time(box_statement) => {
+                let time = ::std::time::Instant::now();
 
-                            let duration = time.elapsed();
-                            let seconds = duration.as_secs();
-                            let nanoseconds = duration.subsec_nanos();
+                if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
+                    eprintln!("{}", why);
+                    self.flow_control.level = 0;
+                    self.flow_control.current_if_mode = 0;
+                }
+                // Collect timing here so we do not count anything but the execution.
+                let duration = time.elapsed();
+                let seconds = duration.as_secs();
+                let nanoseconds = duration.subsec_nanos();
 
-                            let stdout = stdout();
-                            let mut stdout = stdout.lock();
-                            let _ = if seconds > 60 {
-                                writeln!(
-                                    stdout,
-                                    "real    {}m{:02}.{:09}s",
-                                    seconds / 60,
-                                    seconds % 60,
-                                    nanoseconds
-                                )
-                            } else {
-                                writeln!(stdout, "real    {}.{:09}s", seconds, nanoseconds)
-                            };
-                            return condition;
-                        }
-                        Statement::And(box_stmt) => {
-                            match shell.previous_status {
-                                SUCCESS => {
-                                    execute_final(shell, *box_stmt);
-                                },
-                                _ => ()
-                            }
-                        }
-                        Statement::Or(box_stmt) => {
-                            match shell.previous_status {
-                                FAILURE => {
-                                    execute_final(shell, *box_stmt);
-                                },
-                                _ => ()
+                if self.flow_control.level == 0 {
+                    // A statement was executed, output the time
+                    let stdout = stdout();
+                    let mut stdout = stdout.lock();
+                    let _ = if seconds > 60 {
+                        writeln!(
+                            stdout,
+                            "real    {}m{:02}.{:09}s",
+                            seconds / 60,
+                            seconds % 60,
+                            nanoseconds
+                        )
+                    } else {
+                        writeln!(stdout, "real    {}.{:09}s", seconds, nanoseconds)
+                    };
+                } else {
+                    // A statement wasn't executed , which means that current_statement has been
+                    // set to the inner statement. We fix this here.
+                    self.flow_control.current_statement =
+                        Statement::Time(Box::new(self.flow_control.current_statement.clone()));
+                }
+            }
+            Statement::And(box_statement) => {
+                if self.flow_control.level == 0 {
+                    match self.previous_status {
+                        SUCCESS => {
+                            if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
+                                eprintln!("{}", why);
+                                self.flow_control.level = 0;
+                                self.flow_control.current_if_mode = 0;
                             }
                         }
-                        Statement::Not(box_stmt) => {
-                            execute_final(shell, *box_stmt);
-                            match shell.previous_status {
-                                FAILURE => shell.previous_status = SUCCESS,
-                                SUCCESS => shell.previous_status = FAILURE,
-                                _ => ()
+                        _ => (),
+                    }
+                } else {
+                    // A statement wasn't executed , which means that current_statement has been
+                    // set to the inner statement. We fix this here.
+                    self.flow_control.current_statement =
+                        Statement::And(Box::new(self.flow_control.current_statement.clone()));
+                }
+            }
+            Statement::Or(box_statement) => {
+                if self.flow_control.level == 0 {
+                    match self.previous_status {
+                        FAILURE => {
+                            if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
+                                eprintln!("{}", why);
+                                self.flow_control.level = 0;
+                                self.flow_control.current_if_mode = 0;
                             }
-                            shell.variables.set_var("?", &shell.previous_status.to_string());
                         }
                         _ => (),
                     }
-                    Condition::NoOp
-                }
-
-                if let Condition::SigInt = execute_final(self, replacement) {
-                    return;
+                } else {
+                    // A statement wasn't executed , which means that current_statement has been
+                    // set to the inner statement. We fix this here.
+                    self.flow_control.current_statement =
+                        Statement::Or(Box::new(self.flow_control.current_statement.clone()));
                 }
-
-                // Capture any leftover statements.
-                while let Some(statement) = iterator.next() {
-                    if let Err(why) = self.execute_toplevel(&mut iterator, statement) {
+            }
+            Statement::Not(box_statement) => {
+                if self.flow_control.level == 0 {
+                    if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
                         eprintln!("{}", why);
                         self.flow_control.level = 0;
                         self.flow_control.current_if_mode = 0;
-                        return;
                     }
+                    match self.previous_status {
+                        FAILURE => self.previous_status = SUCCESS,
+                        SUCCESS => self.previous_status = FAILURE,
+                        _ => (),
+                    }
+                    let status = self.previous_status.to_string();
+                    self.set_var("?", &status);
+                } else {
+                    // A statement wasn't executed , which means that current_statement has been
+                    // set to the inner statement. We fix this here.
+                    self.flow_control.current_statement =
+                        Statement::Not(Box::new(self.flow_control.current_statement.clone()));
+                }
+            }
+            // At this level, else and else if keywords are forbidden.
+            Statement::ElseIf { .. } | Statement::Else => {
+                eprintln!("ion: syntax error: not an if statement");
+            }
+            // Likewise to else and else if, the end keyword does nothing here.
+            Statement::End => {
+                eprintln!("ion: syntax error: no block to end");
+            }
+            // Collect all cases that are being used by a match construct
+            Statement::Match {
+                expression,
+                mut cases,
+            } => {
+                self.flow_control.level += 1;
+                if let Err(why) = collect_cases(iterator, &mut cases, &mut self.flow_control.level)
+                {
+                    eprintln!("{}", why);
+                }
+                if self.flow_control.level == 0 {
+                    // If all blocks were read we execute the statement
+                    self.execute_match(expression, cases);
+                } else {
+                    // Store the partial function declaration in memory.
+                    self.flow_control.current_statement = Statement::Match { expression, cases };
                 }
             }
+            _ => {}
         }
+        Ok(())
     }
 
-    fn execute_match(&mut self, expression: String, cases: Vec<Case>) -> Condition {
-        // Logic for determining if the LHS of a match-case construct (the value we are
-        // matching against) matches the RHS of a match-case construct (a value
-        // in a case statement). For example, checking to see if the value
-        // "foo" matches the pattern "bar" would be invoked like so :
-        // ```ignore
-        // matches("foo", "bar")
-        // ```
-        fn matches(lhs: &Array, rhs: &Array) -> bool {
-            for v in lhs {
-                if rhs.contains(&v) {
-                    return true;
-                }
+    fn execute_if(
+        &mut self,
+        expression: Pipeline,
+        success: Vec<Statement>,
+        else_if: Vec<ElseIf>,
+        failure: Vec<Statement>,
+    ) -> Condition {
+        let first_condition = iter::once((expression, success));
+        let else_conditions = else_if
+            .into_iter()
+            .map(|cond| (cond.expression, cond.success));
+
+        for (mut condition, mut statements) in first_condition.chain(else_conditions) {
+            if self.run_pipeline(&mut condition) == Some(SUCCESS) {
+                return self.execute_statements(statements);
             }
-            false
         }
 
-        let is_array = is_array(&expression);
-        let value = expand_string(&expression, self, false);
-        let mut condition = Condition::NoOp;
-        for case in cases {
-            // let pattern_is_array = is_array(&value);
-            let pattern = case.value.map(|v| expand_string(&v, self, false));
-            match pattern {
-                None => {
-                    let mut previous_bind = None;
-                    if let Some(ref bind) = case.binding {
-                        if is_array {
-                            previous_bind = self.variables
-                                .get_array(bind)
-                                .map(|x| ReturnValue::Vector(x.clone()));
-                            self.variables.set_array(&bind, value.clone());
-                        } else {
-                            previous_bind = self.get_var(bind).map(|x| ReturnValue::Str(x));
-                            self.set_var(&bind, &value.join(" "));
-                        }
-                    }
-
-                    if let Some(statement) = case.conditional {
-                        self.on_command(&statement);
-                        if self.previous_status != SUCCESS {
-                            continue;
-                        }
-                    }
-
-                    condition = self.execute_statements(case.statements);
-
-                    if let Some(ref bind) = case.binding {
-                        if let Some(value) = previous_bind {
-                            match value {
-                                ReturnValue::Str(value) => self.set_var(bind, &value),
-                                ReturnValue::Vector(values) => {
-                                    self.variables.set_array(bind, values)
-                                }
-                            }
-                        }
-                    }
+        self.execute_statements(failure)
+    }
 
-                    break;
+    fn execute_for(
+        &mut self,
+        variable: &str,
+        values: &[String],
+        statements: Vec<Statement>,
+    ) -> Condition {
+        let ignore_variable = variable == "_";
+        match ForExpression::new(values, self) {
+            ForExpression::Multiple(ref values) if ignore_variable => for _ in values.iter() {
+                match self.execute_statements(statements.clone()) {
+                    Condition::Break => break,
+                    Condition::SigInt => return Condition::SigInt,
+                    _ => (),
                 }
-                Some(ref v) if matches(v, &value) => {
-                    let mut previous_bind = None;
-                    if let Some(ref bind) = case.binding {
-                        if is_array {
-                            previous_bind = self.variables
-                                .get_array(bind)
-                                .map(|x| ReturnValue::Vector(x.clone()));
-                            self.variables.set_array(&bind, value.clone());
-                        } else {
-                            previous_bind = self.get_var(bind).map(|x| ReturnValue::Str(x));
-                            self.set_var(&bind, &value.join(" "));
-                        }
-                    }
-
-                    if let Some(statement) = case.conditional {
-                        self.on_command(&statement);
-                        if self.previous_status != SUCCESS {
-                            continue;
-                        }
-                    }
-
-                    condition = self.execute_statements(case.statements);
-
-                    if let Some(ref bind) = case.binding {
-                        if let Some(value) = previous_bind {
-                            match value {
-                                ReturnValue::Str(value) => self.set_var(bind, &value),
-                                ReturnValue::Vector(values) => {
-                                    self.variables.set_array(bind, values)
-                                }
-                            }
-                        }
-                    }
-
-                    break;
+            },
+            ForExpression::Multiple(values) => for value in values.iter() {
+                self.set_var(variable, &value);
+                match self.execute_statements(statements.clone()) {
+                    Condition::Break => break,
+                    Condition::SigInt => return Condition::SigInt,
+                    _ => (),
                 }
-                Some(_) => (),
-            }
+            },
+            ForExpression::Normal(ref values) if ignore_variable => for _ in values.lines() {
+                match self.execute_statements(statements.clone()) {
+                    Condition::Break => break,
+                    Condition::SigInt => return Condition::SigInt,
+                    _ => (),
+                }
+            },
+            ForExpression::Normal(values) => for value in values.lines() {
+                self.set_var(variable, &value);
+                match self.execute_statements(statements.clone()) {
+                    Condition::Break => break,
+                    Condition::SigInt => return Condition::SigInt,
+                    _ => (),
+                }
+            },
+            ForExpression::Range(start, end) if ignore_variable => for _ in start..end {
+                match self.execute_statements(statements.clone()) {
+                    Condition::Break => break,
+                    Condition::SigInt => return Condition::SigInt,
+                    _ => (),
+                }
+            },
+            ForExpression::Range(start, end) => for value in (start..end).map(|x| x.to_string()) {
+                self.set_var(variable, &value);
+                match self.execute_statements(statements.clone()) {
+                    Condition::Break => break,
+                    Condition::SigInt => return Condition::SigInt,
+                    _ => (),
+                }
+            },
         }
-        condition
+        Condition::NoOp
     }
 
-    fn execute_statements(&mut self, mut statements: Vec<Statement>) -> Condition {
-        let mut iterator = statements.drain(..);
-        while let Some(statement) = iterator.next() {
-            match self.execute_statement(&mut iterator, statement) {
-                Condition::NoOp => {}
-                cond => return cond,
+    fn execute_while(&mut self, expression: Pipeline, statements: Vec<Statement>) -> Condition {
+        while self.run_pipeline(&mut expression.clone()) == Some(SUCCESS) {
+            // Cloning is needed so the statement can be re-iterated again if needed.
+            match self.execute_statements(statements.clone()) {
+                Condition::Break => break,
+                Condition::SigInt => return Condition::SigInt,
+                _ => (),
             }
         }
         Condition::NoOp
@@ -522,8 +545,8 @@ impl FlowLogic for Shell {
                 match self.previous_status {
                     SUCCESS => {
                         condition = self.execute_statement(iterator, *box_statement);
-                    },
-                    _ => condition = Condition::NoOp
+                    }
+                    _ => condition = Condition::NoOp,
                 }
 
                 match condition {
@@ -538,8 +561,8 @@ impl FlowLogic for Shell {
                 match self.previous_status {
                     FAILURE => {
                         condition = self.execute_statement(iterator, *box_statement);
-                    },
-                    _ => condition = Condition::NoOp
+                    }
+                    _ => condition = Condition::NoOp,
                 }
 
                 match condition {
@@ -554,7 +577,7 @@ impl FlowLogic for Shell {
                 match self.previous_status {
                     FAILURE => self.previous_status = SUCCESS,
                     SUCCESS => self.previous_status = FAILURE,
-                    _ => ()
+                    _ => (),
                 }
                 let status = self.previous_status.to_string();
                 self.set_var("?", &status);
@@ -592,360 +615,343 @@ impl FlowLogic for Shell {
             self.break_flow = false;
             Condition::SigInt
         } else {
-            Condition::NoOp
-        }
-    }
-
-    fn execute_while(&mut self, expression: Pipeline, statements: Vec<Statement>) -> Condition {
-        while self.run_pipeline(&mut expression.clone()) == Some(SUCCESS) {
-            // Cloning is needed so the statement can be re-iterated again if needed.
-            match self.execute_statements(statements.clone()) {
-                Condition::Break => break,
-                Condition::SigInt => return Condition::SigInt,
-                _ => (),
-            }
-        }
-        Condition::NoOp
-    }
-
-    fn execute_for(
-        &mut self,
-        variable: &str,
-        values: &[String],
-        statements: Vec<Statement>,
-    ) -> Condition {
-        let ignore_variable = variable == "_";
-        match ForExpression::new(values, self) {
-            ForExpression::Multiple(ref values) if ignore_variable => for _ in values.iter() {
-                match self.execute_statements(statements.clone()) {
-                    Condition::Break => break,
-                    Condition::SigInt => return Condition::SigInt,
-                    _ => (),
-                }
-            },
-            ForExpression::Multiple(values) => for value in values.iter() {
-                self.set_var(variable, &value);
-                match self.execute_statements(statements.clone()) {
-                    Condition::Break => break,
-                    Condition::SigInt => return Condition::SigInt,
-                    _ => (),
-                }
-            },
-            ForExpression::Normal(ref values) if ignore_variable => for _ in values.lines() {
-                match self.execute_statements(statements.clone()) {
-                    Condition::Break => break,
-                    Condition::SigInt => return Condition::SigInt,
-                    _ => (),
-                }
-            },
-            ForExpression::Normal(values) => for value in values.lines() {
-                self.set_var(variable, &value);
-                match self.execute_statements(statements.clone()) {
-                    Condition::Break => break,
-                    Condition::SigInt => return Condition::SigInt,
-                    _ => (),
-                }
-            },
-            ForExpression::Range(start, end) if ignore_variable => for _ in start..end {
-                match self.execute_statements(statements.clone()) {
-                    Condition::Break => break,
-                    Condition::SigInt => return Condition::SigInt,
-                    _ => (),
-                }
-            },
-            ForExpression::Range(start, end) => for value in (start..end).map(|x| x.to_string()) {
-                self.set_var(variable, &value);
-                match self.execute_statements(statements.clone()) {
-                    Condition::Break => break,
-                    Condition::SigInt => return Condition::SigInt,
-                    _ => (),
-                }
-            },
+            Condition::NoOp
         }
-        Condition::NoOp
     }
 
-    fn execute_if(
-        &mut self,
-        expression: Pipeline,
-        success: Vec<Statement>,
-        else_if: Vec<ElseIf>,
-        failure: Vec<Statement>,
-    ) -> Condition {
-        let first_condition = iter::once((expression, success));
-        let else_conditions = else_if
-            .into_iter()
-            .map(|cond| (cond.expression, cond.success));
-
-        for (mut condition, mut statements) in first_condition.chain(else_conditions) {
-            if self.run_pipeline(&mut condition) == Some(SUCCESS) {
-                return self.execute_statements(statements);
+    fn execute_statements(&mut self, mut statements: Vec<Statement>) -> Condition {
+        let mut iterator = statements.drain(..);
+        while let Some(statement) = iterator.next() {
+            match self.execute_statement(&mut iterator, statement) {
+                Condition::NoOp => {}
+                cond => return cond,
             }
         }
-
-        self.execute_statements(failure)
+        Condition::NoOp
     }
 
-    fn execute_toplevel<I>(
-        &mut self,
-        iterator: &mut I,
-        statement: Statement,
-    ) -> Result<(), &'static str>
-    where
-        I: Iterator<Item = Statement>,
-    {
-        match statement {
-            Statement::Error(number) => self.previous_status = number,
-            // Execute a Let Statement
-            Statement::Let(action) => {
-                self.previous_status = self.local(action);
-            }
-            Statement::Export(action) => {
-                self.previous_status = self.export(action);
+    fn execute_match(&mut self, expression: String, cases: Vec<Case>) -> Condition {
+        // Logic for determining if the LHS of a match-case construct (the value we are
+        // matching against) matches the RHS of a match-case construct (a value
+        // in a case statement). For example, checking to see if the value
+        // "foo" matches the pattern "bar" would be invoked like so :
+        // ```ignore
+        // matches("foo", "bar")
+        // ```
+        fn matches(lhs: &Array, rhs: &Array) -> bool {
+            for v in lhs {
+                if rhs.contains(&v) {
+                    return true;
+                }
             }
-            // Collect the statements for the while loop, and if the loop is complete,
-            // execute the while loop with the provided expression.
-            Statement::While {
-                expression,
-                mut statements,
-            } => {
-                self.flow_control.level += 1;
+            false
+        }
 
-                // Collect all of the statements contained within the while block.
-                collect_loops(iterator, &mut statements, &mut self.flow_control.level);
+        let is_array = is_array(&expression);
+        let value = expand_string(&expression, self, false);
+        let mut condition = Condition::NoOp;
+        for case in cases {
+            // let pattern_is_array = is_array(&value);
+            let pattern = case.value.map(|v| expand_string(&v, self, false));
+            match pattern {
+                None => {
+                    let mut previous_bind = None;
+                    if let Some(ref bind) = case.binding {
+                        if is_array {
+                            previous_bind = self.variables
+                                .get_array(bind)
+                                .map(|x| ReturnValue::Vector(x.clone()));
+                            self.variables.set_array(&bind, value.clone());
+                        } else {
+                            previous_bind = self.get_var(bind).map(|x| ReturnValue::Str(x));
+                            self.set_var(&bind, &value.join(" "));
+                        }
+                    }
 
-                if self.flow_control.level == 0 {
-                    // All blocks were read, thus we can immediately execute now
-                    self.execute_while(expression, statements);
-                } else {
-                    // Store the partial `Statement::While` to memory
-                    self.flow_control.current_statement = Statement::While {
-                        expression: expression,
-                        statements: statements,
+                    if let Some(statement) = case.conditional {
+                        self.on_command(&statement);
+                        if self.previous_status != SUCCESS {
+                            continue;
+                        }
+                    }
+
+                    condition = self.execute_statements(case.statements);
+
+                    if let Some(ref bind) = case.binding {
+                        if let Some(value) = previous_bind {
+                            match value {
+                                ReturnValue::Str(value) => self.set_var(bind, &value),
+                                ReturnValue::Vector(values) => {
+                                    self.variables.set_array(bind, values)
+                                }
+                            }
+                        }
                     }
+
+                    break;
                 }
-            }
-            // Collect the statements for the for loop, and if the loop is complete,
-            // execute the for loop with the provided expression.
-            Statement::For {
-                variable,
-                values,
-                mut statements,
-            } => {
-                self.flow_control.level += 1;
+                Some(ref v) if matches(v, &value) => {
+                    let mut previous_bind = None;
+                    if let Some(ref bind) = case.binding {
+                        if is_array {
+                            previous_bind = self.variables
+                                .get_array(bind)
+                                .map(|x| ReturnValue::Vector(x.clone()));
+                            self.variables.set_array(&bind, value.clone());
+                        } else {
+                            previous_bind = self.get_var(bind).map(|x| ReturnValue::Str(x));
+                            self.set_var(&bind, &value.join(" "));
+                        }
+                    }
 
-                // Collect all of the statements contained within the for block.
-                collect_loops(iterator, &mut statements, &mut self.flow_control.level);
+                    if let Some(statement) = case.conditional {
+                        self.on_command(&statement);
+                        if self.previous_status != SUCCESS {
+                            continue;
+                        }
+                    }
 
-                if self.flow_control.level == 0 {
-                    // All blocks were read, thus we can immediately execute now
-                    self.execute_for(&variable, &values, statements);
-                } else {
-                    // Store the partial `Statement::For` to memory
-                    self.flow_control.current_statement = Statement::For {
-                        variable:   variable,
-                        values:     values,
-                        statements: statements,
+                    condition = self.execute_statements(case.statements);
+
+                    if let Some(ref bind) = case.binding {
+                        if let Some(value) = previous_bind {
+                            match value {
+                                ReturnValue::Str(value) => self.set_var(bind, &value),
+                                ReturnValue::Vector(values) => {
+                                    self.variables.set_array(bind, values)
+                                }
+                            }
+                        }
                     }
+
+                    break;
                 }
+                Some(_) => (),
             }
-            // Collect the statements needed for the `success`, `else_if`, and `failure`
-            // conditions; then execute the if statement if it is complete.
-            Statement::If {
-                expression,
-                mut success,
-                mut else_if,
-                mut failure,
-            } => {
-                self.flow_control.level += 1;
+        }
+        condition
+    }
 
-                // Collect all of the success and failure statements within the if condition.
-                // The `mode` value will let us know whether the collector ended while
-                // collecting the success block or the failure block.
-                let mode = collect_if(
-                    iterator,
-                    &mut success,
-                    &mut else_if,
-                    &mut failure,
-                    &mut self.flow_control.level,
-                    0,
-                )?;
+    fn on_command(&mut self, command_string: &str) {
+        self.break_flow = false;
+        let mut iterator =
+            StatementSplitter::new(String::from(command_string)).map(parse_and_validate);
 
-                if self.flow_control.level == 0 {
-                    // All blocks were read, thus we can immediately execute now
-                    self.execute_if(expression, success, else_if, failure);
-                } else {
-                    // Set the mode and partial if statement in memory.
-                    self.flow_control.current_if_mode = mode;
-                    self.flow_control.current_statement = Statement::If {
-                        expression: expression,
-                        success:    success,
-                        else_if:    else_if,
-                        failure:    failure,
-                    };
+        // If the value is set to `0`, this means that we don't need to append to an
+        // existing partial statement block in memory, but can read and execute
+        // new statements.
+        if self.flow_control.level == 0 {
+            while let Some(statement) = iterator.next() {
+                // Executes all statements that it can, and stores the last remaining partial
+                // statement in memory if needed. We can tell if there is a partial statement
+                // later if the value of `level` is not set to `0`.
+                if let Err(why) = self.execute_toplevel(&mut iterator, statement) {
+                    eprintln!("{}", why);
+                    self.flow_control.level = 0;
+                    self.flow_control.current_if_mode = 0;
+                    return;
+                }
+            }
+        } else {
+            fn append_new_commands<I: Iterator<Item = Statement>>(
+                mut iterator: &mut I,
+                current_statement: &mut Statement,
+                level: &mut usize,
+                current_if_mode: &mut u8,
+            ) {
+                match current_statement {
+                    &mut Statement::While {
+                        ref mut statements, ..
+                    }
+                    | &mut Statement::For {
+                        ref mut statements, ..
+                    }
+                    | &mut Statement::Function {
+                        ref mut statements, ..
+                    } => {
+                        collect_loops(&mut iterator, statements, level);
+                    }
+                    &mut Statement::If {
+                        ref mut success,
+                        ref mut else_if,
+                        ref mut failure,
+                        ..
+                    } => {
+                        *current_if_mode = match collect_if(
+                            &mut iterator,
+                            success,
+                            else_if,
+                            failure,
+                            level,
+                            *current_if_mode,
+                        ) {
+                            Ok(mode) => mode,
+                            Err(why) => {
+                                eprintln!("{}", why);
+                                4
+                            }
+                        };
+                    }
+                    &mut Statement::Match { ref mut cases, .. } => {
+                        if let Err(why) = collect_cases(&mut iterator, cases, level) {
+                            eprintln!("{}", why);
+                        }
+                    }
+                    &mut Statement::Time(ref mut box_stmt) => {
+                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
+                    }
+                    &mut Statement::And(ref mut box_stmt) => {
+                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
+                    }
+                    &mut Statement::Or(ref mut box_stmt) => {
+                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
+                    }
+                    &mut Statement::Not(ref mut box_stmt) => {
+                        append_new_commands(iterator, box_stmt.as_mut(), level, current_if_mode);
+                    }
+                    _ => (),
                 }
             }
-            // Collect the statements needed by the function and add the function to the
-            // list of functions if it is complete.
-            Statement::Function {
-                name,
-                args,
-                mut statements,
-                description,
-            } => {
-                self.flow_control.level += 1;
 
-                // The same logic that applies to loops, also applies here.
-                collect_loops(iterator, &mut statements, &mut self.flow_control.level);
+            append_new_commands(
+                &mut iterator,
+                &mut self.flow_control.current_statement,
+                &mut self.flow_control.level,
+                &mut self.flow_control.current_if_mode,
+            );
 
-                if self.flow_control.level == 0 {
-                    // All blocks were read, thus we can add it to the list
-                    self.functions.insert(
-                        name.clone(),
-                        Function::new(description, name, args, statements),
-                    );
-                } else {
-                    // Store the partial function declaration in memory.
-                    self.flow_control.current_statement = Statement::Function {
-                        description: description,
-                        name:        name,
-                        args:        args,
-                        statements:  statements,
-                    }
-                }
-            }
-            // Simply executes a provided pipeline, immediately.
-            Statement::Pipeline(mut pipeline) => {
-                self.run_pipeline(&mut pipeline);
-                if self.flags & ERR_EXIT != 0 && self.previous_status != SUCCESS {
-                    let status = self.previous_status;
-                    self.exit(status);
-                }
+            // If this is true, an error occurred during the if statement
+            if self.flow_control.current_if_mode == 4 {
+                self.flow_control.level = 0;
+                self.flow_control.current_if_mode = 0;
+                self.flow_control.current_statement = Statement::Default;
+                return;
             }
-            Statement::Time(box_statement) => {
-                let time = ::std::time::Instant::now();
 
-                if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
-                    eprintln!("{}", why);
-                    self.flow_control.level = 0;
-                    self.flow_control.current_if_mode = 0;
-                }
-                // Collect timing here so we do not count anything but the execution.
-                let duration = time.elapsed();
-                let seconds = duration.as_secs();
-                let nanoseconds = duration.subsec_nanos();
+            // If the level is set to 0, it means that the statement in memory is finished
+            // and thus is ready for execution.
+            if self.flow_control.level == 0 {
+                // Replaces the `current_statement` with a `Default` value to avoid the
+                // need to clone the value, and clearing it at the same time.
+                let mut replacement = Statement::Default;
+                mem::swap(&mut self.flow_control.current_statement, &mut replacement);
 
-                if self.flow_control.level == 0 {
-                    // A statement was executed, output the time
-                    let stdout = stdout();
-                    let mut stdout = stdout.lock();
-                    let _ = if seconds > 60 {
-                        writeln!(
-                            stdout,
-                            "real    {}m{:02}.{:09}s",
-                            seconds / 60,
-                            seconds % 60,
-                            nanoseconds
-                        )
-                    } else {
-                        writeln!(stdout, "real    {}.{:09}s", seconds, nanoseconds)
-                    };
-                } else {
-                    // A statement wasn't executed , which means that current_statement has been
-                    // set to the inner statement. We fix this here.
-                    self.flow_control.current_statement =
-                        Statement::Time(Box::new(self.flow_control.current_statement.clone()));
-                }
-            }
-            Statement::And(box_statement) => {
-                if self.flow_control.level == 0 {
-                    match self.previous_status {
-                        SUCCESS => {
-                            if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
-                                eprintln!("{}", why);
-                                self.flow_control.level = 0;
-                                self.flow_control.current_if_mode = 0;
+                fn execute_final(shell: &mut Shell, statement: Statement) -> Condition {
+                    match statement {
+                        Statement::Error(number) => shell.previous_status = number,
+                        Statement::Let(action) => {
+                            shell.previous_status = shell.local(action);
+                        }
+                        Statement::Export(action) => {
+                            shell.previous_status = shell.export(action);
+                        }
+                        Statement::While {
+                            expression,
+                            statements,
+                        } => {
+                            if let Condition::SigInt = shell.execute_while(expression, statements) {
+                                return Condition::SigInt;
+                            }
+                        }
+                        Statement::For {
+                            variable,
+                            values,
+                            statements,
+                        } => {
+                            if let Condition::SigInt =
+                                shell.execute_for(&variable, &values, statements)
+                            {
+                                return Condition::SigInt;
                             }
+                        }
+                        Statement::Function {
+                            name,
+                            args,
+                            statements,
+                            description,
+                        } => {
+                            shell.functions.insert(
+                                name.clone(),
+                                Function::new(description, name, args, statements),
+                            );
+                        }
+                        Statement::If {
+                            expression,
+                            success,
+                            else_if,
+                            failure,
+                        } => {
+                            shell.execute_if(expression, success, else_if, failure);
+                        }
+                        Statement::Match { expression, cases } => {
+                            shell.execute_match(expression, cases);
+                        }
+                        Statement::Time(box_stmt) => {
+                            let time = ::std::time::Instant::now();
+
+                            let condition = execute_final(shell, *box_stmt);
+
+                            let duration = time.elapsed();
+                            let seconds = duration.as_secs();
+                            let nanoseconds = duration.subsec_nanos();
+
+                            let stdout = stdout();
+                            let mut stdout = stdout.lock();
+                            let _ = if seconds > 60 {
+                                writeln!(
+                                    stdout,
+                                    "real    {}m{:02}.{:09}s",
+                                    seconds / 60,
+                                    seconds % 60,
+                                    nanoseconds
+                                )
+                            } else {
+                                writeln!(stdout, "real    {}.{:09}s", seconds, nanoseconds)
+                            };
+                            return condition;
+                        }
+                        Statement::And(box_stmt) => match shell.previous_status {
+                            SUCCESS => {
+                                execute_final(shell, *box_stmt);
+                            }
+                            _ => (),
                         },
-                        _ => ()
-                    }
-                } else {
-                    // A statement wasn't executed , which means that current_statement has been
-                    // set to the inner statement. We fix this here.
-                    self.flow_control.current_statement =
-                        Statement::And(Box::new(self.flow_control.current_statement.clone()));
-                }
-            }
-            Statement::Or(box_statement) => {
-                if self.flow_control.level == 0 {
-                    match self.previous_status {
-                        FAILURE => {
-                            if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
-                                eprintln!("{}", why);
-                                self.flow_control.level = 0;
-                                self.flow_control.current_if_mode = 0;
+                        Statement::Or(box_stmt) => match shell.previous_status {
+                            FAILURE => {
+                                execute_final(shell, *box_stmt);
                             }
+                            _ => (),
                         },
-                        _ => ()
+                        Statement::Not(box_stmt) => {
+                            execute_final(shell, *box_stmt);
+                            match shell.previous_status {
+                                FAILURE => shell.previous_status = SUCCESS,
+                                SUCCESS => shell.previous_status = FAILURE,
+                                _ => (),
+                            }
+                            shell
+                                .variables
+                                .set_var("?", &shell.previous_status.to_string());
+                        }
+                        _ => (),
                     }
+                    Condition::NoOp
+                }
 
-                } else {
-                    // A statement wasn't executed , which means that current_statement has been
-                    // set to the inner statement. We fix this here.
-                    self.flow_control.current_statement =
-                        Statement::Or(Box::new(self.flow_control.current_statement.clone()));
+                if let Condition::SigInt = execute_final(self, replacement) {
+                    return;
                 }
 
-            }
-            Statement::Not(box_statement) => {
-                if self.flow_control.level == 0 {
-                    if let Err(why) = self.execute_toplevel(iterator, *box_statement) {
+                // Capture any leftover statements.
+                while let Some(statement) = iterator.next() {
+                    if let Err(why) = self.execute_toplevel(&mut iterator, statement) {
                         eprintln!("{}", why);
                         self.flow_control.level = 0;
                         self.flow_control.current_if_mode = 0;
+                        return;
                     }
-                    match self.previous_status {
-                        FAILURE => self.previous_status = SUCCESS,
-                        SUCCESS => self.previous_status = FAILURE,
-                        _ => ()
-                    }
-                    let status = self.previous_status.to_string();
-                    self.set_var("?", &status);
-                } else {
-                    // A statement wasn't executed , which means that current_statement has been
-                    // set to the inner statement. We fix this here.
-                    self.flow_control.current_statement =
-                        Statement::Not(Box::new(self.flow_control.current_statement.clone()));
-                }
-            }
-            // At this level, else and else if keywords are forbidden.
-            Statement::ElseIf { .. } | Statement::Else => {
-                eprintln!("ion: syntax error: not an if statement");
-            }
-            // Likewise to else and else if, the end keyword does nothing here.
-            Statement::End => {
-                eprintln!("ion: syntax error: no block to end");
-            }
-            // Collect all cases that are being used by a match construct
-            Statement::Match {
-                expression,
-                mut cases,
-            } => {
-                self.flow_control.level += 1;
-                if let Err(why) = collect_cases(iterator, &mut cases, &mut self.flow_control.level)
-                {
-                    eprintln!("{}", why);
-                }
-                if self.flow_control.level == 0 {
-                    // If all blocks were read we execute the statement
-                    self.execute_match(expression, cases);
-                } else {
-                    // Store the partial function declaration in memory.
-                    self.flow_control.current_statement = Statement::Match { expression, cases };
                 }
             }
-            _ => {}
         }
-        Ok(())
     }
 }
diff --git a/src/lib/shell/flow_control.rs b/src/lib/shell/flow_control.rs
index 4ca12bad..3709379b 100644
--- a/src/lib/shell/flow_control.rs
+++ b/src/lib/shell/flow_control.rs
@@ -1,11 +1,8 @@
-use super::Shell;
-use super::flow::FlowLogic;
+use super::{flow::FlowLogic, Shell};
 use fnv::*;
-use parser::assignments::*;
-use parser::pipelines::Pipeline;
+use parser::{assignments::*, pipelines::Pipeline};
 use std::fmt::{self, Display, Formatter};
-use types::*;
-use types::Identifier;
+use types::{Identifier, *};
 
 #[derive(Debug, PartialEq, Clone)]
 pub(crate) struct ElseIf {
@@ -27,7 +24,10 @@ pub(crate) struct ElseIf {
 /// ```
 /// would be represented by the Case object:
 /// ```rust,ignore
-/// Case { value: Some(value), statements: vec![statement0, statement1, ... statementN]}
+/// Case {
+///     value:      Some(value),
+///     statements: vec![statement0, statement1, ... statementN],
+/// }
 /// ```
 /// The wildcard branch, a branch that matches any value, is represented as such:
 /// ```rust,ignore
@@ -167,22 +167,6 @@ impl Display for FunctionError {
 }
 
 impl Function {
-    pub(crate) fn new(
-        description: Option<String>,
-        name: Identifier,
-        args: Vec<KeyBuf>,
-        statements: Vec<Statement>,
-    ) -> Function {
-        Function {
-            description,
-            name,
-            args,
-            statements,
-        }
-    }
-
-    pub(crate) fn get_description<'a>(&'a self) -> Option<&'a String> { self.description.as_ref() }
-
     pub(crate) fn execute(self, shell: &mut Shell, args: &[&str]) -> Result<(), FunctionError> {
         if args.len() - 1 != self.args.len() {
             return Err(FunctionError::InvalidArgumentCount);
@@ -240,6 +224,22 @@ impl Function {
 
         Ok(())
     }
+
+    pub(crate) fn get_description<'a>(&'a self) -> Option<&'a String> { self.description.as_ref() }
+
+    pub(crate) fn new(
+        description: Option<String>,
+        name: Identifier,
+        args: Vec<KeyBuf>,
+        statements: Vec<Statement>,
+    ) -> Function {
+        Function {
+            description,
+            name,
+            args,
+            statements,
+        }
+    }
 }
 
 pub(crate) fn collect_cases<I>(
@@ -254,12 +254,16 @@ where
         ($statement:expr) => {
             match cases.last_mut() {
                 // XXX: When does this actually happen? What syntax error is this???
-                None => return Err(["ion: syntax error: encountered ",
-                                     $statement.short(),
-                                     " outside of `case ...` block"].concat()),
+                None => {
+                    return Err([
+                        "ion: syntax error: encountered ",
+                        $statement.short(),
+                        " outside of `case ...` block",
+                    ].concat())
+                }
                 Some(ref mut case) => case.statements.push($statement),
             }
-        }
+        };
     }
 
     while let Some(statement) = iterator.next() {
diff --git a/src/lib/shell/fork.rs b/src/lib/shell/fork.rs
index 3e8c1f11..5e4847b6 100644
--- a/src/lib/shell/fork.rs
+++ b/src/lib/shell/fork.rs
@@ -1,6 +1,8 @@
 use super::{IonError, Shell};
-use std::fs::File;
-use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::{
+    fs::File,
+    os::unix::io::{AsRawFd, FromRawFd},
+};
 use sys;
 
 #[repr(u8)]
@@ -50,9 +52,6 @@ pub struct IonResult {
 }
 
 impl<'a> Fork<'a> {
-    /// Creates a new `Fork` state from an existing shell.
-    pub fn new(shell: &'a Shell, capture: Capture) -> Fork<'a> { Fork { shell, capture } }
-
     /// Executes a closure within the child of the fork, and returning an `IonResult` in a
     /// non-blocking fashion.
     pub fn exec<F: FnMut(&mut Shell)>(&self, mut child_func: F) -> Result<IonResult, IonError> {
@@ -132,10 +131,11 @@ impl<'a> Fork<'a> {
                     // `waitpid()` is required to reap the child.
                     status: sys::wait_for_child(pid).map_err(|why| IonError::Fork { why })?,
                 })
-            },
-            Err(why) => {
-                Err(IonError::Fork { why: why })
-            },
+            }
+            Err(why) => Err(IonError::Fork { why }),
         }
     }
+
+    /// Creates a new `Fork` state from an existing shell.
+    pub fn new(shell: &'a Shell, capture: Capture) -> Fork<'a> { Fork { shell, capture } }
 }
diff --git a/src/lib/shell/history.rs b/src/lib/shell/history.rs
index 2feb4501..2510a1e2 100644
--- a/src/lib/shell/history.rs
+++ b/src/lib/shell/history.rs
@@ -1,5 +1,4 @@
-use super::Shell;
-use super::status::*;
+use super::{status::*, Shell};
 
 use regex::Regex;
 use std::io::{self, Write};
@@ -70,18 +69,44 @@ trait ShellHistoryPrivate {
 }
 
 impl ShellHistory for Shell {
-    fn print_history(&self, _arguments: &[&str]) -> i32 {
-        if let Some(context) = self.context.as_ref() {
-            let mut buffer = Vec::with_capacity(8 * 1024);
-            for command in &context.history.buffers {
-                let _ = writeln!(buffer, "{}", command);
+    fn update_ignore_patterns(&mut self, patterns: &Array) {
+        let mut flags = IgnoreFlags::empty();
+        let mut regexes = Vec::new();
+        // for convenience and to avoid typos
+        let regex_prefix = "regex:";
+        for pattern in patterns {
+            match pattern.as_ref() {
+                "all" => flags |= IgnoreFlags::ALL,
+                "no_such_command" => flags |= IgnoreFlags::NO_SUCH_COMMAND,
+                "whitespace" => flags |= IgnoreFlags::WHITESPACE,
+                // The length check is there to just ignore empty regex definitions
+                _ if pattern.starts_with(regex_prefix) && pattern.len() > regex_prefix.len() => {
+                    flags |= IgnoreFlags::BASED_ON_REGEX;
+                    let regex_string = &pattern[regex_prefix.len()..];
+                    // We save the compiled regexes, as compiling them can be  an expensive task
+                    if let Ok(regex) = Regex::new(regex_string) {
+                        regexes.push(regex);
+                    }
+                }
+                _ => continue,
             }
-            let stdout = io::stdout();
-            let mut stdout = stdout.lock();
-            let _ = stdout.write_all(&buffer);
-            SUCCESS
+        }
+
+        self.ignore_setting.flags = flags;
+        self.ignore_setting.regexes = if regexes.len() > 0 {
+            Some(regexes)
         } else {
-            FAILURE
+            None
+        }
+    }
+
+    fn save_command_in_history(&mut self, command: &str) {
+        if self.should_save_command(command) {
+            // Mark the command in the context history
+            self.set_context_history_from_vars();
+            if let Err(err) = self.context.as_mut().unwrap().history.push(command.into()) {
+                eprintln!("ion: {}", err);
+            }
         }
     }
 
@@ -109,44 +134,18 @@ impl ShellHistory for Shell {
         }
     }
 
-    fn save_command_in_history(&mut self, command: &str) {
-        if self.should_save_command(command) {
-            // Mark the command in the context history
-            self.set_context_history_from_vars();
-            if let Err(err) = self.context.as_mut().unwrap().history.push(command.into()) {
-                eprintln!("ion: {}", err);
-            }
-        }
-    }
-
-    fn update_ignore_patterns(&mut self, patterns: &Array) {
-        let mut flags = IgnoreFlags::empty();
-        let mut regexes = Vec::new();
-        // for convenience and to avoid typos
-        let regex_prefix = "regex:";
-        for pattern in patterns {
-            match pattern.as_ref() {
-                "all" => flags |= IgnoreFlags::ALL,
-                "no_such_command" => flags |= IgnoreFlags::NO_SUCH_COMMAND,
-                "whitespace" => flags |= IgnoreFlags::WHITESPACE,
-                // The length check is there to just ignore empty regex definitions
-                _ if pattern.starts_with(regex_prefix) && pattern.len() > regex_prefix.len() => {
-                    flags |= IgnoreFlags::BASED_ON_REGEX;
-                    let regex_string = &pattern[regex_prefix.len()..];
-                    // We save the compiled regexes, as compiling them can be  an expensive task
-                    if let Ok(regex) = Regex::new(regex_string) {
-                        regexes.push(regex);
-                    }
-                }
-                _ => continue,
+    fn print_history(&self, _arguments: &[&str]) -> i32 {
+        if let Some(context) = self.context.as_ref() {
+            let mut buffer = Vec::with_capacity(8 * 1024);
+            for command in &context.history.buffers {
+                let _ = writeln!(buffer, "{}", command);
             }
-        }
-
-        self.ignore_setting.flags = flags;
-        self.ignore_setting.regexes = if regexes.len() > 0 {
-            Some(regexes)
+            let stdout = io::stdout();
+            let mut stdout = stdout.lock();
+            let _ = stdout.write_all(&buffer);
+            SUCCESS
         } else {
-            None
+            FAILURE
         }
     }
 }
diff --git a/src/lib/shell/job.rs b/src/lib/shell/job.rs
index 4ad46d3d..6357ff92 100644
--- a/src/lib/shell/job.rs
+++ b/src/lib/shell/job.rs
@@ -1,11 +1,8 @@
 use super::Shell;
 use builtins::{BuiltinFunction, BUILTINS};
-use parser::expand_string;
-use parser::pipelines::RedirectFrom;
+use parser::{expand_string, pipelines::RedirectFrom};
 use smallstring::SmallString;
-use std::fmt;
-use std::fs::File;
-use std::str;
+use std::{fmt, fs::File, str};
 use types::*;
 
 #[derive(Debug, PartialEq, Clone, Copy)]
@@ -27,6 +24,15 @@ pub(crate) struct Job {
 }
 
 impl Job {
+    /// Takes the current job's arguments and expands them, one argument at a
+    /// time, returning a new `Job` with the expanded arguments.
+    pub(crate) fn expand(&mut self, shell: &Shell) {
+        let mut expanded = Array::new();
+        expanded.grow(self.args.len());
+        expanded.extend(self.args.drain().flat_map(|arg| expand_arg(&arg, shell)));
+        self.args = expanded;
+    }
+
     pub(crate) fn new(args: Array, kind: JobKind) -> Self {
         let command = SmallString::from_str(&args[0]);
         let builtin = BUILTINS.get(command.as_ref()).map(|b| b.main);
@@ -37,15 +43,6 @@ impl Job {
             builtin,
         }
     }
-
-    /// Takes the current job's arguments and expands them, one argument at a
-    /// time, returning a new `Job` with the expanded arguments.
-    pub(crate) fn expand(&mut self, shell: &Shell) {
-        let mut expanded = Array::new();
-        expanded.grow(self.args.len());
-        expanded.extend(self.args.drain().flat_map(|arg| expand_arg(&arg, shell)));
-        self.args = expanded;
-    }
 }
 
 impl PartialEq for Job {
@@ -79,25 +76,25 @@ fn expand_arg(arg: &str, shell: &Shell) -> Array {
 pub(crate) enum RefinedJob {
     /// An external program that is executed by this shell
     External {
-        name: Identifier,
-        args: Array,
-        stdin: Option<File>,
+        name:   Identifier,
+        args:   Array,
+        stdin:  Option<File>,
         stdout: Option<File>,
         stderr: Option<File>,
     },
     /// A procedure embedded into Ion
     Builtin {
-        main: BuiltinFunction,
-        args: Array,
-        stdin: Option<File>,
+        main:   BuiltinFunction,
+        args:   Array,
+        stdin:  Option<File>,
         stdout: Option<File>,
         stderr: Option<File>,
     },
     /// Functions can act as commands too!
     Function {
-        name: Identifier,
-        args: Array,
-        stdin: Option<File>,
+        name:   Identifier,
+        args:   Array,
+        stdin:  Option<File>,
         stdout: Option<File>,
         stderr: Option<File>,
     },
@@ -130,8 +127,10 @@ impl TeeItem {
     /// should
     /// never be `RedirectFrom`::Both`
     pub(crate) fn write_to_all(&mut self, extra: Option<RedirectFrom>) -> ::std::io::Result<()> {
-        use std::io::{self, Read, Write};
-        use std::os::unix::io::*;
+        use std::{
+            io::{self, Read, Write},
+            os::unix::io::*,
+        };
         fn write_out<R>(source: &mut R, sinks: &mut [File]) -> io::Result<()>
         where
             R: Read,
@@ -182,54 +181,61 @@ impl TeeItem {
 macro_rules! set_field {
     ($self:expr, $field:ident, $arg:expr) => {
         match *$self {
-            RefinedJob::External { ref mut $field, .. } |
-                RefinedJob::Builtin { ref mut $field,  .. } |
-                RefinedJob::Function { ref mut $field, .. } |
-                RefinedJob::Tee { ref mut $field, .. } => {
+            RefinedJob::External { ref mut $field, .. }
+            | RefinedJob::Builtin { ref mut $field, .. }
+            | RefinedJob::Function { ref mut $field, .. }
+            | RefinedJob::Tee { ref mut $field, .. } => {
                 *$field = Some($arg);
             }
             // Do nothing for Cat
             _ => {}
         }
-    }
+    };
 }
 
 impl RefinedJob {
-    pub(crate) fn external(name: Identifier, args: Array) -> Self {
-        RefinedJob::External {
-            name,
-            args,
-            stdin: None,
-            stdout: None,
-            stderr: None,
+    /// Returns a long description of this job: the commands and arguments
+    pub(crate) fn long(&self) -> String {
+        match *self {
+            RefinedJob::External { ref args, .. }
+            | RefinedJob::Builtin { ref args, .. }
+            | RefinedJob::Function { ref args, .. } => format!("{}", args.join(" ")),
+            // TODO: Figure out real printing
+            RefinedJob::Cat { .. } | RefinedJob::Tee { .. } => "".into(),
         }
     }
 
-    pub(crate) fn builtin(main: BuiltinFunction, args: Array) -> Self {
-        RefinedJob::Builtin {
-            main,
-            args,
-            stdin: None,
-            stdout: None,
-            stderr: None,
+    /// Returns a short description of this job: often just the command
+    /// or builtin name
+    pub(crate) fn short(&self) -> String {
+        match *self {
+            RefinedJob::Builtin { .. } => String::from("Shell Builtin"),
+            RefinedJob::Function { ref name, .. } | RefinedJob::External { ref name, .. } => {
+                name.to_string()
+            }
+            // TODO: Print for real
+            RefinedJob::Cat { .. } => "multi-input".into(),
+            RefinedJob::Tee { .. } => "multi-output".into(),
         }
     }
 
-    pub(crate) fn function(name: Identifier, args: Array) -> Self {
-        RefinedJob::Function {
-            name,
-            args,
-            stdin: None,
-            stdout: None,
-            stderr: None,
+    pub(crate) fn stderr(&mut self, file: File) {
+        set_field!(self, stderr, file);
+    }
+
+    pub(crate) fn stdout(&mut self, file: File) {
+        if let &mut RefinedJob::Cat { ref mut stdout, .. } = self {
+            *stdout = Some(file);
+        } else {
+            set_field!(self, stdout, file);
         }
     }
 
-    pub(crate) fn cat(sources: Vec<File>) -> Self {
-        RefinedJob::Cat {
-            sources,
-            stdin: None,
-            stdout: None,
+    pub(crate) fn stdin(&mut self, file: File) {
+        if let &mut RefinedJob::Cat { ref mut stdin, .. } = self {
+            *stdin = Some(file);
+        } else {
+            set_field!(self, stdin, file);
         }
     }
 
@@ -242,47 +248,41 @@ impl RefinedJob {
         }
     }
 
-    pub(crate) fn stdin(&mut self, file: File) {
-        if let &mut RefinedJob::Cat { ref mut stdin, .. } = self {
-            *stdin = Some(file);
-        } else {
-            set_field!(self, stdin, file);
+    pub(crate) fn cat(sources: Vec<File>) -> Self {
+        RefinedJob::Cat {
+            sources,
+            stdin: None,
+            stdout: None,
         }
     }
 
-    pub(crate) fn stdout(&mut self, file: File) {
-        if let &mut RefinedJob::Cat { ref mut stdout, .. } = self {
-            *stdout = Some(file);
-        } else {
-            set_field!(self, stdout, file);
+    pub(crate) fn function(name: Identifier, args: Array) -> Self {
+        RefinedJob::Function {
+            name,
+            args,
+            stdin: None,
+            stdout: None,
+            stderr: None,
         }
     }
 
-    pub(crate) fn stderr(&mut self, file: File) {
-        set_field!(self, stderr, file);
-    }
-
-    /// Returns a short description of this job: often just the command
-    /// or builtin name
-    pub(crate) fn short(&self) -> String {
-        match *self {
-            RefinedJob::Builtin { .. } => String::from("Shell Builtin"),
-            RefinedJob::Function { ref name, .. } |
-                RefinedJob::External { ref name, .. } => name.to_string(),
-            // TODO: Print for real
-            RefinedJob::Cat { .. } => "multi-input".into(),
-            RefinedJob::Tee { .. } => "multi-output".into(),
+    pub(crate) fn builtin(main: BuiltinFunction, args: Array) -> Self {
+        RefinedJob::Builtin {
+            main,
+            args,
+            stdin: None,
+            stdout: None,
+            stderr: None,
         }
     }
 
-    /// Returns a long description of this job: the commands and arguments
-    pub(crate) fn long(&self) -> String {
-        match *self {
-            RefinedJob::External { ref args, .. } |
-                RefinedJob::Builtin { ref args, .. } |
-                RefinedJob::Function { ref args, .. } => format!("{}", args.join(" ")),
-            // TODO: Figure out real printing
-            RefinedJob::Cat { .. } | RefinedJob::Tee { .. } => "".into(),
+    pub(crate) fn external(name: Identifier, args: Array) -> Self {
+        RefinedJob::External {
+            name,
+            args,
+            stdin: None,
+            stdout: None,
+            stderr: None,
         }
     }
 }
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index dfdf25cb..846dda3d 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -1,62 +1,71 @@
 mod assignments;
+pub(crate) mod binary;
+pub(crate) mod colors;
 mod completer;
+pub(crate) mod directory_stack;
+pub mod flags;
 mod flow;
+pub(crate) mod flow_control;
 mod fork;
+pub mod fork_function;
 mod history;
 mod job;
-pub mod flags;
-pub mod fork_function;
-pub mod status;
-pub mod variables;
-pub(crate) mod binary;
-pub(crate) mod colors;
-pub(crate) mod directory_stack;
-pub(crate) mod flow_control;
 pub(crate) mod pipe_exec;
 pub(crate) mod plugins;
 pub(crate) mod signals;
+pub mod status;
+pub mod variables;
 
-pub use self::binary::Binary;
-pub(crate) use self::flow::FlowLogic;
-pub use self::fork::{Capture, Fork, IonResult};
-pub(crate) use self::history::{IgnoreSetting, ShellHistory};
-pub(crate) use self::job::{Job, JobKind};
-pub(crate) use self::pipe_exec::{foreground, job_control};
-
-use self::directory_stack::DirectoryStack;
-use self::flags::*;
-use self::flow_control::{FlowControl, Function, FunctionError};
-use self::foreground::ForegroundSignals;
-use self::job_control::{BackgroundProcess, JobControl};
-use self::pipe_exec::PipelineExecution;
-use self::status::*;
-use self::variables::Variables;
+pub use self::{
+    binary::Binary,
+    fork::{Capture, Fork, IonResult},
+};
+pub(crate) use self::{
+    flow::FlowLogic,
+    history::{IgnoreSetting, ShellHistory},
+    job::{Job, JobKind},
+    pipe_exec::{foreground, job_control},
+};
+
+use self::{
+    directory_stack::DirectoryStack,
+    flags::*,
+    flow_control::{FlowControl, Function, FunctionError},
+    foreground::ForegroundSignals,
+    job_control::{BackgroundProcess, JobControl},
+    pipe_exec::PipelineExecution,
+    status::*,
+    variables::Variables,
+};
 use builtins::{BuiltinMap, BUILTINS};
 use fnv::FnvHashMap;
 use liner::Context;
-use parser::{ArgumentSplitter, Expander, Select};
-use parser::Terminator;
-use parser::pipelines::Pipeline;
+use parser::{pipelines::Pipeline, ArgumentSplitter, Expander, Select, Terminator};
 use smallvec::SmallVec;
-use std::fs::File;
-use std::io::{self, Read, Write};
-use std::iter::FromIterator;
-use std::ops::Deref;
-use std::path::Path;
-use std::process;
-use std::sync::{Arc, Mutex};
-use std::sync::atomic::Ordering;
-use std::time::SystemTime;
+use std::{
+    fs::File,
+    io::{self, Read, Write},
+    iter::FromIterator,
+    ops::Deref,
+    path::Path,
+    process,
+    sync::{atomic::Ordering, Arc, Mutex},
+    time::SystemTime,
+};
 use sys;
 use types::*;
 use xdg::BaseDirectories;
 
 #[derive(Debug, Fail)]
 pub enum IonError {
-    #[fail(display = "failed to fork: {}", why)] Fork { why: io::Error },
-    #[fail(display = "element does not exist")] DoesNotExist,
-    #[fail(display = "input was not terminated")] Unterminated,
-    #[fail(display = "function error: {}", why)] Function { why: FunctionError },
+    #[fail(display = "failed to fork: {}", why)]
+    Fork { why: io::Error },
+    #[fail(display = "element does not exist")]
+    DoesNotExist,
+    #[fail(display = "input was not terminated")]
+    Unterminated,
+    #[fail(display = "function error: {}", why)]
+    Function { why: FunctionError },
 }
 
 /// The shell structure is a megastructure that manages all of the state of the shell throughout
@@ -106,7 +115,27 @@ pub struct Shell {
 pub struct ShellBuilder;
 
 impl ShellBuilder {
-    pub fn new() -> ShellBuilder { ShellBuilder }
+    pub fn as_binary(self) -> Shell { Shell::new(false) }
+
+    pub fn as_library(self) -> Shell { Shell::new(true) }
+
+    pub fn set_unique_pid(self) -> ShellBuilder {
+        if let Ok(pid) = sys::getpid() {
+            if sys::setpgid(0, pid).is_ok() {
+                let _ = sys::tcsetpgrp(0, pid);
+            }
+        }
+
+        self
+    }
+
+    pub fn block_signals(self) -> ShellBuilder {
+        // This will block SIGTSTP, SIGTTOU, SIGTTIN, and SIGCHLD, which is required
+        // for this shell to manage its own process group / children / etc.
+        signals::block();
+
+        self
+    }
 
     pub fn install_signal_handler(self) -> ShellBuilder {
         extern "C" fn handler(signal: i32) {
@@ -135,102 +164,90 @@ impl ShellBuilder {
         self
     }
 
-    pub fn block_signals(self) -> ShellBuilder {
-        // This will block SIGTSTP, SIGTTOU, SIGTTIN, and SIGCHLD, which is required
-        // for this shell to manage its own process group / children / etc.
-        signals::block();
+    pub fn new() -> ShellBuilder { ShellBuilder }
+}
 
-        self
+impl<'a> Shell {
+    /// A method for capturing the output of the shell, and performing actions without modifying
+    /// the state of the original shell. This performs a fork, taking a closure that controls
+    /// the shell in the child of the fork.
+    ///
+    /// The method is non-blocking, and therefore will immediately return file handles to the
+    /// stdout and stderr of the child. The PID of the child is returned, which may be used to
+    /// wait for and obtain the exit status.
+    pub fn fork<F: FnMut(&mut Shell)>(
+        &self,
+        capture: Capture,
+        child_func: F,
+    ) -> Result<IonResult, IonError> {
+        Fork::new(self, capture).exec(child_func)
     }
 
-    pub fn set_unique_pid(self) -> ShellBuilder {
-        if let Ok(pid) = sys::getpid() {
-            if sys::setpgid(0, pid).is_ok() {
-                let _ = sys::tcsetpgrp(0, pid);
-            }
-        }
-
-        self
+    /// A method for executing a function with the given `name`, using `args` as the input.
+    /// If the function does not exist, an `IonError::DoesNotExist` is returned.
+    pub fn execute_function(&mut self, name: &str, args: &[&str]) -> Result<i32, IonError> {
+        self.functions
+            .get_mut(name.into())
+            .ok_or(IonError::DoesNotExist)
+            .map(|fnc| fnc.clone())
+            .and_then(|function| {
+                function
+                    .execute(self, args)
+                    .map(|_| self.previous_status)
+                    .map_err(|err| IonError::Function { why: err })
+            })
     }
 
-    pub fn as_library(self) -> Shell { Shell::new(true) }
-
-    pub fn as_binary(self) -> Shell { Shell::new(false) }
-}
-
-impl<'a> Shell {
-    pub(crate) fn new(is_library: bool) -> Shell {
-        Shell {
-            builtins:            BUILTINS,
-            context:             None,
-            variables:           Variables::default(),
-            flow_control:        FlowControl::default(),
-            directory_stack:     DirectoryStack::new(),
-            functions:           FnvHashMap::default(),
-            previous_job:        !0,
-            previous_status:     0,
-            flags:               0,
-            background:          Arc::new(Mutex::new(Vec::new())),
-            is_background_shell: false,
-            is_library,
-            break_flow:          false,
-            foreground_signals:  Arc::new(ForegroundSignals::new()),
-            ignore_setting:      IgnoreSetting::default(),
+    /// A method for executing scripts in the Ion shell without capturing. Given a `Path`, this
+    /// method will attempt to execute that file as a script, and then returns the final exit
+    /// status of the evaluated script.
+    pub fn execute_script<SCRIPT: AsRef<Path>>(&mut self, script: SCRIPT) -> io::Result<i32> {
+        let mut script = File::open(script.as_ref())?;
+        let capacity = script.metadata().ok().map_or(0, |x| x.len());
+        let mut command_list = String::with_capacity(capacity as usize);
+        let _ = script.read_to_string(&mut command_list)?;
+        if FAILURE == self.terminate_script_quotes(command_list.lines().map(|x| x.to_owned())) {
+            self.previous_status = FAILURE;
         }
+        Ok(self.previous_status)
     }
 
-    pub(crate) fn next_signal(&self) -> Option<i32> {
-        match signals::PENDING.swap(0, Ordering::SeqCst) {
-            0 => None,
-            signals::SIGINT => Some(sys::SIGINT),
-            signals::SIGHUP => Some(sys::SIGHUP),
-            signals::SIGTERM => Some(sys::SIGTERM),
-            _ => unreachable!(),
+    /// A method for executing commands in the Ion shell without capturing. It takes command(s)
+    /// as
+    /// a string argument, parses them, and executes them the same as it would if you had
+    /// executed
+    /// the command(s) in the command line REPL interface for Ion. If the supplied command is
+    /// not
+    /// terminated, then an error will be returned.
+    pub fn execute_command<CMD>(&mut self, command: CMD) -> Result<i32, IonError>
+    where
+        CMD: Into<Terminator>,
+    {
+        let mut terminator = command.into();
+        if terminator.is_terminated() {
+            self.on_command(&terminator.consume());
+            Ok(self.previous_status)
+        } else {
+            Err(IonError::Unterminated)
         }
     }
 
-    pub(crate) fn prep_for_exit(&mut self) {
-        // The context has two purposes: if it exists, this is an interactive shell; and the
-        // context will also be sent a signal to commit all changes to the history file,
-        // and waiting for the history thread in the background to finish.
-        if self.context.is_some() {
-            if self.flags & HUPONEXIT != 0 {
-                self.resume_stopped();
-                self.background_send(sys::SIGHUP);
-            }
-            let context = self.context.as_mut().unwrap();
-            context.history.commit_history();
-        }
+    /// Gets an array variable, if it exists within the shell's array map.
+    pub fn get_array(&self, name: &str) -> Option<&[String]> {
+        self.variables.get_array(name).map(SmallVec::as_ref)
     }
 
-    /// Cleanly exit ion
-    pub fn exit(&mut self, status: i32) -> ! {
-        self.prep_for_exit();
-        process::exit(status);
+    /// Obtains a variable, returning an empty string if it does not exist.
+    pub(crate) fn get_var_or_empty(&self, name: &str) -> String {
+        self.variables.get_var_or_empty(name)
     }
 
-    /// Evaluates the source init file in the user's home directory.
-    pub fn evaluate_init_file(&mut self) {
-        let base_dirs = match BaseDirectories::with_prefix("ion") {
-            Ok(base_dirs) => base_dirs,
-            Err(err) => {
-                eprintln!("ion: unable to get base directory: {}", err);
-                return;
-            }
-        };
-        match base_dirs.find_config_file("initrc") {
-            Some(initrc) => {
-                if let Err(err) = self.execute_script(&initrc) {
-                    eprintln!("ion: {}", err);
-                }
-            }
-            None => {
-                if let Err(err) = base_dirs.place_config_file("initrc") {
-                    eprintln!("ion: could not create initrc file: {}", err);
-                }
-            }
-        }
-    }
+    /// Gets a string variable, if it exists within the shell's variable map.
+    pub fn get_var(&self, name: &str) -> Option<String> { self.variables.get_var(name) }
+
+    /// Sets a variable of `name` with the given `value` in the shell's
+    /// variable map.
+    pub fn set_var(&mut self, name: &str, value: &str) { self.variables.set_var(name, value); }
 
     /// Executes a pipeline and returns the final exit status of the pipeline.
     pub(crate) fn run_pipeline(&mut self, pipeline: &mut Pipeline) -> Option<i32> {
@@ -338,91 +355,115 @@ impl<'a> Shell {
         exit_status
     }
 
-    /// Sets a variable of `name` with the given `value` in the shell's
-    /// variable map.
-    pub fn set_var(&mut self, name: &str, value: &str) { self.variables.set_var(name, value); }
-
-    /// Gets a string variable, if it exists within the shell's variable map.
-    pub fn get_var(&self, name: &str) -> Option<String> { self.variables.get_var(name) }
-
-    /// Obtains a variable, returning an empty string if it does not exist.
-    pub(crate) fn get_var_or_empty(&self, name: &str) -> String {
-        self.variables.get_var_or_empty(name)
+    /// Evaluates the source init file in the user's home directory.
+    pub fn evaluate_init_file(&mut self) {
+        let base_dirs = match BaseDirectories::with_prefix("ion") {
+            Ok(base_dirs) => base_dirs,
+            Err(err) => {
+                eprintln!("ion: unable to get base directory: {}", err);
+                return;
+            }
+        };
+        match base_dirs.find_config_file("initrc") {
+            Some(initrc) => {
+                if let Err(err) = self.execute_script(&initrc) {
+                    eprintln!("ion: {}", err);
+                }
+            }
+            None => {
+                if let Err(err) = base_dirs.place_config_file("initrc") {
+                    eprintln!("ion: could not create initrc file: {}", err);
+                }
+            }
+        }
     }
 
-    /// Gets an array variable, if it exists within the shell's array map.
-    pub fn get_array(&self, name: &str) -> Option<&[String]> {
-        self.variables.get_array(name).map(SmallVec::as_ref)
+    /// Cleanly exit ion
+    pub fn exit(&mut self, status: i32) -> ! {
+        self.prep_for_exit();
+        process::exit(status);
     }
 
-    /// A method for executing commands in the Ion shell without capturing. It takes command(s)
-    /// as
-    /// a string argument, parses them, and executes them the same as it would if you had
-    /// executed
-    /// the command(s) in the command line REPL interface for Ion. If the supplied command is
-    /// not
-    /// terminated, then an error will be returned.
-    pub fn execute_command<CMD>(&mut self, command: CMD) -> Result<i32, IonError>
-    where
-        CMD: Into<Terminator>,
-    {
-        let mut terminator = command.into();
-        if terminator.is_terminated() {
-            self.on_command(&terminator.consume());
-            Ok(self.previous_status)
-        } else {
-            Err(IonError::Unterminated)
+    pub(crate) fn prep_for_exit(&mut self) {
+        // The context has two purposes: if it exists, this is an interactive shell; and the
+        // context will also be sent a signal to commit all changes to the history file,
+        // and waiting for the history thread in the background to finish.
+        if self.context.is_some() {
+            if self.flags & HUPONEXIT != 0 {
+                self.resume_stopped();
+                self.background_send(sys::SIGHUP);
+            }
+            let context = self.context.as_mut().unwrap();
+            context.history.commit_history();
         }
     }
 
-    /// A method for executing scripts in the Ion shell without capturing. Given a `Path`, this
-    /// method will attempt to execute that file as a script, and then returns the final exit
-    /// status of the evaluated script.
-    pub fn execute_script<SCRIPT: AsRef<Path>>(&mut self, script: SCRIPT) -> io::Result<i32> {
-        let mut script = File::open(script.as_ref())?;
-        let capacity = script.metadata().ok().map_or(0, |x| x.len());
-        let mut command_list = String::with_capacity(capacity as usize);
-        let _ = script.read_to_string(&mut command_list)?;
-        if FAILURE == self.terminate_script_quotes(command_list.lines().map(|x| x.to_owned())) {
-            self.previous_status = FAILURE;
+    pub(crate) fn next_signal(&self) -> Option<i32> {
+        match signals::PENDING.swap(0, Ordering::SeqCst) {
+            0 => None,
+            signals::SIGINT => Some(sys::SIGINT),
+            signals::SIGHUP => Some(sys::SIGHUP),
+            signals::SIGTERM => Some(sys::SIGTERM),
+            _ => unreachable!(),
         }
-        Ok(self.previous_status)
-    }
-
-    /// A method for executing a function with the given `name`, using `args` as the input.
-    /// If the function does not exist, an `IonError::DoesNotExist` is returned.
-    pub fn execute_function(&mut self, name: &str, args: &[&str]) -> Result<i32, IonError> {
-        self.functions
-            .get_mut(name.into())
-            .ok_or(IonError::DoesNotExist)
-            .map(|fnc| fnc.clone())
-            .and_then(|function| {
-                function
-                    .execute(self, args)
-                    .map(|_| self.previous_status)
-                    .map_err(|err| IonError::Function { why: err })
-            })
     }
 
-    /// A method for capturing the output of the shell, and performing actions without modifying
-    /// the state of the original shell. This performs a fork, taking a closure that controls
-    /// the shell in the child of the fork.
-    ///
-    /// The method is non-blocking, and therefore will immediately return file handles to the
-    /// stdout and stderr of the child. The PID of the child is returned, which may be used to
-    /// wait for and obtain the exit status.
-    pub fn fork<F: FnMut(&mut Shell)>(
-        &self,
-        capture: Capture,
-        child_func: F,
-    ) -> Result<IonResult, IonError> {
-        Fork::new(self, capture).exec(child_func)
+    pub(crate) fn new(is_library: bool) -> Shell {
+        Shell {
+            builtins: BUILTINS,
+            context: None,
+            variables: Variables::default(),
+            flow_control: FlowControl::default(),
+            directory_stack: DirectoryStack::new(),
+            functions: FnvHashMap::default(),
+            previous_job: !0,
+            previous_status: 0,
+            flags: 0,
+            background: Arc::new(Mutex::new(Vec::new())),
+            is_background_shell: false,
+            is_library,
+            break_flow: false,
+            foreground_signals: Arc::new(ForegroundSignals::new()),
+            ignore_setting: IgnoreSetting::default(),
+        }
     }
 }
 
 impl<'a> Expander for Shell {
-    fn tilde(&self, input: &str) -> Option<String> {
-        self.variables.tilde_expansion(input, &self.directory_stack)
+    /// Uses a subshell to expand a given command.
+    fn command(&self, command: &str) -> Option<Value> {
+        let mut output = None;
+        match self.fork(Capture::StdoutThenIgnoreStderr, move |shell| {
+            shell.on_command(command)
+        }) {
+            Ok(result) => {
+                let mut string = String::with_capacity(1024);
+                match result.stdout.unwrap().read_to_string(&mut string) {
+                    Ok(_) => output = Some(string),
+                    Err(why) => {
+                        eprintln!("ion: error reading stdout of child: {}", why);
+                    }
+                }
+            }
+            Err(why) => {
+                eprintln!("ion: fork error: {}", why);
+            }
+        }
+
+        // Ensure that the parent retains ownership of the terminal before exiting.
+        let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
+        output
+    }
+
+    /// Expand a string variable given if its quoted / unquoted
+    fn variable(&self, variable: &str, quoted: bool) -> Option<Value> {
+        use ascii_helpers::AsciiReplace;
+        if quoted {
+            self.get_var(variable)
+        } else {
+            self.get_var(variable)
+                .map(|x| x.ascii_replace('\n', ' ').into())
+        }
     }
 
     /// Expand an array variable with some selection
@@ -472,37 +513,8 @@ impl<'a> Expander for Shell {
         }
         found
     }
-    /// Expand a string variable given if its quoted / unquoted
-    fn variable(&self, variable: &str, quoted: bool) -> Option<Value> {
-        use ascii_helpers::AsciiReplace;
-        if quoted {
-            self.get_var(variable)
-        } else {
-            self.get_var(variable)
-                .map(|x| x.ascii_replace('\n', ' ').into())
-        }
-    }
 
-    /// Uses a subshell to expand a given command.
-    fn command(&self, command: &str) -> Option<Value> {
-        let mut output = None;
-        match self.fork(Capture::StdoutThenIgnoreStderr, move |shell| shell.on_command(command)) {
-            Ok(result) => {
-                let mut string = String::with_capacity(1024);
-                match result.stdout.unwrap().read_to_string(&mut string) {
-                    Ok(_) => output = Some(string),
-                    Err(why) => {
-                        eprintln!("ion: error reading stdout of child: {}", why);
-                    }
-                }
-            }
-            Err(why) => {
-                eprintln!("ion: fork error: {}", why);
-            }
-        }
-
-        // Ensure that the parent retains ownership of the terminal before exiting.
-        let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
-        output
+    fn tilde(&self, input: &str) -> Option<String> {
+        self.variables.tilde_expansion(input, &self.directory_stack)
     }
 }
diff --git a/src/lib/shell/pipe_exec/foreground.rs b/src/lib/shell/pipe_exec/foreground.rs
index ff76295d..8b860232 100644
--- a/src/lib/shell/pipe_exec/foreground.rs
+++ b/src/lib/shell/pipe_exec/foreground.rs
@@ -21,26 +21,7 @@ pub(crate) struct ForegroundSignals {
 }
 
 impl ForegroundSignals {
-    pub(crate) fn new() -> ForegroundSignals {
-        ForegroundSignals {
-            grab:   AtomicU32::new(0),
-            status: AtomicU8::new(0),
-            reply:  AtomicU8::new(0),
-        }
-    }
-
-    pub(crate) fn signal_to_grab(&self, pid: u32) { self.grab.store(pid, Ordering::Relaxed); }
-
-    pub(crate) fn reply_with(&self, status: i8) {
-        self.grab.store(0, Ordering::Relaxed);
-        self.status.store(status as u8, Ordering::Relaxed);
-        self.reply.store(REPLIED, Ordering::Relaxed);
-    }
-
-    pub(crate) fn errored(&self) {
-        self.grab.store(0, Ordering::Relaxed);
-        self.reply.store(ERRORED, Ordering::Relaxed);
-    }
+    pub(crate) fn was_grabbed(&self, pid: u32) -> bool { self.grab.load(Ordering::Relaxed) == pid }
 
     pub(crate) fn was_processed(&self) -> Option<BackgroundResult> {
         let reply = self.reply.load(Ordering::Relaxed);
@@ -56,5 +37,24 @@ impl ForegroundSignals {
         }
     }
 
-    pub(crate) fn was_grabbed(&self, pid: u32) -> bool { self.grab.load(Ordering::Relaxed) == pid }
+    pub(crate) fn errored(&self) {
+        self.grab.store(0, Ordering::Relaxed);
+        self.reply.store(ERRORED, Ordering::Relaxed);
+    }
+
+    pub(crate) fn reply_with(&self, status: i8) {
+        self.grab.store(0, Ordering::Relaxed);
+        self.status.store(status as u8, Ordering::Relaxed);
+        self.reply.store(REPLIED, Ordering::Relaxed);
+    }
+
+    pub(crate) fn signal_to_grab(&self, pid: u32) { self.grab.store(pid, Ordering::Relaxed); }
+
+    pub(crate) fn new() -> ForegroundSignals {
+        ForegroundSignals {
+            grab:   AtomicU32::new(0),
+            status: AtomicU8::new(0),
+            reply:  AtomicU8::new(0),
+        }
+    }
 }
diff --git a/src/lib/shell/pipe_exec/fork.rs b/src/lib/shell/pipe_exec/fork.rs
index 17e40b0c..8a6066fb 100644
--- a/src/lib/shell/pipe_exec/fork.rs
+++ b/src/lib/shell/pipe_exec/fork.rs
@@ -3,11 +3,15 @@ use sys;
 /// Ensures that the forked child is given a unique process ID.
 pub(crate) fn create_process_group(pgid: u32) { let _ = sys::setpgid(0, pgid); }
 
-use super::job_control::{JobControl, ProcessState};
-use super::pipe;
-use super::super::Shell;
-use super::super::job::{JobKind, RefinedJob};
-use super::super::status::*;
+use super::{
+    super::{
+        job::{JobKind, RefinedJob},
+        status::*,
+        Shell,
+    },
+    job_control::{JobControl, ProcessState},
+    pipe,
+};
 use std::process::exit;
 
 /// Forks the shell, adding the child to the parent's background list, and executing
diff --git a/src/lib/shell/pipe_exec/job_control.rs b/src/lib/shell/pipe_exec/job_control.rs
index b2d26a3f..079a2efd 100644
--- a/src/lib/shell/pipe_exec/job_control.rs
+++ b/src/lib/shell/pipe_exec/job_control.rs
@@ -1,12 +1,14 @@
-use super::foreground::BackgroundResult;
-use super::super::Shell;
-use super::super::signals;
-use super::super::status::*;
-use std::fmt;
-use std::process;
-use std::sync::{Arc, Mutex};
-use std::thread::{sleep, spawn};
-use std::time::Duration;
+use super::{
+    super::{signals, status::*, Shell},
+    foreground::BackgroundResult,
+};
+use std::{
+    fmt,
+    process,
+    sync::{Arc, Mutex},
+    thread::{sleep, spawn},
+    time::Duration,
+};
 use sys;
 
 use sys::job_control as self_sys;
@@ -67,9 +69,9 @@ pub(crate) fn add_to_background(
     {
         Some(id) => {
             (*processes)[id] = BackgroundProcess {
-                pid:           pid,
+                pid,
                 ignore_sighup: false,
-                state:         state,
+                state,
                 name:          command,
             };
             id as u32
@@ -77,9 +79,9 @@ pub(crate) fn add_to_background(
         None => {
             let njobs = (*processes).len();
             (*processes).push(BackgroundProcess {
-                pid:           pid,
+                pid,
                 ignore_sighup: false,
-                state:         state,
+                state,
                 name:          command,
             });
             njobs as u32
@@ -100,64 +102,35 @@ pub struct BackgroundProcess {
 }
 
 impl JobControl for Shell {
-    fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 {
-        // Resume the background task, if needed.
-        if cont {
-            signals::resume(pid);
+    /// If a SIGTERM is received, a SIGTERM will be sent to all background processes
+    /// before the shell terminates itself.
+    fn handle_signal(&self, signal: i32) -> bool {
+        if signal == sys::SIGTERM || signal == sys::SIGHUP {
+            self.background_send(signal);
+            true
+        } else {
+            false
         }
-        // Pass the TTY to the background job
-        set_foreground_as(pid);
-        // Signal the background thread that is waiting on this process to stop waiting.
-        self.foreground_signals.signal_to_grab(pid);
-        let status = loop {
-            // When the background thread that is monitoring the task receives an exit/stop
-            // signal, the status of that process will be communicated back. To
-            // avoid consuming CPU cycles, we wait 25 ms between polls.
-            match self.foreground_signals.was_processed() {
-                Some(BackgroundResult::Status(stat)) => break stat as i32,
-                Some(BackgroundResult::Errored) => break TERMINATED,
-                None => sleep(Duration::from_millis(25)),
-            }
-        };
-        // Have the shell reclaim the TTY
-        set_foreground_as(process::id());
-        status
     }
 
-    /// Waits until all running background tasks have completed, and listens for signals in the
-    /// event that a signal is sent to kill the running tasks.
-    fn wait_for_background(&mut self) {
-        let sigcode;
-        'event: loop {
-            for process in self.background.lock().unwrap().iter() {
-                if let ProcessState::Running = process.state {
-                    while let Some(signal) = self.next_signal() {
-                        if signal != sys::SIGTSTP {
-                            self.background_send(signal);
-                            sigcode = get_signal_code(signal);
-                            break 'event;
-                        }
-                    }
-                    sleep(Duration::from_millis(100));
-                    continue 'event;
-                }
-            }
-            return;
-        }
-        self.exit(sigcode);
-    }
+    fn send_to_background(&mut self, pid: u32, state: ProcessState, command: String) {
+        // Increment the `Arc` counters so that these fields can be moved into
+        // the upcoming background thread.
+        let processes = self.background.clone();
+        let fg_signals = self.foreground_signals.clone();
 
-    fn watch_foreground(&mut self, pid: i32, command: &str) -> i32 {
-        self_sys::watch_foreground(self, pid, command)
-    }
+        // Add the process to the background list, and mark the job's ID as
+        // the previous job in the shell (in case fg/bg is executed w/ no args).
+        let njob = add_to_background(processes.clone(), pid, state, command);
+        self.previous_job = njob;
+        eprintln!("ion: bg [{}] {}", njob, pid);
 
-    /// Resumes all stopped background jobs
-    fn resume_stopped(&mut self) {
-        for process in self.background.lock().unwrap().iter() {
-            if process.state == ProcessState::Stopped {
-                signals::resume(process.pid);
-            }
-        }
+        // Spawn a background thread that will monitor the progress of the
+        // background process, updating it's state changes until it finally
+        // exits.
+        let _ = spawn(move || {
+            watch_background(fg_signals, processes, pid, njob as usize);
+        });
     }
 
     /// Send a kill signal to all running background tasks.
@@ -177,34 +150,63 @@ impl JobControl for Shell {
         }
     }
 
-    fn send_to_background(&mut self, pid: u32, state: ProcessState, command: String) {
-        // Increment the `Arc` counters so that these fields can be moved into
-        // the upcoming background thread.
-        let processes = self.background.clone();
-        let fg_signals = self.foreground_signals.clone();
+    /// Resumes all stopped background jobs
+    fn resume_stopped(&mut self) {
+        for process in self.background.lock().unwrap().iter() {
+            if process.state == ProcessState::Stopped {
+                signals::resume(process.pid);
+            }
+        }
+    }
 
-        // Add the process to the background list, and mark the job's ID as
-        // the previous job in the shell (in case fg/bg is executed w/ no args).
-        let njob = add_to_background(processes.clone(), pid, state, command);
-        self.previous_job = njob;
-        eprintln!("ion: bg [{}] {}", njob, pid);
+    fn watch_foreground(&mut self, pid: i32, command: &str) -> i32 {
+        self_sys::watch_foreground(self, pid, command)
+    }
 
-        // Spawn a background thread that will monitor the progress of the
-        // background process, updating it's state changes until it finally
-        // exits.
-        let _ = spawn(move || {
-            watch_background(fg_signals, processes, pid, njob as usize);
-        });
+    /// Waits until all running background tasks have completed, and listens for signals in the
+    /// event that a signal is sent to kill the running tasks.
+    fn wait_for_background(&mut self) {
+        let sigcode;
+        'event: loop {
+            for process in self.background.lock().unwrap().iter() {
+                if let ProcessState::Running = process.state {
+                    while let Some(signal) = self.next_signal() {
+                        if signal != sys::SIGTSTP {
+                            self.background_send(signal);
+                            sigcode = get_signal_code(signal);
+                            break 'event;
+                        }
+                    }
+                    sleep(Duration::from_millis(100));
+                    continue 'event;
+                }
+            }
+            return;
+        }
+        self.exit(sigcode);
     }
 
-    /// If a SIGTERM is received, a SIGTERM will be sent to all background processes
-    /// before the shell terminates itself.
-    fn handle_signal(&self, signal: i32) -> bool {
-        if signal == sys::SIGTERM || signal == sys::SIGHUP {
-            self.background_send(signal);
-            true
-        } else {
-            false
+    fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 {
+        // Resume the background task, if needed.
+        if cont {
+            signals::resume(pid);
         }
+        // Pass the TTY to the background job
+        set_foreground_as(pid);
+        // Signal the background thread that is waiting on this process to stop waiting.
+        self.foreground_signals.signal_to_grab(pid);
+        let status = loop {
+            // When the background thread that is monitoring the task receives an exit/stop
+            // signal, the status of that process will be communicated back. To
+            // avoid consuming CPU cycles, we wait 25 ms between polls.
+            match self.foreground_signals.was_processed() {
+                Some(BackgroundResult::Status(stat)) => break stat as i32,
+                Some(BackgroundResult::Errored) => break TERMINATED,
+                None => sleep(Duration::from_millis(25)),
+            }
+        };
+        // Have the shell reclaim the TTY
+        set_foreground_as(process::id());
+        status
     }
 }
diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs
index 9af781d7..dc82fb42 100644
--- a/src/lib/shell/pipe_exec/mod.rs
+++ b/src/lib/shell/pipe_exec/mod.rs
@@ -5,29 +5,36 @@
 //! the background, handling pipeline and conditional operators, and
 //! std{in,out,err} redirections.
 
-mod fork;
-mod streams;
 pub mod foreground;
+mod fork;
 pub mod job_control;
+mod streams;
 
+use self::{
+    fork::fork_pipe,
+    job_control::{JobControl, ProcessState},
+    streams::{duplicate_streams, redir, redirect_streams},
+};
+use super::{
+    flags::*,
+    flow_control::FunctionError,
+    fork_function::command_not_found,
+    job::{RefinedJob, TeeItem},
+    signals::{self, SignalHandler},
+    status::*,
+    JobKind,
+    Shell,
+};
 use builtins::{self, BuiltinFunction};
 use parser::pipelines::{Input, PipeItem, Pipeline, RedirectFrom, Redirection};
-use self::fork::fork_pipe;
-use self::job_control::{JobControl, ProcessState};
-use self::streams::{duplicate_streams, redir, redirect_streams};
-use std::fs::{File, OpenOptions};
-use std::io::{self, Error, Write};
-use std::iter;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
-use std::path::Path;
-use std::process::{self, exit};
-use super::flags::*;
-use super::flow_control::FunctionError;
-use super::fork_function::command_not_found;
-use super::job::{RefinedJob, TeeItem};
-use super::signals::{self, SignalHandler};
-use super::status::*;
-use super::{JobKind, Shell};
+use std::{
+    fs::{File, OpenOptions},
+    io::{self, Error, Write},
+    iter,
+    os::unix::io::{AsRawFd, FromRawFd, RawFd},
+    path::Path,
+    process::{self, exit},
+};
 use sys;
 
 type RefinedItem = (RefinedJob, JobKind, Vec<Redirection>, Vec<Input>);
@@ -99,14 +106,16 @@ fn do_redirection(piped_commands: Vec<RefinedItem>) -> Option<Vec<(RefinedJob, J
                     match unsafe { stdin_of(&string) } {
                         Ok(stdio) => Some(unsafe { File::from_raw_fd(stdio) }),
                         Err(e) => {
-                            eprintln!("ion: failed to redirect herestring '{}' to stdin: {}",
-                                      string, e);
+                            eprintln!(
+                                "ion: failed to redirect herestring '{}' to stdin: {}",
+                                string, e
+                            );
                             None
                         }
                     }
                 }
             }
-        }
+        };
     }
 
     let need_tee = |outs: &[_], kind| {
@@ -142,7 +151,11 @@ fn do_redirection(piped_commands: Vec<RefinedItem>) -> Option<Vec<(RefinedJob, J
             // XXX: Possibly add an assertion here for correctness
             for output in $outputs {
                 match if output.append {
-                    OpenOptions::new().create(true).write(true).append(true).open(&output.file)
+                    OpenOptions::new()
+                        .create(true)
+                        .write(true)
+                        .append(true)
+                        .open(&output.file)
                 } else {
                     File::create(&output.file)
                 } {
@@ -153,15 +166,16 @@ fn do_redirection(piped_commands: Vec<RefinedItem>) -> Option<Vec<(RefinedJob, J
                             Ok(f_copy) => {
                                 $job.stdout(f);
                                 $job.stderr(f_copy);
-                            },
+                            }
                             Err(e) => {
                                 eprintln!(
-                                    "ion: failed to redirect both stdout and stderr to file '{:?}': {}",
-                                    f,
-                                    e);
+                                    "ion: failed to redirect both stdout and stderr to file \
+                                     '{:?}': {}",
+                                    f, e
+                                );
                                 return None;
                             }
-                        }
+                        },
                     },
                     Err(e) => {
                         eprintln!("ion: failed to redirect output into {}: {}", output.file, e);
@@ -169,15 +183,22 @@ fn do_redirection(piped_commands: Vec<RefinedItem>) -> Option<Vec<(RefinedJob, J
                     }
                 }
             }
-        }
+        };
     }
 
     macro_rules! set_one_tee {
         ($new:ident, $outputs:ident, $job:ident, $kind:ident, $teed:ident, $other:ident) => {{
-            let mut tee = TeeItem { sinks: Vec::new(), source: None };
+            let mut tee = TeeItem {
+                sinks:  Vec::new(),
+                source: None,
+            };
             for output in $outputs {
                 match if output.append {
-                    OpenOptions::new().create(true).write(true).append(true).open(&output.file)
+                    OpenOptions::new()
+                        .create(true)
+                        .write(true)
+                        .append(true)
+                        .open(&output.file)
                 } else {
                     File::create(&output.file)
                 } {
@@ -196,15 +217,15 @@ fn do_redirection(piped_commands: Vec<RefinedItem>) -> Option<Vec<(RefinedJob, J
                                     $job.stdout(f);
                                 }
                                 tee.sinks.push(f_copy);
-                            },
+                            }
                             Err(e) => {
                                 eprintln!(
                                     "ion: failed to redirect both stdout and stderr to file '{:?}': {}",
-                                    f,
-                                    e);
+                                    f, e
+                                );
                                 return None;
                             }
-                        }
+                        },
                     },
                     Err(e) => {
                         eprintln!("ion: failed to redirect output into {}: {}", output.file, e);
@@ -220,7 +241,7 @@ fn do_redirection(piped_commands: Vec<RefinedItem>) -> Option<Vec<(RefinedJob, J
             };
             let tee = RefinedJob::tee(items.0, items.1);
             $new.push((tee, $kind));
-        }}
+        }};
     }
 
     // Real logic begins here
@@ -410,88 +431,198 @@ pub(crate) trait PipelineExecution {
 }
 
 impl PipelineExecution for Shell {
-    fn execute_pipeline(&mut self, pipeline: &mut Pipeline) -> i32 {
-        // If the supplied pipeline is a background, a string representing the command
-        // and a boolean representing whether it should be disowned is stored here.
-        let possible_background_name =
-            gen_background_string(&pipeline, self.flags & PRINT_COMMS != 0);
-        // Generates commands for execution, differentiating between external and
-        // builtin commands.
-        let piped_commands = match self.generate_commands(pipeline) {
-            Ok(commands) => commands,
-            Err(error) => return error,
-        };
+    fn exec_external(
+        &mut self,
+        name: &str,
+        args: &[&str],
+        stdin: &Option<File>,
+        stdout: &Option<File>,
+        stderr: &Option<File>,
+    ) -> i32 {
+        let result = sys::fork_and_exec(
+            name,
+            &args,
+            if let Some(ref f) = *stdin {
+                Some(f.as_raw_fd())
+            } else {
+                None
+            },
+            if let Some(ref f) = *stdout {
+                Some(f.as_raw_fd())
+            } else {
+                None
+            },
+            if let Some(ref f) = *stderr {
+                Some(f.as_raw_fd())
+            } else {
+                None
+            },
+            false,
+            || prepare_child(true, 0),
+        );
 
-        // Don't execute commands when the `-n` flag is passed.
-        if self.flags & NO_EXEC != 0 {
-            return SUCCESS;
+        match result {
+            Ok(pid) => {
+                let _ = sys::setpgid(pid, pid);
+                let _ = sys::tcsetpgrp(0, pid);
+                let _ = sys::wait_for_interrupt(pid);
+                let _ = sys::kill(pid, sys::SIGCONT);
+                self.watch_foreground(-(pid as i32), "")
+            }
+            Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
+                if !command_not_found(self, &name) {
+                    eprintln!("ion: command not found: {}", name);
+                }
+                NO_SUCH_COMMAND
+            }
+            Err(ref err) => {
+                eprintln!("ion: command exec error: {}", err);
+                FAILURE
+            }
         }
+    }
 
-        let piped_commands = match do_redirection(piped_commands) {
-            Some(c) => c,
-            None => return COULD_NOT_EXEC
+    fn exec_multi_out(
+        &mut self,
+        items: &mut (Option<TeeItem>, Option<TeeItem>),
+        stdout: &Option<File>,
+        stderr: &Option<File>,
+        stdin: &Option<File>,
+        kind: JobKind,
+    ) -> i32 {
+        if let Some(ref file) = *stdin {
+            redir(file.as_raw_fd(), sys::STDIN_FILENO);
+        }
+        if let Some(ref file) = *stdout {
+            redir(file.as_raw_fd(), sys::STDOUT_FILENO);
+        }
+        if let Some(ref file) = *stderr {
+            redir(file.as_raw_fd(), sys::STDERR_FILENO);
+        }
+        let res = match items {
+            &mut (None, None) => panic!("There must be at least one TeeItem, this is a bug"),
+            &mut (Some(ref mut tee_out), None) => match kind {
+                JobKind::Pipe(RedirectFrom::Stderr) => tee_out.write_to_all(None),
+                JobKind::Pipe(_) => tee_out.write_to_all(Some(RedirectFrom::Stdout)),
+                _ => tee_out.write_to_all(None),
+            },
+            &mut (None, Some(ref mut tee_err)) => match kind {
+                JobKind::Pipe(RedirectFrom::Stdout) => tee_err.write_to_all(None),
+                JobKind::Pipe(_) => tee_err.write_to_all(Some(RedirectFrom::Stderr)),
+                _ => tee_err.write_to_all(None),
+            },
+            &mut (Some(ref mut tee_out), Some(ref mut tee_err)) => {
+                // TODO Make it work with pipes
+                if let Err(e) = tee_out.write_to_all(None) {
+                    Err(e)
+                } else {
+                    tee_err.write_to_all(None)
+                }
+            }
         };
-
-        // If the given pipeline is a background task, fork the shell.
-        if let Some((command_name, disown)) = possible_background_name {
-            fork_pipe(
-                self,
-                piped_commands,
-                command_name,
-                if disown { ProcessState::Empty } else { ProcessState::Running }
-            )
+        if let Err(e) = res {
+            eprintln!("ion: error in multiple output redirection process: {:?}", e);
+            FAILURE
         } else {
-            // While active, the SIGTTOU signal will be ignored.
-            let _sig_ignore = SignalHandler::new();
-            let foreground = !self.is_background_shell;
-            // Execute each command in the pipeline, giving each command the foreground.
-            let exit_status = pipe(self, piped_commands, foreground);
-            // Set the shell as the foreground process again to regain the TTY.
-            if foreground && !self.is_library {
-                let _ = sys::tcsetpgrp(0, process::id());
-            }
-            exit_status
+            SUCCESS
         }
     }
 
-    fn generate_commands(&self, pipeline: &mut Pipeline) -> Result<Vec<RefinedItem>, i32> {
-        let mut results = Vec::new();
-        for item in pipeline.items.drain(..) {
-            let PipeItem {
-                mut job,
-                outputs,
-                inputs,
-            } = item;
-            let refined = {
-                if is_implicit_cd(&job.args[0]) {
-                    RefinedJob::builtin(
-                        builtins::builtin_cd,
-                        iter::once("cd".into()).chain(job.args.drain()).collect(),
-                    )
-                } else if self.functions.contains_key(job.args[0].as_str()) {
-                    RefinedJob::function(job.args[0].clone().into(), job.args.drain().collect())
-                } else if let Some(builtin) = job.builtin {
-                    RefinedJob::builtin(builtin, job.args.drain().collect())
-                } else {
-                    RefinedJob::external(job.args[0].clone().into(), job.args.drain().collect())
+    fn exec_multi_in(
+        &mut self,
+        sources: &mut [File],
+        stdout: &Option<File>,
+        stdin: &mut Option<File>,
+    ) -> i32 {
+        if let Some(ref file) = *stdin {
+            redir(file.as_raw_fd(), sys::STDIN_FILENO)
+        }
+        if let Some(ref file) = *stdout {
+            redir(file.as_raw_fd(), sys::STDOUT_FILENO)
+        }
+
+        fn read_and_write<R: io::Read>(src: &mut R, stdout: &mut io::StdoutLock) -> io::Result<()> {
+            let mut buf = [0; 4096];
+            loop {
+                let len = src.read(&mut buf)?;
+                if len == 0 {
+                    return Ok(());
+                };
+                let mut total = 0;
+                loop {
+                    let wrote = stdout.write(&buf[total..len])?;
+                    total += wrote;
+                    if total == len {
+                        break;
+                    }
                 }
-            };
-            results.push((refined, job.kind, outputs, inputs));
+            }
+        };
+        let stdout = io::stdout();
+        let mut stdout = stdout.lock();
+        for file in stdin.iter_mut().chain(sources) {
+            if let Err(why) = read_and_write(file, &mut stdout) {
+                eprintln!("ion: error in multiple input redirect process: {:?}", why);
+                return FAILURE;
+            }
         }
+        SUCCESS
+    }
 
-        Ok(results)
+    fn exec_function(
+        &mut self,
+        name: &str,
+        args: &[&str],
+        stdout: &Option<File>,
+        stderr: &Option<File>,
+        stdin: &Option<File>,
+    ) -> i32 {
+        if let Some(ref file) = *stdin {
+            redir(file.as_raw_fd(), sys::STDIN_FILENO);
+        }
+        if let Some(ref file) = *stdout {
+            redir(file.as_raw_fd(), sys::STDOUT_FILENO);
+        }
+        if let Some(ref file) = *stderr {
+            redir(file.as_raw_fd(), sys::STDERR_FILENO);
+        }
+
+        let function = self.functions.get(name).cloned().unwrap();
+        match function.execute(self, args) {
+            Ok(()) => SUCCESS,
+            Err(FunctionError::InvalidArgumentCount) => {
+                eprintln!("ion: invalid number of function arguments supplied");
+                FAILURE
+            }
+            Err(FunctionError::InvalidArgumentType(expected_type, value)) => {
+                eprintln!(
+                    "ion: function argument has invalid type: expected {}, found value \'{}\'",
+                    expected_type, value
+                );
+                FAILURE
+            }
+        }
     }
 
-    fn wait(&mut self, pgid: u32, commands: Vec<RefinedJob>) -> i32 {
-        // TODO: Find a way to only do this when absolutely necessary.
-        let as_string = commands
-            .iter()
-            .map(RefinedJob::long)
-            .collect::<Vec<String>>()
-            .join(" | ");
+    fn exec_builtin(
+        &mut self,
+        main: BuiltinFunction,
+        args: &[&str],
+        stdout: &Option<File>,
+        stderr: &Option<File>,
+        stdin: &Option<File>,
+    ) -> i32 {
+        if let Some(ref file) = *stdin {
+            redir(file.as_raw_fd(), sys::STDIN_FILENO);
+        }
+        if let Some(ref file) = *stdout {
+            redir(file.as_raw_fd(), sys::STDOUT_FILENO);
+        }
+        if let Some(ref file) = *stderr {
+            redir(file.as_raw_fd(), sys::STDERR_FILENO);
+        }
 
-        // Watch the foreground group, dropping all commands that exit as they exit.
-        self.watch_foreground(-(pgid as i32), &as_string)
+        main(args, self)
     }
 
     fn exec_job(&mut self, job: &mut RefinedJob, _foreground: bool) -> i32 {
@@ -558,207 +689,117 @@ impl PipelineExecution for Shell {
         }
     }
 
-    fn exec_builtin(
-        &mut self,
-        main: BuiltinFunction,
-        args: &[&str],
-        stdout: &Option<File>,
-        stderr: &Option<File>,
-        stdin: &Option<File>,
-    ) -> i32 {
-        if let Some(ref file) = *stdin {
-            redir(file.as_raw_fd(), sys::STDIN_FILENO);
-        }
-        if let Some(ref file) = *stdout {
-            redir(file.as_raw_fd(), sys::STDOUT_FILENO);
-        }
-        if let Some(ref file) = *stderr {
-            redir(file.as_raw_fd(), sys::STDERR_FILENO);
-        }
+    fn wait(&mut self, pgid: u32, commands: Vec<RefinedJob>) -> i32 {
+        // TODO: Find a way to only do this when absolutely necessary.
+        let as_string = commands
+            .iter()
+            .map(RefinedJob::long)
+            .collect::<Vec<String>>()
+            .join(" | ");
 
-        main(args, self)
+        // Watch the foreground group, dropping all commands that exit as they exit.
+        self.watch_foreground(-(pgid as i32), &as_string)
     }
 
-    fn exec_function(
-        &mut self,
-        name: &str,
-        args: &[&str],
-        stdout: &Option<File>,
-        stderr: &Option<File>,
-        stdin: &Option<File>,
-    ) -> i32 {
-        if let Some(ref file) = *stdin {
-            redir(file.as_raw_fd(), sys::STDIN_FILENO);
-        }
-        if let Some(ref file) = *stdout {
-            redir(file.as_raw_fd(), sys::STDOUT_FILENO);
-        }
-        if let Some(ref file) = *stderr {
-            redir(file.as_raw_fd(), sys::STDERR_FILENO);
+    fn generate_commands(&self, pipeline: &mut Pipeline) -> Result<Vec<RefinedItem>, i32> {
+        let mut results = Vec::new();
+        for item in pipeline.items.drain(..) {
+            let PipeItem {
+                mut job,
+                outputs,
+                inputs,
+            } = item;
+            let refined = {
+                if is_implicit_cd(&job.args[0]) {
+                    RefinedJob::builtin(
+                        builtins::builtin_cd,
+                        iter::once("cd".into()).chain(job.args.drain()).collect(),
+                    )
+                } else if self.functions.contains_key(job.args[0].as_str()) {
+                    RefinedJob::function(job.args[0].clone().into(), job.args.drain().collect())
+                } else if let Some(builtin) = job.builtin {
+                    RefinedJob::builtin(builtin, job.args.drain().collect())
+                } else {
+                    RefinedJob::external(job.args[0].clone().into(), job.args.drain().collect())
+                }
+            };
+            results.push((refined, job.kind, outputs, inputs));
         }
 
-        let function = self.functions.get(name).cloned().unwrap();
-        match function.execute(self, args) {
-            Ok(()) => SUCCESS,
-            Err(FunctionError::InvalidArgumentCount) => {
-                eprintln!("ion: invalid number of function arguments supplied");
-                FAILURE
-            }
-            Err(FunctionError::InvalidArgumentType(expected_type, value)) => {
-                eprintln!(
-                    "ion: function argument has invalid type: expected {}, found value \'{}\'",
-                    expected_type, value
-                );
-                FAILURE
-            }
-        }
+        Ok(results)
     }
 
-    fn exec_multi_in(
-        &mut self,
-        sources: &mut [File],
-        stdout: &Option<File>,
-        stdin: &mut Option<File>,
-    ) -> i32 {
-        if let Some(ref file) = *stdin {
-            redir(file.as_raw_fd(), sys::STDIN_FILENO)
-        }
-        if let Some(ref file) = *stdout {
-            redir(file.as_raw_fd(), sys::STDOUT_FILENO)
+    fn execute_pipeline(&mut self, pipeline: &mut Pipeline) -> i32 {
+        // If the supplied pipeline is a background, a string representing the command
+        // and a boolean representing whether it should be disowned is stored here.
+        let possible_background_name =
+            gen_background_string(&pipeline, self.flags & PRINT_COMMS != 0);
+        // Generates commands for execution, differentiating between external and
+        // builtin commands.
+        let piped_commands = match self.generate_commands(pipeline) {
+            Ok(commands) => commands,
+            Err(error) => return error,
+        };
+
+        // Don't execute commands when the `-n` flag is passed.
+        if self.flags & NO_EXEC != 0 {
+            return SUCCESS;
         }
 
-        fn read_and_write<R: io::Read>(src: &mut R, stdout: &mut io::StdoutLock) -> io::Result<()> {
-            let mut buf = [0; 4096];
-            loop {
-                let len = src.read(&mut buf)?;
-                if len == 0 {
-                    return Ok(());
-                };
-                let mut total = 0;
-                loop {
-                    let wrote = stdout.write(&buf[total..len])?;
-                    total += wrote;
-                    if total == len {
-                        break;
-                    }
-                }
-            }
+        let piped_commands = match do_redirection(piped_commands) {
+            Some(c) => c,
+            None => return COULD_NOT_EXEC,
         };
-        let stdout = io::stdout();
-        let mut stdout = stdout.lock();
-        for file in stdin.iter_mut().chain(sources) {
-            if let Err(why) = read_and_write(file, &mut stdout) {
-                eprintln!("ion: error in multiple input redirect process: {:?}", why);
-                return FAILURE;
-            }
-        }
-        SUCCESS
-    }
 
-    fn exec_multi_out(
-        &mut self,
-        items: &mut (Option<TeeItem>, Option<TeeItem>),
-        stdout: &Option<File>,
-        stderr: &Option<File>,
-        stdin: &Option<File>,
-        kind: JobKind,
-    ) -> i32 {
-        if let Some(ref file) = *stdin {
-            redir(file.as_raw_fd(), sys::STDIN_FILENO);
-        }
-        if let Some(ref file) = *stdout {
-            redir(file.as_raw_fd(), sys::STDOUT_FILENO);
-        }
-        if let Some(ref file) = *stderr {
-            redir(file.as_raw_fd(), sys::STDERR_FILENO);
-        }
-        let res = match items {
-            &mut (None, None) => panic!("There must be at least one TeeItem, this is a bug"),
-            &mut (Some(ref mut tee_out), None) => match kind {
-                JobKind::Pipe(RedirectFrom::Stderr) => tee_out.write_to_all(None),
-                JobKind::Pipe(_) => tee_out.write_to_all(Some(RedirectFrom::Stdout)),
-                _ => tee_out.write_to_all(None),
-            },
-            &mut (None, Some(ref mut tee_err)) => match kind {
-                JobKind::Pipe(RedirectFrom::Stdout) => tee_err.write_to_all(None),
-                JobKind::Pipe(_) => tee_err.write_to_all(Some(RedirectFrom::Stderr)),
-                _ => tee_err.write_to_all(None),
-            },
-            &mut (Some(ref mut tee_out), Some(ref mut tee_err)) => {
-                // TODO Make it work with pipes
-                if let Err(e) = tee_out.write_to_all(None) {
-                    Err(e)
+        // If the given pipeline is a background task, fork the shell.
+        if let Some((command_name, disown)) = possible_background_name {
+            fork_pipe(
+                self,
+                piped_commands,
+                command_name,
+                if disown {
+                    ProcessState::Empty
                 } else {
-                    tee_err.write_to_all(None)
-                }
-            }
-        };
-        if let Err(e) = res {
-            eprintln!("ion: error in multiple output redirection process: {:?}", e);
-            FAILURE
+                    ProcessState::Running
+                },
+            )
         } else {
-            SUCCESS
-        }
-    }
-
-    fn exec_external(
-        &mut self,
-        name: &str,
-        args: &[&str],
-        stdin: &Option<File>,
-        stdout: &Option<File>,
-        stderr: &Option<File>,
-    ) -> i32 {
-        let result = sys::fork_and_exec(
-            name,
-            &args,
-            if let Some(ref f) = *stdin { Some(f.as_raw_fd()) } else { None },
-            if let Some(ref f) = *stdout { Some(f.as_raw_fd()) } else { None },
-            if let Some(ref f) = *stderr { Some(f.as_raw_fd()) } else { None },
-            false,
-            || prepare_child(true, 0)
-        );
-
-        match result {
-            Ok(pid) => {
-                let _ = sys::setpgid(pid, pid);
-                let _ = sys::tcsetpgrp(0, pid);
-                let _ = sys::wait_for_interrupt(pid);
-                let _ = sys::kill(pid, sys::SIGCONT);
-                self.watch_foreground(-(pid as i32), "")
-            }
-            Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
-                if !command_not_found(self, &name) {
-                    eprintln!("ion: command not found: {}", name);
-                }
-                NO_SUCH_COMMAND
-            }
-            Err(ref err) => {
-                eprintln!("ion: command exec error: {}", err);
-                FAILURE
+            // While active, the SIGTTOU signal will be ignored.
+            let _sig_ignore = SignalHandler::new();
+            let foreground = !self.is_background_shell;
+            // Execute each command in the pipeline, giving each command the foreground.
+            let exit_status = pipe(self, piped_commands, foreground);
+            // Set the shell as the foreground process again to regain the TTY.
+            if foreground && !self.is_library {
+                let _ = sys::tcsetpgrp(0, process::id());
             }
+            exit_status
         }
     }
 }
 
 /// When the `&&` or `||` operator is utilized, commands should be executed
 /// based on the previously-recorded exit status. This function will return
-/// **true** to indicate that the current job should be skipped. 
-fn should_skip(
-    previous: &mut JobKind,
-    previous_status: i32,
-    current: JobKind,
-) -> bool {
+/// **true** to indicate that the current job should be skipped.
+fn should_skip(previous: &mut JobKind, previous_status: i32, current: JobKind) -> bool {
     match *previous {
         JobKind::And if previous_status != SUCCESS => {
-            *previous = if JobKind::Or == current { current } else { *previous };
+            *previous = if JobKind::Or == current {
+                current
+            } else {
+                *previous
+            };
             true
         }
         JobKind::Or if previous_status == SUCCESS => {
-            *previous = if JobKind::And == current { current } else { *previous };
+            *previous = if JobKind::And == current {
+                current
+            } else {
+                *previous
+            };
             true
         }
-        _ => false
+        _ => false,
     }
 }
 
@@ -777,7 +818,9 @@ pub(crate) fn pipe(
 
     loop {
         if let Some((mut parent, mut kind)) = commands.next() {
-            if should_skip(&mut previous_kind, previous_status, kind) { continue }
+            if should_skip(&mut previous_kind, previous_status, kind) {
+                continue;
+            }
 
             match kind {
                 JobKind::Pipe(mut mode) => {
@@ -813,7 +856,8 @@ pub(crate) fn pipe(
                                         Some(unsafe { File::from_raw_fd(out_reader) });
                                     parent.stdout(unsafe { File::from_raw_fd(out_writer) });
                                     if is_external {
-                                        possible_external_stdio_pipes.get_or_insert(vec![])
+                                        possible_external_stdio_pipes
+                                            .get_or_insert(vec![])
                                             .push(unsafe { File::from_raw_fd(out_writer) });
                                     }
                                 }
@@ -825,7 +869,8 @@ pub(crate) fn pipe(
                                         Some(unsafe { File::from_raw_fd(err_reader) });
                                     parent.stderr(unsafe { File::from_raw_fd(err_writer) });
                                     if is_external {
-                                        possible_external_stdio_pipes.get_or_insert(vec![])
+                                        possible_external_stdio_pipes
+                                            .get_or_insert(vec![])
                                             .push(unsafe { File::from_raw_fd(err_writer) });
                                     }
                                 }
@@ -837,20 +882,17 @@ pub(crate) fn pipe(
                                 }
                                 Ok((reader, writer)) => {
                                     if is_external {
-                                        possible_external_stdio_pipes.get_or_insert(vec![])
+                                        possible_external_stdio_pipes
+                                            .get_or_insert(vec![])
                                             .push(unsafe { File::from_raw_fd(writer) });
                                     }
                                     child.stdin(unsafe { File::from_raw_fd(reader) });
                                     match mode {
                                         RedirectFrom::Stderr => {
-                                            parent.stderr(unsafe {
-                                                File::from_raw_fd(writer)
-                                            });
+                                            parent.stderr(unsafe { File::from_raw_fd(writer) });
                                         }
                                         RedirectFrom::Stdout => {
-                                            parent.stdout(unsafe {
-                                                File::from_raw_fd(writer)
-                                            });
+                                            parent.stdout(unsafe { File::from_raw_fd(writer) });
                                         }
                                         RedirectFrom::Both => {
                                             let temp = unsafe { File::from_raw_fd(writer) };
@@ -873,14 +915,24 @@ pub(crate) fn pipe(
                             }
                         }
 
-                        match spawn_proc(shell, parent, kind, block_child, &mut last_pid, &mut current_pid, pgid) {
+                        match spawn_proc(
+                            shell,
+                            parent,
+                            kind,
+                            block_child,
+                            &mut last_pid,
+                            &mut current_pid,
+                            pgid,
+                        ) {
                             SUCCESS => (),
-                            error_code => return error_code
+                            error_code => return error_code,
                         }
 
                         possible_external_stdio_pipes = None;
 
-                        if set_process_group(&mut pgid, current_pid) && foreground && !shell.is_library {
+                        if set_process_group(&mut pgid, current_pid) && foreground
+                            && !shell.is_library
+                        {
                             let _ = sys::tcsetpgrp(0, pgid);
                         }
 
@@ -892,9 +944,17 @@ pub(crate) fn pipe(
                         } else {
                             kind = ckind;
                             block_child = false;
-                            match spawn_proc(shell, child, kind, block_child, &mut last_pid, &mut current_pid, pgid) {
+                            match spawn_proc(
+                                shell,
+                                child,
+                                kind,
+                                block_child,
+                                &mut last_pid,
+                                &mut current_pid,
+                                pgid,
+                            ) {
                                 SUCCESS => (),
-                                error_code => return error_code
+                                error_code => return error_code,
                             }
 
                             resume_prior_process(&mut last_pid, current_pid);
@@ -934,20 +994,38 @@ fn spawn_proc(
     block_child: bool,
     last_pid: &mut u32,
     current_pid: &mut u32,
-    pgid: u32
+    pgid: u32,
 ) -> i32 {
     let short = cmd.short();
     match cmd {
-        RefinedJob::External { ref name, ref args, ref stdout, ref stderr, ref stdin} => {
+        RefinedJob::External {
+            ref name,
+            ref args,
+            ref stdout,
+            ref stderr,
+            ref stdin,
+        } => {
             let args: Vec<&str> = args.iter().skip(1).map(|x| x as &str).collect();
             let result = sys::fork_and_exec(
                 name,
                 &args,
-                if let Some(ref f) = *stdin { Some(f.as_raw_fd()) } else { None },
-                if let Some(ref f) = *stdout { Some(f.as_raw_fd()) } else { None },
-                if let Some(ref f) = *stderr { Some(f.as_raw_fd()) } else { None },
+                if let Some(ref f) = *stdin {
+                    Some(f.as_raw_fd())
+                } else {
+                    None
+                },
+                if let Some(ref f) = *stdout {
+                    Some(f.as_raw_fd())
+                } else {
+                    None
+                },
+                if let Some(ref f) = *stderr {
+                    Some(f.as_raw_fd())
+                } else {
+                    None
+                },
                 false,
-                || prepare_child(block_child, pgid)
+                || prepare_child(block_child, pgid),
             );
 
             match result {
@@ -965,7 +1043,13 @@ fn spawn_proc(
                 }
             }
         }
-        RefinedJob::Builtin { main, ref args, ref stdout, ref stderr, ref stdin } => {
+        RefinedJob::Builtin {
+            main,
+            ref args,
+            ref stdout,
+            ref stderr,
+            ref stdin,
+        } => {
             let args: Vec<&str> = args.iter().map(|x| x as &str).collect();
             match unsafe { sys::fork() } {
                 Ok(0) => {
@@ -975,20 +1059,26 @@ fn spawn_proc(
                     close(stderr);
                     close(stdin);
                     exit(ret)
-                },
+                }
                 Ok(pid) => {
                     close(stdin);
                     close(stdout);
                     close(stderr);
                     *last_pid = *current_pid;
                     *current_pid = pid;
-                },
+                }
                 Err(e) => {
                     eprintln!("ion: failed to fork {}: {}", short, e);
                 }
             }
         }
-        RefinedJob::Function { ref name, ref args, ref stdout, ref stderr, ref stdin, } => {
+        RefinedJob::Function {
+            ref name,
+            ref args,
+            ref stdout,
+            ref stderr,
+            ref stdin,
+        } => {
             let args: Vec<&str> = args.iter().map(|x| x as &str).collect();
             match unsafe { sys::fork() } {
                 Ok(0) => {
@@ -998,59 +1088,64 @@ fn spawn_proc(
                     close(stderr);
                     close(stdin);
                     exit(ret)
-                },
+                }
                 Ok(pid) => {
                     close(stdin);
                     close(stdout);
                     close(stderr);
                     *last_pid = *current_pid;
                     *current_pid = pid;
-                },
+                }
                 Err(e) => {
                     eprintln!("ion: failed to fork {}: {}", short, e);
                 }
             }
         }
-        RefinedJob::Cat { ref mut sources, ref stdout, ref mut stdin } => {
-            match unsafe { sys::fork() } {
-                Ok(0) => {
-                    prepare_child(block_child, pgid);
+        RefinedJob::Cat {
+            ref mut sources,
+            ref stdout,
+            ref mut stdin,
+        } => match unsafe { sys::fork() } {
+            Ok(0) => {
+                prepare_child(block_child, pgid);
 
-                    let ret = shell.exec_multi_in(sources, stdout, stdin);
-                    close(stdout);
-                    close(stdin);
-                    exit(ret);
-                }
-                Ok(pid) => {
-                    close(stdin);
-                    close(stdout);
-                    *last_pid = *current_pid;
-                    *current_pid = pid;
-                }
-                Err(e) => eprintln!("ion: failed to fork {}: {}", short, e),
+                let ret = shell.exec_multi_in(sources, stdout, stdin);
+                close(stdout);
+                close(stdin);
+                exit(ret);
             }
+            Ok(pid) => {
+                close(stdin);
+                close(stdout);
+                *last_pid = *current_pid;
+                *current_pid = pid;
+            }
+            Err(e) => eprintln!("ion: failed to fork {}: {}", short, e),
         },
-        RefinedJob::Tee { ref mut items, ref stdout, ref stderr, ref stdin } => {
-            match unsafe { sys::fork() } {
-                Ok(0) => {
-                    prepare_child(block_child, pgid);
+        RefinedJob::Tee {
+            ref mut items,
+            ref stdout,
+            ref stderr,
+            ref stdin,
+        } => match unsafe { sys::fork() } {
+            Ok(0) => {
+                prepare_child(block_child, pgid);
 
-                    let ret = shell.exec_multi_out(items, stdout, stderr, stdin, kind);
-                    close(stdout);
-                    close(stderr);
-                    close(stdin);
-                    exit(ret);
-                },
-                Ok(pid) => {
-                    close(stdin);
-                    close(stdout);
-                    close(stderr);
-                    *last_pid = *current_pid;
-                    *current_pid = pid;
-                }
-                Err(e) => eprintln!("ion: failed to fork {}: {}", short, e),
+                let ret = shell.exec_multi_out(items, stdout, stderr, stdin, kind);
+                close(stdout);
+                close(stderr);
+                close(stdin);
+                exit(ret);
             }
-        }
+            Ok(pid) => {
+                close(stdin);
+                close(stdout);
+                close(stderr);
+                *last_pid = *current_pid;
+                *current_pid = pid;
+            }
+            Err(e) => eprintln!("ion: failed to fork {}: {}", short, e),
+        },
     }
     SUCCESS
 }
@@ -1091,7 +1186,9 @@ fn resume_prior_process(last_pid: &mut u32, current_pid: u32) {
 
 fn set_process_group(pgid: &mut u32, pid: u32) -> bool {
     let pgid_set = *pgid == 0;
-    if pgid_set { *pgid = pid; }
+    if pgid_set {
+        *pgid = pid;
+    }
     let _ = sys::setpgid(pid, *pgid);
     pgid_set
 }
diff --git a/src/lib/shell/pipe_exec/streams.rs b/src/lib/shell/pipe_exec/streams.rs
index 4bb99a2b..205a8ae9 100644
--- a/src/lib/shell/pipe_exec/streams.rs
+++ b/src/lib/shell/pipe_exec/streams.rs
@@ -1,6 +1,8 @@
-use std::fs::File;
-use std::io;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::{
+    fs::File,
+    io,
+    os::unix::io::{AsRawFd, FromRawFd, RawFd},
+};
 use sys;
 
 /// Use dup2 to replace `old` with `new` using `old`s file descriptor ID
diff --git a/src/lib/shell/plugins/methods/redox.rs b/src/lib/shell/plugins/methods/redox.rs
index 0f77cb0e..01ed5c66 100644
--- a/src/lib/shell/plugins/methods/redox.rs
+++ b/src/lib/shell/plugins/methods/redox.rs
@@ -9,8 +9,6 @@ pub(crate) enum MethodArguments {
 pub(crate) struct StringMethodPlugins;
 
 impl StringMethodPlugins {
-    pub(crate) fn new() -> StringMethodPlugins { StringMethodPlugins }
-
     pub(crate) fn execute(
         &self,
         _function: &str,
@@ -18,6 +16,8 @@ impl StringMethodPlugins {
     ) -> Result<Option<String>, StringError> {
         Ok(None)
     }
+
+    pub(crate) fn new() -> StringMethodPlugins { StringMethodPlugins }
 }
 
 /// Collects all dynamically-loaded namespaces and their associated symbols all at once.
diff --git a/src/lib/shell/plugins/methods/unix.rs b/src/lib/shell/plugins/methods/unix.rs
index e365fb4c..a0e2e563 100644
--- a/src/lib/shell/plugins/methods/unix.rs
+++ b/src/lib/shell/plugins/methods/unix.rs
@@ -1,14 +1,7 @@
 use super::super::{config_dir, LibraryIterator, StringError};
 use fnv::FnvHashMap;
-use libloading::{Library, Symbol};
-use libloading::os::unix::Symbol as RawSymbol;
-use std::ffi::CString;
-use std::fs::read_dir;
-use std::mem::forget;
-use std::os::raw::c_char;
-use std::ptr;
-use std::slice;
-use std::str;
+use libloading::{os::unix::Symbol as RawSymbol, Library, Symbol};
+use std::{ffi::CString, fs::read_dir, mem::forget, os::raw::c_char, ptr, slice, str};
 use types::Identifier;
 
 /// Either one or the other will be set. Optional status can be conveyed by setting the
@@ -109,10 +102,28 @@ pub(crate) struct StringMethodPlugins {
 }
 
 impl StringMethodPlugins {
-    pub(crate) fn new() -> StringMethodPlugins {
-        StringMethodPlugins {
-            libraries: Vec::new(),
-            symbols:   FnvHashMap::default(),
+    /// Attempts to execute a function within a dynamically-loaded namespace.
+    ///
+    /// If the function exists, it is executed, and it's return value is then converted into a
+    /// proper Rusty type.
+    pub(crate) fn execute(
+        &self,
+        function: &str,
+        arguments: MethodArguments,
+    ) -> Result<Option<String>, StringError> {
+        let func = self.symbols
+            .get(function.into())
+            .ok_or(StringError::FunctionMissing(function.into()))?;
+        unsafe {
+            let data = (*func)(RawMethodArguments::from(arguments));
+            if data.is_null() {
+                Ok(None)
+            } else {
+                match CString::from_raw(data as *mut c_char).to_str() {
+                    Ok(string) => Ok(Some(string.to_owned())),
+                    Err(_) => Err(StringError::UTF8Result),
+                }
+            }
         }
     }
 
@@ -199,28 +210,10 @@ impl StringMethodPlugins {
         }
     }
 
-    /// Attempts to execute a function within a dynamically-loaded namespace.
-    ///
-    /// If the function exists, it is executed, and it's return value is then converted into a
-    /// proper Rusty type.
-    pub(crate) fn execute(
-        &self,
-        function: &str,
-        arguments: MethodArguments,
-    ) -> Result<Option<String>, StringError> {
-        let func = self.symbols
-            .get(function.into())
-            .ok_or(StringError::FunctionMissing(function.into()))?;
-        unsafe {
-            let data = (*func)(RawMethodArguments::from(arguments));
-            if data.is_null() {
-                Ok(None)
-            } else {
-                match CString::from_raw(data as *mut c_char).to_str() {
-                    Ok(string) => Ok(Some(string.to_owned())),
-                    Err(_) => Err(StringError::UTF8Result),
-                }
-            }
+    pub(crate) fn new() -> StringMethodPlugins {
+        StringMethodPlugins {
+            libraries: Vec::new(),
+            symbols:   FnvHashMap::default(),
         }
     }
 }
diff --git a/src/lib/shell/plugins/mod.rs b/src/lib/shell/plugins/mod.rs
index d792595c..1928f659 100644
--- a/src/lib/shell/plugins/mod.rs
+++ b/src/lib/shell/plugins/mod.rs
@@ -1,6 +1,6 @@
+mod library_iter;
 pub mod methods;
 pub mod namespaces;
-mod library_iter;
 mod string;
 
 #[cfg(not(target_os = "redox"))]
diff --git a/src/lib/shell/plugins/namespaces/redox.rs b/src/lib/shell/plugins/namespaces/redox.rs
index 480b69ce..b6667b31 100644
--- a/src/lib/shell/plugins/namespaces/redox.rs
+++ b/src/lib/shell/plugins/namespaces/redox.rs
@@ -6,11 +6,11 @@ use types::Identifier;
 pub(crate) struct StringNamespace;
 
 impl StringNamespace {
-    pub(crate) fn new() -> Result<StringNamespace, StringError> { Ok(StringNamespace) }
-
     pub(crate) fn execute(&self, _function: Identifier) -> Result<Option<String>, StringError> {
         Ok(None)
     }
+
+    pub(crate) fn new() -> Result<StringNamespace, StringError> { Ok(StringNamespace) }
 }
 
 pub(crate) fn collect() -> FnvHashMap<Identifier, StringNamespace> {
diff --git a/src/lib/shell/plugins/namespaces/unix.rs b/src/lib/shell/plugins/namespaces/unix.rs
index a57c158d..910c2d45 100644
--- a/src/lib/shell/plugins/namespaces/unix.rs
+++ b/src/lib/shell/plugins/namespaces/unix.rs
@@ -1,12 +1,7 @@
 use super::super::{config_dir, LibraryIterator, StringError};
 use fnv::FnvHashMap;
-use libloading::{Library, Symbol};
-use libloading::os::unix::Symbol as RawSymbol;
-use std::ffi::CString;
-use std::fs::read_dir;
-use std::os::raw::c_char;
-use std::slice;
-use std::str;
+use libloading::{os::unix::Symbol as RawSymbol, Library, Symbol};
+use std::{ffi::CString, fs::read_dir, os::raw::c_char, slice, str};
 use types::Identifier;
 
 /// A dynamically-loaded string namespace from an external library.
@@ -27,6 +22,27 @@ pub(crate) struct StringNamespace {
 }
 
 impl StringNamespace {
+    /// Attempts to execute a function within a dynamically-loaded namespace.
+    ///
+    /// If the function exists, it is executed, and it's return value is then converted into a
+    /// proper Rusty type.
+    pub(crate) fn execute(&self, function: Identifier) -> Result<Option<String>, StringError> {
+        let func = self.symbols
+            .get(&function)
+            .ok_or(StringError::FunctionMissing(function.clone()))?;
+        unsafe {
+            let data = (*func)();
+            if data.is_null() {
+                Ok(None)
+            } else {
+                match CString::from_raw(data as *mut c_char).to_str() {
+                    Ok(string) => Ok(Some(string.to_owned())),
+                    Err(_) => Err(StringError::UTF8Result),
+                }
+            }
+        }
+    }
+
     pub(crate) fn new(library: Library) -> Result<StringNamespace, StringError> {
         unsafe {
             let mut symbols = FnvHashMap::default();
@@ -107,27 +123,6 @@ impl StringNamespace {
             Ok(StringNamespace { library, symbols })
         }
     }
-
-    /// Attempts to execute a function within a dynamically-loaded namespace.
-    ///
-    /// If the function exists, it is executed, and it's return value is then converted into a
-    /// proper Rusty type.
-    pub(crate) fn execute(&self, function: Identifier) -> Result<Option<String>, StringError> {
-        let func = self.symbols
-            .get(&function)
-            .ok_or(StringError::FunctionMissing(function.clone()))?;
-        unsafe {
-            let data = (*func)();
-            if data.is_null() {
-                Ok(None)
-            } else {
-                match CString::from_raw(data as *mut c_char).to_str() {
-                    Ok(string) => Ok(Some(string.to_owned())),
-                    Err(_) => Err(StringError::UTF8Result),
-                }
-            }
-        }
-    }
 }
 
 /// Collects all dynamically-loaded namespaces and their associated symbols all at once.
diff --git a/src/lib/shell/plugins/string.rs b/src/lib/shell/plugins/string.rs
index 09e6f79a..85fde4f2 100644
--- a/src/lib/shell/plugins/string.rs
+++ b/src/lib/shell/plugins/string.rs
@@ -1,5 +1,7 @@
-use std::fmt::{self, Display, Formatter};
-use std::io;
+use std::{
+    fmt::{self, Display, Formatter},
+    io,
+};
 use types::Identifier;
 
 #[derive(Debug)]
diff --git a/src/lib/shell/variables/mod.rs b/src/lib/shell/variables/mod.rs
index 29009158..ecfe8f61 100644
--- a/src/lib/shell/variables/mod.rs
+++ b/src/lib/shell/variables/mod.rs
@@ -1,13 +1,16 @@
-use super::colors::Colors;
-use super::directory_stack::DirectoryStack;
-use super::plugins::namespaces::{self, StringNamespace};
-use super::status::{FAILURE, SUCCESS};
+use super::{
+    colors::Colors,
+    directory_stack::DirectoryStack,
+    plugins::namespaces::{self, StringNamespace},
+    status::{FAILURE, SUCCESS},
+};
 use fnv::FnvHashMap;
 use liner::Context;
-use std::env;
-use std::io::{self, BufRead};
-use sys::{self, geteuid, getpid, getuid, is_root};
-use sys::variables as self_sys;
+use std::{
+    env,
+    io::{self, BufRead},
+};
+use sys::{self, geteuid, getpid, getuid, is_root, variables as self_sys};
 use types::{
     Array, ArrayVariableContext, HashMap, HashMapVariableContext, Identifier, Key, Value,
     VariableContext,
@@ -88,131 +91,111 @@ impl Default for Variables {
 const PLUGIN: u8 = 1;
 
 impl Variables {
-    pub(crate) fn has_plugin_support(&self) -> bool { self.flags & PLUGIN != 0 }
-
-    pub(crate) fn enable_plugins(&mut self) { self.flags |= PLUGIN; }
-
-    pub(crate) fn disable_plugins(&mut self) { self.flags &= 255 ^ PLUGIN; }
-
-    pub(crate) fn read<I: IntoIterator>(&mut self, args: I) -> i32
-    where
-        I::Item: AsRef<str>,
-    {
-        if sys::isatty(sys::STDIN_FILENO) {
-            let mut con = Context::new();
-            for arg in args.into_iter().skip(1) {
-                match con.read_line(format!("{}=", arg.as_ref().trim()), &mut |_| {}) {
-                    Ok(buffer) => self.set_var(arg.as_ref(), buffer.trim()),
-                    Err(_) => return FAILURE,
-                }
-            }
-        } else {
-            let stdin = io::stdin();
-            let handle = stdin.lock();
-            let mut lines = handle.lines();
-            for arg in args.into_iter().skip(1) {
-                if let Some(Ok(line)) = lines.next() {
-                    self.set_var(arg.as_ref(), line.trim());
-                }
-            }
-        }
-        SUCCESS
-    }
+    #[allow(dead_code)]
+    pub(crate) fn is_hashmap_reference(key: &str) -> Option<(Identifier, Key)> {
+        let mut key_iter = key.split('[');
 
-    pub fn set_var(&mut self, name: &str, value: &str) {
-        if !name.is_empty() {
-            if value.is_empty() {
-                self.variables.remove(name);
-            } else {
-                if name == "NS_PLUGINS" {
-                    match value {
-                        "0" => self.disable_plugins(),
-                        "1" => self.enable_plugins(),
-                        _ => eprintln!(
-                            "ion: unsupported value for NS_PLUGINS. Value must be either 0 or 1."
-                        ),
+        if let Some(map_name) = key_iter.next() {
+            if Variables::is_valid_variable_name(map_name) {
+                if let Some(mut inner_key) = key_iter.next() {
+                    if inner_key.ends_with(']') {
+                        inner_key = inner_key.split(']').next().unwrap_or("");
+                        inner_key = inner_key.trim_matches(|c| c == '\'' || c == '\"');
+                        return Some((map_name.into(), inner_key.into()));
                     }
-                    return;
                 }
-                self.variables.insert(name.into(), value.into());
             }
         }
+        None
     }
 
-    pub fn set_array(&mut self, name: &str, value: Array) {
-        if !name.is_empty() {
-            if value.is_empty() {
-                self.arrays.remove(name);
-            } else {
-                self.arrays.insert(name.into(), value);
-            }
-        }
-    }
+    pub(crate) fn tilde_expansion(&self, word: &str, dir_stack: &DirectoryStack) -> Option<String> {
+        let mut chars = word.char_indices();
 
-    #[allow(dead_code)]
-    pub(crate) fn set_hashmap_value(&mut self, name: &str, key: &str, value: &str) {
-        if !name.is_empty() {
-            if let Some(map) = self.hashmaps.get_mut(name) {
-                map.insert(key.into(), value.into());
-                return;
-            }
+        let tilde_prefix;
+        let remainder;
 
-            let mut map = HashMap::with_capacity_and_hasher(4, Default::default());
-            map.insert(key.into(), value.into());
-            self.hashmaps.insert(name.into(), map);
+        loop {
+            if let Some((ind, c)) = chars.next() {
+                if c == '/' || c == '$' {
+                    tilde_prefix = &word[1..ind];
+                    remainder = &word[ind..];
+                    break;
+                }
+            } else {
+                tilde_prefix = &word[1..];
+                remainder = "";
+                break;
+            }
         }
-    }
 
-    pub fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) }
-
-    pub fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) }
-
-    pub fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) }
+        match tilde_prefix {
+            "" => if let Some(home) = env::home_dir() {
+                return Some(home.to_string_lossy().to_string() + remainder);
+            },
+            "+" => if let Some(pwd) = self.get_var("PWD") {
+                return Some(pwd.to_string() + remainder);
+            } else if let Ok(pwd) = env::current_dir() {
+                return Some(pwd.to_string_lossy().to_string() + remainder);
+            },
+            "-" => if let Some(oldpwd) = self.get_var("OLDPWD") {
+                return Some(oldpwd.to_string() + remainder);
+            },
+            _ => {
+                let neg;
+                let tilde_num;
 
-    /// Obtains the value for the **SWD** variable.
-    ///
-    /// Useful for getting smaller prompts, this will produce a simplified variant of the
-    /// working directory which the leading `HOME` prefix replaced with a tilde character.
-    fn get_simplified_directory(&self) -> Value {
-        self.get_var("PWD")
-            .unwrap()
-            .replace(&self.get_var("HOME").unwrap(), "~")
-    }
+                if tilde_prefix.starts_with('+') {
+                    tilde_num = &tilde_prefix[1..];
+                    neg = false;
+                } else if tilde_prefix.starts_with('-') {
+                    tilde_num = &tilde_prefix[1..];
+                    neg = true;
+                } else {
+                    tilde_num = tilde_prefix;
+                    neg = false;
+                }
 
-    /// Obtains the value for the **MWD** variable.
-    ///
-    /// Further minimizes the directory path in the same manner that Fish does by default.
-    /// That is, if more than two parents are visible in the path, all parent directories
-    /// of the current directory will be reduced to a single character.
-    fn get_minimal_directory(&self) -> Value {
-        let swd = self.get_simplified_directory();
+                match tilde_num.parse() {
+                    Ok(num) => {
+                        let res = if neg {
+                            dir_stack.dir_from_top(num)
+                        } else {
+                            dir_stack.dir_from_bottom(num)
+                        };
 
-        {
-            // Temporarily borrow the `swd` variable while we attempt to assemble a minimal
-            // variant of the directory path. If that is not possible, we will cancel the
-            // borrow and return `swd` itself as the minified path.
-            let elements = swd.split("/")
-                .filter(|s| !s.is_empty())
-                .collect::<Vec<&str>>();
-            if elements.len() > 2 {
-                let mut output = String::new();
-                for element in &elements[0..elements.len() - 1] {
-                    let mut segmenter = UnicodeSegmentation::graphemes(*element, true);
-                    let grapheme = segmenter.next().unwrap();
-                    output.push_str(grapheme);
-                    if grapheme == "." {
-                        output.push_str(segmenter.next().unwrap());
+                        if let Some(path) = res {
+                            return Some(path.to_str().unwrap().to_string());
+                        }
                     }
-                    output.push('/');
+                    Err(_) => if let Some(home) = self_sys::get_user_home(tilde_prefix) {
+                        return Some(home + remainder);
+                    },
                 }
-                output.push_str(&elements[elements.len() - 1]);
-                return output;
             }
         }
+        None
+    }
 
-        swd
+    pub(crate) fn is_valid_variable_name(name: &str) -> bool {
+        name.chars().all(Variables::is_valid_variable_character)
+    }
+
+    pub(crate) fn is_valid_variable_character(c: char) -> bool {
+        c.is_alphanumeric() || c == '_' || c == '?'
+    }
+
+    pub fn get_vars<'a>(&'a self) -> impl Iterator<Item = Identifier> + 'a {
+        self.variables
+            .keys()
+            .cloned()
+            .chain(env::vars().map(|(k, _)| k.into()))
     }
 
+    pub fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) }
+
+    pub fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() }
+
     pub fn get_var(&self, name: &str) -> Option<Value> {
         match name {
             "MWD" => return Some(self.get_minimal_directory()),
@@ -274,110 +257,130 @@ impl Variables {
         }
     }
 
-    pub fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() }
+    /// Obtains the value for the **MWD** variable.
+    ///
+    /// Further minimizes the directory path in the same manner that Fish does by default.
+    /// That is, if more than two parents are visible in the path, all parent directories
+    /// of the current directory will be reduced to a single character.
+    fn get_minimal_directory(&self) -> Value {
+        let swd = self.get_simplified_directory();
 
-    pub fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) }
+        {
+            // Temporarily borrow the `swd` variable while we attempt to assemble a minimal
+            // variant of the directory path. If that is not possible, we will cancel the
+            // borrow and return `swd` itself as the minified path.
+            let elements = swd.split("/")
+                .filter(|s| !s.is_empty())
+                .collect::<Vec<&str>>();
+            if elements.len() > 2 {
+                let mut output = String::new();
+                for element in &elements[0..elements.len() - 1] {
+                    let mut segmenter = UnicodeSegmentation::graphemes(*element, true);
+                    let grapheme = segmenter.next().unwrap();
+                    output.push_str(grapheme);
+                    if grapheme == "." {
+                        output.push_str(segmenter.next().unwrap());
+                    }
+                    output.push('/');
+                }
+                output.push_str(&elements[elements.len() - 1]);
+                return output;
+            }
+        }
 
-    pub fn get_vars<'a>(&'a self) -> impl Iterator<Item = Identifier> + 'a {
-        self.variables
-            .keys()
-            .cloned()
-            .chain(env::vars().map(|(k, _)| k.into()))
+        swd
     }
 
-    pub(crate) fn is_valid_variable_character(c: char) -> bool {
-        c.is_alphanumeric() || c == '_' || c == '?'
+    /// Obtains the value for the **SWD** variable.
+    ///
+    /// Useful for getting smaller prompts, this will produce a simplified variant of the
+    /// working directory which the leading `HOME` prefix replaced with a tilde character.
+    fn get_simplified_directory(&self) -> Value {
+        self.get_var("PWD")
+            .unwrap()
+            .replace(&self.get_var("HOME").unwrap(), "~")
     }
 
-    pub(crate) fn is_valid_variable_name(name: &str) -> bool {
-        name.chars().all(Variables::is_valid_variable_character)
-    }
+    pub fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) }
 
-    pub(crate) fn tilde_expansion(&self, word: &str, dir_stack: &DirectoryStack) -> Option<String> {
-        let mut chars = word.char_indices();
+    pub fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) }
 
-        let tilde_prefix;
-        let remainder;
+    pub fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) }
 
-        loop {
-            if let Some((ind, c)) = chars.next() {
-                if c == '/' || c == '$' {
-                    tilde_prefix = &word[1..ind];
-                    remainder = &word[ind..];
-                    break;
-                }
-            } else {
-                tilde_prefix = &word[1..];
-                remainder = "";
-                break;
+    #[allow(dead_code)]
+    pub(crate) fn set_hashmap_value(&mut self, name: &str, key: &str, value: &str) {
+        if !name.is_empty() {
+            if let Some(map) = self.hashmaps.get_mut(name) {
+                map.insert(key.into(), value.into());
+                return;
             }
-        }
-
-        match tilde_prefix {
-            "" => if let Some(home) = env::home_dir() {
-                return Some(home.to_string_lossy().to_string() + remainder);
-            },
-            "+" => if let Some(pwd) = self.get_var("PWD") {
-                return Some(pwd.to_string() + remainder);
-            } else if let Ok(pwd) = env::current_dir() {
-                return Some(pwd.to_string_lossy().to_string() + remainder);
-            },
-            "-" => if let Some(oldpwd) = self.get_var("OLDPWD") {
-                return Some(oldpwd.to_string() + remainder);
-            },
-            _ => {
-                let neg;
-                let tilde_num;
 
-                if tilde_prefix.starts_with('+') {
-                    tilde_num = &tilde_prefix[1..];
-                    neg = false;
-                } else if tilde_prefix.starts_with('-') {
-                    tilde_num = &tilde_prefix[1..];
-                    neg = true;
-                } else {
-                    tilde_num = tilde_prefix;
-                    neg = false;
-                }
+            let mut map = HashMap::with_capacity_and_hasher(4, Default::default());
+            map.insert(key.into(), value.into());
+            self.hashmaps.insert(name.into(), map);
+        }
+    }
 
-                match tilde_num.parse() {
-                    Ok(num) => {
-                        let res = if neg {
-                            dir_stack.dir_from_top(num)
-                        } else {
-                            dir_stack.dir_from_bottom(num)
-                        };
+    pub fn set_array(&mut self, name: &str, value: Array) {
+        if !name.is_empty() {
+            if value.is_empty() {
+                self.arrays.remove(name);
+            } else {
+                self.arrays.insert(name.into(), value);
+            }
+        }
+    }
 
-                        if let Some(path) = res {
-                            return Some(path.to_str().unwrap().to_string());
-                        }
+    pub fn set_var(&mut self, name: &str, value: &str) {
+        if !name.is_empty() {
+            if value.is_empty() {
+                self.variables.remove(name);
+            } else {
+                if name == "NS_PLUGINS" {
+                    match value {
+                        "0" => self.disable_plugins(),
+                        "1" => self.enable_plugins(),
+                        _ => eprintln!(
+                            "ion: unsupported value for NS_PLUGINS. Value must be either 0 or 1."
+                        ),
                     }
-                    Err(_) => if let Some(home) = self_sys::get_user_home(tilde_prefix) {
-                        return Some(home + remainder);
-                    },
+                    return;
                 }
+                self.variables.insert(name.into(), value.into());
             }
         }
-        None
     }
 
-    #[allow(dead_code)]
-    pub(crate) fn is_hashmap_reference(key: &str) -> Option<(Identifier, Key)> {
-        let mut key_iter = key.split('[');
-
-        if let Some(map_name) = key_iter.next() {
-            if Variables::is_valid_variable_name(map_name) {
-                if let Some(mut inner_key) = key_iter.next() {
-                    if inner_key.ends_with(']') {
-                        inner_key = inner_key.split(']').next().unwrap_or("");
-                        inner_key = inner_key.trim_matches(|c| c == '\'' || c == '\"');
-                        return Some((map_name.into(), inner_key.into()));
-                    }
+    pub(crate) fn read<I: IntoIterator>(&mut self, args: I) -> i32
+    where
+        I::Item: AsRef<str>,
+    {
+        if sys::isatty(sys::STDIN_FILENO) {
+            let mut con = Context::new();
+            for arg in args.into_iter().skip(1) {
+                match con.read_line(format!("{}=", arg.as_ref().trim()), &mut |_| {}) {
+                    Ok(buffer) => self.set_var(arg.as_ref(), buffer.trim()),
+                    Err(_) => return FAILURE,
+                }
+            }
+        } else {
+            let stdin = io::stdin();
+            let handle = stdin.lock();
+            let mut lines = handle.lines();
+            for arg in args.into_iter().skip(1) {
+                if let Some(Ok(line)) = lines.next() {
+                    self.set_var(arg.as_ref(), line.trim());
                 }
             }
         }
-        None
+        SUCCESS
     }
+
+    pub(crate) fn disable_plugins(&mut self) { self.flags &= 255 ^ PLUGIN; }
+
+    pub(crate) fn enable_plugins(&mut self) { self.flags |= PLUGIN; }
+
+    pub(crate) fn has_plugin_support(&self) -> bool { self.flags & PLUGIN != 0 }
 }
 
 #[cfg(test)]
diff --git a/src/lib/sys/redox/job_control.rs b/src/lib/sys/redox/job_control.rs
index 3eea4136..c0be50d7 100644
--- a/src/lib/sys/redox/job_control.rs
+++ b/src/lib/sys/redox/job_control.rs
@@ -1,17 +1,17 @@
-use shell::Shell;
-use shell::foreground::ForegroundSignals;
-use shell::job_control::*;
-use shell::status::{FAILURE, TERMINATED};
-use std::sync::{Arc, Mutex};
-use std::thread::sleep;
-use std::time::Duration;
+use shell::{
+    foreground::ForegroundSignals,
+    job_control::*,
+    status::{FAILURE, TERMINATED},
+    Shell,
+};
+use std::{
+    sync::{Arc, Mutex},
+    thread::sleep,
+    time::Duration,
+};
 use syscall::{
-    kill, waitpid,
-    ECHILD,
-    SIGINT, SIGPIPE,
-    WCONTINUED, WNOHANG, WUNTRACED,
-    wifcontinued, wifexited, wifsignaled, wifstopped,
-    wcoredump, wexitstatus, wstopsig, wtermsig
+    kill, waitpid, wcoredump, wexitstatus, wifcontinued, wifexited, wifsignaled, wifstopped,
+    wstopsig, wtermsig, ECHILD, SIGINT, SIGPIPE, WCONTINUED, WNOHANG, WUNTRACED,
 };
 
 const OPTS: usize = WUNTRACED | WCONTINUED | WNOHANG;
@@ -91,24 +91,22 @@ pub(crate) fn watch_foreground(shell: &mut Shell, pid: i32, command: &str) -> i3
         unsafe {
             status = 0;
             match waitpid(pid as usize, &mut status, WUNTRACED) {
-                Err(err) => {
-                    match err.errno {
-                        ECHILD => break signaled,
-                        errno => {
-                            eprintln!("ion: waitpid error: {}", errno);
-                            break FAILURE;
-                        }
+                Err(err) => match err.errno {
+                    ECHILD => break signaled,
+                    errno => {
+                        eprintln!("ion: waitpid error: {}", errno);
+                        break FAILURE;
                     }
-                }
+                },
                 Ok(0) => (),
                 Ok(_pid) if wifexited(status) => break wexitstatus(status) as i32,
                 Ok(pid) if wifsignaled(status) => {
                     let signal = wtermsig(status);
                     if signal == SIGPIPE {
-                        continue
+                        continue;
                     } else if wcoredump(status) {
                         eprintln!("ion: process ({}) had a core dump", pid);
-                        continue
+                        continue;
                     }
 
                     eprintln!("ion: process ({}) ended by signal {}", pid, signal);
diff --git a/src/lib/sys/redox/mod.rs b/src/lib/sys/redox/mod.rs
index 49e30169..3f283a6c 100644
--- a/src/lib/sys/redox/mod.rs
+++ b/src/lib/sys/redox/mod.rs
@@ -1,13 +1,15 @@
 extern crate syscall;
 
-use std::{io, mem, slice};
-use std::env;
-use std::os::unix::ffi::OsStrExt;
-use std::os::unix::io::RawFd;
-use std::path::PathBuf;
-use std::process::{exit, ExitStatus};
-use std::os::unix::process::ExitStatusExt;
-use syscall::{EINTR, WUNTRACED, SigAction, waitpid};
+use std::{
+    env,
+    io,
+    mem,
+    os::unix::{ffi::OsStrExt, io::RawFd, process::ExitStatusExt},
+    path::PathBuf,
+    process::{exit, ExitStatus},
+    slice,
+};
+use syscall::{waitpid, SigAction, EINTR, WUNTRACED};
 
 pub mod job_control;
 
@@ -58,7 +60,7 @@ pub fn wait_for_child(pid: u32) -> io::Result<u8> {
         match waitpid(pid as usize, &mut status, WUNTRACED) {
             Err(ref error) if error.errno == ECHILD => break,
             Err(error) => return Err(io::Error::from_raw_os_error(error.errno)),
-            _ => ()
+            _ => (),
         }
     }
 
@@ -93,7 +95,7 @@ pub(crate) fn fork_and_exec<F: Fn()>(
     stdout: Option<RawFd>,
     stderr: Option<RawFd>,
     clear_env: bool,
-    before_exec: F
+    before_exec: F,
 ) -> io::Result<u32> {
     unsafe {
         match fork()? {
diff --git a/src/lib/sys/unix/job_control.rs b/src/lib/sys/unix/job_control.rs
index 2d3696d0..3dc6a89a 100644
--- a/src/lib/sys/unix/job_control.rs
+++ b/src/lib/sys/unix/job_control.rs
@@ -1,12 +1,16 @@
-use libc::*;
-use shell::Shell;
-use shell::foreground::ForegroundSignals;
-use shell::job_control::*;
-use shell::status::{FAILURE, TERMINATED};
-use std::sync::{Arc, Mutex};
-use std::thread::sleep;
-use std::time::Duration;
 use super::{errno, write_errno};
+use libc::*;
+use shell::{
+    foreground::ForegroundSignals,
+    job_control::*,
+    status::{FAILURE, TERMINATED},
+    Shell,
+};
+use std::{
+    sync::{Arc, Mutex},
+    thread::sleep,
+    time::Duration,
+};
 
 const OPTS: i32 = WUNTRACED | WCONTINUED | WNOHANG;
 
@@ -86,25 +90,23 @@ pub(crate) fn watch_foreground(shell: &mut Shell, pid: i32, command: &str) -> i3
         unsafe {
             status = 0;
             match waitpid(pid, &mut status, WUNTRACED) {
-                -1 => {
-                    match errno() {
-                        ECHILD if signaled == 0 => break exit_status,
-                        ECHILD => break signaled,
-                        errno => {
-                            write_errno("ion: waitpid error: ", errno);
-                            break FAILURE;
-                        }
+                -1 => match errno() {
+                    ECHILD if signaled == 0 => break exit_status,
+                    ECHILD => break signaled,
+                    errno => {
+                        write_errno("ion: waitpid error: ", errno);
+                        break FAILURE;
                     }
-                }
+                },
                 0 => (),
                 _pid if WIFEXITED(status) => exit_status = WEXITSTATUS(status),
                 pid if WIFSIGNALED(status) => {
                     let signal = WTERMSIG(status);
                     if signal == SIGPIPE {
-                        continue
+                        continue;
                     } else if WCOREDUMP(status) {
                         eprintln!("ion: process ({}) had a core dump", pid);
-                        continue
+                        continue;
                     }
 
                     eprintln!("ion: process ({}) ended by signal {}", pid, signal);
@@ -120,7 +122,11 @@ pub(crate) fn watch_foreground(shell: &mut Shell, pid: i32, command: &str) -> i3
                     signaled = 128 + signal as i32;
                 }
                 pid if WIFSTOPPED(status) => {
-                    shell.send_to_background(pid.abs() as u32, ProcessState::Stopped, command.into());
+                    shell.send_to_background(
+                        pid.abs() as u32,
+                        ProcessState::Stopped,
+                        command.into(),
+                    );
                     shell.break_flow = true;
                     break 128 + WSTOPSIG(status);
                 }
diff --git a/src/lib/sys/unix/mod.rs b/src/lib/sys/unix/mod.rs
index 40623c04..99e34995 100644
--- a/src/lib/sys/unix/mod.rs
+++ b/src/lib/sys/unix/mod.rs
@@ -3,11 +3,16 @@ extern crate libc;
 pub mod job_control;
 pub mod signals;
 
-use libc::{c_char, c_int, pid_t, sighandler_t, strerror, waitpid, ECHILD, EINTR, WEXITSTATUS, WUNTRACED};
-use std::{io, ptr, env};
-use std::io::Write;
-use std::ffi::{CStr, CString};
-use std::os::unix::io::RawFd;
+use libc::{
+    c_char, c_int, pid_t, sighandler_t, strerror, waitpid, ECHILD, EINTR, WEXITSTATUS, WUNTRACED,
+};
+use std::{
+    env,
+    ffi::{CStr, CString},
+    io::{self, Write},
+    os::unix::io::RawFd,
+    ptr,
+};
 
 pub(crate) const PATH_SEPARATOR: &str = ":";
 pub(crate) const NULL_PATH: &str = "/dev/null";
@@ -63,7 +68,7 @@ pub fn wait_for_interrupt(pid: u32) -> io::Result<()> {
         match unsafe { waitpid(pid as i32, &mut status, WUNTRACED) } {
             -1 if errno() == EINTR => continue,
             -1 => break Err(io::Error::from_raw_os_error(errno())),
-            _ => break Ok(())
+            _ => break Ok(()),
         }
     }
 }
@@ -358,8 +363,7 @@ fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
 }
 
 pub mod variables {
-    use users_unix::get_user_by_name;
-    use users_unix::os::unix::UserExt;
+    use users_unix::{get_user_by_name, os::unix::UserExt};
 
     pub(crate) fn get_user_home(username: &str) -> Option<String> {
         match get_user_by_name(username) {
diff --git a/src/lib/sys/unix/signals.rs b/src/lib/sys/unix/signals.rs
index 97fa4d51..5b466a02 100644
--- a/src/lib/sys/unix/signals.rs
+++ b/src/lib/sys/unix/signals.rs
@@ -1,6 +1,5 @@
 use libc::*;
-use std::mem;
-use std::ptr;
+use std::{mem, ptr};
 
 /// Blocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so that the shell never receives
 /// them.
diff --git a/src/main.rs b/src/main.rs
index ad618c3c..9d3a8f73 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,15 +1,14 @@
 extern crate ion_shell;
 extern crate smallvec;
 
-use ion_shell::JobControl;
-use ion_shell::MAN_ION;
-use ion_shell::flags::NO_EXEC;
-use ion_shell::{Binary, ShellBuilder};
+use ion_shell::{flags::NO_EXEC, Binary, JobControl, ShellBuilder, MAN_ION};
 use smallvec::SmallVec;
-use std::env;
-use std::error::Error;
-use std::io::{stdout, Write};
-use std::iter::FromIterator;
+use std::{
+    env,
+    error::Error,
+    io::{stdout, Write},
+    iter::FromIterator,
+};
 
 fn main() {
     let mut shell = ShellBuilder::new()
-- 
GitLab