use pest::Parser as PestParserTrait;
use pest_derive::Parser;
use super::ParserConfig;
use crate::core::{Dice, RollResult};
use crate::dice::*;
use crate::error::{DiceError, DiceResult};
#[derive(Parser)]
#[grammar = "src/parser/dice.pest"]
struct DiceParser;
pub struct PestParser {
config: ParserConfig,
}
impl PestParser {
pub fn new(config: ParserConfig) -> Self {
Self { config }
}
pub fn eval(&self, expr: &str) -> DiceResult<RollResult> {
let dice = self.parse(expr)?;
Ok(dice.roll())
}
pub fn parse(&self, expr: &str) -> DiceResult<Box<dyn Dice>> {
let pairs = DiceParser::parse(Rule::expression, expr)
.map_err(|e| DiceError::ParseError(e.to_string()))?;
self.parse_expression(pairs)
}
fn parse_expression(&self, pairs: pest::iterators::Pairs<Rule>) -> DiceResult<Box<dyn Dice>> {
for pair in pairs {
match pair.as_rule() {
Rule::expression => return self.parse_expression(pair.into_inner()),
Rule::term => return self.parse_term(pair.into_inner()),
_ => {}
}
}
Err(DiceError::ParseError("Empty expression".into()))
}
fn parse_term(&self, pairs: pest::iterators::Pairs<Rule>) -> DiceResult<Box<dyn Dice>> {
let mut current: Option<Box<dyn Dice>> = None;
for pair in pairs {
match pair.as_rule() {
Rule::factor => {
let factor = self.parse_factor(pair.into_inner())?;
current = Some(factor);
}
Rule::op => {
let op = pair.as_str();
println!("DEBUG: Found operator: {}", op);
}
_ => {}
}
}
current.ok_or_else(|| DiceError::ParseError("No valid term found".into()))
}
fn parse_factor(&self, pairs: pest::iterators::Pairs<Rule>) -> DiceResult<Box<dyn Dice>> {
for pair in pairs {
match pair.as_rule() {
Rule::dice_expr => return self.parse_dice_expr(pair.into_inner()),
Rule::number => {
let num = pair
.as_str()
.parse::<i64>()
.map_err(|e| DiceError::ParseError(e.to_string()))?;
return Ok(Box::new(ConstantDice::new(num)));
}
Rule::paren_expr => return self.parse_expression(pair.into_inner()),
_ => {}
}
}
Err(DiceError::ParseError("No valid factor found".into()))
}
fn parse_dice_expr(&self, pairs: pest::iterators::Pairs<Rule>) -> DiceResult<Box<dyn Dice>> {
let mut dice_type = None;
let mut count = 1u32;
let mut sides = self.config.default_sides;
let mut params = DiceParams::new();
for pair in pairs {
match pair.as_rule() {
Rule::dice_type => {
dice_type = Some(pair.as_str());
}
Rule::dice_count => {
count = pair
.as_str()
.parse::<u32>()
.map_err(|e| DiceError::ParseError(e.to_string()))?;
}
Rule::dice_sides => {
sides = pair
.as_str()
.parse::<u32>()
.map_err(|e| DiceError::ParseError(e.to_string()))?;
}
Rule::dice_param => {
self.parse_dice_param(pair.into_inner(), &mut params)?;
}
_ => {}
}
}
let dice_type =
dice_type.ok_or_else(|| DiceError::ParseError("No dice type specified".into()))?;
match dice_type {
"d" => self.build_standard_dice(count, sides, params),
"a" => self.build_infinite_pool(count, sides, params),
"c" => self.build_double_cross_pool(count, sides, params),
"f" | "df" => self.build_fate_dice(count),
_ => Err(DiceError::ParseError(format!(
"Unknown dice type: {}",
dice_type
))),
}
}
fn parse_dice_param(
&self,
pairs: pest::iterators::Pairs<Rule>,
params: &mut DiceParams,
) -> DiceResult<()> {
for pair in pairs {
match pair.as_rule() {
Rule::keep_highest => {
let n = pair
.into_inner()
.next()
.and_then(|p| p.as_str().parse::<u32>().ok())
.ok_or_else(|| {
DiceError::ParseError("Invalid keep highest parameter".into())
})?;
params.keep_highest = Some(n);
}
Rule::keep_lowest => {
let n = pair
.into_inner()
.next()
.and_then(|p| p.as_str().parse::<u32>().ok())
.ok_or_else(|| {
DiceError::ParseError("Invalid keep lowest parameter".into())
})?;
params.keep_lowest = Some(n);
}
Rule::explode => {
let threshold = pair
.into_inner()
.next()
.and_then(|p| p.as_str().parse::<u32>().ok())
.unwrap_or(params.sides);
params.explode_threshold = Some(threshold);
}
Rule::success_threshold => {
let threshold = pair
.into_inner()
.next()
.and_then(|p| p.as_str().parse::<u32>().ok())
.ok_or_else(|| DiceError::ParseError("Invalid success threshold".into()))?;
params.success_threshold = Some(threshold);
}
_ => {}
}
}
Ok(())
}
fn build_standard_dice(
&self,
count: u32,
sides: u32,
params: DiceParams,
) -> DiceResult<Box<dyn Dice>> {
let mut dice = StandardDice::new(count, sides);
if let Some(n) = params.keep_highest {
dice = dice.keep_highest(n);
}
if let Some(n) = params.keep_lowest {
dice = dice.keep_lowest(n);
}
if let Some(threshold) = params.success_threshold {
dice = dice.with_success_threshold(threshold);
}
if let Some(threshold) = params.explode_threshold {
let exploding = ExplodingDice::new(count, sides, threshold);
return Ok(Box::new(exploding));
}
Ok(Box::new(dice))
}
fn build_infinite_pool(
&self,
count: u32,
sides: u32,
params: DiceParams,
) -> DiceResult<Box<dyn Dice>> {
let add_threshold = params.success_threshold.ok_or_else(|| {
DiceError::ParseError("Infinite pool requires success threshold".into())
})?;
let mut pool = InfiniteAddingPool::new(count, add_threshold).with_sides(sides);
if let Some(threshold) = params.success_threshold {
pool = pool.with_success_threshold(threshold);
}
Ok(Box::new(pool))
}
fn build_double_cross_pool(
&self,
count: u32,
sides: u32,
params: DiceParams,
) -> DiceResult<Box<dyn Dice>> {
let add_threshold = params.success_threshold.ok_or_else(|| {
DiceError::ParseError("Double cross pool requires success threshold".into())
})?;
let pool = DoubleCrossPool::new(count, add_threshold).with_sides(sides);
Ok(Box::new(pool))
}
fn build_fate_dice(&self, count: u32) -> DiceResult<Box<dyn Dice>> {
Ok(Box::new(FateDice::new(count)))
}
}
#[derive(Debug, Default)]
struct DiceParams {
keep_highest: Option<u32>,
keep_lowest: Option<u32>,
explode_threshold: Option<u32>,
success_threshold: Option<u32>,
sides: u32,
}
impl DiceParams {
fn new() -> Self {
Self::default()
}
}
struct ConstantDice {
value: i64,
}
impl ConstantDice {
fn new(value: i64) -> Self {
Self { value }
}
}
impl Dice for ConstantDice {
fn roll(&self) -> RollResult {
RollResult::new(Vec::new(), self.value, self.value.to_string())
}
fn describe(&self) -> String {
self.value.to_string()
}
fn expected_value(&self) -> f64 {
self.value as f64
}
fn min_value(&self) -> i64 {
self.value
}
fn max_value(&self) -> i64 {
self.value
}
}