1 use crate::common::*; 2 3 /// Methods commmon to all AST nodes. Currently only used in parser unit tests. 4 pub(crate) trait Node<'src> { 5 /// Construct an untyped tree of atoms representing this Node. This function, 6 /// and `Tree` type, are only used in parser unit tests. tree(&self) -> Tree<'src>7 fn tree(&self) -> Tree<'src>; 8 } 9 10 impl<'src> Node<'src> for Ast<'src> { tree(&self) -> Tree<'src>11 fn tree(&self) -> Tree<'src> { 12 Tree::atom("justfile") 13 .extend(self.items.iter().map(|item| item.tree())) 14 .extend(self.warnings.iter().map(|warning| warning.tree())) 15 } 16 } 17 18 impl<'src> Node<'src> for Item<'src> { tree(&self) -> Tree<'src>19 fn tree(&self) -> Tree<'src> { 20 match self { 21 Item::Alias(alias) => alias.tree(), 22 Item::Assignment(assignment) => assignment.tree(), 23 Item::Comment(comment) => comment.tree(), 24 Item::Recipe(recipe) => recipe.tree(), 25 Item::Set(set) => set.tree(), 26 } 27 } 28 } 29 30 impl<'src> Node<'src> for Alias<'src, Name<'src>> { tree(&self) -> Tree<'src>31 fn tree(&self) -> Tree<'src> { 32 Tree::atom(Keyword::Alias.lexeme()) 33 .push(self.name.lexeme()) 34 .push(self.target.lexeme()) 35 } 36 } 37 38 impl<'src> Node<'src> for Assignment<'src> { tree(&self) -> Tree<'src>39 fn tree(&self) -> Tree<'src> { 40 if self.export { 41 Tree::atom("assignment") 42 .push("#") 43 .push(Keyword::Export.lexeme()) 44 } else { 45 Tree::atom("assignment") 46 } 47 .push(self.name.lexeme()) 48 .push(self.value.tree()) 49 } 50 } 51 52 impl<'src> Node<'src> for Expression<'src> { tree(&self) -> Tree<'src>53 fn tree(&self) -> Tree<'src> { 54 match self { 55 Expression::Concatination { lhs, rhs } => Tree::atom("+").push(lhs.tree()).push(rhs.tree()), 56 Expression::Conditional { 57 lhs, 58 rhs, 59 then, 60 otherwise, 61 operator, 62 } => { 63 let mut tree = Tree::atom(Keyword::If.lexeme()); 64 tree.push_mut(lhs.tree()); 65 tree.push_mut(operator.to_string()); 66 tree.push_mut(rhs.tree()); 67 tree.push_mut(then.tree()); 68 tree.push_mut(otherwise.tree()); 69 tree 70 } 71 Expression::Call { thunk } => { 72 use Thunk::*; 73 74 let mut tree = Tree::atom("call"); 75 76 match thunk { 77 Nullary { name, .. } => tree.push_mut(name.lexeme()), 78 Unary { name, arg, .. } => { 79 tree.push_mut(name.lexeme()); 80 tree.push_mut(arg.tree()); 81 } 82 Binary { 83 name, args: [a, b], .. 84 } => { 85 tree.push_mut(name.lexeme()); 86 tree.push_mut(a.tree()); 87 tree.push_mut(b.tree()); 88 } 89 BinaryPlus { 90 name, 91 args: ([a, b], rest), 92 .. 93 } => { 94 tree.push_mut(name.lexeme()); 95 tree.push_mut(a.tree()); 96 tree.push_mut(b.tree()); 97 for arg in rest { 98 tree.push_mut(arg.tree()); 99 } 100 } 101 Ternary { 102 name, 103 args: [a, b, c], 104 .. 105 } => { 106 tree.push_mut(name.lexeme()); 107 tree.push_mut(a.tree()); 108 tree.push_mut(b.tree()); 109 tree.push_mut(c.tree()); 110 } 111 } 112 113 tree 114 } 115 Expression::Variable { name } => Tree::atom(name.lexeme()), 116 Expression::StringLiteral { 117 string_literal: StringLiteral { cooked, .. }, 118 } => Tree::string(cooked), 119 Expression::Backtick { contents, .. } => Tree::atom("backtick").push(Tree::string(contents)), 120 Expression::Group { contents } => Tree::List(vec![contents.tree()]), 121 } 122 } 123 } 124 125 impl<'src> Node<'src> for UnresolvedRecipe<'src> { tree(&self) -> Tree<'src>126 fn tree(&self) -> Tree<'src> { 127 let mut t = Tree::atom("recipe"); 128 129 if self.quiet { 130 t.push_mut("#"); 131 t.push_mut("quiet"); 132 } 133 134 if let Some(doc) = self.doc { 135 t.push_mut(Tree::string(doc)); 136 } 137 138 t.push_mut(self.name.lexeme()); 139 140 if !self.parameters.is_empty() { 141 let mut params = Tree::atom("params"); 142 143 for parameter in &self.parameters { 144 if let Some(prefix) = parameter.kind.prefix() { 145 params.push_mut(prefix); 146 } 147 148 params.push_mut(parameter.tree()); 149 } 150 151 t.push_mut(params); 152 } 153 154 if !self.dependencies.is_empty() { 155 let mut dependencies = Tree::atom("deps"); 156 let mut subsequents = Tree::atom("sups"); 157 158 for (i, dependency) in self.dependencies.iter().enumerate() { 159 let mut d = Tree::atom(dependency.recipe.lexeme()); 160 161 for argument in &dependency.arguments { 162 d.push_mut(argument.tree()); 163 } 164 165 if i < self.priors { 166 dependencies.push_mut(d); 167 } else { 168 subsequents.push_mut(d); 169 } 170 } 171 172 if let Tree::List(_) = dependencies { 173 t.push_mut(dependencies); 174 } 175 176 if let Tree::List(_) = subsequents { 177 t.push_mut(subsequents); 178 } 179 } 180 181 if !self.body.is_empty() { 182 t.push_mut(Tree::atom("body").extend(self.body.iter().map(|line| line.tree()))); 183 } 184 185 t 186 } 187 } 188 189 impl<'src> Node<'src> for Parameter<'src> { tree(&self) -> Tree<'src>190 fn tree(&self) -> Tree<'src> { 191 let mut children = vec![Tree::atom(self.name.lexeme())]; 192 193 if let Some(default) = &self.default { 194 children.push(default.tree()); 195 } 196 197 Tree::List(children) 198 } 199 } 200 201 impl<'src> Node<'src> for Line<'src> { tree(&self) -> Tree<'src>202 fn tree(&self) -> Tree<'src> { 203 Tree::list(self.fragments.iter().map(|fragment| fragment.tree())) 204 } 205 } 206 207 impl<'src> Node<'src> for Fragment<'src> { tree(&self) -> Tree<'src>208 fn tree(&self) -> Tree<'src> { 209 match self { 210 Fragment::Text { token } => Tree::string(token.lexeme()), 211 Fragment::Interpolation { expression } => Tree::List(vec![expression.tree()]), 212 } 213 } 214 } 215 216 impl<'src> Node<'src> for Set<'src> { tree(&self) -> Tree<'src>217 fn tree(&self) -> Tree<'src> { 218 use Setting::*; 219 220 let mut set = Tree::atom(Keyword::Set.lexeme()); 221 set.push_mut(self.name.lexeme().replace('-', "_")); 222 223 match &self.value { 224 DotenvLoad(value) | Export(value) | PositionalArguments(value) => { 225 set.push_mut(value.to_string()); 226 } 227 Shell(setting::Shell { command, arguments }) => { 228 set.push_mut(Tree::string(&command.cooked)); 229 for argument in arguments { 230 set.push_mut(Tree::string(&argument.cooked)); 231 } 232 } 233 } 234 235 set 236 } 237 } 238 239 impl<'src> Node<'src> for Warning { tree(&self) -> Tree<'src>240 fn tree(&self) -> Tree<'src> { 241 unreachable!() 242 } 243 } 244 245 impl<'src> Node<'src> for str { tree(&self) -> Tree<'src>246 fn tree(&self) -> Tree<'src> { 247 Tree::atom("comment").push(["\"", self, "\""].concat()) 248 } 249 } 250