strict stack op

This commit is contained in:
2025-07-18 21:19:30 +01:00
parent dc61fe31b0
commit 8bff8dda04
2 changed files with 75 additions and 33 deletions

View File

@@ -1,46 +1,83 @@
use core::{convert::Infallible, error::Error, fmt::Display};
use num_traits::{PrimInt, Unsigned}; use num_traits::{PrimInt, Unsigned};
use crate::{Decimal, DecimalError}; 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)] #[derive(Debug, Clone)]
pub struct RPNCalc<const SS: usize = 2, const DS: usize = 5, E = u16> { pub struct StackCalc<const SS: usize = 2, const DS: usize = 5, E = u16> {
stack: [Decimal<DS, E>; SS], stack: [Decimal<DS, E>; SS],
cur: usize, index: usize,
} }
impl<const SS: usize, const DS: usize, E: PrimInt> Default for RPNCalc<SS, DS, E> { impl<const SS: usize, const DS: usize, E: PrimInt> Default for StackCalc<SS, DS, E> {
fn default() -> Self { fn default() -> Self {
RPNCalc { StackCalc {
stack: [Decimal::default(); SS], stack: [Decimal::default(); SS],
cur: 0, index: 0,
} }
} }
} }
impl<const SS: usize, const DS: usize, E: PrimInt + Unsigned> RPNCalc<SS, DS, E> { impl From<DecimalError> for StackCalcError {
pub fn enter(&mut self, val: Decimal<DS, E>) { fn from(val: DecimalError) -> Self {
self.stack[self.cur] = val; StackCalcError::DecimalError(val)
if self.cur == SS - 1 {
self.cur = 0;
} else {
self.cur += 1;
} }
} }
pub fn pop(&mut self) -> Decimal<DS, E> { impl From<Infallible> for StackCalcError {
self.cur = if self.cur == 0 { SS - 1 } else { self.cur - 1 }; fn from(_: Infallible) -> Self {
let ret = self.stack[self.cur]; unreachable!()
self.stack[self.cur] = Decimal::default(); }
ret
} }
pub fn add(&mut self) -> Result<Decimal<DS, E>, DecimalError> { impl<const SS: usize, const DS: usize, E: PrimInt + Unsigned> StackCalc<SS, DS, E> {
let a = self.pop(); pub fn push<V>(&mut self, val: V) -> Result<(), StackCalcError>
let b = self.pop(); where
V: TryInto<Decimal<DS, E>>,
StackCalcError: From<<V as TryInto<Decimal<DS, E>>>::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) -> Result<Decimal<DS, E>, StackCalcError> {
if self.index == 0 {
return Err(StackCalcError::StackUnderflow);
}
self.index -= 1;
Ok(self.stack[self.index])
}
pub fn add(&mut self) -> Result<Decimal<DS, E>, StackCalcError> {
let a = self.pop()?;
let b = self.pop()?;
let a = f64::try_from(a)?; let a = f64::try_from(a)?;
let b = f64::try_from(b)?; let b = f64::try_from(b)?;
let res = Decimal::try_from(a + b)?; let res = Decimal::try_from(a + b)?;
self.enter(res); self.push(res)?;
Ok(res) Ok(res)
} }
} }
@@ -49,20 +86,25 @@ impl<const SS: usize, const DS: usize, E: PrimInt + Unsigned> RPNCalc<SS, DS, E>
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_add() -> Result<(), DecimalError> { fn test_add() -> Result<(), StackCalcError> {
let mut calc = RPNCalc::<3, 5>::default(); let mut calc = StackCalc::<3, 7>::default();
calc.enter(Decimal::try_from(1337.42)?); calc.push(Decimal::default())?;
calc.push(1337.42)?;
assert_eq!(calc.add()?, Decimal::try_from(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)?); assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0)?);
calc.enter(Decimal::try_from(2.0)?); calc.push(2.0)?;
calc.enter(Decimal::try_from(3.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(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!(
assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); calc.add().unwrap_err(),
assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); StackCalcError::StackUnderflow
assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0 + 5.0)?); ));
Ok(()) Ok(())
} }
} }

View File

@@ -17,8 +17,8 @@ impl Error for DecimalError {}
impl Display for DecimalError { impl Display for DecimalError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self { match self {
DecimalError::ExponentOverflow => f.write_str("Exponent overflow"), DecimalError::ExponentOverflow => f.write_str("exponent overflow"),
DecimalError::NotANumber => f.write_str("Not a number"), DecimalError::NotANumber => f.write_str("not a number"),
} }
} }
} }