use nom;
use nom::digit;
use nom::types::CompleteStr;
use std::str::FromStr;
use crate::expression::Expression;
use Expression::{Constant, Die, Sum};
named!(
parse_eval_signs<CompleteStr, char>,
map!(
take_while!(call!(|c| c == '+' || c == '-')),
|input: CompleteStr| {
let neg_count = input.chars().filter(|&x| x == '-').count();
match neg_count % 2 {
1 => '-',
_ => '+',
}
}
)
);
named!(
pub parse_unsigned_number<CompleteStr, u32>,
map_res!(
recognize!(digit),
|CompleteStr(string)| u32::from_str(string)
)
);
named!(
pub parse_signed_number<CompleteStr, i32>,
map!(
pair!(
opt!(parse_eval_signs),
parse_unsigned_number
),
|(sign, value): (Option<char>, u32)| {
match sign {
Some('-') => -1 * value as i32,
_ => 1 * value as i32
}
}
)
);
named!(
pub parse_constant<CompleteStr, Expression>,
ws!(
map!(parse_signed_number, |num: i32| Constant(num))
)
);
named!(
parse_die_single<CompleteStr, Expression>,
do_parse!(
ws!(tag!("d")) >>
num: parse_unsigned_number >>
(Die(num))
)
);
named!(
parse_die_coefficient<CompleteStr, Expression>,
map!(
do_parse!(
coefficient: ws!(parse_unsigned_number) >>
tag!("d") >>
num: parse_unsigned_number >>
(coefficient, num)
),
|(coefficient, num)| {
if coefficient == 0 {
return Constant(0)
}
(0..coefficient-1).fold(Die(num), |acc, _| {
Sum(Box::new(acc), Box::new(Die(num)))
})
}
)
);
named!(
pub parse_die<CompleteStr, Expression>,
alt_complete!(parse_die_single | parse_die_coefficient)
);
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::test_helpers::test_parser;
#[test]
fn test_parse_number_success() {
let cases = vec![
("-999999999", -999999999),
("-1234", -1234),
("---10", -10),
("-10", -10),
("-1", -1),
("0", 0),
("1", 1),
("+++--1", 1),
("10", 10),
("1234", 1234),
("--1234", 1234),
("999999999", 999999999),
];
test_parser(parse_signed_number, cases);
}
#[test]
fn test_parse_constant() {
let cases = vec![
("4", Constant(4)),
(" 5 ", Constant(5)),
("\n\t --10 ", Constant(10)),
("\n\t ---10 ", Constant(-10)),
];
test_parser(parse_constant, cases);
}
#[test]
fn test_parse_die() {
let cases = vec![
("d4", Die(4)),
(" d6", Die(6)),
(" d10", Die(10)),
("\nd12", Die(12)),
("\td20 ", Die(20)),
(" 2d6 ", Sum(Box::new(Die(6)), Box::new(Die(6)))),
(
"4d6",
Sum(
Box::new(Sum(
Box::new(Sum(Box::new(Die(6)), Box::new(Die(6)))),
Box::new(Die(6)),
)),
Box::new(Die(6)),
),
),
(
"6d6",
Sum(
Box::new(Sum(
Box::new(Sum(
Box::new(Sum(
Box::new(Sum(Box::new(Die(6)), Box::new(Die(6)))),
Box::new(Die(6)),
)),
Box::new(Die(6)),
)),
Box::new(Die(6)),
)),
Box::new(Die(6)),
),
),
];
test_parser(parse_die, cases);
}
}