diff --git a/examples/maps.ion b/examples/maps.ion new file mode 100644 index 0000000000000000000000000000000000000000..64c90c9c442dd91e281242fffeb8713bd76eb105 --- /dev/null +++ b/examples/maps.ion @@ -0,0 +1,24 @@ +let map:hmap[] = [key1=one key3=three] +echo @map +let map[key2] = two +echo @map +echo @map[key1] +echo @map[key2] +echo @map[key3] + +let map:hmap[int] = [uno=1 dos=2 tres=3] +echo @map + +let map:hmap[int[]] = [key1=[1 2 3 4 5] key2=[6 7 8]] +echo @map + +let map:bmap[] = [key1=one key3=three] +echo @map +let map[key2] = two +echo @map +echo @map[key1] +echo @map[key2] +echo @map[key3] + +let map:bmap[float] = [ichi=1.0 ni=2.0 san=3.0] +echo @map diff --git a/examples/maps.out b/examples/maps.out new file mode 100644 index 0000000000000000000000000000000000000000..ba0dd15afb3183fe83e0ee50b28f57b63ab7d349 --- /dev/null +++ b/examples/maps.out @@ -0,0 +1,13 @@ +one three +one three two +one +two +three +3 2 1 +1 2 3 4 5 6 7 8 +one three +one two three +one +two +three +1.0 2.0 3.0 diff --git a/members/lexers/src/assignments/keys.rs b/members/lexers/src/assignments/keys.rs index fe2ce4750b62f2c63071ae83e1a1b7227c1e84c2..cf503166d6e0a162d9f0c543760f6c4a70b10ac9 100644 --- a/members/lexers/src/assignments/keys.rs +++ b/members/lexers/src/assignments/keys.rs @@ -165,6 +165,7 @@ mod tests { let mut parser = KeyIterator::new("a:int b[] c:bool d e:int[] \ f[0] g[$index] h[1]:int \ i:hmap[] j:hmap[float] k:hmap[int[]] l:hmap[hmap[bool[]]] \ + m:bmap[] n:bmap[int] o:bmap[float[]] p:bmap[hmap[bool]] \ d:a"); assert_eq!( parser.next().unwrap(), @@ -250,6 +251,34 @@ mod tests { kind: Primitive::HashMap(Box::new(Primitive::HashMap(Box::new(Primitive::BooleanArray)))), },) ); + assert_eq!( + parser.next().unwrap(), + Ok(Key { + name: "m", + kind: Primitive::BTreeMap(Box::new(Primitive::Any)), + },) + ); + assert_eq!( + parser.next().unwrap(), + Ok(Key { + name: "n", + kind: Primitive::BTreeMap(Box::new(Primitive::Integer)), + },) + ); + assert_eq!( + parser.next().unwrap(), + Ok(Key { + name: "o", + kind: Primitive::BTreeMap(Box::new(Primitive::FloatArray)), + },) + ); + assert_eq!( + parser.next().unwrap(), + Ok(Key { + name: "p", + kind: Primitive::BTreeMap(Box::new(Primitive::HashMap(Box::new(Primitive::Boolean)))), + },) + ); assert_eq!(parser.next().unwrap(), Err(TypeError::Invalid("a".into()))); } } diff --git a/src/lib/parser/assignments/actions.rs b/src/lib/parser/assignments/actions.rs index 795c6860f0fbd24f43da75c213826682e16c22a2..c997b919af097344091f948d9add74ba181b8e1d 100644 --- a/src/lib/parser/assignments/actions.rs +++ b/src/lib/parser/assignments/actions.rs @@ -101,7 +101,8 @@ impl<'a> Action<'a> { | Primitive::FloatArray | Primitive::IntegerArray | Primitive::StrArray - | Primitive::HashMap(_) => if is_array(value) { + | Primitive::HashMap(_) + | Primitive::BTreeMap(_) => if is_array(value) { Ok(Action::UpdateArray(var, operator, value)) } else { Err(AssignmentError::InvalidValue(var.kind, Primitive::Any)) diff --git a/src/lib/parser/assignments/checker.rs b/src/lib/parser/assignments/checker.rs index 2dd7f9e2c03a448c8649fade92ebdbb9e47c19e5..84fe2e51134636daec58225d5a88fb185bd03a92 100644 --- a/src/lib/parser/assignments/checker.rs +++ b/src/lib/parser/assignments/checker.rs @@ -152,7 +152,7 @@ fn get_array<E: Expander>(shell: &E, value: &str) -> VariableType { fn get_hash_map<E: Expander>(shell: &E, expression: &str, inner_kind: &Primitive) -> Result<VariableType, TypeError> { let array = expand_string(expression, shell, false); - let mut hash_map: HashMap = HashMap::with_capacity_and_hasher(array.len(), Default::default()); + let mut hmap: HashMap = HashMap::with_capacity_and_hasher(array.len(), Default::default()); for string in array { if let Some(found) = string.find('=') { @@ -160,13 +160,49 @@ fn get_hash_map<E: Expander>(shell: &E, expression: &str, inner_kind: &Primitive let value = &string[found + 1..]; match value_check(shell, value, inner_kind) { Ok(VariableType::Str(str_)) => { - hash_map.insert(key.into(), VariableType::Str(str_)); + hmap.insert(key.into(), VariableType::Str(str_)); } Ok(VariableType::Array(array)) => { - hash_map.insert(key.into(), VariableType::Array(array)); + hmap.insert(key.into(), VariableType::Array(array)); } Ok(VariableType::HashMap(map)) => { - hash_map.insert(key.into(), VariableType::HashMap(map)); + hmap.insert(key.into(), VariableType::HashMap(map)); + } + Ok(VariableType::BTreeMap(map)) => { + hmap.insert(key.into(), VariableType::BTreeMap(map)); + } + Err(type_error) => return Err(type_error), + _ => (), + } + + } else { + return Err(TypeError::BadValue(inner_kind.clone())) + } + } + + Ok(VariableType::HashMap(hmap)) +} + +fn get_btree_map<E: Expander>(shell: &E, expression: &str, inner_kind: &Primitive) -> Result<VariableType, TypeError> { + let array = expand_string(expression, shell, false); + let mut bmap: BTreeMap = BTreeMap::new(); + + for string in array { + if let Some(found) = string.find('=') { + let key = &string[..found]; + let value = &string[found + 1..]; + match value_check(shell, value, inner_kind) { + Ok(VariableType::Str(str_)) => { + bmap.insert(key.into(), VariableType::Str(str_)); + } + Ok(VariableType::Array(array)) => { + bmap.insert(key.into(), VariableType::Array(array)); + } + Ok(VariableType::HashMap(map)) => { + bmap.insert(key.into(), VariableType::HashMap(map)); + } + Ok(VariableType::BTreeMap(map)) => { + bmap.insert(key.into(), VariableType::BTreeMap(map)); } Err(type_error) => return Err(type_error), _ => (), @@ -177,7 +213,7 @@ fn get_hash_map<E: Expander>(shell: &E, expression: &str, inner_kind: &Primitive } } - Ok(VariableType::HashMap(hash_map)) + Ok(VariableType::BTreeMap(bmap)) } pub(crate) fn value_check<E: Expander>( @@ -228,8 +264,8 @@ pub(crate) fn value_check<E: Expander>( is_float_array(get_array!()).map_err(|_| TypeError::BadValue(expected.clone())) } Primitive::HashMap(ref kind) if is_array => get_hash_map(shell, value, kind), - Primitive::BTreeMap(_) if is_array => Err(TypeError::BadValue(expected.clone())), - Primitive::Indexed(_, kind) => value_check(shell, value, &*kind), + Primitive::BTreeMap(ref kind) if is_array => get_btree_map(shell, value, kind), + Primitive::Indexed(_, ref kind) => value_check(shell, value, kind), _ => Err(TypeError::BadValue(expected.clone())), } } diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs index 599e52fe604c8e9b9f477301046ea1581483edac..9f4c798ed5571a6e2eb3f718c2ac25f826e96740 100644 --- a/src/lib/shell/assignments.rs +++ b/src/lib/shell/assignments.rs @@ -163,8 +163,11 @@ impl VariableStore for Shell { Ok(VariableType::Str(value)) => { collected.insert(key.name, VariableType::Str(value)); } - Ok(VariableType::HashMap(map)) => { - collected.insert(key.name, VariableType::HashMap(map)); + Ok(VariableType::HashMap(hmap)) => { + collected.insert(key.name, VariableType::HashMap(hmap)); + } + Ok(VariableType::BTreeMap(bmap)) => { + collected.insert(key.name, VariableType::BTreeMap(bmap)); } Err(why) => { eprintln!("ion: assignment error: {}: {}", key.name, why); @@ -371,11 +374,19 @@ impl VariableStore for Shell { match action { Ok(Action::UpdateArray(key, _, _)) => { match collected.remove(key.name) { - map @ Some(VariableType::HashMap(_)) => { + hmap @ Some(VariableType::HashMap(_)) => { if let Primitive::HashMap(_) = key.kind { - self.variables.set(key.name, map.unwrap()); + self.variables.set(key.name, hmap.unwrap()); + } else if let Primitive::Indexed(_, _) = key.kind { + eprintln!("ion: cannot insert hmap into index"); + return FAILURE; + } + } + bmap @ Some(VariableType::BTreeMap(_)) => { + if let Primitive::BTreeMap(_) = key.kind { + self.variables.set(key.name, bmap.unwrap()); } else if let Primitive::Indexed(_, _) = key.kind { - eprintln!("ion: cannot insert hash map into index"); + eprintln!("ion: cannot insert bmap into index"); return FAILURE; } } @@ -392,8 +403,11 @@ impl VariableStore for Shell { match value_check(self, index_value, index_kind) { Ok(VariableType::Str(ref index)) => { match self.variables.get_mut(key.name) { - Some(VariableType::HashMap(map)) => { - map.entry(SmallString::from_str(index)).or_insert(VariableType::Str(value)); + Some(VariableType::HashMap(hmap)) => { + hmap.entry(SmallString::from_str(index)).or_insert(VariableType::Str(value)); + } + Some(VariableType::BTreeMap(bmap)) => { + bmap.entry(index.clone()).or_insert(VariableType::Str(value)); } Some(VariableType::Array(array)) => { let index_num = match index.parse::<usize>() { @@ -415,7 +429,11 @@ impl VariableStore for Shell { return FAILURE; } Ok(VariableType::HashMap(_)) => { - eprintln!("ion: index variable cannot be a hash map"); + eprintln!("ion: index variable cannot be a hmap"); + return FAILURE; + } + Ok(VariableType::BTreeMap(_)) => { + eprintln!("ion: index variable cannot be a bmap"); return FAILURE; } Err(why) => { diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index e3f8173b62652538acc14cf5ed7833fc074f29f6..29b0bb02571249c02b644a0ce40dfe31de918eff 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -455,19 +455,16 @@ impl<'a> Expander for Shell { /// Expand an array variable with some selection fn array(&self, name: &str, selection: Select) -> Option<Array> { - let mut found = match self.variables.get::<Array>(name) { - Some(array) => match selection { - Select::None => None, - Select::All => Some(array.clone()), - Select::Index(id) => id + if let Some(array) = self.variables.get::<Array>(name) { + match selection { + Select::All => return Some(array.clone()), + Select::Index(id) => return id .resolve(array.len()) .and_then(|n| array.get(n)) .map(|x| Array::from_iter(Some(x.to_owned()))), Select::Range(range) => if let Some((start, length)) = range.bounds(array.len()) { - if array.len() <= start { - None - } else { - Some( + if array.len() > start { + return Some( array .iter() .skip(start) @@ -476,41 +473,57 @@ impl<'a> Expander for Shell { .collect::<Array>(), ) } - } else { - None - }, - Select::Key(_) => None, - }, - None => None, - }; - if found.is_none() { - found = match self.variables.get::<HashMap>(name) { - Some(map) => match selection { - Select::All => { - let mut array = Array::new(); - for (_, value) in map.iter() { - let f = format!("{}", value); - match *value { - VariableType::Str(_) => array.push(f), - VariableType::Array(_) | VariableType::HashMap(_) => { - for split in f.split_whitespace() { - array.push(split.to_owned()); - } + } + _ => (), + } + } else if let Some(hmap) = self.variables.get::<HashMap>(name) { + match selection { + Select::All => { + let mut array = Array::new(); + for (_, value) in hmap.iter() { + let f = format!("{}", value); + match *value { + VariableType::Str(_) => array.push(f), + VariableType::Array(_) | VariableType::HashMap(_) | VariableType::BTreeMap(_) => { + for split in f.split_whitespace() { + array.push(split.to_owned()); } - _ => (), } + _ => (), } - Some(array) } - Select::Key(ref key) => { - Some(array![format!("{}", map.get(key).unwrap_or(&VariableType::Str("".into())))]) + return Some(array) + } + Select::Key(ref key) => { + return Some(array![format!("{}", hmap.get(key).unwrap_or(&VariableType::Str("".into())))]) + } + _ => (), + } + } else if let Some(bmap) = self.variables.get::<BTreeMap>(name) { + match selection { + Select::All => { + let mut array = Array::new(); + for (_, value) in bmap.iter() { + let f = format!("{}", value); + match *value { + VariableType::Str(_) => array.push(f), + VariableType::Array(_) | VariableType::HashMap(_) | VariableType::BTreeMap(_) => { + for split in f.split_whitespace() { + array.push(split.to_owned()); + } + } + _ => (), + } } - _ => None, - }, - None => None, + return Some(array) + } + Select::Key(ref key) => { + return Some(array![format!("{}", bmap.get(&(&*key).to_string()).unwrap_or(&VariableType::Str("".into())))]) + } + _ => (), } } - found + None } fn tilde(&self, input: &str) -> Option<String> { diff --git a/src/lib/shell/variables/mod.rs b/src/lib/shell/variables/mod.rs index c93f2b6fa77eff8a88c1894e4b9e5db833bcf0b6..c9281bb3d159f288fb938dbec05498d92fea4808 100644 --- a/src/lib/shell/variables/mod.rs +++ b/src/lib/shell/variables/mod.rs @@ -18,7 +18,7 @@ use std::{ }; use sys::{self, geteuid, getpid, getuid, is_root, variables as self_sys}; use types::{ - self, Alias, Array, HashMap, Identifier, Key, Value, + self, Alias, Array, BTreeMap, HashMap, Identifier, Key, Value, }; use unicode_segmentation::UnicodeSegmentation; use xdg::BaseDirectories; @@ -33,6 +33,7 @@ pub enum VariableType { Alias(Alias), Array(Array), HashMap(HashMap), + BTreeMap(BTreeMap), Function(Function), None, } @@ -73,6 +74,15 @@ impl From<VariableType> for HashMap { } } +impl From<VariableType> for BTreeMap { + fn from(var: VariableType) -> Self { + match var { + VariableType::BTreeMap(btree_map) => btree_map, + _ => BTreeMap::new(), + } + } +} + impl From<VariableType> for Function { fn from(var: VariableType) -> Self { match var { @@ -106,6 +116,12 @@ impl From<HashMap> for VariableType { } } +impl From<BTreeMap> for VariableType { + fn from(btree_map: BTreeMap) -> Self { + VariableType::BTreeMap(btree_map) + } +} + impl From<Function> for VariableType { fn from(function: Function) -> Self { VariableType::Function(function) @@ -127,6 +143,15 @@ impl fmt::Display for VariableType { format.pop(); write!(f, "{}", format) } + VariableType::BTreeMap(ref map) => { + let mut format = map.into_iter().fold(String::new(), |mut format, (_, var_type)| { + format.push_str(&format!("{}", var_type)); + format.push(' '); + format + }); + format.pop(); + write!(f, "{}", format) + } _ => write!(f, "") } } @@ -139,12 +164,14 @@ pub struct Scope { /// Any previous scopes need to be accessed through `super::`. namespace: bool } + impl Deref for Scope { type Target = FnvHashMap<Identifier, VariableType>; fn deref(&self) -> &Self::Target { &self.vars } } + impl DerefMut for Scope { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.vars @@ -508,7 +535,12 @@ impl Variables { } } else if specified_type == TypeId::of::<types::HashMap>() { match self.get_ref(name) { - Some(VariableType::HashMap(hash_map)) => Some(T::from(VariableType::HashMap(hash_map.clone()))), + Some(VariableType::HashMap(hmap)) => Some(T::from(VariableType::HashMap(hmap.clone()))), + _ => None + } + } else if specified_type == TypeId::of::<types::BTreeMap>() { + match self.get_ref(name) { + Some(VariableType::BTreeMap(bmap)) => Some(T::from(VariableType::BTreeMap(bmap.clone()))), _ => None } } else if specified_type == TypeId::of::<Function>() { diff --git a/src/lib/types.rs b/src/lib/types.rs index 96008dba1470575ba51540a35c183776daea12d7..3a5b6944f99d190ee8941033c58f13253bc59c68 100644 --- a/src/lib/types.rs +++ b/src/lib/types.rs @@ -2,10 +2,11 @@ use fnv::FnvHashMap; use smallstring::SmallString; use smallvec::SmallVec; use shell::variables::VariableType; -use std::ops::{Deref, DerefMut}; +use std::{collections::BTreeMap as StdBTreeMap, ops::{Deref, DerefMut}}; pub type Array = SmallVec<[Value; 4]>; pub type HashMap = FnvHashMap<Key, VariableType>; +pub type BTreeMap = StdBTreeMap<String, VariableType>; pub type Identifier = SmallString; pub type Key = SmallString; pub type Value = String;