diff --git a/benches/dice.rs b/benches/dice.rs index b03847b..2618029 100644 --- a/benches/dice.rs +++ b/benches/dice.rs @@ -6,7 +6,7 @@ use test::Bencher; use dicey::{ dice::{Dice, DieRoll, Rolled}, - term::Describe, + expr::Describe, }; #[bench] diff --git a/src/dice.rs b/src/dice.rs index b3100f0..15aa24b 100644 --- a/src/dice.rs +++ b/src/dice.rs @@ -2,15 +2,15 @@ //! All functionality for directly creating dice, rolling them, and working with their resulting rolls. //! -//! This is the home of the dice "primitives". For using as part of a larger expression, see [`Term::dice`]. +//! This is the home of the dice "primitives". For using as part of a larger expression, see [`Expr::dice`]. //! -//! [`Term::dice`]: ../term/enum.Term.html#variant.Dice +//! [`Expr::dice`]: ../expr/enum.Expr.html#variant.Dice use std::{cmp, fmt}; use fastrand::Rng; -use crate::term::Describe; +use crate::expr::Describe; /// A set of one or more rollable dice with a specific number of sides, along with a collection of modifiers to apply to /// any resulting rolls from them. @@ -550,7 +550,7 @@ impl Rolled<'_> { } impl Describe for Rolled<'_> { - /// Builds a string of the dice expression the roll is from and a list of all of the individual rolled dice + /// Builds a string of the dice the roll is from and a list of all of the individual rolled dice /// (see [`DieRoll::fmt()`]). /// /// If `list_limit` is specified and there are more rolls than it, the output will be truncated and appended with @@ -559,7 +559,7 @@ impl Describe for Rolled<'_> { /// # Examples /// /// ``` - /// use dicey::{dice::{Dice, DieRoll, Rolled}, term::Describe}; + /// use dicey::{dice::{Dice, DieRoll, Rolled}, expr::Describe}; /// /// let dice = Dice::builder().count(4).sides(6).keep_high(2).build(); /// let kh_mod = dice.modifiers.first().expect("no first modifier"); diff --git a/src/term.rs b/src/expr.rs similarity index 54% rename from src/term.rs rename to src/expr.rs index 740daef..8391174 100644 --- a/src/term.rs +++ b/src/expr.rs @@ -2,17 +2,17 @@ use crate::dice::{Dice, Error as DiceError, Rolled}; -/// Generates an implementation of [TermType] and [DescribeBinaryTerm] for an enum type. +/// Generates an implementation of [`ExprType`] and [`DescribeBinaryExpr`] for an enum type. /// This is very tightly coupled with the expected variants (Val, Dice, Neg, Add, Sub, Mul, DivDown, DivUp). -macro_rules! term_type_impl { +macro_rules! expr_type_impl { ($name:ty) => { - impl HasTermType for $name { - fn term_type(&self) -> TermType { + impl HasExprType for $name { + fn expr_type(&self) -> ExprType { match self { - Self::Num(..) | Self::Dice(..) => TermType::Value, - Self::Neg(..) => TermType::Unary, - Self::Add(..) | Self::Sub(..) => TermType::Additive, - Self::Mul(..) | Self::DivDown(..) | Self::DivUp(..) => TermType::Multiplicative, + Self::Num(..) | Self::Dice(..) => ExprType::Value, + Self::Neg(..) => ExprType::Unary, + Self::Add(..) | Self::Sub(..) => ExprType::Additive, + Self::Mul(..) | Self::DivDown(..) | Self::DivUp(..) => ExprType::Multiplicative, } } @@ -37,62 +37,62 @@ macro_rules! term_type_impl { } } - impl DescribeBinaryTerm for $name {} + impl DescribeBinaryExpr for $name {} }; } -/// Individual terms usable in expressions +/// Individual elements of a full mathematical dice expression #[derive(Debug, Clone, PartialEq, Eq)] -pub enum Term { +pub enum Expr { /// Standalone integer Num(i32), /// Dice literal Dice(Dice), - /// Negation of a term (makes it negative) + /// Negation of an expression (makes the result of it negative) Neg(Box), - /// Sum of two terms + /// Sum of two expressions Add(Box, Box), - /// Difference of two terms + /// Difference of two expressions Sub(Box, Box), - /// Product of two terms + /// Product of two expressions Mul(Box, Box), - /// Integer quotient of two terms (rounded down) + /// Integer quotient of two expressions (rounded down) DivDown(Box, Box), - /// Integer quotient of two terms (rounded up) + /// Integer quotient of two expressions (rounded up) DivUp(Box, Box), } -term_type_impl!(Term); +expr_type_impl!(Expr); -impl Term { - /// Evaluates the term. For most types of terms, this will directly result in a 1:1 equivalent [`EvaledTerm`], - /// with the notable exception of [`Term::Dice`]. For dice terms, the dice they contain are rolled, resulting - /// in an [`EvaledTerm::Dice`] with the [`Rolled`] set of dice. - pub fn eval(&self) -> Result { +impl Expr { + /// Evaluates the expression. For most types of expressions, this will directly result in a 1:1 equivalent + /// [`EvaledExpr`], with the notable exception of [`Expr::Dice`]. For dice expressions, the dice they contain are + /// rolled, resulting in an [`EvaledExpr::Dice`] with the [`Rolled`] set of dice. + pub fn eval(&self) -> Result { Ok(match self { - Self::Num(x) => EvaledTerm::Num(*x), - Self::Dice(dice) => EvaledTerm::Dice(dice.roll()?), + Self::Num(x) => EvaledExpr::Num(*x), + Self::Dice(dice) => EvaledExpr::Dice(dice.roll()?), - Self::Neg(x) => EvaledTerm::Neg(Box::new(x.eval()?)), + Self::Neg(x) => EvaledExpr::Neg(Box::new(x.eval()?)), - Self::Add(a, b) => EvaledTerm::Add(Box::new(a.eval()?), Box::new(b.eval()?)), - Self::Sub(a, b) => EvaledTerm::Sub(Box::new(a.eval()?), Box::new(b.eval()?)), - Self::Mul(a, b) => EvaledTerm::Mul(Box::new(a.eval()?), Box::new(b.eval()?)), - Self::DivDown(a, b) => EvaledTerm::DivDown(Box::new(a.eval()?), Box::new(b.eval()?)), - Self::DivUp(a, b) => EvaledTerm::DivUp(Box::new(a.eval()?), Box::new(b.eval()?)), + Self::Add(a, b) => EvaledExpr::Add(Box::new(a.eval()?), Box::new(b.eval()?)), + Self::Sub(a, b) => EvaledExpr::Sub(Box::new(a.eval()?), Box::new(b.eval()?)), + Self::Mul(a, b) => EvaledExpr::Mul(Box::new(a.eval()?), Box::new(b.eval()?)), + Self::DivDown(a, b) => EvaledExpr::DivDown(Box::new(a.eval()?), Box::new(b.eval()?)), + Self::DivUp(a, b) => EvaledExpr::DivUp(Box::new(a.eval()?), Box::new(b.eval()?)), }) } - /// Checks whether the term is deterministic (will always yield the same value with every evaluation). - /// A [`Term::Num`] will always return `true`, a [`Term::Dice`] will always return `false` unless the dice they - /// contain only have one side, and all unary and binary terms forward the check to their children. + /// Checks whether the expression is deterministic (will always yield the same value with every evaluation). + /// A [`Expr::Num`] will always return `true`, a [`Expr::Dice`] will always return `false` unless the dice they + /// contain only have one side, and all unary and binary expressions forward the check to their children. #[must_use] pub fn is_deterministic(&self) -> bool { match self { @@ -106,10 +106,10 @@ impl Term { } } -impl Describe for Term { - /// Builds a full usable expression from the terms. Operations are grouped with parentheses whenever the order of - /// operations could be considered ambiguous, such as when mixing addition and multiplication together. All strings - /// output from this should result in the exact same Term layout when re-parsing them. +impl Describe for Expr { + /// Builds a full usable expression string from the expressions. Operations are grouped with parentheses whenever + /// the order of operations could be considered ambiguous, such as when mixing addition and multiplication together. + /// All strings output from this should result in the exact same expression layout when re-parsing them. /// /// `list_limit` does not affect the output of this implementation in any way since there are no possible lists of /// elements included, so it is always safe to pass `None`. @@ -124,58 +124,58 @@ impl Describe for Term { _ => format!("-({})", x.describe(None)), }, - Self::Add(a, b) => self.describe_binary_term('+', a.as_ref(), b.as_ref(), None), - Self::Sub(a, b) => self.describe_binary_term('-', a.as_ref(), b.as_ref(), None), - Self::Mul(a, b) => self.describe_binary_term('*', a.as_ref(), b.as_ref(), None), - Self::DivDown(a, b) => self.describe_binary_term('/', a.as_ref(), b.as_ref(), None), - Self::DivUp(a, b) => self.describe_binary_term('\\', a.as_ref(), b.as_ref(), None), + Self::Add(a, b) => self.describe_binary_expr('+', a.as_ref(), b.as_ref(), None), + Self::Sub(a, b) => self.describe_binary_expr('-', a.as_ref(), b.as_ref(), None), + Self::Mul(a, b) => self.describe_binary_expr('*', a.as_ref(), b.as_ref(), None), + Self::DivDown(a, b) => self.describe_binary_expr('/', a.as_ref(), b.as_ref(), None), + Self::DivUp(a, b) => self.describe_binary_expr('\\', a.as_ref(), b.as_ref(), None), } } } -impl std::fmt::Display for Term { +impl std::fmt::Display for Expr { /// Formats the value using the given formatter. [Read more][core::fmt::Debug::fmt()] /// - /// The output of this implementation is equivalent to [`Term::describe(None)`]. + /// The output of this implementation is equivalent to [`Expr::describe(None)`]. /// - /// [`Term::describe(None)`]: ./enum.Term.html#method.describe + /// [`Expr::describe(None)`]: ./enum.Expr.html#method.describe fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.describe(None)) } } -/// Individual evaluated terms from expressions +/// Individual elements of an evaluated mathematical dice expression #[derive(Debug, Clone, PartialEq, Eq)] -pub enum EvaledTerm<'r> { +pub enum EvaledExpr<'r> { /// Standalone integer Num(i32), /// Rolled dice Dice(Rolled<'r>), - /// Negation of a term (makes it negative) + /// Negation of an expression (makes the result of it negative) Neg(Box), - /// Sum of two terms + /// Sum of two expressions Add(Box, Box), - /// Difference of two terms + /// Difference of two expressions Sub(Box, Box), - /// Product of two terms + /// Product of two expressions Mul(Box, Box), - /// Integer quotient of two terms (rounded down) + /// Integer quotient of two expressions (rounded down) DivDown(Box, Box), - /// Integer quotient of two terms (rounded up) + /// Integer quotient of two expressions (rounded up) DivUp(Box, Box), } -term_type_impl!(EvaledTerm<'_>); +expr_type_impl!(EvaledExpr<'_>); -impl EvaledTerm<'_> { - /// Calculates the total (sum) of the evaluated term and all of its children (if any). +impl EvaledExpr<'_> { + /// Calculates the total (sum) of the evaluated expression and all of its children (if any). pub fn calc(&self) -> Result { match self { Self::Num(x) => Ok(*x), @@ -202,7 +202,7 @@ impl EvaledTerm<'_> { } } -impl Describe for EvaledTerm<'_> { +impl Describe for EvaledExpr<'_> { fn describe(&self, list_limit: Option) -> String { match self { Self::Num(x) => x.to_string(), @@ -213,22 +213,22 @@ impl Describe for EvaledTerm<'_> { _ => format!("-({})", x.describe(list_limit)), }, - Self::Add(a, b) => self.describe_binary_term('+', a.as_ref(), b.as_ref(), list_limit), - Self::Sub(a, b) => self.describe_binary_term('-', a.as_ref(), b.as_ref(), list_limit), - Self::Mul(a, b) => self.describe_binary_term('*', a.as_ref(), b.as_ref(), list_limit), - Self::DivDown(a, b) => self.describe_binary_term('/', a.as_ref(), b.as_ref(), list_limit), - Self::DivUp(a, b) => self.describe_binary_term('\\', a.as_ref(), b.as_ref(), list_limit), + Self::Add(a, b) => self.describe_binary_expr('+', a.as_ref(), b.as_ref(), list_limit), + Self::Sub(a, b) => self.describe_binary_expr('-', a.as_ref(), b.as_ref(), list_limit), + Self::Mul(a, b) => self.describe_binary_expr('*', a.as_ref(), b.as_ref(), list_limit), + Self::DivDown(a, b) => self.describe_binary_expr('/', a.as_ref(), b.as_ref(), list_limit), + Self::DivUp(a, b) => self.describe_binary_expr('\\', a.as_ref(), b.as_ref(), list_limit), } } } -impl std::fmt::Display for EvaledTerm<'_> { +impl std::fmt::Display for EvaledExpr<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.describe(None)) } } -/// Error resulting from evaluating a [`Term`] or calculating an [`EvaledTerm`] +/// Error resulting from evaluating a [`Expr`] or calculating an [`EvaledExpr`] #[derive(thiserror::Error, Debug)] pub enum Error { /// Dice-related error (likely during rolling) @@ -244,9 +244,9 @@ pub enum Error { Division, } -/// Operation type for an individual term +/// Operation type for an individual expression #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TermType { +pub enum ExprType { /// Single value, no operation Value, @@ -260,24 +260,24 @@ pub enum TermType { Multiplicative, } -/// Trait that offers [`TermType`]-related information -pub trait HasTermType { - /// Gets the type of this term. - fn term_type(&self) -> TermType; +/// Trait that offers [`ExprType`]-related information +pub trait HasExprType { + /// Gets the type of this expression. + fn expr_type(&self) -> ExprType; - /// Checks whether this term is a single value. + /// Checks whether this expression is a single value. fn is_value(&self) -> bool; - /// Checks whether this term is a unary operation. + /// Checks whether this expression is a unary operation. fn is_unary(&self) -> bool; - /// Checks whether this term is an additive operation. + /// Checks whether this expression is an additive operation. fn is_additive(&self) -> bool; - /// Checks whether this term is a multiplicative operation. + /// Checks whether this expression is a multiplicative operation. fn is_multiplicative(&self) -> bool; - /// Checks whether this term is a binary (additive or multiplicative) operation. + /// Checks whether this expression is a binary (additive or multiplicative) operation. fn is_binary(&self) -> bool { self.is_additive() || self.is_multiplicative() } @@ -293,34 +293,34 @@ pub trait Describe { } /// Trait for describing binary expressions with influence from own type. -/// Used for, e.g. wrapping parentheses around parts of expressions based on [TermType] of self and the expression. -trait DescribeBinaryTerm: HasTermType + Describe { +/// Used for, e.g. wrapping parentheses around parts of expressions based on [`ExprType`] of self and the expression. +trait DescribeBinaryExpr: HasExprType + Describe { /// Builds a detailed description for a binary expression with parentheses added to disambiguate mixed /// additive/multiplicative operations. - fn describe_binary_term( + fn describe_binary_expr( &self, op: char, - a: &impl DescribeBinaryTerm, - b: &impl DescribeBinaryTerm, + a: &impl DescribeBinaryExpr, + b: &impl DescribeBinaryExpr, list_limit: Option, ) -> String { format!( "{} {} {}", - match (self.term_type(), a.term_type()) { - (TermType::Additive, TermType::Multiplicative) - | (TermType::Multiplicative, TermType::Additive) - | (TermType::Unary, TermType::Additive) - | (TermType::Unary, TermType::Multiplicative) - | (TermType::Unary, TermType::Unary) => paren_wrap(a.describe(list_limit)), + match (self.expr_type(), a.expr_type()) { + (ExprType::Additive, ExprType::Multiplicative) + | (ExprType::Multiplicative, ExprType::Additive) + | (ExprType::Unary, ExprType::Additive) + | (ExprType::Unary, ExprType::Multiplicative) + | (ExprType::Unary, ExprType::Unary) => paren_wrap(a.describe(list_limit)), _ => a.describe(list_limit), }, op, - match (self.term_type(), b.term_type()) { - (TermType::Additive, TermType::Multiplicative) - | (TermType::Multiplicative, TermType::Additive) - | (TermType::Unary, TermType::Additive) - | (TermType::Unary, TermType::Multiplicative) - | (TermType::Unary, TermType::Unary) => paren_wrap(b.describe(list_limit)), + match (self.expr_type(), b.expr_type()) { + (ExprType::Additive, ExprType::Multiplicative) + | (ExprType::Multiplicative, ExprType::Additive) + | (ExprType::Unary, ExprType::Additive) + | (ExprType::Unary, ExprType::Multiplicative) + | (ExprType::Unary, ExprType::Unary) => paren_wrap(b.describe(list_limit)), _ => b.describe(list_limit), } ) diff --git a/src/lib.rs b/src/lib.rs index 08c7c12..bb991b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,12 +6,12 @@ //! [FoundryVTT's modifiers]: https://foundryvtt.com/article/dice-modifiers/ pub mod dice; -pub mod term; +pub mod expr; #[cfg(feature = "parse")] pub mod parse; #[cfg(feature = "parse")] -pub use parse::term as parser; +pub use parse::expr as parser; #[cfg(test)] mod tests; diff --git a/src/main.rs b/src/main.rs index 534b37f..3f22a02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,7 @@ fn main() { evaled .calc() .map(|total| total.to_string()) - .or_else(|err| Ok::<_, dicey::term::Error>(err.to_string())) + .or_else(|err| Ok::<_, dicey::expr::Error>(err.to_string())) .unwrap() ); } diff --git a/src/parse.rs b/src/parse.rs index 01fb998..7c1f813 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,7 +1,7 @@ #![cfg(feature = "parse")] #![allow(clippy::tabs_in_doc_comments)] -//! Parser generator functions and implementations of [`std::str::FromStr`] for all dice and term data structures. +//! Parser generator functions and implementations of [`std::str::FromStr`] for all dice and expression data structures. //! Requires the `parse` feature (enabled by default). //! //! The parser generators generate parsers for parsing dice, dice modifiers, modifier conditions, and full mathematical @@ -18,21 +18,21 @@ //! assert_eq!(dice, Dice::builder().count(6).sides(8).explode(None, true).build()); //! ``` //! -//! ## Parsing Terms (mathematical dice expressions) +//! ## Parsing expressions //! ``` -//! use dicey::{dice::Dice, term::Term}; +//! use dicey::{dice::Dice, expr::Expr}; //! -//! let term: Term = "6d8x + 4d6 - 3".parse().expect("unable to parse term"); +//! let expr: Expr = "6d8x + 4d6 - 3".parse().expect("unable to parse expression"); //! assert_eq!( -//! term, -//! Term::Sub( -//! Box::new(Term::Add( -//! Box::new(Term::Dice( +//! expr, +//! Expr::Sub( +//! Box::new(Expr::Add( +//! Box::new(Expr::Dice( //! Dice::builder().count(6).sides(8).explode(None, true).build() //! )), -//! Box::new(Term::Dice(Dice::new(4, 6))), +//! Box::new(Expr::Dice(Dice::new(4, 6))), //! )), -//! Box::new(Term::Num(3)), +//! Box::new(Expr::Num(3)), //! ) //! ); //! ``` @@ -41,12 +41,12 @@ use chumsky::prelude::*; use crate::{ dice::{Condition, Dice, Modifier}, - term::Term, + expr::Expr, }; /// Generates a parser that specifically handles dice with or without modifiers like "d20", "2d20kh", "8d6x", etc. pub fn dice_part<'src>() -> impl Parser<'src, &'src str, Dice, extra::Err>> + Copy { - // Parser for dice expressions + // Parser for dice literals text::int(10) .or_not() .then_ignore(just('d')) @@ -166,34 +166,34 @@ pub fn condition<'src>() -> impl Parser<'src, &'src str, Condition, extra::Err() -> impl Parser<'src, &'src str, Term, extra::Err>> + Clone { +/// dice, etc. +pub fn expr_part<'src>() -> impl Parser<'src, &'src str, Expr, extra::Err>> + Clone { // Helper function for operators let op = |c| just(c).padded(); - recursive(|term| { + recursive(|expr| { // Parser for numbers let int = text::int(10).try_map(|s: &str, span| { s.parse() - .map(Term::Num) + .map(Expr::Num) .map_err(|e| Rich::custom(span, format!("{}", e))) }); - // Parser for dice expressions - let dice = dice_part().map(Term::Dice); + // Parser for dice literals + let dice = dice_part().map(Expr::Dice); // Parser for expressions enclosed in parentheses - let atom = dice.or(int).or(term.delimited_by(just('('), just(')'))).padded(); + let atom = dice.or(int).or(expr.delimited_by(just('('), just(')'))).padded(); // Parser for negative sign - let unary = op('-').repeated().foldr(atom, |_op, rhs| Term::Neg(Box::new(rhs))); + let unary = op('-').repeated().foldr(atom, |_op, rhs| Expr::Neg(Box::new(rhs))); // Parser for multiplication and division (round up or down) let product = unary.clone().foldl( choice(( - op('*').to(Term::Mul as fn(_, _) -> _), - op('/').to(Term::DivDown as fn(_, _) -> _), - op('\\').to(Term::DivUp as fn(_, _) -> _), + op('*').to(Expr::Mul as fn(_, _) -> _), + op('/').to(Expr::DivDown as fn(_, _) -> _), + op('\\').to(Expr::DivUp as fn(_, _) -> _), )) .then(unary) .repeated(), @@ -203,8 +203,8 @@ pub fn term_part<'src>() -> impl Parser<'src, &'src str, Term, extra::Err _), - op('-').to(Term::Sub as fn(_, _) -> _), + op('+').to(Expr::Add as fn(_, _) -> _), + op('-').to(Expr::Sub as fn(_, _) -> _), )) .then(product) .repeated(), @@ -214,12 +214,12 @@ pub fn term_part<'src>() -> impl Parser<'src, &'src str, Term, extra::Err() -> impl Parser<'src, &'src str, Term, extra::Err>> + Clone { - term_part().then_ignore(end()) +/// dice, etc. and expects end of input +pub fn expr<'src>() -> impl Parser<'src, &'src str, Expr, extra::Err>> + Clone { + expr_part().then_ignore(end()) } -/// Error that can occur while parsing a string into a dice or term-related structure via [`std::str::FromStr`]. +/// Error that can occur while parsing a string into a dice or expression-related structure via [`std::str::FromStr`]. #[derive(Debug, Clone)] pub struct Error { /// Details of the originating one or more [`chumsky::error::Rich`]s that occurred during parsing @@ -272,12 +272,12 @@ impl std::str::FromStr for Condition { } } -impl std::str::FromStr for Term { +impl std::str::FromStr for Expr { type Err = Error; fn from_str(s: &str) -> Result { let lc = s.to_lowercase(); - let result = term().parse(&lc).into_result().map_err(|errs| Error { + let result = expr().parse(&lc).into_result().map_err(|errs| Error { details: errs.iter().map(|err| err.to_string()).collect::>().join("; "), }); result diff --git a/src/tests/expr.rs b/src/tests/expr.rs new file mode 100644 index 0000000..0021f7f --- /dev/null +++ b/src/tests/expr.rs @@ -0,0 +1,88 @@ +use crate::{ + dice::Dice, + expr::{Error, EvaledExpr, Expr}, +}; + +#[test] +fn basic_negation() { + let expr = Expr::Neg(Box::new(Expr::Num(42))); + let result = expr.eval().unwrap().calc(); + assert_eq!(result.unwrap(), -42); +} + +#[test] +fn basic_addition() { + let expr = Expr::Add(Box::new(Expr::Num(42)), Box::new(Expr::Num(69))); + let result = expr.eval().unwrap().calc(); + assert_eq!(result.unwrap(), 111); +} + +#[test] +fn basic_subtraction() { + let expr = Expr::Sub(Box::new(Expr::Num(42)), Box::new(Expr::Num(69))); + let result = expr.eval().unwrap().calc(); + assert_eq!(result.unwrap(), -27); +} + +#[test] +fn basic_multiplication() { + let expr = Expr::Mul(Box::new(Expr::Num(42)), Box::new(Expr::Num(69))); + let result = expr.eval().unwrap().calc(); + assert_eq!(result.unwrap(), 2898); +} + +#[test] +fn basic_division_rounded_down() { + let expr = Expr::DivDown(Box::new(Expr::Num(50)), Box::new(Expr::Num(11))); + let result = expr.eval().unwrap().calc(); + assert_eq!(result.unwrap(), 4); +} + +#[test] +fn basic_division_rounded_up() { + let expr = Expr::DivUp(Box::new(Expr::Num(50)), Box::new(Expr::Num(11))); + let result = expr.eval().unwrap().calc(); + assert_eq!(result.unwrap(), 5); +} + +#[test] +fn complex_math() { + let expr = Expr::Sub( + Box::new(Expr::Mul( + Box::new(Expr::Neg(Box::new(Expr::Num(5)))), + Box::new(Expr::Add(Box::new(Expr::Num(3)), Box::new(Expr::Num(1)))), + )), + Box::new(Expr::DivDown( + Box::new(Expr::Neg(Box::new(Expr::Num(4)))), + Box::new(Expr::Num(2)), + )), + ); + let result = expr.eval().unwrap().calc(); + assert_eq!(result.unwrap(), -18); +} + +#[test] +fn basic_dice_math() { + let dice = Dice::new(4, 6); + let expr = Expr::Add(Box::new(Expr::Dice(dice)), Box::new(Expr::Num(8))); + let evaled = expr.eval().unwrap(); + + let dice_total = match evaled { + EvaledExpr::Add(ref boxed, _) => match boxed.as_ref() { + EvaledExpr::Dice(evaled_dice) => evaled_dice.total().unwrap() as i32, + _ => panic!(), + }, + _ => panic!(), + }; + + let total = evaled.calc().unwrap(); + assert_eq!(total, dice_total + 8); +} + +#[test] +fn calc_overflow() { + let expr = Expr::Add(Box::new(Expr::Num(i32::MAX)), Box::new(Expr::Num(1))); + let result = expr.eval().unwrap().calc(); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), Error::Overflow)); +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 38993af..c543bc9 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,5 +1,5 @@ mod dice; -mod term; +mod expr; #[cfg(feature = "parse")] mod parse; diff --git a/src/tests/parse.rs b/src/tests/parse.rs index 4ccc681..8732885 100644 --- a/src/tests/parse.rs +++ b/src/tests/parse.rs @@ -2,73 +2,73 @@ use chumsky::Parser; use crate::{ dice::{Condition, Dice}, - parse::{dice as dice_parser, term as term_parser}, - term::Term, + expr::Expr, + parse::{dice as dice_parser, expr as expr_parser}, }; #[test] fn basic_negation() { - let expected = Term::Neg(Box::new(Term::Num(42))); - let ast = term_parser().parse("-42").unwrap(); + let expected = Expr::Neg(Box::new(Expr::Num(42))); + let ast = expr_parser().parse("-42").unwrap(); assert_eq!(ast, expected); } #[test] fn basic_addition() { - let expected = Term::Add(Box::new(Term::Num(42)), Box::new(Term::Num(69))); - let ast = term_parser().parse("42 + 69").unwrap(); + let expected = Expr::Add(Box::new(Expr::Num(42)), Box::new(Expr::Num(69))); + let ast = expr_parser().parse("42 + 69").unwrap(); assert_eq!(ast, expected); } #[test] fn basic_subtraction() { - let expected = Term::Sub(Box::new(Term::Num(42)), Box::new(Term::Num(69))); - let ast = term_parser().parse("42 - 69").unwrap(); + let expected = Expr::Sub(Box::new(Expr::Num(42)), Box::new(Expr::Num(69))); + let ast = expr_parser().parse("42 - 69").unwrap(); assert_eq!(ast, expected); } #[test] fn basic_multiplication() { - let expected = Term::Mul(Box::new(Term::Num(42)), Box::new(Term::Num(69))); - let ast = term_parser().parse("42 * 69").unwrap(); + let expected = Expr::Mul(Box::new(Expr::Num(42)), Box::new(Expr::Num(69))); + let ast = expr_parser().parse("42 * 69").unwrap(); assert_eq!(ast, expected); } #[test] fn basic_division_rounded_down() { - let expected = Term::DivDown(Box::new(Term::Num(50)), Box::new(Term::Num(11))); - let ast = term_parser().parse("50 / 11").unwrap(); + let expected = Expr::DivDown(Box::new(Expr::Num(50)), Box::new(Expr::Num(11))); + let ast = expr_parser().parse("50 / 11").unwrap(); assert_eq!(ast, expected); } #[test] fn basic_division_rounded_up() { - let expected = Term::DivUp(Box::new(Term::Num(50)), Box::new(Term::Num(11))); - let ast = term_parser().parse("50 \\ 11").unwrap(); + let expected = Expr::DivUp(Box::new(Expr::Num(50)), Box::new(Expr::Num(11))); + let ast = expr_parser().parse("50 \\ 11").unwrap(); assert_eq!(ast, expected); } #[test] fn complex_math() { - let expected = Term::Sub( - Box::new(Term::Mul( - Box::new(Term::Neg(Box::new(Term::Num(5)))), - Box::new(Term::Add(Box::new(Term::Num(3)), Box::new(Term::Num(1)))), + let expected = Expr::Sub( + Box::new(Expr::Mul( + Box::new(Expr::Neg(Box::new(Expr::Num(5)))), + Box::new(Expr::Add(Box::new(Expr::Num(3)), Box::new(Expr::Num(1)))), )), - Box::new(Term::DivDown( - Box::new(Term::Neg(Box::new(Term::Num(4)))), - Box::new(Term::Num(2)), + Box::new(Expr::DivDown( + Box::new(Expr::Neg(Box::new(Expr::Num(4)))), + Box::new(Expr::Num(2)), )), ); - let ast = term_parser().parse("-5 * (3 + 1) - -4 / 2").unwrap(); + let ast = expr_parser().parse("-5 * (3 + 1) - -4 / 2").unwrap(); assert_eq!(ast, expected); } #[test] fn basic_dice_math() { let dice = Dice::new(4, 6); - let expected = Term::Add(Box::new(Term::Dice(dice)), Box::new(Term::Num(8))); - let ast = term_parser().parse("4d6 + 8").unwrap(); + let expected = Expr::Add(Box::new(Expr::Dice(dice)), Box::new(Expr::Num(8))); + let ast = expr_parser().parse("4d6 + 8").unwrap(); assert_eq!(ast, expected); } @@ -128,7 +128,7 @@ fn dice_keep_low_2() { #[test] fn invalid_token() { let bigboi = i32::MAX as i64 + 1; - let term = format!("{}", bigboi); - let ast = term_parser().parse(&term); + let expr = format!("{}", bigboi); + let ast = expr_parser().parse(&expr); assert!(ast.has_errors()); } diff --git a/src/tests/term.rs b/src/tests/term.rs deleted file mode 100644 index fa09523..0000000 --- a/src/tests/term.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::{ - dice::Dice, - term::{Error, EvaledTerm, Term}, -}; - -#[test] -fn basic_negation() { - let term = Term::Neg(Box::new(Term::Num(42))); - let result = term.eval().unwrap().calc(); - assert_eq!(result.unwrap(), -42); -} - -#[test] -fn basic_addition() { - let term = Term::Add(Box::new(Term::Num(42)), Box::new(Term::Num(69))); - let result = term.eval().unwrap().calc(); - assert_eq!(result.unwrap(), 111); -} - -#[test] -fn basic_subtraction() { - let term = Term::Sub(Box::new(Term::Num(42)), Box::new(Term::Num(69))); - let result = term.eval().unwrap().calc(); - assert_eq!(result.unwrap(), -27); -} - -#[test] -fn basic_multiplication() { - let term = Term::Mul(Box::new(Term::Num(42)), Box::new(Term::Num(69))); - let result = term.eval().unwrap().calc(); - assert_eq!(result.unwrap(), 2898); -} - -#[test] -fn basic_division_rounded_down() { - let term = Term::DivDown(Box::new(Term::Num(50)), Box::new(Term::Num(11))); - let result = term.eval().unwrap().calc(); - assert_eq!(result.unwrap(), 4); -} - -#[test] -fn basic_division_rounded_up() { - let term = Term::DivUp(Box::new(Term::Num(50)), Box::new(Term::Num(11))); - let result = term.eval().unwrap().calc(); - assert_eq!(result.unwrap(), 5); -} - -#[test] -fn complex_math() { - let term = Term::Sub( - Box::new(Term::Mul( - Box::new(Term::Neg(Box::new(Term::Num(5)))), - Box::new(Term::Add(Box::new(Term::Num(3)), Box::new(Term::Num(1)))), - )), - Box::new(Term::DivDown( - Box::new(Term::Neg(Box::new(Term::Num(4)))), - Box::new(Term::Num(2)), - )), - ); - let result = term.eval().unwrap().calc(); - assert_eq!(result.unwrap(), -18); -} - -#[test] -fn basic_dice_math() { - let dice = Dice::new(4, 6); - let term = Term::Add(Box::new(Term::Dice(dice)), Box::new(Term::Num(8))); - let evaled = term.eval().unwrap(); - - let dice_total = match evaled { - EvaledTerm::Add(ref boxed, _) => match boxed.as_ref() { - EvaledTerm::Dice(evaled_dice) => evaled_dice.total().unwrap() as i32, - _ => panic!(), - }, - _ => panic!(), - }; - - let total = evaled.calc().unwrap(); - assert_eq!(total, dice_total + 8); -} - -#[test] -fn calc_overflow() { - let term = Term::Add(Box::new(Term::Num(i32::MAX)), Box::new(Term::Num(1))); - let result = term.eval().unwrap().calc(); - assert!(result.is_err()); - assert!(matches!(result.unwrap_err(), Error::Overflow)); -}