evalr 0.1.0

Expression evaluator
Documentation
use rand::Rng;

pub fn evaluate_expression(expression: &str) -> Result<f64, &'static str> {
    let tokens = tokenize(expression)?;
    let postfix_tokens = infix_to_postfix(&tokens)?;
    let result = evaluate_postfix(&postfix_tokens)?;
    Ok(result)
}

fn tokenize(expression: &str) -> Result<Vec<String>, &'static str> {
    let mut tokens = Vec::new();
    let mut number = String::new();

    for c in expression.chars() {
        if c.is_whitespace() {
            continue;
        } else if c.is_digit(10) || c == '.' {
            number.push(c);
        } else if c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' {
            if !number.is_empty() {
                tokens.push(number.clone());
                number.clear();
            }
            tokens.push(c.to_string());
        } else {
            return Err("Invalid character in expression");
        }
    }

    if !number.is_empty() {
        tokens.push(number);
    }

    Ok(tokens)
}

fn infix_to_postfix(tokens: &[String]) -> Result<Vec<String>, &'static str> {
    let mut output = Vec::new();
    let mut operators = Vec::new();

    for token in tokens {
        if let Ok(_) = token.parse::<f64>() {
            output.push(token.clone());
        } else if token == "(" {
            operators.push(token.clone());
        } else if token == ")" {
            while let Some(op) = operators.pop() {
                if op == "(" {
                    break;
                }
                output.push(op);
            }
        } else if token == "+" || token == "-" || token == "*" || token == "/" {
            while let Some(op) = operators.last() {
                if precedence(op) >= precedence(token) {
                    output.push(operators.pop().unwrap());
                } else {
                    break;
                }
            }
            operators.push(token.clone());
        } else {
            return Err("Invalid token in expression");
        }
    }

    while let Some(op) = operators.pop() {
        output.push(op);
    }

    Ok(output)
}

fn precedence(op: &str) -> i32 {
    match op {
        "+" | "-" => 1,
        "*" | "/" => 2,
        _ => 0,
    }
}

fn evaluate_postfix(tokens: &[String]) -> Result<f64, &'static str> {
    let mut stack = Vec::new();

    for token in tokens {
        if let Ok(num) = token.parse::<f64>() {
            stack.push(num);
        } else if token == "+" || token == "-" || token == "*" || token == "/" {
            let b = stack.pop().ok_or("Invalid expression")?;
            let a = stack.pop().ok_or("Invalid expression")?;
            let result = match token.as_str() {
                "+" => a + b,
                "-" => a - b,
                "*" => a * b,
                "/" => a / b,
                _ => return Err("Invalid operator"),
            };
            stack.push(result);
        } else {
            return Err("Invalid token in expression");
        }
    }

    stack.pop().ok_or("Invalid expression")
}

pub fn generate_random_expression() -> String {
    let mut rng = rand::thread_rng();
    let operators = vec!["+", "-", "*", "/"];
    let mut expression = String::new();

    // Function to generate a random number
    fn generate_number(rng: &mut rand::rngs::ThreadRng) -> String {
        rng.gen_range(1..10).to_string()
    }

    // Function to generate a simple expression
    fn generate_simple_expression(rng: &mut rand::rngs::ThreadRng, operators: &Vec<&str>) -> String {
        let num1 = generate_number(rng);
        let op = operators[rng.gen_range(0..operators.len())];
        let num2 = generate_number(rng);
        format!("{} {} {}", num1, op, num2)
    }

    // Generate the outer expression
    for _ in 0..3 {
        // Randomly decide whether to add a nested expression
        if rng.gen_bool(0.5) {
            expression.push('(');
            expression.push_str(&generate_simple_expression(&mut rng, &operators));
            expression.push(')');
        } else {
            expression.push_str(&generate_number(&mut rng));
        }

        // Add operator
        let op = operators[rng.gen_range(0..operators.len())];
        expression.push_str(" ");
        expression.push_str(op);
        expression.push_str(" ");
    }

    // Add a final expression or number
    if rng.gen_bool(0.5) {
        expression.push('(');
        expression.push_str(&generate_simple_expression(&mut rng, &operators));
        expression.push(')');
    } else {
        expression.push_str(&generate_number(&mut rng));
    }

    expression
}