simplify bliplib API

This commit is contained in:
Breval Ferrari 2025-06-01 23:32:18 +02:00
parent c0e3478ae0
commit 3168370a37
Signed by: breval
GPG key ID: 5310EC237FE283D1
2 changed files with 216 additions and 168 deletions

View file

@ -1,12 +1,15 @@
use std::{ 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 cfg_if::cfg_if;
use derive_new::new; use derive_new::new;
use derive_wrapper::{AsRef, From}; use derive_wrapper::{AsRef, From};
use fasteval::{Compiler as _, EvalNamespace, Evaler, Instruction, Slab}; use fasteval::{Compiler as _, EvalNamespace, Evaler, Instruction, Slab};
use lazy_static::lazy_static;
use thiserror::Error; use thiserror::Error;
cfg_if! { cfg_if! {
@ -19,30 +22,19 @@ cfg_if! {
#[derive(From, AsRef, Default)] #[derive(From, AsRef, Default)]
#[cfg_attr(test, derive(Debug))] #[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 { pub trait Type {
fn type_id(&self) -> TypeId; fn type_id(&self) -> TypeId;
} }
macro_rules! impl_type { impl<T> Type for T
($name:ty) => { where
impl Type for $name { T: Default + 'static,
fn type_id(&self) -> std::any::TypeId { {
<Self as std::any::Any>::type_id(self) fn type_id(&self) -> TypeId {
} <Self as Any>::type_id(&Default::default())
} }
};
}
macro_rules! impl_type_life {
($name:ty) => {
impl Type for $name {
fn type_id(&self) -> TypeId {
<$name as Any>::type_id(&Default::default())
}
}
};
} }
#[cfg(not(test))] #[cfg(not(test))]
@ -56,7 +48,7 @@ pub trait Token: Debug + Type {
} }
#[cfg(test)] #[cfg(test)]
impl PartialEq for TokenVec<'_> { impl PartialEq for TokenVec {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
format!("{self:?}") == format!("{other:?}") format!("{self:?}") == format!("{other:?}")
} }
@ -66,8 +58,6 @@ impl PartialEq for TokenVec<'_> {
#[cfg_attr(test, derive(Debug, PartialEq))] #[cfg_attr(test, derive(Debug, PartialEq))]
pub struct Silence; pub struct Silence;
impl_type!(Silence);
impl Token for Silence { impl Token for Silence {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
let mut next = context.render(None)?; let mut next = context.render(None)?;
@ -80,8 +70,6 @@ impl Token for Silence {
#[cfg_attr(test, derive(Debug, PartialEq))] #[cfg_attr(test, derive(Debug, PartialEq))]
pub struct Marker; pub struct Marker;
impl_type!(Marker);
impl Token for Marker { impl Token for Marker {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
context.result.clear(); context.result.clear();
@ -93,8 +81,6 @@ impl Token for Marker {
#[cfg_attr(test, derive(Debug, PartialEq))] #[cfg_attr(test, derive(Debug, PartialEq))]
pub struct Note(pub u8); pub struct Note(pub u8);
impl_type!(Note);
impl Token for Note { impl Token for Note {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
let mut next = context.render(Some(self.0))?; let mut next = context.render(Some(self.0))?;
@ -107,20 +93,22 @@ impl Token for Note {
#[cfg_attr(test, derive(Debug, PartialEq))] #[cfg_attr(test, derive(Debug, PartialEq))]
pub struct VariableChange(pub char, pub Expression); 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 { impl Token for VariableChange {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { 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) Ok(context)
} }
} }
#[derive(Default)] #[derive(Default)]
#[cfg_attr(test, derive(Debug, PartialEq))] #[cfg_attr(test, derive(Debug, PartialEq))]
pub struct Loop<'a>(pub LoopCount, pub TokenVec<'a>); pub struct Loop(pub LoopCount, pub TokenVec);
impl_type_life!(Loop<'_>);
#[cfg_attr(test, derive(Debug, PartialEq))] #[cfg_attr(test, derive(Debug, PartialEq))]
pub enum LoopCount { 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> { fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
let mut old_result = context.result.clone(); let mut old_result = context.result.clone();
let count = match self.0 { let count = match self.0 {
@ -157,11 +145,9 @@ impl Token for Loop<'_> {
#[derive(Default)] #[derive(Default)]
#[cfg_attr(test, derive(Debug, PartialEq))] #[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> { fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
let mut old_result = context.result.clone(); let mut old_result = context.result.clone();
context.result.clear(); context.result.clear();
@ -194,22 +180,15 @@ impl Token for Tuplet<'_> {
} }
} }
#[derive(new, Default)]
#[cfg_attr(test, derive(Debug, PartialEq))] #[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! { impl Token for Slope {
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<'_> {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { 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 context = self
.1 .1
.0 .0
@ -228,6 +207,12 @@ pub struct Expression {
pub(crate) slab: Slab, pub(crate) slab: Slab,
} }
impl AsRef<Expression> for Expression {
fn as_ref(&self) -> &Expression {
self
}
}
impl Clone for Expression { impl Clone for Expression {
fn clone(&self) -> Self { fn clone(&self) -> Self {
self.from self.from

View file

@ -1,9 +1,6 @@
use std::{ use std::{borrow::Borrow, collections::BTreeMap, marker::PhantomData};
collections::{BTreeMap, HashMap},
fmt::Display,
};
use derive_builder::Builder; use derive_new::new;
use fasteval::Evaler; use fasteval::Evaler;
use nom::{ use nom::{
AsChar, Compare, Finish, IResult, Input, Parser as NomParser, AsChar, Compare, Finish, IResult, Input, Parser as NomParser,
@ -25,18 +22,35 @@ use crate::compiler::{
VariableChange, VariableChange,
}; };
#[derive(Builder)] #[derive(new)]
pub struct Parser<'n, 's, 'v, N: AsRef<str> + Clone> { pub struct Parser<N, NS, S, SS, SV, V>
notes: &'n [N], where
slopes: &'s HashMap<String, VariableChange>, N: AsRef<[NS]>,
variables: &'v [char], 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( pub fn parse_all(
&self, &self,
input: &'a str, input: &'a str,
) -> Result<TokenVec<'s>, Error<nom_locate::LocatedSpan<&'a str>>> { ) -> Result<TokenVec, Error<nom_locate::LocatedSpan<&'a str>>> {
token_parser(self) token_parser(self)
.parse_complete(LocatedSpan::new(input)) .parse_complete(LocatedSpan::new(input))
.finish() .finish()
@ -44,14 +58,19 @@ impl<'a, 's, N: AsRef<str> + Clone> Parser<'_, 's, '_, N> {
} }
} }
fn token_parser<'a, 's, I, N>( fn token_parser<'a, I, N, NS, S, SS, SV, V>(
parser: &Parser<'_, 's, '_, N>, parser: &Parser<N, NS, S, SS, SV, V>,
) -> impl NomParser<I, Output = TokenVec<'s>, Error = nom::error::Error<I>> ) -> impl NomParser<I, Output = TokenVec, Error = nom::error::Error<I>>
where where
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy, I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <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 = || { let space_or_comment = || {
value( value(
@ -64,8 +83,8 @@ where
alt(( alt((
Silence::parser().map(into_box), Silence::parser().map(into_box),
Marker::parser().map(into_box), Marker::parser().map(into_box),
Note::parser(parser.notes).map(into_box), Note::parser(parser.notes.as_ref()).map(into_box),
VariableChange::parser(parser.variables).map(into_box), VariableChange::parser(&parser.variables).map(into_box),
Loop::parser(parser).map(into_box), Loop::parser(parser).map(into_box),
Tuplet::parser(parser).map(into_box), Tuplet::parser(parser).map(into_box),
Slope::parser(parser).map(into_box), Slope::parser(parser).map(into_box),
@ -100,60 +119,87 @@ impl Marker {
} }
impl Note { impl Note {
fn parser<'n, N, I>( fn parser<'a, N, NS, I>(
notes: &'n [N], notes: N,
) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>> ) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>> + 'a
where where
N: AsRef<str> + Clone, N: IntoIterator<Item = NS>,
NS: AsRef<str>,
I: Input + for<'z> Compare<&'z str>, I: Input + for<'z> Compare<&'z str>,
{ {
|input: I| { let notes = {
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = { let mut sorted = notes
let mut sorted = notes.iter().enumerate().collect::<Vec<(usize, &N)>>(); .into_iter()
sorted.sort_by_key(|(_, n)| n.as_ref().len()); .map(|s| s.as_ref().to_string())
sorted .enumerate()
} .collect::<Vec<_>>();
.drain(..) sorted.sort_by_key(|(_, n)| n.len());
.rev() sorted
.map(|(i, t)| { };
Box::new(move |input: I| value(Note(i as u8), tag(t.clone().as_ref())).parse(input)) move |input: I| {
as Box<dyn Fn(I) -> IResult<I, Self>> let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = notes
}) .clone()
.collect(); .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) alt(parsers.as_mut_slice()).parse(input)
} }
} }
} }
impl VariableChange { 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 where
I: Input + AsRef<str> + Copy, I: Input + AsRef<str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
V: AsRef<[char]>,
{ {
move |i: I| { move |i: I| {
preceded(char('$'), one_of(variables)) preceded(
.and(expression_parser(variables)) char('$'),
.map(|(name, change)| VariableChange(name, change)) one_of(variables.as_ref().iter().collect::<String>().as_str()),
.parse(i) )
.and(expression_parser(variables.as_ref()))
.map(|(name, change)| VariableChange(name, change))
.parse(i)
} }
} }
} }
impl<'s> Loop<'s> { impl Loop {
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self> fn parser<I, N, NS, S, SS, SV, V>(
parser: &Parser<N, NS, S, SS, SV, V>,
) -> impl Fn(I) -> IResult<I, Self>
where where
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy, I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <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( delimited(
char('('), char('('),
opt(alt(( opt(alt((
usize.map(LoopCount::Litteral), 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)), .and(token_parser(parser)),
char(')'), char(')'),
@ -164,13 +210,20 @@ impl<'s> Loop<'s> {
} }
} }
impl<'s> Tuplet<'s> { impl Tuplet {
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self> fn parser<I, N, NS, S, SS, SV, V>(
parser: &Parser<N, NS, S, SS, SV, V>,
) -> impl Fn(I) -> IResult<I, Self>
where where
I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy, I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <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| { |input| {
delimited(char('['), token_parser(parser), char(']')) delimited(char('['), token_parser(parser), char(']'))
@ -180,54 +233,70 @@ impl<'s> Tuplet<'s> {
} }
} }
impl<'s> Slope<'s> { impl Slope {
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self> 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 where
I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy, I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <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| {
let iter: std::iter::Rev<std::vec::IntoIter<(&'s String, &'s VariableChange)>> = { let slopes = {
let mut vec = parser let mut vec = parser
.slopes .slopes
.iter() .clone()
.collect::<Vec<(&'s String, &'s VariableChange)>>(); .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.sort_by_key(|(name, _)| name.len());
vec vec
} };
.into_iter() let iter: std::iter::Rev<std::vec::IntoIter<(String, VariableChange)>> =
.rev(); slopes.into_iter().rev();
delimited( delimited(
char('{'), char('{'),
alt(iter alt(iter
.map(|(k, v)| { .map(|(k, v)| {
Box::new(move |input: I| value(v, tag(k.as_str())).parse(input)) Box::new(move |input: I| value(v.clone(), tag(k.as_str())).parse(input))
as Box<dyn 's + Fn(I) -> IResult<I, &'s VariableChange>> 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()) .as_mut_slice())
.and(token_parser(parser)), .and(token_parser(parser)),
char('}'), char('}'),
) )
.map(|(i, v)| Self(i, v)) .map(|(i, v)| Self::new(i, v))
.parse(input) .parse(input)
} }
} }
} }
/// Will return the longest valid fasteval expression /// Will return the longest valid fasteval expression
fn expression_parser<I: Input + AsRef<str> + Copy, C: Into<char> + Ord + Display>( fn expression_parser<'v, I, V, C>(variables: V) -> impl 'v + Fn(I) -> IResult<I, Expression>
variables: &[C], where
) -> impl Fn(I) -> IResult<I, Expression> { I: Input + AsRef<str> + Copy,
|input: I| { 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| { take_while_map(|i: I| {
i.as_ref().parse::<Expression>().ok().and_then(|e| { i.as_ref().parse::<Expression>().ok().and_then(|e| {
e.instruction e.instruction
.eval( .eval(
&e.slab, &e.slab,
&mut BTreeMap::from_iter(variables.iter().map(|v| (v.to_string(), 0.0))), &mut BTreeMap::from_iter(variables.clone().drain(..)),
) )
.ok() .ok()
.is_some() .is_some()
@ -261,9 +330,11 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Parser;
use std::collections::HashMap; use std::collections::HashMap;
use nom::{IResult, Parser}; use nom::{IResult, Parser as _};
use crate::{ use crate::{
compiler::{ compiler::{
@ -272,7 +343,31 @@ mod tests {
parser::expression_parser, 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", "", "mi"],
HashMap::from([
("nor".to_string(), very_fancy_slope()),
("normal".to_string(), normal_slope()),
]),
['n'],
)
}
#[test] #[test]
fn expression_parser_test() { fn expression_parser_test() {
@ -435,17 +530,9 @@ mod tests {
let slopes = Default::default(); let slopes = Default::default();
fn parser_builder<'s>( fn parser_builder<'s>(
slopes: &'s HashMap<String, VariableChange>, slopes: &'s HashMap<String, VariableChange>,
) -> impl Fn(&str) -> IResult<&str, Loop<'s>> { ) -> impl Fn(&str) -> IResult<&str, Loop> {
move |input: &str| { move |input: &str| {
Loop::parser( Loop::parser(&Parser::new(["do", "", "mi"], slopes, ['n'])).parse(input)
&ParserBuilder::create_empty()
.notes(&["do", "", "mi"])
.slopes(slopes)
.variables(&['n'])
.build()
.unwrap(),
)
.parse(input)
} }
} }
let parser = parser_builder(&slopes); let parser = parser_builder(&slopes);
@ -497,17 +584,9 @@ mod tests {
let slopes = Default::default(); let slopes = Default::default();
fn parser_builder<'s>( fn parser_builder<'s>(
slopes: &'s HashMap<String, VariableChange>, slopes: &'s HashMap<String, VariableChange>,
) -> impl Fn(&str) -> IResult<&str, Tuplet<'s>> { ) -> impl Fn(&str) -> IResult<&str, Tuplet> {
move |input: &str| { move |input: &str| {
Tuplet::parser( Tuplet::parser(&Parser::new(["do", "", "mi"], slopes, ['n'])).parse(input)
&ParserBuilder::create_empty()
.notes(&["do", "", "mi"])
.slopes(slopes)
.variables(&['n'])
.build()
.unwrap(),
)
.parse(input)
} }
} }
let parser = parser_builder(&slopes); let parser = parser_builder(&slopes);
@ -542,46 +621,30 @@ mod tests {
#[test] #[test]
fn slope() { fn slope() {
let normal = || VariableChange('n', "1".parse().unwrap()); let p = parser_generator();
let very_fancy = || VariableChange('n', "1+1".parse().unwrap()); let parser_builder = || Slope::parser(&p);
let slopes = HashMap::from([ let parser = parser_builder();
("nor".to_string(), very_fancy()), let normal_value = normal_slope();
("normal".to_string(), normal()), let very_fancy_value = very_fancy_slope();
]);
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", "", "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 mut working_cases = vec![ let mut working_cases = vec![
( (
"{normal.%}", "{normal.%}",
( (
"", "",
Slope( Slope(
&normal_value, normal_value.clone(),
TokenVec(vec![Box::new(Silence), Box::new(Marker)]), 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", "{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![ let mut not_working_cases = vec![