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