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
103
src/parser.rs
103
src/parser.rs
|
@ -26,14 +26,14 @@ use crate::compiler::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Builder)]
|
#[derive(Builder)]
|
||||||
pub struct Parser<'i, 'n, 's, 'v> {
|
pub struct Parser<'i, 'n, 's, 'v, N: AsRef<str> + Clone> {
|
||||||
input: &'i str,
|
input: &'i str,
|
||||||
notes: &'n [String],
|
notes: &'n [N],
|
||||||
slopes: &'s HashMap<String, VariableChange>,
|
slopes: &'s HashMap<String, VariableChange>,
|
||||||
variables: &'v [char],
|
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>>> {
|
pub fn parse_all(&self) -> Result<TokenVec<'s>, Error<nom_locate::LocatedSpan<&'i str>>> {
|
||||||
token_parser(self)
|
token_parser(self)
|
||||||
.parse_complete(LocatedSpan::new(self.input))
|
.parse_complete(LocatedSpan::new(self.input))
|
||||||
|
@ -42,13 +42,14 @@ impl<'i, 's> Parser<'i, '_, 's, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token_parser<'a, 's, I>(
|
fn token_parser<'a, 's, I, N>(
|
||||||
parser: &Parser<'_, '_, 's, '_>,
|
parser: &Parser<'_, '_, 's, '_, N>,
|
||||||
) -> impl NomParser<I, Output = TokenVec<'s>, Error = nom::error::Error<I>>
|
) -> impl NomParser<I, Output = TokenVec<'s>, Error = nom::error::Error<I>>
|
||||||
where
|
where
|
||||||
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
|
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
|
||||||
<I as Input>::Item: AsChar,
|
<I as Input>::Item: AsChar,
|
||||||
<I as Input>::Item: PartialEq<char>,
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
N: AsRef<str> + Clone,
|
||||||
{
|
{
|
||||||
let space_or_comment = || {
|
let space_or_comment = || {
|
||||||
value(
|
value(
|
||||||
|
@ -97,20 +98,24 @@ impl Marker {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Note {
|
impl Note {
|
||||||
fn parser<'n, I>(
|
fn parser<'n, N, I>(
|
||||||
notes: &'n [String],
|
notes: &'n [N],
|
||||||
) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
|
) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
|
||||||
where
|
where
|
||||||
|
N: AsRef<str> + Clone,
|
||||||
I: Input + for<'z> Compare<&'z str>,
|
I: Input + for<'z> Compare<&'z str>,
|
||||||
{
|
{
|
||||||
|input: I| {
|
|input: I| {
|
||||||
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = notes
|
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = {
|
||||||
.iter()
|
let mut sorted = notes.iter().enumerate().collect::<Vec<(usize, &N)>>();
|
||||||
.enumerate()
|
sorted.sort_by_key(|(_, n)| n.as_ref().len());
|
||||||
|
sorted
|
||||||
|
}
|
||||||
|
.drain(..)
|
||||||
|
.rev()
|
||||||
.map(|(i, t)| {
|
.map(|(i, t)| {
|
||||||
Box::new(move |input: I| {
|
Box::new(move |input: I| value(Note(i as u8), tag(t.clone().as_ref())).parse(input))
|
||||||
value(Note(i as u8), tag(t.clone().as_str())).parse(input)
|
as Box<dyn Fn(I) -> IResult<I, Self>>
|
||||||
}) as Box<dyn Fn(I) -> IResult<I, Self>>
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
alt(parsers.as_mut_slice()).parse(input)
|
alt(parsers.as_mut_slice()).parse(input)
|
||||||
|
@ -134,11 +139,14 @@ impl VariableChange {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Loop<'s> {
|
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
|
where
|
||||||
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
|
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
|
||||||
<I as Input>::Item: AsChar,
|
<I as Input>::Item: AsChar,
|
||||||
<I as Input>::Item: PartialEq<char>,
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
N: AsRef<str> + Clone,
|
||||||
{
|
{
|
||||||
|input| {
|
|input| {
|
||||||
delimited(
|
delimited(
|
||||||
|
@ -157,11 +165,14 @@ impl<'s> Loop<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Tuplet<'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
|
where
|
||||||
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
|
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
|
||||||
<I as Input>::Item: AsChar,
|
<I as Input>::Item: AsChar,
|
||||||
<I as Input>::Item: PartialEq<char>,
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
N: AsRef<str> + Clone,
|
||||||
{
|
{
|
||||||
|input| {
|
|input| {
|
||||||
delimited(char('['), token_parser(parser), char(']'))
|
delimited(char('['), token_parser(parser), char(']'))
|
||||||
|
@ -172,11 +183,14 @@ impl<'s> Tuplet<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Slope<'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
|
where
|
||||||
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
|
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
|
||||||
<I as Input>::Item: AsChar,
|
<I as Input>::Item: AsChar,
|
||||||
<I as Input>::Item: PartialEq<char>,
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
N: AsRef<str> + Clone,
|
||||||
{
|
{
|
||||||
|input| {
|
|input| {
|
||||||
let iter: std::collections::hash_map::Iter<'s, String, VariableChange> =
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use nom::Parser;
|
use nom::Parser;
|
||||||
use nom_locate::LocatedSpan;
|
|
||||||
|
|
||||||
use crate::{compiler::Silence, parser::expression_parser};
|
use crate::{
|
||||||
|
compiler::{Marker, Note, Silence},
|
||||||
|
parser::expression_parser,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expression_parser_test() {
|
fn expression_parser_test() {
|
||||||
|
@ -301,4 +317,55 @@ mod tests {
|
||||||
assert!(output.is_err(), "result was not Err: {output:?}");
|
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