diff --git a/README.md b/README.md index bef4c03ba6b1cbeac8cd6057a2b482318db872f7..7b382eb96433bce55499e1026e6398575e3815d7 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Ion is a shell for UNIX platforms, and is the default shell in Redox. It is stil - [x] Executing Scripts with an @args Array - [x] Aliases - [x] Variables (**$variable**) +- [x] Multiple Variable Assignments - [x] Substring Slicing of Variables - [x] Arrays (**@array**) - [x] Array Expressions (**[]**) @@ -70,6 +71,14 @@ a similar action, only setting the variable globally as an environment variable let git_branch = $(git rev-parse --abbrev-ref HEAD ^> /dev/null) ``` +It is also possible to assign multiple variables at once, or swap variables. + +```ion +let a b = 1 2 +let a b = [1 2] +let a b = [$b $a] +``` + If the command is executed without any arguments, it will simply list all available variables. ### Using Variables diff --git a/examples/fibonacci.ion b/examples/fibonacci.ion index cf7c877f6e10ca30c46292e3d1ee504630504f18..94cee368a22e6a32b448aa2d14ae94a472ca3c93 100644 --- a/examples/fibonacci.ion +++ b/examples/fibonacci.ion @@ -2,8 +2,7 @@ fn fib n if test $n -le 1 echo $n else - let output = 1 - let previous = 1 + let output previous = 1 1 for _ in 2..$n let temp = $output let output += $previous diff --git a/examples/let.ion b/examples/let.ion new file mode 100644 index 0000000000000000000000000000000000000000..43faa47a82afdc352c0aeb52219b5ca97bd4ad67 --- /dev/null +++ b/examples/let.ion @@ -0,0 +1,12 @@ +let a = 5 +echo $a +let a b = [10 $a] +echo $a +echo $b +let b a = [$a $b] +echo $a +echo $b +let a b c = 1 2 3 +echo $a +echo $b +echo $c diff --git a/examples/let.out b/examples/let.out new file mode 100644 index 0000000000000000000000000000000000000000..5f0f24c642d49dc437df4f4f603e4fe7265c9362 --- /dev/null +++ b/examples/let.out @@ -0,0 +1,8 @@ +5 +10 +5 +5 +10 +1 +2 +3 diff --git a/src/parser/assignments.rs b/src/parser/assignments.rs index e3504289514e07baf50f895df269eb8d5c2e94b1..aea3db8952e4824b64b06d9bae02f45a8f5aba51 100644 --- a/src/parser/assignments.rs +++ b/src/parser/assignments.rs @@ -15,6 +15,7 @@ pub enum Binding { KeyOnly(Identifier), KeyValue(Identifier, VString), Math(Identifier, Operator, VString), + MultipleKeys(Vec<Identifier>, VString) } #[derive(Debug, PartialEq, Clone)] @@ -39,6 +40,7 @@ pub fn parse_assignment(arguments: &str) -> Binding { // Find the key and advance the iterator until the equals operator is found. let mut key = "".to_owned(); + let mut keys: Vec<Identifier> = Vec::new(); let mut found_key = false; let mut operator = None; @@ -56,7 +58,10 @@ pub fn parse_assignment(arguments: &str) -> Binding { while let Some(character) = char_iter.next() { match character { ' ' if key.is_empty() => (), - ' ' => found_key = true, + ' ' => { + keys.push(key.clone().into()); + key.clear(); + }, '+' => { match_operator!(Operator::Add); break @@ -78,6 +83,7 @@ pub fn parse_assignment(arguments: &str) -> Binding { break }, '=' => { + if !key.is_empty() { keys.push(key.into()); } found_key = true; break }, @@ -86,9 +92,19 @@ pub fn parse_assignment(arguments: &str) -> Binding { } } - if !found_key && key.is_empty() { + if !found_key { + Binding::ListEntries + } else if keys.len() > 1 { + for key in &keys { + if !Variables::is_valid_variable_name(&key) { + return Binding::InvalidKey(key.clone()); + } + } + Binding::MultipleKeys(keys, char_iter.skip_while(|&x| x == ' ').collect::<VString>()) + } else if keys.is_empty() { Binding::ListEntries } else { + let key = keys.drain(..).next().unwrap(); let value = char_iter.skip_while(|&x| x == ' ').collect::<VString>(); if value.is_empty() { Binding::KeyOnly(key.into()) diff --git a/src/shell/assignments.rs b/src/shell/assignments.rs index c3ce46e388ca90c807195b8b5fa1ef4328944281..79da421c7229f5ae70e980035a7df403313c4318 100644 --- a/src/shell/assignments.rs +++ b/src/shell/assignments.rs @@ -23,6 +23,7 @@ use super::status::*; enum Action { UpdateString(Identifier, VString), + UpdateStrings(Vec<Identifier>, VArray), UpdateArray(Identifier, VArray), List } @@ -77,6 +78,14 @@ fn parse_assignment(binding: Binding, Value::String(value) => Ok(Action::UpdateString(key, value)), Value::Array(array) => Ok(Action::UpdateArray(key, array)) }, + Binding::MultipleKeys(keys, value) => match parse_expression(&value, &expanders) { + Value::String(value) => { + let array = value.split_whitespace().map(String::from) + .collect::<VArray>(); + Ok(Action::UpdateStrings(keys, array)) + }, + Value::Array(array) => Ok(Action::UpdateStrings(keys, array)) + }, Binding::KeyOnly(key) => { let stderr = io::stderr(); let _ = writeln!(&mut stderr.lock(), "ion: please provide value for variable '{}'", key); @@ -120,6 +129,11 @@ pub fn let_assignment(binding: Binding, vars: &mut Variables, dir_stack: &Direct match parse_assignment(binding, vars, dir_stack) { Ok(Action::UpdateArray(key, array)) => vars.set_array(&key, array), Ok(Action::UpdateString(key, string)) => vars.set_var(&key, &string), + Ok(Action::UpdateStrings(keys, array)) => { + for (key, value) in keys.iter().zip(array.iter()) { + vars.set_var(key, value); + } + }, Ok(Action::List) => { print_vars(&vars.variables); print_arrays(&vars.arrays); @@ -136,6 +150,11 @@ pub fn export_variable(binding : Binding, vars: &mut Variables, dir_stack : &Dir match parse_assignment(binding, vars, dir_stack) { Ok(Action::UpdateArray(key, array)) => env::set_var(&key, array.join(" ")), Ok(Action::UpdateString(key, string)) => env::set_var(&key, string), + Ok(Action::UpdateStrings(keys, array)) => { + for (key, value) in keys.iter().zip(array.iter()) { + env::set_var(key, value); + } + } Ok(Action::List) => { let stdout = io::stdout(); let stdout = &mut stdout.lock();