1#![warn(missing_docs)]
4#![warn(rustdoc::missing_crate_level_docs)]
5
6use astro_float::{BigFloat, RoundingMode};
7use thiserror::Error;
8
9pub mod ast;
10mod builtins;
11pub mod context;
12pub mod eval;
13pub mod formatting;
14pub mod parser;
15
16pub type Number = BigFloat;
18
19pub type CalcResult = Result<Number, CalcError>;
21
22pub const PREC: usize = 128;
24
25pub const BASE_10_PREC: usize = 38; pub const RM: RoundingMode = RoundingMode::ToEven;
30
31#[derive(Error, Debug, Clone)]
33pub enum CalcError {
34 #[error("ERROR: name not found: {0}")]
36 NameNotFound(String),
37
38 #[error("ERROR: name already bound: {0}")]
40 NameAlreadyBound(String),
41
42 #[error("ERROR: expected {0} arguments, found {1}")]
44 IncorrectArity(usize, usize),
45
46 #[error("ERROR: number parsing error")]
48 ParseNum,
49
50 #[error("ERROR: parsing error")]
52 ParseError,
53
54 #[error("ERROR: IO error")]
56 IOError,
57
58 #[error("ERROR: unknown error")]
60 Unknown,
61}
62
63#[cfg(test)]
64mod tests {
65 use super::ast::*;
66 use super::context::Context;
67 use super::eval::*;
68 use crate::PREC;
69
70 use astro_float::BigFloat;
71
72 #[test]
73 fn test_atom_eval() {
74 let num = BigFloat::from_i32(123, PREC);
75
76 let mut ctx = Context::new();
77 ctx.bind_value("a".to_string(), num.clone())
78 .expect("failed to bind value");
79
80 let sym_atom = Atom::Symbol("a".to_string());
81 let res = eval_atom(&sym_atom, &ctx).expect("failed to evaluate symbol atom");
82 assert_eq!(num, res);
83
84 let num_atom = Atom::Num(num.clone());
85 let res = eval_atom(&num_atom, &ctx).expect("failed to evaluate number atom");
86 assert_eq!(num, res);
87 }
88
89 #[test]
90 fn test_expr_eval() {
91 let num = BigFloat::from_i32(123, PREC);
92 let num2 = BigFloat::from_i32(-123, PREC);
93 let num3 = BigFloat::from_i32(10, PREC);
94 let num4 = BigFloat::from_i32(20, PREC);
95 let num5 = BigFloat::from_i32(30, PREC);
96
97 let ctx = Context::new();
98
99 let expr = Expr::UnaryExpr {
100 op: UnaryOp::Negate,
101 data: Box::new(Expr::AtomExpr(Atom::Num(num))),
102 };
103 let res = eval_expr(&expr, &ctx).unwrap();
104 assert_eq!(res, num2);
105
106 let lhs = Expr::AtomExpr(Atom::Num(num3));
107 let rhs = Expr::AtomExpr(Atom::Num(num4));
108 let add_expr = Expr::BinaryExpr {
109 lhs: Box::new(lhs),
110 rhs: Box::new(rhs),
111 op: BinaryOp::Plus,
112 };
113 let res = eval_expr(&add_expr, &ctx).unwrap();
114 assert_eq!(res, num5);
115 }
116
117 #[test]
118 fn test_function_call() {
119 let num1 = BigFloat::from_i32(10, PREC);
120 let num2 = BigFloat::from_i32(20, PREC);
121 let num3 = BigFloat::from_i32(30, PREC);
122
123 let mut ctx = Context::new();
124
125 let func = UserFunc::new(
126 vec!["x".to_string(), "y".to_string()],
127 Expr::BinaryExpr {
128 lhs: Box::new(Expr::AtomExpr(Atom::Symbol("x".to_string()))),
129 rhs: Box::new(Expr::AtomExpr(Atom::Symbol("y".to_string()))),
130 op: BinaryOp::Plus,
131 },
132 );
133 ctx.bind_fn("f".to_string(), func).unwrap();
134
135 let func_call = Expr::FunctionCall {
136 function: "f".to_string(),
137 args: vec![
138 Expr::AtomExpr(Atom::Num(num1)),
139 Expr::AtomExpr(Atom::Num(num2)),
140 ],
141 };
142
143 let res = eval_expr(&func_call, &ctx).unwrap();
144
145 assert_eq!(res, num3);
146 }
147}