mod ast;
mod calc;
mod graph;
mod parser;
mod solver;
pub use crate::ast::Number;
use crate::ast::Statement;
use crate::calc::{calc_operand, CalcError, TopLevelEnv};
use crate::graph::GraphError;
pub use crate::graph::{Area, Graph, Range};
use crate::parser::{parse, ParserError};
use crate::solver::{solve_for, SolverError};
use thiserror::Error;
#[derive(Debug, PartialEq, Eq, Error)]
pub enum Error {
#[error(transparent)]
ParserError(#[from] ParserError),
#[error(transparent)]
CalcError(#[from] CalcError),
#[error(transparent)]
SolverError(#[from] SolverError),
#[error(transparent)]
GraphError(#[from] GraphError),
}
#[derive(Debug, PartialEq)]
pub enum Value {
Void,
Number(Number),
Solved { variable: String, value: Number },
Graph(Graph),
}
#[derive(Debug, Default)]
pub struct Calculator {
env: TopLevelEnv,
}
impl Calculator {
pub fn new() -> Self {
Self::default()
}
pub fn execute(&mut self, line: &str) -> Result<Value, Error> {
let st = parse(line)?;
match st {
Statement::Expression { op } => Ok(Value::Number(calc_operand(&op, &self.env)?)),
Statement::Assignment { sym, op } => {
self.env.put(sym, calc_operand(&op, &self.env)?)?;
Ok(Value::Void)
}
Statement::SolveFor { lhs, rhs, sym } => Ok(Value::Solved {
variable: sym.to_string(),
value: solve_for(&lhs, &rhs, &sym, &self.env)?,
}),
Statement::Function { name, fun } => {
self.env.put_fun(name, fun);
Ok(Value::Void)
}
Statement::Plot { name } => Ok(Value::Graph(Graph::new(&name, &self.env)?)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple_calc() {
let mut calc = Calculator::new();
assert_eq!(Ok(Value::Number(3.0)), calc.execute("1 + 2"));
}
#[test]
fn simple_assign() {
let mut calc = Calculator::new();
assert_eq!(Ok(Value::Void), calc.execute("a := 1"));
assert_eq!(Ok(Value::Number(1.0)), calc.execute("a"));
}
#[test]
fn simple_function() {
let mut calc = Calculator::new();
assert_eq!(Ok(Value::Void), calc.execute("fun(x, y) := y - x"));
assert_eq!(
Ok(Value::Number(20.0)),
calc.execute("fun(1 + 2, 3 * 9) - 4")
);
}
#[test]
fn simple_solve_for() {
let mut calc = Calculator::new();
assert_eq!(
Ok(Value::Solved {
variable: "y".to_string(),
value: 4.0
}),
calc.execute("solve 3 * y - 2 = y + 6 for y")
);
}
#[test]
fn simple_plot() {
let mut calc = Calculator::new();
assert_eq!(Ok(Value::Void), calc.execute("f(x) := x ^ 2"));
let graph = calc.execute("plot f").unwrap();
assert!(matches!(&graph, Value::Graph(_)));
if let Value::Graph(graph) = graph {
let plot = graph
.plot(
&Area::new(-100., -100., 100., 100.),
&Area::new(0., 0., 80., 30.),
)
.unwrap();
assert!(!plot.points.is_empty());
}
}
}
pub const HELP_SUMMARY: &str = include_str!("../doc/summary.md");