From 570da21956e2a3263d4231f5de4cc67dc085ecf7 Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 20 Oct 2026 17:42:50 -0700 Subject: [PATCH 1/5] refactor: replace duplicate eval code with macro --- src/nodes/eval.rs | 151 ++++++++++++++-------------------------------- 1 file changed, 46 insertions(+), 105 deletions(-) diff --git a/src/nodes/eval.rs b/src/nodes/eval.rs index 2ebfee0..2683745 100644 --- a/src/nodes/eval.rs +++ b/src/nodes/eval.rs @@ -89,105 +89,35 @@ fn collapse_toplevel(nodes: &mut Vec, mut end: usize) -> Result<(), Error> end -= 1; } - while let Some(operator) = nodes[start..end] - .iter() - .position(|n| *n == Node::Operator(Operator::Exp)) - { - let lhs = match nodes.remove(start + operator - 1) { - Node::Number(n) => n, - n => return Err(Error::InvalidOperand(n)), - }; - end -= 1; - match nodes.get_mut(start + operator) { - None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), + macro_rules! collapse_operator { + ($operator:expr) => { + while let Some(operator) = nodes[start..end] + .iter() + .position(|n| *n == Node::Operator($operator)) + { + let lhs = match nodes.remove(start + operator - 1) { + Node::Number(n) => n, + n => return Err(Error::InvalidOperand(n)), + }; + end -= 1; + match nodes.get_mut(start + operator) { + None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), - Some(Node::Number(n)) => n.pow_from(lhs), - Some(n) => return Err(Error::InvalidOperand(n.clone())), - }; + Some(Node::Number(n)) => $operator.for_float()(n, lhs), + Some(n) => return Err(Error::InvalidOperand(n.clone())), + }; - nodes.remove(start + operator - 1); - end -= 1; + nodes.remove(start + operator - 1); + end -= 1; + } + }; } - while let Some(operator) = nodes[start..end] - .iter() - .position(|n| *n == Node::Operator(Operator::Div)) - { - let lhs = match nodes.remove(start + operator - 1) { - Node::Number(n) => n, - n => return Err(Error::InvalidOperand(n)), - }; - end -= 1; - match nodes.get_mut(start + operator) { - None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), - - Some(Node::Number(n)) => n.div_from(lhs), - Some(n) => return Err(Error::InvalidOperand(n.clone())), - }; - - nodes.remove(start + operator - 1); - end -= 1; - } - - while let Some(operator) = nodes[start..end] - .iter() - .position(|n| *n == Node::Operator(Operator::Mult)) - { - let lhs = match nodes.remove(start + operator - 1) { - Node::Number(n) => n, - n => return Err(Error::InvalidOperand(n)), - }; - end -= 1; - match nodes.get_mut(start + operator) { - None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), - - Some(Node::Number(n)) => n.mul_from(lhs), - Some(n) => return Err(Error::InvalidOperand(n.clone())), - }; - - nodes.remove(start + operator - 1); - end -= 1; - } - - while let Some(operator) = nodes[start..end] - .iter() - .position(|n| *n == Node::Operator(Operator::Minus)) - { - let lhs = match nodes.remove(start + operator - 1) { - Node::Number(n) => n, - n => return Err(Error::InvalidOperand(n)), - }; - end -= 1; - match nodes.get_mut(start + operator) { - None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), - - Some(Node::Number(n)) => n.sub_from(lhs), - Some(n) => return Err(Error::InvalidOperand(n.clone())), - }; - - nodes.remove(start + operator - 1); - end -= 1; - } - - while let Some(operator) = nodes[start..end] - .iter() - .position(|n| *n == Node::Operator(Operator::Plus)) - { - let lhs = match nodes.remove(start + operator - 1) { - Node::Number(n) => n, - n => return Err(Error::InvalidOperand(n)), - }; - end -= 1; - match nodes.get_mut(start + operator) { - None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), - - Some(Node::Number(n)) => n.add_from(lhs), - Some(n) => return Err(Error::InvalidOperand(n.clone())), - }; - - nodes.remove(start + operator - 1); - end -= 1; - } + collapse_operator!(Operator::Exp); + collapse_operator!(Operator::Div); + collapse_operator!(Operator::Mult); + collapse_operator!(Operator::Minus); + collapse_operator!(Operator::Plus); if nodes.len() > 1 { nodes.remove(end); @@ -200,18 +130,29 @@ fn collapse_toplevel(nodes: &mut Vec, mut end: usize) -> Result<(), Error> impl Function { fn for_float(&self) -> impl FnMut(&mut Float) { match self { - Function::Sqrt => Float::sqrt_mut, + Self::Sqrt => Float::sqrt_mut, - Function::Ln => Float::ln_mut, + Self::Ln => Float::ln_mut, - Function::Sine => Float::sin_mut, - Function::ArcSine => Float::asin_mut, - Function::Cosine => Float::cos_mut, - Function::ArcCosine => Float::acos_mut, - Function::Tangent => Float::tan_mut, - Function::ArcTangent => Float::atan_mut, - Function::Secant => Float::sec_mut, - Function::CoSecant => Float::csc_mut, + Self::Sine => Float::sin_mut, + Self::ArcSine => Float::asin_mut, + Self::Cosine => Float::cos_mut, + Self::ArcCosine => Float::acos_mut, + Self::Tangent => Float::tan_mut, + Self::ArcTangent => Float::atan_mut, + Self::Secant => Float::sec_mut, + Self::CoSecant => Float::csc_mut, + } + } +} +impl Operator { + fn for_float(&self) -> impl FnMut(&mut Float, Float) { + match self { + Self::Plus => Float::add_from, + Self::Minus => Float::sub_from, + Self::Mult => Float::mul_from, + Self::Div => Float::div_from, + Self::Exp => Float::pow_from, } } } From 56e8fbca99ad32f8abbe933ccdfb5176d94aa7d1 Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 20 Oct 2026 17:58:57 -0700 Subject: [PATCH 2/5] refactor: nicer operator/function eval functions --- src/nodes/eval.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/nodes/eval.rs b/src/nodes/eval.rs index 2683745..cbc139b 100644 --- a/src/nodes/eval.rs +++ b/src/nodes/eval.rs @@ -83,7 +83,7 @@ fn collapse_toplevel(nodes: &mut Vec, mut end: usize) -> Result<(), Error> return Err(Error::MissingArgument(node.clone())); }; - function.for_float()(n); + function.apply_to(n); nodes.remove(start + index); end -= 1; @@ -103,7 +103,7 @@ fn collapse_toplevel(nodes: &mut Vec, mut end: usize) -> Result<(), Error> match nodes.get_mut(start + operator) { None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), - Some(Node::Number(n)) => $operator.for_float()(n, lhs), + Some(Node::Number(n)) => $operator.apply_to(n, lhs), Some(n) => return Err(Error::InvalidOperand(n.clone())), }; @@ -128,31 +128,31 @@ fn collapse_toplevel(nodes: &mut Vec, mut end: usize) -> Result<(), Error> } impl Function { - fn for_float(&self) -> impl FnMut(&mut Float) { + fn apply_to(&self, n: &mut Float) { match self { - Self::Sqrt => Float::sqrt_mut, + Self::Sqrt => Float::sqrt_mut(n), - Self::Ln => Float::ln_mut, + Self::Ln => Float::ln_mut(n), - Self::Sine => Float::sin_mut, - Self::ArcSine => Float::asin_mut, - Self::Cosine => Float::cos_mut, - Self::ArcCosine => Float::acos_mut, - Self::Tangent => Float::tan_mut, - Self::ArcTangent => Float::atan_mut, - Self::Secant => Float::sec_mut, - Self::CoSecant => Float::csc_mut, + Self::Sine => Float::sin_mut(n), + Self::ArcSine => Float::asin_mut(n), + Self::Cosine => Float::cos_mut(n), + Self::ArcCosine => Float::acos_mut(n), + Self::Tangent => Float::tan_mut(n), + Self::ArcTangent => Float::atan_mut(n), + Self::Secant => Float::sec_mut(n), + Self::CoSecant => Float::csc_mut(n), } } } impl Operator { - fn for_float(&self) -> impl FnMut(&mut Float, Float) { + fn apply_to(&self, rhs: &mut Float, lhs: Float) { match self { - Self::Plus => Float::add_from, - Self::Minus => Float::sub_from, - Self::Mult => Float::mul_from, - Self::Div => Float::div_from, - Self::Exp => Float::pow_from, + Self::Plus => Float::add_from(rhs, lhs), + Self::Minus => Float::sub_from(rhs, lhs), + Self::Mult => Float::mul_from(rhs, lhs), + Self::Div => Float::div_from(rhs, lhs), + Self::Exp => Float::pow_from(rhs, lhs), } } } From 9bd0318702e2b421b0cbf652798393f13cff9222 Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 20 Oct 2026 18:00:08 -0700 Subject: [PATCH 3/5] refactor: make Operator/Function ordering consistent --- src/nodes/eval.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/nodes/eval.rs b/src/nodes/eval.rs index cbc139b..25fe190 100644 --- a/src/nodes/eval.rs +++ b/src/nodes/eval.rs @@ -127,6 +127,17 @@ fn collapse_toplevel(nodes: &mut Vec, mut end: usize) -> Result<(), Error> Ok(()) } +impl Operator { + fn apply_to(&self, rhs: &mut Float, lhs: Float) { + match self { + Self::Plus => Float::add_from(rhs, lhs), + Self::Minus => Float::sub_from(rhs, lhs), + Self::Mult => Float::mul_from(rhs, lhs), + Self::Div => Float::div_from(rhs, lhs), + Self::Exp => Float::pow_from(rhs, lhs), + } + } +} impl Function { fn apply_to(&self, n: &mut Float) { match self { @@ -145,17 +156,6 @@ impl Function { } } } -impl Operator { - fn apply_to(&self, rhs: &mut Float, lhs: Float) { - match self { - Self::Plus => Float::add_from(rhs, lhs), - Self::Minus => Float::sub_from(rhs, lhs), - Self::Mult => Float::mul_from(rhs, lhs), - Self::Div => Float::div_from(rhs, lhs), - Self::Exp => Float::pow_from(rhs, lhs), - } - } -} #[cfg(test)] mod tests { From 0ede27e9272de4b3e21a08f1ee2d2f90cbd82ede Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 20 Oct 2026 19:26:14 -0700 Subject: [PATCH 4/5] refactor: rename confusing index variable --- src/nodes/eval.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nodes/eval.rs b/src/nodes/eval.rs index 25fe190..33c606b 100644 --- a/src/nodes/eval.rs +++ b/src/nodes/eval.rs @@ -91,23 +91,23 @@ fn collapse_toplevel(nodes: &mut Vec, mut end: usize) -> Result<(), Error> macro_rules! collapse_operator { ($operator:expr) => { - while let Some(operator) = nodes[start..end] + while let Some(index) = nodes[start..end] .iter() .position(|n| *n == Node::Operator($operator)) { - let lhs = match nodes.remove(start + operator - 1) { + let lhs = match nodes.remove(start + index - 1) { Node::Number(n) => n, n => return Err(Error::InvalidOperand(n)), }; end -= 1; - match nodes.get_mut(start + operator) { - None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + operator)), + match nodes.get_mut(start + index) { + None => return Err(Error::OutOfBounds(Nodes(nodes.clone()), start + index)), Some(Node::Number(n)) => $operator.apply_to(n, lhs), Some(n) => return Err(Error::InvalidOperand(n.clone())), }; - nodes.remove(start + operator - 1); + nodes.remove(start + index - 1); end -= 1; } }; From f1f9c11a046b0d7fb2a25e80ccce8d7d7f73dced Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 20 Oct 2026 21:27:40 -0700 Subject: [PATCH 5/5] feat: use more precise pi with pretty_eval, I still get: -9.194801691 * 10^-618 but at least that's more accurate than the prior: 1.224646799 * 10^-16 --- src/nodes/parse.rs | 7 +++++-- src/nodes/tests.rs | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/nodes/parse.rs b/src/nodes/parse.rs index 96d4236..e3da6e6 100644 --- a/src/nodes/parse.rs +++ b/src/nodes/parse.rs @@ -1,7 +1,10 @@ use core::f64; use std::str::FromStr; -use rug::{Float, float::ParseFloatError}; +use rug::{ + Float, + float::{self, ParseFloatError}, +}; use crate::nodes::{Function, Node, Nodes, Operator}; @@ -10,7 +13,7 @@ impl Node { Ok(match s { "c" => Self::from(299_792_458), "A" => Self::from(6.02214076e23), - "pi" | "π" => Self::from(f64::consts::PI), + "pi" | "π" => Self::from(float::Constant::Pi), // Planck's constant, in J*s (joule-seconds) "h" => Self::from(6.62607015e-34), "e" => Self::from(f64::consts::E), diff --git a/src/nodes/tests.rs b/src/nodes/tests.rs index 15b6c21..2f22d8a 100644 --- a/src/nodes/tests.rs +++ b/src/nodes/tests.rs @@ -51,3 +51,8 @@ fn constant_implicit_mult() { assert_eq!(eval("2pi"), 6.283185307179586); assert_eq!(eval("pi2"), 6.283185307179586); } + +#[test] +fn pi_precision() { + assert_eq!(eval("sin(pi)"), 0.) +}