note test, make Note parser generic over notes and match longer strings first
This commit is contained in:
parent
fa49625f7f
commit
22771168d2
1 changed files with 88 additions and 21 deletions
109
src/parser.rs
109
src/parser.rs
|
@ -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,22 +98,26 @@ 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()
|
||||
.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>>
|
||||
})
|
||||
.collect();
|
||||
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_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", "ré", "mi", "m"]).parse(input);
|
||||
let mut working_cases = vec![
|
||||
("do", ("", Note(0))),
|
||||
("ré", ("", 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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue