note test, make Note parser generic over notes and match longer strings first

This commit is contained in:
Breval Ferrari 2025-05-21 12:22:58 +02:00
parent fa49625f7f
commit 22771168d2

View file

@ -26,14 +26,14 @@ use crate::compiler::{
};
#[derive(Builder)]
pub struct Parser<'i, 'n, 's, 'v> {
pub struct Parser<'i, 'n, 's, 'v, N: AsRef<str> + Clone> {
input: &'i str,
notes: &'n [String],
notes: &'n [N],
slopes: &'s HashMap<String, VariableChange>,
variables: &'v [char],
}
impl<'i, 's> Parser<'i, '_, 's, '_> {
impl<'i, 's, N: AsRef<str> + Clone> Parser<'i, '_, 's, '_, N> {
pub fn parse_all(&self) -> Result<TokenVec<'s>, Error<nom_locate::LocatedSpan<&'i str>>> {
token_parser(self)
.parse_complete(LocatedSpan::new(self.input))
@ -42,13 +42,14 @@ impl<'i, 's> Parser<'i, '_, 's, '_> {
}
}
fn token_parser<'a, 's, I>(
parser: &Parser<'_, '_, 's, '_>,
fn token_parser<'a, 's, I, N>(
parser: &Parser<'_, '_, 's, '_, N>,
) -> impl NomParser<I, Output = TokenVec<'s>, Error = nom::error::Error<I>>
where
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
<I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone,
{
let space_or_comment = || {
value(
@ -97,20 +98,24 @@ impl Marker {
}
impl Note {
fn parser<'n, I>(
notes: &'n [String],
fn parser<'n, N, I>(
notes: &'n [N],
) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
where
N: AsRef<str> + Clone,
I: Input + for<'z> Compare<&'z str>,
{
|input: I| {
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = notes
.iter()
.enumerate()
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = {
let mut sorted = notes.iter().enumerate().collect::<Vec<(usize, &N)>>();
sorted.sort_by_key(|(_, n)| n.as_ref().len());
sorted
}
.drain(..)
.rev()
.map(|(i, t)| {
Box::new(move |input: I| {
value(Note(i as u8), tag(t.clone().as_str())).parse(input)
}) as Box<dyn Fn(I) -> IResult<I, Self>>
Box::new(move |input: I| value(Note(i as u8), tag(t.clone().as_ref())).parse(input))
as Box<dyn Fn(I) -> IResult<I, Self>>
})
.collect();
alt(parsers.as_mut_slice()).parse(input)
@ -134,11 +139,14 @@ impl VariableChange {
}
impl<'s> Loop<'s> {
fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
fn parser<'i, 'n, 'v, I, N>(
parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self>
where
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
<I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone,
{
|input| {
delimited(
@ -157,11 +165,14 @@ impl<'s> Loop<'s> {
}
impl<'s> Tuplet<'s> {
fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
fn parser<'i, 'n, 'v, I, N>(
parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self>
where
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
<I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone,
{
|input| {
delimited(char('['), token_parser(parser), char(']'))
@ -172,11 +183,14 @@ impl<'s> Tuplet<'s> {
}
impl<'s> Slope<'s> {
fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
fn parser<'i, 'n, 'v, I, N>(
parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self>
where
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
<I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone,
{
|input| {
let iter: std::collections::hash_map::Iter<'s, String, VariableChange> =
@ -245,9 +259,11 @@ fn expression_parser<'a, I: Input + AsRef<str>, C: Into<char> + Ord + Display>(
#[cfg(test)]
mod tests {
use nom::Parser;
use nom_locate::LocatedSpan;
use crate::{compiler::Silence, parser::expression_parser};
use crate::{
compiler::{Marker, Note, Silence},
parser::expression_parser,
};
#[test]
fn expression_parser_test() {
@ -301,4 +317,55 @@ mod tests {
assert!(output.is_err(), "result was not Err: {output:?}");
}
}
#[test]
fn marker() {
let parser = |input| Marker::parser().parse(input);
let mut working_cases = vec![
("%", ("", Marker)),
("%dd", ("dd", Marker)),
("%%", ("%", Marker)),
];
let mut not_working_cases = vec!["", ",", "d%", " "];
for (test, expected) in working_cases.drain(..) {
let output = parser(test);
if let Ok(result) = output {
assert_eq!(expected, result);
} else {
panic!("result was not Ok: {output:?}");
}
}
for test in not_working_cases.drain(..) {
let output = parser(test);
assert!(output.is_err(), "result was not Err: {output:?}");
}
}
#[test]
fn note() {
let parser = |input| Note::parser(&["do", "r", "", "mi", "m"]).parse(input);
let mut working_cases = vec![
("do", ("", Note(0))),
("", ("", Note(2))),
("rér", ("r", Note(2))),
("r.", (".", Note(1))),
("mi", ("", Note(3))),
("mif", ("f", Note(3))),
("mi ", (" ", Note(3))),
("m ", (" ", Note(4))),
];
let mut not_working_cases = vec!["", ",", " mi", " ", "dré"];
for (test, expected) in working_cases.drain(..) {
let output = parser(test);
if let Ok(result) = output {
assert_eq!(expected, result, "{test}");
} else {
panic!("result was not Ok: {output:?}");
}
}
for test in not_working_cases.drain(..) {
let output = parser(test);
assert!(output.is_err(), "result was not Err: {output:?}");
}
}
}