diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 643b049..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "blip" -version = "0.0.0" -edition = "2021" - -[dependencies] -amplify = "4" -anyhow = "1.0" -cfg-if = "1.0.0" -clap = { version = "4.5", features = ["derive"] } -derived-deref = "2.1.0" -fasteval = "0.2.4" -nom = "7.1.3" -serde = { version = "1.0", features = ["derive"] } -serde_yml = "0.0.12" -splines = "4" -strum = { version = "0.26", features = ["derive"] } -strum_macros = "0.26" -tinyaudio = { version = "1", optional = true } -bng_macros = { path = "bng_macros" } -const_format = "0.2.33" -thiserror = "2" -derive-new = "0.7" -naan = "0.1" -lazy_static = "1.5" -tune = "0.35" - -[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 deleted file mode 100644 index 9920c81..0000000 --- a/bng_macros/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "bng_macros" -version = "0.1.0" -edition = "2021" - -[lib] -proc-macro = true - -[dependencies] -inflector = "0.11.4" -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 deleted file mode 100644 index bed291d..0000000 --- a/bng_macros/src/lib.rs +++ /dev/null @@ -1,183 +0,0 @@ -extern crate proc_macro; -use inflector::Inflector; -use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::{quote, ToTokens}; -use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsUnnamed, Ident}; - -#[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) -} - -#[proc_macro_derive(QuickModifierParser)] -pub fn quick_modifier_parser(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - impl_quick_modifier_parser(ast) -} - -#[proc_macro_derive(ModifierParser)] -pub fn modifier_parser(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - impl_modifier_parser(ast) -} - -trait ToIdent: ToString { - fn to_ident(&self) -> Ident { - Ident::new(&self.to_string(), Span::call_site()) - } -} - -impl ToIdent for S {} - -fn impl_slope_modifier_parser(ast: DeriveInput) -> TokenStream { - let name = &ast.ident; - let fn_name = name.to_string().to_snake_case().to_ident(); - if let Data::Enum(DataEnum { variants, .. }) = ast.data { - let match_arms = variants.iter().map(|variant| { - let variant_name = &variant.ident; - let variant_name_lower = variant_name.to_string().to_lowercase().to_ident(); - let const_name = &variant.ident.to_string().to_uppercase().to_ident(); - quote! { - nom::error::context( - stringify!(#variant_name_lower), - nom::combinator::value( - #name::#variant_name, - nom::character::complete::char(SlopeModifier::#const_name) - ) - ) - } - }); - - quote! { - pub fn #fn_name<'a, E: nom::error::ParseError<&'a str> + nom::error::ContextError<&'a str>>( - i: &'a str, - ) -> nom::IResult<&'a str, #name, E> { - nom::error::context( - "slope modifier", - nom::branch::alt(( - #(#match_arms),* - )) - )(i) - } - } - .into() - } else { - panic!("this macro only works on enums") - } -} - -fn impl_quick_modifier_parser(ast: DeriveInput) -> TokenStream { - let name = &ast.ident; - let fn_name = name.to_string().to_snake_case().to_ident(); - if let Data::Enum(DataEnum { variants, .. }) = ast.data { - let match_arms = variants.iter().map(|variant| { - let variant_name = &variant.ident; - let variant_name_lower = variant_name.to_string().to_lowercase().to_ident(); - let const_name = &variant.ident.to_string().to_uppercase().to_ident(); - quote! { - nom::error::context( - stringify!(#variant_name_lower), - nom::combinator::map( - nom::branch::alt( - ( - nom::combinator::value( - lex::UP, - nom::character::complete::char(#name::#const_name.0) - ), - nom::combinator::value( - lex::DOWN, - nom::character::complete::char(#name::#const_name.1) - ) - ) - ), - #name::#variant_name - ) - ) - } - }); - - quote! { - pub fn #fn_name<'a, E: nom::error::ParseError<&'a str> + nom::error::ContextError<&'a str>>( - i: &'a str, - ) -> nom::IResult<&'a str, #name, E> { - nom::error::context( - "quick modifier", - nom::branch::alt(( - #(#match_arms),* - )) - )(i) - } - } - .into() - } else { - panic!("this macro only works on enums") - } -} - -fn impl_modifier_parser(ast: DeriveInput) -> TokenStream { - let name = &ast.ident; - let fn_name = name.to_string().to_snake_case().to_ident(); - if let Data::Enum(DataEnum { variants, .. }) = ast.data { - let match_arms = variants.iter().map(|variant| { - let variant_name = &variant.ident; - let variant_name_lower = variant_name.to_string().to_lowercase().to_ident(); - let const_name = &variant.ident.to_string().to_uppercase().to_ident(); - if let Fields::Unnamed(FieldsUnnamed { - paren_token: _, - unnamed, - }) = &variant.fields - { - let type_name = { - let type_name = &unnamed[0].ty.to_token_stream().to_string(); - match type_name.strip_prefix("NonZero") { - Some(s) => { - let lower = s.to_lowercase().to_ident(); - let type_name = &unnamed[0].ty; - quote! { - nom::combinator::map_opt( - nom::character::complete::#lower, - std::num::#type_name::new - ) - } - } - _ => { - let type_name = &unnamed[0].ty; - quote! { - nom::character::complete::#type_name - } - } - } - }; - quote! { - nom::error::context( - stringify!(#variant_name_lower), - nom::combinator::map( - nom::sequence::preceded(nom::character::complete::char(#name::#const_name), #type_name), - #name::#variant_name - ) - ) - } - } else { - panic!("this macro only works on unnamed fields") - } - }); - - quote! { - pub fn #fn_name<'a, E: nom::error::ParseError<&'a str> + nom::error::ContextError<&'a str>>( - i: &'a str, - ) -> nom::IResult<&'a str, #name, E> { - nom::error::context( - "modifier", - nom::branch::alt(( - #(#match_arms),* - )) - )(i) - } - } - .into() - } else { - panic!("this macro only works on enums") - } -} diff --git a/check.txt b/check.txt deleted file mode 100644 index e3c7726..0000000 --- a/check.txt +++ /dev/null @@ -1,122 +0,0 @@ - Checking bng v0.1.0 (/home/p6nj/bng) -error[E0720]: cannot resolve opaque type - --> src/bng/score/lex/lexer.rs:35:39 - | -35 | pub fn root<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atoms, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type -... -42 | / terminated( -43 | | many1(preceded(maybe_yml_str_space(), atom(notes))), -44 | | maybe_yml_str_space(), -45 | | ) -46 | | .map(Atoms) - | |_______________- returning here with type `nom::Map std::result::Result<(&str, std::vec::Vec), nom::Err>, fn(std::vec::Vec) -> Atoms {Atoms}, std::vec::Vec>` - | - ::: /home/p6nj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/sequence/mod.rs:100:6 - | -100 | ) -> impl FnMut(I) -> IResult - | ---------------------------------- returning this type `nom::Map std::result::Result<(&str, std::vec::Vec), nom::Err>, fn(std::vec::Vec) -> Atoms {Atoms}, std::vec::Vec>` - -error[E0720]: cannot resolve opaque type - --> src/bng/score/lex/lexer.rs:98:37 - | -98 | fn r#loop<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atom, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type -... -105 | / context( -106 | | "loop", -107 | | delimited( -108 | | char(Atom::LOOP.0), -... | -121 | | ) -122 | | .map(|(n, v)| Atom::Loop(n, v)) - | |___________________________________- returning here with type `nom::Map std::result::Result<(&str, (NonZero, Atoms)), nom::Err>, {closure@src/bng/score/lex/lexer.rs:122:10: 122:18}, (NonZero, Atoms)>` - | - ::: /home/p6nj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/error.rs:236:6 - | -236 | ) -> impl FnMut(I) -> IResult - | --------------------------------- returning this type `nom::Map std::result::Result<(&str, (NonZero, Atoms)), nom::Err>, {closure@src/bng/score/lex/lexer.rs:122:10: 122:18}, (NonZero, Atoms)>` - -error[E0720]: cannot resolve opaque type - --> src/bng/score/lex/lexer.rs:185:35 - | -185 | fn atom<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atom, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type -... -192 | / context( -193 | | "atom", -194 | | alt(( -195 | | note(notes), -... | -204 | | )), -205 | | ) - | |_____- returning here with type `impl FnMut(&str) -> std::result::Result<(&str, Atom), nom::Err>` - | - ::: /home/p6nj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/error.rs:236:6 - | -236 | ) -> impl FnMut(I) -> IResult - | --------------------------------- returning this opaque type `impl FnMut(&str) -> std::result::Result<(&str, Atom), nom::Err>` - -error[E0391]: cycle detected when computing type of opaque `bng::score::lex::lexer::root::{opaque#0}` - --> src/bng/score/lex/lexer.rs:35:39 - | -35 | pub fn root<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atoms, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires type-checking `bng::score::lex::lexer::root`... - --> src/bng/score/lex/lexer.rs:46:6 - | -46 | .map(Atoms) - | ^^^ - = note: ...which requires evaluating trait selection obligation `nom::sequence::terminated::{opaque#0}: core::marker::Sync`... - = note: ...which again requires computing type of opaque `bng::score::lex::lexer::root::{opaque#0}`, completing the cycle -note: cycle used when computing type of `bng::score::lex::lexer::root::{opaque#0}` - --> src/bng/score/lex/lexer.rs:35:39 - | -35 | pub fn root<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atoms, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0391]: cycle detected when computing type of opaque `bng::score::lex::lexer::tuple::{opaque#0}` - --> src/bng/score/lex/lexer.rs:125:36 - | -125 | fn tuple<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atom, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires type-checking `bng::score::lex::lexer::tuple`... - --> src/bng/score/lex/lexer.rs:136:6 - | -136 | .map(Atom::Tuple) - | ^^^ - = note: ...which requires evaluating trait selection obligation `nom::error::context::{opaque#0}: core::marker::Sync`... - = note: ...which again requires computing type of opaque `bng::score::lex::lexer::tuple::{opaque#0}`, completing the cycle -note: cycle used when computing type of `bng::score::lex::lexer::tuple::{opaque#0}` - --> src/bng/score/lex/lexer.rs:125:36 - | -125 | fn tuple<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atom, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0391]: cycle detected when computing type of opaque `bng::score::lex::lexer::slope::{opaque#0}` - --> src/bng/score/lex/lexer.rs:139:36 - | -139 | fn slope<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atom, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires type-checking `bng::score::lex::lexer::slope`... - --> src/bng/score/lex/lexer.rs:165:6 - | -165 | .map(|((sm, i), v)| Atom::Slope(sm, i, v)) - | ^^^ - = note: ...which requires evaluating trait selection obligation `nom::error::context::{opaque#0}: core::marker::Sync`... - = note: ...which again requires computing type of opaque `bng::score::lex::lexer::slope::{opaque#0}`, completing the cycle -note: cycle used when computing type of `bng::score::lex::lexer::slope::{opaque#0}` - --> src/bng/score/lex/lexer.rs:139:36 - | -139 | fn slope<'a, E>(notes: &'a str) -> impl Parser<&'a str, Atom, E> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -Some errors have detailed explanations: E0391, E0720. -For more information about an error, try `rustc --explain E0391`. -error: could not compile `bng` (bin "bng") due to 6 previous errors diff --git a/class.puml b/class.puml deleted file mode 100644 index c3d2103..0000000 --- a/class.puml +++ /dev/null @@ -1,61 +0,0 @@ -@startuml Class Diagram - -package core { - package str { - package traits { - interface FromStr { - from_str(s: &str): Result - } - } - } -} - -package cli { - package docstr {} - package schema { - +enum Cli { - +Play { PlayOpts } - +Export { ExportOptions } - +List { ListOptions } - } - +struct PlayOptions { - #input: FileContents - } - +struct ExportOptions { - ~input: FileContents - ~output: AudioFileName - } - +enum ListOptions { - +Extensions - +struct Math { -kind: Option } - +Glyphs - } - +enum MathDocKind { - +Functions - +Operators - +Literals - } - +struct FileContents { - 0: String - } - FileContents --|> FromStr: type Error = io::Error - +struct AudioFileName { - 0: String - } - +struct UnsupportedFileExtensionError { - } - AudioFileName -|> FromStr: type Error = UnsupportedFileExtensionError - Cli --> PlayOptions - Cli --> ExportOptions - Cli --> ListOptions - PlayOptions --> FileContents - ExportOptions -> FileContents - ExportOptions -> AudioFileName - ListOptions --> MathDocKind - AudioFileName --> UnsupportedFileExtensionError - } -} -package lang {} -package dasp {} - -@enduml diff --git a/poc/Ancient Greek Didymus Chromatic.scl b/poc/Ancient Greek Didymus Chromatic.scl deleted file mode 100755 index c43c603..0000000 --- a/poc/Ancient Greek Didymus Chromatic.scl +++ /dev/null @@ -1,12 +0,0 @@ -! C:\Audio\Tunings\MTS Packs\World Scales Pack\scl\Ancient Greek Didymus Chromatic.scl -! -Didymus' Chromatic - 7 -! - 16/15 - 10/9 - 4/3 - 3/2 - 8/5 - 5/3 - 2/1 diff --git a/poc/Ancient Greek.kbm b/poc/Ancient Greek.kbm deleted file mode 100755 index 23c3f4c..0000000 --- a/poc/Ancient Greek.kbm +++ /dev/null @@ -1,28 +0,0 @@ -! Ancient Greek scales on E (white notes) -! Size of map: -12 -! First MIDI note number to retune: -0 -! Last MIDI note number to retune: -127 -! Middle note where the first entry in the mapping is mapped to: -64 -! Reference note for which frequency is given: -64 -! Frequency to tune the above note to (floating point e.g. 440.0): -329.627557 -! Scale degree to consider as formal octave: -7 -! Mapping. -0 -1 -1 -2 -2 -3 -3 -4 -5 -5 -6 -6 diff --git a/poc/complete.yml b/poc/complete.yml deleted file mode 100644 index e53bf17..0000000 --- a/poc/complete.yml +++ /dev/null @@ -1,57 +0,0 @@ -# fixed -instruments: - # instrument name - sine: - # fixed - expr: sin(2*PI*f*t) # instrument formula (f is the frequency in Hertz, t is the time in seconds) - square: - expr: v*abs(sin(2*PI*f*t)) - # fixed - vars: - # name of the variable - v: 1 # initial value of the variable -scl: - Didymus: poc/Ancient Greek Didymus Chromatic.scl -kbm: - Greek: poc/Ancient Greek.kbm -channels: - melody: - instr: sine - score: - notes: cCdDefFgGaAb - sheet: | - aabc. - 'ab°A - +d+d+d--- - /ff/f\\ - ab>c< - comment?: ;, - start here: ':' - 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: [] - modifier: ! - volume modifier prefix: v - octave modifier prefix: o - length modifier prefix: l - tempo modifier prefix: t, diff --git a/poc/nokbm.yml b/poc/nokbm.yml deleted file mode 100644 index 3b8165a..0000000 --- a/poc/nokbm.yml +++ /dev/null @@ -1,55 +0,0 @@ -# fixed -instruments: - # instrument name - sine: - # fixed - expr: sin(2*PI*f*t) # instrument formula (f is the frequency in Hertz, t is the time in seconds) - square: - expr: v*abs(sin(2*PI*f*t)) - # fixed - vars: - # name of the variable - v: 1 # initial value of the variable -scl: - Greek: poc/Ancient Greek Didymus Chromatic.scl -channels: - melody: - instr: sine - score: - notes: cCdDefFgGaAb - sheet: | - aabc. - 'ab°A - +d+d+d--- - /ff/f\\ - ab>c< - comment?: ;, - start here: ':' - 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: [] - modifier: ! - volume modifier prefix: v - octave modifier prefix: o - length modifier prefix: l - tempo modifier prefix: t, diff --git a/poc/noscl.yml b/poc/noscl.yml deleted file mode 100644 index 2bee6f6..0000000 --- a/poc/noscl.yml +++ /dev/null @@ -1,55 +0,0 @@ -# fixed -instruments: - # instrument name - sine: - # fixed - expr: sin(2*PI*f*t) # instrument formula (f is the frequency in Hertz, t is the time in seconds) - square: - expr: v*abs(sin(2*PI*f*t)) - # fixed - vars: - # name of the variable - v: 1 # initial value of the variable -kbm: - Greek: poc/Ancient Greek.kbm -channels: - melody: - instr: sine - score: - notes: cCdDefFgGaAb - sheet: | - aabc. - 'ab°A - +d+d+d--- - /ff/f\\ - ab>c< - comment?: ;, - start here: ':' - 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: [] - modifier: ! - volume modifier prefix: v - octave modifier prefix: o - length modifier prefix: l - tempo modifier prefix: t, diff --git a/poc/nothing.yml b/poc/nothing.yml deleted file mode 100644 index 24916a4..0000000 --- a/poc/nothing.yml +++ /dev/null @@ -1,49 +0,0 @@ -# fixed -instruments: - # instrument name - sine: - # fixed - expr: sin(2*PI*f*t) # instrument formula (f is the frequency in Hertz, t is the time in seconds) - square: - expr: v*abs(sin(2*PI*f*t)) -channels: - melody: - instr: sine - score: - notes: cCdDefFgGaAb - sheet: | - aabc. - 'ab°A - +d+d+d--- - /ff/f\\ - ab>c< - comment?: ;, - start here: ':' - 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: [] - modifier: ! - volume modifier prefix: v - octave modifier prefix: o - length modifier prefix: l - tempo modifier prefix: t, diff --git a/src/bng.rs b/src/bng.rs deleted file mode 100644 index 31cb5ec..0000000 --- a/src/bng.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::{collections::HashMap, str::FromStr}; - -use amplify::{From, Wrapper}; -use derive_new::new; -use derived_deref::Deref; -use fasteval::{Compiler, Instruction}; -pub(super) use instrument::Instrument; -pub(super) use score::Atom; -use score::Atoms; -use serde::{de::Visitor, Deserialize}; -use tune::scala::{Kbm, Scl}; - -mod de; -mod instrument; -mod score; - -#[derive(Debug, PartialEq, Wrapper, From)] -pub struct Expression(Instruction); - -#[derive(new, Deserialize)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub(super) struct Channel { - instr: String, - score: Atoms, -} - -#[derive(Deserialize, Default)] -#[cfg_attr(debug_assertions, derive(new, Debug))] -#[serde(deny_unknown_fields)] -#[serde(default)] -pub(super) struct BngFile { - instruments: HashMap, - channels: HashMap, - #[serde(rename = "scl")] - scales: HashMap, - #[serde(rename = "kbm")] - keyboard_mappings: HashMap, -} - -#[derive(Debug, Wrapper, From)] -struct Scale(Scl); -#[derive(Debug, Wrapper, From)] -struct KeyboardMapping(Kbm); diff --git a/src/bng/de.rs b/src/bng/de.rs deleted file mode 100644 index 29844ff..0000000 --- a/src/bng/de.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::{fmt::Display, marker::PhantomData, str::FromStr}; - -use fasteval::Compiler; -use serde::{de::Visitor, Deserialize}; - -use super::{Expression, KeyboardMapping, Scale}; - -mod from_str; - -struct FromStrVisitor<'a, V> { - expecting: &'a str, - phantom: PhantomData, -} - -impl<'a, V> FromStrVisitor<'a, V> { - fn new(expecting: &'a str) -> Self { - FromStrVisitor { - expecting, - phantom: PhantomData, - } - } -} - -impl<'de, 'a, V> Visitor<'de> for FromStrVisitor<'a, V> -where - V: FromStr, - ::Err: Display, -{ - type Value = V; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str(self.expecting) - } - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - v.parse::().map_err(serde::de::Error::custom) - } -} - -macro_rules! deserialize_fromstr_expect { - ($type:ty, $expect:literal) => { - impl<'de> serde::de::Deserialize<'de> for $type { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_str(FromStrVisitor::new($expect)) - } - } - }; -} - -deserialize_fromstr_expect!( - Expression, - "a math expression following fasteval syntax (https://docs.rs/fasteval)" -); - -deserialize_fromstr_expect!(Scale, "a .scl file containing a Scala scale"); -deserialize_fromstr_expect!( - KeyboardMapping, - "a .kbm file containing a Scala keyboard mapping" -); diff --git a/src/bng/de/from_str.rs b/src/bng/de/from_str.rs deleted file mode 100644 index f2e9996..0000000 --- a/src/bng/de/from_str.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::{fs::File, io, str::FromStr}; - -use amplify::{From, Wrapper}; -use fasteval::Compiler; -use thiserror::Error; -use tune::scala::{Kbm, KbmImportError, Scl, SclImportError}; - -use crate::bng::{KeyboardMapping, Scale}; - -use super::super::Expression; - -impl FromStr for Expression { - type Err = fasteval::Error; - fn from_str(s: &str) -> Result { - let parser = fasteval::Parser::new(); - let mut slab = fasteval::Slab::new(); - Ok(parser - .parse(s, &mut slab.ps)? - .from(&slab.ps) - .compile(&slab.ps, &mut slab.cs) - .into()) - } -} - -#[derive(Debug, Error)] -#[error("{0:?}")] -pub struct ScalaImportError(E); - -impl FromStr for Scale { - type Err = ScalaImportError; - fn from_str(s: &str) -> Result { - Scl::import( - File::open(s) - .map_err(Into::into) - .map_err(ScalaImportError)?, - ) - .map(Scale::from) - .map_err(ScalaImportError) - } -} - -impl FromStr for KeyboardMapping { - type Err = ScalaImportError; - fn from_str(s: &str) -> Result { - Kbm::import( - File::open(s) - .map_err(Into::into) - .map_err(ScalaImportError)?, - ) - .map(KeyboardMapping::from) - .map_err(ScalaImportError) - } -} diff --git a/src/bng/instrument.rs b/src/bng/instrument.rs deleted file mode 100644 index 6dc9cc8..0000000 --- a/src/bng/instrument.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::collections::HashMap; - -#[cfg(debug_assertions)] -use super::Expression as Instruction; -use derive_new::new; -use derived_deref::Deref; -use serde::Deserialize; - -#[derive(Deref, new, Deserialize)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct Instrument { - #[target] - expr: Instruction, - vars: Option>, -} diff --git a/src/bng/score.rs b/src/bng/score.rs deleted file mode 100644 index 71ffa77..0000000 --- a/src/bng/score.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::{ - error::Error, - num::{NonZeroU16, NonZeroU8}, -}; - -use amplify::From; -use anyhow::Context; -use bng_macros::{ModifierParser, QuickModifierParser, SlopeModifierParser}; -use derive_new::new; -use derived_deref::Deref; -use nom::{ - branch::alt, - character::{complete::char, streaming::one_of}, - combinator::{all_consuming, eof}, - multi::many0, - sequence::{preceded, terminated}, -}; -use serde::{ - de::{self as serde_de, Visitor}, - Deserialize, -}; -use strum::EnumDiscriminants; -use thiserror::Error; - -mod de; -mod lex; -pub use de::*; - -use super::Expression as Instruction; - -#[derive(Deref, From, Default, Clone)] -#[cfg_attr(debug_assertions, derive(Debug, PartialEq))] -pub struct Atoms(Vec); - -#[cfg_attr(debug_assertions, derive(Debug, PartialEq))] -pub enum Atom { - Note(u8), - Rest, - StartHere, - Modifier(Modifier), - QuickModifier(QuickModifier), - Loop(NonZeroU8, Atoms), - Tuple(Atoms), - Slope(SlopeModifier, Instruction, Atoms), - Comment, -} - -impl Clone for Atom { - fn clone(&self) -> Self { - match self { - Self::Note(n) => Self::Note(*n), - Self::Rest => Self::Rest, - Self::StartHere => Self::StartHere, - Self::Modifier(m) => Self::Modifier(m.clone()), - Self::QuickModifier(q) => Self::QuickModifier(q.clone()), - Self::Comment => Self::Comment, - _ => unimplemented!("variant can't be cloned right now"), - } - } -} - -#[derive(Clone, ModifierParser)] -#[cfg_attr(debug_assertions, derive(Debug, PartialEq))] -pub enum Modifier { - Volume(u8), - Octave(u8), - Length(NonZeroU8), - Tempo(NonZeroU16), -} - -impl Default for Modifier { - fn default() -> Self { - Modifier::Volume(Default::default()) - } -} - -#[derive(QuickModifierParser, Clone)] -#[cfg_attr(debug_assertions, derive(Debug, PartialEq))] -pub enum QuickModifier { - Volume(bool), - Octave(bool), - Length(bool), - Pizz(bool), -} - -#[derive(Clone, Copy, SlopeModifierParser, Debug)] -#[cfg_attr(debug_assertions, derive(PartialEq))] -pub enum SlopeModifier { - Note, - Volume, - Octave, - Length, - Tempo, -} diff --git a/src/bng/score/de.rs b/src/bng/score/de.rs deleted file mode 100644 index cf5aba7..0000000 --- a/src/bng/score/de.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::{ - fmt::{Debug, Display}, - num::TryFromIntError, - ops::Deref, -}; - -use derive_new::new; -use naan::fun::{F1Once, F2Once}; -use nom::{ - character::complete::one_of, - combinator::all_consuming, - error::{context, convert_error, ContextError, FromExternalError, ParseError, VerboseError}, - multi::{many0, many1}, - sequence::{preceded, terminated}, - IResult, Needed, Parser, -}; -use serde::{ - de::{self, Deserializer, Visitor}, - Deserialize, -}; -use thiserror::Error; - -use crate::bng::score::lex::lexer::{root, set_notes}; - -use super::{Atom, Atoms}; - -impl<'de> Deserialize<'de> for Atoms { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] - enum Field { - Notes, - Sheet, - } - #[derive(Deserialize, new)] - struct NotesSheet { - notes: String, - sheet: String, - } - let NotesSheet { notes, sheet } = NotesSheet::deserialize(deserializer)?; - if sheet.is_empty() { - Ok(Default::default()) - } else { - set_notes(notes).unwrap(); - all_consuming(root) - .parse(&sheet) - .map_err(|e| pretty_verbose_err.curry().call1(sheet.as_str()).call1(e)) - .map_err(de::Error::custom) - .map(|(i, r)| r) - } - } -} - -fn pretty_verbose_err(input: I, e: nom::Err>) -> String -where - I: Deref, -{ - match e { - nom::Err::Incomplete(Needed::Unknown) => "needed some more bytes".to_string(), - nom::Err::Incomplete(Needed::Size(n)) => format!("needed {} more bytes", n), - nom::Err::Error(e) | nom::Err::Failure(e) => convert_error(input, e), - } -} diff --git a/src/bng/score/lex.rs b/src/bng/score/lex.rs deleted file mode 100644 index 948dd89..0000000 --- a/src/bng/score/lex.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::{Atom, Modifier, QuickModifier, SlopeModifier}; - -pub(super) mod lexer; - -pub(super) const ON: bool = true; -pub(super) const OFF: bool = false; -pub(super) const UP: bool = true; -pub(super) const DOWN: bool = false; - -struct WrappingTokens; -impl WrappingTokens { - const PARENTHESES: (char, char) = ('(', ')'); - const SQUARE_BRACKETS: (char, char) = ('[', ']'); - const BRACKETS: (char, char) = ('{', '}'); - const SEMICOLON_COMMA: (char, char) = (';', ','); - const PLUS_MINUS: (char, char) = ('+', '-'); - const RIGHT_LEFT: (char, char) = ('>', '<'); - const SLASH_BACKSLASH: (char, char) = ('/', '\\'); - const QUOTE_DEG: (char, char) = ('\'', '°'); -} - -impl QuickModifier { - 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 Modifier { - pub(super) const VOLUME: char = 'v'; - pub(super) const OCTAVE: char = 'o'; - pub(super) const LENGTH: char = 'l'; - pub(super) const TEMPO: char = 't'; -} - -impl SlopeModifier { - pub(super) const NOTE: char = 'n'; - pub(super) const VOLUME: char = 'v'; - pub(super) const OCTAVE: char = 'o'; - pub(super) const LENGTH: char = 'l'; - pub(super) const TEMPO: char = 't'; -} - -impl Atom { - pub(super) const REST: char = '.'; - pub(super) const START_HERE: char = ':'; - pub(super) const MODIFIER: char = '!'; - pub(super) const LOOP: (char, char) = WrappingTokens::PARENTHESES; - pub(super) const TUPLE: (char, char) = WrappingTokens::SQUARE_BRACKETS; - pub(super) const SLOPE: (char, char) = WrappingTokens::BRACKETS; - pub(super) const COMMENT: (char, char) = WrappingTokens::SEMICOLON_COMMA; -} diff --git a/src/bng/score/lex/lexer.rs b/src/bng/score/lex/lexer.rs deleted file mode 100644 index 75c552e..0000000 --- a/src/bng/score/lex/lexer.rs +++ /dev/null @@ -1,230 +0,0 @@ -use std::{ - collections::BTreeMap, - num::{NonZeroU16, NonZeroU8, TryFromIntError}, - sync::{Mutex, MutexGuard, PoisonError}, -}; - -use clap::builder::TypedValueParser; -use const_format::concatcp; -use fasteval::Compiler; -use lazy_static::lazy_static; -use nom::{ - branch::alt, - bytes::complete::{is_not, tag, take_till, take_till1}, - character::complete::{anychar, char, one_of, space1, u16, u8}, - combinator::{all_consuming, cut, eof, map_opt, map_res, not, opt, value, verify}, - error::{context, ContextError, ErrorKind, FromExternalError, ParseError}, - multi::{many0, many1, many_till}, - sequence::{delimited, pair, preceded, separated_pair, terminated}, - Err, IResult, Parser, -}; - -use crate::bng::score::Atoms; - -use super::{ - super::super::Expression as Instruction, Atom, Modifier, QuickModifier, SlopeModifier, -}; - -#[cfg(test)] -mod tests; - -lazy_static! { - static ref NOTES: Mutex> = Mutex::new(None); -} - -pub fn set_notes( - notes: impl ToString, -) -> Result, PoisonError>>> { - Ok(NOTES.lock()?.replace(notes.to_string())) -} - -fn maybe_yml_str_space<'a, E>(i: &'a str) -> IResult<&'a str, Vec, E> -where - E: nom::error::ParseError<&'a str> + ContextError<&'a str>, -{ - context("yml white space", many0(one_of(" \t\r\n"))).parse(i) -} - -pub fn root<'a, E>(i: &'a str) -> IResult<&'a str, Atoms, E> -where - E: ParseError<&'a str> - + ContextError<&'a str> - + FromExternalError<&'a str, TryFromIntError> - + FromExternalError<&'a str, E>, -{ - many0(delimited(maybe_yml_str_space, atom, maybe_yml_str_space)) - .map(Atoms) - .parse(i) -} - -fn note<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> + ContextError<&'a str> + FromExternalError<&'a str, TryFromIntError>, -{ - if let Some(notes) = NOTES.lock().unwrap().as_deref() { - context( - "note", - map_res(map_opt(one_of(notes), |c| notes.find(c)), u8::try_from), - ) - .map(Atom::Note) - .parse(i) - } else { - panic!("trouble locking the note set!") - } -} - -fn rest<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> + ContextError<&'a str>, -{ - value(Atom::Rest, context("rest", char(Atom::REST))).parse(i) -} - -fn start_here<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> + ContextError<&'a str>, -{ - value( - Atom::StartHere, - context("start_here", char(Atom::START_HERE)), - ) - .parse(i) -} - -fn modifier<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> + ContextError<&'a str>, -{ - use super::super::modifier; - context("modifier", preceded(char(Atom::MODIFIER), cut(modifier))) - .map(Atom::Modifier) - .parse(i) -} - -fn quick_modifier<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> + ContextError<&'a str>, -{ - use super::super::quick_modifier; - context("quick_modifier", quick_modifier) - .map(Atom::QuickModifier) - .parse(i) -} - -fn r#loop<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> - + ContextError<&'a str> - + FromExternalError<&'a str, TryFromIntError> - + FromExternalError<&'a str, E>, -{ - context( - "loop", - delimited( - char(Atom::LOOP.0), - cut(pair( - map_opt(opt(u8), |n| { - if let Some(n) = n { - NonZeroU8::new(n) - } else { - unsafe { Some(NonZeroU8::new_unchecked(2)) } - } - }), - root, - )), - cut(char(Atom::LOOP.1)), - ), - ) - .map(|(n, v)| Atom::Loop(n, v)) - .parse(i) -} - -fn tuple<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> - + ContextError<&'a str> - + FromExternalError<&'a str, TryFromIntError> - + FromExternalError<&'a str, E>, -{ - context( - "tuple", - delimited(char(Atom::TUPLE.0), cut(root), cut(char(Atom::TUPLE.1))), - ) - .map(Atom::Tuple) - .parse(i) -} - -fn slope<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> - + ContextError<&'a str> - + FromExternalError<&'a str, TryFromIntError> - + FromExternalError<&'a str, E>, -{ - use super::super::slope_modifier; - context( - "slope_starts", - delimited( - char(Atom::SLOPE.0), - cut(separated_pair( - separated_pair( - slope_modifier, - char(' '), - map_res(take_till1(|c| c == ','), |s: &str| { - s.parse() - .map_err(|_| E::from_error_kind(s, ErrorKind::Verify)) - }), - ), - char(','), - root, - )), - cut(char(Atom::SLOPE.1)), - ), - ) - .map(|((sm, i), v)| Atom::Slope(sm, i, v)) - .parse(i) -} - -fn comment<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> + ContextError<&'a str>, -{ - context( - "comment", - value( - Atom::Comment, - delimited( - char(Atom::COMMENT.0), - many0(alt(( - value((), comment), - value((), is_not(concatcp!(Atom::COMMENT.0, Atom::COMMENT.1))), - ))), - cut(char(Atom::COMMENT.1)), - ), - ), - )(i) -} - -fn atom<'a, E>(i: &'a str) -> IResult<&'a str, Atom, E> -where - E: ParseError<&'a str> - + ContextError<&'a str> - + FromExternalError<&'a str, TryFromIntError> - + FromExternalError<&'a str, E>, -{ - context( - "atom", - alt(( - comment, - start_here, - modifier, - quick_modifier, - r#loop, - tuple, - slope, - rest, - note, - )), - ) - .parse(i) -} diff --git a/src/bng/score/lex/lexer/tests.rs b/src/bng/score/lex/lexer/tests.rs deleted file mode 100644 index 8e73570..0000000 --- a/src/bng/score/lex/lexer/tests.rs +++ /dev/null @@ -1,389 +0,0 @@ -use const_format::concatcp; -use nom::{ - error::{Error, ErrorKind, VerboseError}, - Err, -}; - -use flat_atom::{ - FASTEVAL_INSTRUCTION as FLATATOM_FASTEVAL_INSTRUCTION, SAMPLE_STR as FLATATOM_SAMPLE_STRING, -}; - -fn set_notes() { - super::set_notes("abcdefg").unwrap(); -} - -type NomError<'a> = VerboseError<&'a str>; - -mod flat_atom { - use std::num::NonZeroU8; - - use fasteval::Compiler; - use lazy_static::lazy_static; - use nom::Parser; - - use crate::bng::score::Atoms; - - use super::super::{ - super::super::super::Expression as Instruction, super::UP, atom, Atom, Modifier, - QuickModifier, SlopeModifier, - }; - use super::*; - - pub(super) const SAMPLE_STR: &str = concatcp!( - Atom::TUPLE.0, - "acc", - Atom::TUPLE.1, - "ed", - Atom::COMMENT.0, - "hello", - Atom::COMMENT.1 - ); - - pub(super) fn sample_str_expr() -> Atoms { - Atoms(vec![ - Atom::Tuple(Atoms(vec![Atom::Note(0), Atom::Note(2), Atom::Note(2)])), - Atom::Note(4), - Atom::Note(3), - Atom::Comment, - ]) - } - - pub(super) const FASTEVAL_INSTRUCTION: &str = "1-cos((PI*x)/2)"; - - #[test] - fn note() { - set_notes(); - assert_eq!( - Ok((SAMPLE_STR, Atom::Note(2))), - atom::.parse(concatcp!('c', SAMPLE_STR)) - ) - } - - #[test] - fn rest() { - assert_eq!( - Ok((SAMPLE_STR, Atom::Rest)), - atom::.parse(concatcp!(Atom::REST, SAMPLE_STR)) - ) - } - - #[test] - fn start_here() { - assert_eq!( - Ok((SAMPLE_STR, Atom::StartHere)), - atom::.parse(concatcp!(Atom::START_HERE, SAMPLE_STR)) - ) - } - - #[test] - fn modifier() { - assert_eq!( - Ok(( - SAMPLE_STR, - Atom::Modifier(Modifier::Length(unsafe { NonZeroU8::new_unchecked(2) })) - )), - atom::.parse(concatcp!(Atom::MODIFIER, Modifier::LENGTH, 2u8, SAMPLE_STR)) - ) - } - - #[test] - fn quick_modifier() { - assert_eq!( - Ok((SAMPLE_STR, Atom::QuickModifier(QuickModifier::Length(UP)))), - atom::.parse(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR)) - ) - } - - #[test] - fn r#loop() { - set_notes(); - assert_eq!( - Ok(( - SAMPLE_STR, - Atom::Loop(unsafe { NonZeroU8::new_unchecked(3) }, sample_str_expr()) - )), - atom::.parse(concatcp!( - Atom::LOOP.0, - 3u8, - SAMPLE_STR, - Atom::LOOP.1, - SAMPLE_STR - )) - ); - assert_eq!( - Ok(( - SAMPLE_STR, - Atom::Loop(unsafe { NonZeroU8::new_unchecked(2) }, sample_str_expr()) - )), - atom::.parse(concatcp!( - Atom::LOOP.0, - SAMPLE_STR, - Atom::LOOP.1, - SAMPLE_STR - )) - ); - assert_eq!( - Err(nom::Err::Failure(Error::new( - concatcp!(0u8, SAMPLE_STR), - ErrorKind::MapOpt - ))), - atom.parse(concatcp!(Atom::LOOP.0, 0u8, SAMPLE_STR)) - ) - } - - #[test] - fn tuple() { - set_notes(); - assert_eq!( - Ok((SAMPLE_STR, Atom::Tuple(sample_str_expr()))), - atom::.parse(concatcp!( - Atom::TUPLE.0, - SAMPLE_STR, - Atom::TUPLE.1, - SAMPLE_STR - )) - ) - } - - #[test] - fn slope() { - set_notes(); - assert_eq!( - Ok(( - SAMPLE_STR, - Atom::Slope( - SlopeModifier::Note, - FASTEVAL_INSTRUCTION.parse().unwrap(), - sample_str_expr() - ) - )), - atom::.parse(concatcp!( - Atom::SLOPE.0, - SlopeModifier::NOTE, - ' ', - FASTEVAL_INSTRUCTION, - ',', - SAMPLE_STR, - Atom::SLOPE.1, - SAMPLE_STR - )) - ) - } - - #[test] - fn comment() { - set_notes(); - assert_eq!( - Ok((SAMPLE_STR, Atom::Comment)), - atom::.parse(concatcp!( - Atom::COMMENT.0, - "hi I'm a little pony", - SAMPLE_STR, - Atom::COMMENT.1, - SAMPLE_STR - )) - ) - } - - #[test] - fn nested_comments() { - set_notes(); - assert_eq!( - Ok(("", Atom::Comment)), - atom::.parse(";d;;dd,ef,,") - ) - } -} - -mod modifier { - use std::num::{NonZeroU16, NonZeroU8}; - - use const_format::concatcp; - use nom::error::{Error, ErrorKind, VerboseError}; - - use super::{NomError, FLATATOM_SAMPLE_STRING as SAMPLE_STR}; - use crate::bng::score::{modifier, Modifier}; - - #[test] - fn volume() { - assert_eq!( - Ok((SAMPLE_STR, Modifier::Volume(2))), - modifier::(concatcp!(Modifier::VOLUME, 2u8, SAMPLE_STR)) - ); - assert_eq!( - Err(nom::Err::Error(Error::new( - concatcp!(Modifier::VOLUME, 2556u16, SAMPLE_STR), - ErrorKind::Char - ))), - modifier(concatcp!(Modifier::VOLUME, 2556u16, SAMPLE_STR)) - ); - } - - #[test] - fn octave() { - assert_eq!( - Ok((SAMPLE_STR, Modifier::Octave(2))), - modifier::(concatcp!(Modifier::OCTAVE, 2u8, SAMPLE_STR)) - ); - assert_eq!( - Err(nom::Err::Error(Error::new( - concatcp!(Modifier::OCTAVE, 2556u16, SAMPLE_STR), - ErrorKind::Char - ))), - modifier(concatcp!(Modifier::OCTAVE, 2556u16, SAMPLE_STR)) - ); - } - - #[test] - fn length() { - assert_eq!( - Ok(( - SAMPLE_STR, - Modifier::Length(unsafe { NonZeroU8::new_unchecked(2) }) - )), - modifier::(concatcp!(Modifier::LENGTH, 2u8, SAMPLE_STR)) - ); - assert_eq!( - Err(nom::Err::Error(Error::new( - concatcp!(Modifier::LENGTH, 2556u16, SAMPLE_STR), - ErrorKind::Char - ))), - modifier(concatcp!(Modifier::LENGTH, 2556u16, SAMPLE_STR)) - ); - assert_eq!( - Err(nom::Err::Error(Error::new( - concatcp!(Modifier::LENGTH, 0u8, SAMPLE_STR), - ErrorKind::Char - ))), - modifier(concatcp!(Modifier::LENGTH, 0u8, SAMPLE_STR)) - ); - } - - #[test] - fn tempo() { - assert_eq!( - Ok(( - SAMPLE_STR, - Modifier::Tempo(unsafe { NonZeroU16::new_unchecked(2) }) - )), - modifier::(concatcp!(Modifier::TEMPO, 2u8, SAMPLE_STR)) - ); - assert_eq!( - Err(nom::Err::Error(Error::new( - concatcp!(655353u32, SAMPLE_STR), - ErrorKind::Digit - ))), - modifier(concatcp!(Modifier::TEMPO, 655353u32, SAMPLE_STR)) - ); - } -} - -mod quick_modifier { - use const_format::concatcp; - use nom::error::VerboseError; - - use super::{NomError, FLATATOM_SAMPLE_STRING as SAMPLE_STR}; - use crate::bng::score::{ - lex::{DOWN, OFF, ON, UP}, - quick_modifier, QuickModifier, - }; - - #[test] - fn volume() { - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Volume(UP))), - quick_modifier::(concatcp!(QuickModifier::VOLUME.0, SAMPLE_STR)) - ); - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Volume(DOWN))), - quick_modifier::(concatcp!(QuickModifier::VOLUME.1, SAMPLE_STR)) - ); - } - - #[test] - fn octave() { - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Octave(UP))), - quick_modifier::(concatcp!(QuickModifier::OCTAVE.0, SAMPLE_STR)) - ); - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Octave(DOWN))), - quick_modifier::(concatcp!(QuickModifier::OCTAVE.1, SAMPLE_STR)) - ); - } - - #[test] - fn length() { - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Length(UP))), - quick_modifier::(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR)) - ); - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Length(DOWN))), - quick_modifier::(concatcp!(QuickModifier::LENGTH.1, SAMPLE_STR)) - ); - } - - #[test] - fn pizz() { - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Pizz(ON))), - quick_modifier::(concatcp!(QuickModifier::PIZZ.0, SAMPLE_STR)) - ); - assert_eq!( - Ok((SAMPLE_STR, QuickModifier::Pizz(OFF))), - quick_modifier::(concatcp!(QuickModifier::PIZZ.1, SAMPLE_STR)) - ); - } -} - -#[cfg(test)] -mod slope_modifier { - use const_format::concatcp; - use nom::error::VerboseError; - - use super::{NomError, FLATATOM_FASTEVAL_INSTRUCTION as INSTRUCTION}; - use crate::bng::score::{slope_modifier, Atom, SlopeModifier}; - - const SAMPLE_STR: &str = concatcp!(' ', INSTRUCTION, Atom::SLOPE.1); - - #[test] - fn note() { - assert_eq!( - Ok((SAMPLE_STR, SlopeModifier::Note)), - slope_modifier::(concatcp!(SlopeModifier::NOTE, SAMPLE_STR)) - ) - } - - #[test] - fn volume() { - assert_eq!( - Ok((SAMPLE_STR, SlopeModifier::Volume)), - slope_modifier::(concatcp!(SlopeModifier::VOLUME, SAMPLE_STR)) - ) - } - - #[test] - fn octave() { - assert_eq!( - Ok((SAMPLE_STR, SlopeModifier::Octave)), - slope_modifier::(concatcp!(SlopeModifier::OCTAVE, SAMPLE_STR)) - ) - } - - #[test] - fn length() { - assert_eq!( - Ok((SAMPLE_STR, SlopeModifier::Length)), - slope_modifier::(concatcp!(SlopeModifier::LENGTH, SAMPLE_STR)) - ) - } - - #[test] - fn tempo() { - assert_eq!( - Ok((SAMPLE_STR, SlopeModifier::Tempo)), - slope_modifier::(concatcp!(SlopeModifier::TEMPO, SAMPLE_STR)) - ) - } -} diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 3368888..0000000 --- a/src/cli.rs +++ /dev/null @@ -1,105 +0,0 @@ -use std::{convert::Infallible, fmt::Display, fs::read_to_string, io, str::FromStr}; - -use amplify::{From, Wrapper}; -use clap::Parser; -use strum::EnumString; - -pub mod doc; - -/// Cli entry point -#[derive(Clone, Parser)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub enum BngCli { - /// Play the song through default sink - Play(PlayOpts), - /// Export the song to a sound file - Export(ExportOpts), - /// List supported sound file extensions and instrument / song available expressions - #[command(subcommand)] - List(ListOpts), -} - -/// [`BngCli`] "play" command options -#[derive(Clone, Parser)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct PlayOpts { - #[arg(value_parser = FileContents::from_str)] - pub(super) input: FileContents, -} - -/// [`BngCli`] "export" command options -#[derive(Clone, Parser)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct ExportOpts { - /// Input file (written song file) - #[arg(value_parser = FileContents::from_str)] - input: FileContents, - /// Output file (sound file) - #[arg(value_parser = AudioFileName::from_str)] - output: AudioFileName, -} - -/// [`BngCli`] "list" command sub-commands -#[derive(Clone, Parser)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub enum ListOpts { - /// List supported sound file extensions to export songs - Extensions, - /// Show the math syntax used for instrument definition and slopes - Math { - /// Kind of syntax you want to list (functions, operators or literals) - #[arg(value_parser = MathDocKind::from_str)] - kind: Option, - }, - /// List available score glyphs and their meaning - Glyphs, -} - -#[derive(Clone, Copy, EnumString)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub enum MathDocKind { - #[strum(ascii_case_insensitive)] - Functions, - #[strum(ascii_case_insensitive)] - Operators, - #[strum(ascii_case_insensitive)] - Literals, -} - -#[derive(Clone, Wrapper, From)] -#[wrapper(Deref)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct FileContents(String); - -impl FromStr for FileContents { - type Err = io::Error; - fn from_str(s: &str) -> Result { - read_to_string(s).map(Into::into) - } -} - -#[derive(Clone, Wrapper, From)] -#[wrapper(Deref)] -#[cfg_attr(debug_assertions, derive(Debug))] -pub struct AudioFileName(String); - -#[derive(Debug)] -pub struct UnsupportedFileExtensionError; - -impl std::error::Error for UnsupportedFileExtensionError {} - -impl Display for UnsupportedFileExtensionError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "The extension of the selected output sound file is not supported." - ) - } -} - -impl FromStr for AudioFileName { - type Err = UnsupportedFileExtensionError; - fn from_str(s: &str) -> Result { - Ok(s.to_owned().into()) - } -} diff --git a/src/cli/doc.rs b/src/cli/doc.rs deleted file mode 100644 index 408ee40..0000000 --- a/src/cli/doc.rs +++ /dev/null @@ -1,98 +0,0 @@ -pub mod math { - pub const FUNCTIONS: &str = r#"* print(...strings and values...) -- Prints to stderr. Very useful to 'probe' an expression. - Evaluates to the last value. - Example: `print("x is", x, "and y is", y)` - Example: `x + print("y:", y) + z == x+y+z` - -* log(base=10, val) -- Logarithm with optional 'base' as first argument. - If not provided, 'base' defaults to '10'. - Example: `log(100) + log(e(), 100)` - -* e() -- Euler's number (2.718281828459045) -* pi() -- π (3.141592653589793) - -* int(val) -* ceil(val) -* floor(val) -* round(modulus=1, val) -- Round with optional 'modulus' as first argument. - Example: `round(1.23456) == 1 && round(0.001, 1.23456) == 1.235` - -* abs(val) -* sign(val) - -* min(val, ...) -- Example: `min(1, -2, 3, -4) == -4` -* max(val, ...) -- Example: `max(1, -2, 3, -4) == 3` - -* sin(radians) * asin(val) -* cos(radians) * acos(val) -* tan(radians) * atan(val) -* sinh(val) * asinh(val) -* cosh(val) * acosh(val) -* tanh(val) * atanh(val)"#; - pub const OPERATORS: &str = r#"Listed in order of precedence: - -(Highest Precedence) ^ Exponentiation - % Modulo - / Division - * Multiplication - - Subtraction - + Addition - == != < <= >= > Comparisons (all have equal precedence) - && and Logical AND with short-circuit -(Lowest Precedence) || or Logical OR with short-circuit"#; - pub const LITERALS: &str = r#"Several numeric formats are supported: - -Integers: 1, 2, 10, 100, 1001 - -Decimals: 1.0, 1.23456, 0.000001 - -Exponents: 1e3, 1E3, 1e-3, 1E-3, 1.2345e100 - -Suffix: - 1.23p = 0.00000000000123 - 1.23n = 0.00000000123 - 1.23µ, 1.23u = 0.00000123 - 1.23m = 0.00123 - 1.23K, 1.23k = 1230 - 1.23M = 1230000 - 1.23G = 1230000000 - 1.23T = 1230000000000"#; -} - -pub mod glyphs { - pub const ALL: &str = r#"rest: . -pizz.: '° -volume: +- -length: /\ -octave: >< -comment?: ;, -start here: ':' -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: [] -modifier: ! -volume modifier prefix: v -octave modifier prefix: o -length modifier prefix: l -tempo modifier prefix: t - -EXAMPLE -aabc. -'ab°A -+d+d+d--- -/ff/f\\ -ab>c Result<(), Error> { - // println!("{}", option_env!("TEST").unwrap_or("ok")); - let args = Cli::parse(); - // #[cfg(debug_assertions)] - // { - // println!("{}", serde_yml::to_string(&bngfile_generator())?); - // println!("{:?}", args); - // } - match args { - Cli::Play(PlayOpts { input }) => { - let bng_file: BngFile = serde_yml::from_str(&input)?; - #[cfg(debug_assertions)] - println!("{:#?}", bng_file); - } - Cli::List(l) => match l { - ListOpts::Extensions => println!("you can't export songs yet"), - ListOpts::Math { kind } => match kind { - None => println!( - "Functions :\n{}\n\nOperators :\n{}\n\nLiterals :\n{}", - doc::math::FUNCTIONS, - doc::math::OPERATORS, - doc::math::LITERALS - ), - Some(m) => println!( - "{}", - match m { - MathDocKind::Functions => doc::math::FUNCTIONS, - MathDocKind::Operators => doc::math::OPERATORS, - MathDocKind::Literals => doc::math::LITERALS, - } - ), - }, - ListOpts::Glyphs => println!("{}", doc::glyphs::ALL), - }, - Cli::Export(_) => unimplemented!("can't do that yet"), - } - Ok(()) -}