parse-regex 0.0.1

A recursive descent parser for regular expressions
pub type ParseResult = Result<RegEx, &'static str>;

#[derive(Eq, PartialEq, Debug)]
pub enum RegEx {
    Or(Box<RegEx>, Box<RegEx>),
    Sequence(Vec<Box<RegEx>>),
    Repetition(Box<RegEx>),
    Terminal(char),
}

pub struct RegExParser {
    s: String,
    pos: usize
}

impl RegExParser {
    pub fn new(s: String) -> RegExParser {
        RegExParser { s: s, pos: 0 }
    }

    pub fn parse(&mut self) -> ParseResult {
        self.regex()
    }

    fn regex(&mut self) -> ParseResult {
        let t1 = try!(self.term());
        if self.more() && self.peek() == '|' {
            self.consume('|').unwrap();
            let t2 = try!(self.term());
            Ok(RegEx::Or(Box::new(t1), Box::new(t2)))
        } else {
            Ok(t1)
        }
    }

    fn term(&mut self) -> ParseResult {
        let mut v = Vec::new();
        while self.more() && self.peek() != ')' && self.peek() != '|' {
            let f = try!(self.factor());
            v.push(Box::new(f));
        }
        Ok(RegEx::Sequence(v))
    }

    fn factor(&mut self) -> ParseResult {
        let b = try!(self.base());
        if self.more() && self.peek() == '*' {
            self.consume('*').unwrap();
            Ok(RegEx::Repetition(Box::new(b)))
        } else {
            Ok(b)
        }
    }

    fn base(&mut self) -> ParseResult {
        if self.peek() == '(' {
            self.consume('(').unwrap();
            let r = try!(self.regex());
            self.consume(')').unwrap();
            Ok(r)
        } else {
            Ok(RegEx::Terminal(self.next()))
        }
    }

    fn peek(&self) -> char {
        assert!(self.s.len() > self.pos);
        self.s.char_at(self.pos)
    }

    fn consume(&mut self, c: char) -> Result<(), String> {
        let p = self.peek();
        if p == c {
            self.pos += 1;
            Ok(())
        } else {
            Err(format!("Expected {} at position {} but got {}", c, self.pos, p))
        }
    }

    fn next(&mut self) -> char {
        let c = self.peek();
        self.consume(c).unwrap();
        c
    }

    fn more(&self) -> bool {
        self.s.len() > self.pos
    }
}

#[cfg(test)]
mod test {
    use parser::RegEx::{Or, Repetition, Sequence, Terminal};
    use parser::RegExParser;

    #[test]
    fn test_basic() {
        let mut p = RegExParser::new("".to_string());
        assert_eq!(p.parse(), Ok(Sequence(vec![])));

        let mut p = RegExParser::new("a".to_string());
        assert_eq!(p.parse(), Ok(Sequence(vec![Box::new(Terminal('a'))])));

        let mut p = RegExParser::new("a|b".to_string());
        assert_eq!(p.parse(), Ok(Or(Box::new(Sequence(vec![Box::new(Terminal('a'))])),
                                    Box::new(Sequence(vec![Box::new(Terminal('b'))])))));

        let mut p = RegExParser::new("a*".to_string());
        assert_eq!(p.parse(), Ok(Sequence(vec![Box::new(Repetition(Box::new(Terminal('a'))))])));

        let mut p = RegExParser::new("((a|b*)|a*)|aab".to_string());
        let res = Ok(Or(
            Box::new(Sequence(vec![Box::new(Or(
                Box::new(Sequence(
                    vec![Box::new(Or(Box::new(Sequence(vec![Box::new(Terminal('a'))])),
                                     Box::new(Sequence(
                                         vec![Box::new(Repetition(Box::new(Terminal('b'))))]))))])),
                Box::new(Sequence(vec![Box::new(Repetition(Box::new(Terminal('a'))))]))))])),
            Box::new(Sequence(vec![Box::new(Terminal('a')), Box::new(Terminal('a')),
                                   Box::new(Terminal('b'))]))));
        assert_eq!(p.parse(), res);
    }
}