topdown-rs 0.0.1

A top-down parsing library
use super::{CharSeq, ParserResult, Parser, Succ, Error, Fail};

pub struct Chainl<'a, T> {
    term: &'a (Parser<T>+'a),
    operator: &'a (Parser< |T, T|: 'a -> T>+'a),
    allow_trailing_operator: bool
}

pub fn chainl<'a, T>(p: &'a (Parser<T>+'a), o: &'a (Parser<|T, T|: 'a -> T>+'a), a: bool) -> Chainl<'a, T> {
    return Chainl{term: p, operator: o, allow_trailing_operator: a};
}

impl<'a, T> Chainl<'a, T> {
    #[allow(unused_variables)]
    fn tail(&self, cs: &mut CharSeq, l: T) -> ParserResult<T> {
        match cs.accept(self.operator) {
            Succ(o) => {
                match cs.accept(self.term) {
                    Succ(r) => return self.tail(cs, o(l, r)),
                    Fail(m, lo) => {
                        if self.allow_trailing_operator {
                            return Succ(l)
                        } else {
                            return Fail(m, lo)
                        }
                    },
                    Error(m, l) => return Error(m, l)
                }
            },
            Fail(m, lo) => Succ(l),
            Error(m, lo) => Succ(l)
        }
    }
}

impl<'a, T> Parser<T> for Chainl<'a, T> {
    fn _parse(&self, cs: &mut CharSeq) -> ParserResult<T> {
        match cs.accept(self.term) {
            Succ(l) => return self.tail(cs, l),
            Fail(m, l) => return Fail(m, l),
            Error(m, l) => return Error(m, l)
        }
    }

}

#[cfg(test)]
#[allow(unused_variables)]
#[allow(unused_imports)]
mod tests {
    use super::chainl;
    use super::super::{CharSeq, re, ParserResult, Parser, Succ, Fail, Error};

    struct Op<'a>;

    impl<'a> Parser<|int, int|:'a->int> for Op<'a> {
        fn _parse(&self, cs: &mut CharSeq) -> ParserResult<|int, int|:'a->int> {
            return cs.accept(&re("\\+|-"))
                .map(|o| 
                     if o.as_slice() == "+" {
                        return |a:int, b:int| a+b;
                    } else {
                        return |a:int, b:int| a-b
                    }
                    );
        }
    }

    struct Expr;

    impl Parser<int> for Expr {
        fn _parse(&self, cs: &mut CharSeq) -> ParserResult<int> {
            return cs.accept(&re("[1-9][0-9]*")).map(|x| x.as_slice().parse().unwrap());
        }
    }

    #[test]
    fn test_chainl1() {
        let mut cs = CharSeq::new("1-5", "<mem>");
        let term = Expr;
        let op = Op;
        let expr = chainl(&term, &op, false);
        match cs.accept(&expr) {
            Succ(c) => assert_eq!(c, -4),
            Fail(a, b) => assert!(false, "failed"),
            Error(a, b) => assert!(false, "error")
        }
    }


    #[test]
    fn test_chainl2() {
        let mut cs = CharSeq::new("1-5-6", "<mem>");
        let term = Expr;
        let op = Op;
        let expr = chainl(&term, &op, false);
        match cs.accept(&expr) {
            Succ(c) => assert_eq!(c, -10),
            Fail(a, b) => assert!(false, "failed"),
            Error(a, b) => assert!(false, "error")
        }
    }
}