1use std::fmt::Display;
4
5use astro_float::Consts;
6
7use crate::ast::{Atom, BinaryOp, Expr, Stmt, UnaryOp, UserFunc};
8use crate::context::Context;
9use crate::formatting::float_to_string;
10use crate::{CalcError, CalcResult, Number, PREC, RM};
11
12pub fn eval_atom(atom: &Atom, ctx: &Context) -> CalcResult {
16 match atom {
17 Atom::Num(num) => Ok(num.clone()),
18 Atom::Symbol(name) => ctx.lookup_value(name),
19 }
20}
21
22pub fn eval_expr(expr: &Expr, ctx: &Context) -> CalcResult {
24 match expr {
25 Expr::AtomExpr(atom) => eval_atom(atom, ctx),
26 Expr::UnaryExpr { op, data } => {
27 let data = eval_expr(data, ctx)?;
28 match op {
29 UnaryOp::Negate => Ok(-data),
30 }
31 }
32 Expr::BinaryExpr { lhs, rhs, op } => {
33 let lhs = eval_expr(lhs, ctx)?;
34 let rhs = eval_expr(rhs, ctx)?;
35 Ok(match op {
36 BinaryOp::Plus => lhs.add(&rhs, PREC, RM),
37 BinaryOp::Minus => lhs.sub(&rhs, PREC, RM),
38 BinaryOp::Times => lhs.mul(&rhs, PREC, RM),
39 BinaryOp::Divide => lhs.div(&rhs, PREC, RM),
40 BinaryOp::Power => {
41 let mut consts = Consts::new().unwrap();
43 lhs.pow(&rhs, PREC, RM, &mut consts)
44 }
45 })
46 }
47 Expr::FunctionCall { function, args } => {
48 let function = ctx.lookup_fn(function)?;
49 let args = args
50 .into_iter()
51 .map(|arg| eval_expr(arg, ctx))
52 .collect::<Result<Vec<Number>, CalcError>>()?;
53
54 function.call(&args, ctx)
55 }
56 }
57}
58
59pub enum CalcValue {
61 Ok,
63
64 Value(Number),
66}
67
68impl Display for CalcValue {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 CalcValue::Ok => write!(f, "ok"),
72 CalcValue::Value(v) => write!(f, "{}", float_to_string(v)),
73 }
74 }
75}
76
77pub fn eval_stmt(stmt: &Stmt, ctx: &mut Context) -> Result<CalcValue, CalcError> {
79 match stmt {
80 Stmt::FuncDef { name, params, body } => {
81 let func = UserFunc::new(params.clone(), body.clone());
82 ctx.bind_fn(name.clone(), func)?;
83 Ok(CalcValue::Ok)
84 }
85 Stmt::Assignment { name, value } => {
86 let value = eval_expr(&value, ctx)?;
87 ctx.bind_value(name.clone(), value)
88 .map(|v| CalcValue::Value(v))
89 }
90 Stmt::ExprStmt(expr) => eval_expr(expr, ctx).map(|v| CalcValue::Value(v)),
91 }
92}