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 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<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],
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 {
RPNCalc {
StackCalc {
stack: [Decimal::default(); SS],
cur: 0,
index: 0,
}
}
}
impl<const SS: usize, const DS: usize, E: PrimInt + Unsigned> RPNCalc<SS, DS, E> {
pub fn enter(&mut self, val: Decimal<DS, E>) {
self.stack[self.cur] = val;
if self.cur == SS - 1 {
self.cur = 0;
} else {
self.cur += 1;
impl From<DecimalError> for StackCalcError {
fn from(val: DecimalError) -> Self {
StackCalcError::DecimalError(val)
}
}
impl From<Infallible> for StackCalcError {
fn from(_: Infallible) -> Self {
unreachable!()
}
}
impl<const SS: usize, const DS: usize, E: PrimInt + Unsigned> StackCalc<SS, DS, E> {
pub fn push<V>(&mut self, val: V) -> Result<(), StackCalcError>
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) -> Decimal<DS, E> {
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<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>, DecimalError> {
let a = self.pop();
let b = self.pop();
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 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<const SS: usize, const DS: usize, E: PrimInt + Unsigned> RPNCalc<SS, DS, E>
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(())
}
}

View File

@@ -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"),
}
}
}