lexington 0.3.0

A very simple library for lexing / parsing
Documentation
use lexington::{Any,Lexer,Match,Matcher,Scanner,Token,Within};    
use lexington::{ShiftReduceParser,ShiftReduceRule};

/// A minimal kind of token for testing.
#[derive(Clone,Copy,PartialEq)]
enum Kind { WhiteSpace, Number, LeftBrace, RightBrace }

#[test]
fn shift_reduce_parser_01() {
    check("1", Ok(vec![1]));
}

#[test]
fn shift_reduce_parser_02() {
    check("x", Err(()));
}

#[test]
fn shift_reduce_parser_03() {
    check("(1)", Ok(vec![1]));
}

#[test]
fn shift_reduce_parser_04() {
    check("((1))", Ok(vec![1]));
}

#[test]
fn shift_reduce_parser_05() {
    check("(1 2)", Ok(vec![1,2]));
}    

#[test]
fn shift_reduce_parser_06() {
    check("(1 2 3)", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_07() {
    check("((1) 2 3)", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_08() {
    check("((1 2) 3)", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_09() {
    check("(((1) 2) 3)", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_10() {
    check("((1 2 3))", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_11() {
    check("(1 (2 3))", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_12() {
    check("(1 (2 (3)))", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_13() {
    check("(1 (2) 3)", Ok(vec![1,2,3]));
}    

#[test]
fn shift_reduce_parser_14() {
    check("(1 2 (3))", Ok(vec![1,2,3]));
}    

fn check(input: &str, expected: Result<Vec<usize>,()>) {
    let tokens = lex(input);
    // Simple reduction rule for combining vectors
    let reduction_rule = |mut l:Vec<usize>,r:Vec<usize>| { l.extend(r); Ok(l) };    
    // Rule for parsing strings into numbers
    let actual = ShiftReduceParser::new()
        .apply(reduction_rule)
        .terminate(Kind::Number,|tok| vec![input[tok.range()].parse().unwrap()])
        .skip(Kind::WhiteSpace)
        .open(Kind::LeftBrace, Vec::new())
        .close(Kind::RightBrace)
        .parse(tokens);    
    // Check input matches output
    assert_eq!(actual,expected);
}

// ========================================================================
// Helpers
// ========================================================================    

/// Construct a very simple lexer for S-expressions, and scan them to
/// produce a list of zero or more tokens.
fn lex(input: &str) -> Vec<Token<Kind>> {
    // [ \n\t]+
    let whitespace = Any([' ','\n','\t']).one_or_more();
    // [0..9]+
    let number = Within('0'..='9').one_or_more();
    // Construct scanner
    let scanner = Match(whitespace,Kind::WhiteSpace)
        .and_match(number,Kind::Number)
        .and_match('(',Kind::LeftBrace)
        .and_match(')',Kind::RightBrace);
    // Construct the lexer.
    Lexer::new(input,scanner).collect()
}