note length parser

This commit is contained in:
Denis Redozubov 2023-05-02 19:34:21 +04:00
parent e7a933b837
commit 2251a09bc2

View file

@ -2,9 +2,11 @@ use std::str;
use std::vec::Vec; use std::vec::Vec;
pub use nom::character::complete::{char, digit1}; pub use nom::character::complete::{char, digit1};
use nom::sequence::{separated_pair, tuple};
use nom::{Err, IResult}; use nom::{Err, IResult};
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::combinator::{map, map_res};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -21,85 +23,83 @@ pub enum BasicLength {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModdedLength { pub enum ModdedLength {
Plain(BasicLength), Plain(BasicLength),
Dotted(BasicLength), Dotted(BasicLength)
Triplet(BasicLength)
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum Length { pub enum Length {
Simple(ModdedLength), Simple(ModdedLength),
Tied(ModdedLength, ModdedLength) Tied(ModdedLength, ModdedLength),
Triplet(ModdedLength)
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum Note { pub enum Note {
Hit(Length), Hit,
Rest(Length) Rest
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Group { notes: Vec<Note> } pub struct Group { notes: Vec<Note>, length: Length }
fn hit(len: Length, input: &str) -> IResult<&str, Note> { fn hit(input: &str) -> IResult<&str, Note> {
// note that this is really creating a function, the parser for abc // note that this is really creating a function, the parser for abc
// vvvvv // vvvvv
// which is then called here, returning an IResult<&str, &str> // which is then called here, returning an IResult<&str, &str>
// vvvvv // vvvvv
let (rem, c) = char('x')(input)?; map(char('x'), |_| { Note::Hit })(input)
Ok((rem, Note::Hit(len)))
} }
fn rest( len: Length, input: &str) -> IResult<&str, Note> { fn rest(input: &str) -> IResult<&str, Note> {
let (rem, c) = char('-')(input)?; map(char('-'), |_| { Note::Rest })(input)
Ok((rem, Note::Rest(len)))
} }
fn note(input: &str, len: Length) -> IResult<&str, Note> { fn note(input: &str) -> IResult<&str, Note> {
alt((hit(len), rest(len)))(input) alt((hit, rest))(input)
} }
fn length_basic(input: &str) -> IResult<&str, Length> { fn length_basic(input: &str) -> IResult<&str, BasicLength> {
match map_res(digit1, str::parse)(input) { match map_res(digit1, str::parse)(input) {
Ok(r,1) => Ok(r, Whole), Ok((r,1)) => Ok((r, BasicLength::Whole)),
Ok(r,2) => Ok(r, Half), Ok((r,2)) => Ok((r, BasicLength::Half)),
Ok(r,4) => Ok(r, Fourth), Ok((r,4)) => Ok((r, BasicLength::Fourth)),
Ok(r,8) => Ok(r, Eighth), Ok((r,8)) => Ok((r, BasicLength::Eighth)),
Ok(r,16) => Ok(r, Sixteenth), Ok((r,16)) => Ok((r, BasicLength::Sixteenth)),
Ok(r,32) => Ok(r, ThirtySecond), Ok((r,32)) => Ok((r, BasicLength::ThirtySecond)),
Ok(r, 64) => Ok(r, SixtyFourth), Ok((r, 64)) => Ok((r, BasicLength::SixtyFourth)),
e => e Ok((r, i)) => {
Err(Err::Error(nom::error::make_error(r, nom::error::ErrorKind::Fail)))
},
Err(e) => Err(e)
} }
} }
fn tie_length(input: &str) -> IResult<&str, Length> { fn dotted_length(input: &str) -> IResult<&str, ModdedLength> {
let (rem, (l1,l2)) = separated_pair(length_basic, char('+'), length_basic)?; map(tuple((length_basic, char('.'))), |(l, _)| { ModdedLength::Dotted(l)})(input)
Ok(rem, Tie(l1, l2)) }
fn modded_length(input: &str) -> IResult<&str, ModdedLength> {
alt((dotted_length, map(length_basic, |x| {ModdedLength::Plain(x)})))(input)
}
fn triplet_length(input: &str) -> IResult<&str, Length> {
map(tuple((modded_length, char('t'))), |(l, _)| { Length::Triplet(l)})(input)
}
fn tied_length(input: &str) -> IResult<&str, Length> {
map(separated_pair(modded_length, char('+'), modded_length), |(x, y)| { Length::Tied(x,y)})(input)
} }
fn length(input: &str) -> IResult<&str, Length> { fn length(input: &str) -> IResult<&str, Length> {
let (rem, len) = alt(tie_length, length_basic)(input)?; alt((triplet_length, tied_length, map(modded_length, |x| { Length::Simple(x) })))(input)
let (rem2, is_triplet) = char('t')(rem)?;
if is_triplet {
Triplet(len)
} else {
leṇ
}
} }
#[test] #[test]
fn parse_basic_length() { fn parse_basic_length() {
assert_eq!( assert_eq!(length("16"), Ok(("", Length::Simple(ModdedLength::Plain(BasicLength::Sixteenth)))));
length("16"), assert_eq!(length("8+16"), Ok(("", Length::Tied(ModdedLength::Plain(BasicLength::Eighth), ModdedLength::Plain(BasicLength::Sixteenth)))));
Ok(( assert_eq!(length("8t"), Ok(("", Length::Triplet(ModdedLength::Plain(BasicLength::Eighth)))));
"", assert_eq!(length("4.t"), Ok(("", Length::Triplet(ModdedLength::Dotted(BasicLength::Fourth)))));
Sixteenth
))
);
assert_eq!(
length("8+16"),
Ok("", Tie(Eighth, Sixteenth))
);
assert_eq!(length("8t"), Triplet(Eighth))
} }
// “x” hit // “x” hit