From 19b2379ff0153e0e76c246375e0fdcb767e5742f Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Mon, 30 Jun 2025 20:43:33 +0200 Subject: [PATCH] support floating point numbers in math mode --- worf/src/lib/modes/math.rs | 47 ++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/worf/src/lib/modes/math.rs b/worf/src/lib/modes/math.rs index 992ae8d..97504d6 100644 --- a/worf/src/lib/modes/math.rs +++ b/worf/src/lib/modes/math.rs @@ -63,7 +63,8 @@ impl ItemProvider for MathProvider { #[derive(Debug, Clone, Copy)] enum Token { - Num(i64), + Int(i64), + Float(f64), Op(char), ShiftLeft, ShiftRight, @@ -91,12 +92,16 @@ fn normalize_bases(expr: &str) -> String { } fn insert_implicit_multiplication(tokens: &mut VecDeque, last_token: Option<&Token>) { - if matches!(last_token, Some(Token::Num(_) | Token::Op(')'))) { + if matches!( + last_token, + Some(Token::Int(_) | Token::Float(_) | Token::Op(')')) + ) { tokens.push_back(Token::Op('*')); } } /// Tokenize a normalized expression string into tokens +#[allow(clippy::too_many_lines)] fn tokenize(expr: &str) -> Result, String> { let mut tokens = VecDeque::new(); let chars: Vec = expr.chars().collect(); @@ -139,7 +144,7 @@ fn tokenize(expr: &str) -> Result, String> { } } - // Single-character operators, parentheses, or digits + // Single-character operators, parentheses, or digits/float match c { '+' | '-' | '*' | '/' | '&' | '|' | '^' => { let token = Token::Op(c); @@ -160,7 +165,7 @@ fn tokenize(expr: &str) -> Result, String> { last_token = Some(token); i += 1; } - '0'..='9' => { + '0'..='9' | '.' => { // Only insert implicit multiplication if the last token is ')' and the last token in tokens is not already an operator (except ')') if let Some(Token::Op(')')) = last_token { if let Some(Token::Op(op)) = tokens.back() { @@ -172,14 +177,35 @@ fn tokenize(expr: &str) -> Result, String> { } } let start = i; - while i < chars.len() && chars[i].is_ascii_digit() { + let mut has_dot = c == '.'; + if c == '.' && (i + 1 >= chars.len() || !chars[i + 1].is_ascii_digit()) { + return Err("Invalid float literal".to_owned()); + } + i += 1; + while i < chars.len() + && (chars[i].is_ascii_digit() || (!has_dot && chars[i] == '.')) + { + if chars[i] == '.' { + has_dot = true; + } i += 1; } let num_str: String = chars[start..i].iter().collect(); - let n = num_str.parse::().unwrap(); - let token = Token::Num(n); - tokens.push_back(token); - last_token = Some(token); + if has_dot { + let n = num_str + .parse::() + .map_err(|_| "Invalid float literal".to_owned())?; + let token = Token::Float(n); + tokens.push_back(token); + last_token = Some(token); + } else { + let n = num_str + .parse::() + .map_err(|_| "Invalid integer literal".to_owned())?; + let token = Token::Int(n); + tokens.push_back(token); + last_token = Some(token); + } } _ => return Err("Invalid character in expression".to_owned()), } @@ -242,7 +268,8 @@ fn eval_expr(tokens: &mut VecDeque) -> Result { while let Some(token) = tokens.pop_front() { match token { - Token::Num(n) => values.push(Value::Int(n)), + Token::Int(n) => values.push(Value::Int(n)), + Token::Float(f) => values.push(Value::Float(f)), Token::Op('(') => { ops.push(Token::Op('(')); }