1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
mod ast;
mod calc;
mod parser;
mod solver;

use crate::ast::{Number, Statement};
use crate::calc::{calc_operand, CalcError, TopLevelEnv};
use crate::parser::{parse, ParserError};
use crate::solver::{solve_for, SolverError};

use thiserror::Error;

/// Calculator error
#[derive(Debug, PartialEq, Eq, Error)]
pub enum Error {
    /// errors derived from parsers
    #[error(transparent)]
    ParserError(#[from] ParserError),
    /// errors derived from calculator
    #[error(transparent)]
    CalcError(#[from] CalcError),
    /// errors derived from solver
    #[error(transparent)]
    SolverError(#[from] SolverError),
}

/// # Calculator
///
/// See it in action on [https://msuesskraut.github.io/calc/index.html](https://msuesskraut.github.io/calc/index.html).
/// Further examples are in [`Calculator::execute`].
#[derive(Debug, Default)]
pub struct Calculator {
    env: TopLevelEnv,
}

impl Calculator {
    /// constructs an calculator without any known variables
    pub fn new() -> Self {
        Self::default()
    }

    /// Executes a command line.
    /// 3 kinds of statements are supported:
    /// - Expression:
    ///   ```
    ///   use rust_expression::Calculator;
    ///   let mut c = Calculator::new();
    ///   assert_eq!(Ok(Some(3.0)), c.execute("1 + 2"));
    ///   ```
    /// - Variable assignments:
    ///   ```
    ///   # use rust_expression::Calculator;
    ///   # let mut c = Calculator::new();
    ///   assert_eq!(Ok(None), c.execute("a := 6"));
    ///   assert_eq!(Ok(Some(36.0)), c.execute("a ^ 2"));
    ///   ```
    /// - Solving linear expressions:
    ///   ```
    ///   # use rust_expression::Calculator;
    ///   # let mut c = Calculator::new();
    ///   # c.execute("a := 6");
    ///   assert_eq!(Ok(Some(4.0)), c.execute("solve 3 * x - 2 = x + a for x"));
    ///   ```
    /// - Function definition:
    ///   ```
    ///   # use rust_expression::Calculator;
    ///   # let mut c = Calculator::new();
    ///   assert_eq!(Ok(None), c.execute("fun(x, y) := y - x"));
    ///   assert_eq!(Ok(Some(20.0)), c.execute("fun(1 + 2, 3 * 9) - 4"));
    ///   ```
    pub fn execute(&mut self, line: &str) -> Result<Option<Number>, Error> {
        let st = parse(line)?;
        match st {
            Statement::Expression { op } => Ok(Some(calc_operand(&op, &self.env)?)),
            Statement::Assignment { sym, op } => {
                self.env.put(sym, calc_operand(&op, &self.env)?);
                Ok(None)
            }
            Statement::SolveFor { lhs, rhs, sym } => {
                Ok(Some(solve_for(&lhs, &rhs, &sym, &self.env)?))
            }
            Statement::Function { name, fun } => {
                self.env.put_fun(name, fun);
                Ok(None)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn simple_calc() {
        let mut calc = Calculator::new();
        assert_eq!(Ok(Some(3.0)), calc.execute("1 + 2"));
    }

    #[test]
    fn simple_assign() {
        let mut calc = Calculator::new();
        assert_eq!(Ok(None), calc.execute("a := 1"));
        assert_eq!(Ok(Some(1.0)), calc.execute("a"));
    }

    #[test]
    fn simple_function() {
        let mut calc = Calculator::new();
        assert_eq!(Ok(None), calc.execute("fun(x, y) := y - x"));
        assert_eq!(Ok(Some(20.0)), calc.execute("fun(1 + 2, 3 * 9) - 4"));
    }
}