diff --git a/poc/poc.yml b/poc/poc.yml index 274a713..c6f23c4 100644 --- a/poc/poc.yml +++ b/poc/poc.yml @@ -14,38 +14,36 @@ channels: melody: instr: sine score: - notes: cCdDefFgGaAb - sheet: - aabc. - 'rt°y - +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 + aabc. + 'rt°y + +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 index d3a2a8d..b4a9cd3 100644 --- a/src/bng.rs +++ b/src/bng.rs @@ -6,7 +6,6 @@ use derived_deref::Deref; use fasteval::{Compiler, Instruction}; pub(super) use instrument::Instrument; pub(super) use score::Atom; -use score::Atoms; #[cfg(debug_assertions)] use serde::Serialize; use serde::{de::Visitor, Deserialize}; @@ -66,16 +65,15 @@ impl FromStr for Expression { } } -#[derive(new, Deserialize)] -#[cfg_attr(debug_assertions, derive(Serialize, Debug))] +#[derive(new)] +#[cfg_attr(debug_assertions, derive(Serialize))] pub(super) struct Channel { instr: String, - score: Atoms, + score: Vec, } -#[derive(Deserialize)] -#[cfg_attr(debug_assertions, derive(new, Debug, Serialize))] +#[cfg_attr(debug_assertions, derive(new, Serialize))] pub(super) struct BngFile { - instruments: HashMap, + instruments: Vec>, channels: HashMap, } diff --git a/src/bng/instrument.rs b/src/bng/instrument.rs index af5164f..382a636 100644 --- a/src/bng/instrument.rs +++ b/src/bng/instrument.rs @@ -2,14 +2,13 @@ use std::collections::HashMap; use derive_new::new; use derived_deref::Deref; -use serde::Deserialize; #[cfg(debug_assertions)] use serde::Serialize; use super::Expression as Instruction; -#[derive(Deref, new, Deserialize)] -#[cfg_attr(debug_assertions, derive(Serialize, Debug))] +#[derive(Deref, new)] +#[cfg_attr(debug_assertions, derive(Serialize))] pub struct Instrument { #[target] expr: Instruction, diff --git a/src/bng/score.rs b/src/bng/score.rs index fa58350..89174e8 100644 --- a/src/bng/score.rs +++ b/src/bng/score.rs @@ -1,117 +1,16 @@ use std::num::{NonZeroU16, NonZeroU8}; -use amplify::From; use anyhow::Context; use bng_macros::{QuickModifierParser, SlopeModifierParser}; -use derive_new::new; -use derived_deref::Deref; -use lex::lexer::flat_atom_parser; -use nom::multi::many0; #[cfg(debug_assertions)] use serde::Serialize; -use serde::{ - de::{self, Visitor}, - Deserialize, -}; use strum::EnumDiscriminants; -use thiserror::Error; -use utils::{inflate, InflateError}; mod lex; mod utils; use super::Expression as Instruction; -#[derive(Deref, From)] -#[cfg_attr(debug_assertions, derive(Serialize, Debug))] -pub struct Atoms(Vec); - -#[derive(Debug, Error)] -enum AtomsSerializeError { - #[error("error while parsing with nom")] - Parsing(String), - #[error("error while inflating flat atoms")] - Inflation(#[from] InflateError), -} - -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, - } - struct NotesSheetVisitor; - impl<'de> Visitor<'de> for NotesSheetVisitor { - type Value = NotesSheet; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a \"notes\" field and a \"sheet\" field") - } - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let notes = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?; - let sheet = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - Ok(NotesSheet::new(notes, sheet)) - } - fn visit_map(self, mut map: A) -> Result - where - A: de::MapAccess<'de>, - { - let mut notes = None; - let mut sheet = None; - while let Some(key) = map.next_key()? { - match key { - Field::Notes => { - if notes.is_some() { - return Err(de::Error::duplicate_field("notes")); - } - notes = Some(map.next_value()?); - } - Field::Sheet => { - if sheet.is_some() { - return Err(de::Error::duplicate_field("sheet")); - } - sheet = Some(map.next_value()?); - } - } - } - let notes = notes.ok_or_else(|| de::Error::missing_field("notes"))?; - let sheet = sheet.ok_or_else(|| de::Error::missing_field("sheet"))?; - Ok(NotesSheet::new(notes, sheet)) - } - } - const FIELDS: &[&str] = &["notes", "sheet"]; - let NotesSheet { notes, sheet } = - deserializer.deserialize_struct("NotesSheet", FIELDS, NotesSheetVisitor)?; - let x = many0(flat_atom_parser(¬es))(&sheet) - .map_err(|e| e.to_string()) - .map_err(AtomsSerializeError::Parsing) - .map_err(de::Error::custom) - .and_then(|(_, v)| { - inflate(v) - .map_err(AtomsSerializeError::from) - .map_err(de::Error::custom) - }) - .map(Atoms); - x - } -} - #[cfg_attr(debug_assertions, derive(Debug, PartialEq))] pub enum Atom { Note(u8), diff --git a/src/bng/score/lex/lexer.rs b/src/bng/score/lex/lexer.rs index 7631144..acf1372 100644 --- a/src/bng/score/lex/lexer.rs +++ b/src/bng/score/lex/lexer.rs @@ -8,7 +8,7 @@ use fasteval::Compiler; use nom::{ branch::alt, bytes::complete::{take_till, take_till1}, - character::complete::{anychar, char, one_of, space1, u16, u8}, + character::complete::{anychar, char, one_of, u16, u8}, combinator::{map_opt, map_res, opt, value, verify}, multi::many0, sequence::{delimited, pair, preceded, separated_pair, terminated}, @@ -38,54 +38,50 @@ impl Parse for Modifier { } } -pub fn flat_atom_parser(notes: &str) -> impl Parser<&str, FlatAtom, nom::error::Error<&str>> { - preceded( - many0(alt((char('\t'), char(' '), char('\n'), char('\r')))), - alt(( - map_res(map_opt(one_of(notes), |c| notes.find(c)), u8::try_from).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), +fn flat_atom_parser(notes: &str) -> impl Parser<&str, FlatAtom, nom::error::Error<&str>> { + alt(( + map_res(map_opt(one_of(notes), |c| notes.find(c)), u8::try_from).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), + map_opt(opt(u8), |n| { + if let Some(n) = n { + NonZeroU8::new(n) + } else { + unsafe { Some(NonZeroU8::new_unchecked(2)) } + } + }), + ) + .map(FlatAtom::LoopStarts), + value(FlatAtom::LoopEnds, char(Atom::LOOP.1)), + value(FlatAtom::TupleStarts, char(Atom::TUPLE.0)), + value(FlatAtom::TupleEnds, char(Atom::TUPLE.1)), + terminated( preceded( - char(Atom::LOOP.0), - map_opt(opt(u8), |n| { - if let Some(n) = n { - NonZeroU8::new(n) - } else { - unsafe { Some(NonZeroU8::new_unchecked(2)) } - } - }), - ) - .map(FlatAtom::LoopStarts), - value(FlatAtom::LoopEnds, char(Atom::LOOP.1)), - value(FlatAtom::TupleStarts, char(Atom::TUPLE.0)), - value(FlatAtom::TupleEnds, char(Atom::TUPLE.1)), - terminated( - preceded( - char(Atom::SLOPE.0), - separated_pair( - SlopeModifier::parse, - char(' '), - map_res(take_till1(|c| c == ','), |s: &str| { - s.parse().map_err(|_| { - nom::error::Error::new(s, nom::error::ErrorKind::Verify) - }) - }), - ), - ), - char(','), - ) - .map(|(sm, i)| FlatAtom::SlopeStarts(sm, i)), - value(FlatAtom::SlopeEnds, char(Atom::SLOPE.1)), - value( - FlatAtom::Comment, - delimited( - char(Atom::COMMENT.0), - take_till(|c| c == Atom::COMMENT.1), - char(Atom::COMMENT.1), + char(Atom::SLOPE.0), + separated_pair( + SlopeModifier::parse, + char(' '), + map_res(take_till1(|c| c == ','), |s: &str| { + s.parse() + .map_err(|_| nom::error::Error::new(s, nom::error::ErrorKind::Verify)) + }), ), ), - )), - ) + char(','), + ) + .map(|(sm, i)| FlatAtom::SlopeStarts(sm, i)), + value(FlatAtom::SlopeEnds, char(Atom::SLOPE.1)), + value( + FlatAtom::Comment, + delimited( + char(Atom::COMMENT.0), + take_till(|c| c == Atom::COMMENT.1), + char(Atom::COMMENT.1), + ), + ), + )) } diff --git a/src/bng/score/utils.rs b/src/bng/score/utils.rs index e7bb56d..2177b12 100644 --- a/src/bng/score/utils.rs +++ b/src/bng/score/utils.rs @@ -7,7 +7,7 @@ mod tests; #[derive(Debug, EnumDiscriminants)] #[strum_discriminants(derive(Display))] -pub enum Wrapper { +enum Wrapper { Loop(NonZeroU8), Tuple, Slope(SlopeModifier, Instruction), @@ -15,12 +15,12 @@ pub enum Wrapper { #[derive(Debug, Error)] #[cfg_attr(test, derive(PartialEq))] -pub enum InflateError { +enum InflateError { #[error("misplaced {0} end symbol")] MismatchedEnd(WrapperDiscriminants), } -pub fn inflate(mut flat_atoms: Vec) -> Result, InflateError> { +fn inflate(mut flat_atoms: Vec) -> Result, InflateError> { type Error = InflateError; let mut result = Vec::with_capacity(flat_atoms.len()); let mut loop_stack: Vec> = Vec::new(); diff --git a/src/main.rs b/src/main.rs index f87805a..0af91ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,41 +12,29 @@ mod cli; fn main() -> Result<(), serde_yml::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); - } - _ => unimplemented!("can't do that yet"), - } + println!( + "{}", + serde_yml::to_string(&BngFile::new( + vec![HashMap::from([ + ( + "sine".to_string(), + Instrument::new("sin(2*PI*f*t)".parse().unwrap(), None) + ), + ( + "square".to_string(), + Instrument::new( + "v*abs(sin(2*PI*f*t))".parse().unwrap(), + Some(HashMap::from([("v".to_string(), 1f32)])) + ) + ) + ])], + HashMap::::from([( + "melody".to_string(), + Channel::new("sine".to_string(), vec![]) + )]) + ))? + ); + #[cfg(debug_assertions)] + println!("{:?}", args); Ok(()) } - -#[cfg(debug_assertions)] -fn bngfile_generator() -> BngFile { - BngFile::new( - HashMap::from([ - ( - "sine".to_string(), - Instrument::new("sin(2*PI*f*t)".parse().unwrap(), None), - ), - ( - "square".to_string(), - Instrument::new( - "v*abs(sin(2*PI*f*t))".parse().unwrap(), - Some(HashMap::from([("v".to_string(), 1f32)])), - ), - ), - ]), - HashMap::::from([( - "melody".to_string(), - Channel::new("sine".to_string(), vec![].into()), - )]), - ) -}