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::{
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

View file

@ -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", "", "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", "", "mi"])
.slopes(slopes)
.variables(&['n'])
.build()
.unwrap(),
)
.parse(input)
Loop::parser(&Parser::new(["do", "", "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", "", "mi"])
.slopes(slopes)
.variables(&['n'])
.build()
.unwrap(),
)
.parse(input)
Tuplet::parser(&Parser::new(["do", "", "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", "", "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![