2022-06-18 21:56:31 -07:00
|
|
|
use super::*;
|
2019-11-07 10:55:15 -08:00
|
|
|
|
2021-04-05 21:28:37 -07:00
|
|
|
use std::{borrow::Cow, mem};
|
2019-11-07 10:55:15 -08:00
|
|
|
|
2020-02-14 04:49:25 -08:00
|
|
|
/// Construct a `Tree` from a symbolic expression literal. This macro, and the
|
2021-03-28 22:38:07 -07:00
|
|
|
/// Tree type, are only used in the Parser unit tests, providing a concise
|
|
|
|
/// notation for representing the expected results of parsing a given string.
|
2019-11-07 10:55:15 -08:00
|
|
|
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("+")
|
|
|
|
};
|
2020-06-13 01:49:13 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
*
|
|
|
|
} => {
|
|
|
|
$crate::tree::Tree::atom("*")
|
|
|
|
};
|
2020-10-26 18:16:42 -07:00
|
|
|
|
2021-07-22 00:20:25 -07:00
|
|
|
{
|
|
|
|
&&
|
|
|
|
} => {
|
|
|
|
$crate::tree::Tree::atom("&&")
|
|
|
|
};
|
|
|
|
|
2020-10-26 18:16:42 -07:00
|
|
|
{
|
|
|
|
==
|
|
|
|
} => {
|
|
|
|
$crate::tree::Tree::atom("==")
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
!=
|
|
|
|
} => {
|
|
|
|
$crate::tree::Tree::atom("!=")
|
|
|
|
};
|
2019-11-07 10:55:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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<Tree<'text>>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'text> Tree<'text> {
|
|
|
|
/// Construct an Atom from a text scalar
|
|
|
|
pub(crate) fn atom(text: impl Into<Cow<'text, str>>) -> Tree<'text> {
|
|
|
|
Tree::Atom(text.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct a List from an iterable of trees
|
|
|
|
pub(crate) fn list(children: impl IntoIterator<Item = Tree<'text>>) -> Tree<'text> {
|
|
|
|
Tree::List(children.into_iter().collect())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convenience function to create an atom containing quoted text
|
|
|
|
pub(crate) fn string(contents: impl AsRef<str>) -> Tree<'text> {
|
|
|
|
Tree::atom(format!("\"{}\"", contents.as_ref()))
|
|
|
|
}
|
|
|
|
|
2020-02-14 04:49:25 -08:00
|
|
|
/// Push a child node into self, turning it into a List if it was an Atom
|
2019-11-07 10:55:15 -08:00
|
|
|
pub(crate) fn push(self, tree: impl Into<Tree<'text>>) -> Tree<'text> {
|
|
|
|
match self {
|
|
|
|
Tree::List(mut children) => {
|
|
|
|
children.push(tree.into());
|
|
|
|
Tree::List(children)
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2019-11-07 10:55:15 -08:00
|
|
|
Tree::Atom(text) => Tree::List(vec![Tree::Atom(text), tree.into()]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 04:49:25 -08:00
|
|
|
/// Extend a self with a tail of Trees, turning self into a List if it was an
|
|
|
|
/// Atom
|
2019-11-07 10:55:15 -08:00
|
|
|
pub(crate) fn extend<I, T>(self, tail: I) -> Tree<'text>
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = T>,
|
|
|
|
T: Into<Tree<'text>>,
|
|
|
|
{
|
|
|
|
// 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<Tree<'text>>) {
|
2020-07-16 21:37:33 -07:00
|
|
|
*self = mem::replace(self, Tree::List(Vec::new())).push(tree.into());
|
2019-11-07 10:55:15 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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, " ")?;
|
|
|
|
}
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "{child}")?;
|
2019-11-07 10:55:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
write!(f, ")")
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2022-12-15 16:53:21 -08:00
|
|
|
Tree::Atom(text) => write!(f, "{text}"),
|
2019-11-07 10:55:15 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'text, T> From<T> for Tree<'text>
|
|
|
|
where
|
|
|
|
T: Into<Cow<'text, str>>,
|
|
|
|
{
|
|
|
|
fn from(text: T) -> Tree<'text> {
|
|
|
|
Tree::Atom(text.into())
|
|
|
|
}
|
|
|
|
}
|