diff --git a/src/formatting.rs b/src/formatting.rs index b157564..d229d54 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -2,9 +2,7 @@ use rug::Float; use crate::nodes::{self, Error}; -pub const MAX_DIGITS: usize = 10; - -pub fn pretty_eval(mut input: String) -> Result { +pub fn pretty_eval(mut input: String, max_digits: usize) -> Result { // no need to remove the characters here, // as we ignore the them in all other logic already let should_add_back_alignment = input.contains('&'); @@ -21,7 +19,7 @@ pub fn pretty_eval(mut input: String) -> Result { let result = nodes::eval(&input)?; let display_digits = get_significant_digits(&result) - .min(MAX_DIGITS) + .min(max_digits) .max(if result >= 1000 || result < 1 { 1 } else if result >= 100 { diff --git a/src/main.rs b/src/main.rs index 32f04a3..aace0d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ fn main() -> ExitCode { } }; - match formatting::pretty_eval(input) { + match formatting::pretty_eval(input, 10) { Ok(s) => println!("{s}"), Err(e) => { diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index da7c34e..d8e05d9 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -4,7 +4,7 @@ use std::fmt::{Debug, Display}; -use rug::Float; +use rug::{Assign, Float}; mod eval; pub mod parse; @@ -53,6 +53,14 @@ impl Display for Node { Debug::fmt(&self, f) } } +impl From for Node +where + Float: Assign, +{ + fn from(value: T) -> Self { + Self::Number(Float::with_val(PREC, value)) + } +} #[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum Operator { diff --git a/src/nodes/parse.rs b/src/nodes/parse.rs index 07f6d4c..22a0518 100644 --- a/src/nodes/parse.rs +++ b/src/nodes/parse.rs @@ -8,11 +8,12 @@ use crate::nodes::{Function, Node, Nodes, Operator, PREC}; impl Node { fn from_name(s: &str) -> Result { Ok(match s { - "c" => Self::Number(Float::with_val(PREC, 299_792_458)), - "A" => Self::Number(Float::with_val(PREC, 6.02214076e23)), - "pi" => Self::Number(Float::with_val(PREC, f64::consts::PI)), + "c" => Self::from(299_792_458), + "A" => Self::from(6.02214076e23), + "pi" => Self::from(f64::consts::PI), // Planck's constant, in J*s (joule-seconds) - "h" => Self::Number(Float::with_val(PREC, 6.62607015e-34)), + "h" => Self::from(6.62607015e-34), + "e" => Self::from(f64::consts::E), _ => Self::Function(s.parse()?), }) } @@ -70,10 +71,14 @@ impl FromStr for Nodes { fn from_str(s: &str) -> Result { /// A superset of `char::is_numeric` /// that includes other characters parseable into numbers, - /// such as `.` (for decimal) and `e` for scientific notation. + /// now only `.` (for decimals) + /// + /// since including `e` makes it not parsable as a constant, + /// this function effectively only checks if the character can *start* a number + /// (since a number cannot start with `e`) fn is_number_char(c: char) -> bool { match c { - '.' | 'e' => true, + '.' => true, c => c.is_numeric(), } } @@ -114,8 +119,10 @@ impl FromStr for Nodes { } ParsingState::ReadingNumber(start_index) => { if !is_number_char(c) - // don't stop reading number on a special case, eg "1e-1" - && !(filtered_string.chars().nth(i - 1) == Some('e') && c == '-') + && !(c == 'e' && filtered_string.chars().nth(i + 1).is_some_and(char::is_numeric)) + // don't stop reading number on special case eg "1e-1" + && !(c == 'e' && filtered_string.chars().nth(i + 1).is_some_and(|c| c == '-')) + && !(c == '-' && filtered_string.chars().nth(i - 1).is_some_and(|c| c == 'e')) { nodes.push(Node::Number( Float::parse(&filtered_string[start_index..i]) @@ -196,10 +203,23 @@ fn test() { &[ Node::Function(Function::Sine), Node::ParenthesisStart, - Node::Number(Float::with_val(PREC, 2)), + Node::from(2), Node::ParenthesisEnd, Node::Operator(Operator::Plus), - Node::Number(Float::with_val(PREC, 1)) + Node::from(1), + ], + ) +} +#[test] +fn test_e() { + assert_eq!( + Nodes::from_str("5e * 5").unwrap().as_ref(), + &[ + Node::from(5), + Node::Operator(Operator::Mult), + Node::from(f64::consts::E), + Node::Operator(Operator::Mult), + Node::from(5), ], ) }