From 3b36bfba68ea5a4ed123203a90c378114f7301d3 Mon Sep 17 00:00:00 2001 From: stratact <stratact1@gmail.com> Date: Fri, 22 Mar 2019 16:23:25 +0000 Subject: [PATCH] Simplify the assignments code --- Cargo.lock | 11 +- Cargo.toml | 2 +- src/lib/shell/assignments.rs | 244 +++++++++-------------------------- src/lib/shell/mod.rs | 124 +++++++++++++++++- 4 files changed, 186 insertions(+), 195 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb0b4264..5fecace3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,7 +341,7 @@ dependencies = [ "ion_builtins 0.1.0", "ion_lexers 0.1.0", "ion_sys 0.1.0", - "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "liner 0.4.5 (git+https://gitlab.redox-os.org/redox-os/liner)", @@ -391,14 +391,6 @@ dependencies = [ "users 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "itertools" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "itertools" version = "0.8.0" @@ -931,7 +923,6 @@ dependencies = [ "checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" -"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" diff --git a/Cargo.toml b/Cargo.toml index 693a1387..c520cddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ ion_lexers = { path = "members/lexers" } ion_sys = { path = "members/sys" } ion-ranges = { path = "members/ranges" } hashbrown = "0.1.2" -itertools = "0.7.9" +itertools = "0.8" getopts = "0.2" [lib] diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs index 048df1e8..3868ef98 100644 --- a/src/lib/shell/assignments.rs +++ b/src/lib/shell/assignments.rs @@ -9,11 +9,8 @@ use crate::{ shell::{history::ShellHistory, variables::Value}, types, }; -use hashbrown::HashMap; - use std::{ env, - ffi::OsString, fmt::{self, Display}, io::{self, BufWriter, Write}, result::Result, @@ -52,11 +49,10 @@ pub(crate) trait VariableStore { /// Export a variable to the process environment given a binding fn export(&mut self, action: ExportAction) -> i32; /// Collect all updates to perform on variables for a given assignement action - fn create_patch<'a>( + fn calculate<'a>( &mut self, actions: AssignmentActions<'a>, - ) -> Result<HashMap<Key<'a>, Value>, String>; - fn patch(&mut self, patch: HashMap<Key, Value>) -> Result<(), String>; + ) -> Result<Vec<(Key<'a>, Value)>, String>; } impl VariableStore for Shell { @@ -117,185 +113,62 @@ impl VariableStore for Shell { SUCCESS } - fn create_patch<'a>( + fn calculate<'a>( &mut self, actions: AssignmentActions<'a>, - ) -> Result<HashMap<Key<'a>, Value>, String> { - let mut patch = HashMap::new(); - actions - .map(|act| act.map_err(|e| e.to_string())) - .map(|action| { - action - .and_then(|act| { - // sanitize variable names - if ["HOME", "HOST", "PWD", "MWD", "SWD", "?"].contains(&act.0.name) { - Err(format!("not allowed to set `{}`", act.0.name)) - } else { - Ok(act) - } - }) - .and_then(|Action(key, operator, expression)| { - value_check(self, &expression, &key.kind) - .map_err(|why| format!("{}: {}", key.name, why)) - .map(|rhs| (rhs, key, operator)) - }) - .and_then(|(rhs, key, operator)| { - if operator == Operator::OptionalEqual - && self.variables.get_ref(key.name).is_some() - { - return Ok(()); - } - if [Operator::Equal, Operator::OptionalEqual].contains(&operator) { - // When we changed the HISTORY_IGNORE variable, update the - // ignore patterns. This happens first because `set_array` - // consumes 'values' - if key.name == "HISTORY_IGNORE" { - if let Value::Array(array) = &rhs { - self.update_ignore_patterns(array); - } + ) -> Result<Vec<(Key<'a>, Value)>, String> { + let mut backup: Vec<_> = Vec::new(); + for action in actions.map(|act| act.map_err(|e| e.to_string())) { + action + .and_then(|Action(key, operator, expression)| { + // sanitize variable names + if ["HOME", "HOST", "PWD", "MWD", "SWD", "?"].contains(&key.name) { + Err(format!("not allowed to set `{}`", key.name)) + } else { + Ok((key, operator, expression)) + } + }) + .and_then(|(key, operator, expression)| { + value_check(self, &expression, &key.kind) + .map_err(|why| format!("{}: {}", key.name, why)) + .map(|rhs| (key, operator, rhs)) + }) + .and_then(|(key, operator, rhs)| { + if operator == Operator::OptionalEqual + && self.variables.get_ref(key.name).is_some() + { + Ok(()) + } else if [Operator::Equal, Operator::OptionalEqual].contains(&operator) { + // When we changed the HISTORY_IGNORE variable, update the + // ignore patterns. This happens first because `set_array` + // consumes 'values' + if key.name == "HISTORY_IGNORE" { + if let Value::Array(array) = &rhs { + self.update_ignore_patterns(array); } - - return match (&rhs, &key.kind) { - (Value::HashMap(_), Primitive::Indexed(..)) => { - Err("cannot insert hmap into index".to_string()) - } - (Value::BTreeMap(_), Primitive::Indexed(..)) => { - Err("cannot insert bmap into index".to_string()) - } - (Value::Array(_), Primitive::Indexed(..)) => { - Err("multi-dimensional arrays are not yet supported" - .to_string()) - } - _ => { - patch.insert(key, rhs); - Ok(()) - } - }; } - self.variables - .get_mut(key.name) - .ok_or_else(|| { - format!("cannot update non existing variable `{}`", key.name) - }) - .and_then(|lhs| match rhs { - Value::Str(rhs) => match lhs { - Value::Str(lhs) => math(&key.kind, operator, &rhs) - .and_then(|action| parse(&lhs, &*action)) - .map(|value| { - patch.insert(key, Value::Str(value.into())); - }) - .map_err(|why| why.to_string()), - Value::Array(ref mut array) => match operator { - Operator::Concatenate => { - array.push(rhs); - Ok(()) - } - Operator::ConcatenateHead => { - array.insert(0, rhs); - Ok(()) - } - Operator::Filter => { - array.retain(|item| item != &rhs); - Ok(()) - } - _ => math(&Primitive::Float, operator, &rhs) - .and_then(|action| { - array - .iter_mut() - .map(|el| { - parse(el, &*action) - .map(|result| *el = result.into()) - }) - .find(|e| e.is_err()) - .unwrap_or(Ok(())) - }) - .map_err(|why| why.to_string()), - }, - _ => Err("type does not support this operator".to_string()), - }, - Value::Array(values) => { - if let Value::Array(ref mut array) = lhs { - match operator { - Operator::Concatenate => array.extend(values), - Operator::ConcatenateHead => values - .into_iter() - .rev() - .for_each(|value| array.insert(0, value)), - Operator::Filter => { - array.retain(|item| !values.contains(item)) - } - _ => {} - } - } - Ok(()) - } - _ => unreachable!(), - }) - }) - }) - .find(|e| e.is_err()) - .unwrap_or_else(|| Ok(())) - .map(|_| patch) - } - - fn patch(&mut self, patch: HashMap<Key, Value>) -> Result<(), String> { - patch - .into_iter() - .map(|(key, val)| match (&val, &key.kind) { - (Value::Str(_), Primitive::Indexed(ref index_name, ref index_kind)) => { - value_check(self, index_name, index_kind) - .map_err(|why| format!("assignment error: {}: {}", key.name, why)) - .and_then(|index| match index { - Value::Array(_) => Err("index variable cannot be an array".to_string()), - Value::HashMap(_) => Err("index variable cannot be a hmap".to_string()), - Value::BTreeMap(_) => { - Err("index variable cannot be a bmap".to_string()) + match (&rhs, &key.kind) { + (Value::HashMap(_), Primitive::Indexed(..)) => { + Err("cannot insert hmap into index".to_string()) } - Value::Str(ref index) => self - .variables - .get_mut(key.name) - .map(|lhs| match lhs { - Value::HashMap(hmap) => { - hmap.insert(index.clone(), val); - Ok(()) - } - Value::BTreeMap(bmap) => { - bmap.insert(index.clone(), val); - Ok(()) - } - Value::Array(array) => index - .parse::<usize>() - .map_err(|_| { - format!( - "index variable is not a numeric value: `{}`", - index - ) - }) - .map(|index_num| { - if let (Some(var), Value::Str(value)) = - (array.get_mut(index_num), val) - { - *var = value; - } - }), - _ => Ok(()), - }) - .unwrap_or(Ok(())), - _ => Ok(()), - }) - } - (Value::Str(_), _) - | (Value::HashMap(_), Primitive::HashMap(_)) - | (Value::BTreeMap(_), Primitive::BTreeMap(_)) - | (Value::Array(_), _) => { - self.variables.set(key.name, val); - Ok(()) - } - _ => Ok(()), - }) - .find(|e| e.is_err()) - .unwrap_or_else(|| Ok(())) + (Value::BTreeMap(_), Primitive::Indexed(..)) => { + Err("cannot insert bmap into index".to_string()) + } + (Value::Array(_), Primitive::Indexed(..)) => { + Err("multi-dimensional arrays are not yet supported".to_string()) + } + _ => { + backup.push((key, rhs)); + Ok(()) + } + } + } else { + self.overwrite(key, operator, rhs) + } + })?; + } + Ok(backup) } fn local(&mut self, action: LocalAction) -> i32 { @@ -306,7 +179,12 @@ impl VariableStore for Shell { } LocalAction::Assign(ref keys, op, ref vals) => { let actions = AssignmentActions::new(keys, op, vals); - if let Err(why) = self.create_patch(actions).and_then(|patch| self.patch(patch)) { + if let Err(why) = self.calculate(actions).and_then(|apply| { + for (key, value) in apply { + self.assign(key, value)? + } + Ok(()) + }) { eprintln!("ion: assignment error: {}", why); FAILURE } else { @@ -318,7 +196,7 @@ impl VariableStore for Shell { } #[derive(Debug)] -enum MathError { +pub enum MathError { RHS, LHS, Unsupported, @@ -336,7 +214,7 @@ impl Display for MathError { } } -fn parse<T: str::FromStr, F: Fn(T) -> Option<f64>>( +pub fn parse<T: str::FromStr, F: Fn(T) -> Option<f64>>( lhs: &str, operation: F, ) -> Result<String, MathError> { @@ -346,7 +224,7 @@ fn parse<T: str::FromStr, F: Fn(T) -> Option<f64>>( .map(|x| x.to_string()) } -fn math<'a>( +pub fn math<'a>( key: &Primitive, operator: Operator, value: &'a str, diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index 91c0b732..4de5cd4e 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -40,6 +40,7 @@ pub(crate) use self::{ }; use self::{ + assignments::{math, parse}, directory_stack::DirectoryStack, flags::*, flow_control::{FlowControl, Function, FunctionError}, @@ -51,7 +52,11 @@ use self::{ }; use crate::{ builtins::{BuiltinMap, BUILTINS}, - parser::{pipelines::Pipeline, Expander, MapKeyIter, MapValueIter, Select, Terminator}, + lexers::{Key, Operator, Primitive}, + parser::{ + assignments::value_check, pipelines::Pipeline, Expander, MapKeyIter, MapValueIter, Select, + Terminator, + }, sys, types::{self, Array}, }; @@ -405,6 +410,123 @@ impl Shell { shell.update_ignore_patterns(&ignore_patterns); shell } + + pub fn assign(&mut self, key: Key, value: Value) -> Result<(), String> { + match (&key.kind, &value) { + (Primitive::Indexed(ref index_name, ref index_kind), Value::Str(_)) => { + let index = value_check(self, index_name, index_kind) + .map_err(|why| format!("{}: {}", key.name, why))?; + + match index { + Value::Str(ref index) => { + let lhs = self + .variables + .get_mut(key.name) + .ok_or_else(|| "index value does not exist".to_string())?; + + match lhs { + Value::HashMap(hmap) => { + let _ = hmap.insert(index.clone(), value); + Ok(()) + } + Value::BTreeMap(bmap) => { + let _ = bmap.insert(index.clone(), value); + Ok(()) + } + Value::Array(array) => { + let index_num = index.parse::<usize>().map_err(|_| { + format!("index variable is not a numeric value: `{}`", index) + })?; + + if let (Some(var), Value::Str(val)) = + (array.get_mut(index_num), value) + { + *var = val; + } + Ok(()) + } + _ => Ok(()), + } + } + Value::Array(_) => Err("index variable cannot be an array".into()), + Value::HashMap(_) => Err("index variable cannot be a hmap".into()), + Value::BTreeMap(_) => Err("index variable cannot be a bmap".into()), + _ => Ok(()), + } + } + (_, Value::Str(_)) + | (_, Value::Array(_)) + | (Primitive::HashMap(_), Value::HashMap(_)) + | (Primitive::BTreeMap(_), Value::BTreeMap(_)) => { + self.variables.set(key.name, value); + Ok(()) + } + _ => Ok(()), + } + } + + pub fn overwrite(&mut self, key: Key, operator: Operator, rhs: Value) -> Result<(), String> { + let lhs = self + .variables + .get_mut(key.name) + .ok_or_else(|| format!("cannot update non existing variable `{}`", key.name))?; + + match lhs { + Value::Str(lhs) => { + if let Value::Str(rhs) = rhs { + let action = math(&key.kind, operator, &rhs).map_err(|why| why.to_string())?; + let value = parse(&lhs, &*action).map_err(|why| why.to_string())?; + *lhs = value.into(); + } + Ok(()) + } + Value::Array(array) => match rhs { + Value::Str(rhs) => match operator { + Operator::Concatenate => { + array.push(rhs.clone()); + Ok(()) + } + Operator::ConcatenateHead => { + array.insert(0, rhs.clone()); + Ok(()) + } + Operator::Filter => { + array.retain(|item| item != &rhs); + Ok(()) + } + _ => math(&Primitive::Float, operator, &rhs) + .and_then(|action| { + array + .iter_mut() + .map(|el| parse(el, &*action).map(|result| *el = result.into())) + .find(|e| e.is_err()) + .unwrap_or(Ok(())) + }) + .map_err(|why| why.to_string()), + }, + Value::Array(values) => { + match operator { + Operator::Concatenate => array.extend(values.clone()), + Operator::ConcatenateHead => values + .into_iter() + .rev() + .for_each(|value| array.insert(0, value.clone())), + Operator::Filter => array.retain(|item| !values.contains(item)), + _ => {} + } + Ok(()) + } + _ => Ok(()), + }, + _ => { + if let Value::Str(_) = rhs { + Err("type does not support this operator".to_string()) + } else { + Ok(()) + } + } + } + } } impl<'a> Expander for Shell { -- GitLab