diff --git a/src/compiler.rs b/src/compiler.rs index aa00c99..95c660b 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,8 +1,15 @@ -use std::{collections::HashMap, fmt::Debug, str::FromStr}; +use std::{ + collections::{BTreeMap, HashMap}, + fmt::Debug, + str::FromStr, +}; use derive_new::new; use derive_wrapper::{AsRef, From}; -use fasteval::{Compiler, Instruction, Slab}; +use fasteval::{Compiler, Evaler, Instruction, Slab}; +use thiserror::Error; + +const SAMPLE_RATE: u16 = 48000; #[derive(From, AsRef)] #[cfg_attr(test, derive(Debug))] @@ -10,16 +17,12 @@ pub struct TokenVec<'a>(pub(crate) Vec>); #[cfg(not(test))] pub trait Token { - fn apply(&self, context: Context) -> Context { - todo!() - } + fn apply(&self, context: Context) -> Result; } #[cfg(test)] pub trait Token: Debug { - fn apply(&self, context: Context) -> Context { - todo!() - } + fn apply(&self, context: Context) -> Result; } #[cfg(test)] @@ -33,24 +36,45 @@ impl PartialEq for TokenVec<'_> { #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Silence; -impl Token for Silence {} +impl Token for Silence { + fn apply(&self, mut context: Context) -> Result { + context.render(None)?; + Ok(context) + } +} #[derive(Clone, Copy)] #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Marker; -impl Token for Marker {} +impl Token for Marker { + fn apply(&self, mut context: Context) -> Result { + context.result.clear(); + Ok(context) + } +} #[derive(Clone, Copy)] #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Note(pub u8); -impl Token for Note {} +impl Token for Note { + fn apply(&self, mut context: Context) -> Result { + let mut next = context.render(Some(self.0))?; + context.result.append(&mut next); + Ok(context) + } +} #[cfg_attr(test, derive(Debug, PartialEq))] pub struct VariableChange(pub char, pub Expression); -impl Token for VariableChange {} +impl Token for VariableChange { + fn apply(&self, mut context: Context) -> Result { + *context.get_mut(self.0)? = context.eval(&self.1)?; + Ok(context) + } +} #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Loop<'a>(pub LoopCount, pub TokenVec<'a>); @@ -67,17 +91,29 @@ impl Default for LoopCount { } } -impl Token for Loop<'_> {} +impl Token for Loop<'_> { + fn apply(&self, context: Context) -> Result { + todo!() + } +} #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Tuplet<'a>(pub TokenVec<'a>); -impl Token for Tuplet<'_> {} +impl Token for Tuplet<'_> { + fn apply(&self, context: Context) -> Result { + todo!() + } +} #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Slope<'a>(pub &'a VariableChange, pub TokenVec<'a>); -impl Token for Slope<'_> {} +impl Token for Slope<'_> { + fn apply(&self, context: Context) -> Result { + todo!() + } +} #[derive(new)] #[cfg_attr(any(debug_assertions, test), derive(Debug))] @@ -119,11 +155,112 @@ pub struct Context { pub slopes: HashMap, } +#[derive(Debug, Error)] +#[error("variable not found: {0}")] +pub struct VariableNotFoundError(char); + +#[derive(Debug, Error)] +#[error("expression not found: {0}")] +pub struct SlopeNotFoundError(char); + +#[derive(Debug, Error)] +pub enum CompilerError { + #[error("expression evaluation: {0:?}")] + FastEval(#[from] fasteval::Error), + #[error("{0}")] + VariableNotFound(#[from] VariableNotFoundError), + #[error("{0}")] + SlopeNotFound(#[from] SlopeNotFoundError), +} + impl Context { - pub fn current_length(&self) -> f64 { - todo!() + pub fn current_length(&self) -> Result { + self.eval(self.get_slope('l').map_err(CompilerError::from)?) + .map_err(Into::into) } - pub fn render(&self, n: Option) -> Vec { - todo!() + + pub fn get(&self, name: char) -> Result<&f64, VariableNotFoundError> { + self.variables.get(&name).ok_or(VariableNotFoundError(name)) + } + + pub fn get_mut(&mut self, name: char) -> Result<&mut f64, VariableNotFoundError> { + self.variables + .get_mut(&name) + .ok_or(VariableNotFoundError(name)) + } + + pub fn get_slope(&self, name: char) -> Result<&Expression, SlopeNotFoundError> { + self.slopes.get(&name).ok_or(SlopeNotFoundError(name)) + } + + fn tick(&mut self) -> Result<(), CompilerError> { + *self.get_mut('t')? += 1f64 / SAMPLE_RATE as f64; + { + let changes = self + .slopes + .iter() + .map( + |(v, Expression { instruction, slab })| -> Result<(char, f64), fasteval::Error> { + Ok(( + *v, + instruction.eval( + slab, + &mut self + .variables + .iter() + .map(|(c, f)| (c.to_string(), *f)) + .collect::>(), + )?, + )) + }, + ) + .collect::, fasteval::Error>>()?; + for (variable, new_value) in changes { + *self.get_mut(variable)? = new_value; + } + } + Ok(()) + } + + fn namespace_generator(&self) -> BTreeMap { + self.variables + .iter() + .map(|(c, f)| (c.to_string(), *f)) + .collect() + } + + pub fn eval( + &self, + Expression { instruction, slab }: &Expression, + ) -> Result { + instruction.eval(&slab, &mut self.namespace_generator()) + } + + pub fn render(&mut self, n: Option) -> Result, CompilerError> { + let curr_t = *self.get('t')?; + if let Some(note) = n { + let mut result = Vec::new(); + while (self.current_length()? * SAMPLE_RATE as f64) > *self.get('t')? - curr_t { + { + let Expression { instruction, slab } = &self.instrument; + result.push(instruction.eval(&slab, &mut { + let mut map = self + .variables + .iter() + .map(|(c, f)| (format!("{c}"), *f)) + .collect::>(); + map.insert('n'.to_string(), note as f64); + map + })?); + } + self.tick()?; + } + Ok(result) + } else { + while (self.current_length()? * SAMPLE_RATE as f64) > *self.get('t')? - curr_t { + self.tick()?; + } + Ok(vec![0.0; (*self.get('t')? - curr_t) as usize]) + } } }