use pest::{
prec_climber::*,
iterators::*,
};
use random::*;
lazy_static! {
static ref PREC_CLIMBER: PrecClimber<Rule> = {
use self::Assoc::*;
use self::Rule::*;
PrecClimber::new(vec![
Operator::new(plus, Left) | Operator::new(minus, Left),
Operator::new(times, Left) | Operator::new(slash, Left),
Operator::new(roll, Right),
])
};
}
#[derive(Parser)]
#[grammar = "rouler.pest"]
pub struct RollParser;
const _GRAMMAR : &str = include_str!("rouler.pest");
pub fn compute(expr: Pairs<Rule>) -> i64 {
PREC_CLIMBER.climb(
expr,
|pair: Pair<Rule>| match pair.as_rule() {
Rule::number => pair.as_str().parse::<i64>().unwrap(),
Rule::expr => compute(pair.into_inner()),
Rule::custom_dice => {
let mut inner = pair.into_inner();
let num = inner.next().unwrap();
let lhs = num.as_str().parse::<i64>().expect("Did not find a number on LHS!");
let d = inner.next().unwrap().as_str();
assert!(d == "d" || d == "D");
let mut sides = vec![];
for s in inner {
if s.as_rule() == Rule::number {
sides.push(s.as_str().parse::<u64>().expect("Non-number found on RHS!"));
}
};
lhs.signum() * roll_custom_dice_raw(lhs.abs(), &sides)
},
_ => unreachable!(),
},
|lhs: i64, op: Pair<Rule>, rhs: i64| match op.as_rule() {
Rule::roll => {
if rhs < 1 {
panic!("Sides must be greater than zero")
} else {
match lhs.signum() {
0 => panic!("Number of sides must not be zero"),
-1 => -roll_dice_raw(lhs.abs(), rhs as u64),
1 => roll_dice_raw(lhs.abs(), rhs as u64),
_ => unreachable!(),
}
}
},
Rule::plus => lhs + rhs,
Rule::minus => lhs - rhs,
Rule::times => lhs * rhs,
Rule::slash => lhs / rhs,
_ => unreachable!(),
}
)
}