322 lines
8.1 KiB
Rust
322 lines
8.1 KiB
Rust
use super::*;
|
|
|
|
/// Methods common to all AST nodes. Currently only used in parser unit tests.
|
|
pub(crate) trait Node<'src> {
|
|
/// Construct an untyped tree of atoms representing this Node. This function,
|
|
/// and `Tree` type, are only used in parser unit tests.
|
|
fn tree(&self) -> Tree<'src>;
|
|
}
|
|
|
|
impl<'src> Node<'src> for Ast<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
Tree::atom("justfile")
|
|
.extend(self.items.iter().map(Node::tree))
|
|
.extend(self.warnings.iter().map(Node::tree))
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Item<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
match self {
|
|
Self::Alias(alias) => alias.tree(),
|
|
Self::Assignment(assignment) => assignment.tree(),
|
|
Self::Comment(comment) => comment.tree(),
|
|
Self::Import {
|
|
relative, optional, ..
|
|
} => {
|
|
let mut tree = Tree::atom("import");
|
|
|
|
if *optional {
|
|
tree = tree.push("?");
|
|
}
|
|
|
|
tree.push(format!("{relative}"))
|
|
}
|
|
Self::Module {
|
|
name,
|
|
optional,
|
|
relative,
|
|
..
|
|
} => {
|
|
let mut tree = Tree::atom("mod");
|
|
|
|
if *optional {
|
|
tree = tree.push("?");
|
|
}
|
|
|
|
tree = tree.push(name.lexeme());
|
|
|
|
if let Some(relative) = relative {
|
|
tree = tree.push(format!("{relative}"));
|
|
}
|
|
|
|
tree
|
|
}
|
|
Self::Recipe(recipe) => recipe.tree(),
|
|
Self::Set(set) => set.tree(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Alias<'src, Name<'src>> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
Tree::atom(Keyword::Alias.lexeme())
|
|
.push(self.name.lexeme())
|
|
.push(self.target.lexeme())
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Assignment<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
if self.export {
|
|
Tree::atom("assignment")
|
|
.push("#")
|
|
.push(Keyword::Export.lexeme())
|
|
} else {
|
|
Tree::atom("assignment")
|
|
}
|
|
.push(self.name.lexeme())
|
|
.push(self.value.tree())
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Expression<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
match self {
|
|
Self::Assert {
|
|
condition: Condition { lhs, rhs, operator },
|
|
error,
|
|
} => Tree::atom(Keyword::Assert.lexeme())
|
|
.push(lhs.tree())
|
|
.push(operator.to_string())
|
|
.push(rhs.tree())
|
|
.push(error.tree()),
|
|
Self::Concatenation { lhs, rhs } => Tree::atom("+").push(lhs.tree()).push(rhs.tree()),
|
|
Self::Conditional {
|
|
condition: Condition { lhs, rhs, operator },
|
|
then,
|
|
otherwise,
|
|
} => {
|
|
let mut tree = Tree::atom(Keyword::If.lexeme());
|
|
tree.push_mut(lhs.tree());
|
|
tree.push_mut(operator.to_string());
|
|
tree.push_mut(rhs.tree());
|
|
tree.push_mut(then.tree());
|
|
tree.push_mut(otherwise.tree());
|
|
tree
|
|
}
|
|
Self::Call { thunk } => {
|
|
use Thunk::*;
|
|
|
|
let mut tree = Tree::atom("call");
|
|
|
|
match thunk {
|
|
Nullary { name, .. } => tree.push_mut(name.lexeme()),
|
|
Unary { name, arg, .. } => {
|
|
tree.push_mut(name.lexeme());
|
|
tree.push_mut(arg.tree());
|
|
}
|
|
UnaryOpt {
|
|
name, args: (a, b), ..
|
|
} => {
|
|
tree.push_mut(name.lexeme());
|
|
tree.push_mut(a.tree());
|
|
if let Some(b) = b.as_ref() {
|
|
tree.push_mut(b.tree());
|
|
}
|
|
}
|
|
UnaryPlus {
|
|
name,
|
|
args: (a, rest),
|
|
..
|
|
} => {
|
|
tree.push_mut(name.lexeme());
|
|
tree.push_mut(a.tree());
|
|
for arg in rest {
|
|
tree.push_mut(arg.tree());
|
|
}
|
|
}
|
|
Binary {
|
|
name, args: [a, b], ..
|
|
} => {
|
|
tree.push_mut(name.lexeme());
|
|
tree.push_mut(a.tree());
|
|
tree.push_mut(b.tree());
|
|
}
|
|
BinaryPlus {
|
|
name,
|
|
args: ([a, b], rest),
|
|
..
|
|
} => {
|
|
tree.push_mut(name.lexeme());
|
|
tree.push_mut(a.tree());
|
|
tree.push_mut(b.tree());
|
|
for arg in rest {
|
|
tree.push_mut(arg.tree());
|
|
}
|
|
}
|
|
Ternary {
|
|
name,
|
|
args: [a, b, c],
|
|
..
|
|
} => {
|
|
tree.push_mut(name.lexeme());
|
|
tree.push_mut(a.tree());
|
|
tree.push_mut(b.tree());
|
|
tree.push_mut(c.tree());
|
|
}
|
|
}
|
|
|
|
tree
|
|
}
|
|
Self::Variable { name } => Tree::atom(name.lexeme()),
|
|
Self::StringLiteral {
|
|
string_literal: StringLiteral { cooked, .. },
|
|
} => Tree::string(cooked),
|
|
Self::Backtick { contents, .. } => Tree::atom("backtick").push(Tree::string(contents)),
|
|
Self::Group { contents } => Tree::List(vec![contents.tree()]),
|
|
Self::Join { lhs: None, rhs } => Tree::atom("/").push(rhs.tree()),
|
|
Self::Join {
|
|
lhs: Some(lhs),
|
|
rhs,
|
|
} => Tree::atom("/").push(lhs.tree()).push(rhs.tree()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for UnresolvedRecipe<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
let mut t = Tree::atom("recipe");
|
|
|
|
if self.quiet {
|
|
t.push_mut("#");
|
|
t.push_mut("quiet");
|
|
}
|
|
|
|
if let Some(doc) = self.doc {
|
|
t.push_mut(Tree::string(doc));
|
|
}
|
|
|
|
t.push_mut(self.name.lexeme());
|
|
|
|
if !self.parameters.is_empty() {
|
|
let mut params = Tree::atom("params");
|
|
|
|
for parameter in &self.parameters {
|
|
if let Some(prefix) = parameter.kind.prefix() {
|
|
params.push_mut(prefix);
|
|
}
|
|
|
|
params.push_mut(parameter.tree());
|
|
}
|
|
|
|
t.push_mut(params);
|
|
}
|
|
|
|
if !self.dependencies.is_empty() {
|
|
let mut dependencies = Tree::atom("deps");
|
|
let mut subsequents = Tree::atom("sups");
|
|
|
|
for (i, dependency) in self.dependencies.iter().enumerate() {
|
|
let mut d = Tree::atom(dependency.recipe.lexeme());
|
|
|
|
for argument in &dependency.arguments {
|
|
d.push_mut(argument.tree());
|
|
}
|
|
|
|
if i < self.priors {
|
|
dependencies.push_mut(d);
|
|
} else {
|
|
subsequents.push_mut(d);
|
|
}
|
|
}
|
|
|
|
if let Tree::List(_) = dependencies {
|
|
t.push_mut(dependencies);
|
|
}
|
|
|
|
if let Tree::List(_) = subsequents {
|
|
t.push_mut(subsequents);
|
|
}
|
|
}
|
|
|
|
if !self.body.is_empty() {
|
|
t.push_mut(Tree::atom("body").extend(self.body.iter().map(Node::tree)));
|
|
}
|
|
|
|
t
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Parameter<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
let mut children = vec![Tree::atom(self.name.lexeme())];
|
|
|
|
if let Some(default) = &self.default {
|
|
children.push(default.tree());
|
|
}
|
|
|
|
Tree::List(children)
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Line<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
Tree::list(self.fragments.iter().map(Node::tree))
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Fragment<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
match self {
|
|
Self::Text { token } => Tree::string(token.lexeme()),
|
|
Self::Interpolation { expression } => Tree::List(vec![expression.tree()]),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Set<'src> {
|
|
fn tree(&self) -> Tree<'src> {
|
|
let mut set = Tree::atom(Keyword::Set.lexeme());
|
|
set.push_mut(self.name.lexeme().replace('-', "_"));
|
|
|
|
match &self.value {
|
|
Setting::AllowDuplicateRecipes(value)
|
|
| Setting::AllowDuplicateVariables(value)
|
|
| Setting::DotenvLoad(value)
|
|
| Setting::Export(value)
|
|
| Setting::Fallback(value)
|
|
| Setting::PositionalArguments(value)
|
|
| Setting::Quiet(value)
|
|
| Setting::WindowsPowerShell(value)
|
|
| Setting::IgnoreComments(value) => {
|
|
set.push_mut(value.to_string());
|
|
}
|
|
Setting::Shell(Shell { command, arguments })
|
|
| Setting::WindowsShell(Shell { command, arguments }) => {
|
|
set.push_mut(Tree::string(&command.cooked));
|
|
for argument in arguments {
|
|
set.push_mut(Tree::string(&argument.cooked));
|
|
}
|
|
}
|
|
Setting::DotenvFilename(value) | Setting::DotenvPath(value) | Setting::Tempdir(value) => {
|
|
set.push_mut(Tree::string(value));
|
|
}
|
|
}
|
|
|
|
set
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for Warning {
|
|
fn tree(&self) -> Tree<'src> {
|
|
unreachable!()
|
|
}
|
|
}
|
|
|
|
impl<'src> Node<'src> for str {
|
|
fn tree(&self) -> Tree<'src> {
|
|
Tree::atom("comment").push(["\"", self, "\""].concat())
|
|
}
|
|
}
|