first four parsers
This commit is contained in:
parent
0240602c19
commit
4fa49e2181
4 changed files with 263 additions and 75 deletions
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::Debug,
|
||||
|
@ -7,32 +9,32 @@ use std::{
|
|||
str::FromStr,
|
||||
};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use bliplib::parser::TokenParser;
|
||||
use clap::{Parser, builder::EnumValueParser};
|
||||
use derive_new::new;
|
||||
use anyhow::anyhow;
|
||||
use bliplib::compiler::Expression;
|
||||
use clap::Parser;
|
||||
use derive_wrapper::AsRef;
|
||||
use fasteval::{Compiler, Instruction, Slab};
|
||||
use hound::SampleFormat;
|
||||
use mp3lame_encoder::{Bitrate, Quality};
|
||||
use nom::{
|
||||
AsBytes, Compare, Finish, Input, Offset, Parser as _,
|
||||
Finish, Parser as P,
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete::{char, u16, usize},
|
||||
combinator::{opt, rest, success, value},
|
||||
error::{ErrorKind, ParseError},
|
||||
combinator::{rest, success, value},
|
||||
error::ErrorKind,
|
||||
sequence::preceded,
|
||||
};
|
||||
use nom_locate::{LocatedSpan, position};
|
||||
use strum::{Display, EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr, ToString};
|
||||
use strum::{EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr};
|
||||
use thiserror::Error;
|
||||
|
||||
const DEFAULT_INSTRUMENT: &str = "sin(2*PI*(442+442*((n+1)/N))*t)";
|
||||
const DEFAULT_LENGTH: &str = "2^(2-log_2(l))*(60/T)";
|
||||
|
||||
fn main() {
|
||||
dbg!(Cli::parse());
|
||||
let cli = Cli::parse();
|
||||
#[cfg(debug_assertions)]
|
||||
dbg!(cli);
|
||||
}
|
||||
|
||||
#[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)]
|
||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
||||
#[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 mp3<'a>() -> impl TokenParser<&'a str, AudioFormat> {
|
||||
fn audio_format_parser(input: &str) -> Result<AudioFormat, anyhow::Error> {
|
||||
fn mp3<'a>() -> impl P<
|
||||
LocatedSpan<&'a str>,
|
||||
Output = AudioFormat,
|
||||
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||
> {
|
||||
preceded(
|
||||
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||
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(
|
||||
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||
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 }),
|
||||
)
|
||||
}
|
||||
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(
|
||||
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||
AudioFormatDiscriminants::Flac.into(),
|
||||
|
@ -362,11 +351,15 @@ fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error>
|
|||
.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((
|
||||
mp3(),
|
||||
wav(),
|
||||
flac(),
|
||||
mp3::<'a>(),
|
||||
wav::<'a>(),
|
||||
flac::<'a>(),
|
||||
rest.map_res(|r: LocatedSpan<&'a str>| {
|
||||
Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(AudioFormat::Raw(
|
||||
RawAudioFormat::try_from(*r)
|
||||
|
|
|
@ -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 trait Token {
|
||||
fn apply(&self, context: Context) -> Context;
|
||||
fn apply(&self, context: Context) -> Context {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, derive(Default))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Silence;
|
||||
|
||||
impl Token for Silence {}
|
||||
|
||||
#[cfg_attr(debug_assertions, derive(Default))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Marker;
|
||||
|
||||
impl Token for Marker {}
|
||||
|
||||
#[cfg_attr(debug_assertions, derive(Default))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Note(pub u8);
|
||||
|
||||
impl Token for Note {}
|
||||
|
||||
#[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))]
|
||||
pub struct Loop(pub usize, pub TokenVec);
|
||||
|
||||
impl Token for Loop {}
|
||||
|
||||
#[cfg_attr(debug_assertions, derive(Default))]
|
||||
pub struct Tuplet(pub TokenVec);
|
||||
|
||||
impl Token for Tuplet {}
|
||||
|
||||
#[cfg_attr(debug_assertions, derive(Default))]
|
||||
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 result: Vec<f64>,
|
||||
pub variables: HashMap<char, f64>,
|
||||
pub instrument: Instruction,
|
||||
pub slopes: HashMap<char, Instruction>,
|
||||
pub instrument: Expression,
|
||||
pub slopes: HashMap<char, Expression>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub mod compiler;
|
||||
pub mod parser;
|
||||
|
|
203
src/parser.rs
203
src/parser.rs
|
@ -1,20 +1,23 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt::Display,
|
||||
};
|
||||
|
||||
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 crate::compiler::{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>>>
|
||||
{
|
||||
}
|
||||
use crate::compiler::{Expression, Marker, Note, Silence, Token, VariableChange};
|
||||
|
||||
pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> {
|
||||
pub notes: &'n [I],
|
||||
|
@ -23,33 +26,181 @@ pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<
|
|||
}
|
||||
|
||||
#[derive(Builder)]
|
||||
pub struct Parser<'i, 'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> {
|
||||
input: &'i I,
|
||||
notes: &'n [I],
|
||||
slopes: &'s HashMap<I, VariableChange>,
|
||||
pub struct Parser<'i, 'n, 's, 'v, C: Into<char>> {
|
||||
input: &'i str,
|
||||
notes: &'n [String],
|
||||
slopes: &'s HashMap<String, VariableChange>,
|
||||
variables: &'v [C],
|
||||
}
|
||||
|
||||
impl<'i, 'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> Parser<'i, 'n, 's, 'v, I, C> {
|
||||
pub fn parse_all() -> Result<Vec<Box<dyn Token>>, Error<nom_locate::LocatedSpan<&'i I>>> {
|
||||
todo!()
|
||||
impl<'i, 'n, 's, 'v, C> Parser<'i, 'n, 's, 'v, C>
|
||||
where
|
||||
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 {
|
||||
fn parser<I: Input>() -> impl TokenParser<I, Self> {
|
||||
success(Default::default())
|
||||
fn parser<'a>() -> impl NomParser<
|
||||
LocatedSpan<&'a str>,
|
||||
Output = Self,
|
||||
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||
> {
|
||||
value(Self, char('.'))
|
||||
}
|
||||
}
|
||||
|
||||
impl Marker {
|
||||
fn parser<I: Input>() -> impl TokenParser<I, Self> {
|
||||
success(Default::default())
|
||||
fn parser<'a>() -> impl NomParser<
|
||||
LocatedSpan<&'a str>,
|
||||
Output = Self,
|
||||
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||
> {
|
||||
value(Marker, char('%'))
|
||||
}
|
||||
}
|
||||
|
||||
impl Note {
|
||||
fn parser<I: Input>() -> impl TokenParser<I, Self> {
|
||||
success(Default::default())
|
||||
fn parser<'a, 'n>(
|
||||
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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue