#[cfg(not(feature = "std"))]
use alloc::{string::String, string::ToString};
use crate::ast::Expr;
use crate::compiler::CompiledExpr;
use crate::error::{CompileError, EvalError, ParseError};
use crate::parser;
#[derive(Debug, Clone)]
pub struct Expression {
ast: Expr,
source: Option<String>,
}
impl Expression {
pub fn parse(input: &str) -> Result<Self, ParseError> {
let ast = parser::parse(input)?;
Ok(Expression {
ast,
source: Some(String::from(input)),
})
}
pub fn from_ast(ast: Expr) -> Self {
Expression { ast, source: None }
}
pub fn ast(&self) -> &Expr {
&self.ast
}
pub fn source(&self) -> Option<&str> {
self.source.as_deref()
}
pub fn compile(self, var_names: &[&str]) -> Result<Executable, CompileError> {
let compiled = CompiledExpr::compile(&self.ast, var_names)?;
Ok(Executable {
compiled,
source: self.source,
})
}
pub fn compile_no_vars(self) -> Result<Executable, CompileError> {
self.compile(&[])
}
}
#[derive(Debug, Clone)]
pub struct Executable {
compiled: CompiledExpr,
source: Option<String>,
}
impl Executable {
#[inline]
pub fn eval(&self, vars: &[f64]) -> Result<f64, EvalError> {
self.compiled.eval(vars)
}
#[inline]
pub fn eval_with_current(&self, current: f64, vars: &[f64]) -> Result<f64, EvalError> {
self.compiled.eval_with_current(current, vars)
}
pub fn num_variables(&self) -> usize {
self.compiled.num_variables()
}
pub fn uses_current_value(&self) -> bool {
self.compiled.uses_current_value()
}
pub fn source(&self) -> Option<&str> {
self.source.as_deref()
}
pub fn compiled(&self) -> &CompiledExpr {
&self.compiled
}
}
pub fn eval(expr: &str, var_names: &[&str], vars: &[f64]) -> Result<f64, EvalError> {
let parsed = Expression::parse(expr).map_err(|e| EvalError::MathError(e.to_string()))?;
let compiled = parsed
.compile(var_names)
.map_err(|e| EvalError::MathError(e.to_string()))?;
compiled.eval(vars)
}
pub fn eval_with_current(
expr: &str,
current: f64,
var_names: &[&str],
vars: &[f64],
) -> Result<f64, EvalError> {
let parsed = Expression::parse(expr).map_err(|e| EvalError::MathError(e.to_string()))?;
let compiled = parsed
.compile(var_names)
.map_err(|e| EvalError::MathError(e.to_string()))?;
compiled.eval_with_current(current, vars)
}