cypress 0.3.0

Build simple yet expressive parsers
Documentation
use cypress::prelude::*;

#[test]
fn t_just() {
    let input = b"ABC".into_input();

    let parser = just('A');

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, b'A'),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_then() {
    let input = b"ABC".into_input();

    let parser = just('A').then(just('B'));

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, (b'A', b'B')),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_or() {
    let input = b"B".into_input();

    let parser = just('A').or(just('B'));

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, b'B'),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_map() {
    let input = b"A".into_input();

    let parser = just('A').map(|_| 1);

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, 1),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_choice() {
    let input = b"A".into_input();

    let parser = choice!(just('C'), just('A'), just('C'));

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, b'A'),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_pbetween() {
    let input = "[A]".as_bytes().into_input();

    let parser = pbetween(just('['), just('A'), just(']'));

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, b'A'),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_pmany() {
    let input = b"ABCDE".into_input();

    let parser = pletter().many();

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, [b'A', b'B', b'C', b'D', b'E']),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_padded() {
    let input = b"   A   ".into_input();

    let parser = ppadded(just('A'), pws::<u8>());

    match parser.parse(input) {
        Ok(PSuccess { val, rest }) => {
            assert_eq!(val, b'A');
            assert_eq!(rest.loc, rest.tokens.len())
        }
        Err(_) => assert!(false),
    }
}

#[test]
fn t_ident() {
    let input = "Noah Cape".into_input();

    let parser = pident("Noah")
        .padded_by(pws())
        .then(pident("Cape").padded_by(pws()));

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => {
            assert_eq!(val, ("Noah", "Cape"))
        }
        Err(_) => assert!(false),
    }
}

#[test]
fn t_pstring() {
    let input = "\"This is a string\"".into_input();

    let parser = just('\"')
        .then(any().and(just('\"').not()).many())
        .then(just('\"'))
        .map(|((_, xs), _)| String::from_utf8(xs).unwrap());

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, String::from("This is a string")),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_recursive() {
    let input = b"(1+(2+3))".into_input();

    #[derive(Debug, PartialEq, Clone)]
    enum AST {
        Num(u32),
        Expr(Box<AST>, Box<AST>),
    }

    let parser = recursive(|expr| {
        Box::new(choice!(
            pnum().map(|a: u8| AST::Num((a - b'0').into())),
            just('(')
                .then(expr.clone())
                .then(just('+').then(expr))
                .then(just(')'))
                .map(|(((_, lhs), (_, rhs)), _)| AST::Expr(Box::new(lhs), Box::new(rhs)))
        ))
    });

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(
            val,
            AST::Expr(
                Box::new(AST::Num(1)),
                Box::new(AST::Expr(Box::new(AST::Num(2)), Box::new(AST::Num(3))))
            )
        ),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_select() {
    #[derive(Debug, PartialEq, Clone)]
    enum Expr {
        Left,
        Right,
    }
    let input = "<><".into_input();

    let parser = select! {
        '<' => Expr::Left,
        (just('>')) => Expr::Right,
    }
    .many();

    match parser.parse(input) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, vec![Expr::Left, Expr::Right, Expr::Left]),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_parser_macro() {
    let input = "A+B";

    #[derive(Debug, PartialEq)]
    enum Expr {
        A,
        B,
    }

    let parser = sequence!((just('A')) > '+' > 'B' => |_| (Expr::A, Expr::B));

    match parser.parse(input.into_input()) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, (Expr::A, Expr::B)),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_puntil_end() {
    let input = "<><>>>-<<";

    let parser = choice!(just('<'), just('>')).many().until_end();

    match parser.parse(input.into_input()) {
        Ok(_) => assert!(false),
        Err(_) => assert!(true),
    }
}

#[test]
fn t_foldl() {
    let input = "1 - 2 - 3 - 4";

    let pdigit = pnum().map(|n: u8| (n - b'0') as i32).padded_by(pws());

    let parser = pdigit
        .clone()
        .foldl(just('-').ignore_then(pdigit).many(), |a, b| a - b);

    match parser.parse(input.into_input()) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, -8),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_precedence_macro() {
    #[derive(Clone, PartialEq, Debug)]
    enum BinOp {
        Add,
        Sub,
        Mult,
        Div,
    }

    #[derive(Clone, PartialEq, Debug)]
    enum Expr {
        Num(i32),
        Op(Box<Expr>, BinOp, Box<Expr>),
    }

    let atom = pnum()
        .many1()
        .map(|xs| Expr::Num(String::from_utf8(xs).unwrap().parse::<i32>().unwrap()))
        .padded_by(pws());

    let parser = precedence! {
        atom,
        {
            choice!(
                just('*').into_(BinOp::Mult),
                just('/').into_(BinOp::Div),
            )
            =>
            |a, (bop, b)| Expr::Op(Box::new(a), bop, Box::new(b))
        },
        {
            choice!(
                just('+').into_(BinOp::Add),
                just('-').into_(BinOp::Sub),
            )
            =>
            |a, (bop, b)| Expr::Op(Box::new(a), bop, Box::new(b))
        },
    };

    let input = "3 + 2 * 2 - 1";

    let expected_result = Expr::Op(
        Box::new(Expr::Op(
            Box::new(Expr::Num(3)),
            BinOp::Add,
            Box::new(Expr::Op(
                Box::new(Expr::Num(2)),
                BinOp::Mult,
                Box::new(Expr::Num(2)),
            )),
        )),
        BinOp::Sub,
        Box::new(Expr::Num(1)),
    );

    match parser.parse(input.into_input()) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, expected_result),
        Err(_) => assert!(false),
    }
}

#[test]
fn t_foldl_precedence() {
    #[derive(Clone, PartialEq, Debug)]
    enum BinOp {
        Add,
        Sub,
        Mult,
        Div,
    }

    #[derive(Clone, PartialEq, Debug)]
    enum Expr {
        Num(i32),
        Op(Box<Expr>, BinOp, Box<Expr>),
    }

    let atom = pnum()
        .many1()
        .map(|xs| Expr::Num(String::from_utf8(xs).unwrap().parse::<i32>().unwrap()))
        .padded_by(pws());

    let mul_div = atom.clone().foldl(
        (just('*').into_(BinOp::Mult).or(just('/').into_(BinOp::Div)))
            .then(atom.clone())
            .many(),
        |a, (bop, b)| Expr::Op(Box::new(a), bop, Box::new(b)),
    );

    let parser = mul_div.clone().foldl(
        (just('+').into_(BinOp::Add).or(just('-').into_(BinOp::Sub)))
            .then(mul_div)
            .many(),
        |a, (bop, b)| Expr::Op(Box::new(a), bop, Box::new(b)),
    );

    let input = "3 + 2 * 2 - 1";

    let expected_result = Expr::Op(
        Box::new(Expr::Op(
            Box::new(Expr::Num(3)),
            BinOp::Add,
            Box::new(Expr::Op(
                Box::new(Expr::Num(2)),
                BinOp::Mult,
                Box::new(Expr::Num(2)),
            )),
        )),
        BinOp::Sub,
        Box::new(Expr::Num(1)),
    );

    match parser.parse(input.into_input()) {
        Ok(PSuccess { val, rest: _ }) => assert_eq!(val, expected_result),
        Err(_) => assert!(false),
    }
}