turn slopes map into a vec to have multiple slopes for same variable, struct Compiler
This commit is contained in:
parent
7852f2d5c5
commit
73fb1d52b5
1 changed files with 73 additions and 11 deletions
|
@ -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())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue