move input to parsing time, loop parser test

This commit is contained in:
Breval Ferrari 2025-05-21 18:11:39 +02:00
parent 1044ad9b1d
commit 28bd727090
2 changed files with 83 additions and 19 deletions

View file

@ -6,7 +6,7 @@ use fasteval::{Compiler, Instruction, Slab};
#[derive(From, AsRef)]
#[cfg_attr(test, derive(Debug))]
pub struct TokenVec<'a>(Vec<Box<dyn Token + 'a>>);
pub struct TokenVec<'a>(pub(crate) Vec<Box<dyn Token + 'a>>);
#[cfg(not(test))]
pub trait Token {
@ -63,7 +63,7 @@ pub enum LoopCount {
impl Default for LoopCount {
fn default() -> Self {
LoopCount::Litteral(1)
LoopCount::Litteral(2)
}
}

View file

@ -26,24 +26,26 @@ use crate::compiler::{
};
#[derive(Builder)]
pub struct Parser<'i, 'n, 's, 'v, N: AsRef<str> + Clone> {
input: &'i str,
pub struct Parser<'n, 's, 'v, N: AsRef<str> + Clone> {
notes: &'n [N],
slopes: &'s HashMap<String, VariableChange>,
variables: &'v [char],
}
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>>> {
impl<'a, 's, N: AsRef<str> + Clone> Parser<'_, 's, '_, N> {
pub fn parse_all(
&self,
input: &'a str,
) -> Result<TokenVec<'s>, Error<nom_locate::LocatedSpan<&'a str>>> {
token_parser(self)
.parse_complete(LocatedSpan::new(self.input))
.parse_complete(LocatedSpan::new(input))
.finish()
.map(|(_, o)| o)
}
}
fn token_parser<'a, 's, I, N>(
parser: &Parser<'_, '_, 's, '_, 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> + Copy,
@ -139,9 +141,7 @@ impl VariableChange {
}
impl<'s> Loop<'s> {
fn parser<'i, 'n, 'v, I, N>(
parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self>
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self>
where
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy,
<I as Input>::Item: AsChar,
@ -165,9 +165,7 @@ impl<'s> Loop<'s> {
}
impl<'s> Tuplet<'s> {
fn parser<'i, 'n, 'v, I, N>(
parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self>
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self>
where
I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
<I as Input>::Item: AsChar,
@ -183,9 +181,7 @@ impl<'s> Tuplet<'s> {
}
impl<'s> Slope<'s> {
fn parser<'i, 'n, 'v, I, N>(
parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self>
fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult<I, Self>
where
I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
<I as Input>::Item: AsChar,
@ -257,13 +253,19 @@ where
#[cfg(test)]
mod tests {
use nom::Parser;
use std::collections::HashMap;
use nom::{IResult, Parser};
use crate::{
compiler::{Marker, Note, Silence, VariableChange},
compiler::{
Loop, LoopCount, Marker, Note, Silence, Slope, Token, TokenVec, VariableChange,
},
parser::expression_parser,
};
use super::ParserBuilder;
#[test]
fn expression_parser_test() {
let parser = expression_parser(&['x']);
@ -419,4 +421,66 @@ mod tests {
);
}
}
#[test]
fn r#loop() {
let slopes = Default::default();
fn parser_builder<'s>(
slopes: &'s HashMap<String, VariableChange>,
) -> impl Fn(&str) -> IResult<&str, Loop<'s>> {
move |input: &str| {
Loop::parser(
&ParserBuilder::create_empty()
.notes(&["do", "", "mi"])
.slopes(slopes)
.variables(&['n'])
.build()
.unwrap(),
)
.parse(input)
}
}
let parser = parser_builder(&slopes);
let mut working_cases = vec![
(
"(.%)",
(
"",
Loop(
LoopCount::Litteral(2),
TokenVec(vec![Box::new(Silence), Box::new(Marker)]),
),
),
),
("()", ("", Loop(LoopCount::Litteral(2), TokenVec(vec![])))),
("(4)", ("", Loop(LoopCount::Litteral(4), TokenVec(vec![])))),
(
"(n)",
("", Loop(LoopCount::Variable('n'), TokenVec(vec![]))),
),
(
"(ndo)",
(
"",
Loop(LoopCount::Variable('n'), TokenVec(vec![Box::new(Note(0))])),
),
),
];
let mut not_working_cases = vec!["", "(", ")", "(2", "(p)"];
for (test, expected) in working_cases.drain(..) {
let output = parser(test);
if let Ok(result) = output {
assert_eq!(expected, result, "case \"{test}\"");
} else {
panic!("result of \"{test}\" was not Ok: {output:?}");
}
}
for test in not_working_cases.drain(..) {
let output = parser(test);
assert!(
output.is_err(),
"result of \"{test}\" was not Err: {output:?}"
);
}
}
}