use std::{fmt, ops};
use std::rc::Rc;
use std::sync::Arc;
pub mod builder;
pub type DefaultParameter = Parameter<String>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Parameter<T> {
At,
Star,
Pound,
Question,
Dash,
Dollar,
Bang,
Positional(u32),
Var(T),
}
pub type DefaultParameterSubstitution = ParameterSubstitution<
DefaultParameter,
TopLevelWord<String>,
TopLevelCommand<String>,
DefaultArithmetic
>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ParameterSubstitution<P, W, C, A> {
Command(Vec<C>),
Len(P),
Arith(Option<A>),
Default(bool, P, Option<W>),
Assign(bool, P, Option<W>),
Error(bool, P, Option<W>),
Alternative(bool, P, Option<W>),
RemoveSmallestSuffix(P, Option<W>),
RemoveLargestSuffix(P, Option<W>),
RemoveSmallestPrefix(P, Option<W>),
RemoveLargestPrefix(P, Option<W>),
}
pub type ShellWord<T, W, C> = ComplexWord<Word<T, SimpleWord<T, Parameter<T>,
Box<ParameterSubstitution<Parameter<T>, W, C, Arithmetic<T>>
>>>>;
pub type DefaultComplexWord = ComplexWord<DefaultWord>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ComplexWord<W> {
Concat(Vec<W>),
Single(W),
}
pub type DefaultWord = Word<String, DefaultSimpleWord>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Word<L, W> {
Simple(W),
DoubleQuoted(Vec<W>),
SingleQuoted(L),
}
pub type DefaultSimpleWord = SimpleWord<
String,
DefaultParameter,
Box<DefaultParameterSubstitution>
>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum SimpleWord<L, P, S> {
Literal(L),
Escaped(L),
Param(P),
Subst(S),
Star,
Question,
SquareOpen,
SquareClose,
Tilde,
Colon,
}
pub type DefaultRedirect = Redirect<TopLevelWord<String>>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Redirect<W> {
Read(Option<u16>, W),
Write(Option<u16>, W),
ReadWrite(Option<u16>, W),
Append(Option<u16>, W),
Clobber(Option<u16>, W),
Heredoc(Option<u16>, W),
DupRead(Option<u16>, W),
DupWrite(Option<u16>, W),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct GuardBodyPair<C> {
pub guard: Vec<C>,
pub body: Vec<C>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PatternBodyPair<W, C> {
pub patterns: Vec<W>,
pub body: Vec<C>,
}
pub type DefaultCommand = Command<DefaultAndOrList>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Command<T> {
Job(T),
List(T),
}
pub type CommandList<T, W, C> = AndOrList<ListableCommand<ShellPipeableCommand<T, W, C>>>;
pub type AtomicCommandList<T, W, C>
= AndOrList<ListableCommand<AtomicShellPipeableCommand<T, W, C>>>;
pub type ShellPipeableCommand<T, W, C> = PipeableCommand<
T,
Box<SimpleCommand<T, W, Redirect<W>>>,
Box<ShellCompoundCommand<T, W, C>>,
Rc<ShellCompoundCommand<T, W, C>>
>;
pub type AtomicShellPipeableCommand<T, W, C> = PipeableCommand<
T,
Box<SimpleCommand<T, W, Redirect<W>>>,
Box<ShellCompoundCommand<T, W, C>>,
Arc<ShellCompoundCommand<T, W, C>>
>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AndOr<T> {
And(T),
Or(T),
}
pub type DefaultAndOrList = AndOrList<DefaultListableCommand>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct AndOrList<T> {
pub first: T,
pub rest: Vec<AndOr<T>>,
}
pub type DefaultListableCommand = ListableCommand<DefaultPipeableCommand>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ListableCommand<T> {
Pipe(bool, Vec<T>),
Single(T),
}
pub type DefaultPipeableCommand = ShellPipeableCommand<
String,
TopLevelWord<String>,
TopLevelCommand<String>
>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PipeableCommand<N, S, C, F> {
Simple(S),
Compound(C),
FunctionDef(N, F),
}
pub type ShellCompoundCommand<T, W, C>
= CompoundCommand<CompoundCommandKind<T, W, C>, Redirect<W>>;
pub type DefaultCompoundCommand = ShellCompoundCommand<
String,
TopLevelWord<String>,
TopLevelCommand<String>
>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CompoundCommand<T, R> {
pub kind: T,
pub io: Vec<R>,
}
pub type DefaultCompoundCommandKind = CompoundCommandKind<
String,
TopLevelWord<String>,
TopLevelCommand<String>
>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum CompoundCommandKind<V, W, C> {
Brace(Vec<C>),
Subshell(Vec<C>),
While(GuardBodyPair<C>),
Until(GuardBodyPair<C>),
If {
conditionals: Vec<GuardBodyPair<C>>,
else_branch: Option<Vec<C>>,
},
For {
var: V,
words: Option<Vec<W>>,
body: Vec<C>,
},
Case {
word: W,
arms: Vec<PatternBodyPair<W, C>>,
},
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RedirectOrEnvVar<R, V, W> {
Redirect(R),
EnvVar(V, Option<W>),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RedirectOrCmdWord<R, W> {
Redirect(R),
CmdWord(W),
}
pub type DefaultSimpleCommand = SimpleCommand<
String,
TopLevelWord<String>,
Redirect<TopLevelWord<String>>
>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SimpleCommand<V, W, R> {
pub redirects_or_env_vars: Vec<RedirectOrEnvVar<R, V, W>>,
pub redirects_or_cmd_words: Vec<RedirectOrCmdWord<R, W>>,
}
pub type DefaultArithmetic = Arithmetic<String>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Arithmetic<T> {
Var(T),
Literal(isize),
Pow(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
PostIncr(T),
PostDecr(T),
PreIncr(T),
PreDecr(T),
UnaryPlus(Box<Arithmetic<T>>),
UnaryMinus(Box<Arithmetic<T>>),
LogicalNot(Box<Arithmetic<T>>),
BitwiseNot(Box<Arithmetic<T>>),
Mult(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Div(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Modulo(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Add(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Sub(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
ShiftLeft(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
ShiftRight(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Less(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
LessEq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Great(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
GreatEq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Eq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
NotEq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
BitwiseAnd(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
BitwiseXor(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
BitwiseOr(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
LogicalAnd(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
LogicalOr(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Ternary(Box<Arithmetic<T>>, Box<Arithmetic<T>>, Box<Arithmetic<T>>),
Assign(T, Box<Arithmetic<T>>),
Sequence(Vec<Arithmetic<T>>),
}
macro_rules! impl_top_level_cmd {
($(#[$attr:meta])* pub struct $Cmd:ident, $CmdList:ident, $Word:ident) => {
$(#[$attr])*
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct $Cmd<T>(pub Command<$CmdList<T, $Word<T>, $Cmd<T>>>);
impl<T> ops::Deref for $Cmd<T> {
type Target = Command<$CmdList<T, $Word<T>, $Cmd<T>>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> ops::DerefMut for $Cmd<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> PartialEq<Command<$CmdList<T, $Word<T>, $Cmd<T>>>> for $Cmd<T> where T: PartialEq<T>
{
fn eq(&self, other: &Command<$CmdList<T, $Word<T>, $Cmd<T>>>) -> bool {
&self.0 == other
}
}
impl<T> From<Command<$CmdList<T, $Word<T>, $Cmd<T>>>> for $Cmd<T> {
fn from(inner: Command<$CmdList<T, $Word<T>, $Cmd<T>>>) -> Self {
$Cmd(inner)
}
}
};
}
impl_top_level_cmd! {
pub struct TopLevelCommand,
CommandList,
TopLevelWord
}
impl_top_level_cmd! {
pub struct AtomicTopLevelCommand,
AtomicCommandList,
AtomicTopLevelWord
}
macro_rules! impl_top_level_word {
($(#[$attr:meta])* pub struct $Word:ident, $Cmd:ident) => {
$(#[$attr])*
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct $Word<T>(pub ShellWord<T, $Word<T>, $Cmd<T>>);
impl<T> ops::Deref for $Word<T> {
type Target = ShellWord<T, $Word<T>, $Cmd<T>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> ops::DerefMut for $Word<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> PartialEq<ShellWord<T, $Word<T>, $Cmd<T>>> for $Word<T> where T: PartialEq<T> {
fn eq(&self, other: &ShellWord<T, $Word<T>, $Cmd<T>>) -> bool {
&self.0 == other
}
}
impl<T> From<ShellWord<T, $Word<T>, $Cmd<T>>> for $Word<T> {
fn from(inner: ShellWord<T, $Word<T>, $Cmd<T>>) -> Self {
$Word(inner)
}
}
};
}
impl_top_level_word! {
pub struct TopLevelWord,
TopLevelCommand
}
impl_top_level_word! {
pub struct AtomicTopLevelWord,
AtomicTopLevelCommand
}
impl<T: fmt::Display> fmt::Display for Parameter<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::Parameter::*;
match *self {
At => fmt.write_str("$@"),
Star => fmt.write_str("$*"),
Pound => fmt.write_str("$#"),
Question => fmt.write_str("$?"),
Dash => fmt.write_str("$-"),
Dollar => fmt.write_str("$$"),
Bang => fmt.write_str("$!"),
Var(ref p) => write!(fmt, "${{{}}}", p),
Positional(p) => if p <= 9 {
write!(fmt, "${}", p)
} else {
write!(fmt, "${{{}}}", p)
},
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_display_parameter() {
use lexer::Lexer;
use parse::DefaultParser;
use super::Parameter::*;
use super::ComplexWord::Single;
use super::SimpleWord::Param;
use super::TopLevelWord;
use super::Word::Simple;
let params = vec!(
At,
Star,
Pound,
Question,
Dash,
Dollar,
Bang,
Positional(0),
Positional(10),
Positional(100),
Var(String::from("foo_bar123")),
);
for p in params {
let src = p.to_string();
let correct = TopLevelWord(Single(Simple(Param(p))));
let parsed = match DefaultParser::new(Lexer::new(src.chars())).word() {
Ok(Some(w)) => w,
Ok(None) => panic!("The source \"{}\" generated from the command `{:#?}` failed to parse as anything", src, correct),
Err(e) => panic!("The source \"{}\" generated from the command `{:#?}` failed to parse: {}", src, correct, e),
};
if correct != parsed {
panic!("The source \"{}\" generated from the command `{:#?}` was parsed as `{:#?}`", src, correct, parsed);
}
}
}
}