turn slopes map into a vec to have multiple slopes for same variable, struct Compiler

This commit is contained in:
brevalferrari 2025-05-28 12:54:01 +02:00
parent 7852f2d5c5
commit 73fb1d52b5

View file

@ -7,7 +7,7 @@ use std::{
use derive_new::new;
use derive_wrapper::{AsRef, From};
use fasteval::{Compiler, EvalNamespace, Evaler, Instruction, Slab};
use fasteval::{Compiler as _, EvalNamespace, Evaler, Instruction, Slab};
use lazy_static::lazy_static;
use thiserror::Error;
@ -204,13 +204,13 @@ impl Type for Slope<'_> {
impl Token for Slope<'_> {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
context.slopes.insert(self.0.0, self.0.1.clone());
context.slopes.push((self.0.0, self.0.1.clone()));
context = self
.1
.0
.iter()
.try_fold(context, |context, t| t.apply(context))?;
context.slopes.remove(&self.0.0);
context.slopes.pop();
Ok(context)
}
}
@ -231,7 +231,6 @@ impl Clone for Expression {
}
}
#[cfg(test)]
impl PartialEq for Expression {
fn eq(&self, other: &Self) -> bool {
format!("{self:?}") == format!("{other:?}")
@ -254,13 +253,16 @@ impl FromStr for Expression {
}
}
#[derive(Clone)]
#[derive(Clone, new)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct Context {
#[new(default)]
pub result: Vec<f64>,
#[new(into_iter = "(char, f64)")]
pub variables: HashMap<char, f64>,
pub instrument: Expression,
pub slopes: HashMap<char, Expression>,
#[new(into_iter = "(char, Expression)")]
pub slopes: Vec<(char, Expression)>,
}
#[derive(Debug, Error)]
@ -282,9 +284,19 @@ pub enum CompilerError {
}
impl Context {
pub fn current_length(&self) -> Result<f64, CompilerError> {
self.eval(self.get_slope('l').map_err(CompilerError::from)?)
.map_err(Into::into)
pub fn current_length(&mut self) -> Result<f64, CompilerError> {
*self = self
.get_slopes_for('L')
.map_err(CompilerError::from)?
.iter()
.try_fold(
self.clone(),
|mut acc, slope| -> Result<Context, CompilerError> {
*acc.get_mut('L')? = self.eval(slope).map_err(Into::<CompilerError>::into)?;
Ok(acc)
},
)?;
Ok(*self.get('L')?)
}
pub fn get(&self, name: char) -> Result<&f64, VariableNotFoundError> {
@ -297,8 +309,17 @@ impl Context {
.ok_or(VariableNotFoundError(name))
}
pub fn get_slope(&self, name: char) -> Result<&Expression, SlopeNotFoundError> {
self.slopes.get(&name).ok_or(SlopeNotFoundError(name))
pub fn get_slopes_for(&self, var: char) -> Result<Vec<&Expression>, SlopeNotFoundError> {
let result: Vec<&Expression> = self
.slopes
.iter()
.filter_map(|(c, e)| (c == &var).then_some(e))
.collect();
if result.is_empty() {
return Err(SlopeNotFoundError(var));
} else {
Ok(result)
}
}
fn tick(&mut self) -> Result<(), CompilerError> {
@ -383,15 +404,56 @@ impl Context {
Ok(vec![0.0; (*self.get('t')? - curr_t) as usize])
}
}
pub fn finalize(self) -> Vec<f64> {
self.result
}
}
#[derive(From)]
struct Compiler(Context);
impl Compiler {
fn step(self, token: impl Token) -> Result<Self, CompilerError> {
token.apply(self.0).map(Into::into)
}
fn compile_all<'a>(
self,
tokens: impl IntoIterator<Item = impl Token>,
) -> Result<Vec<f64>, CompilerError> {
tokens
.into_iter()
.try_fold(self, |acc, token| acc.step(token))
.map(|c| c.0)
.map(Context::finalize)
}
}
#[cfg(test)]
mod tests {
use crate::compiler::Expression;
use super::Context;
#[test]
fn expression_is_clone() {
let expr: Expression = "1 + 5 / x".parse().unwrap();
assert_eq!(expr, expr.clone());
}
fn context_generator() -> Context {
Context::new(
[
('a', 5.0),
('t', 0.0),
('n', 0.0),
('N', 12.0),
('L', 0.0),
('l', 4.0),
('T', 60.0),
],
"sin(2*PI*(442+442*((n+1)/N))*t)".parse().unwrap(),
[('L', "2^(2-log_2(l))*(60/T)")].map(|(c, e)| (c, e.parse().unwrap())),
)
}
}