diff --git a/src/compiler.rs b/src/compiler.rs index 6b696f9..d27c864 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -35,7 +35,18 @@ pub struct VariableChange(pub char, pub Expression); impl Token for VariableChange {} #[cfg_attr(debug_assertions, derive(Default))] -pub struct Loop(pub usize, pub TokenVec); +pub struct Loop(pub LoopCount, pub TokenVec); + +pub enum LoopCount { + Litteral(usize), + Variable(char), +} + +impl Default for LoopCount { + fn default() -> Self { + LoopCount::Litteral(1) + } +} impl Token for Loop {} diff --git a/src/parser.rs b/src/parser.rs index 0700e20..ad2e53b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -6,66 +6,67 @@ use std::{ use derive_builder::Builder; use fasteval::Evaler; use nom::{ - AsBytes, AsChar, Compare, FindToken, Finish, IResult, Input, Offset, Parser as NomParser, + Finish, IResult, Input, Parser as NomParser, branch::alt, bytes::complete::{tag, take, take_till}, character::{ - complete::{char, space1}, + complete::{char, space1, usize}, streaming::one_of, }, - combinator::value, + combinator::{opt, value}, error::{Error, ErrorKind}, multi::many0, sequence::{delimited, preceded}, }; use nom_locate::LocatedSpan; -use crate::compiler::{Expression, Marker, Note, Silence, Token, VariableChange}; - -pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare, C: Into> { - pub notes: &'n [I], - pub slopes: &'s HashMap, - pub variables: &'v [C], -} +use crate::compiler::{Expression, Loop, LoopCount, Marker, Note, Silence, Token, VariableChange}; #[derive(Builder)] -pub struct Parser<'i, 'n, 's, 'v, C: Into> { +pub struct Parser<'i, 'n, 's, 'v> { input: &'i str, notes: &'n [String], slopes: &'s HashMap, - variables: &'v [C], + variables: &'v [char], } -impl<'i, 'n, 's, 'v, C> Parser<'i, 'n, 's, 'v, C> -where - C: Into + Clone + Ord + Display, - &'v [C]: FindToken, -{ +impl<'i, 'n, 's, 'v> Parser<'i, 'n, 's, 'v> { pub fn parse_all( &self, ) -> Result>, Error>> { - let space_or_comment = || { - value( - (), - many0(value((), char('#').and(take_till(|c| c == '\n'))).or(value((), space1))), - ) - }; - many0(delimited( - space_or_comment(), - alt(( - Silence::parser().map(into_box), - Marker::parser().map(into_box), - Note::parser(self.notes).map(into_box), - VariableChange::parser::, C>(self.variables).map(into_box), - )), - space_or_comment(), - )) - .parse_complete(LocatedSpan::new(self.input)) - .finish() - .map(|(_, o)| o) + token_parser(self) + .parse_complete(LocatedSpan::new(self.input)) + .finish() + .map(|(_, o)| o) } } +fn token_parser<'a, 'i, 'n, 's, 'v>( + parser: &Parser, +) -> impl NomParser< + LocatedSpan<&'a str>, + Output = Vec>, + Error = nom::error::Error>, +> { + let space_or_comment = || { + value( + (), + many0(value((), char('#').and(take_till(|c| c == '\n'))).or(value((), space1))), + ) + }; + many0(delimited( + space_or_comment(), + alt(( + Silence::parser().map(into_box), + Marker::parser().map(into_box), + Note::parser(parser.notes).map(into_box), + VariableChange::parser(parser.variables).map(into_box), + Loop::parser(parser).map(into_box), + )), + space_or_comment(), + )) +} + fn into_box(token: impl Token + 'static) -> Box { Box::new(token) } @@ -107,7 +108,7 @@ impl Note { .enumerate() .map(|(i, t)| { Box::new(move |input: LocatedSpan<&'a str>| { - value(Note(i.clone() as u8), tag(t.clone().as_str())).parse(input) + value(Note(i as u8), tag(t.clone().as_str())).parse(input) }) as Box) -> TokenResult<'a, Self>> }) @@ -118,13 +119,9 @@ impl Note { } impl VariableChange { - fn parser<'c, I: Input + AsBytes + Offset, C: Into + Clone + Ord + Display>( - variables: &'c [C], - ) -> impl for<'a> Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self> - where - ::Item: AsChar, - &'c [C]: FindToken, - { + fn parser( + variables: &[char], + ) -> impl for<'a> Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self> { move |i: LocatedSpan<&str>| { preceded(char('$'), one_of(variables)) .and(expression_parser(variables)) @@ -134,15 +131,35 @@ impl VariableChange { } } +impl Loop { + fn parser<'a, 'i, 'n, 's, 'v>( + parser: &Parser<'i, 'n, 's, 'v>, + ) -> impl Fn(LocatedSpan<&str>) -> TokenResult { + |input| { + delimited( + char('('), + opt(alt(( + usize.map(LoopCount::Litteral), + one_of(parser.variables).map(LoopCount::Variable), + ))) + .and(token_parser(parser)), + char(')'), + ) + .map(|(c, v)| Self(c.unwrap_or_default(), v)) + .parse(input) + } + } +} + /// Will return the longest valid fasteval expression -fn expression_parser<'c, C: Into + Ord + Display>( - variables: &'c [C], +fn expression_parser + Ord + Display>( + variables: &[C], ) -> impl Fn(LocatedSpan<&str>) -> TokenResult { move |input: LocatedSpan<&str>| { let mut end_index = 0; let mut current_expression = None; while input.input_len() > end_index { - if let Some(e) = (&input[..end_index + 1]) + if let Some(e) = input[..end_index + 1] .parse::() .ok() .and_then(|e| { @@ -150,7 +167,7 @@ fn expression_parser<'c, C: Into + Ord + Display>( .eval( &e.slab, &mut BTreeMap::from_iter( - variables.into_iter().map(|v| (v.to_string(), 0.0)), + variables.iter().map(|v| (v.to_string(), 0.0)), ), ) .ok() @@ -160,15 +177,13 @@ fn expression_parser<'c, C: Into + Ord + Display>( { current_expression = Some(e); end_index += 1; + } else if let Some(e) = current_expression { + return take(end_index).parse(input).map(move |(r, _)| (r, e)); } else { - if let Some(e) = current_expression { - return take(end_index).parse(input).map(move |(r, _)| (r, e)); - } else { - return Err(nom::Err::Failure(nom::error::Error::new( - input, - ErrorKind::Satisfy, - ))); - } + return Err(nom::Err::Failure(nom::error::Error::new( + input, + ErrorKind::Satisfy, + ))); } } if let Some(e) = current_expression { @@ -208,12 +223,12 @@ mod tests { for test in working_test_cases.drain(..) { let output = parser(LocatedSpan::new(test)); - assert!(matches!(output, Ok(_)), "result was not Ok: {output:?}"); + assert!(output.is_ok(), "result was not Ok: {output:?}"); } for test in not_working_test_cases.drain(..) { let output = parser(LocatedSpan::new(test)); - assert!(matches!(output, Err(_)), "result was not Err: {output:?}"); + assert!(output.is_err(), "result was not Err: {output:?}"); } } }