use std::fmt::{Display, Write};
use crate::tokenizer;
const DISPLAY_INDENT: &str = "    ";
#[derive(Clone, Debug)]
pub struct Program {
    pub complete_commands: Vec<CompleteCommand>,
}
impl Display for Program {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for complete_command in &self.complete_commands {
            write!(f, "{}", complete_command)?;
        }
        Ok(())
    }
}
pub type CompleteCommand = CompoundList;
pub type CompleteCommandItem = CompoundListItem;
#[derive(Clone, Debug)]
pub enum SeparatorOperator {
    Async,
    Sequence,
}
impl Display for SeparatorOperator {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SeparatorOperator::Async => write!(f, "&"),
            SeparatorOperator::Sequence => write!(f, ";"),
        }
    }
}
#[derive(Clone, Debug)]
pub struct AndOrList {
    pub first: Pipeline,
    pub additional: Vec<AndOr>,
}
impl Display for AndOrList {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.first)?;
        for item in &self.additional {
            write!(f, "{}", item)?;
        }
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub enum AndOr {
    And(Pipeline),
    Or(Pipeline),
}
impl Display for AndOr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AndOr::And(pipeline) => write!(f, " && {}", pipeline),
            AndOr::Or(pipeline) => write!(f, " || {}", pipeline),
        }
    }
}
#[derive(Clone, Debug)]
pub struct Pipeline {
    pub bang: bool,
    pub seq: Vec<Command>,
}
impl Display for Pipeline {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.bang {
            write!(f, "!")?;
        }
        for (i, command) in self.seq.iter().enumerate() {
            if i > 0 {
                write!(f, " |")?;
            }
            write!(f, "{}", command)?;
        }
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub enum Command {
    Simple(SimpleCommand),
    Compound(CompoundCommand, Option<RedirectList>),
    Function(FunctionDefinition),
    ExtendedTest(ExtendedTestExpr),
}
impl Display for Command {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Command::Simple(simple_command) => write!(f, "{}", simple_command),
            Command::Compound(compound_command, redirect_list) => {
                write!(f, "{}", compound_command)?;
                if let Some(redirect_list) = redirect_list {
                    write!(f, "{}", redirect_list)?;
                }
                Ok(())
            }
            Command::Function(function_definition) => write!(f, "{}", function_definition),
            Command::ExtendedTest(extended_test_expr) => {
                write!(f, "[[ {} ]]", extended_test_expr)
            }
        }
    }
}
#[derive(Clone, Debug)]
pub enum CompoundCommand {
    Arithmetic(ArithmeticCommand),
    ArithmeticForClause(ArithmeticForClauseCommand),
    BraceGroup(BraceGroupCommand),
    Subshell(SubshellCommand),
    ForClause(ForClauseCommand),
    CaseClause(CaseClauseCommand),
    IfClause(IfClauseCommand),
    WhileClause(WhileOrUntilClauseCommand),
    UntilClause(WhileOrUntilClauseCommand),
}
impl Display for CompoundCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CompoundCommand::Arithmetic(arithmetic_command) => write!(f, "{}", arithmetic_command),
            CompoundCommand::ArithmeticForClause(arithmetic_for_clause_command) => {
                write!(f, "{}", arithmetic_for_clause_command)
            }
            CompoundCommand::BraceGroup(brace_group_command) => {
                write!(f, "{}", brace_group_command)
            }
            CompoundCommand::Subshell(subshell_command) => write!(f, "{}", subshell_command),
            CompoundCommand::ForClause(for_clause_command) => write!(f, "{}", for_clause_command),
            CompoundCommand::CaseClause(case_clause_command) => {
                write!(f, "{}", case_clause_command)
            }
            CompoundCommand::IfClause(if_clause_command) => write!(f, "{}", if_clause_command),
            CompoundCommand::WhileClause(while_or_until_clause_command) => {
                write!(f, "while {}", while_or_until_clause_command)
            }
            CompoundCommand::UntilClause(while_or_until_clause_command) => {
                write!(f, "until {}", while_or_until_clause_command)
            }
        }
    }
}
#[derive(Clone, Debug)]
pub struct ArithmeticCommand {
    pub expr: UnexpandedArithmeticExpr,
}
impl Display for ArithmeticCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "(({}))", self.expr)
    }
}
#[derive(Clone, Debug)]
pub struct SubshellCommand(pub CompoundList);
impl Display for SubshellCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "( ")?;
        write!(f, "{}", self.0)?;
        write!(f, " )")
    }
}
#[derive(Clone, Debug)]
pub struct ForClauseCommand {
    pub variable_name: String,
    pub values: Option<Vec<Word>>,
    pub body: DoGroupCommand,
}
impl Display for ForClauseCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "for {} in ", self.variable_name)?;
        if let Some(values) = &self.values {
            for (i, value) in values.iter().enumerate() {
                if i > 0 {
                    write!(f, " ")?;
                }
                write!(f, "{}", value)?;
            }
        }
        writeln!(f, ";")?;
        write!(f, "{}", self.body)
    }
}
#[derive(Clone, Debug)]
pub struct ArithmeticForClauseCommand {
    pub initializer: Option<UnexpandedArithmeticExpr>,
    pub condition: Option<UnexpandedArithmeticExpr>,
    pub updater: Option<UnexpandedArithmeticExpr>,
    pub body: DoGroupCommand,
}
impl Display for ArithmeticForClauseCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "for ((")?;
        if let Some(initializer) = &self.initializer {
            write!(f, "{}", initializer)?;
        }
        write!(f, "; ")?;
        if let Some(condition) = &self.condition {
            write!(f, "{}", condition)?;
        }
        write!(f, "; ")?;
        if let Some(updater) = &self.updater {
            write!(f, "{}", updater)?;
        }
        writeln!(f, "))")?;
        write!(f, "{}", self.body)
    }
}
#[derive(Clone, Debug)]
pub struct CaseClauseCommand {
    pub value: Word,
    pub cases: Vec<CaseItem>,
}
impl Display for CaseClauseCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "case {} in", self.value)?;
        for case in &self.cases {
            write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{}", case)?;
        }
        writeln!(f)?;
        write!(f, "esac")
    }
}
#[derive(Clone, Debug)]
pub struct CompoundList(pub Vec<CompoundListItem>);
impl Display for CompoundList {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for (i, item) in self.0.iter().enumerate() {
            if i > 0 {
                writeln!(f)?;
            }
            write!(f, "{}", item.0)?;
            if i == self.0.len() - 1 && matches!(item.1, SeparatorOperator::Sequence) {
                } else {
                write!(f, "{}", item.1)?;
            }
        }
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub struct CompoundListItem(pub AndOrList, pub SeparatorOperator);
impl Display for CompoundListItem {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)?;
        write!(f, "{}", self.1)?;
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub struct IfClauseCommand {
    pub condition: CompoundList,
    pub then: CompoundList,
    pub elses: Option<Vec<ElseClause>>,
}
impl Display for IfClauseCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "if {}; then", self.condition)?;
        write!(
            indenter::indented(f).with_str(DISPLAY_INDENT),
            "{}",
            self.then
        )?;
        if let Some(elses) = &self.elses {
            for else_clause in elses {
                write!(f, "{}", else_clause)?;
            }
        }
        writeln!(f)?;
        write!(f, "fi")?;
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub struct ElseClause {
    pub condition: Option<CompoundList>,
    pub body: CompoundList,
}
impl Display for ElseClause {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f)?;
        if let Some(condition) = &self.condition {
            writeln!(f, "elif {}; then", condition)?;
        } else {
            writeln!(f, "else")?;
        }
        write!(
            indenter::indented(f).with_str(DISPLAY_INDENT),
            "{}",
            self.body
        )
    }
}
#[derive(Clone, Debug)]
pub struct CaseItem {
    pub patterns: Vec<Word>,
    pub cmd: Option<CompoundList>,
}
impl Display for CaseItem {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f)?;
        for (i, pattern) in self.patterns.iter().enumerate() {
            if i > 0 {
                write!(f, "|")?;
            }
            write!(f, "{}", pattern)?;
        }
        writeln!(f, ")")?;
        if let Some(cmd) = &self.cmd {
            write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{}", cmd)?;
        }
        writeln!(f)?;
        write!(f, ";;")
    }
}
#[derive(Clone, Debug)]
pub struct WhileOrUntilClauseCommand(pub CompoundList, pub DoGroupCommand);
impl Display for WhileOrUntilClauseCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}; {}", self.0, self.1)
    }
}
#[derive(Clone, Debug)]
pub struct FunctionDefinition {
    pub fname: String,
    pub body: FunctionBody,
    pub source: String,
}
impl Display for FunctionDefinition {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "{} () ", self.fname)?;
        write!(f, "{}", self.body)?;
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub struct FunctionBody(pub CompoundCommand, pub Option<RedirectList>);
impl Display for FunctionBody {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)?;
        if let Some(redirect_list) = &self.1 {
            write!(f, "{}", redirect_list)?;
        }
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub struct BraceGroupCommand(pub CompoundList);
impl Display for BraceGroupCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "{{ ")?;
        write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{}", self.0)?;
        writeln!(f)?;
        write!(f, "}}")?;
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub struct DoGroupCommand(pub CompoundList);
impl Display for DoGroupCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "do")?;
        write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{}", self.0)?;
        writeln!(f)?;
        write!(f, "done")
    }
}
#[derive(Clone, Debug)]
pub struct SimpleCommand {
    pub prefix: Option<CommandPrefix>,
    pub word_or_name: Option<Word>,
    pub suffix: Option<CommandSuffix>,
}
impl Display for SimpleCommand {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut wrote_something = false;
        if let Some(prefix) = &self.prefix {
            if wrote_something {
                write!(f, " ")?;
            }
            write!(f, "{}", prefix)?;
            wrote_something = true;
        }
        if let Some(word_or_name) = &self.word_or_name {
            if wrote_something {
                write!(f, " ")?;
            }
            write!(f, "{}", word_or_name)?;
            wrote_something = true;
        }
        if let Some(suffix) = &self.suffix {
            if wrote_something {
                write!(f, " ")?;
            }
            write!(f, "{}", suffix)?;
        }
        Ok(())
    }
}
#[derive(Clone, Debug, Default)]
pub struct CommandPrefix(pub Vec<CommandPrefixOrSuffixItem>);
impl Display for CommandPrefix {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for (i, item) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, " ")?;
            }
            write!(f, "{}", item)?;
        }
        Ok(())
    }
}
#[derive(Clone, Default, Debug)]
pub struct CommandSuffix(pub Vec<CommandPrefixOrSuffixItem>);
impl Display for CommandSuffix {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for (i, item) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, " ")?;
            }
            write!(f, "{}", item)?;
        }
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub enum CommandPrefixOrSuffixItem {
    IoRedirect(IoRedirect),
    Word(Word),
    AssignmentWord(Assignment, Word),
}
impl Display for CommandPrefixOrSuffixItem {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CommandPrefixOrSuffixItem::IoRedirect(io_redirect) => write!(f, "{}", io_redirect),
            CommandPrefixOrSuffixItem::Word(word) => write!(f, "{}", word),
            CommandPrefixOrSuffixItem::AssignmentWord(_assignment, word) => write!(f, "{}", word),
        }
    }
}
#[derive(Clone, Debug)]
pub struct Assignment {
    pub name: AssignmentName,
    pub value: AssignmentValue,
    pub append: bool,
}
impl Display for Assignment {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.name)?;
        if self.append {
            write!(f, "+")?;
        }
        write!(f, "={}", self.value)
    }
}
#[derive(Clone, Debug)]
pub enum AssignmentName {
    VariableName(String),
    ArrayElementName(String, String),
}
impl Display for AssignmentName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AssignmentName::VariableName(name) => write!(f, "{}", name),
            AssignmentName::ArrayElementName(name, index) => {
                write!(f, "{}[{}]", name, index)
            }
        }
    }
}
#[derive(Clone, Debug)]
pub enum AssignmentValue {
    Scalar(Word),
    Array(Vec<(Option<Word>, Word)>),
}
impl Display for AssignmentValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AssignmentValue::Scalar(word) => write!(f, "{}", word),
            AssignmentValue::Array(words) => {
                write!(f, "(")?;
                for (i, value) in words.iter().enumerate() {
                    if i > 0 {
                        write!(f, " ")?;
                    }
                    match value {
                        (Some(key), value) => write!(f, "[{}]={}", key, value)?,
                        (None, value) => write!(f, "{}", value)?,
                    }
                }
                write!(f, ")")
            }
        }
    }
}
#[derive(Clone, Debug)]
pub struct RedirectList(pub Vec<IoRedirect>);
impl Display for RedirectList {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for item in &self.0 {
            write!(f, "{}", item)?;
        }
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub enum IoRedirect {
    File(Option<u32>, IoFileRedirectKind, IoFileRedirectTarget),
    HereDocument(Option<u32>, IoHereDocument),
    HereString(Option<u32>, Word),
    OutputAndError(Word),
}
impl Display for IoRedirect {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            IoRedirect::File(fd_num, kind, target) => {
                if let Some(fd_num) = fd_num {
                    write!(f, "{}", fd_num)?;
                }
                write!(f, "{} {}", kind, target)?;
            }
            IoRedirect::OutputAndError(target) => {
                write!(f, "&> {}", target)?;
            }
            IoRedirect::HereDocument(
                fd_num,
                IoHereDocument {
                    remove_tabs,
                    here_end,
                    doc,
                },
            ) => {
                if let Some(fd_num) = fd_num {
                    write!(f, "{}", fd_num)?;
                }
                write!(f, "<<")?;
                if *remove_tabs {
                    write!(f, "-")?;
                }
                writeln!(f, "{}", here_end)?;
                write!(f, "{}", doc)?;
                writeln!(f, "{}", here_end)?;
            }
            IoRedirect::HereString(fd_num, s) => {
                if let Some(fd_num) = fd_num {
                    write!(f, "{}", fd_num)?;
                }
                write!(f, "<<< {}", s)?;
            }
        }
        Ok(())
    }
}
#[derive(Clone, Debug)]
pub enum IoFileRedirectKind {
    Read,
    Write,
    Append,
    ReadAndWrite,
    Clobber,
    DuplicateInput,
    DuplicateOutput,
}
impl Display for IoFileRedirectKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            IoFileRedirectKind::Read => write!(f, "<"),
            IoFileRedirectKind::Write => write!(f, ">"),
            IoFileRedirectKind::Append => write!(f, ">>"),
            IoFileRedirectKind::ReadAndWrite => write!(f, "<>"),
            IoFileRedirectKind::Clobber => write!(f, ">|"),
            IoFileRedirectKind::DuplicateInput => write!(f, "<&"),
            IoFileRedirectKind::DuplicateOutput => write!(f, ">&"),
        }
    }
}
#[derive(Clone, Debug)]
pub enum IoFileRedirectTarget {
    Filename(Word),
    Fd(u32),
    ProcessSubstitution(SubshellCommand),
}
impl Display for IoFileRedirectTarget {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            IoFileRedirectTarget::Filename(word) => write!(f, "{}", word),
            IoFileRedirectTarget::Fd(fd) => write!(f, "{}", fd),
            IoFileRedirectTarget::ProcessSubstitution(subshell_command) => {
                write!(f, "{}", subshell_command)
            }
        }
    }
}
#[derive(Clone, Debug)]
pub struct IoHereDocument {
    pub remove_tabs: bool,
    pub here_end: Word,
    pub doc: Word,
}
#[derive(Clone, Debug)]
pub enum TestExpr {
    False,
    Literal(String),
    And(Box<TestExpr>, Box<TestExpr>),
    Or(Box<TestExpr>, Box<TestExpr>),
    Not(Box<TestExpr>),
    Parenthesized(Box<TestExpr>),
    UnaryTest(UnaryPredicate, String),
    BinaryTest(BinaryPredicate, String, String),
}
impl Display for TestExpr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TestExpr::False => Ok(()),
            TestExpr::Literal(s) => write!(f, "{s}"),
            TestExpr::And(left, right) => write!(f, "{left} -a {right}"),
            TestExpr::Or(left, right) => write!(f, "{left} -o {right}"),
            TestExpr::Not(expr) => write!(f, "! {}", expr),
            TestExpr::Parenthesized(expr) => write!(f, "( {expr} )"),
            TestExpr::UnaryTest(pred, word) => write!(f, "{pred} {word}"),
            TestExpr::BinaryTest(left, op, right) => write!(f, "{left} {op} {right}"),
        }
    }
}
#[derive(Clone, Debug)]
pub enum ExtendedTestExpr {
    And(Box<ExtendedTestExpr>, Box<ExtendedTestExpr>),
    Or(Box<ExtendedTestExpr>, Box<ExtendedTestExpr>),
    Not(Box<ExtendedTestExpr>),
    Parenthesized(Box<ExtendedTestExpr>),
    UnaryTest(UnaryPredicate, Word),
    BinaryTest(BinaryPredicate, Word, Word),
}
impl Display for ExtendedTestExpr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ExtendedTestExpr::And(left, right) => {
                write!(f, "{} && {}", left, right)
            }
            ExtendedTestExpr::Or(left, right) => {
                write!(f, "{} || {}", left, right)
            }
            ExtendedTestExpr::Not(expr) => {
                write!(f, "! {}", expr)
            }
            ExtendedTestExpr::Parenthesized(expr) => {
                write!(f, "( {} )", expr)
            }
            ExtendedTestExpr::UnaryTest(pred, word) => {
                write!(f, "{} {}", pred, word)
            }
            ExtendedTestExpr::BinaryTest(pred, left, right) => {
                write!(f, "{} {} {}", left, pred, right)
            }
        }
    }
}
#[derive(Clone, Debug)]
pub enum UnaryPredicate {
    FileExists,
    FileExistsAndIsBlockSpecialFile,
    FileExistsAndIsCharSpecialFile,
    FileExistsAndIsDir,
    FileExistsAndIsRegularFile,
    FileExistsAndIsSetgid,
    FileExistsAndIsSymlink,
    FileExistsAndHasStickyBit,
    FileExistsAndIsFifo,
    FileExistsAndIsReadable,
    FileExistsAndIsNotZeroLength,
    FdIsOpenTerminal,
    FileExistsAndIsSetuid,
    FileExistsAndIsWritable,
    FileExistsAndIsExecutable,
    FileExistsAndOwnedByEffectiveGroupId,
    FileExistsAndModifiedSinceLastRead,
    FileExistsAndOwnedByEffectiveUserId,
    FileExistsAndIsSocket,
    ShellOptionEnabled,
    ShellVariableIsSetAndAssigned,
    ShellVariableIsSetAndNameRef,
    StringHasZeroLength,
    StringHasNonZeroLength,
}
impl Display for UnaryPredicate {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            UnaryPredicate::FileExists => write!(f, "-e"),
            UnaryPredicate::FileExistsAndIsBlockSpecialFile => write!(f, "-b"),
            UnaryPredicate::FileExistsAndIsCharSpecialFile => write!(f, "-c"),
            UnaryPredicate::FileExistsAndIsDir => write!(f, "-d"),
            UnaryPredicate::FileExistsAndIsRegularFile => write!(f, "-f"),
            UnaryPredicate::FileExistsAndIsSetgid => write!(f, "-g"),
            UnaryPredicate::FileExistsAndIsSymlink => write!(f, "-h"),
            UnaryPredicate::FileExistsAndHasStickyBit => write!(f, "-k"),
            UnaryPredicate::FileExistsAndIsFifo => write!(f, "-p"),
            UnaryPredicate::FileExistsAndIsReadable => write!(f, "-r"),
            UnaryPredicate::FileExistsAndIsNotZeroLength => write!(f, "-s"),
            UnaryPredicate::FdIsOpenTerminal => write!(f, "-t"),
            UnaryPredicate::FileExistsAndIsSetuid => write!(f, "-u"),
            UnaryPredicate::FileExistsAndIsWritable => write!(f, "-w"),
            UnaryPredicate::FileExistsAndIsExecutable => write!(f, "-x"),
            UnaryPredicate::FileExistsAndOwnedByEffectiveGroupId => write!(f, "-G"),
            UnaryPredicate::FileExistsAndModifiedSinceLastRead => write!(f, "-N"),
            UnaryPredicate::FileExistsAndOwnedByEffectiveUserId => write!(f, "-O"),
            UnaryPredicate::FileExistsAndIsSocket => write!(f, "-S"),
            UnaryPredicate::ShellOptionEnabled => write!(f, "-o"),
            UnaryPredicate::ShellVariableIsSetAndAssigned => write!(f, "-v"),
            UnaryPredicate::ShellVariableIsSetAndNameRef => write!(f, "-R"),
            UnaryPredicate::StringHasZeroLength => write!(f, "-z"),
            UnaryPredicate::StringHasNonZeroLength => write!(f, "-n"),
        }
    }
}
#[derive(Clone, Debug)]
pub enum BinaryPredicate {
    FilesReferToSameDeviceAndInodeNumbers,
    LeftFileIsNewerOrExistsWhenRightDoesNot,
    LeftFileIsOlderOrDoesNotExistWhenRightDoes,
    StringExactlyMatchesPattern,
    StringDoesNotExactlyMatchPattern,
    StringMatchesRegex,
    StringContainsSubstring,
    LeftSortsBeforeRight,
    LeftSortsAfterRight,
    ArithmeticEqualTo,
    ArithmeticNotEqualTo,
    ArithmeticLessThan,
    ArithmeticLessThanOrEqualTo,
    ArithmeticGreaterThan,
    ArithmeticGreaterThanOrEqualTo,
}
impl Display for BinaryPredicate {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BinaryPredicate::FilesReferToSameDeviceAndInodeNumbers => write!(f, "-ef"),
            BinaryPredicate::LeftFileIsNewerOrExistsWhenRightDoesNot => write!(f, "-nt"),
            BinaryPredicate::LeftFileIsOlderOrDoesNotExistWhenRightDoes => write!(f, "-ot"),
            BinaryPredicate::StringExactlyMatchesPattern => write!(f, "=="),
            BinaryPredicate::StringDoesNotExactlyMatchPattern => write!(f, "!="),
            BinaryPredicate::StringMatchesRegex => write!(f, "=~"),
            BinaryPredicate::StringContainsSubstring => write!(f, "=~"),
            BinaryPredicate::LeftSortsBeforeRight => write!(f, "<"),
            BinaryPredicate::LeftSortsAfterRight => write!(f, ">"),
            BinaryPredicate::ArithmeticEqualTo => write!(f, "-eq"),
            BinaryPredicate::ArithmeticNotEqualTo => write!(f, "-ne"),
            BinaryPredicate::ArithmeticLessThan => write!(f, "-lt"),
            BinaryPredicate::ArithmeticLessThanOrEqualTo => write!(f, "-le"),
            BinaryPredicate::ArithmeticGreaterThan => write!(f, "-gt"),
            BinaryPredicate::ArithmeticGreaterThanOrEqualTo => write!(f, "-ge"),
        }
    }
}
#[derive(Clone, Debug)]
pub struct Word {
    pub value: String,
}
impl Display for Word {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.value)
    }
}
impl From<&tokenizer::Token> for Word {
    fn from(t: &tokenizer::Token) -> Word {
        match t {
            tokenizer::Token::Word(value, _) => Word {
                value: value.clone(),
            },
            tokenizer::Token::Operator(value, _) => Word {
                value: value.clone(),
            },
        }
    }
}
impl Word {
    pub fn new(s: &str) -> Self {
        Self {
            value: s.to_owned(),
        }
    }
    pub fn flatten(&self) -> String {
        self.value.clone()
    }
}
#[derive(Clone, Debug)]
pub struct UnexpandedArithmeticExpr {
    pub value: String,
}
impl Display for UnexpandedArithmeticExpr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.value)
    }
}
#[derive(Clone, Debug)]
pub enum ArithmeticExpr {
    Literal(i64),
    Reference(ArithmeticTarget),
    UnaryOp(UnaryOperator, Box<ArithmeticExpr>),
    BinaryOp(BinaryOperator, Box<ArithmeticExpr>, Box<ArithmeticExpr>),
    Conditional(
        Box<ArithmeticExpr>,
        Box<ArithmeticExpr>,
        Box<ArithmeticExpr>,
    ),
    Assignment(ArithmeticTarget, Box<ArithmeticExpr>),
    BinaryAssignment(BinaryOperator, ArithmeticTarget, Box<ArithmeticExpr>),
    UnaryAssignment(UnaryAssignmentOperator, ArithmeticTarget),
}
impl Display for ArithmeticExpr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ArithmeticExpr::Literal(literal) => write!(f, "{literal}"),
            ArithmeticExpr::Reference(target) => write!(f, "{target}"),
            ArithmeticExpr::UnaryOp(op, operand) => write!(f, "{op}{operand}"),
            ArithmeticExpr::BinaryOp(op, left, right) => {
                if matches!(op, BinaryOperator::Comma) {
                    write!(f, "{left}{op} {right}")
                } else {
                    write!(f, "{left} {op} {right}")
                }
            }
            ArithmeticExpr::Conditional(condition, if_branch, else_branch) => {
                write!(f, "{condition} ? {if_branch} : {else_branch}")
            }
            ArithmeticExpr::Assignment(target, value) => write!(f, "{target} = {value}"),
            ArithmeticExpr::BinaryAssignment(op, target, operand) => {
                write!(f, "{target} {op}= {operand}")
            }
            ArithmeticExpr::UnaryAssignment(op, target) => match op {
                UnaryAssignmentOperator::PrefixIncrement
                | UnaryAssignmentOperator::PrefixDecrement => write!(f, "{op}{target}"),
                UnaryAssignmentOperator::PostfixIncrement
                | UnaryAssignmentOperator::PostfixDecrement => write!(f, "{target}{op}"),
            },
        }
    }
}
#[derive(Clone, Copy, Debug)]
pub enum BinaryOperator {
    Power,
    Multiply,
    Divide,
    Modulo,
    Comma,
    Add,
    Subtract,
    ShiftLeft,
    ShiftRight,
    LessThan,
    LessThanOrEqualTo,
    GreaterThan,
    GreaterThanOrEqualTo,
    Equals,
    NotEquals,
    BitwiseAnd,
    BitwiseXor,
    BitwiseOr,
    LogicalAnd,
    LogicalOr,
}
impl Display for BinaryOperator {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BinaryOperator::Power => write!(f, "**"),
            BinaryOperator::Multiply => write!(f, "*"),
            BinaryOperator::Divide => write!(f, "/"),
            BinaryOperator::Modulo => write!(f, "%"),
            BinaryOperator::Comma => write!(f, ","),
            BinaryOperator::Add => write!(f, "+"),
            BinaryOperator::Subtract => write!(f, "-"),
            BinaryOperator::ShiftLeft => write!(f, "<<"),
            BinaryOperator::ShiftRight => write!(f, ">>"),
            BinaryOperator::LessThan => write!(f, "<"),
            BinaryOperator::LessThanOrEqualTo => write!(f, "<="),
            BinaryOperator::GreaterThan => write!(f, ">"),
            BinaryOperator::GreaterThanOrEqualTo => write!(f, ">="),
            BinaryOperator::Equals => write!(f, "=="),
            BinaryOperator::NotEquals => write!(f, "!="),
            BinaryOperator::BitwiseAnd => write!(f, "&"),
            BinaryOperator::BitwiseXor => write!(f, "^"),
            BinaryOperator::BitwiseOr => write!(f, "|"),
            BinaryOperator::LogicalAnd => write!(f, "&&"),
            BinaryOperator::LogicalOr => write!(f, "||"),
        }
    }
}
#[derive(Clone, Copy, Debug)]
pub enum UnaryOperator {
    UnaryPlus,
    UnaryMinus,
    BitwiseNot,
    LogicalNot,
}
impl Display for UnaryOperator {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            UnaryOperator::UnaryPlus => write!(f, "+"),
            UnaryOperator::UnaryMinus => write!(f, "-"),
            UnaryOperator::BitwiseNot => write!(f, "~"),
            UnaryOperator::LogicalNot => write!(f, "!"),
        }
    }
}
#[derive(Clone, Copy, Debug)]
pub enum UnaryAssignmentOperator {
    PrefixIncrement,
    PrefixDecrement,
    PostfixIncrement,
    PostfixDecrement,
}
impl Display for UnaryAssignmentOperator {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            UnaryAssignmentOperator::PrefixIncrement => write!(f, "++"),
            UnaryAssignmentOperator::PrefixDecrement => write!(f, "--"),
            UnaryAssignmentOperator::PostfixIncrement => write!(f, "++"),
            UnaryAssignmentOperator::PostfixDecrement => write!(f, "--"),
        }
    }
}
#[derive(Clone, Debug)]
pub enum ArithmeticTarget {
    Variable(String),
    ArrayElement(String, Box<ArithmeticExpr>),
}
impl Display for ArithmeticTarget {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ArithmeticTarget::Variable(name) => write!(f, "{name}"),
            ArithmeticTarget::ArrayElement(name, index) => write!(f, "{}[{}]", name, index),
        }
    }
}