verbose error support
This commit is contained in:
parent
a2566f1be3
commit
bdb42ac846
4 changed files with 120 additions and 125 deletions
46
poc/poc.yml
46
poc/poc.yml
|
@ -15,7 +15,7 @@ channels:
|
|||
instr: sine
|
||||
score:
|
||||
notes: cCdDefFgGaAb
|
||||
sheet:
|
||||
sheet: |
|
||||
aabc.
|
||||
'ab°A
|
||||
+d+d+d---
|
||||
|
@ -28,24 +28,26 @@ channels:
|
|||
[ffe]
|
||||
{l 1-cos((PI*x)/2),acced}
|
||||
abbc!o5cc!v15feed!l4fedd!t60Gdd
|
||||
# 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
|
||||
|
||||
; syntax :
|
||||
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,
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
|
||||
use derive_new::new;
|
||||
use nom::{
|
||||
character::complete::one_of,
|
||||
combinator::all_consuming,
|
||||
error::{context, convert_error, ContextError, ParseError, VerboseError},
|
||||
multi::{many0, many1},
|
||||
sequence::{preceded, terminated},
|
||||
Parser,
|
||||
Needed, Parser,
|
||||
};
|
||||
use serde::{
|
||||
de::{self, Deserializer, Visitor},
|
||||
|
@ -21,29 +24,12 @@ use super::{
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
enum AtomsSerializeError {
|
||||
#[error("sheet parsing error: {0}")]
|
||||
#[error("sheet parsing error:\n{0}")]
|
||||
Parsing(String),
|
||||
#[error("sheet semantics: {0}")]
|
||||
Inflation(#[from] InflateError),
|
||||
}
|
||||
|
||||
fn nom_err_message(e: nom::Err<nom::error::Error<&str>>) -> String {
|
||||
match e {
|
||||
nom::Err::Incomplete(needed) => format!(
|
||||
"input is incomplete, needed {} byte(s) more",
|
||||
match needed {
|
||||
nom::Needed::Unknown => "?".to_string(),
|
||||
nom::Needed::Size(s) => s.to_string(),
|
||||
}
|
||||
),
|
||||
nom::Err::Error(e) | nom::Err::Failure(e) => format!(
|
||||
"got error code {code:#?} at \"{input}\"",
|
||||
code = e.code,
|
||||
input = e.input
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Atoms {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
|
@ -64,37 +50,30 @@ impl<'de> Deserialize<'de> for Atoms {
|
|||
if sheet.is_empty() {
|
||||
Ok(Default::default())
|
||||
} else {
|
||||
atom_mapper::<D, _>(&sheet, atom(¬es))
|
||||
all_consuming(terminated(
|
||||
many1(preceded(maybe_yml_str_space(), atom(¬es))),
|
||||
maybe_yml_str_space(),
|
||||
))(&sheet)
|
||||
.map_err(|e: nom::Err<VerboseError<&str>>| 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(sheet.as_str(), e),
|
||||
})
|
||||
.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_yml_str_space<'a, E>() -> impl Parser<&'a str, Vec<char>, E>
|
||||
where
|
||||
E: nom::error::ParseError<&'a str>,
|
||||
E: nom::error::ParseError<&'a str> + ContextError<&'a str>,
|
||||
{
|
||||
many0(one_of(" \t\r"))
|
||||
}
|
||||
|
||||
fn atom_mapper<'a, 'de, D, P>(
|
||||
input: &'a str,
|
||||
parser: P,
|
||||
) -> Result<Atoms, <D as Deserializer<'de>>::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
P: Parser<&'a str, FlatAtom, nom::error::Error<&'a str>>,
|
||||
{
|
||||
all_consuming(terminated(
|
||||
many1(preceded(maybe_yml_str_space(), parser)),
|
||||
maybe_yml_str_space(),
|
||||
))(input)
|
||||
.map_err(nom_err_message)
|
||||
.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)
|
||||
context("yml white space", many0(one_of(" \t\r\n")))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
num::{NonZeroU16, NonZeroU8},
|
||||
num::{NonZeroU16, NonZeroU8, TryFromIntError},
|
||||
};
|
||||
|
||||
use clap::builder::TypedValueParser;
|
||||
|
@ -10,7 +10,7 @@ use nom::{
|
|||
bytes::complete::{take_till, take_till1},
|
||||
character::complete::{anychar, char, one_of, space1, u16, u8},
|
||||
combinator::{map_opt, map_res, opt, value, verify},
|
||||
error::{context, ContextError, ParseError},
|
||||
error::{context, ContextError, ErrorKind, FromExternalError, ParseError},
|
||||
multi::many0,
|
||||
sequence::{delimited, pair, preceded, separated_pair, terminated},
|
||||
Err, IResult, Parser,
|
||||
|
@ -25,50 +25,59 @@ use super::{
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub fn atom(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).map(FlatAtom::Modifier),
|
||||
quick_modifier.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(
|
||||
pub fn atom<'a, E>(notes: &'a str) -> impl Parser<&'a str, FlatAtom, E>
|
||||
where
|
||||
E: ParseError<&'a str>
|
||||
+ ContextError<&'a str>
|
||||
+ FromExternalError<&'a str, TryFromIntError>
|
||||
+ FromExternalError<&'a str, E>,
|
||||
{
|
||||
context(
|
||||
"atom",
|
||||
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).map(FlatAtom::Modifier),
|
||||
quick_modifier.map(FlatAtom::QuickModifier),
|
||||
preceded(
|
||||
char(Atom::SLOPE.0),
|
||||
separated_pair(
|
||||
slope_modifier,
|
||||
char(' '),
|
||||
map_res(take_till1(|c| c == ','), |s: &str| {
|
||||
s.parse()
|
||||
.map_err(|_| nom::error::Error::new(s, nom::error::ErrorKind::Verify))
|
||||
}),
|
||||
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(
|
||||
slope_modifier,
|
||||
char(' '),
|
||||
map_res(take_till1(|c| c == ','), |s: &str| {
|
||||
s.parse()
|
||||
.map_err(|_| E::from_error_kind(s, 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),
|
||||
),
|
||||
),
|
||||
))
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ mod flat_atom {
|
|||
fn note() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::Note(2))),
|
||||
atom("abcdefg").parse(concatcp!('c', SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!('c', SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ mod flat_atom {
|
|||
fn rest() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::Rest)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::REST, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::REST, SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ mod flat_atom {
|
|||
fn start_here() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::StartHere)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::START_HERE, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::START_HERE, SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,12 @@ mod flat_atom {
|
|||
SAMPLE_STR,
|
||||
FlatAtom::Modifier(Modifier::Length(unsafe { NonZeroU8::new_unchecked(2) }))
|
||||
)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::MODIFIER, Modifier::LENGTH, 2u8, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(
|
||||
Atom::MODIFIER,
|
||||
Modifier::LENGTH,
|
||||
2u8,
|
||||
SAMPLE_STR
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -73,7 +78,7 @@ mod flat_atom {
|
|||
SAMPLE_STR,
|
||||
FlatAtom::QuickModifier(QuickModifier::Length(UP))
|
||||
)),
|
||||
atom("abcdefg").parse(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -84,14 +89,14 @@ mod flat_atom {
|
|||
SAMPLE_STR,
|
||||
FlatAtom::LoopStarts(unsafe { NonZeroU8::new_unchecked(3) })
|
||||
)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::LOOP.0, 3u8, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::LOOP.0, 3u8, SAMPLE_STR))
|
||||
);
|
||||
assert_eq!(
|
||||
Ok((
|
||||
SAMPLE_STR,
|
||||
FlatAtom::LoopStarts(unsafe { NonZeroU8::new_unchecked(2) })
|
||||
)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::LOOP.0, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::LOOP.0, SAMPLE_STR))
|
||||
);
|
||||
assert_eq!(
|
||||
Err(nom::Err::Error(Error::new(
|
||||
|
@ -106,7 +111,7 @@ mod flat_atom {
|
|||
fn loop_ends() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::LoopEnds)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::LOOP.1, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::LOOP.1, SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -114,7 +119,7 @@ mod flat_atom {
|
|||
fn tuple_starts() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::TupleStarts)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::TUPLE.0, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::TUPLE.0, SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -122,7 +127,7 @@ mod flat_atom {
|
|||
fn tuple_ends() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::TupleEnds)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::TUPLE.1, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::TUPLE.1, SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -133,7 +138,7 @@ mod flat_atom {
|
|||
SAMPLE_STR,
|
||||
FlatAtom::SlopeStarts(SlopeModifier::Note, FASTEVAL_INSTRUCTION.parse().unwrap())
|
||||
)),
|
||||
atom("abcdefg").parse(concatcp!(
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(
|
||||
Atom::SLOPE.0,
|
||||
SlopeModifier::NOTE,
|
||||
' ',
|
||||
|
@ -148,7 +153,7 @@ mod flat_atom {
|
|||
fn slope_ends() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::SlopeEnds)),
|
||||
atom("abcdefg").parse(concatcp!(Atom::SLOPE.1, SAMPLE_STR))
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(Atom::SLOPE.1, SAMPLE_STR))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -156,7 +161,7 @@ mod flat_atom {
|
|||
fn comment() {
|
||||
assert_eq!(
|
||||
Ok((SAMPLE_STR, FlatAtom::Comment)),
|
||||
atom("abcdefg").parse(concatcp!(
|
||||
atom::<Error<&str>>("abcdefg").parse(concatcp!(
|
||||
Atom::COMMENT.0,
|
||||
"hi I'm a little pony",
|
||||
SAMPLE_STR,
|
||||
|
|
Loading…
Reference in a new issue