From 99eb9f048f44f348629a22ffe3f36e0aa09b550f Mon Sep 17 00:00:00 2001 From: Lyle Mantooth Date: Sun, 4 Feb 2024 23:00:17 -0500 Subject: [PATCH 1/2] Use nom to parse instrument data. --- Cargo.lock | 25 ++++++++++++++++++++ Cargo.toml | 1 + src/instruments.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 85 insertions(+) create mode 100644 src/instruments.rs diff --git a/Cargo.lock b/Cargo.lock index 8670b04..6d22eb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,31 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "slamshuffle" version = "0.1.0" +dependencies = [ + "nom", +] diff --git a/Cargo.toml b/Cargo.toml index 564d871..0547914 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +nom = "7" diff --git a/src/instruments.rs b/src/instruments.rs new file mode 100644 index 0000000..26051fb --- /dev/null +++ b/src/instruments.rs @@ -0,0 +1,58 @@ +use nom::{ + bytes::complete::tag_no_case, + character::complete::{hex_digit1, line_ending, not_line_ending, space1}, + combinator::{map_parser, map_res, opt}, + error::{Error, ErrorKind}, + multi::fold_many0, + sequence::{preceded, separated_pair, terminated}, + IResult, +}; + +#[derive(Default)] +pub struct InstrumentSet([u8; 32]); + +impl InstrumentSet { + pub fn generate(input: &str) -> Self { + match Self::parse(input) { + Ok((_, set)) => set, + Err(_) => panic!(), + } + } + + fn parse<'i>(input: &'i str) -> IResult<&'i str, Self> { + fold_many0( + terminated( + opt(preceded( + tag_no_case("#WAVE"), + map_parser(not_line_ending, wave_numbers), + )), + line_ending, + ), + Self::default, + |mut set, bytes: Option<(usize, u8)>| match bytes { + Some((slot, sample)) => { + let pos = slot - 0x20; + set.0[pos] = sample; + set + } + None => set, + }, + )(input) + } +} + +fn wave_numbers(input: &str) -> IResult<&str, (usize, u8)> { + separated_pair( + map_res(hex_digit1, |s: &str| { + s.parse::().map_err(|_| nom::error::make_error::<&str, Error<&str>>(s, ErrorKind::HexDigit)).and_then(|n| { + if (0x20..0x30).contains(&n) { + Ok(n) + } else { + Err(nom::error::make_error(s, ErrorKind::MapRes)) + } + }) + }), + space1, + map_res(hex_digit1, |s: &str| s.parse::()), + )(input) +} diff --git a/src/lib.rs b/src/lib.rs index b0011ee..db615be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ +pub mod instruments; pub mod mfvi; From 3018db7dd1507249900e31c27e484d6fb7422685 Mon Sep 17 00:00:00 2001 From: Lyle Mantooth Date: Sun, 4 Feb 2024 23:40:19 -0500 Subject: [PATCH 2/2] Handle hex or decimal digits in #WAVE macro. Now with tests! --- src/instruments.rs | 71 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/src/instruments.rs b/src/instruments.rs index 26051fb..0beb4c3 100644 --- a/src/instruments.rs +++ b/src/instruments.rs @@ -1,14 +1,16 @@ use nom::{ + branch::alt, bytes::complete::tag_no_case, - character::complete::{hex_digit1, line_ending, not_line_ending, space1}, - combinator::{map_parser, map_res, opt}, + character::complete::{digit1, hex_digit1, line_ending, not_line_ending, space1}, + combinator::{map_parser, map_res, opt, success, value}, error::{Error, ErrorKind}, - multi::fold_many0, - sequence::{preceded, separated_pair, terminated}, + multi::fold_many1, + sequence::{preceded, separated_pair, terminated, tuple}, IResult, }; #[derive(Default)] +#[cfg_attr(test, derive(Debug, PartialEq))] pub struct InstrumentSet([u8; 32]); impl InstrumentSet { @@ -20,10 +22,10 @@ impl InstrumentSet { } fn parse<'i>(input: &'i str) -> IResult<&'i str, Self> { - fold_many0( + fold_many1( terminated( opt(preceded( - tag_no_case("#WAVE"), + tag_no_case("#WAVE "), map_parser(not_line_ending, wave_numbers), )), line_ending, @@ -31,7 +33,7 @@ impl InstrumentSet { Self::default, |mut set, bytes: Option<(usize, u8)>| match bytes { Some((slot, sample)) => { - let pos = slot - 0x20; + let pos = (slot - 0x20) * 2; set.0[pos] = sample; set } @@ -41,18 +43,55 @@ impl InstrumentSet { } } +fn hexordecimal(input: &str) -> IResult<&str, (u32, &str)> { + alt(( + tuple((value(16, tag_no_case("0x")), hex_digit1)), + tuple((success(10), digit1)), + ))(input) +} + fn wave_numbers(input: &str) -> IResult<&str, (usize, u8)> { separated_pair( - map_res(hex_digit1, |s: &str| { - s.parse::().map_err(|_| nom::error::make_error::<&str, Error<&str>>(s, ErrorKind::HexDigit)).and_then(|n| { - if (0x20..0x30).contains(&n) { - Ok(n) - } else { - Err(nom::error::make_error(s, ErrorKind::MapRes)) - } - }) + map_res(hexordecimal, |(radix, s)| { + usize::from_str_radix(s, radix) + .map_err(|_| nom::error::make_error::<&str, Error<&str>>(s, ErrorKind::HexDigit)) + .and_then(|n| { + if (0x20..0x30).contains(&n) { + Ok(n) + } else { + Err(nom::error::make_error(s, ErrorKind::MapRes)) + } + }) }), space1, - map_res(hex_digit1, |s: &str| s.parse::()), + map_res(hexordecimal, |(radix, s)| { + u8::from_str_radix(s, radix) + }), )(input) } + +#[cfg(test)] +mod test { + use super::InstrumentSet; + + #[test] + fn parse_waves() { + let input = " +#WAVE 0x20 0xA5 flute +#WAVE 0x21 0x95 clari +#WAVE 0x22 0x65 strings +#WAVE 0x23 0x63 ostrings +#WAVE 0x24 0x34 guitar +#WAVE 0x25 0x53 fbass + "; + + assert_eq!( + InstrumentSet::generate(input), + InstrumentSet([ + 0xa5, 0x00, 0x95, 0x00, 0x65, 0x00, 0x63, 0x00, 0x34, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + ]) + ); + } +}