From 47bb706821d59799c315c55f3aa9a790ba6619ad Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Mon, 16 Mar 2020 11:44:03 +0800 Subject: [PATCH] feat: Make arrays able to nest Prior to this commit, the following type was considered invalid: `[[float]]` Of course, sometimes it is helpful to have arrays within arrays, and as such, this patch allows for nested arrays. To do this, the array types in `Primitive` have all been converted to a `Primitive::Array` type, that holds a `Box<>` to another `Primitive`. There is - of course - a slight performance penalty to be expected with moving to using another `Box` for arrays, however after having run the benchmarks, the difference appears to be negligible. --- src/lib/assignments/actions.rs | 15 +++++-------- src/lib/assignments/checker.rs | 18 +++++---------- src/lib/parser/lexers/assignments/keys.rs | 14 ++++++------ .../parser/lexers/assignments/primitive.rs | 22 +++++-------------- src/lib/parser/statement/functions.rs | 2 +- 5 files changed, 24 insertions(+), 47 deletions(-) diff --git a/src/lib/assignments/actions.rs b/src/lib/assignments/actions.rs index 4294771c..5ac31109 100644 --- a/src/lib/assignments/actions.rs +++ b/src/lib/assignments/actions.rs @@ -107,10 +107,7 @@ impl<'a> Action<'a> { ) -> Result, AssignmentError<'a>> { match var.kind { Primitive::Indexed(..) | Primitive::Str => Ok(Action(var, operator, value)), - Primitive::StrArray - | Primitive::BooleanArray - | Primitive::FloatArray - | Primitive::IntegerArray + Primitive::Array(_) | Primitive::HashMap(_) | Primitive::BTreeMap(_) => { if is_array { @@ -120,7 +117,7 @@ impl<'a> Action<'a> { } } _ if !is_array => Ok(Action(var, operator, value)), - _ => Err(AssignmentError::InvalidValue(var.kind, Primitive::StrArray)), + _ => Err(AssignmentError::InvalidValue(var.kind, Primitive::Array(Box::new(Primitive::Str)))), } } } @@ -167,7 +164,7 @@ mod tests { assert_eq!( actions[1], Ok(Action( - Key { name: "b", kind: Primitive::StrArray }, + Key { name: "b", kind: Primitive::Array(Box::new(Primitive::Str)) }, Operator::Equal, "[two three]", )) @@ -175,7 +172,7 @@ mod tests { assert_eq!( actions[2], Ok(Action( - Key { name: "c", kind: Primitive::IntegerArray }, + Key { name: "c", kind: Primitive::Array(Box::new(Primitive::Integer)) }, Operator::Equal, "[4 5 6]", )) @@ -186,7 +183,7 @@ mod tests { assert_eq!(actions.len(), 3); assert_eq!( actions[0], - Ok(Action(Key { name: "a", kind: Primitive::StrArray }, Operator::Equal, "[one two]",)) + Ok(Action(Key { name: "a", kind: Primitive::Array(Box::new(Primitive::Str)) }, Operator::Equal, "[one two]",)) ); assert_eq!( actions[1], @@ -195,7 +192,7 @@ mod tests { assert_eq!( actions[2], Ok(Action( - Key { name: "c", kind: Primitive::StrArray }, + Key { name: "c", kind: Primitive::Array(Box::new(Primitive::Str)) }, Operator::Equal, "[four five]", )) diff --git a/src/lib/assignments/checker.rs b/src/lib/assignments/checker.rs index d51877de..50c8b3ee 100644 --- a/src/lib/assignments/checker.rs +++ b/src/lib/assignments/checker.rs @@ -99,21 +99,13 @@ pub fn value_check( if is_array(value) { let extracted = shell.get_array(value)?; match expected { - Primitive::StrArray | Primitive::Str => extracted + Primitive::Str => extracted .iter() .map(|item| value_check(shell, item, &Primitive::Str)) .collect::>(), - Primitive::BooleanArray => extracted + Primitive::Array(ref inner) => extracted .iter() - .map(|item| value_check(shell, item, &Primitive::Boolean)) - .collect::>(), - Primitive::IntegerArray => extracted - .iter() - .map(|item| value_check(shell, item, &Primitive::Integer)) - .collect::>(), - Primitive::FloatArray => extracted - .iter() - .map(|item| value_check(shell, item, &Primitive::Float)) + .map(|item| value_check(shell, item, inner)) .collect::>(), Primitive::HashMap(_) | Primitive::BTreeMap(_) => get_map_of(expected, shell, value), Primitive::Indexed(_, ref kind) => value_check(shell, value, kind), @@ -181,13 +173,13 @@ mod test { #[test] fn is_integer_array_() { assert_eq!( - value_check(&mut DummyExpander, "[1 2 3]", &Primitive::IntegerArray).unwrap(), + value_check(&mut DummyExpander, "[1 2 3]", &Primitive::Array(Box::new(Primitive::Integer))).unwrap(), Value::Array(vec![ Value::Str("1".into()), Value::Str("2".into()), Value::Str("3".into()) ]) ); - assert!(value_check(&mut DummyExpander, "[1 2 three]", &Primitive::IntegerArray).is_err()); + assert!(value_check(&mut DummyExpander, "[1 2 three]", &Primitive::Array(Box::new(Primitive::Integer))).is_err()); } } diff --git a/src/lib/parser/lexers/assignments/keys.rs b/src/lib/parser/lexers/assignments/keys.rs index e0c83f9f..6a256a0d 100644 --- a/src/lib/parser/lexers/assignments/keys.rs +++ b/src/lib/parser/lexers/assignments/keys.rs @@ -63,7 +63,7 @@ impl<'a> KeyIterator<'a> { && (eol || self.data.as_bytes()[self.read + 1] == b' ') { let kind = match &self.data[index_ident_start..self.read] { - "" => Primitive::StrArray, + "" => Primitive::Array(Box::new(Primitive::Str)), s => Primitive::Indexed(s.to_owned(), Box::new(Primitive::Str)), }; self.read += 1; @@ -82,7 +82,7 @@ impl<'a> KeyIterator<'a> { } let kind = match &self.data[index_ident_start..index_ident_end] { - "" => Primitive::StrArray, + "" => Primitive::Array(Box::new(Primitive::Str)), s => match Primitive::parse(&self.data[index_ident_end + 2..self.read]) { Some(kind) => Primitive::Indexed(s.to_owned(), Box::new(kind)), None => { @@ -171,10 +171,10 @@ mod tests { p:bmap[hmap[bool]] d:a", ); assert_eq!(parser.next().unwrap(), Ok(Key { name: "a", kind: Primitive::Integer },)); - assert_eq!(parser.next().unwrap(), Ok(Key { name: "b", kind: Primitive::StrArray },)); + assert_eq!(parser.next().unwrap(), Ok(Key { name: "b", kind: Primitive::Array(Box::new(Primitive::Str)) },)); assert_eq!(parser.next().unwrap(), Ok(Key { name: "c", kind: Primitive::Boolean },)); assert_eq!(parser.next().unwrap(), Ok(Key { name: "d", kind: Primitive::Str },)); - assert_eq!(parser.next().unwrap(), Ok(Key { name: "e", kind: Primitive::IntegerArray },)); + assert_eq!(parser.next().unwrap(), Ok(Key { name: "e", kind: Primitive::Array(Box::new(Primitive::Integer)) },)); assert_eq!( parser.next().unwrap(), Ok(Key { name: "f", kind: Primitive::Indexed("0".into(), Box::new(Primitive::Str)) },) @@ -203,14 +203,14 @@ mod tests { ); assert_eq!( parser.next().unwrap(), - Ok(Key { name: "k", kind: Primitive::HashMap(Box::new(Primitive::IntegerArray)) },) + Ok(Key { name: "k", kind: Primitive::HashMap(Box::new(Primitive::Array(Box::new(Primitive::Integer)))) },) ); assert_eq!( parser.next().unwrap(), Ok(Key { name: "l", kind: Primitive::HashMap(Box::new(Primitive::HashMap(Box::new( - Primitive::BooleanArray + Primitive::Array(Box::new(Primitive::Boolean)) )))), },) ); @@ -224,7 +224,7 @@ mod tests { ); assert_eq!( parser.next().unwrap(), - Ok(Key { name: "o", kind: Primitive::BTreeMap(Box::new(Primitive::FloatArray)) },) + Ok(Key { name: "o", kind: Primitive::BTreeMap(Box::new(Primitive::Array(Box::new(Primitive::Float)))) },) ); assert_eq!( parser.next().unwrap(), diff --git a/src/lib/parser/lexers/assignments/primitive.rs b/src/lib/parser/lexers/assignments/primitive.rs index 571badd1..0e07958c 100644 --- a/src/lib/parser/lexers/assignments/primitive.rs +++ b/src/lib/parser/lexers/assignments/primitive.rs @@ -5,20 +5,14 @@ use std::fmt::{self, Display, Formatter}; pub enum Primitive { /// A plain string (ex: `"a string"`) Str, - /// An array of string (ex: `["-" b c d]`) - StrArray, /// A true-false value Boolean, - /// An array of booleans - BooleanArray, /// An integer numeric type Integer, - /// An array of integer numeric type - IntegerArray, /// A floating-point value Float, - /// A floating-point value array - FloatArray, + /// Arrays + Array(Box), /// A hash map HashMap(Box), /// A btreemap @@ -31,13 +25,9 @@ impl Primitive { pub(crate) fn parse(data: &str) -> Option { match data { "str" => Some(Primitive::Str), - "[str]" => Some(Primitive::StrArray), "bool" => Some(Primitive::Boolean), - "[bool]" => Some(Primitive::BooleanArray), "int" => Some(Primitive::Integer), - "[int]" => Some(Primitive::IntegerArray), "float" => Some(Primitive::Float), - "[float]" => Some(Primitive::FloatArray), _ => { let open_bracket = data.find('[')?; let close_bracket = data.rfind(']')?; @@ -49,7 +39,8 @@ impl Primitive { } else if kind == "bmap" { Some(Primitive::BTreeMap(Box::new(Self::parse(inner)?))) } else { - None + // It's an array + Some(Primitive::Array(Box::new(Self::parse(inner)?))) } } } @@ -60,13 +51,10 @@ impl Display for Primitive { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match *self { Primitive::Str => write!(f, "str"), - Primitive::StrArray => write!(f, "[str]"), Primitive::Boolean => write!(f, "bool"), - Primitive::BooleanArray => write!(f, "[bool]"), Primitive::Float => write!(f, "float"), - Primitive::FloatArray => write!(f, "[float]"), Primitive::Integer => write!(f, "int"), - Primitive::IntegerArray => write!(f, "[int]"), + Primitive::Array(ref kind) => write!(f, "[{}]", kind), Primitive::HashMap(ref kind) => match **kind { Primitive::Str => write!(f, "hmap[]"), ref kind => write!(f, "hmap[{}]", kind), diff --git a/src/lib/parser/statement/functions.rs b/src/lib/parser/statement/functions.rs index a95220cc..f5ebe1d8 100644 --- a/src/lib/parser/statement/functions.rs +++ b/src/lib/parser/statement/functions.rs @@ -56,7 +56,7 @@ mod tests { Ok(vec![ KeyBuf { name: "a".into(), kind: Primitive::Integer }, KeyBuf { name: "b".into(), kind: Primitive::Boolean }, - KeyBuf { name: "c".into(), kind: Primitive::StrArray }, + KeyBuf { name: "c".into(), kind: Primitive::Array(Box::new(Primitive::Str)) }, KeyBuf { name: "d".into(), kind: Primitive::Str }, ]) ); -- GitLab