From 8bff8dda04eb060f1748768e4949a782004407ed Mon Sep 17 00:00:00 2001 From: Hexa Dust Date: Fri, 18 Jul 2025 21:19:30 +0100 Subject: [PATCH] strict stack op --- src/calc.rs | 104 ++++++++++++++++++++++++++++++++++++---------------- src/lib.rs | 4 +- 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/calc.rs b/src/calc.rs index 0d49217..510e91d 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -1,46 +1,83 @@ +use core::{convert::Infallible, error::Error, fmt::Display}; + use num_traits::{PrimInt, Unsigned}; use crate::{Decimal, DecimalError}; +#[derive(Debug)] +pub enum StackCalcError { + StackOverflow, + StackUnderflow, + DecimalError(DecimalError), +} + +impl Error for StackCalcError {} +impl Display for StackCalcError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + StackCalcError::StackOverflow => f.write_str("stack overflow"), + StackCalcError::StackUnderflow => f.write_str("stack underflow"), + StackCalcError::DecimalError(de) => de.fmt(f), + } + } +} + #[derive(Debug, Clone)] -pub struct RPNCalc { +pub struct StackCalc { stack: [Decimal; SS], - cur: usize, + index: usize, } -impl Default for RPNCalc { +impl Default for StackCalc { fn default() -> Self { - RPNCalc { + StackCalc { stack: [Decimal::default(); SS], - cur: 0, + index: 0, } } } -impl RPNCalc { - pub fn enter(&mut self, val: Decimal) { - self.stack[self.cur] = val; - if self.cur == SS - 1 { - self.cur = 0; - } else { - self.cur += 1; +impl From for StackCalcError { + fn from(val: DecimalError) -> Self { + StackCalcError::DecimalError(val) + } +} + +impl From for StackCalcError { + fn from(_: Infallible) -> Self { + unreachable!() + } +} + +impl StackCalc { + pub fn push(&mut self, val: V) -> Result<(), StackCalcError> + where + V: TryInto>, + StackCalcError: From<>>::Error>, + { + if self.index == SS { + return Err(StackCalcError::StackOverflow); } + self.stack[self.index] = val.try_into()?; + self.index += 1; + Ok(()) } - pub fn pop(&mut self) -> Decimal { - self.cur = if self.cur == 0 { SS - 1 } else { self.cur - 1 }; - let ret = self.stack[self.cur]; - self.stack[self.cur] = Decimal::default(); - ret + pub fn pop(&mut self) -> Result, StackCalcError> { + if self.index == 0 { + return Err(StackCalcError::StackUnderflow); + } + self.index -= 1; + Ok(self.stack[self.index]) } - pub fn add(&mut self) -> Result, DecimalError> { - let a = self.pop(); - let b = self.pop(); + pub fn add(&mut self) -> Result, StackCalcError> { + let a = self.pop()?; + let b = self.pop()?; let a = f64::try_from(a)?; let b = f64::try_from(b)?; let res = Decimal::try_from(a + b)?; - self.enter(res); + self.push(res)?; Ok(res) } } @@ -49,20 +86,25 @@ impl RPNCalc mod tests { use super::*; #[test] - fn test_add() -> Result<(), DecimalError> { - let mut calc = RPNCalc::<3, 5>::default(); - calc.enter(Decimal::try_from(1337.42)?); + fn test_add() -> Result<(), StackCalcError> { + let mut calc = StackCalc::<3, 7>::default(); + calc.push(Decimal::default())?; + calc.push(1337.42)?; assert_eq!(calc.add()?, Decimal::try_from(1337.42)?); - calc.enter(Decimal::try_from(1337.42)?); + calc.push(1337.42)?; assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0)?); - calc.enter(Decimal::try_from(2.0)?); - calc.enter(Decimal::try_from(3.0)?); + calc.push(2.0)?; + calc.push(3.0)?; + assert!(matches!( + calc.push(3.0).unwrap_err(), + StackCalcError::StackOverflow + )); assert_eq!(calc.add()?, Decimal::try_from(5.0)?); assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); - assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); - assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); - assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); - assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); + assert!(matches!( + calc.add().unwrap_err(), + StackCalcError::StackUnderflow + )); Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 1e3e90c..bf64f0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,8 +17,8 @@ impl Error for DecimalError {} impl Display for DecimalError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - DecimalError::ExponentOverflow => f.write_str("Exponent overflow"), - DecimalError::NotANumber => f.write_str("Not a number"), + DecimalError::ExponentOverflow => f.write_str("exponent overflow"), + DecimalError::NotANumber => f.write_str("not a number"), } } }