ion-shell 1.0.1

The Ion Shell
use parser::assignments::parse_assignment;
use parser::{pipelines, ArgumentSplitter};
use shell::flow_control::{ElseIf, Statement};

#[pub]
parse_ -> Statement
      = let_
      / if_
      / else_if_
      / else_
      / for_
      / while_
      / fn_
      / end_
      / break_
      / continue_
      / pipelines

#[pub]
let_ -> Statement
    = whitespace* "let" whitespace? value:$(.*) {
        Statement::Let { expression: parse_assignment(value) }
    }

#[pub]
break_ -> Statement
    = whitespace* "break" { Statement::Break }

#[pub]
continue_ -> Statement
    = whitespace* "continue" { Statement::Continue }

#[pub]
if_ -> Statement
    = whitespace* "if" whitespace? command:$(.*) {?
        let mut possible_error = None;
        let pipeline = pipelines::collect(&mut possible_error, command);

        match possible_error {
            Some(error) => Err(error),
            None => Ok(Statement::If {
                expression: pipeline,
                success: Vec::new(),
                else_if: Vec::new(),
                failure: Vec::new()
            })
        }
    }

#[pub]
else_if_ -> Statement
    = whitespace* "else" whitespace? "if" whitespace? command:$(.*) {?
        let mut possible_error = None;
        let pipeline = pipelines::collect(&mut possible_error, command);

        match possible_error {
            Some(error) => Err(error),
            None => Ok(Statement::ElseIf(ElseIf {
                expression: pipeline,
                success:    Vec::new(),
            }))
        }
    }

#[pub]
else_ -> Statement
    = whitespace* "else" whitespace*  { Statement::Else }

#[pub]
end_ -> Statement
    = whitespace* "end" whitespace* { Statement ::End }

#[pub]
fn_ -> Statement
    = whitespace* "fn " n:_name whitespace* args:_args whitespace* description:_description? {
        Statement::Function {
            description: description.unwrap_or("".into()),
            name: n.into(),
            args: args,
            statements: Vec::new(),
        }
    }

_description -> String
      = "--" whitespace* description:$([^\r\n]*) { description.into() }

_name -> String
      = n:$([A-z0-9_]+) { n.into() }

_args -> Vec<String>
      = _arg ** " "

_arg -> String
     = n:$([A-z0-9]+) { n.into() }

#[pub]
for_ -> Statement
    = whitespace* "for" whitespace? n:_name whitespace? "in" whitespace? expr:$(.*) {
        Statement::For {
            variable: n.into(),
            values: ArgumentSplitter::new(expr).map(String::from).collect(),
            statements: Vec::new(),
        }
    }

#[pub]
while_ -> Statement
    = whitespace* "while" whitespace? command:$(.*) {?
        let mut possible_error = None;
        let pipeline = pipelines::collect(&mut possible_error, command);

        match possible_error {
            Some(error) => Err(error),
            None => Ok(Statement::While {
                expression: pipeline,
                statements: Vec::new()
            })
        }
    }

pub pipelines -> Statement
    = (unused* newline)* [#] .* { Statement::Default }
    / [ \n\t\r]* pipeline:_pipelines { pipeline }
    / (unused*) ** newline { Statement::Default }


// Converts the pipeline string into a statement, handling redirection, piping, and backgrounds.
_pipelines -> Statement
    = command:$(.+) {?
    let mut possible_error = None;
    let pipeline = pipelines::collect(&mut possible_error, command);

    match possible_error {
        Some(error) => Err(error),
        None        => Ok(Statement::Pipeline(pipeline))
    }
}

unused -> ()
    = whitespace comment? { () }
    / comment { () }

comment -> ()
    = [#] [^\r\n]*

whitespace -> ()
    = [ \t]+

job_ending -> ()
    = [;]
    / newline

newline -> ()
    = [\r\n]