Compare commits
2 commits
040bb4ecb3
...
b2b8546f20
Author | SHA1 | Date | |
---|---|---|---|
b2b8546f20 | |||
9e0ead7605 |
4 changed files with 123 additions and 62 deletions
|
@ -1,7 +1,8 @@
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
use proc_macro2::Span;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields};
|
use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, Ident};
|
||||||
|
|
||||||
#[proc_macro_derive(ModifierParser)]
|
#[proc_macro_derive(ModifierParser)]
|
||||||
pub fn modifier_parser(input: TokenStream) -> TokenStream {
|
pub fn modifier_parser(input: TokenStream) -> TokenStream {
|
||||||
|
@ -31,11 +32,12 @@ fn impl_modifier_parser(ast: DeriveInput) -> TokenStream {
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected unnamed fields in enum variants");
|
panic!("Expected unnamed fields in enum variants");
|
||||||
};
|
};
|
||||||
|
let const_name =
|
||||||
|
Ident::new(&variant.ident.to_string().to_uppercase(), Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
nom::combinator::map(
|
nom::combinator::map(
|
||||||
nom::sequence::preceded(
|
nom::sequence::preceded(
|
||||||
nom::character::complete::char(#name::#variant_name(Default::default()).token()),
|
nom::character::complete::char(#name::#const_name),
|
||||||
nom::character::complete::#variant_type
|
nom::character::complete::#variant_type
|
||||||
),
|
),
|
||||||
#name::#variant_name
|
#name::#variant_name
|
||||||
|
@ -63,15 +65,15 @@ fn impl_slope_modifier_parser(ast: DeriveInput) -> TokenStream {
|
||||||
if let Data::Enum(DataEnum { variants, .. }) = ast.data {
|
if let Data::Enum(DataEnum { variants, .. }) = ast.data {
|
||||||
let match_arms = variants.iter().map(|variant| {
|
let match_arms = variants.iter().map(|variant| {
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
|
let const_name = Ident::new(&variant.ident.to_string().to_uppercase(),Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
tag(#name::#variant_name)
|
nom::combinator::value(#name::#variant_name, nom::character::complete::char(SlopeModifier::#const_name))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl lex::lexer::Parse for #name {
|
impl lex::lexer::Parse for #name {
|
||||||
fn parse(input: &str) -> nom::IResult<&str, #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((
|
nom::branch::alt((
|
||||||
#(#match_arms),*
|
#(#match_arms),*
|
||||||
))(input)
|
))(input)
|
||||||
|
@ -89,18 +91,19 @@ fn impl_quick_modifier_parser(ast: DeriveInput) -> TokenStream {
|
||||||
if let Data::Enum(DataEnum { variants, .. }) = ast.data {
|
if let Data::Enum(DataEnum { variants, .. }) = ast.data {
|
||||||
let match_arms = variants.iter().map(|variant| {
|
let match_arms = variants.iter().map(|variant| {
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
|
let const_name =
|
||||||
|
Ident::new(&variant.ident.to_string().to_uppercase(), Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
nom::combinator::map(
|
nom::combinator::map(
|
||||||
nom::branch::alt(
|
nom::branch::alt(
|
||||||
(
|
(
|
||||||
nom::combinator::value(
|
nom::combinator::value(
|
||||||
true,
|
lex::MORE,
|
||||||
nom::character::complete::char(#name::#variant_name(Default::default()).token().0)
|
nom::character::complete::char(#name::#const_name.0)
|
||||||
),
|
),
|
||||||
nom::combinator::value(
|
nom::combinator::value(
|
||||||
false,
|
lex::LESS,
|
||||||
nom::character::complete::char(#name::#variant_name(Default::default()).token().1)
|
nom::character::complete::char(#name::#const_name.1)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,10 +2,9 @@ use bng_macros::{ModifierParser, QuickModifierParser, 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(char),
|
||||||
Rest,
|
Rest,
|
||||||
StartHere,
|
StartHere,
|
||||||
Modifier(Modifier),
|
Modifier(Modifier),
|
||||||
|
@ -16,6 +15,30 @@ pub(super) enum Atom {
|
||||||
Comment,
|
Comment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) enum FlatAtom {
|
||||||
|
Note(char),
|
||||||
|
Rest,
|
||||||
|
StartHere,
|
||||||
|
Modifier(Modifier),
|
||||||
|
QuickModifier(QuickModifier),
|
||||||
|
LoopStarts(u8),
|
||||||
|
LoopEnds,
|
||||||
|
TupleStarts,
|
||||||
|
TupleEnds,
|
||||||
|
SlopeStarts(SlopeModifier, Instruction),
|
||||||
|
SlopeEnds,
|
||||||
|
Comment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for FlatAtom {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Rest => Self::Rest,
|
||||||
|
_ => unimplemented!("variant can't be cloned"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(ModifierParser)]
|
#[derive(ModifierParser)]
|
||||||
pub(super) enum Modifier {
|
pub(super) enum Modifier {
|
||||||
Volume(u8),
|
Volume(u8),
|
||||||
|
@ -24,6 +47,12 @@ pub(super) enum Modifier {
|
||||||
Tempo(u16),
|
Tempo(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Modifier {
|
||||||
|
fn default() -> Self {
|
||||||
|
Modifier::Volume(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(QuickModifierParser)]
|
#[derive(QuickModifierParser)]
|
||||||
pub(super) enum QuickModifier {
|
pub(super) enum QuickModifier {
|
||||||
Volume(bool),
|
Volume(bool),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use super::{Atom, Modifier, QuickModifier, SlopeModifier};
|
use super::{Atom, FlatAtom, Modifier, QuickModifier, SlopeModifier};
|
||||||
|
|
||||||
pub(super) mod lexer;
|
pub(super) mod lexer;
|
||||||
|
|
||||||
const MORE: bool = true;
|
pub(super) const MORE: bool = true;
|
||||||
const LESS: bool = false;
|
pub(super) const LESS: bool = false;
|
||||||
const ON: bool = true;
|
pub(super) const ON: bool = true;
|
||||||
const OFF: bool = false;
|
pub(super) const OFF: bool = false;
|
||||||
|
|
||||||
struct WrappingTokens;
|
struct WrappingTokens;
|
||||||
impl WrappingTokens {
|
impl WrappingTokens {
|
||||||
|
@ -19,51 +19,34 @@ impl WrappingTokens {
|
||||||
const QUOTE_DEG: (char, char) = ('\'', '°');
|
const QUOTE_DEG: (char, char) = ('\'', '°');
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) trait Token<T> {
|
impl QuickModifier {
|
||||||
fn token(self) -> T;
|
pub(super) const VOLUME: (char, char) = WrappingTokens::PLUS_MINUS;
|
||||||
|
pub(super) const OCTAVE: (char, char) = WrappingTokens::RIGHT_LEFT;
|
||||||
|
pub(super) const LENGTH: (char, char) = WrappingTokens::SLASH_BACKSLASH;
|
||||||
|
pub(super) const PIZZ: (char, char) = WrappingTokens::QUOTE_DEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token<(char, char)> for QuickModifier {
|
impl Modifier {
|
||||||
fn token(self) -> (char, char) {
|
pub(super) const VOLUME: char = 'v';
|
||||||
match self {
|
pub(super) const OCTAVE: char = 'o';
|
||||||
Self::Volume(_) => WrappingTokens::PLUS_MINUS,
|
pub(super) const LENGTH: char = 'l';
|
||||||
Self::Octave(_) => WrappingTokens::RIGHT_LEFT,
|
pub(super) const TEMPO: char = 't';
|
||||||
Self::Length(_) => WrappingTokens::SLASH_BACKSLASH,
|
|
||||||
Self::Pizz(_) => WrappingTokens::QUOTE_DEG,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token<char> for Modifier {
|
impl SlopeModifier {
|
||||||
fn token(self) -> char {
|
pub(super) const NOTE: char = 'n';
|
||||||
match self {
|
pub(super) const VOLUME: char = 'v';
|
||||||
Self::Volume(_) => 'v',
|
pub(super) const OCTAVE: char = 'o';
|
||||||
Self::Octave(_) => 'o',
|
pub(super) const LENGTH: char = 'l';
|
||||||
Self::Length(_) => 'l',
|
pub(super) const TEMPO: char = 't';
|
||||||
Self::Tempo(_) => 't',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token<char> for SlopeModifier {
|
impl Atom {
|
||||||
fn token(self) -> char {
|
pub(super) const REST: char = '.';
|
||||||
match self {
|
pub(super) const START_HERE: char = ':';
|
||||||
Self::Note => 'n',
|
pub(super) const MODIFIER: char = '!';
|
||||||
Self::Volume => 'v',
|
pub(super) const LOOP: (char, char) = WrappingTokens::PARENTHESES;
|
||||||
Self::Octave => 'o',
|
pub(super) const TUPLE: (char, char) = WrappingTokens::SQUARE_BRACKETS;
|
||||||
Self::Length => 'l',
|
pub(super) const SLOPE: (char, char) = WrappingTokens::BRACKETS;
|
||||||
Self::Tempo => 't',
|
pub(super) const COMMENT: (char, char) = WrappingTokens::SEMICOLON_COMMA;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token<char> for Atom {
|
|
||||||
fn token(self) -> char {
|
|
||||||
match self {
|
|
||||||
Atom::Rest => '.',
|
|
||||||
Atom::StartHere => ':',
|
|
||||||
Atom::Modifier(_) => '!',
|
|
||||||
_ => unimplemented!("not a singleton"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,57 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use clap::builder::TypedValueParser;
|
||||||
|
use fasteval::{Compiler, Instruction};
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt, character::complete::char, combinator::value, sequence::delimited, IResult, Parser,
|
branch::alt,
|
||||||
|
bytes::complete::take_till1,
|
||||||
|
character::complete::{anychar, char, one_of, u8},
|
||||||
|
combinator::{map_res, value},
|
||||||
|
multi::many0,
|
||||||
|
sequence::{delimited, pair, preceded, separated_pair},
|
||||||
|
Err, IResult, Parser,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::bng::score::QuickModifier;
|
use crate::bng::score::{Atom, FlatAtom, Modifier, QuickModifier, SlopeModifier};
|
||||||
|
|
||||||
use super::Token;
|
|
||||||
|
|
||||||
pub(crate) trait Parse: Sized {
|
pub(crate) trait Parse: Sized {
|
||||||
fn parse(input: &str) -> IResult<&str, Self>;
|
fn parse(input: &str) -> IResult<&str, Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn atom_parser<'a>(notes: &'a str) -> impl Parser<&str, FlatAtom, nom::error::Error<&str>> {
|
||||||
|
alt((
|
||||||
|
one_of(notes).map(FlatAtom::Note),
|
||||||
|
value(FlatAtom::Rest, char(Atom::REST)),
|
||||||
|
value(FlatAtom::StartHere, char(Atom::START_HERE)),
|
||||||
|
preceded(char(Atom::MODIFIER), Modifier::parse).map(FlatAtom::Modifier),
|
||||||
|
QuickModifier::parse.map(FlatAtom::QuickModifier),
|
||||||
|
preceded(char(Atom::LOOP.0), u8).map(FlatAtom::LoopStarts),
|
||||||
|
value(FlatAtom::LoopEnds, char(Atom::LOOP.1)),
|
||||||
|
value(FlatAtom::TupleStarts, char(Atom::TUPLE.0)),
|
||||||
|
value(FlatAtom::TupleEnds, char(Atom::TUPLE.1)),
|
||||||
|
preceded(
|
||||||
|
char(Atom::SLOPE.0),
|
||||||
|
separated_pair(
|
||||||
|
SlopeModifier::parse,
|
||||||
|
char(' '),
|
||||||
|
map_res(take_till1(|c| c == ','), |s| {
|
||||||
|
let parser = fasteval::Parser::new();
|
||||||
|
let mut slab = fasteval::Slab::new();
|
||||||
|
Result::<Instruction, nom::error::Error<&str>>::Ok(
|
||||||
|
parser
|
||||||
|
.parse(s, &mut slab.ps)
|
||||||
|
.map_err(|_| nom::error::Error::new(s, nom::error::ErrorKind::Verify))?
|
||||||
|
.from(&slab.ps)
|
||||||
|
.compile(&slab.ps, &mut slab.cs),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map(|(sm, i)| FlatAtom::SlopeStarts(sm, i)),
|
||||||
|
value(FlatAtom::SlopeEnds, char(Atom::SLOPE.1)),
|
||||||
|
value(
|
||||||
|
FlatAtom::Comment,
|
||||||
|
delimited(char(Atom::COMMENT.0), anychar, char(Atom::COMMENT.1)),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue