1 use crate::common::*;
2 
3 use std::{borrow::Cow, mem};
4 
5 /// Construct a `Tree` from a symbolic expression literal. This macro, and the
6 /// Tree type, are only used in the Parser unit tests, providing a concise
7 /// notation for representing the expected results of parsing a given string.
8 macro_rules! tree {
9   {
10     ($($child:tt)*)
11   } => {
12     $crate::tree::Tree::List(vec![$(tree!($child),)*])
13   };
14 
15   {
16     $atom:ident
17   } => {
18     $crate::tree::Tree::atom(stringify!($atom))
19   };
20 
21   {
22     $atom:literal
23   } => {
24     $crate::tree::Tree::atom(format!("\"{}\"", $atom))
25   };
26 
27   {
28     #
29   } => {
30     $crate::tree::Tree::atom("#")
31   };
32 
33   {
34     +
35   } => {
36     $crate::tree::Tree::atom("+")
37   };
38 
39   {
40     *
41   } => {
42     $crate::tree::Tree::atom("*")
43   };
44 
45   {
46     &&
47   } => {
48     $crate::tree::Tree::atom("&&")
49   };
50 
51   {
52     ==
53   } => {
54     $crate::tree::Tree::atom("==")
55   };
56 
57   {
58     !=
59   } => {
60     $crate::tree::Tree::atom("!=")
61   };
62 }
63 
64 /// A `Tree` is either…
65 #[derive(Debug, PartialEq)]
66 pub(crate) enum Tree<'text> {
67   /// …an atom containing text, or…
68   Atom(Cow<'text, str>),
69   /// …a list containing zero or more `Tree`s.
70   List(Vec<Tree<'text>>),
71 }
72 
73 impl<'text> Tree<'text> {
74   /// Construct an Atom from a text scalar
atom(text: impl Into<Cow<'text, str>>) -> Tree<'text>75   pub(crate) fn atom(text: impl Into<Cow<'text, str>>) -> Tree<'text> {
76     Tree::Atom(text.into())
77   }
78 
79   /// Construct a List from an iterable of trees
list(children: impl IntoIterator<Item = Tree<'text>>) -> Tree<'text>80   pub(crate) fn list(children: impl IntoIterator<Item = Tree<'text>>) -> Tree<'text> {
81     Tree::List(children.into_iter().collect())
82   }
83 
84   /// Convenience function to create an atom containing quoted text
string(contents: impl AsRef<str>) -> Tree<'text>85   pub(crate) fn string(contents: impl AsRef<str>) -> Tree<'text> {
86     Tree::atom(format!("\"{}\"", contents.as_ref()))
87   }
88 
89   /// Push a child node into self, turning it into a List if it was an Atom
push(self, tree: impl Into<Tree<'text>>) -> Tree<'text>90   pub(crate) fn push(self, tree: impl Into<Tree<'text>>) -> Tree<'text> {
91     match self {
92       Tree::List(mut children) => {
93         children.push(tree.into());
94         Tree::List(children)
95       }
96       Tree::Atom(text) => Tree::List(vec![Tree::Atom(text), tree.into()]),
97     }
98   }
99 
100   /// Extend a self with a tail of Trees, turning self into a List if it was an
101   /// Atom
extend<I, T>(self, tail: I) -> Tree<'text> where I: IntoIterator<Item = T>, T: Into<Tree<'text>>,102   pub(crate) fn extend<I, T>(self, tail: I) -> Tree<'text>
103   where
104     I: IntoIterator<Item = T>,
105     T: Into<Tree<'text>>,
106   {
107     // Tree::List(children.into_iter().collect())
108     let mut head = match self {
109       Tree::List(children) => children,
110       Tree::Atom(text) => vec![Tree::Atom(text)],
111     };
112 
113     for child in tail {
114       head.push(child.into());
115     }
116 
117     Tree::List(head)
118   }
119 
120   /// Like `push`, but modify self in-place
push_mut(&mut self, tree: impl Into<Tree<'text>>)121   pub(crate) fn push_mut(&mut self, tree: impl Into<Tree<'text>>) {
122     *self = mem::replace(self, Tree::List(Vec::new())).push(tree.into());
123   }
124 }
125 
126 impl Display for Tree<'_> {
fmt(&self, f: &mut Formatter) -> fmt::Result127   fn fmt(&self, f: &mut Formatter) -> fmt::Result {
128     match self {
129       Tree::List(children) => {
130         write!(f, "(")?;
131 
132         for (i, child) in children.iter().enumerate() {
133           if i > 0 {
134             write!(f, " ")?;
135           }
136           write!(f, "{}", child)?;
137         }
138 
139         write!(f, ")")
140       }
141       Tree::Atom(text) => write!(f, "{}", text),
142     }
143   }
144 }
145 
146 impl<'text, T> From<T> for Tree<'text>
147 where
148   T: Into<Cow<'text, str>>,
149 {
from(text: T) -> Tree<'text>150   fn from(text: T) -> Tree<'text> {
151     Tree::Atom(text.into())
152   }
153 }
154