#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub const fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
pub const fn empty() -> Self {
Self { start: 0, end: 0 }
}
pub const fn is_empty(&self) -> bool {
self.start >= self.end
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Node {
pub kind: NodeKind,
pub span: Span,
}
impl Node {
pub const fn new(kind: NodeKind, span: Span) -> Self {
Self { kind, span }
}
pub const fn empty(kind: NodeKind) -> Self {
Self {
kind,
span: Span::empty(),
}
}
pub fn source_text<'a>(&self, source: &'a str) -> &'a str {
if self.span.is_empty() {
return "";
}
let byte_start = source.char_indices().nth(self.span.start).map(|(i, _)| i);
let byte_end = source
.char_indices()
.nth(self.span.end)
.map_or(source.len(), |(i, _)| i);
match byte_start {
Some(s) if byte_end <= source.len() => &source[s..byte_end],
_ => "",
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(clippy::use_self)]
pub enum NodeKind {
Word {
value: String,
parts: Vec<Node>,
spans: Vec<crate::lexer::word_builder::WordSpan>,
},
WordLiteral { value: String },
Command {
assignments: Vec<Node>,
words: Vec<Node>,
redirects: Vec<Node>,
},
Pipeline {
commands: Vec<Node>,
separators: Vec<PipeSep>,
},
List { items: Vec<ListItem> },
If {
condition: Box<Node>,
then_body: Box<Node>,
else_body: Option<Box<Node>>,
redirects: Vec<Node>,
},
While {
condition: Box<Node>,
body: Box<Node>,
redirects: Vec<Node>,
},
Until {
condition: Box<Node>,
body: Box<Node>,
redirects: Vec<Node>,
},
For {
var: String,
words: Option<Vec<Node>>,
body: Box<Node>,
redirects: Vec<Node>,
},
ForArith {
init: String,
cond: String,
incr: String,
body: Box<Node>,
redirects: Vec<Node>,
},
Select {
var: String,
words: Option<Vec<Node>>,
body: Box<Node>,
redirects: Vec<Node>,
},
Case {
word: Box<Node>,
patterns: Vec<CasePattern>,
redirects: Vec<Node>,
},
Function { name: String, body: Box<Node> },
Subshell {
body: Box<Node>,
redirects: Vec<Node>,
},
BraceGroup {
body: Box<Node>,
redirects: Vec<Node>,
},
Coproc {
name: Option<String>,
command: Box<Node>,
},
Redirect {
op: String,
target: Box<Node>,
fd: i32,
},
HereDoc {
delimiter: String,
content: String,
strip_tabs: bool,
quoted: bool,
fd: i32,
complete: bool,
},
ParamExpansion {
param: String,
op: Option<String>,
arg: Option<String>,
},
ParamLength { param: String },
ParamIndirect {
param: String,
op: Option<String>,
arg: Option<String>,
},
CommandSubstitution { command: Box<Node>, brace: bool },
ProcessSubstitution {
direction: String,
command: Box<Node>,
},
AnsiCQuote { content: String },
LocaleString { content: String },
ArithmeticExpansion { expression: Option<Box<Node>> },
ArithmeticCommand {
expression: Option<Box<Node>>,
redirects: Vec<Node>,
raw_content: String,
},
ArithNumber { value: String },
ArithVar { name: String },
ArithBinaryOp {
op: String,
left: Box<Node>,
right: Box<Node>,
},
ArithUnaryOp { op: String, operand: Box<Node> },
ArithPreIncr { operand: Box<Node> },
ArithPostIncr { operand: Box<Node> },
ArithPreDecr { operand: Box<Node> },
ArithPostDecr { operand: Box<Node> },
ArithAssign {
op: String,
target: Box<Node>,
value: Box<Node>,
},
ArithTernary {
condition: Box<Node>,
if_true: Option<Box<Node>>,
if_false: Option<Box<Node>>,
},
ArithComma { left: Box<Node>, right: Box<Node> },
ArithSubscript { array: String, index: Box<Node> },
ArithEmpty,
ArithEscape { ch: String },
ArithDeprecated { expression: String },
ArithConcat { parts: Vec<Node> },
ConditionalExpr {
body: Box<Node>,
redirects: Vec<Node>,
},
UnaryTest { op: String, operand: Box<Node> },
BinaryTest {
op: String,
left: Box<Node>,
right: Box<Node>,
},
CondAnd { left: Box<Node>, right: Box<Node> },
CondOr { left: Box<Node>, right: Box<Node> },
CondNot { operand: Box<Node> },
CondParen { inner: Box<Node> },
CondTerm {
value: String,
spans: Vec<crate::lexer::word_builder::WordSpan>,
},
Negation { pipeline: Box<Node> },
Time { pipeline: Box<Node>, posix: bool },
Array { elements: Vec<Node> },
Empty,
Comment { text: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListOperator {
And,
Or,
Semi,
Background,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PipeSep {
Pipe,
PipeBoth,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ListItem {
pub command: Node,
pub operator: Option<ListOperator>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CasePattern {
pub patterns: Vec<Node>,
pub body: Option<Node>,
pub terminator: String,
}
impl CasePattern {
pub const fn new(patterns: Vec<Node>, body: Option<Node>, terminator: String) -> Self {
Self {
patterns,
body,
terminator,
}
}
}