serialize everything (error on the sheet type)

This commit is contained in:
Ponj 2024-10-31 00:21:24 -04:00
parent 9c6f76fd6e
commit 829cb64511
Signed by: p6nj
GPG key ID: 6FED68D87C479A59
7 changed files with 232 additions and 111 deletions

View file

@ -14,36 +14,38 @@ channels:
melody: melody:
instr: sine instr: sine
score: score:
aabc. notes: cCdDefFgGaAb
'rt°y sheet:
+d+d+d--- aabc.
/ff/f\\ 'rt°y
ab>c<ba +d+d+d---
;this is a comment (or lyrics whatever), /ff/f\\
s:df ab>c<ba
(3deff) ;this is a comment (or lyrics whatever),
(deff) s:df
[ffe] (3deff)
{l 1-cos((PI*x)/2),acced} (deff)
abbc!o5cc!v15feed!l4fedd!t60hdd [ffe]
# rest: . {l 1-cos((PI*x)/2),acced}
# pizz.: '° abbc!o5cc!v15feed!l4fedd!t60hdd
# volume: +- # rest: .
# length: /\ # pizz.: '°
# octave: >< # volume: +-
# comment?: ;, # length: /\
# start here: ':' # octave: ><
# slope: {MODIFIER EXPR, score} # comment?: ;,
# note modifier prefix: n # start here: ':'
# volume modifier prefix: v # slope: {MODIFIER EXPR, score}
# octave modifier prefix: o # note modifier prefix: n
# length modifier prefix: l # volume modifier prefix: v
# tempo modifier prefix: t # octave modifier prefix: o
# loop: () # length modifier prefix: l
# loop with count: (COUNT, score) # tempo modifier prefix: t
# tuple: [] # loop: ()
# modifier: ! # loop with count: (COUNT, score)
# volume modifier prefix: v # tuple: []
# octave modifier prefix: o # modifier: !
# length modifier prefix: l # volume modifier prefix: v
# tempo modifier prefix: t # octave modifier prefix: o
# length modifier prefix: l
# tempo modifier prefix: t

View file

@ -6,6 +6,7 @@ use derived_deref::Deref;
use fasteval::{Compiler, Instruction}; use fasteval::{Compiler, Instruction};
pub(super) use instrument::Instrument; pub(super) use instrument::Instrument;
pub(super) use score::Atom; pub(super) use score::Atom;
use score::Atoms;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use serde::Serialize; use serde::Serialize;
use serde::{de::Visitor, Deserialize}; use serde::{de::Visitor, Deserialize};
@ -65,15 +66,16 @@ impl FromStr for Expression {
} }
} }
#[derive(new)] #[derive(new, Deserialize)]
#[cfg_attr(debug_assertions, derive(Serialize))] #[cfg_attr(debug_assertions, derive(Serialize, Debug))]
pub(super) struct Channel { pub(super) struct Channel {
instr: String, instr: String,
score: Vec<Atom>, score: Atoms,
} }
#[cfg_attr(debug_assertions, derive(new, Serialize))] #[derive(Deserialize)]
#[cfg_attr(debug_assertions, derive(new, Debug, Serialize))]
pub(super) struct BngFile { pub(super) struct BngFile {
instruments: Vec<HashMap<String, Instrument>>, instruments: HashMap<String, Instrument>,
channels: HashMap<String, Channel>, channels: HashMap<String, Channel>,
} }

View file

@ -2,13 +2,14 @@ use std::collections::HashMap;
use derive_new::new; use derive_new::new;
use derived_deref::Deref; use derived_deref::Deref;
use serde::Deserialize;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use serde::Serialize; use serde::Serialize;
use super::Expression as Instruction; use super::Expression as Instruction;
#[derive(Deref, new)] #[derive(Deref, new, Deserialize)]
#[cfg_attr(debug_assertions, derive(Serialize))] #[cfg_attr(debug_assertions, derive(Serialize, Debug))]
pub struct Instrument { pub struct Instrument {
#[target] #[target]
expr: Instruction, expr: Instruction,

View file

@ -1,16 +1,116 @@
use std::num::{NonZeroU16, NonZeroU8}; use std::num::{NonZeroU16, NonZeroU8};
use amplify::From;
use anyhow::Context; use anyhow::Context;
use bng_macros::{QuickModifierParser, SlopeModifierParser}; 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)] #[cfg(debug_assertions)]
use serde::Serialize; use serde::Serialize;
use serde::{
de::{self, Visitor},
Deserialize,
};
use strum::EnumDiscriminants; use strum::EnumDiscriminants;
use thiserror::Error;
use utils::{inflate, InflateError};
mod lex; mod lex;
mod utils; mod utils;
use super::Expression as Instruction; use super::Expression as Instruction;
#[derive(Deref, From)]
#[cfg_attr(debug_assertions, derive(Serialize, Debug))]
pub struct Atoms(Vec<Atom>);
#[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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
Notes,
Sheet,
}
#[derive(Deserialize, new)]
struct NotesSheet<'a> {
notes: &'a str,
sheet: &'a str,
}
struct NotesSheetVisitor;
impl<'de> Visitor<'de> for NotesSheetVisitor {
type Value = NotesSheet<'de>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a \"notes\" field and a \"sheet\" field")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<A>(self, mut map: A) -> Result<Self::Value, A::Error>
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)?;
many0(flat_atom_parser(notes))(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)
}
}
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))] #[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
pub enum Atom { pub enum Atom {
Note(u8), Note(u8),

View file

@ -8,7 +8,7 @@ use fasteval::Compiler;
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::complete::{take_till, take_till1}, bytes::complete::{take_till, take_till1},
character::complete::{anychar, char, one_of, u16, u8}, character::complete::{anychar, char, one_of, space1, u16, u8},
combinator::{map_opt, map_res, opt, value, verify}, combinator::{map_opt, map_res, opt, value, verify},
multi::many0, multi::many0,
sequence::{delimited, pair, preceded, separated_pair, terminated}, sequence::{delimited, pair, preceded, separated_pair, terminated},
@ -38,50 +38,54 @@ impl Parse for Modifier {
} }
} }
fn flat_atom_parser(notes: &str) -> impl Parser<&str, FlatAtom, nom::error::Error<&str>> { pub fn flat_atom_parser(notes: &str) -> impl Parser<&str, FlatAtom, nom::error::Error<&str>> {
alt(( preceded(
map_res(map_opt(one_of(notes), |c| notes.find(c)), u8::try_from).map(FlatAtom::Note), many0(alt((char('\t'), char(' '), char('\n'), char('\r')))),
value(FlatAtom::Rest, char(Atom::REST)), alt((
value(FlatAtom::StartHere, char(Atom::START_HERE)), map_res(map_opt(one_of(notes), |c| notes.find(c)), u8::try_from).map(FlatAtom::Note),
preceded(char(Atom::MODIFIER), Modifier::parse).map(FlatAtom::Modifier), value(FlatAtom::Rest, char(Atom::REST)),
QuickModifier::parse.map(FlatAtom::QuickModifier), value(FlatAtom::StartHere, char(Atom::START_HERE)),
preceded( preceded(char(Atom::MODIFIER), Modifier::parse).map(FlatAtom::Modifier),
char(Atom::LOOP.0), QuickModifier::parse.map(FlatAtom::QuickModifier),
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( preceded(
char(Atom::SLOPE.0), char(Atom::LOOP.0),
separated_pair( map_opt(opt(u8), |n| {
SlopeModifier::parse, if let Some(n) = n {
char(' '), NonZeroU8::new(n)
map_res(take_till1(|c| c == ','), |s: &str| { } else {
s.parse() unsafe { Some(NonZeroU8::new_unchecked(2)) }
.map_err(|_| nom::error::Error::new(s, nom::error::ErrorKind::Verify)) }
}), }),
)
.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(','), )),
) )
.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),
),
),
))
} }

View file

@ -7,7 +7,7 @@ mod tests;
#[derive(Debug, EnumDiscriminants)] #[derive(Debug, EnumDiscriminants)]
#[strum_discriminants(derive(Display))] #[strum_discriminants(derive(Display))]
enum Wrapper { pub enum Wrapper {
Loop(NonZeroU8), Loop(NonZeroU8),
Tuple, Tuple,
Slope(SlopeModifier, Instruction), Slope(SlopeModifier, Instruction),
@ -15,12 +15,12 @@ enum Wrapper {
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
enum InflateError { pub enum InflateError {
#[error("misplaced {0} end symbol")] #[error("misplaced {0} end symbol")]
MismatchedEnd(WrapperDiscriminants), MismatchedEnd(WrapperDiscriminants),
} }
fn inflate(mut flat_atoms: Vec<FlatAtom>) -> Result<Vec<Atom>, InflateError> { pub fn inflate(mut flat_atoms: Vec<FlatAtom>) -> Result<Vec<Atom>, InflateError> {
type Error = InflateError; type Error = InflateError;
let mut result = Vec::with_capacity(flat_atoms.len()); let mut result = Vec::with_capacity(flat_atoms.len());
let mut loop_stack: Vec<Vec<Atom>> = Vec::new(); let mut loop_stack: Vec<Vec<Atom>> = Vec::new();

View file

@ -12,29 +12,41 @@ mod cli;
fn main() -> Result<(), serde_yml::Error> { fn main() -> Result<(), serde_yml::Error> {
// println!("{}", option_env!("TEST").unwrap_or("ok")); // println!("{}", option_env!("TEST").unwrap_or("ok"));
let args = Cli::parse(); let args = Cli::parse();
println!( // #[cfg(debug_assertions)]
"{}", // {
serde_yml::to_string(&BngFile::new( // println!("{}", serde_yml::to_string(&bngfile_generator())?);
vec![HashMap::from([ // println!("{:?}", args);
( // }
"sine".to_string(), match args {
Instrument::new("sin(2*PI*f*t)".parse().unwrap(), None) Cli::Play(PlayOpts { input }) => {
), let bng_file: BngFile = serde_yml::from_str(&input)?;
( #[cfg(debug_assertions)]
"square".to_string(), println!("{:?}", bng_file);
Instrument::new( }
"v*abs(sin(2*PI*f*t))".parse().unwrap(), _ => unimplemented!("can't do that yet"),
Some(HashMap::from([("v".to_string(), 1f32)])) }
)
)
])],
HashMap::<String, Channel>::from([(
"melody".to_string(),
Channel::new("sine".to_string(), vec![])
)])
))?
);
#[cfg(debug_assertions)]
println!("{:?}", args);
Ok(()) 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::<String, Channel>::from([(
"melody".to_string(),
Channel::new("sine".to_string(), vec![].into()),
)]),
)
}