first four parsers

This commit is contained in:
Breval Ferrari 2025-05-20 16:45:30 +02:00
parent 0240602c19
commit 4fa49e2181
4 changed files with 263 additions and 75 deletions

View file

@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::{ use std::{
error::Error, error::Error,
fmt::Debug, fmt::Debug,
@ -7,32 +9,32 @@ use std::{
str::FromStr, str::FromStr,
}; };
use anyhow::{Context, anyhow}; use anyhow::anyhow;
use bliplib::parser::TokenParser; use bliplib::compiler::Expression;
use clap::{Parser, builder::EnumValueParser}; use clap::Parser;
use derive_new::new;
use derive_wrapper::AsRef; use derive_wrapper::AsRef;
use fasteval::{Compiler, Instruction, Slab};
use hound::SampleFormat; use hound::SampleFormat;
use mp3lame_encoder::{Bitrate, Quality}; use mp3lame_encoder::{Bitrate, Quality};
use nom::{ use nom::{
AsBytes, Compare, Finish, Input, Offset, Parser as _, Finish, Parser as P,
branch::alt, branch::alt,
bytes::complete::tag, bytes::complete::tag,
character::complete::{char, u16, usize}, character::complete::{char, u16, usize},
combinator::{opt, rest, success, value}, combinator::{rest, success, value},
error::{ErrorKind, ParseError}, error::ErrorKind,
sequence::preceded, sequence::preceded,
}; };
use nom_locate::{LocatedSpan, position}; use nom_locate::{LocatedSpan, position};
use strum::{Display, EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr, ToString}; use strum::{EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr};
use thiserror::Error; use thiserror::Error;
const DEFAULT_INSTRUMENT: &str = "sin(2*PI*(442+442*((n+1)/N))*t)"; const DEFAULT_INSTRUMENT: &str = "sin(2*PI*(442+442*((n+1)/N))*t)";
const DEFAULT_LENGTH: &str = "2^(2-log_2(l))*(60/T)"; const DEFAULT_LENGTH: &str = "2^(2-log_2(l))*(60/T)";
fn main() { fn main() {
dbg!(Cli::parse()); let cli = Cli::parse();
#[cfg(debug_assertions)]
dbg!(cli);
} }
#[derive(Parser)] #[derive(Parser)]
@ -188,31 +190,6 @@ where
)) ))
} }
#[derive(new)]
#[cfg_attr(debug_assertions, derive(Debug))]
struct Expression {
instruction: Instruction,
slab: Slab,
}
impl Clone for Expression {
fn clone(&self) -> Self {
unimplemented!()
}
}
impl FromStr for Expression {
type Err = fasteval::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut slab = Slab::new();
let instruction = fasteval::Parser::new()
.parse(s, &mut slab.ps)?
.from(&slab.ps)
.compile(&slab.ps, &mut slab.cs);
Ok(Expression::new(instruction, slab))
}
}
#[derive(Parser, Clone)] #[derive(Parser, Clone)]
#[cfg_attr(debug_assertions, derive(Debug))] #[cfg_attr(debug_assertions, derive(Debug))]
#[group(required = false, multiple = false)] #[group(required = false, multiple = false)]
@ -293,8 +270,12 @@ impl Default for AudioFormat {
} }
} }
fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error> { fn audio_format_parser(input: &str) -> Result<AudioFormat, anyhow::Error> {
fn mp3<'a>() -> impl TokenParser<&'a str, AudioFormat> { fn mp3<'a>() -> impl P<
LocatedSpan<&'a str>,
Output = AudioFormat,
Error = nom::error::Error<LocatedSpan<&'a str>>,
> {
preceded( preceded(
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>( tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
AudioFormatDiscriminants::Mp3.into(), AudioFormatDiscriminants::Mp3.into(),
@ -342,7 +323,11 @@ fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error>
}), }),
) )
} }
fn wav<'a>() -> impl TokenParser<&'a str, AudioFormat> { fn wav<'a>() -> impl P<
LocatedSpan<&'a str>,
Output = AudioFormat,
Error = nom::error::Error<LocatedSpan<&'a str>>,
> {
preceded( preceded(
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>( tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
AudioFormatDiscriminants::Wav.into(), AudioFormatDiscriminants::Wav.into(),
@ -352,7 +337,11 @@ fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error>
.map(|(bps, sample_format)| AudioFormat::Wav { bps, sample_format }), .map(|(bps, sample_format)| AudioFormat::Wav { bps, sample_format }),
) )
} }
fn flac<'a>() -> impl TokenParser<&'a str, AudioFormat> { fn flac<'a>() -> impl P<
LocatedSpan<&'a str>,
Output = AudioFormat,
Error = nom::error::Error<LocatedSpan<&'a str>>,
> {
preceded( preceded(
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>( tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
AudioFormatDiscriminants::Flac.into(), AudioFormatDiscriminants::Flac.into(),
@ -362,11 +351,15 @@ fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error>
.map(|bps| AudioFormat::Flac { bps }), .map(|bps| AudioFormat::Flac { bps }),
) )
} }
fn parser<'a>() -> impl TokenParser<&'a str, AudioFormat> { fn parser<'a>() -> impl P<
LocatedSpan<&'a str>,
Output = AudioFormat,
Error = nom::error::Error<LocatedSpan<&'a str>>,
> {
alt(( alt((
mp3(), mp3::<'a>(),
wav(), wav::<'a>(),
flac(), flac::<'a>(),
rest.map_res(|r: LocatedSpan<&'a str>| { rest.map_res(|r: LocatedSpan<&'a str>| {
Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(AudioFormat::Raw( Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(AudioFormat::Raw(
RawAudioFormat::try_from(*r) RawAudioFormat::try_from(*r)

View file

@ -1,42 +1,84 @@
use std::collections::HashMap; use std::{collections::HashMap, str::FromStr};
use fasteval::Instruction; use derive_new::new;
use fasteval::{Compiler, Instruction, Slab};
pub type TokenVec = Vec<Box<dyn Token>>; pub type TokenVec = Vec<Box<dyn Token>>;
pub trait Token { pub trait Token {
fn apply(&self, context: Context) -> Context; fn apply(&self, context: Context) -> Context {
todo!()
}
} }
#[cfg_attr(debug_assertions, derive(Default))] #[cfg_attr(debug_assertions, derive(Default))]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Silence; pub struct Silence;
impl Token for Silence {}
#[cfg_attr(debug_assertions, derive(Default))] #[cfg_attr(debug_assertions, derive(Default))]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Marker; pub struct Marker;
impl Token for Marker {}
#[cfg_attr(debug_assertions, derive(Default))] #[cfg_attr(debug_assertions, derive(Default))]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Note(pub u8); pub struct Note(pub u8);
impl Token for Note {}
#[cfg_attr(debug_assertions, derive(Default))] #[cfg_attr(debug_assertions, derive(Default))]
pub struct VariableChange(pub char, pub Instruction); pub struct VariableChange(pub char, pub Expression);
impl Token for VariableChange {}
#[cfg_attr(debug_assertions, derive(Default))] #[cfg_attr(debug_assertions, derive(Default))]
pub struct Loop(pub usize, pub TokenVec); pub struct Loop(pub usize, pub TokenVec);
impl Token for Loop {}
#[cfg_attr(debug_assertions, derive(Default))] #[cfg_attr(debug_assertions, derive(Default))]
pub struct Tuplet(pub TokenVec); pub struct Tuplet(pub TokenVec);
impl Token for Tuplet {}
#[cfg_attr(debug_assertions, derive(Default))] #[cfg_attr(debug_assertions, derive(Default))]
pub struct Slope(pub VariableChange, pub TokenVec); pub struct Slope(pub VariableChange, pub TokenVec);
impl Token for Slope {}
#[derive(new)]
#[cfg_attr(debug_assertions, derive(Debug, Default))]
pub struct Expression {
pub(crate) instruction: Instruction,
pub(crate) slab: Slab,
}
impl Clone for Expression {
fn clone(&self) -> Self {
unimplemented!()
}
}
impl FromStr for Expression {
type Err = fasteval::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut slab = Slab::new();
let instruction = fasteval::Parser::new()
.parse(s, &mut slab.ps)?
.from(&slab.ps)
.compile(&slab.ps, &mut slab.cs);
Ok(Expression::new(instruction, slab))
}
}
pub struct Context { pub struct Context {
pub result: Vec<f64>, pub result: Vec<f64>,
pub variables: HashMap<char, f64>, pub variables: HashMap<char, f64>,
pub instrument: Instruction, pub instrument: Expression,
pub slopes: HashMap<char, Instruction>, pub slopes: HashMap<char, Expression>,
} }
impl Context { impl Context {

View file

@ -1,2 +1,4 @@
#![allow(dead_code)]
pub mod compiler; pub mod compiler;
pub mod parser; pub mod parser;

View file

@ -1,20 +1,23 @@
use std::collections::HashMap; use std::{
collections::{BTreeMap, HashMap},
fmt::Display,
};
use derive_builder::Builder; use derive_builder::Builder;
use nom::{Compare, Input, combinator::success, error::Error}; use fasteval::Evaler;
use nom::{
AsBytes, AsChar, Compare, FindToken, Finish, IResult, Input, Offset, Parser as NomParser,
branch::alt,
bytes::complete::{tag, take},
character::{complete::char, streaming::one_of},
combinator::value,
error::{Error, ErrorKind},
multi::many0,
sequence::preceded,
};
use nom_locate::LocatedSpan; use nom_locate::LocatedSpan;
use crate::compiler::{Marker, Note, Silence, Token, VariableChange}; use crate::compiler::{Expression, Marker, Note, Silence, Token, VariableChange};
pub trait TokenParser<I, O>:
nom::Parser<LocatedSpan<I>, Output = O, Error = Error<LocatedSpan<I>>>
{
}
impl<I, T, O> TokenParser<I, O> for T where
T: nom::Parser<LocatedSpan<I>, Output = O, Error = Error<LocatedSpan<I>>>
{
}
pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> { pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> {
pub notes: &'n [I], pub notes: &'n [I],
@ -23,33 +26,181 @@ pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<
} }
#[derive(Builder)] #[derive(Builder)]
pub struct Parser<'i, 'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> { pub struct Parser<'i, 'n, 's, 'v, C: Into<char>> {
input: &'i I, input: &'i str,
notes: &'n [I], notes: &'n [String],
slopes: &'s HashMap<I, VariableChange>, slopes: &'s HashMap<String, VariableChange>,
variables: &'v [C], variables: &'v [C],
} }
impl<'i, 'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> Parser<'i, 'n, 's, 'v, I, C> { impl<'i, 'n, 's, 'v, C> Parser<'i, 'n, 's, 'v, C>
pub fn parse_all() -> Result<Vec<Box<dyn Token>>, Error<nom_locate::LocatedSpan<&'i I>>> { where
todo!() C: Into<char> + Clone + Ord + Display,
&'v [C]: FindToken<char>,
{
pub fn parse_all(
&self,
) -> Result<Vec<Box<dyn Token>>, Error<nom_locate::LocatedSpan<&'i str>>> {
many0(alt((
Silence::parser().map(into_box),
Marker::parser().map(into_box),
Note::parser(self.notes).map(into_box),
VariableChange::parser::<LocatedSpan<&'i str>, C>(self.variables).map(into_box),
)))
.parse_complete(LocatedSpan::new(self.input))
.finish()
.map(|(_, o)| o)
} }
} }
fn into_box(token: impl Token + 'static) -> Box<dyn Token> {
Box::new(token)
}
pub type TokenResult<'a, T> = IResult<LocatedSpan<&'a str>, T>;
impl Silence { impl Silence {
fn parser<I: Input>() -> impl TokenParser<I, Self> { fn parser<'a>() -> impl NomParser<
success(Default::default()) LocatedSpan<&'a str>,
Output = Self,
Error = nom::error::Error<LocatedSpan<&'a str>>,
> {
value(Self, char('.'))
} }
} }
impl Marker { impl Marker {
fn parser<I: Input>() -> impl TokenParser<I, Self> { fn parser<'a>() -> impl NomParser<
success(Default::default()) LocatedSpan<&'a str>,
Output = Self,
Error = nom::error::Error<LocatedSpan<&'a str>>,
> {
value(Marker, char('%'))
} }
} }
impl Note { impl Note {
fn parser<I: Input>() -> impl TokenParser<I, Self> { fn parser<'a, 'n>(
success(Default::default()) notes: &'n [String],
) -> impl NomParser<
LocatedSpan<&'a str>,
Output = Self,
Error = nom::error::Error<LocatedSpan<&'a str>>,
> {
move |input: LocatedSpan<&'a str>| {
let mut parsers: Vec<Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>> =
notes
.iter()
.enumerate()
.map(|(i, t)| {
Box::new(move |input: LocatedSpan<&'a str>| {
value(Note(i.clone() as u8), tag(t.clone().as_str())).parse(input)
})
as Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>
})
.collect();
alt(parsers.as_mut_slice()).parse(input)
}
}
}
impl VariableChange {
fn parser<'c, I: Input + AsBytes + Offset, C: Into<char> + Clone + Ord + Display>(
variables: &'c [C],
) -> impl for<'a> Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>
where
<I as Input>::Item: AsChar,
&'c [C]: FindToken<char>,
{
move |i: LocatedSpan<&str>| {
preceded(char('$'), one_of(variables))
.and(expression_parser(variables))
.map(|(name, change)| VariableChange(name, change))
.parse(i)
}
}
}
/// Will return the longest valid fasteval expression
fn expression_parser<'c, C: Into<char> + Ord + Display>(
variables: &'c [C],
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Expression> {
move |input: LocatedSpan<&str>| {
let mut end_index = 0;
let mut current_expression = None;
while input.input_len() > end_index {
if let Some(e) = (&input[..end_index + 1])
.parse::<Expression>()
.ok()
.and_then(|e| {
e.instruction
.eval(
&e.slab,
&mut BTreeMap::from_iter(
variables.into_iter().map(|v| (v.to_string(), 0.0)),
),
)
.ok()
.is_some()
.then_some(e)
})
{
current_expression = Some(e);
end_index += 1;
} else {
if let Some(e) = current_expression {
return take(end_index).parse(input).map(move |(r, _)| (r, e));
} else {
return Err(nom::Err::Failure(nom::error::Error::new(
input,
ErrorKind::Satisfy,
)));
}
}
}
if let Some(e) = current_expression {
Ok::<(LocatedSpan<&str>, Expression), nom::Err<nom::error::Error<LocatedSpan<&str>>>>((
input, e,
))
} else {
Err(nom::Err::Incomplete(nom::Needed::Unknown))
}
}
}
#[cfg(test)]
mod tests {
use nom_locate::LocatedSpan;
use crate::parser::expression_parser;
#[test]
fn expression_parser_test() {
let parser = expression_parser::<char>(&['x']);
let mut working_test_cases = vec![
"1",
"1x",
"56coucou", // should stop after 56 because c is not a known variable
"8x + 1 heille salut ça va ou quoi 46 - 5x",
];
let mut not_working_test_cases = vec![
"",
"(",
"y",
"abcdexx489",
" ", // spaces are not expressions
" h", // because of previous, this fails
" 1", // this too but the parser should remain dumb and not expect spaces before / after
];
for test in working_test_cases.drain(..) {
let output = parser(LocatedSpan::new(test));
assert!(matches!(output, Ok(_)), "result was not Ok: {output:?}");
}
for test in not_working_test_cases.drain(..) {
let output = parser(LocatedSpan::new(test));
assert!(matches!(output, Err(_)), "result was not Err: {output:?}");
}
} }
} }