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