use mathexpr::Expression;
use rustyline::error::ReadlineError;
use rustyline::DefaultEditor;
use std::collections::HashMap;
const HISTORY_FILE: &str = ".mathexpr_history";
fn main() {
println!("mathexpr calculator");
println!("Type 'help' for available functions, 'quit' to exit.\n");
let mut rl = match DefaultEditor::new() {
Ok(rl) => rl,
Err(e) => {
eprintln!("Failed to initialize readline: {}", e);
return;
}
};
let _ = rl.load_history(HISTORY_FILE);
let mut variables: HashMap<String, f64> = HashMap::new();
loop {
match rl.readline("mathexpr> ") {
Ok(line) => {
let input = line.trim();
if input.is_empty() {
continue;
}
let _ = rl.add_history_entry(&line);
match input.to_lowercase().as_str() {
"quit" | "exit" => {
println!("Goodbye!");
break;
}
"vars" => {
print_variables(&variables);
continue;
}
"help" => {
print_help();
continue;
}
_ => {}
}
if let Some((name, expr)) = parse_assignment(input) {
match evaluate_expression(expr, &variables) {
Ok(value) => {
println!("{}", value);
variables.insert(name.to_string(), value);
}
Err(e) => println!("Error: {}", e),
}
} else {
match evaluate_expression(input, &variables) {
Ok(value) => println!("{}", value),
Err(e) => println!("Error: {}", e),
}
}
}
Err(ReadlineError::Interrupted) => {
println!();
continue;
}
Err(ReadlineError::Eof) => {
println!("Goodbye!");
break;
}
Err(e) => {
eprintln!("Error: {}", e);
break;
}
}
}
let _ = rl.save_history(HISTORY_FILE);
}
fn parse_assignment(input: &str) -> Option<(&str, &str)> {
let eq_pos = input.find('=')?;
let name = input[..eq_pos].trim();
if name.is_empty() || !is_valid_identifier(name) {
return None;
}
if name == "pi" || name == "e" {
return None;
}
let expr = input[eq_pos + 1..].trim();
if expr.is_empty() {
return None;
}
Some((name, expr))
}
fn is_valid_identifier(s: &str) -> bool {
let mut chars = s.chars();
match chars.next() {
Some(c) if c.is_alphabetic() || c == '_' => {}
_ => return false,
}
chars.all(|c| c.is_alphanumeric() || c == '_')
}
fn evaluate_expression(
expr: &str,
variables: &HashMap<String, f64>,
) -> Result<f64, Box<dyn std::error::Error>> {
let parsed = Expression::parse(expr)?;
let var_names: Vec<&str> = variables.keys().map(|s| s.as_str()).collect();
let var_values: Vec<f64> = var_names.iter().map(|name| variables[*name]).collect();
let compiled = parsed.compile(&var_names)?;
let result = compiled.eval(&var_values)?;
Ok(result)
}
fn print_variables(variables: &HashMap<String, f64>) {
if variables.is_empty() {
println!("No variables defined.");
} else {
let mut vars: Vec<_> = variables.iter().collect();
vars.sort_by_key(|(k, _)| *k);
for (name, value) in vars {
println!(" {} = {}", name, value);
}
}
}
fn print_help() {
println!("mathexpr - Mathematical Expression Calculator\n");
println!("USAGE:");
println!(" <expression> Evaluate an expression");
println!(" <name> = <expr> Define a variable\n");
println!("COMMANDS:");
println!(" vars List all defined variables");
println!(" help Show this help message");
println!(" quit, exit Exit the calculator\n");
println!("OPERATORS:");
println!(" + - * / Basic arithmetic");
println!(" % Modulo");
println!(" ^ Exponentiation (right-associative)\n");
println!("CONSTANTS:");
println!(" pi 3.14159...");
println!(" e 2.71828...\n");
println!("FUNCTIONS:");
println!(" Core: abs, sqrt, cbrt, exp, log/ln, log2, log10, pow, mod");
println!(" Trig: sin, cos, tan, asin, acos, atan");
println!(" Hyper: sinh, cosh, tanh");
println!(" Round: floor, ceil, round, trunc, signum");
println!(" Bounds: min, max, clamp\n");
println!("EXAMPLES:");
println!(" 2 + 3 * 4 => 14");
println!(" sqrt(3^2 + 4^2) => 5");
println!(" x = 10 => 10");
println!(" 2 * pi * x => 62.83...");
}