From 03226fb869d82bfec2d704a4c9930174ded5d159 Mon Sep 17 00:00:00 2001 From: Jonathan Cornaz Date: Tue, 13 Sep 2022 21:58:07 +0200 Subject: [PATCH] feat: expression evaluation --- src/amount/expression.rs | 34 ++++++++++++++++++++++++++++++++++ src/amount/mod.rs | 7 ++++++- src/lib.rs | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/amount/expression.rs b/src/amount/expression.rs index 8986b3c..3c20ae3 100644 --- a/src/amount/expression.rs +++ b/src/amount/expression.rs @@ -74,6 +74,30 @@ impl Expression { fn minus(left: Self, right: Self) -> Self { Self::operation(Operator::Substract, left, right) } + + /// Evaluate the expression + #[must_use] + pub fn evaluate(&self) -> Value { + match self { + Expression::Value(value) => *value, + Expression::Operation(Operation { + operator, + left, + right, + }) => operator.evaluate(left.evaluate(), right.evaluate()), + } + } +} + +impl Operator { + fn evaluate(self, Value(left): Value, Value(right): Value) -> Value { + Value(match self { + Operator::Divide => left / right, + Operator::Multiply => left * right, + Operator::Add => left + right, + Operator::Substract => left - right, + }) + } } pub(super) fn parse(input: &str) -> IResult<&str, Expression> { @@ -213,4 +237,14 @@ mod tests { fn parse_expression(#[case] input: &str, #[case] expected: Expression) { assert_eq!(parse(input), Ok(("", expected))); } + + #[rstest] + #[case(Expression::value(1), 1)] + #[case(Expression::plus(Expression::value(1), Expression::value(1)), 2)] + #[case(Expression::minus(Expression::value(5), Expression::value(2)), 3)] + #[case(Expression::div(Expression::value(12), Expression::value(3)), 4)] + #[case(Expression::mul(Expression::value(2), Expression::value(3)), 6)] + fn evaluate(#[case] expression: Expression, #[case] expected_result: impl Into) { + assert_eq!(expression.evaluate(), Value(expected_result.into())); + } } diff --git a/src/amount/mod.rs b/src/amount/mod.rs index 833d0c3..b5a446c 100644 --- a/src/amount/mod.rs +++ b/src/amount/mod.rs @@ -3,7 +3,7 @@ use nom::{ sequence::separated_pair, IResult, }; -pub use self::expression::Expression; +pub use self::expression::{Expression, Value}; mod expression; @@ -27,6 +27,11 @@ impl<'a> Amount<'a> { &self.expression } + #[must_use] + pub fn value(&self) -> Value { + self.expression.evaluate() + } + #[must_use] pub fn currency(&self) -> &str { self.currency diff --git a/src/lib.rs b/src/lib.rs index e7ea386..0e68b44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![deny(future_incompatible, unsafe_code)] #![warn(nonstandard_style, rust_2018_idioms, clippy::pedantic)] +#![cfg_attr(test, allow(clippy::needless_pass_by_value))] //! A rust parsing library for [beancount](https://beancount.github.io/docs/) files //! @@ -34,6 +35,7 @@ mod string; mod transaction; use crate::directive::directive; + pub use crate::{ account::Account, amount::{Amount, Expression},