make parsers generic over I again for tests and other things
This commit is contained in:
parent
8563ee3de2
commit
16f79c302a
1 changed files with 93 additions and 81 deletions
174
src/parser.rs
174
src/parser.rs
|
@ -6,7 +6,7 @@ use std::{
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use fasteval::Evaler;
|
use fasteval::Evaler;
|
||||||
use nom::{
|
use nom::{
|
||||||
Finish, IResult, Input, Parser as NomParser,
|
AsChar, Compare, Finish, IResult, Input, Parser as NomParser,
|
||||||
branch::alt,
|
branch::alt,
|
||||||
bytes::complete::{tag, take, take_till},
|
bytes::complete::{tag, take, take_till},
|
||||||
character::{
|
character::{
|
||||||
|
@ -43,13 +43,14 @@ impl<'i, 's> Parser<'i, '_, 's, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token_parser<'a, 's>(
|
fn token_parser<'a, 's, I>(
|
||||||
parser: &Parser<'_, '_, 's, '_>,
|
parser: &Parser<'_, '_, 's, '_>,
|
||||||
) -> impl NomParser<
|
) -> impl NomParser<I, Output = Vec<Box<dyn Token + 's>>, Error = nom::error::Error<I>>
|
||||||
LocatedSpan<&'a str>,
|
where
|
||||||
Output = Vec<Box<dyn Token + 's>>,
|
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
|
||||||
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
<I as Input>::Item: AsChar,
|
||||||
> {
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
{
|
||||||
let space_or_comment = || {
|
let space_or_comment = || {
|
||||||
value(
|
value(
|
||||||
(),
|
(),
|
||||||
|
@ -75,58 +76,55 @@ fn into_box<'a>(token: impl Token + 'a) -> Box<dyn Token + 'a> {
|
||||||
Box::new(token)
|
Box::new(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TokenResult<'a, T> = IResult<LocatedSpan<&'a str>, T>;
|
|
||||||
|
|
||||||
impl Silence {
|
impl Silence {
|
||||||
fn parser<'a>() -> impl NomParser<
|
fn parser<I>() -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
|
||||||
LocatedSpan<&'a str>,
|
where
|
||||||
Output = Self,
|
I: Input,
|
||||||
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
<I as Input>::Item: AsChar,
|
||||||
> {
|
{
|
||||||
value(Self, char('.'))
|
value(Self, char('.'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Marker {
|
impl Marker {
|
||||||
fn parser<'a>() -> impl NomParser<
|
fn parser<I>() -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
|
||||||
LocatedSpan<&'a str>,
|
where
|
||||||
Output = Self,
|
I: Input,
|
||||||
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
<I as Input>::Item: AsChar,
|
||||||
> {
|
{
|
||||||
value(Marker, char('%'))
|
value(Marker, char('%'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Note {
|
impl Note {
|
||||||
fn parser<'a, 'n>(
|
fn parser<'n, I>(
|
||||||
notes: &'n [String],
|
notes: &'n [String],
|
||||||
) -> impl NomParser<
|
) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
|
||||||
LocatedSpan<&'a str>,
|
where
|
||||||
Output = Self,
|
I: Input + for<'z> Compare<&'z str>,
|
||||||
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
{
|
||||||
> {
|
|input: I| {
|
||||||
|input: LocatedSpan<&'a str>| {
|
let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = notes
|
||||||
let mut parsers: Vec<Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>> =
|
.iter()
|
||||||
notes
|
.enumerate()
|
||||||
.iter()
|
.map(|(i, t)| {
|
||||||
.enumerate()
|
Box::new(move |input: I| {
|
||||||
.map(|(i, t)| {
|
value(Note(i as u8), tag(t.clone().as_str())).parse(input)
|
||||||
Box::new(move |input: LocatedSpan<&'a str>| {
|
}) as Box<dyn Fn(I) -> IResult<I, Self>>
|
||||||
value(Note(i as u8), tag(t.clone().as_str())).parse(input)
|
})
|
||||||
})
|
.collect();
|
||||||
as Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
alt(parsers.as_mut_slice()).parse(input)
|
alt(parsers.as_mut_slice()).parse(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariableChange {
|
impl VariableChange {
|
||||||
fn parser(
|
fn parser<I>(variables: &[char]) -> impl Fn(I) -> IResult<I, Self>
|
||||||
variables: &[char],
|
where
|
||||||
) -> impl for<'a> Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self> {
|
I: Input + AsRef<str>,
|
||||||
move |i: LocatedSpan<&str>| {
|
<I as Input>::Item: AsChar,
|
||||||
|
{
|
||||||
|
move |i: I| {
|
||||||
preceded(char('$'), one_of(variables))
|
preceded(char('$'), one_of(variables))
|
||||||
.and(expression_parser(variables))
|
.and(expression_parser(variables))
|
||||||
.map(|(name, change)| VariableChange(name, change))
|
.map(|(name, change)| VariableChange(name, change))
|
||||||
|
@ -136,9 +134,12 @@ impl VariableChange {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Loop<'s> {
|
impl<'s> Loop<'s> {
|
||||||
fn parser<'i, 'n, 'v>(
|
fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
|
||||||
parser: &Parser<'i, 'n, 's, 'v>,
|
where
|
||||||
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Self> {
|
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
|
||||||
|
<I as Input>::Item: AsChar,
|
||||||
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
{
|
||||||
|input| {
|
|input| {
|
||||||
delimited(
|
delimited(
|
||||||
char('('),
|
char('('),
|
||||||
|
@ -156,9 +157,12 @@ impl<'s> Loop<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Tuplet<'s> {
|
impl<'s> Tuplet<'s> {
|
||||||
fn parser<'i, 'n, 'v>(
|
fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
|
||||||
parser: &Parser<'i, 'n, 's, 'v>,
|
where
|
||||||
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Self> {
|
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
|
||||||
|
<I as Input>::Item: AsChar,
|
||||||
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
{
|
||||||
|input| {
|
|input| {
|
||||||
delimited(char('['), token_parser(parser), char(']'))
|
delimited(char('['), token_parser(parser), char(']'))
|
||||||
.map(Self)
|
.map(Self)
|
||||||
|
@ -168,34 +172,24 @@ impl<'s> Tuplet<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Slope<'s> {
|
impl<'s> Slope<'s> {
|
||||||
fn parser<'a, 'i, 'n, 'v>(
|
fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
|
||||||
parser: &Parser<'i, 'n, 's, 'v>,
|
where
|
||||||
) -> impl Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self> {
|
I: Input + for<'z> Compare<&'z str> + AsRef<str>,
|
||||||
|
<I as Input>::Item: AsChar,
|
||||||
|
<I as Input>::Item: PartialEq<char>,
|
||||||
|
{
|
||||||
|input| {
|
|input| {
|
||||||
let iter: std::collections::hash_map::Iter<'s, String, VariableChange> =
|
let iter: std::collections::hash_map::Iter<'s, String, VariableChange> =
|
||||||
parser.slopes.iter();
|
parser.slopes.iter();
|
||||||
delimited(
|
delimited(
|
||||||
char('{'),
|
char('{'),
|
||||||
alt(
|
alt(iter
|
||||||
iter
|
.map(|(k, v)| {
|
||||||
.map(|(k, v)| {
|
Box::new(move |input: I| value(v, tag(k.as_str())).parse(input))
|
||||||
Box::new(move|input: LocatedSpan<&'a str>| {
|
as Box<dyn 's + Fn(I) -> IResult<I, &'s VariableChange>>
|
||||||
value(v, tag(k.as_str())).parse(input)
|
})
|
||||||
})
|
.collect::<Vec<Box<dyn 's + Fn(I) -> IResult<I, &'s VariableChange>>>>()
|
||||||
as Box<
|
.as_mut_slice())
|
||||||
dyn 's + Fn(
|
|
||||||
LocatedSpan<&'a str>,
|
|
||||||
)
|
|
||||||
-> TokenResult<'a, &'s VariableChange>,
|
|
||||||
>
|
|
||||||
})
|
|
||||||
.collect::<Vec<
|
|
||||||
Box<
|
|
||||||
dyn 's + Fn(LocatedSpan<&'a str>) -> TokenResult<'a, &'s VariableChange>,
|
|
||||||
>,
|
|
||||||
>>()
|
|
||||||
.as_mut_slice(),
|
|
||||||
)
|
|
||||||
.and(token_parser(parser)),
|
.and(token_parser(parser)),
|
||||||
char('}'),
|
char('}'),
|
||||||
)
|
)
|
||||||
|
@ -206,14 +200,14 @@ impl<'s> Slope<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Will return the longest valid fasteval expression
|
/// Will return the longest valid fasteval expression
|
||||||
fn expression_parser<C: Into<char> + Ord + Display>(
|
fn expression_parser<'a, I: Input + AsRef<str>, C: Into<char> + Ord + Display>(
|
||||||
variables: &[C],
|
variables: &[C],
|
||||||
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Expression> {
|
) -> impl Fn(I) -> IResult<I, Expression> {
|
||||||
|input: LocatedSpan<&str>| {
|
|input: I| {
|
||||||
let mut end_index = 0;
|
let mut end_index = 0;
|
||||||
let mut current_expression = None;
|
let mut current_expression = None;
|
||||||
while input.input_len() > end_index {
|
while input.input_len() > end_index {
|
||||||
if let Some(e) = input[..end_index + 1]
|
if let Some(e) = input.as_ref()[..end_index + 1]
|
||||||
.parse::<Expression>()
|
.parse::<Expression>()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|e| {
|
.and_then(|e| {
|
||||||
|
@ -241,9 +235,7 @@ fn expression_parser<C: Into<char> + Ord + Display>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(e) = current_expression {
|
if let Some(e) = current_expression {
|
||||||
Ok::<(LocatedSpan<&str>, Expression), nom::Err<nom::error::Error<LocatedSpan<&str>>>>((
|
Ok((input, e))
|
||||||
input, e,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Err(nom::Err::Incomplete(nom::Needed::Unknown))
|
Err(nom::Err::Incomplete(nom::Needed::Unknown))
|
||||||
}
|
}
|
||||||
|
@ -252,13 +244,14 @@ fn expression_parser<C: Into<char> + Ord + Display>(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use nom::Parser;
|
||||||
use nom_locate::LocatedSpan;
|
use nom_locate::LocatedSpan;
|
||||||
|
|
||||||
use crate::parser::expression_parser;
|
use crate::{compiler::Silence, parser::expression_parser};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expression_parser_test() {
|
fn expression_parser_test() {
|
||||||
let parser = expression_parser::<char>(&['x']);
|
let parser = expression_parser(&['x']);
|
||||||
let mut working_test_cases = vec![
|
let mut working_test_cases = vec![
|
||||||
"1",
|
"1",
|
||||||
"1x",
|
"1x",
|
||||||
|
@ -276,13 +269,32 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
for test in working_test_cases.drain(..) {
|
for test in working_test_cases.drain(..) {
|
||||||
let output = parser(LocatedSpan::new(test));
|
let output = parser(test);
|
||||||
assert!(output.is_ok(), "result was not Ok: {output:?}");
|
assert!(output.is_ok(), "result was not Ok: {output:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
for test in not_working_test_cases.drain(..) {
|
for test in not_working_test_cases.drain(..) {
|
||||||
let output = parser(LocatedSpan::new(test));
|
let output = parser(test);
|
||||||
assert!(output.is_err(), "result was not Err: {output:?}");
|
assert!(output.is_err(), "result was not Err: {output:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn silence() {
|
||||||
|
// let parser = |input: &'static str| Silence::parser().parse(input.into());
|
||||||
|
// let mut working_cases = vec![(".", ()), ".dd", ".."];
|
||||||
|
// let mut not_working_cases = vec![];
|
||||||
|
// 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:?}");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue