#[derive(Debug)] pub enum Ast { AssignVar(AssignVar), Pipes(Pipes), } #[derive(Debug)] pub struct AssignVar { pub to: String, // TODO: body } #[derive(Debug)] pub struct Pipes { pub cmds: Vec, } #[derive(Debug)] pub struct Command { pub cmd: Vec, pub args: Vec>, } #[derive(Debug)] pub enum ParseError { /// "clean" EOF, i.e. not in the middle of something Eof, /// "unclean" EOF, i.e. EOF after beginning a quoted string Incomplete, UnexpectedPipe, Unknown(u8), } type Result = std::result::Result; pub fn do_parse(mut x: &[u8]) -> Result { Ast::parse(&mut x) } trait Parse: Sized { fn parse(b: &mut &[u8]) -> Result; } #[inline(always)] fn parse(b: &mut &[u8]) -> Result { T::parse(b) } fn spaces(b: &mut &[u8]) { while let Some(b' ' | b'\t') = b.get(0) { *b = &b[1..]; } } #[inline(always)] fn adv(b: &mut &[u8]) { *b = &b[1..] } fn parse_quoted_string(b: &mut &[u8], delim: u8) -> Result> { // TODO: escape sequence stuff let mut s = Vec::new(); while b.len() > 0 { if b[0] == delim { adv(b); return Ok(s); } s.push(b[0]); adv(b); } if delim == b' ' { Ok(s) } else { Err(ParseError::Incomplete) } } impl Parse for Vec { fn parse(b: &mut &[u8]) -> Result { spaces(b); if b.is_empty() { return Err(ParseError::Eof); } let c = b[0]; if c == b'|' { Err(ParseError::UnexpectedPipe) } else if c == b'\'' || c == b'"' { adv(b); parse_quoted_string(b, c) } else if c.is_ascii_graphic() { parse_quoted_string(b, b' ') } else { Err(ParseError::Unknown(c)) } } } impl Parse for Ast { fn parse(b: &mut &[u8]) -> Result { Ok(Self::Pipes(parse(b)?)) } } impl Parse for Command { fn parse(b: &mut &[u8]) -> Result { let path: Vec = parse(b)?; let mut args = Vec::new(); loop { let arg: Result> = parse(b); match arg { Ok(arg) => args.push(arg), Err(ParseError::Eof | ParseError::UnexpectedPipe) => break, Err(e) => Err(e)?, } } Ok(Self { cmd: path, args }) } } impl Parse for Pipes { fn parse(b: &mut &[u8]) -> Result { let mut cmds: Vec = vec![parse(b)?]; loop { spaces(b); if b.is_empty() { return Ok(Pipes { cmds }); } let c = b[0]; if c == b'|' { adv(b); cmds.push(parse(b)?); } else { Err(ParseError::Unknown(c))?; } } } }