use std::ops::Range;
pub type Span = Range<usize>;
#[derive(Debug, Clone)]
pub enum Cmd {
Simple(SimpleCmd),
Seq {
items: Vec<SeqItem>,
span: Span,
},
AndOr {
first: Box<Cmd>,
rest: Vec<(AndOrOp, Cmd)>,
span: Span,
},
Pipeline {
negated: bool,
cmds: Vec<Cmd>,
span: Span,
},
If {
arms: Vec<(Cmd, Cmd)>,
otherwise: Option<Box<Cmd>>,
span: Span,
},
Case {
word: Word,
arms: Vec<CaseArm>,
span: Span,
},
For {
var: String,
items: Option<Vec<Word>>,
body: Box<Cmd>,
span: Span,
},
ArithFor {
init: Option<ArithExpr>,
cond: Option<ArithExpr>,
step: Option<ArithExpr>,
body: Box<Cmd>,
span: Span,
},
Loop {
kind: LoopKind,
cond: Box<Cmd>,
body: Box<Cmd>,
span: Span,
},
Group {
body: Box<Cmd>,
span: Span,
},
Subshell {
body: Box<Cmd>,
span: Span,
},
FuncDef {
name: String,
body: Box<Cmd>,
span: Span,
},
Arith {
expr: ArithExpr,
span: Span,
},
Cond {
expr: TestExpr,
span: Span,
},
Redirected {
body: Box<Cmd>,
redirects: Vec<Redirect>,
span: Span,
},
Unsupported {
why: &'static str,
span: Span,
},
}
#[derive(Debug, Clone)]
pub struct SeqItem {
pub cmd: Cmd,
pub background: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AndOrOp {
And,
Or,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoopKind {
While,
Until,
}
#[derive(Debug, Clone)]
pub struct CaseArm {
pub patterns: Vec<Word>,
pub body: Cmd,
pub term: CaseTerm,
#[allow(dead_code)]
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CaseTerm {
Break,
FallThrough,
TestNext,
}
#[derive(Debug, Clone)]
pub struct SimpleCmd {
pub assigns: Vec<Assign>,
pub words: Vec<Word>,
pub redirects: Vec<Redirect>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Assign {
pub name: String,
pub append: bool,
pub value: Word,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Redirect {
#[allow(dead_code)]
pub op: String,
pub target: Word,
#[allow(dead_code)]
pub heredoc: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Word {
pub parts: Vec<Part>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum Part {
Lit(String),
SingleQuoted(String),
DoubleQuoted(Vec<Part>),
Escaped(char),
Param(ParamRef),
ParamOp { name: String, raw: String },
CmdSubst { body: Box<Cmd>, span: Span },
Arith(ArithExpr),
Brace(BraceExpr),
Glob(char),
#[allow(dead_code)]
Tilde(String),
ProcSubst { body: Box<Cmd>, span: Span },
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ParamRef {
Named(String),
Positional(u32),
All,
Special(char),
}
#[derive(Debug, Clone)]
pub enum BraceExpr {
IntRange {
from: i64,
to: i64,
step: i64,
width: usize,
},
CharRange { from: char, to: char, step: i64 },
List(Vec<Word>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ArithExpr {
Num(i64),
Var(String),
Unary(ArithUnOp, Box<ArithExpr>),
Binary(ArithBinOp, Box<ArithExpr>, Box<ArithExpr>),
Assign {
var: String,
op: Option<ArithBinOp>,
value: Box<ArithExpr>,
},
IncDec {
var: String,
delta: i64,
prefix: bool,
},
Comma(Box<ArithExpr>, Box<ArithExpr>),
Ternary(Box<ArithExpr>, Box<ArithExpr>, Box<ArithExpr>),
Unknown(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArithUnOp {
Neg,
Not,
BitNot,
Plus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArithBinOp {
Add,
Sub,
Mul,
Div,
Rem,
Lt,
Le,
Gt,
Ge,
Eq,
Ne,
And,
Or,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
Pow,
}
#[derive(Debug, Clone)]
pub enum TestExpr {
Unary {
op: String,
operand: Word,
},
Binary {
left: Word,
op: String,
right: Word,
},
Word(Word),
Not(Box<TestExpr>),
And(Box<TestExpr>, Box<TestExpr>),
Or(Box<TestExpr>, Box<TestExpr>),
Unknown,
}
impl Cmd {
pub fn span(&self) -> Span {
match self {
Cmd::Simple(s) => s.span.clone(),
Cmd::Seq { span, .. }
| Cmd::AndOr { span, .. }
| Cmd::Pipeline { span, .. }
| Cmd::If { span, .. }
| Cmd::Case { span, .. }
| Cmd::For { span, .. }
| Cmd::ArithFor { span, .. }
| Cmd::Loop { span, .. }
| Cmd::Group { span, .. }
| Cmd::Subshell { span, .. }
| Cmd::FuncDef { span, .. }
| Cmd::Arith { span, .. }
| Cmd::Cond { span, .. }
| Cmd::Redirected { span, .. }
| Cmd::Unsupported { span, .. } => span.clone(),
}
}
}
impl Word {
pub fn literal(&self) -> Option<String> {
let mut out = String::new();
for p in &self.parts {
match p {
Part::Lit(s) | Part::SingleQuoted(s) => out.push_str(s),
Part::Escaped(c) => out.push(*c),
Part::DoubleQuoted(inner) => {
for q in inner {
match q {
Part::Lit(s) => out.push_str(s),
Part::Escaped(c) => out.push(*c),
_ => return None,
}
}
}
_ => return None,
}
}
Some(out)
}
}