Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 90 additions & 57 deletions src/parser/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ pub(super) fn make_stderr_redirect() -> Node {
}

/// Walks an AST node and fills in empty `HereDoc` content from the lexer queue.
#[allow(clippy::too_many_lines, clippy::match_same_arms)]
pub(super) fn fill_heredoc_contents(node: &mut Node, lexer: &mut crate::lexer::Lexer) {
match &mut node.kind {
NodeKind::HereDoc { content, .. } if content.is_empty() => {
Expand All @@ -117,22 +116,8 @@ pub(super) fn fill_heredoc_contents(node: &mut Node, lexer: &mut crate::lexer::L
assignments,
words,
redirects,
} => {
for a in assignments {
fill_heredoc_contents(a, lexer);
}
for w in words {
fill_heredoc_contents(w, lexer);
}
for r in redirects {
fill_heredoc_contents(r, lexer);
}
}
NodeKind::Pipeline { commands, .. } => {
for c in commands {
fill_heredoc_contents(c, lexer);
}
}
} => fill_command(assignments, words, redirects, lexer),
NodeKind::Pipeline { commands, .. } => fill_each(commands, lexer),
NodeKind::List { items } => {
for item in items {
fill_heredoc_contents(&mut item.command, lexer);
Expand All @@ -143,16 +128,13 @@ pub(super) fn fill_heredoc_contents(node: &mut Node, lexer: &mut crate::lexer::L
then_body,
else_body,
redirects,
} => {
fill_heredoc_contents(condition, lexer);
fill_heredoc_contents(then_body, lexer);
if let Some(eb) = else_body {
fill_heredoc_contents(eb, lexer);
}
for r in redirects {
fill_heredoc_contents(r, lexer);
}
}
} => fill_if(
condition,
then_body,
else_body.as_deref_mut(),
redirects,
lexer,
),
NodeKind::While {
condition,
body,
Expand All @@ -162,44 +144,20 @@ pub(super) fn fill_heredoc_contents(node: &mut Node, lexer: &mut crate::lexer::L
condition,
body,
redirects,
} => {
fill_heredoc_contents(condition, lexer);
fill_heredoc_contents(body, lexer);
for r in redirects {
fill_heredoc_contents(r, lexer);
}
}
NodeKind::Subshell { body, redirects } | NodeKind::BraceGroup { body, redirects } => {
fill_heredoc_contents(body, lexer);
for r in redirects {
fill_heredoc_contents(r, lexer);
}
}
NodeKind::For {
} => fill_cond_body_redirects(condition, body, redirects, lexer),
NodeKind::Subshell { body, redirects }
| NodeKind::BraceGroup { body, redirects }
| NodeKind::For {
body, redirects, ..
}
| NodeKind::Select {
body, redirects, ..
} => {
fill_heredoc_contents(body, lexer);
for r in redirects {
fill_heredoc_contents(r, lexer);
}
}
} => fill_body_and_redirects(body, redirects, lexer),
NodeKind::Case {
patterns,
redirects,
..
} => {
for p in patterns {
if let Some(body) = &mut p.body {
fill_heredoc_contents(body, lexer);
}
}
for r in redirects {
fill_heredoc_contents(r, lexer);
}
}
} => fill_case(patterns, redirects, lexer),
NodeKind::Negation { pipeline } | NodeKind::Time { pipeline, .. } => {
fill_heredoc_contents(pipeline, lexer);
}
Expand All @@ -209,3 +167,78 @@ pub(super) fn fill_heredoc_contents(node: &mut Node, lexer: &mut crate::lexer::L
_ => {}
}
}

/// Recurses `fill_heredoc_contents` into every node slot.
fn fill_each(nodes: &mut [Node], lexer: &mut crate::lexer::Lexer) {
for n in nodes {
fill_heredoc_contents(n, lexer);
}
}

/// Walks `Command` constituents: assignments, then words, then redirects.
fn fill_command(
assignments: &mut [Node],
words: &mut [Node],
redirects: &mut [Node],
lexer: &mut crate::lexer::Lexer,
) {
fill_each(assignments, lexer);
fill_each(words, lexer);
fill_each(redirects, lexer);
}

/// Walks `If` constituents: condition, then-body, optional else-body,
/// then redirects.
fn fill_if(
condition: &mut Node,
then_body: &mut Node,
else_body: Option<&mut Node>,
redirects: &mut [Node],
lexer: &mut crate::lexer::Lexer,
) {
fill_heredoc_contents(condition, lexer);
fill_heredoc_contents(then_body, lexer);
if let Some(eb) = else_body {
fill_heredoc_contents(eb, lexer);
}
fill_each(redirects, lexer);
}

/// Walks `body` then its trailing redirects. Used by compound commands
/// that carry exactly a body + redirects pair (`Subshell`, `BraceGroup`,
/// `For`, `Select`).
fn fill_body_and_redirects(
body: &mut Node,
redirects: &mut [Node],
lexer: &mut crate::lexer::Lexer,
) {
fill_heredoc_contents(body, lexer);
fill_each(redirects, lexer);
}

/// Walks `condition`, `body`, then the trailing redirects. Used by
/// `While` and `Until`.
fn fill_cond_body_redirects(
condition: &mut Node,
body: &mut Node,
redirects: &mut [Node],
lexer: &mut crate::lexer::Lexer,
) {
fill_heredoc_contents(condition, lexer);
fill_heredoc_contents(body, lexer);
fill_each(redirects, lexer);
}

/// Walks each case pattern's optional body, then the trailing redirects.
fn fill_case(
patterns: &mut [crate::ast::CasePattern],
redirects: &mut [Node],
lexer: &mut crate::lexer::Lexer,
) {
for p in patterns {
if let Some(body) = &mut p.body {
fill_heredoc_contents(body, lexer);
}
}
fill_each(redirects, lexer);
}
42 changes: 42 additions & 0 deletions tests/parable/14_here_documents.tests
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,45 @@ EOF
next
"))
---


=== heredoc in if else branch
if true; then cat <<A
first
A
else cat <<B
second
B
fi
---
(if (command (word "true")) (command (word "cat") (redirect "<<" "first
")) (command (word "cat") (redirect "<<" "second
")))
---


=== heredoc in case pattern body
case $x in a) cat <<EOF
content
EOF
;;
esac
---
(case (word "$x") (pattern ((word "a")) (command (word "cat") (redirect "<<" "content
"))))
---


=== heredoc nested while inside if else
if true; then while false; do cat <<A
outer
A
done; else cat <<B
inner
B
fi
---
(if (command (word "true")) (while (command (word "false")) (command (word "cat") (redirect "<<" "outer
"))) (command (word "cat") (redirect "<<" "inner
")))
---
Loading