From 87abf20e674a60a485cd2361e6b64fdb390d8da3 Mon Sep 17 00:00:00 2001 From: p6nj Date: Fri, 4 Oct 2024 12:29:28 -0400 Subject: [PATCH 1/2] new slope system --- poc/poc.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/poc/poc.yml b/poc/poc.yml index 83e7cf7..c6f23c4 100644 --- a/poc/poc.yml +++ b/poc/poc.yml @@ -24,7 +24,7 @@ channels: (3deff) (deff) [ffe] - {1-cos((PI*x)/2),acced} + {l 1-cos((PI*x)/2),acced} abbc!o5cc!v15feed!l4fedd!t60hdd # rest: . # pizz.: '° @@ -33,7 +33,12 @@ channels: # octave: >< # comment?: ;, # start here: ':' - # glissando: {EXPR, score} + # slope: {MODIFIER EXPR, score} + # note modifier prefix: n + # volume modifier prefix: v + # octave modifier prefix: o + # length modifier prefix: l + # tempo modifier prefix: t # loop: () # loop with count: (COUNT, score) # tuple: [] From f9841267ec9c55fd81dbb418277640f6c8d72e75 Mon Sep 17 00:00:00 2001 From: p6nj Date: Fri, 4 Oct 2024 12:30:24 -0400 Subject: [PATCH 2/2] two parsers with macros, enum deps, allow dead code --- Cargo.toml | 9 +++++ bng_macros/Cargo.toml | 12 ++++++ bng_macros/src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++ src/bng/score.rs | 4 ++ src/bng/score/lex.rs | 19 ++++++++- src/bng/score/lex/lexer.rs | 5 +++ 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 bng_macros/Cargo.toml create mode 100644 bng_macros/src/lib.rs create mode 100644 src/bng/score/lex/lexer.rs diff --git a/Cargo.toml b/Cargo.toml index 10353de..dbeb371 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,18 @@ nom = "7.1.3" serde = "1.0.209" serde_yml = "0.0.12" splines = "4.3.1" +strum = { version = "0.26", features = ["derive"] } +strum_macros = "0.26" tinyaudio = { version = "0.1", optional = true } +bng_macros = { path = "bng_macros" } [features] default = ["play", "save"] play = ["dep:tinyaudio"] save = [] + +[workspace] +members = ["bng_macros"] + +[lints.rust] +unused = "allow" diff --git a/bng_macros/Cargo.toml b/bng_macros/Cargo.toml new file mode 100644 index 0000000..1f75c20 --- /dev/null +++ b/bng_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bng_macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full"] } diff --git a/bng_macros/src/lib.rs b/bng_macros/src/lib.rs new file mode 100644 index 0000000..bca0a42 --- /dev/null +++ b/bng_macros/src/lib.rs @@ -0,0 +1,79 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields}; + +#[proc_macro_derive(ModifierParser)] +pub fn modifier_parser(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + impl_modifier_parser(ast) +} + +#[proc_macro_derive(SlopeModifierParser)] +pub fn slope_modifier_parser(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + impl_slope_modifier_parser(ast) +} + +fn impl_modifier_parser(ast: DeriveInput) -> TokenStream { + let name = &ast.ident; + if let Data::Enum(DataEnum { variants, .. }) = ast.data { + let match_arms = variants.iter().map(|variant| { + let variant_name = &variant.ident; + let variant_type = if let Fields::Unnamed(ref fields) = variant.fields { + &fields.unnamed[0].ty + } else { + panic!("Expected unnamed fields in enum variants"); + }; + + quote! { + nom::combinator::map( + nom::sequence::preceded( + nom::character::complete::char(#name::#variant_name(Default::default()).token()), + nom::character::complete::#variant_type + ), + #name::#variant_name + ) + } + }); + + quote! { + impl lex::lexer::Parse for #name { + fn parse(input: &str) -> nom::IResult<&str, #name> { + nom::branch::alt(( + #(#match_arms),* + ))(input) + } + } + } + .into() + } else { + panic!("this macro only works on enums") + } +} + +fn impl_slope_modifier_parser(ast: DeriveInput) -> TokenStream { + let name = &ast.ident; + if let Data::Enum(DataEnum { variants, .. }) = ast.data { + let match_arms = variants.iter().map(|variant| { + let variant_name = &variant.ident; + quote! { + tag(#name::#variant_name) + } + }); + + quote! { + impl lex::lexer::Parse for #name { + fn parse(input: &str) -> nom::IResult<&str, #name> { + let tag = |sm: SlopeModifier| nom::combinator::value(sm, nom::character::complete::char(sm.token())); + nom::branch::alt(( + #(#match_arms),* + ))(input) + } + } + } + .into() + } else { + panic!("this macro only works on enums") + } +} diff --git a/src/bng/score.rs b/src/bng/score.rs index 26d8e95..27573ce 100644 --- a/src/bng/score.rs +++ b/src/bng/score.rs @@ -1,6 +1,8 @@ +use bng_macros::{ModifierParser, SlopeModifierParser}; use fasteval::Instruction; mod lex; +use lex::Token; pub(super) enum Atom { Note(u8), @@ -12,6 +14,7 @@ pub(super) enum Atom { EmptyWrapper(WrapperKind), } +#[derive(ModifierParser)] pub(super) enum Modifier { Volume(u8), Octave(u8), @@ -33,6 +36,7 @@ pub(super) enum WrapperKind { Comment, } +#[derive(Clone, Copy, SlopeModifierParser)] pub(super) enum SlopeModifier { Note, Volume, diff --git a/src/bng/score/lex.rs b/src/bng/score/lex.rs index 75ca318..f8b9dfe 100644 --- a/src/bng/score/lex.rs +++ b/src/bng/score/lex.rs @@ -1,4 +1,7 @@ -use super::{Atom, Modifier, WrapperKind}; +use super::{Atom, Modifier, SlopeModifier, WrapperKind}; + +pub(super) mod lexer; + const MORE: bool = true; const LESS: bool = false; const ON: bool = true; @@ -12,7 +15,7 @@ impl WrappingTokens { const SEMICOLON_COMMA: (char, char) = (';', ','); } -trait Token { +pub(super) trait Token { fn token(self) -> T; } @@ -38,6 +41,18 @@ impl Token for Modifier { } } +impl Token for SlopeModifier { + fn token(self) -> char { + match self { + Self::Note => 'n', + Self::Volume => 'v', + Self::Octave => 'o', + Self::Length => 'l', + Self::Tempo => 't', + } + } +} + impl Token for Atom { fn token(self) -> char { match self { diff --git a/src/bng/score/lex/lexer.rs b/src/bng/score/lex/lexer.rs new file mode 100644 index 0000000..ee3999b --- /dev/null +++ b/src/bng/score/lex/lexer.rs @@ -0,0 +1,5 @@ +use nom::IResult; + +pub(crate) trait Parse: Sized { + fn parse(input: &str) -> IResult<&str, Self>; +}