use crate::ast::HistoryIndexKind;
use std::ops::{Add, Div, Mul, Sub};
mod f64;
mod i64;
mod u64;
#[derive(Debug, thiserror::Error)]
pub enum ArithmeticError {
#[error("overflow")]
Overflow,
#[error("underflow")]
Underflow,
#[error("attempt to divide by 0")]
DivideBy0,
}
#[derive(Debug, thiserror::Error)]
pub enum BasicError<T: std::fmt::Debug, E: 'static + std::error::Error> {
#[error(transparent)]
Arithmetic(#[from] ArithmeticError),
#[error("operation `{0}` not implemented for {}", std::any::type_name::<T>())]
NotImplemented(&'static str, std::marker::PhantomData<T>),
#[error("parsing: {0}")]
Parse(#[source] E),
#[error("{0:?} history index {1} out of bounds: [0..{2})")]
HistoryOOB(HistoryIndexKind, usize, usize),
}
pub(crate) fn not_implemented<T, E>(symbol: &'static str) -> Result<T, BasicError<T, E>>
where
T: std::fmt::Debug,
E: std::error::Error,
{
Err(BasicError::NotImplemented(symbol, std::marker::PhantomData))
}
impl<T, E> CalcableError for BasicError<T, E>
where
T: std::fmt::Debug,
E: 'static + std::error::Error,
{
fn unimplemented(operation: &'static str) -> Self {
Self::NotImplemented(operation, std::marker::PhantomData)
}
fn history_out_of_bounds(
kind: HistoryIndexKind,
requested_index: usize,
history_len: usize,
) -> Self {
Self::HistoryOOB(kind, requested_index, history_len)
}
}
pub trait CalcableError {
fn unimplemented(operation: &'static str) -> Self;
fn history_out_of_bounds(
kind: HistoryIndexKind,
requested_index: usize,
history_len: usize,
) -> Self;
}
pub trait Calcable:
Clone
+ std::fmt::Display
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
{
type Err: std::error::Error + CalcableError;
const E: Option<Self>;
const PI: Option<Self>;
fn parse_binary(s: &str) -> Result<Self, <Self as Calcable>::Err>;
fn parse_octal(s: &str) -> Result<Self, <Self as Calcable>::Err>;
fn parse_decimal(s: &str) -> Result<Self, <Self as Calcable>::Err>;
fn parse_hex(s: &str) -> Result<Self, <Self as Calcable>::Err>;
fn from_f32(f: f32) -> Option<Self>;
fn neg(self) -> Option<Self>;
fn not(self) -> Option<Self>;
fn add(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn sub(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn mul(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn div(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn trunc_div(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn pow(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn rem(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn shl(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn shr(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn rotate_left(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn rotate_right(self, other: Self) -> Result<Self, <Self as Calcable>::Err>;
fn bit_and(self, other: Self) -> Option<Self>;
fn bit_or(self, other: Self) -> Option<Self>;
fn bit_xor(self, other: Self) -> Option<Self>;
fn abs(self) -> Option<Self>;
fn ceil(self) -> Option<Self>;
fn floor(self) -> Option<Self>;
fn round(self) -> Option<Self>;
fn sin(self) -> Option<Self>;
fn cos(self) -> Option<Self>;
fn tan(self) -> Option<Self>;
fn sinh(self) -> Option<Self>;
fn cosh(self) -> Option<Self>;
fn tanh(self) -> Option<Self>;
fn asin(self) -> Option<Self>;
fn acos(self) -> Option<Self>;
fn atan(self) -> Option<Self>;
fn asinh(self) -> Option<Self>;
fn acosh(self) -> Option<Self>;
fn atanh(self) -> Option<Self>;
fn rad(self) -> Option<Self> {
Some(Self::PI? / Self::from_f32(180.0)? * self)
}
fn deg(self) -> Option<Self> {
Some(Self::from_f32(180.0)? / Self::PI? * self)
}
fn sqrt(self) -> Option<Self>;
fn cbrt(self) -> Option<Self>;
fn log(self) -> Option<Self> {
Some(self.ln()? / Self::from_f32(10.0)?.ln()?)
}
fn lg(self) -> Option<Self> {
Some(self.ln()? / Self::from_f32(2.0)?.ln()?)
}
fn ln(self) -> Option<Self>;
fn exp(self) -> Option<Self> {
Self::E?.pow(self).ok()
}
}
pub(crate) fn clean_input(s: &str, leading: &str) -> String {
let mut input = String::with_capacity(s.len());
input.extend(s.chars().filter(|&c| c != '_'));
input.trim_start_matches(leading).to_string()
}