use crate::common::*; use std::mem; /// Construct a `Tree` from a symbolic expression literal. This macro, and the /// Tree type, are only used in the Parser unit tests, as a concise notation /// representing the expected results of parsing a given string. macro_rules! tree { { ($($child:tt)*) } => { $crate::tree::Tree::List(vec![$(tree!($child),)*]) }; { $atom:ident } => { $crate::tree::Tree::atom(stringify!($atom)) }; { $atom:literal } => { $crate::tree::Tree::atom(format!("\"{}\"", $atom)) }; { # } => { $crate::tree::Tree::atom("#") }; { + } => { $crate::tree::Tree::atom("+") }; } /// A `Tree` is either… #[derive(Debug, PartialEq)] pub(crate) enum Tree<'text> { /// …an atom containing text, or… Atom(Cow<'text, str>), /// …a list containing zero or more `Tree`s. List(Vec>), } impl<'text> Tree<'text> { /// Construct an Atom from a text scalar pub(crate) fn atom(text: impl Into>) -> Tree<'text> { Tree::Atom(text.into()) } /// Construct a List from an iterable of trees pub(crate) fn list(children: impl IntoIterator>) -> Tree<'text> { Tree::List(children.into_iter().collect()) } /// Convenience function to create an atom containing quoted text pub(crate) fn string(contents: impl AsRef) -> Tree<'text> { Tree::atom(format!("\"{}\"", contents.as_ref())) } /// Push a child node into self, turning it into a List if it was an Atom pub(crate) fn push(self, tree: impl Into>) -> Tree<'text> { match self { Tree::List(mut children) => { children.push(tree.into()); Tree::List(children) }, Tree::Atom(text) => Tree::List(vec![Tree::Atom(text), tree.into()]), } } /// Extend a self with a tail of Trees, turning self into a List if it was an /// Atom pub(crate) fn extend(self, tail: I) -> Tree<'text> where I: IntoIterator, T: Into>, { // Tree::List(children.into_iter().collect()) let mut head = match self { Tree::List(children) => children, Tree::Atom(text) => vec![Tree::Atom(text)], }; for child in tail { head.push(child.into()); } Tree::List(head) } /// Like `push`, but modify self in-place pub(crate) fn push_mut(&mut self, tree: impl Into>) { let tree = mem::replace(self, Tree::List(Vec::new())).push(tree.into()); mem::replace(self, tree); } } impl Display for Tree<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Tree::List(children) => { write!(f, "(")?; for (i, child) in children.iter().enumerate() { if i > 0 { write!(f, " ")?; } write!(f, "{}", child)?; } write!(f, ")") }, Tree::Atom(text) => write!(f, "{}", text), } } } impl<'text, T> From for Tree<'text> where T: Into>, { fn from(text: T) -> Tree<'text> { Tree::Atom(text.into()) } }