rust_expression/lib.rs
1mod ast;
2mod calc;
3mod graph;
4mod parser;
5mod solver;
6
7pub use crate::ast::Number;
8use crate::ast::Statement;
9use crate::calc::{calc_operand, CalcError, TopLevelEnv};
10use crate::graph::GraphError;
11pub use crate::graph::{Area, Graph, Range};
12use crate::parser::{parse, ParserError};
13use crate::solver::{solve_for, SolverError};
14
15use thiserror::Error;
16
17/// Calculator error
18#[derive(Debug, PartialEq, Eq, Error)]
19pub enum Error {
20 /// errors derived from parsers
21 #[error(transparent)]
22 ParserError(#[from] ParserError),
23 /// errors derived from calculator
24 #[error(transparent)]
25 CalcError(#[from] CalcError),
26 /// errors derived from solver
27 #[error(transparent)]
28 SolverError(#[from] SolverError),
29 /// errors derived from graph
30 #[error(transparent)]
31 GraphError(#[from] GraphError),
32}
33
34#[derive(Debug, PartialEq)]
35pub enum Value {
36 Void,
37 Number(Number),
38 Solved { variable: String, value: Number },
39 Graph(Graph),
40}
41
42/// # Calculator
43///
44/// See it in action on [https://msuesskraut.github.io/calc/index.html](https://msuesskraut.github.io/calc/index.html).
45/// Further examples are in [`Calculator::execute`].
46#[derive(Debug, Default)]
47pub struct Calculator {
48 env: TopLevelEnv,
49}
50
51impl Calculator {
52 /// constructs an calculator without any known variables
53 pub fn new() -> Self {
54 Self::default()
55 }
56
57 /// Executes a command line.
58 /// These kinds of statements are supported:
59 /// - Expression:
60 /// ```
61 /// use rust_expression::{Calculator, Value};
62 /// let mut c = Calculator::new();
63 /// assert_eq!(Ok(Value::Number(3.0)), c.execute("1 + 2"));
64 /// ```
65 /// - Variable assignments:
66 /// ```
67 /// # use rust_expression::{Calculator, Value};
68 /// # let mut c = Calculator::new();
69 /// assert_eq!(Ok(Value::Void), c.execute("a := 6"));
70 /// assert_eq!(Ok(Value::Number(36.0)), c.execute("a ^ 2"));
71 /// ```
72 /// - Solving linear expressions:
73 /// ```
74 /// # use rust_expression::{Calculator, Value};
75 /// # let mut c = Calculator::new();
76 /// # c.execute("a := 6");
77 /// assert_eq!(Ok(Value::Solved {variable: "x".to_string(), value: 4.0}), c.execute("solve 3 * x - 2 = x + a for x"));
78 /// ```
79 /// - Function definition:
80 /// ```
81 /// # use rust_expression::{Calculator, Value};
82 /// # let mut c = Calculator::new();
83 /// # c.execute("a := 6");
84 /// assert_eq!(Ok(Value::Void), c.execute("fun(x, y) := y - x"));
85 /// assert_eq!(Ok(Value::Number(2.0)), c.execute("fun(1.5 * 2, 3 + a) - 4"));
86 /// ```
87 /// - Create a plot:
88 /// ```
89 /// # use rust_expression::{Calculator, Value};
90 /// # use rust_expression::Area;
91 /// # let mut c = Calculator::new();
92 /// assert_eq!(Ok(Value::Void), c.execute("f(x) := x ^ 2"));
93 ///
94 /// match c.execute("plot f") {
95 /// Ok(Value::Graph(graph)) => {
96 /// let area = Area::new(-100., -100., 100., 100.);
97 /// let screen = Area::new(0., 0., 60., 40.);
98 /// let plot = graph.plot(&area, &screen).unwrap();
99 /// assert_eq!(Some(20.), plot.points[30]);
100 /// }
101 /// // ...
102 /// # _ => unimplemented!(),
103 /// }
104 /// ```
105 pub fn execute(&mut self, line: &str) -> Result<Value, Error> {
106 let st = parse(line)?;
107 match st {
108 Statement::Expression { op } => Ok(Value::Number(calc_operand(&op, &self.env)?)),
109 Statement::Assignment { sym, op } => {
110 self.env.put(sym, calc_operand(&op, &self.env)?)?;
111 Ok(Value::Void)
112 }
113 Statement::SolveFor { lhs, rhs, sym } => Ok(Value::Solved {
114 variable: sym.to_string(),
115 value: solve_for(&lhs, &rhs, &sym, &self.env)?,
116 }),
117 Statement::Function { name, fun } => {
118 self.env.put_fun(name, fun);
119 Ok(Value::Void)
120 }
121 Statement::Plot { name } => Ok(Value::Graph(Graph::new(&name, &self.env)?)),
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn simple_calc() {
132 let mut calc = Calculator::new();
133 assert_eq!(Ok(Value::Number(3.0)), calc.execute("1 + 2"));
134 }
135
136 #[test]
137 fn simple_assign() {
138 let mut calc = Calculator::new();
139 assert_eq!(Ok(Value::Void), calc.execute("a := 1"));
140 assert_eq!(Ok(Value::Number(1.0)), calc.execute("a"));
141 }
142
143 #[test]
144 fn simple_function() {
145 let mut calc = Calculator::new();
146 assert_eq!(Ok(Value::Void), calc.execute("fun(x, y) := y - x"));
147 assert_eq!(
148 Ok(Value::Number(20.0)),
149 calc.execute("fun(1 + 2, 3 * 9) - 4")
150 );
151 }
152
153 #[test]
154 fn simple_solve_for() {
155 let mut calc = Calculator::new();
156 assert_eq!(
157 Ok(Value::Solved {
158 variable: "y".to_string(),
159 value: 4.0
160 }),
161 calc.execute("solve 3 * y - 2 = y + 6 for y")
162 );
163 }
164
165 #[test]
166 fn simple_plot() {
167 let mut calc = Calculator::new();
168 assert_eq!(Ok(Value::Void), calc.execute("f(x) := x ^ 2"));
169 let graph = calc.execute("plot f").unwrap();
170 assert!(matches!(&graph, Value::Graph(_)));
171 if let Value::Graph(graph) = graph {
172 let plot = graph
173 .plot(
174 &Area::new(-100., -100., 100., 100.),
175 &Area::new(0., 0., 80., 30.),
176 )
177 .unwrap();
178 assert!(!plot.points.is_empty());
179 }
180 }
181}
182
183pub const HELP_SUMMARY: &str = include_str!("../doc/summary.md");