pub enum Ast { AssignVar(AssignVar), Pipes(Pipes), } pub struct AssignVar { pub to: String, // TODO: body } pub struct Pipes { pub cmds: Vec, } pub struct Command { pub path: String, pub args: Vec, } 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, UnknownChar(char), } type Result = std::result::Result; pub fn do_parse(x: &str) -> Result { let chars: Vec = x.chars().collect(); Ast::parse(&mut &chars[..]) } trait Parse: Sized { fn parse(b: &mut &[char]) -> Result; } #[inline(always)] fn parse(b: &mut &[char]) -> Result { T::parse(b) } fn spaces(b: &mut &[char]) { while let Some(' ' | '\t') = b.get(0) { *b = &b[1..]; } } #[inline(always)] fn adv(b: &mut &[char]) { *b = &b[1..] } fn parse_quoted_string(b: &mut &[char], delim: char) -> Result { // TODO: escape sequence stuff *b = &b[1..]; let mut s = String::new(); while b.len() > 0 { if b[0] == delim { adv(b); return Ok(s); } s.push(b[0]); adv(b); } Err(ParseError::Incomplete) } impl Parse for String { fn parse(b: &mut &[char]) -> Result { spaces(b); if b.is_empty() { return Err(ParseError::Eof); } let c = b[0]; if c == '|' { Err(ParseError::UnexpectedPipe) } else if c == '\'' || c == '"' { adv(b); parse_quoted_string(b, c) } else if c.is_ascii_graphic() { parse_quoted_string(b, ' ') } else { Err(ParseError::UnknownChar(c)) } } } impl Parse for Ast { fn parse(b: &mut &[char]) -> Result { Ok(Self::Pipes(parse(b)?)) } } impl Parse for Command { fn parse(b: &mut &[char]) -> Result { let path: String = 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 { path, args }) } } impl Parse for Pipes { fn parse(b: &mut &[char]) -> Result { let cmds: Vec = vec![parse(b)?]; loop { spaces(b); if b.is_empty() { return Ok(Pipes { cmds }); } let c = b[0]; if c == '|' { adv(b); } else { Err(ParseError::UnknownChar(c))?; } } } }