diff --git a/Cargo.toml b/Cargo.toml index 2838578f2bca3977f36f88a200ac4187e7bc8869..9eabcce626244b67484225f7735f552863a0bc77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ name = "ion" [dependencies] glob = "0.2" -liner = "0.1" +liner = { git = "https://github.com/redox-os/liner/", branch = "redox" } peg-syntax-ext = "0.4" permutate = "0.2" diff --git a/README.md b/README.md index 2f01c90886504abaedcd927f7a586372a7ee609d..1991c59d5ce018e5e541addb2f4736fd09cca0bf 100644 --- a/README.md +++ b/README.md @@ -14,23 +14,28 @@ core functionality is complete. Features below: - [x] Variables - [x] Functions - [ ] Arrays +- [ ] Array Process Expressions - [ ] Maps +- [x] Process Expressions - [x] For Loops - [ ] Foreach Loops - [x] While Loops - [x] If Conditionals - [x] Piping Stdout/Stderr - [x] Redirecting Stdout/Stderr +- [ ] Piping Builtins & Functions - [x] && and || Conditionals - [x] Background Jobs - [ ] Background Jobs Control - [ ] Signal Handling -- [ ] Autosuggestions +- [ ] Autosuggestions (90%) - [ ] Syntax Highlighting - [x] Multiline Comments and Commands - [ ] Multiline Editing - [x] Tab Completion (Needs Improvements) - [ ] Unescape sequences like '\n' and '\t' +- [ ] Builtin Plugins +- [ ] Prompt Plugins ## Shell Syntax diff --git a/src/builtins/README.md b/src/builtins/README.md index 88931bf705b14f74ead46ff711a6aec89e9a892f..8899c937c76c5e4da2549ce53634b099085ce6f5 100644 --- a/src/builtins/README.md +++ b/src/builtins/README.md @@ -2,7 +2,11 @@ This directory contains the source code of Ion's builtin commands and documentation for their usage. -## Variables +## source.rs + +Contains the source command + +## variables.rs The **variables.rs** module contains commands relating to setting and removing aliases, variables, and exports. The shell stores aliases and variables within two separate `BTreeMap` structures inside the same `Variables` structure, which is contained within the `Shell` structure. @@ -27,8 +31,7 @@ unalias ls The `let` command sets a variable to the value of the expression that follows. These variables are stored as local values within the shell, so other processes many not access these values. ```ion -// TODO: Ion Shell does not yet implement stderr redirection. -let git_branch = $(git rev-parse --abbrev-ref HEAD 2> /dev/null) +let git_branch = $(git rev-parse --abbrev-ref HEAD ^> /dev/null) ``` If the command is executed without any arguments, it will simply list all available variables. diff --git a/src/parser/shell_expand/braces.rs b/src/parser/shell_expand/braces.rs index f76290329fe510e95ad944ac7fc18b4cc12de0e4..7c7a12add7592aec6dc943109b04d580ea754d63 100644 --- a/src/parser/shell_expand/braces.rs +++ b/src/parser/shell_expand/braces.rs @@ -4,17 +4,17 @@ use super::permutate::Permutator; /// A token primitive for the `expand_braces` function. pub enum BraceToken { Normal(String), Expander } -pub fn expand_braces(tokens: Vec<BraceToken>, mut expanders: Vec<Vec<String>>) -> Vec<String> { +pub fn expand_braces(tokens: &[BraceToken], mut expanders: Vec<Vec<String>>) -> Vec<String> { if expanders.len() > 1 { let tmp: Vec<Vec<&str>> = expanders.iter() .map(|list| list.iter().map(AsRef::as_ref).collect::<Vec<&str>>()) .collect(); let vector_of_arrays: Vec<&[&str]> = tmp.iter().map(AsRef::as_ref).collect(); - multiple_brace_expand(&vector_of_arrays[..], &tokens) + multiple_brace_expand(&vector_of_arrays[..], tokens) } else if expanders.len() == 1 { let elements = expanders.drain(..).next().expect("there should be at least one value"); let elements: Vec<&str> = elements.iter().map(AsRef::as_ref).collect(); - single_brace_expand(&elements, &tokens) + single_brace_expand(&elements, tokens) } else { Vec::new() } diff --git a/src/parser/shell_expand/mod.rs b/src/parser/shell_expand/mod.rs index c9c500289ce65c08277726a73eeac09f1ae9a7d6..9ac6e00854100049fd31c132cffe2c23477f722f 100644 --- a/src/parser/shell_expand/mod.rs +++ b/src/parser/shell_expand/mod.rs @@ -16,6 +16,52 @@ pub struct ExpanderFunctions<'f> { pub command: &'f Fn(&str, bool) -> Option<String> } +fn expand_process(current: &mut String, command: &str, quoted: bool, + expand_func: &ExpanderFunctions) +{ + let mut expanded = String::with_capacity(command.len()); + for token in CommandExpander::new(command) { + match token { + CommandToken::Normal(string) => expanded.push_str(string), + CommandToken::Variable(var) => { + if let Some(result) = (expand_func.variable)(var, quoted) { + expanded.push_str(&result); + } + } + } + } + + if let Some(result) = (expand_func.command)(&expanded, quoted) { + current.push_str(&result); + } +} + +fn expand_brace(current: &mut String, expanders: &mut Vec<Vec<String>>, + tokens: &mut Vec<BraceToken>, nodes: Vec<&str>, expand_func: &ExpanderFunctions, + reverse_quoting: bool) +{ + let mut temp = Vec::new(); + for word in nodes.into_iter() + .flat_map(|node| expand_string(node, expand_func, reverse_quoting)) + { + match parse_range(&word) { + Some(elements) => for word in elements { temp.push(word) }, + None => temp.push(word), + } + } + + if !temp.is_empty() { + if !current.is_empty() { + tokens.push(BraceToken::Normal(current.clone())); + current.clear(); + } + tokens.push(BraceToken::Expander); + expanders.push(temp); + } else { + current.push_str("{}"); + } +} + /// Performs shell expansions to an input string, efficiently returning the final expanded form. /// Shells must provide their own batteries for expanding tilde and variable words. pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_quoting: bool) -> Vec<String> { @@ -37,30 +83,8 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu for word in token_buffer.drain(..) { match word { - WordToken::Brace(nodes) => { - let mut temp = Vec::new(); - for word in nodes.into_iter() - .flat_map(|node| expand_string(node, expand_func, reverse_quoting)) - { - match parse_range(&word) { - Some(elements) => for word in elements { - temp.push(word) - }, - None => temp.push(word), - } - } - - if temp.len() > 0 { - if !current.is_empty() { - tokens.push(BraceToken::Normal(current.clone())); - current.clear(); - } - tokens.push(BraceToken::Expander); - expanders.push(temp); - } else { - current.push_str("{}"); - } - }, + WordToken::Brace(nodes) => + expand_brace(&mut current, &mut expanders, &mut tokens, nodes, expand_func, reverse_quoting), WordToken::Normal(text) => current.push_str(text), WordToken::Whitespace(_) => unreachable!(), WordToken::Tilde(text) => current.push_str(match (expand_func.tilde)(text) { @@ -69,21 +93,7 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu }), WordToken::Process(command, quoted) => { let quoted = if reverse_quoting { !quoted } else { quoted }; - let mut expanded = String::with_capacity(command.len()); - for token in CommandExpander::new(&command) { - match token { - CommandToken::Normal(string) => expanded.push_str(string), - CommandToken::Variable(var) => { - if let Some(result) = (expand_func.variable)(var, quoted) { - expanded.push_str(&result); - } - } - } - } - - if let Some(result) = (expand_func.command)(&expanded, quoted) { - current.push_str(&result); - } + expand_process(&mut current, command, quoted, expand_func); }, WordToken::Variable(text, quoted) => { let quoted = if reverse_quoting { !quoted } else { quoted }; @@ -95,15 +105,13 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu } } - - - if expanders.len() == 0 { + if expanders.is_empty() { expanded_words.push(current); } else { if !current.is_empty() { tokens.push(BraceToken::Normal(current)); } - for word in braces::expand_braces(tokens, expanders) { + for word in braces::expand_braces(&tokens, expanders) { expanded_words.push(word); } } @@ -120,21 +128,7 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu }), WordToken::Process(command, quoted) => { let quoted = if reverse_quoting { !quoted } else { quoted }; - let mut expanded = String::with_capacity(command.len()); - for token in CommandExpander::new(&command) { - match token { - CommandToken::Normal(string) => expanded.push_str(string), - CommandToken::Variable(var) => { - if let Some(result) = (expand_func.variable)(var, quoted) { - expanded.push_str(&result); - } - } - } - } - - if let Some(result) = (expand_func.command)(&expanded, quoted) { - output.push_str(&result); - } + expand_process(&mut output, command, quoted, expand_func); }, WordToken::Variable(text, quoted) => { let quoted = if reverse_quoting { !quoted } else { quoted }; diff --git a/src/parser/shell_expand/words.rs b/src/parser/shell_expand/words.rs index 2d16478e7c108e8eddae221e53e66e2243a45bae..c5cbad607efe028a84f23b7bbebccbf5971cf422 100644 --- a/src/parser/shell_expand/words.rs +++ b/src/parser/shell_expand/words.rs @@ -95,9 +95,9 @@ impl<'a> WordIterator<'a> { while let Some(character) = iterator.next() { match character { // If found, this is not a `Variable` but an `ArrayToString` - b'(' => { - unimplemented!() - }, + // b'(' => { + // unimplemented!() + // }, // Only alphanumerical and underscores are allowed in variable names 0...47 | 58...64 | 91...94 | 96 | 123...127 => { return WordToken::Variable(&self.data[start..self.read], self.flags & DQUOTE != 0); diff --git a/src/shell/flow.rs b/src/shell/flow.rs index 60f821207a2f0a9578d8fea5376b40d76792194c..4732ace74831191218233886bbb78ee210e2fa72 100644 --- a/src/shell/flow.rs +++ b/src/shell/flow.rs @@ -177,7 +177,7 @@ impl<'a> FlowLogic for Shell<'a> { fn glob_expand(arg: &str) -> Vec<String> { let mut expanded = Vec::new(); if arg.contains(|chr| chr == '?' || chr == '*' || chr == '[') { - if let Ok(glob) = glob(&arg) { + if let Ok(glob) = glob(arg) { for path in glob.filter_map(Result::ok) { expanded.push(path.to_string_lossy().into_owned()); }