use crate::filtermodifier::FilterModifier;
use crate::roll::{roll_die, Roll};
use core::fmt;
use core::option::Option::Some;
use core::result::Result::{Err, Ok};
use std::fmt::Display;
use std::num::NonZeroU64;
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
pub const DEFAULT_SIDES: &str = "20";
#[derive(Debug, PartialEq)]
pub enum Value {
Float(f64),
Int(i64),
}
impl From<Value> for f64 {
fn from(v: Value) -> Self {
match v {
Value::Int(i) => i as f64,
Value::Float(f) => f,
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Float(v) => f.write_str(&v.to_string()),
Self::Int(v) => f.write_str(&v.to_string()),
}
}
}
impl Add for Value {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Float(i), Value::Float(j)) => Value::Float(i as f64 + j as f64),
(Value::Int(i), Value::Float(j)) => Value::Float(i as f64 + j as f64),
(Value::Float(i), Value::Int(j)) => Value::Float(i as f64 + j as f64),
(Value::Int(i), Value::Int(j)) => Value::Int(i + j),
}
}
}
impl Sub for Value {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Float(i), Value::Float(j)) => Value::Float(i as f64 - j),
(Value::Int(i), Value::Float(j)) => Value::Float(i as f64 - j as f64),
(Value::Float(i), Value::Int(j)) => Value::Float(i as f64 - j as f64),
(Value::Int(i), Value::Int(j)) => Value::Int(i - j),
}
}
}
impl Mul for Value {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Float(i), Value::Float(j)) => Value::Float(i as f64 * j as f64),
(Value::Int(i), Value::Float(j)) => Value::Float(i as f64 * j as f64),
(Value::Float(i), Value::Int(j)) => Value::Float(i as f64 * j as f64),
(Value::Int(i), Value::Int(j)) => Value::Int(i * j),
}
}
}
impl Div for Value {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Float(i), Value::Float(j)) => Value::Float(i as f64 / j as f64),
(Value::Int(i), Value::Float(j)) => Value::Float(i as f64 / j as f64),
(Value::Float(i), Value::Int(j)) => Value::Float(i as f64 / j as f64),
(Value::Int(i), Value::Int(j)) => Value::Float(i as f64 / j as f64),
}
}
}
impl Rem for Value {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Float(i), Value::Float(j)) => Value::Float(i as f64 % j as f64),
(Value::Int(i), Value::Float(j)) => Value::Float(i as f64 % j as f64),
(Value::Float(i), Value::Int(j)) => Value::Float(i as f64 % j as f64),
(Value::Int(i), Value::Int(j)) => Value::Int(i % j),
}
}
}
impl Neg for Value {
type Output = Self;
fn neg(self) -> Self::Output {
match self {
Value::Float(i) => Value::Float(-i),
Value::Int(i) => Value::Int(-i),
}
}
}
impl Value {
pub fn floor(self) -> Self {
match self {
Value::Float(i) => Value::Int(i.floor() as i64),
i => i,
}
}
pub fn pow(self, rhs: Self) -> Self {
match (self, rhs) {
(Value::Float(i), Value::Float(j)) => Value::Float((i as f64).powf(j as f64)),
(Value::Int(i), Value::Float(j)) => Value::Float((i as f64).powf(j as f64)),
(Value::Float(i), Value::Int(j)) => Value::Float((i as f64).powf(j as f64)),
(Value::Int(i), Value::Int(j)) if j < 0 => Value::Float((i as f64).powf(j as f64)),
(Value::Int(i), Value::Int(j)) => Value::Int((i as i64).pow(j as u32)),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Ast {
Add(Box<Ast>, Box<Ast>),
Sub(Box<Ast>, Box<Ast>),
Mul(Box<Ast>, Box<Ast>),
Div(Box<Ast>, Box<Ast>),
Mod(Box<Ast>, Box<Ast>),
IDiv(Box<Ast>, Box<Ast>),
Power(Box<Ast>, Box<Ast>),
Minus(Box<Ast>),
Dice(
Option<Box<Ast>>,
Option<Box<Ast>>,
FilterModifier<Box<Ast>>,
u64,
),
Const(String),
}
impl Display for Ast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Ast::Add(l, r) => {
l.fmt(f)?;
write!(f, " + ")?;
r.fmt(f)?;
}
Ast::Sub(l, r) => {
l.fmt(f)?;
write!(f, " - ")?;
r.fmt(f)?;
}
Ast::Mul(l, r) => {
l.fmt(f)?;
write!(f, " * ")?;
r.fmt(f)?;
}
Ast::Div(l, r) => {
l.fmt(f)?;
write!(f, " / ")?;
r.fmt(f)?;
}
Ast::Mod(l, r) => {
l.fmt(f)?;
write!(f, " mod ")?;
r.fmt(f)?;
}
Ast::IDiv(l, r) => {
l.fmt(f)?;
write!(f, " // ")?;
r.fmt(f)?;
}
Ast::Power(l, r) => {
l.fmt(f)?;
write!(f, " ** ")?;
r.fmt(f)?;
}
Ast::Minus(t) => {
write!(f, "-")?;
t.fmt(f)?;
}
Ast::Dice(times, sides, fm, _) => {
if let Some(t) = times {
t.fmt(f)?;
}
write!(f, "d")?;
if let Some(s) = sides {
s.fmt(f)?;
}
fm.fmt(f)?;
}
Ast::Const(s) => f.write_str(s)?,
}
Ok(())
}
}
impl Ast {
pub fn interp(self, rolls: &mut Vec<(u64, Roll)>) -> Result<Value, String> {
Ok(match self {
Ast::Add(l, r) => l.interp(rolls)? + r.interp(rolls)?,
Ast::Sub(l, r) => l.interp(rolls)? - r.interp(rolls)?,
Ast::Div(l, r) => l.interp(rolls)? / r.interp(rolls)?,
Ast::Mul(l, r) => l.interp(rolls)? * r.interp(rolls)?,
Ast::Mod(l, r) => l.interp(rolls)? % r.interp(rolls)?,
Ast::IDiv(l, r) => (l.interp(rolls)? / r.interp(rolls)?).floor(),
Ast::Power(l, r) => l.interp(rolls)?.pow(r.interp(rolls)?),
Ast::Minus(l) => -l.interp(rolls)?,
Ast::Const(val) => {
let dots = val.matches('.').count();
if dots == 0 {
Value::Int(val.parse::<i64>().map_err(|e| e.to_string())?)
} else if dots == 1 {
Value::Float(val.parse::<f64>().map_err(|e| e.to_string())?)
} else {
return Err(format!(
"{} couldn't be parsed as number (too many dots)",
val
));
}
}
Ast::Dice(None, r, fm, dp) => {
Ast::Dice(Some(Box::new(Ast::Const("1".to_string()))), r, fm, dp).interp(rolls)?
}
Ast::Dice(l, None, fm, dp) => Ast::Dice(
l,
Some(Box::new(Ast::Const(DEFAULT_SIDES.to_string()))),
fm,
dp,
)
.interp(rolls)?,
Ast::Dice(Some(l), Some(r), fm, dp) => {
if let (Value::Int(lv), Value::Int(rv)) = (l.interp(rolls)?, r.interp(rolls)?) {
let fm_value: FilterModifier<Value> = fm.map(|i| i.interp(rolls)).swap()?;
let fm_int = fm_value
.map(|i| {
if let Value::Int(v) = i {
Ok(v as u64)
} else {
Err(format!("{:?}: couldn't be parsed as int", i))
}
})
.swap()?;
let roll = roll_die(
lv as u64,
NonZeroU64::new(rv as u64).ok_or("Can't roll zero sided die")?,
fm_int,
rand_core::OsRng,
);
let total = roll.total;
rolls.push((dp, roll));
Value::Int(total)
} else {
return Err("couldn't be parsed as dice roll (no ints)".to_string());
}
}
})
}
}