topdown-rs 0.3.3

A top-down parsing library
Documentation
#![crate_type = "rlib"]
#![crate_type = "dylib"]
extern crate regex;

pub use self::ParserResult::{Succ, Fail, Error};
use std::cmp::{min, Ordering};
use std::fmt;

pub use re::re;
pub use chainl::chainl;
pub use chainr::chainr;
pub use choice::choice;
pub use until::until;
pub use many::many;
pub use many1::many1;
pub use keyword::keyword;
pub use try::try;
pub use skip::skip;
pub use cond::cond;
pub use opt::opt;
pub use wrapper::wrap;
pub use split::split;
pub use many_until::many_until;

pub mod re;
pub mod chainl;
pub mod chainr;
pub mod choice;
pub mod until;
pub mod many;
pub mod many1;
pub mod keyword;
pub mod try;
pub mod skip;
pub mod cond;
pub mod opt;
pub mod wrapper;
pub mod split;
pub mod many_until;
mod macros;


pub struct CharSeq<'a> {
    pub pos: usize,
    pub text: String,
    pub place: String,
    hooks: Vec<&'a (ParserHook+'a)>,
    pub enable_hook: bool,
    pub trace: bool
}

#[derive(Clone)]
pub struct LocationInfo {
    pub col: usize,
    pub row: usize,
    pub pos: usize,
    pub place: String
}

impl Ord for LocationInfo {
    fn cmp(&self, other: &LocationInfo) -> Ordering {
        if self.row == other.row {
            self.col.cmp(&other.col)
        } else {
            self.row.cmp(&other.row)
        }
    }
}

impl PartialOrd for LocationInfo {
    fn partial_cmp(&self, other: &LocationInfo) -> Option<Ordering> {
        Some(self.pos.cmp(&other.pos))
    }
    fn lt(&self, other: &LocationInfo) -> bool {
        self.pos < other.pos
    }
    fn le(&self, other: &LocationInfo) -> bool {
        self.pos <= other.pos
    }
    fn gt(&self, other: &LocationInfo) -> bool {
        self.pos > other.pos
    }
    fn ge(&self, other: &LocationInfo) -> bool {
        self.pos >= other.pos
    }
}


impl PartialEq for LocationInfo {
    fn eq(&self, other: &LocationInfo) -> bool {
        self.pos == other.pos
    }
}

impl Eq for LocationInfo {
}

impl fmt::Display for LocationInfo {
    fn fmt(&self, f:&mut fmt::Formatter) -> fmt::Result {
        f.write_str(format!("Col: {}, Row: {} at {}", self.col, self.row, self.place).as_str())
    }
}

pub enum ParserResult<T> {
    Succ(T),
    Error(String, LocationInfo),
    Fail(String, LocationInfo),
}

impl<T> ParserResult<T> {
    pub fn map<S, F>(self, f: F) -> ParserResult<S> where F: Fn(T) -> S {
        match self {
            Succ(c) => Succ(f(c)),
            Fail(m, l) => Fail(m.clone(), l.clone()),
            Error(m, l) => Error(m.clone(), l.clone())
        }
    }

    pub fn and_then<S, F>(self, mut f: F) -> ParserResult<S> where F: FnMut(T) -> ParserResult<S> {
        match self {
            Succ(c) => f(c),
            Fail(m, l) => Fail(m.clone(), l.clone()),
            Error(m, l) => Error(m.clone(), l.clone())
        }
    }
}

impl<'a> CharSeq<'a> {
    pub fn view(&self) -> &str {
        return &self.text[self.pos..];
    }

    pub fn eof(&self) -> bool {
        return self.text.len() <= self.pos;
    }

    pub fn new(t: &str, l: &str) -> CharSeq<'a> {
        return CharSeq{pos: 0, text: t.to_string(), place: l.to_string(), hooks: Vec::new(), enable_hook: true, trace: false};
    }

    pub fn fail<T>(&self, msg: &str) -> ParserResult<T> {
        return Fail(msg.to_string(), self.get_location());
    }

    pub fn get_location(&self) -> LocationInfo {
        let mut col = 1;
        let mut row = 1;
        let max = self.pos;
        let mut i = 0;
        for c in self.text.chars() {
            if i >= max {
                break;
            }
            col += 1;
            if c == '\n' {
                row += 1;
                col = 1;
            }
            i+=1;
        }
        return LocationInfo{col: col, row:row, pos: self.pos, place: self.place.clone()};
    }

    pub fn current(&self) -> char {
        if self.eof() {
            return '\0';
        } else {
            return self.text.chars().nth(self.pos).unwrap();
        }
    }

    pub fn next(&mut self) {
        if !self.eof() {
            self.pos += 1;
        }
    }

    pub fn add_hook(&mut self, hook: &'a (ParserHook+'a)) {
        self.hooks.push(hook);
    }

    #[allow(unused_variables)]
    pub fn do_hook(&mut self) {
        if !self.enable_hook {
            return ();
        }
        self.enable_hook = false;
        for h in self.hooks.clone().iter() {
            h.hook(self);
        }
        self.enable_hook = true;
        return ();
    }

    pub fn accept<T>(&mut self, p: &Parser<T>) -> ParserResult<T> {
        if self.trace {
            println!("{}", self.get_location());
        }
        return p.parse(self);
    }
}


pub trait Parser<T> {
    fn parse(&self, cs: &mut CharSeq) -> ParserResult<T> {
        cs.do_hook();
        let r = self._parse(cs);
        cs.do_hook();
        return r;
    }

    fn _parse(&self, cs: &mut CharSeq) -> ParserResult<T>;
    
    #[allow(unused_variables)]
    fn lookahead(&self, cs: &mut CharSeq) -> bool {
        let p = cs.pos;
        let result = match self.parse(cs) {
            Succ(c) => true,
            Fail(m, l) => false,
            Error(m, l) => false
        };
        cs.pos = p;
        return result;
    }
}

pub trait ParserHook {
    fn hook(&self, cs: &mut CharSeq);
}

impl<'a> Parser<String> for &'a str {
    fn _parse(&self, cs: &mut CharSeq) -> ParserResult<String> {
        if cs.view()[..min(self.len(), cs.view().len())] == **self {
            cs.pos = cs.pos + self.len();
            return Succ(self.to_string().clone());
        } else {
            return cs.fail(*self);
        }
    }
}

#[derive(Clone)]
pub struct EOF;

impl Parser<()> for EOF {
    fn _parse(&self, cs: &mut CharSeq) -> ParserResult<()> {
        if cs.eof() {
            Succ(())
        } else {
            cs.fail("not eof")
        }
    }
}


#[cfg(test)]
#[allow(unused_variables)]
#[allow(unused_imports)]
mod tests {
    use super::{CharSeq, ParserResult, Parser, Succ, Fail, Error};
    #[test]
    fn test_s1() {
        let mut cs = CharSeq::new("abcabcdef", "<mem>");
        assert!(cs.pos == 0);
        match cs.accept(&"abc") {
            Succ(a) => assert!(a == "abc"),
            _ => assert!(false, "bug")
        }
        assert!(cs.pos == 3);
        assert!(cs.view() == "abcdef");

        match cs.accept(&"abc") {
            Succ(a) => assert!(a == "abc"),
            _ => assert!(false, "bug")
        }
        assert!(cs.pos == 6);
        assert!(cs.view() == "def");
        
        match cs.accept(&"abc") {
            Succ(a) => assert!(false, format!("unexcepted result: {}, pos:{}", a, cs.pos)),
            Fail(a, b) => (),
            Error(a, b) => assert!(false, "error")
        }
        assert!(cs.pos == 6);
     
        match cs.accept(&"def") {
            Succ(a) => assert!(a=="def"),
            _ => assert!(false, "bug")
        }
        assert!(cs.pos == 9);
        assert!(cs.eof());

        match cs.accept(&"def") {
            Succ(a) => assert!(false, format!("unexcepted result: {}, pos: {}", a , cs.pos)),
            Fail(a, b) => (),
            Error(a, b) => assert!(false, "error")
        }
        assert!(cs.pos == 9);
        assert!(cs.eof());
    }

    #[test]
    fn test_s2() {
        let mut cs = CharSeq::new("abcdefghi", "<mem>");
        let r = cs.accept(&"abc").and_then(
            |a| cs.accept(&"def").and_then(
                |b| cs.accept(&"ghi").map(
                    |c| {
                        assert!(a == "abc");
                        assert!(b == "def");
                        assert!(c == "ghi");
                    }
                )
            )
        );
    }

    #[test]
    fn test_s3() {
        let mut cs = CharSeq::new("a", "<mem>");
        match cs.accept(&"abc") {
            Succ(a) => assert!(false, format!("unexcepted result: {}, pos: {}", a , cs.pos)),
            Fail(a, b) => (),
            Error(a, b) => assert!(false, "error")
        }
    }
}