finx 0.1.0

A fast, lightweight embeddable scripting language
Documentation
//! Calculator example demonstrating a practical use case for Finx

use finx::{Finx, register_function};
use std::io::{self, Write};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Finx Calculator Example ===");
    println!("Type expressions to evaluate, 'help' for commands, 'quit' to exit\n");

    let mut calc = create_calculator()?;

    // Interactive REPL loop
    loop {
        print!("> ");
        io::stdout().flush()?;

        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        let input = input.trim();

        if input.is_empty() {
            continue;
        }

        match input {
            "quit" | "exit" => break,
            "help" => show_help(),
            "clear" => {
                calc.clear_output();
                println!("Output cleared.");
            }
            "vars" => show_variables(&mut calc),
            "history" => show_history(&calc),
            _ => {
                match calc.eval(input) {
                    Ok(result) => {
                        println!("= {}", result);

                        // Store result in 'ans' variable for next calculation
                        calc.execute(&format!("let ans = {}", result)).ok();
                    }
                    Err(e) => {
                        println!("Error: {}", e);
                    }
                }
            }
        }
    }

    println!("Goodbye!");
    Ok(())
}

fn create_calculator() -> Result<Finx, Box<dyn std::error::Error>> {
    let mut calc = Finx::new();

    // Register mathematical functions
    register_function!(calc, "sin", 1, |x: f64| -> f64 { x.sin() });
    register_function!(calc, "cos", 1, |x: f64| -> f64 { x.cos() });
    register_function!(calc, "tan", 1, |x: f64| -> f64 { x.tan() });
    register_function!(calc, "asin", 1, |x: f64| -> f64 { x.asin() });
    register_function!(calc, "acos", 1, |x: f64| -> f64 { x.acos() });
    register_function!(calc, "atan", 1, |x: f64| -> f64 { x.atan() });

    register_function!(calc, "log", 1, |x: f64| -> f64 { x.ln() });
    register_function!(calc, "log10", 1, |x: f64| -> f64 { x.log10() });
    register_function!(calc, "exp", 1, |x: f64| -> f64 { x.exp() });

    register_function!(calc, "sqrt", 1, |x: f64| -> f64 { x.sqrt() });
    register_function!(calc, "cbrt", 1, |x: f64| -> f64 { x.cbrt() });
    register_function!(calc, "pow", 2, |base: f64, exp: f64| -> f64 {
        base.powf(exp)
    });

    register_function!(calc, "abs", 1, |x: f64| -> f64 { x.abs() });
    register_function!(calc, "ceil", 1, |x: f64| -> f64 { x.ceil() });
    register_function!(calc, "floor", 1, |x: f64| -> f64 { x.floor() });
    register_function!(calc, "round", 1, |x: f64| -> f64 { x.round() });

    register_function!(calc, "min", 2, |a: f64, b: f64| -> f64 { a.min(b) });
    register_function!(calc, "max", 2, |a: f64, b: f64| -> f64 { a.max(b) });

    // Degree/radian conversion
    register_function!(calc, "deg", 1, |rad: f64| -> f64 {
        rad * 180.0 / std::f64::consts::PI
    });
    register_function!(calc, "rad", 1, |deg: f64| -> f64 {
        deg * std::f64::consts::PI / 180.0
    });

    // Constants
    calc.execute(&format!("let pi = {}", std::f64::consts::PI))?;
    calc.execute(&format!("let e = {}", std::f64::consts::E))?;
    calc.execute("let ans = 0")?; // Previous answer

    // Utility functions
    calc.execute(
        r#"
        fn factorial(n) {
            if n <= 1 {
                return 1;
            }
            return n * factorial(n - 1);
        }
        
        fn fibonacci(n) {
            if n <= 1 {
                return n;
            }
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
        
        fn gcd(a, b) {
            if b == 0 {
                return a;
            }
            return gcd(b, a % b);
        }
        
        fn lcm(a, b) {
            return abs(a * b) / gcd(a, b);
        }
        
        fn quadratic(a, b, c) {
            let discriminant = b * b - 4 * a * c;
            if discriminant < 0 {
                print("No real solutions");
                return null;
            }
            let sqrt_disc = sqrt(discriminant);
            let x1 = (-b + sqrt_disc) / (2 * a);
            let x2 = (-b - sqrt_disc) / (2 * a);
            print("x1 = " + x1 + ", x2 = " + x2);
            return x1;
        }
    "#,
    )?;

    Ok(calc)
}

fn show_help() {
    println!("Available commands:");
    println!("  help      - Show this help");
    println!("  quit/exit - Exit the calculator");
    println!("  clear     - Clear output history");
    println!("  vars      - Show defined variables");
    println!("  history   - Show calculation history");
    println!();
    println!("Available functions:");
    println!("  Basic: +, -, *, /, %");
    println!("  Trig: sin, cos, tan, asin, acos, atan");
    println!("  Log: log (natural), log10, exp");
    println!("  Power: sqrt, cbrt, pow(base, exp)");
    println!("  Rounding: abs, ceil, floor, round");
    println!("  Comparison: min, max");
    println!("  Conversion: deg (rad→deg), rad (deg→rad)");
    println!("  Advanced: factorial, fibonacci, gcd, lcm, quadratic");
    println!();
    println!("Constants: pi, e, ans (previous result)");
    println!();
    println!("Examples:");
    println!("  sin(rad(30))     # sine of 30 degrees");
    println!("  sqrt(2) * pi     # square root of 2 times pi");
    println!("  factorial(5)     # 5! = 120");
    println!("  quadratic(1, -5, 6)  # solve x² - 5x + 6 = 0");
}

fn show_variables(calc: &mut Finx) {
    println!("Defined variables:");

    // Try to evaluate common variables
    let vars = ["pi", "e", "ans"];
    for var in &vars {
        match calc.eval(var) {
            Ok(value) => println!("  {} = {}", var, value),
            Err(_) => {} // Variable not defined
        }
    }
}

fn show_history(calc: &Finx) {
    let output = calc.get_output();
    if output.is_empty() {
        println!("No calculation history.");
    } else {
        println!("Recent output:");
        for (i, line) in output.iter().rev().take(10).enumerate() {
            println!("  [{}] {}", output.len() - i, line);
        }
    }
}