1pub mod val;
2
3use crate::{function::eval_fn_call, parser::fn_call::FnCall};
4use std::convert::TryFrom;
5
6use val::*;
7
8pub mod unit;
9use unit::*;
10
11pub mod unit_expr;
12
13use crate::{error::CalcError, statement::Scope};
14
15#[derive(Debug, Clone)]
16pub enum Expr {
17 Atom(Val),
18 ParenExpr(Box<Expr>),
19 Ident(String),
20 FnCall(FnCall),
21 Cons(Op, Vec<Expr>),
22}
23
24impl std::fmt::Display for Expr {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Expr::Atom(v) => write!(f, "{}", v),
28 Expr::ParenExpr(e) => write!(f, "({:?})", e),
29 Expr::Ident(n) => write!(f, "{}", n),
30 Expr::FnCall(fc) => write!(f, "{:?}", fc),
31 Expr::Cons(op, e) => write!(f, "({:?}, {:?})", op, e),
32 }
33 }
34}
35
36impl Expr {
37 pub fn eval(&self, scope: &Scope) -> Result<Val, CalcError> {
38 let e = |a: &Expr| a.eval(scope);
39 Ok(match self {
40 Expr::Atom(v) => v.clamp_num(),
41 Expr::ParenExpr(ex) => e(ex)?,
42 Expr::Ident(n) => {
43 if let Some(v) = scope.variables.get(n) {
44 v.clone()
45 } else {
46 (1.0, Unit::try_from(n)?).into()
47 }
48 }
49 Expr::FnCall(fc) => eval_fn_call(fc, scope)?,
50 Expr::Cons(op, xs) => match (op, xs.as_slice()) {
51 (Op::Plus, [a, b]) => (e(a)? + e(b)?)?,
52 (Op::Minus, [a, b]) => (e(a)? - e(b)?)?,
53 (Op::Mul, [a, b]) => e(a)? * e(b)?,
54 (Op::Div, [a, b]) => e(a)? / e(b)?,
55 (Op::Exp, [a, b]) => e(a)?.pow(&e(b)?),
56 (Op::AddUnit(u, _), [v]) => e(v)?.with_unit(&u),
57 _ => return Err(CalcError::MathError),
58 },
59 })
60 }
61
62 pub fn remove_parens(&self) -> Self {
63 if let Expr::ParenExpr(b) = self {
64 *b.clone()
65 } else {
66 self.clone()
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
72pub enum Op {
73 Plus,
74 Minus,
75 Mul,
76 Div,
77 Exp,
78 AddUnit(Unit, String),
79}