simplify bliplib API
This commit is contained in:
parent
c0e3478ae0
commit
3168370a37
2 changed files with 216 additions and 168 deletions
|
@ -1,12 +1,15 @@
|
|||
use std::{
|
||||
any::{Any, TypeId}, collections::{BTreeMap, HashMap}, f64, fmt::Debug, str::FromStr
|
||||
any::{Any, TypeId},
|
||||
collections::{BTreeMap, HashMap},
|
||||
f64,
|
||||
fmt::Debug,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use derive_new::new;
|
||||
use derive_wrapper::{AsRef, From};
|
||||
use fasteval::{Compiler as _, EvalNamespace, Evaler, Instruction, Slab};
|
||||
use lazy_static::lazy_static;
|
||||
use thiserror::Error;
|
||||
|
||||
cfg_if! {
|
||||
|
@ -19,30 +22,19 @@ cfg_if! {
|
|||
|
||||
#[derive(From, AsRef, Default)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct TokenVec<'a>(pub(crate) Vec<Box<dyn Token + 'a>>);
|
||||
pub struct TokenVec(pub(crate) Vec<Box<dyn Token>>);
|
||||
|
||||
pub trait Type {
|
||||
fn type_id(&self) -> TypeId;
|
||||
}
|
||||
|
||||
macro_rules! impl_type {
|
||||
($name:ty) => {
|
||||
impl Type for $name {
|
||||
fn type_id(&self) -> std::any::TypeId {
|
||||
<Self as std::any::Any>::type_id(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_type_life {
|
||||
($name:ty) => {
|
||||
impl Type for $name {
|
||||
fn type_id(&self) -> TypeId {
|
||||
<$name as Any>::type_id(&Default::default())
|
||||
}
|
||||
}
|
||||
};
|
||||
impl<T> Type for T
|
||||
where
|
||||
T: Default + 'static,
|
||||
{
|
||||
fn type_id(&self) -> TypeId {
|
||||
<Self as Any>::type_id(&Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
@ -56,7 +48,7 @@ pub trait Token: Debug + Type {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl PartialEq for TokenVec<'_> {
|
||||
impl PartialEq for TokenVec {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
format!("{self:?}") == format!("{other:?}")
|
||||
}
|
||||
|
@ -66,8 +58,6 @@ impl PartialEq for TokenVec<'_> {
|
|||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct Silence;
|
||||
|
||||
impl_type!(Silence);
|
||||
|
||||
impl Token for Silence {
|
||||
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
|
||||
let mut next = context.render(None)?;
|
||||
|
@ -80,8 +70,6 @@ impl Token for Silence {
|
|||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct Marker;
|
||||
|
||||
impl_type!(Marker);
|
||||
|
||||
impl Token for Marker {
|
||||
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
|
||||
context.result.clear();
|
||||
|
@ -93,8 +81,6 @@ impl Token for Marker {
|
|||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct Note(pub u8);
|
||||
|
||||
impl_type!(Note);
|
||||
|
||||
impl Token for Note {
|
||||
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
|
||||
let mut next = context.render(Some(self.0))?;
|
||||
|
@ -107,20 +93,22 @@ impl Token for Note {
|
|||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct VariableChange(pub char, pub Expression);
|
||||
|
||||
impl_type!(VariableChange);
|
||||
impl AsRef<VariableChange> for VariableChange {
|
||||
fn as_ref(&self) -> &VariableChange {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Token for VariableChange {
|
||||
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
|
||||
*context.get_mut(self.0)? = context.eval(&self.1)?;
|
||||
*context.get_mut(self.0)? = context.eval(self.1.as_ref())?;
|
||||
Ok(context)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct Loop<'a>(pub LoopCount, pub TokenVec<'a>);
|
||||
|
||||
impl_type_life!(Loop<'_>);
|
||||
pub struct Loop(pub LoopCount, pub TokenVec);
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub enum LoopCount {
|
||||
|
@ -134,7 +122,7 @@ impl Default for LoopCount {
|
|||
}
|
||||
}
|
||||
|
||||
impl Token for Loop<'_> {
|
||||
impl Token for Loop {
|
||||
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
|
||||
let mut old_result = context.result.clone();
|
||||
let count = match self.0 {
|
||||
|
@ -157,11 +145,9 @@ impl Token for Loop<'_> {
|
|||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct Tuplet<'a>(pub TokenVec<'a>);
|
||||
pub struct Tuplet(pub TokenVec);
|
||||
|
||||
impl_type_life!(Tuplet<'_>);
|
||||
|
||||
impl Token for Tuplet<'_> {
|
||||
impl Token for Tuplet {
|
||||
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
|
||||
let mut old_result = context.result.clone();
|
||||
context.result.clear();
|
||||
|
@ -194,22 +180,15 @@ impl Token for Tuplet<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(new, Default)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct Slope<'a>(pub &'a VariableChange, pub TokenVec<'a>);
|
||||
pub struct Slope(pub VariableChange, pub TokenVec);
|
||||
|
||||
lazy_static! {
|
||||
static ref VARIABLE_CHANGE_DEFAULT: VariableChange = Default::default();
|
||||
}
|
||||
|
||||
impl Type for Slope<'_> {
|
||||
fn type_id(&self) -> TypeId {
|
||||
<Slope as Any>::type_id(&Slope(&VARIABLE_CHANGE_DEFAULT, Default::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Token for Slope<'_> {
|
||||
impl Token for Slope {
|
||||
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
|
||||
context.slopes.push((self.0.0, self.0.1.clone()));
|
||||
context
|
||||
.slopes
|
||||
.push((self.0.as_ref().0, self.0.as_ref().1.as_ref().clone()));
|
||||
context = self
|
||||
.1
|
||||
.0
|
||||
|
@ -228,6 +207,12 @@ pub struct Expression {
|
|||
pub(crate) slab: Slab,
|
||||
}
|
||||
|
||||
impl AsRef<Expression> for Expression {
|
||||
fn as_ref(&self) -> &Expression {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Expression {
|
||||
fn clone(&self) -> Self {
|
||||
self.from
|
||||
|
|
297
src/parser.rs
297
src/parser.rs
|
@ -1,9 +1,6 @@
|
|||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt::Display,
|
||||
};
|
||||
use std::{borrow::Borrow, collections::BTreeMap, marker::PhantomData};
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_new::new;
|
||||
use fasteval::Evaler;
|
||||
use nom::{
|
||||
AsChar, Compare, Finish, IResult, Input, Parser as NomParser,
|
||||
|
@ -25,18 +22,35 @@ use crate::compiler::{
|
|||
VariableChange,
|
||||
};
|
||||
|
||||
#[derive(Builder)]
|
||||
pub struct Parser<'n, 's, 'v, N: AsRef<str> + Clone> {
|
||||
notes: &'n [N],
|
||||
slopes: &'s HashMap<String, VariableChange>,
|
||||
variables: &'v [char],
|
||||
#[derive(new)]
|
||||
pub struct Parser<N, NS, S, SS, SV, V>
|
||||
where
|
||||
N: AsRef<[NS]>,
|
||||
NS: AsRef<str>,
|
||||
S: IntoIterator<Item = (SS, SV)>,
|
||||
SS: AsRef<str>,
|
||||
SV: Borrow<VariableChange>,
|
||||
V: AsRef<[char]>,
|
||||
{
|
||||
notes: N,
|
||||
slopes: S,
|
||||
variables: V,
|
||||
phantom: PhantomData<(NS, SS)>,
|
||||
}
|
||||
|
||||
impl<'a, 's, N: AsRef<str> + Clone> Parser<'_, 's, '_, N> {
|
||||
impl<'a, N, NS, S, SS, SV, V> Parser<N, NS, S, SS, SV, V>
|
||||
where
|
||||
N: AsRef<[NS]>,
|
||||
NS: AsRef<str>,
|
||||
S: IntoIterator<Item = (SS, SV)> + Clone,
|
||||
SS: AsRef<str>,
|
||||
SV: Borrow<VariableChange>,
|
||||
V: AsRef<[char]>,
|
||||
{
|
||||
pub fn parse_all(
|
||||
&self,
|
||||
input: &'a str,
|
||||
) -> Result<TokenVec<'s>, Error<nom_locate::LocatedSpan<&'a str>>> {
|
||||
) -> Result<TokenVec, Error<nom_locate::LocatedSpan<&'a str>>> {
|
||||
token_parser(self)
|
||||
.parse_complete(LocatedSpan::new(input))
|
||||
.finish()
|
||||
|
@ -44,14 +58,19 @@ impl<'a, 's, N: AsRef<str> + Clone> Parser<'_, 's, '_, N> {
|
|||
}
|
||||
}
|
||||
|
||||
fn token_parser<'a, 's, I, N>(
|
||||
parser: &Parser<'_, 's, '_, N>,
|
||||
) -> impl NomParser<I, Output = TokenVec<'s>, Error = nom::error::Error<I>>
|
||||
fn token_parser<'a, I, N, NS, S, SS, SV, V>(
|
||||
parser: &Parser<N, NS, S, SS, SV, V>,
|
||||
) -> impl NomParser<I, Output = TokenVec, Error = nom::error::Error<I>>
|
||||
where
|
||||
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy,
|
||||
<I as Input>::Item: AsChar,
|
||||
<I as Input>::Item: PartialEq<char>,
|
||||
N: AsRef<str> + Clone,
|
||||
N: AsRef<[NS]>,
|
||||
NS: AsRef<str>,
|
||||
S: IntoIterator<Item = (SS, SV)> + Clone,
|
||||
SS: AsRef<str>,
|
||||
SV: Borrow<VariableChange>,
|
||||
V: AsRef<[char]>,
|
||||
{
|
||||
let space_or_comment = || {
|
||||
value(
|
||||
|
@ -64,8 +83,8 @@ where
|
|||
alt((
|
||||
Silence::parser().map(into_box),
|
||||
Marker::parser().map(into_box),
|
||||
Note::parser(parser.notes).map(into_box),
|
||||
VariableChange::parser(parser.variables).map(into_box),
|
||||
Note::parser(parser.notes.as_ref()).map(into_box),
|
||||
VariableChange::parser(&parser.variables).map(into_box),
|
||||
Loop::parser(parser).map(into_box),
|
||||
Tuplet::parser(parser).map(into_box),
|
||||
Slope::parser(parser).map(into_box),
|
||||
|
@ -100,60 +119,87 @@ impl Marker {
|
|||
}
|
||||
|
||||
impl Note {
|
||||
fn parser<'n, N, I>(
|
||||
notes: &'n [N],
|
||||
) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
|
||||
fn parser<'a, N, NS, I>(
|
||||
notes: N,
|
||||
) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>> + 'a
|
||||
where
|
||||
N: AsRef<str> + Clone,
|
||||
N: IntoIterator<Item = NS>,
|
||||
NS: AsRef<str>,
|
||||
I: Input + for<'z> Compare<&'z str>,
|
||||
{
|
||||
|input: I| {
|
||||
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = {
|
||||
let mut sorted = notes.iter().enumerate().collect::<Vec<(usize, &N)>>();
|
||||
sorted.sort_by_key(|(_, n)| n.as_ref().len());
|
||||
sorted
|
||||
}
|
||||
.drain(..)
|
||||
.rev()
|
||||
.map(|(i, t)| {
|
||||
Box::new(move |input: I| value(Note(i as u8), tag(t.clone().as_ref())).parse(input))
|
||||
as Box<dyn Fn(I) -> IResult<I, Self>>
|
||||
})
|
||||
.collect();
|
||||
let notes = {
|
||||
let mut sorted = notes
|
||||
.into_iter()
|
||||
.map(|s| s.as_ref().to_string())
|
||||
.enumerate()
|
||||
.collect::<Vec<_>>();
|
||||
sorted.sort_by_key(|(_, n)| n.len());
|
||||
sorted
|
||||
};
|
||||
move |input: I| {
|
||||
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = notes
|
||||
.clone()
|
||||
.drain(..)
|
||||
.rev()
|
||||
.map(|(i, t)| {
|
||||
Box::new(move |input: I| {
|
||||
value(Note(i as u8), tag(t.clone().as_ref())).parse(input)
|
||||
}) as Box<dyn Fn(I) -> IResult<I, Self>>
|
||||
})
|
||||
.collect();
|
||||
alt(parsers.as_mut_slice()).parse(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VariableChange {
|
||||
fn parser<I>(variables: &[char]) -> impl Fn(I) -> IResult<I, Self>
|
||||
fn parser<I, V>(variables: V) -> impl Fn(I) -> IResult<I, Self>
|
||||
where
|
||||
I: Input + AsRef<str> + Copy,
|
||||
<I as Input>::Item: AsChar,
|
||||
V: AsRef<[char]>,
|
||||
{
|
||||
move |i: I| {
|
||||
preceded(char('$'), one_of(variables))
|
||||
.and(expression_parser(variables))
|
||||
.map(|(name, change)| VariableChange(name, change))
|
||||
.parse(i)
|
||||
preceded(
|
||||
char('$'),
|
||||
one_of(variables.as_ref().iter().collect::<String>().as_str()),
|
||||
)
|
||||
.and(expression_parser(variables.as_ref()))
|
||||
.map(|(name, change)| VariableChange(name, change))
|
||||
.parse(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Loop<'s> {
|
||||
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self>
|
||||
impl Loop {
|
||||
fn parser<I, N, NS, S, SS, SV, V>(
|
||||
parser: &Parser<N, NS, S, SS, SV, V>,
|
||||
) -> impl Fn(I) -> IResult<I, Self>
|
||||
where
|
||||
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy,
|
||||
<I as Input>::Item: AsChar,
|
||||
<I as Input>::Item: PartialEq<char>,
|
||||
N: AsRef<str> + Clone,
|
||||
N: AsRef<[NS]>,
|
||||
NS: AsRef<str>,
|
||||
S: IntoIterator<Item = (SS, SV)> + Clone,
|
||||
SS: AsRef<str>,
|
||||
SV: Borrow<VariableChange>,
|
||||
V: AsRef<[char]>,
|
||||
{
|
||||
|input| {
|
||||
move |input| {
|
||||
delimited(
|
||||
char('('),
|
||||
opt(alt((
|
||||
usize.map(LoopCount::Litteral),
|
||||
one_of(parser.variables).map(LoopCount::Variable),
|
||||
one_of(
|
||||
parser
|
||||
.variables
|
||||
.as_ref()
|
||||
.iter()
|
||||
.collect::<String>()
|
||||
.as_str(),
|
||||
)
|
||||
.map(LoopCount::Variable),
|
||||
)))
|
||||
.and(token_parser(parser)),
|
||||
char(')'),
|
||||
|
@ -164,13 +210,20 @@ impl<'s> Loop<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s> Tuplet<'s> {
|
||||
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self>
|
||||
impl Tuplet {
|
||||
fn parser<I, N, NS, S, SS, SV, V>(
|
||||
parser: &Parser<N, NS, S, SS, SV, V>,
|
||||
) -> impl Fn(I) -> IResult<I, Self>
|
||||
where
|
||||
I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
|
||||
<I as Input>::Item: AsChar,
|
||||
<I as Input>::Item: PartialEq<char>,
|
||||
N: AsRef<str> + Clone,
|
||||
N: AsRef<[NS]>,
|
||||
NS: AsRef<str>,
|
||||
S: IntoIterator<Item = (SS, SV)> + Clone,
|
||||
SS: AsRef<str>,
|
||||
SV: Borrow<VariableChange>,
|
||||
V: AsRef<[char]>,
|
||||
{
|
||||
|input| {
|
||||
delimited(char('['), token_parser(parser), char(']'))
|
||||
|
@ -180,54 +233,70 @@ impl<'s> Tuplet<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s> Slope<'s> {
|
||||
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self>
|
||||
impl Slope {
|
||||
fn parser<'p, I, N, NS, S, SS, SV, V>(
|
||||
parser: &'p Parser<N, NS, S, SS, SV, V>,
|
||||
) -> impl Fn(I) -> IResult<I, Self>
|
||||
where
|
||||
I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
|
||||
<I as Input>::Item: AsChar,
|
||||
<I as Input>::Item: PartialEq<char>,
|
||||
N: AsRef<str> + Clone,
|
||||
N: AsRef<[NS]>,
|
||||
NS: AsRef<str>,
|
||||
S: IntoIterator<Item = (SS, SV)> + Clone,
|
||||
SS: AsRef<str>,
|
||||
SV: Borrow<VariableChange>,
|
||||
V: AsRef<[char]>,
|
||||
{
|
||||
|input| {
|
||||
let iter: std::iter::Rev<std::vec::IntoIter<(&'s String, &'s VariableChange)>> = {
|
||||
move |input| {
|
||||
let slopes = {
|
||||
let mut vec = parser
|
||||
.slopes
|
||||
.iter()
|
||||
.collect::<Vec<(&'s String, &'s VariableChange)>>();
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(s1, s2)| (s1.as_ref().to_string(), s2.borrow().clone()))
|
||||
.collect::<Vec<(String, VariableChange)>>();
|
||||
vec.sort_by_key(|(name, _)| name.len());
|
||||
vec
|
||||
}
|
||||
.into_iter()
|
||||
.rev();
|
||||
};
|
||||
let iter: std::iter::Rev<std::vec::IntoIter<(String, VariableChange)>> =
|
||||
slopes.into_iter().rev();
|
||||
delimited(
|
||||
char('{'),
|
||||
alt(iter
|
||||
.map(|(k, v)| {
|
||||
Box::new(move |input: I| value(v, tag(k.as_str())).parse(input))
|
||||
as Box<dyn 's + Fn(I) -> IResult<I, &'s VariableChange>>
|
||||
Box::new(move |input: I| value(v.clone(), tag(k.as_str())).parse(input))
|
||||
as Box<dyn Fn(I) -> IResult<I, VariableChange>>
|
||||
})
|
||||
.collect::<Vec<Box<dyn 's + Fn(I) -> IResult<I, &'s VariableChange>>>>()
|
||||
.collect::<Vec<Box<dyn Fn(I) -> IResult<I, VariableChange>>>>()
|
||||
.as_mut_slice())
|
||||
.and(token_parser(parser)),
|
||||
char('}'),
|
||||
)
|
||||
.map(|(i, v)| Self(i, v))
|
||||
.map(|(i, v)| Self::new(i, v))
|
||||
.parse(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Will return the longest valid fasteval expression
|
||||
fn expression_parser<I: Input + AsRef<str> + Copy, C: Into<char> + Ord + Display>(
|
||||
variables: &[C],
|
||||
) -> impl Fn(I) -> IResult<I, Expression> {
|
||||
|input: I| {
|
||||
fn expression_parser<'v, I, V, C>(variables: V) -> impl 'v + Fn(I) -> IResult<I, Expression>
|
||||
where
|
||||
I: Input + AsRef<str> + Copy,
|
||||
V: IntoIterator<Item = C>,
|
||||
C: Borrow<char>,
|
||||
{
|
||||
let variables: Vec<(String, f64)> = variables
|
||||
.into_iter()
|
||||
.map(|v| (v.borrow().to_string(), 0.0))
|
||||
.collect();
|
||||
move |input: I| {
|
||||
take_while_map(|i: I| {
|
||||
i.as_ref().parse::<Expression>().ok().and_then(|e| {
|
||||
e.instruction
|
||||
.eval(
|
||||
&e.slab,
|
||||
&mut BTreeMap::from_iter(variables.iter().map(|v| (v.to_string(), 0.0))),
|
||||
&mut BTreeMap::from_iter(variables.clone().drain(..)),
|
||||
)
|
||||
.ok()
|
||||
.is_some()
|
||||
|
@ -261,9 +330,11 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Parser;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use nom::{IResult, Parser};
|
||||
use nom::{IResult, Parser as _};
|
||||
|
||||
use crate::{
|
||||
compiler::{
|
||||
|
@ -272,7 +343,31 @@ mod tests {
|
|||
parser::expression_parser,
|
||||
};
|
||||
|
||||
use super::ParserBuilder;
|
||||
fn very_fancy_slope() -> VariableChange {
|
||||
VariableChange('n', "1+1".parse().unwrap())
|
||||
}
|
||||
|
||||
fn normal_slope() -> VariableChange {
|
||||
VariableChange('n', "1".parse().unwrap())
|
||||
}
|
||||
|
||||
fn parser_generator() -> Parser<
|
||||
[&'static str; 3],
|
||||
&'static str,
|
||||
HashMap<String, VariableChange>,
|
||||
String,
|
||||
VariableChange,
|
||||
[char; 1],
|
||||
> {
|
||||
Parser::new(
|
||||
["do", "ré", "mi"],
|
||||
HashMap::from([
|
||||
("nor".to_string(), very_fancy_slope()),
|
||||
("normal".to_string(), normal_slope()),
|
||||
]),
|
||||
['n'],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression_parser_test() {
|
||||
|
@ -435,17 +530,9 @@ mod tests {
|
|||
let slopes = Default::default();
|
||||
fn parser_builder<'s>(
|
||||
slopes: &'s HashMap<String, VariableChange>,
|
||||
) -> impl Fn(&str) -> IResult<&str, Loop<'s>> {
|
||||
) -> impl Fn(&str) -> IResult<&str, Loop> {
|
||||
move |input: &str| {
|
||||
Loop::parser(
|
||||
&ParserBuilder::create_empty()
|
||||
.notes(&["do", "ré", "mi"])
|
||||
.slopes(slopes)
|
||||
.variables(&['n'])
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.parse(input)
|
||||
Loop::parser(&Parser::new(["do", "ré", "mi"], slopes, ['n'])).parse(input)
|
||||
}
|
||||
}
|
||||
let parser = parser_builder(&slopes);
|
||||
|
@ -497,17 +584,9 @@ mod tests {
|
|||
let slopes = Default::default();
|
||||
fn parser_builder<'s>(
|
||||
slopes: &'s HashMap<String, VariableChange>,
|
||||
) -> impl Fn(&str) -> IResult<&str, Tuplet<'s>> {
|
||||
) -> impl Fn(&str) -> IResult<&str, Tuplet> {
|
||||
move |input: &str| {
|
||||
Tuplet::parser(
|
||||
&ParserBuilder::create_empty()
|
||||
.notes(&["do", "ré", "mi"])
|
||||
.slopes(slopes)
|
||||
.variables(&['n'])
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.parse(input)
|
||||
Tuplet::parser(&Parser::new(["do", "ré", "mi"], slopes, ['n'])).parse(input)
|
||||
}
|
||||
}
|
||||
let parser = parser_builder(&slopes);
|
||||
|
@ -542,46 +621,30 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn slope() {
|
||||
let normal = || VariableChange('n', "1".parse().unwrap());
|
||||
let very_fancy = || VariableChange('n', "1+1".parse().unwrap());
|
||||
let slopes = HashMap::from([
|
||||
("nor".to_string(), very_fancy()),
|
||||
("normal".to_string(), normal()),
|
||||
]);
|
||||
fn parser_builder<'s>(
|
||||
slopes: &'s HashMap<String, VariableChange>,
|
||||
) -> impl Fn(&str) -> IResult<&str, Slope<'s>> {
|
||||
move |input: &str| {
|
||||
Slope::parser(
|
||||
&ParserBuilder::create_empty()
|
||||
.notes(&["do", "ré", "mi"])
|
||||
.slopes(slopes)
|
||||
.variables(&['n'])
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.parse(input)
|
||||
}
|
||||
}
|
||||
let parser = parser_builder(&slopes);
|
||||
let normal_value = normal();
|
||||
let very_fancy_value = very_fancy();
|
||||
let p = parser_generator();
|
||||
let parser_builder = || Slope::parser(&p);
|
||||
let parser = parser_builder();
|
||||
let normal_value = normal_slope();
|
||||
let very_fancy_value = very_fancy_slope();
|
||||
let mut working_cases = vec![
|
||||
(
|
||||
"{normal.%}",
|
||||
(
|
||||
"",
|
||||
Slope(
|
||||
&normal_value,
|
||||
normal_value.clone(),
|
||||
TokenVec(vec![Box::new(Silence), Box::new(Marker)]),
|
||||
),
|
||||
),
|
||||
),
|
||||
("{normal}", ("", Slope(&normal_value, TokenVec(vec![])))),
|
||||
("{nor}", ("", Slope(&very_fancy_value, TokenVec(vec![])))),
|
||||
(
|
||||
"{normal}",
|
||||
("", Slope(normal_value.clone(), TokenVec(vec![]))),
|
||||
),
|
||||
("{nor}", ("", Slope(very_fancy_value, TokenVec(vec![])))),
|
||||
(
|
||||
"{normal do}f",
|
||||
("f", Slope(&normal_value, TokenVec(vec![Box::new(Note(0))]))),
|
||||
("f", Slope(normal_value, TokenVec(vec![Box::new(Note(0))]))),
|
||||
),
|
||||
];
|
||||
let mut not_working_cases = vec![
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue