From de52f18788809614dd2db56fa777ad1491072b8d Mon Sep 17 00:00:00 2001 From: Ben Ruijl Date: Fri, 7 Jun 2024 12:05:42 +0200 Subject: [PATCH] Support parsing floats in scientific notation --- src/coefficient.rs | 2 +- src/parser.rs | 74 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/coefficient.rs b/src/coefficient.rs index 69533687..7a95c6ab 100644 --- a/src/coefficient.rs +++ b/src/coefficient.rs @@ -1353,7 +1353,7 @@ mod test { ); assert_eq!( r, - "1.5707963267948966192313216916397514420985846996875529104874722*x+21.472450319416851" + "1.5707963267948966192313216916397514420985846996875529104874722*x+21.472450421034925" ); } diff --git a/src/parser.rs b/src/parser.rs index 28b3a774..5ee38801 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -650,7 +650,7 @@ impl Token { let ops = ['\0', '^', '+', '*', '-', '(', ')', '/', ',', '[', ']']; let whitespace = [' ', '\t', '\n', '\r', '\\']; - let forbidden = [';', ':', '&', '!', '%']; + let forbidden = [';', ':', '&', '!', '%', '.']; let mut char_iter = input.chars(); let mut c = char_iter.next().unwrap_or('\0'); // add EOF as a token @@ -680,12 +680,55 @@ impl Token { } } ParseState::Number => { - if c != '_' && c != ' ' && c != '.' && !c.is_ascii_digit() { + let mut last_digit_is_exp = false; + loop { + if c.is_ascii_digit() { + id_buffer.push(c); + c = char_iter.next().unwrap_or('\0'); + last_digit_is_exp = false; + continue; + } + + if last_digit_is_exp && c != '-' && c != '+' { + // input cannot be a floating point number + // add a multiplication operator, e.g 2.2ex => 2.2*ex + + let e = id_buffer.pop().unwrap(); + state = ParseState::Any; + stack.push(Token::Number(id_buffer.as_str().into())); + id_buffer.clear(); + + extra_ops.push(e); + extra_ops.push(c); + c = '*'; + + break; + } + + if c == '\0' { + last_digit_is_exp = false; + break; + } + + let digit_is_exp = c == 'e' || c == 'E'; + + if c != '_' && c != ' ' { + if !digit_is_exp && !last_digit_is_exp && c != '.' { + break; + } + + id_buffer.push(c); + } + + last_digit_is_exp = digit_is_exp; + + c = char_iter.next().unwrap_or('\0'); + } + + if !last_digit_is_exp { state = ParseState::Any; stack.push(Token::Number(id_buffer.as_str().into())); id_buffer.clear(); - } else if c != '_' && c != ' ' { - id_buffer.push(c); } } ParseState::RationalPolynomial => { @@ -1247,7 +1290,13 @@ impl Token { mod test { use std::sync::Arc; - use crate::{atom::Atom, domains::integer::Z, parser::Token, state::State}; + use crate::{ + atom::Atom, + domains::integer::Z, + parser::Token, + printer::{AtomPrinter, PrintOptions}, + state::State, + }; #[test] fn pow() { @@ -1271,13 +1320,24 @@ mod test { fn liberal() { let input = Atom::parse( "89233_21837281 x - ^2 / y + 5", + ^2 / y + 5 + 5x", ) .unwrap(); - let res = Atom::parse("8923321837281*x^2*y^-1+5").unwrap(); + let res = Atom::parse("8923321837281*x^2*y^-1+5+5x").unwrap(); assert_eq!(input, res); } + #[test] + fn float() { + let input = Atom::parse("1.2x+1e-5+1e+5 1.1234e23 +2exp(5)").unwrap(); + + let r = format!( + "{}", + AtomPrinter::new_with_options(input.as_view(), PrintOptions::file()) + ); + assert_eq!(r, "1.2000000000000000*x+2*exp(5)+1.1234000000000000e28"); + } + #[test] fn square_bracket_function() { let input = Atom::parse("v1 [v1, v2]+5 + v1[]").unwrap();