use crate::ast::{Node, NodeKind};
use crate::token::Token;
pub fn word_node_from_token(tok: Token) -> Node {
let parts = super::word_parts::decompose_word_with_spans(&tok.value, &tok.spans);
Node::empty(NodeKind::Word {
parts,
value: tok.value,
spans: tok.spans,
})
}
pub fn word_node(value: &str) -> Node {
Node::empty(NodeKind::Word {
parts: super::word_parts::decompose_word_literal(value),
value: value.to_string(),
spans: Vec::new(),
})
}
pub(super) fn cond_term_from_token(tok: Token) -> Node {
Node::empty(NodeKind::CondTerm {
value: tok.value,
spans: tok.spans,
})
}
pub(super) fn is_fd_number(s: &str) -> bool {
!s.is_empty() && s.len() <= 2 && s.chars().all(|c| c.is_ascii_digit())
}
pub(super) fn is_varfd(s: &str) -> bool {
s.starts_with('{')
&& s.ends_with('}')
&& s.len() >= 3
&& s.as_bytes()
.get(1)
.is_some_and(|&c| c.is_ascii_alphabetic() || c == b'_')
&& s[1..s.len() - 1]
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
pub(super) fn is_cond_binary_op(s: &str) -> bool {
matches!(
s,
"==" | "!="
| "=~"
| "<"
| ">"
| "-eq"
| "-ne"
| "-lt"
| "-le"
| "-gt"
| "-ge"
| "-nt"
| "-ot"
| "-ef"
| "="
)
}
#[allow(clippy::needless_pass_by_value)]
pub(super) fn add_stderr_redirect(node: Option<&mut Node>) -> bool {
if let Some(Node {
kind: NodeKind::Command { redirects, .. },
..
}) = node
{
redirects.push(make_stderr_redirect());
true
} else {
false
}
}
pub(super) fn make_stderr_redirect() -> Node {
Node::empty(NodeKind::Redirect {
op: ">&".to_string(),
target: Box::new(Node::empty(NodeKind::Word {
value: "1".to_string(),
parts: vec![Node::empty(NodeKind::WordLiteral {
value: "1".to_string(),
})],
spans: Vec::new(),
})),
fd: 2,
})
}
pub(super) fn parse_heredoc_delimiter(raw: &str) -> (String, bool) {
let mut result = String::new();
let mut quoted = false;
let mut chars = raw.chars();
while let Some(c) = chars.next() {
match c {
'\'' => {
quoted = true;
for c in chars.by_ref() {
if c == '\'' {
break;
}
result.push(c);
}
}
'"' => {
quoted = true;
for c in chars.by_ref() {
if c == '"' {
break;
}
result.push(c);
}
}
'\\' => {
quoted = true;
if let Some(next) = chars.next() {
result.push(next);
}
}
_ => result.push(c),
}
}
(result, quoted)
}
#[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() => {
if let Some(c) = lexer.take_heredoc_content() {
*content = c;
}
}
NodeKind::Command {
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);
}
}
NodeKind::List { items } => {
for item in items {
fill_heredoc_contents(&mut item.command, lexer);
}
}
NodeKind::If {
condition,
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);
}
}
NodeKind::While {
condition,
body,
redirects,
}
| NodeKind::Until {
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 {
body, redirects, ..
}
| NodeKind::Select {
body, redirects, ..
} => {
fill_heredoc_contents(body, lexer);
for r in redirects {
fill_heredoc_contents(r, 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);
}
}
NodeKind::Negation { pipeline } | NodeKind::Time { pipeline, .. } => {
fill_heredoc_contents(pipeline, lexer);
}
NodeKind::Function { body, .. } | NodeKind::Coproc { command: body, .. } => {
fill_heredoc_contents(body, lexer);
}
_ => {}
}
}