frithu 0.1.0

A robust, runtime parsing engine based on the Earley/Marpa algorithm.
Documentation
extern crate frithu;

use frithu::grammar;
use frithu::parser;

fn calc_grammar() -> grammar::Grammar {
    let mut builder = grammar::GrammarBuilder::new("Sum");

    builder.rule("Number").terminal('0').add();
    builder.rule("Number").terminal('1').add();
    builder.rule("Number").terminal('2').add();
    builder.rule("Number").terminal('3').add();
    builder.rule("Number").terminal('4').add();
    builder.rule("Number").terminal('5').add();
    builder.rule("Number").terminal('6').add();
    builder.rule("Number").terminal('7').add();
    builder.rule("Number").terminal('8').add();
    builder.rule("Number").terminal('9').add();

    builder
        .rule("Number")
        .terminal('0')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('1')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('2')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('3')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('4')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('5')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('6')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('7')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('8')
        .nonterminal("Number")
        .add();
    builder
        .rule("Number")
        .terminal('9')
        .nonterminal("Number")
        .add();

    builder.rule("Factor").nonterminal("Number").add();
    builder
        .rule("Factor")
        .terminal('(')
        .nonterminal("Sum")
        .terminal(')')
        .add();

    builder.rule("Product").nonterminal("Factor").add();
    builder
        .rule("Product")
        .nonterminal("Product")
        .terminal('*')
        .nonterminal("Factor")
        .add();
    builder
        .rule("Product")
        .nonterminal("Product")
        .terminal('/')
        .nonterminal("Factor")
        .add();

    builder.rule("Sum").nonterminal("Product").add();
    builder
        .rule("Sum")
        .nonterminal("Sum")
        .terminal('+')
        .nonterminal("Product")
        .add();
    builder
        .rule("Sum")
        .nonterminal("Sum")
        .terminal('-')
        .nonterminal("Product")
        .add();

    builder.build().expect("Could not build valid grammar?")
}

#[test]
fn recognise_valid_input() {
    let grammar = calc_grammar();
    let mut parser =
        parser::Parser::new(&grammar).expect("grammar has bad initial rule?");

    let mut trees = Default::default();

    for c in "1+(2*3-4)".chars() {
        trees = parser.feed(c).expect("broken rule reference?");
    }

    for each in trees.iter() {
        each.dump(0);
    }

    assert_eq!(trees.len(), 1);
}

#[test]
fn reject_incomplete_input() {
    let grammar = calc_grammar();
    let mut parser =
        parser::Parser::new(&grammar).expect("grammar has bad initial rule?");

    let mut trees = Default::default();
    for c in "1+(2*".chars() {
        trees = parser.feed(c).expect("broken rule reference?");
    }

    for each in trees.iter() {
        each.dump(0);
    }

    assert_eq!(trees.len(), 0);
}

#[test]
fn reject_invalid_input() {
    let grammar = calc_grammar();
    let mut parser =
        parser::Parser::new(&grammar).expect("grammar has bad initial rule?");

    parser.feed('1').expect("parser rejected a digit?");
    let actual = parser
        .feed('x')
        .expect_err("parser accepted invalid input?");
    let expected = parser::Error::InvalidInput {
        offset: 1,
        got: 'x',
        expected: "0123456789*+-/".chars().collect(),
    };

    assert_eq!(actual, expected);
}