two parsers with macros, enum deps, allow dead code
This commit is contained in:
parent
87abf20e67
commit
f9841267ec
6 changed files with 126 additions and 2 deletions
|
@ -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"
|
||||
|
|
12
bng_macros/Cargo.toml
Normal file
12
bng_macros/Cargo.toml
Normal 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
79
bng_macros/src/lib.rs
Normal 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")
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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<T> {
|
||||
pub(super) trait Token<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 {
|
||||
fn token(self) -> char {
|
||||
match self {
|
||||
|
|
5
src/bng/score/lex/lexer.rs
Normal file
5
src/bng/score/lex/lexer.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use nom::IResult;
|
||||
|
||||
pub(crate) trait Parse: Sized {
|
||||
fn parse(input: &str) -> IResult<&str, Self>;
|
||||
}
|
Loading…
Reference in a new issue