126 lines
4 KiB
Rust
126 lines
4 KiB
Rust
use std::io::Write;
|
|
|
|
use mathvis::evaluation_tree::{args::Args, Evaluatable};
|
|
use num::complex::Complex64;
|
|
|
|
fn main() {
|
|
println!("Welcome to CLI calculator!");
|
|
let mut input = String::new();
|
|
'outer: loop {
|
|
// read input from stdin
|
|
print!("\n\nPlease, input your formula: ");
|
|
std::io::stdout().flush().unwrap();
|
|
input.clear();
|
|
if std::io::stdin().read_line(&mut input).unwrap() == 0 {
|
|
return;
|
|
}
|
|
input.pop(); // pop '\n'
|
|
|
|
// lex to tokens
|
|
let tokens = match mathvis::lexer::lex(input.as_str()) {
|
|
Ok((left, tokens)) => {
|
|
if !left.is_empty() {
|
|
println!("Info: \"{left}\" was ignored");
|
|
}
|
|
tokens
|
|
}
|
|
Err(err) => {
|
|
eprintln!("Failed to evaluate: {err}");
|
|
continue 'outer;
|
|
}
|
|
};
|
|
|
|
// parse to tree
|
|
let tree = match mathvis::evaluation_tree::EvaluationTree::from_tokens(tokens.as_slice()) {
|
|
Ok(tree) => tree,
|
|
Err(err) => {
|
|
eprintln!("Failed to parse tokens: {err}");
|
|
continue 'outer;
|
|
}
|
|
};
|
|
|
|
// create the arguments
|
|
let mut args = tree.args(); // input args
|
|
args.merge(default_args()); // merge with default args
|
|
|
|
if args.functions2().into_iter().next().is_some() {
|
|
// there are functions2, this expression will not be evaluated
|
|
eprintln!("CLI calculator does not support functions2");
|
|
continue 'outer;
|
|
}
|
|
|
|
if let Some(name) = args
|
|
.functions_mut()
|
|
.into_iter()
|
|
.find_map(|(name, v)| v.is_none().then_some(name))
|
|
{
|
|
// there are unknown functions
|
|
eprintln!("{name} is an unknown function. CLI calculator does not support your own function definition, please use trig functions only");
|
|
continue 'outer;
|
|
}
|
|
|
|
// ask user to assign all the variables
|
|
for (name, val) in args.variables_mut() {
|
|
if val.is_some() {
|
|
continue;
|
|
}
|
|
print!("Please assign {name} := ");
|
|
std::io::stdout().flush().unwrap();
|
|
input.clear();
|
|
if std::io::stdin().read_line(&mut input).unwrap() == 0 {
|
|
return;
|
|
}
|
|
input.pop(); // pop '\n'
|
|
match input.parse() {
|
|
Ok(parsed) => {
|
|
*val = Some(parsed);
|
|
}
|
|
Err(err) => {
|
|
eprintln!("Bad assignment format: {err}");
|
|
continue 'outer;
|
|
}
|
|
}
|
|
}
|
|
|
|
// evaluate
|
|
let res = match tree.evaluate(&args) {
|
|
Ok(res) => res,
|
|
Err(err) => {
|
|
eprintln!("Unexpected error while evaluation: {err}");
|
|
continue 'outer;
|
|
}
|
|
};
|
|
|
|
println!("The end result is {res}");
|
|
}
|
|
}
|
|
|
|
fn default_args() -> Args {
|
|
let mut args = Args::new();
|
|
// well-known constants
|
|
args.assign_variable("pi", Complex64::from(std::f64::consts::PI));
|
|
args.assign_variable("e", Complex64::from(std::f64::consts::E));
|
|
|
|
// basic trig functions
|
|
args.assign_function("sin", Complex64::sin);
|
|
args.assign_function("cos", Complex64::cos);
|
|
args.assign_function("tan", Complex64::tan);
|
|
args.assign_function("tg", Complex64::tan);
|
|
args.assign_function("cot", cot);
|
|
args.assign_function("ctg", cot);
|
|
args.assign_function("asin", Complex64::asin);
|
|
args.assign_function("acos", Complex64::acos);
|
|
args.assign_function("atan", Complex64::atan);
|
|
args.assign_function("arctg", Complex64::atan);
|
|
args.assign_function("acot", acot);
|
|
args.assign_function("arcctg", acot);
|
|
args
|
|
}
|
|
|
|
fn cot(z: Complex64) -> Complex64 {
|
|
Complex64::new(1.0, 0.0) / z.tan()
|
|
}
|
|
|
|
fn acot(z: Complex64) -> Complex64 {
|
|
Complex64::new(std::f64::consts::FRAC_PI_2, 0.0) - z.atan()
|
|
}
|