use core::marker::PhantomData;
use crate::parser::{Backend, Binary, Parser, Syntax, Unary};
use crate::{Context, Error, ErrorKind, Number, Span};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum PercentSign {
#[default]
Remainder,
Percentage,
}
#[derive(Debug, Clone, Copy)]
pub struct Evaluator<'c, C: ?Sized, const ARGUMENTS: usize = 8> {
context: &'c C,
syntax: Syntax,
}
impl<'c, C: ?Sized, const ARGUMENTS: usize> Evaluator<'c, C, ARGUMENTS> {
pub const DEFAULT_MAX_DEPTH: usize = 32;
pub const fn new(context: &'c C) -> Self {
Self {
context,
syntax: Syntax {
max_depth: Self::DEFAULT_MAX_DEPTH,
percent_sign: PercentSign::Remainder,
implicit_multiplication: false,
},
}
}
pub const fn max_depth(mut self, max_depth: usize) -> Self {
self.syntax.max_depth = max_depth;
self
}
pub const fn percent_sign(mut self, percent_sign: PercentSign) -> Self {
self.syntax.percent_sign = percent_sign;
self
}
pub const fn implicit_multiplication(mut self, implicit_multiplication: bool) -> Self {
self.syntax.implicit_multiplication = implicit_multiplication;
self
}
pub(crate) const fn syntax(&self) -> Syntax {
self.syntax
}
pub fn evaluate<N: Number>(&self, input: &str) -> Result<N, Error>
where
C: Context<N>,
{
let evaluation = Evaluation {
context: self.context,
skipping: false,
failure: None,
number: PhantomData,
};
let (value, evaluation) = Parser::<_, ARGUMENTS>::parse(input, self.syntax, evaluation)?;
evaluation.failure.map_or(Ok(value), Err)
}
}
#[derive(Clone, Copy)]
struct Branch {
truth: Option<bool>,
skipping: bool,
}
struct Evaluation<'c, C: ?Sized, N> {
context: &'c C,
skipping: bool,
failure: Option<Error>,
number: PhantomData<N>,
}
impl<N: Number, C: Context<N> + ?Sized> Evaluation<'_, C, N> {
#[cold]
fn fail(&mut self, error: Error) -> N {
self.failure = Some(error);
self.skipping = true;
N::default()
}
fn compute(&mut self, span: Span, operation: impl FnOnce() -> Result<N, ErrorKind>) -> N {
if self.skipping {
return N::default();
}
match operation() {
Ok(value) => value,
Err(kind) => self.fail(Error::new(kind, span)),
}
}
fn truth(&mut self, value: N, span: Span) -> Option<bool> {
if self.skipping {
return None;
}
match value.is_zero() {
Ok(zero) => Some(!zero),
Err(kind) => {
self.fail(Error::new(kind, span));
None
}
}
}
fn restore(&mut self, branch: Branch) {
self.skipping = branch.skipping || self.failure.is_some();
}
}
impl<'i, N: Number, C: Context<N> + ?Sized> Backend<'i> for Evaluation<'_, C, N> {
type Value = N;
type Branch = Branch;
fn number(&mut self, literal: &'i str, span: Span) -> Result<N, Error> {
N::parse(literal).ok_or(Error::new(ErrorKind::InvalidNumber, span))
}
fn variable(&mut self, name: &'i str, span: Span) -> Result<N, Error> {
if self.skipping {
return Ok(N::default());
}
Ok(match self.context.variable(name) {
Some(value) => value,
None => self.fail(Error::new(ErrorKind::UnknownVariable, span)),
})
}
fn call(
&mut self,
name: &'i str,
name_span: Span,
arguments: &[N],
span: Span,
) -> Result<N, Error> {
if self.skipping {
return Ok(N::default());
}
Ok(match self.context.call(name, arguments) {
Some(Ok(value)) => value,
Some(Err(kind)) => self.fail(Error::new(kind, span)),
None => self.fail(Error::new(ErrorKind::UnknownFunction, name_span)),
})
}
fn unary(&mut self, operator: Unary, operand: N, span: Span) -> Result<N, Error> {
Ok(self.compute(span, || operator.apply(operand)))
}
fn binary(&mut self, operator: Binary, left: N, right: N, span: Span) -> Result<N, Error> {
Ok(self.compute(span, || operator.apply(left, right)))
}
fn logic(&mut self, or: bool, left: N, span: Span) -> Result<Branch, Error> {
let branch = Branch {
truth: self.truth(left, span),
skipping: self.skipping,
};
if branch.truth == Some(or) {
self.skipping = true;
}
Ok(branch)
}
fn logic_end(&mut self, or: bool, branch: Branch, right: N, span: Span) -> Result<N, Error> {
let truth = if branch.truth == Some(or) {
self.restore(branch);
Some(or)
} else {
self.truth(right, span)
};
Ok(match truth {
Some(truth) => self.compute(span, || N::from_bool(truth)),
None => N::default(),
})
}
fn condition(&mut self, condition: N, span: Span) -> Result<Branch, Error> {
let branch = Branch {
truth: self.truth(condition, span),
skipping: self.skipping,
};
if branch.truth == Some(false) {
self.skipping = true;
}
Ok(branch)
}
fn otherwise(&mut self, branch: Branch, _: Span) -> Result<Branch, Error> {
self.restore(branch);
if branch.truth == Some(true) {
self.skipping = true;
}
Ok(branch)
}
fn conditional_end(&mut self, branch: Branch, then: N, otherwise: N) -> N {
self.restore(branch);
if branch.truth == Some(true) {
then
} else {
otherwise
}
}
}
pub fn evaluate_with<N: Number, C: Context<N> + ?Sized>(
input: &str,
context: &C,
) -> Result<N, Error> {
Evaluator::<C>::new(context).evaluate(input)
}