diff --git a/lib/src/atom/mod.rs b/lib/src/atom/mod.rs index e15f0853f..9293296ef 100644 --- a/lib/src/atom/mod.rs +++ b/lib/src/atom/mod.rs @@ -117,7 +117,7 @@ use std::any::Any; use std::fmt::{Display, Debug}; use std::convert::TryFrom; -use crate::common::collections::ImmutableString; +use crate::common::collections::{ImmutableString, CowArray}; // Symbol atom @@ -152,14 +152,14 @@ impl Display for SymbolAtom { /// An expression atom structure. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ExpressionAtom { - children: Vec, + children: CowArray, } impl ExpressionAtom { /// Constructs new expression from vector of sub-atoms. Not intended to be /// used directly, use [Atom::expr] instead. #[doc(hidden)] - pub(crate) fn new(children: Vec) -> Self { + pub(crate) fn new(children: CowArray) -> Self { Self{ children } } @@ -170,17 +170,17 @@ impl ExpressionAtom { /// Returns a reference to a vector of sub-atoms. pub fn children(&self) -> &[Atom] { - &self.children + self.children.as_slice() } /// Returns a mutable reference to a vector of sub-atoms. pub fn children_mut(&mut self) -> &mut [Atom] { - &mut self.children + self.children.as_slice_mut() } /// Converts into a vector of sub-atoms. pub fn into_children(self) -> Vec { - self.children + self.children.into() } } @@ -837,7 +837,7 @@ impl Atom { /// assert_eq!(expr, same_expr); /// assert_ne!(expr, other_expr); /// ``` - pub fn expr>>(children: T) -> Self { + pub fn expr>>(children: T) -> Self { Self::Expression(ExpressionAtom::new(children.into())) } @@ -1093,8 +1093,8 @@ mod test { } #[inline] - fn expression(children: Vec) -> Atom { - Atom::Expression(ExpressionAtom{ children }) + fn expression(children: [Atom; N]) -> Atom { + Atom::Expression(ExpressionAtom::new(CowArray::Allocated(Box::new(children)))) } #[inline] @@ -1175,15 +1175,15 @@ mod test { #[test] fn test_expr_expression() { assert_eq!(expr!("=" ("fact" n) ("*" n ("-" n "1"))), - expression(vec![symbol("="), expression(vec![symbol("fact"), variable("n")]), - expression(vec![symbol("*"), variable("n"), - expression(vec![symbol("-"), variable("n"), symbol("1") ]) ]) ])); + expression([symbol("="), expression([symbol("fact"), variable("n")]), + expression([symbol("*"), variable("n"), + expression([symbol("-"), variable("n"), symbol("1") ]) ]) ])); assert_eq!(expr!("=" n {[1, 2, 3]}), - expression(vec![symbol("="), variable("n"), value([1, 2, 3])])); + expression([symbol("="), variable("n"), value([1, 2, 3])])); assert_eq!(expr!("=" {6} ("fact" n)), - expression(vec![symbol("="), value(6), expression(vec![symbol("fact"), variable("n")])])); + expression([symbol("="), value(6), expression([symbol("fact"), variable("n")])])); assert_eq!(expr!({TestMulX(3)} {TestInteger(6)}), - expression(vec![grounded(TestMulX(3)), grounded(TestInteger(6))])); + expression([grounded(TestMulX(3)), grounded(TestInteger(6))])); } #[test] @@ -1239,8 +1239,8 @@ mod test { assert_eq!(Atom::gnd(TestMulX(3)).clone(), grounded(TestMulX(3))); assert_eq!(Atom::expr([Atom::sym("="), Atom::value(6), Atom::expr([Atom::sym("fact"), Atom::var("n")])]).clone(), - expression(vec![symbol("="), value(6), - expression(vec![symbol("fact"), variable("n")])])); + expression([symbol("="), value(6), + expression([symbol("fact"), variable("n")])])); } #[test] diff --git a/lib/src/common/collections.rs b/lib/src/common/collections.rs index da8f318d1..b1fb834cd 100644 --- a/lib/src/common/collections.rs +++ b/lib/src/common/collections.rs @@ -161,6 +161,97 @@ impl From for ImmutableString { } } +#[derive(Debug, Clone)] +pub enum CowArray { + Allocated(Box<[T]>), + Literal(&'static [T]), +} + +impl CowArray { + + pub fn new() -> Self { + Self::Literal(&[]) + } + + pub fn as_slice(&self) -> &[T] { + match self { + Self::Allocated(array) => &*array, + Self::Literal(array) => array, + } + } + + pub fn as_slice_mut(&mut self) -> &mut [T] where T: Clone { + match self { + Self::Allocated(array) => &mut *array, + Self::Literal(array) => { + *self = Self::Allocated((*array).into()); + self.as_slice_mut() + } + } + } + + pub fn len(&self) -> usize { + self.as_slice().len() + } + + pub fn iter(&self) -> impl Iterator { + self.as_slice().iter() + } +} + +impl PartialEq for CowArray { + fn eq(&self, other: &Self) -> bool { + self.as_slice() == other.as_slice() + } +} + +impl Eq for CowArray {} + +impl Display for CowArray { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[") + .and_then(|_| self.as_slice().iter().take(1).fold(Ok(()), + |res, atom| res.and_then(|_| write!(f, "{}", atom)))) + .and_then(|_| self.as_slice().iter().skip(1).fold(Ok(()), + |res, atom| res.and_then(|_| write!(f, " {}", atom)))) + .and_then(|_| write!(f, "]")) + } +} + +impl From<&'static [T]> for CowArray { + fn from(a: &'static [T]) -> Self { + CowArray::Literal(a) + } +} + +impl From<[T; N]> for CowArray { + fn from(a: [T; N]) -> Self { + CowArray::Allocated(Box::new(a)) + } +} + +impl From> for CowArray { + fn from(v: Vec) -> Self { + CowArray::Allocated(v.into_boxed_slice()) + } +} + +impl Into> for CowArray { + fn into(self) -> Vec { + match self { + Self::Allocated(array) => array.into(), + Self::Literal(array) => array.into(), + } + } +} + +impl<'a, T> Into<&'a [T]> for &'a CowArray { + fn into(self) -> &'a [T] { + self.as_slice() + } +} + + #[cfg(test)] mod test { use super::*; diff --git a/lib/src/metta/interpreter_minimal.rs b/lib/src/metta/interpreter_minimal.rs index e5133fa58..650533e2d 100644 --- a/lib/src/metta/interpreter_minimal.rs +++ b/lib/src/metta/interpreter_minimal.rs @@ -7,6 +7,7 @@ use crate::space::*; use crate::metta::*; use crate::metta::types::*; use crate::metta::runner::stdlib_minimal::IfEqualOp; +use crate::common::collections::CowArray; use std::fmt::{Debug, Display, Formatter}; use std::convert::TryFrom; @@ -679,11 +680,11 @@ fn collapse_bind_ret(stack: Rc>, atom: Atom, bindings: Bindings) let Stack{ prev: _, atom: collapse, ret: _, finished: _, vars: _ } = stack_ref; match atom_as_slice_mut(collapse) { Some([_op, Atom::Expression(finished_placeholder), _bindings]) => { - let mut finished = ExpressionAtom::new(Vec::new()); + let mut finished = ExpressionAtom::new(CowArray::new()); std::mem::swap(&mut finished, finished_placeholder); let mut finished = finished.into_children(); finished.push(atom_bindings_into_atom(nested, bindings)); - std::mem::swap(&mut ExpressionAtom::new(finished), finished_placeholder); + std::mem::swap(&mut ExpressionAtom::new(finished.into()), finished_placeholder); }, _ => panic!("Unexpected state"), };