use std::collections::HashMap;
use mpl::output::Output;
use mpl::parser::Parser;
use mpl::rules::{RightRule, RightRuleKind, Rules};
use mpl::span::{Len, Start, StartAndLenSpan};
use mpl::symbols::{StrTerminal, Variable};
use mpl::trees::AST;
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
enum LSumVariable {
LSum,
N,
N1,
N2,
}
impl Variable for LSumVariable {}
type LSumSpan = StartAndLenSpan<u32, u16>;
type LSumAst = AST<LSumVariable, LSumSpan, ()>;
struct LSumParser;
impl<'i, V, P, L, R, O> Parser<'i, str, StrTerminal<'i>, V, StartAndLenSpan<P, L>, P, R, O>
for LSumParser
where
V: Variable,
P: Start<str, L>,
L: Len<str, P>,
R: Rules<StrTerminal<'i>, V>,
O: Output<'i, str, V, StartAndLenSpan<P, L>>,
{
}
fn make_rules() -> HashMap<LSumVariable, RightRule<StrTerminal<'static>, LSumVariable>> {
use LSumVariable::*;
use RightRuleKind as K;
let mut rules = HashMap::new();
rules.insert(
LSum,
RightRule::from_right_rule_kind((K::V(LSum), K::V(N)), K::V(N)),
);
rules.insert(
N,
RightRule::from_right_rule_kind((K::T(StrTerminal::Char('1')), K::Empty), K::V(N1)),
);
rules.insert(
N1,
RightRule::from_right_rule_kind((K::T(StrTerminal::Char('2')), K::Empty), K::V(N2)),
);
rules.insert(
N2,
RightRule::from_right_rule_kind((K::T(StrTerminal::Char('3')), K::Empty), K::Failure),
);
rules
}
fn parse(input: &str) -> Result<LSumAst, LSumAst> {
let parser = LSumParser;
let rules = make_rules();
let span = StartAndLenSpan::<u32, u16>::from_start_len(0, input.len() as u16);
parser.squirrel_parse(input, &rules, &LSumVariable::LSum, &span)
}
#[test]
fn single_digit() {
let r = parse("1");
assert!(r.is_ok(), "expected parse success");
}
#[test]
fn two_digits_grows_seed_once() {
let r = parse("12");
assert!(r.is_ok(), "expected parse success");
let ast = r.unwrap();
assert_eq!(ast.span.start, 0);
assert_eq!(ast.span.len, 2);
}
#[test]
fn three_digits_left_associates() {
let r = parse("123");
assert!(r.is_ok(), "expected parse success");
let ast = r.unwrap();
assert_eq!(ast.span.start, 0);
assert_eq!(ast.span.len, 3);
let first_choice = ast.as_first().expect("first choice");
let lhs_ast = &first_choice.lhs;
let lhs_internal = lhs_ast.as_internal().expect("internal node on LHS");
assert_eq!(lhs_internal.value.0, LSumVariable::LSum,
"LHS of outermost LSum should be another LSum (left-associative)");
}
#[test]
fn rejects_invalid_input() {
let r = parse("9");
assert!(r.is_err(), "9 should not parse");
let r = parse("129");
assert!(r.is_err(), "129 should not match the full span");
}
#[test]
fn empty_input_fails() {
let r = parse("");
assert!(r.is_err(), "empty input should fail");
}
#[test]
fn stress_chain_of_ten() {
let r = parse("1212123212");
assert!(r.is_ok(), "10-char input should parse");
let ast = r.unwrap();
assert_eq!(ast.span.start, 0);
assert_eq!(ast.span.len, 10);
}