two parsers with macros, enum deps, allow dead code

This commit is contained in:
Ponj 2024-10-04 12:30:24 -04:00
parent 87abf20e67
commit f9841267ec
Signed by: p6nj
GPG key ID: 6FED68D87C479A59
6 changed files with 126 additions and 2 deletions

View file

@ -14,9 +14,18 @@ nom = "7.1.3"
serde = "1.0.209" serde = "1.0.209"
serde_yml = "0.0.12" serde_yml = "0.0.12"
splines = "4.3.1" splines = "4.3.1"
strum = { version = "0.26", features = ["derive"] }
strum_macros = "0.26"
tinyaudio = { version = "0.1", optional = true } tinyaudio = { version = "0.1", optional = true }
bng_macros = { path = "bng_macros" }
[features] [features]
default = ["play", "save"] default = ["play", "save"]
play = ["dep:tinyaudio"] play = ["dep:tinyaudio"]
save = [] save = []
[workspace]
members = ["bng_macros"]
[lints.rust]
unused = "allow"

12
bng_macros/Cargo.toml Normal file
View file

@ -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"] }

79
bng_macros/src/lib.rs Normal file
View file

@ -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")
}
}

View file

@ -1,6 +1,8 @@
use bng_macros::{ModifierParser, SlopeModifierParser};
use fasteval::Instruction; use fasteval::Instruction;
mod lex; mod lex;
use lex::Token;
pub(super) enum Atom { pub(super) enum Atom {
Note(u8), Note(u8),
@ -12,6 +14,7 @@ pub(super) enum Atom {
EmptyWrapper(WrapperKind), EmptyWrapper(WrapperKind),
} }
#[derive(ModifierParser)]
pub(super) enum Modifier { pub(super) enum Modifier {
Volume(u8), Volume(u8),
Octave(u8), Octave(u8),
@ -33,6 +36,7 @@ pub(super) enum WrapperKind {
Comment, Comment,
} }
#[derive(Clone, Copy, SlopeModifierParser)]
pub(super) enum SlopeModifier { pub(super) enum SlopeModifier {
Note, Note,
Volume, Volume,

View file

@ -1,4 +1,7 @@
use super::{Atom, Modifier, WrapperKind}; use super::{Atom, Modifier, SlopeModifier, WrapperKind};
pub(super) mod lexer;
const MORE: bool = true; const MORE: bool = true;
const LESS: bool = false; const LESS: bool = false;
const ON: bool = true; const ON: bool = true;
@ -12,7 +15,7 @@ impl WrappingTokens {
const SEMICOLON_COMMA: (char, char) = (';', ','); const SEMICOLON_COMMA: (char, char) = (';', ',');
} }
trait Token<T> { pub(super) trait Token<T> {
fn token(self) -> T; fn token(self) -> T;
} }
@ -38,6 +41,18 @@ impl Token<char> for Modifier {
} }
} }
impl Token<char> 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<char> for Atom { impl Token<char> for Atom {
fn token(self) -> char { fn token(self) -> char {
match self { match self {

View file

@ -0,0 +1,5 @@
use nom::IResult;
pub(crate) trait Parse: Sized {
fn parse(input: &str) -> IResult<&str, Self>;
}