first four parsers
This commit is contained in:
parent
0240602c19
commit
4fa49e2181
4 changed files with 263 additions and 75 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
@ -7,32 +9,32 @@ use std::{
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, anyhow};
|
use anyhow::anyhow;
|
||||||
use bliplib::parser::TokenParser;
|
use bliplib::compiler::Expression;
|
||||||
use clap::{Parser, builder::EnumValueParser};
|
use clap::Parser;
|
||||||
use derive_new::new;
|
|
||||||
use derive_wrapper::AsRef;
|
use derive_wrapper::AsRef;
|
||||||
use fasteval::{Compiler, Instruction, Slab};
|
|
||||||
use hound::SampleFormat;
|
use hound::SampleFormat;
|
||||||
use mp3lame_encoder::{Bitrate, Quality};
|
use mp3lame_encoder::{Bitrate, Quality};
|
||||||
use nom::{
|
use nom::{
|
||||||
AsBytes, Compare, Finish, Input, Offset, Parser as _,
|
Finish, Parser as P,
|
||||||
branch::alt,
|
branch::alt,
|
||||||
bytes::complete::tag,
|
bytes::complete::tag,
|
||||||
character::complete::{char, u16, usize},
|
character::complete::{char, u16, usize},
|
||||||
combinator::{opt, rest, success, value},
|
combinator::{rest, success, value},
|
||||||
error::{ErrorKind, ParseError},
|
error::ErrorKind,
|
||||||
sequence::preceded,
|
sequence::preceded,
|
||||||
};
|
};
|
||||||
use nom_locate::{LocatedSpan, position};
|
use nom_locate::{LocatedSpan, position};
|
||||||
use strum::{Display, EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr, ToString};
|
use strum::{EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const DEFAULT_INSTRUMENT: &str = "sin(2*PI*(442+442*((n+1)/N))*t)";
|
const DEFAULT_INSTRUMENT: &str = "sin(2*PI*(442+442*((n+1)/N))*t)";
|
||||||
const DEFAULT_LENGTH: &str = "2^(2-log_2(l))*(60/T)";
|
const DEFAULT_LENGTH: &str = "2^(2-log_2(l))*(60/T)";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dbg!(Cli::parse());
|
let cli = Cli::parse();
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
dbg!(cli);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -188,31 +190,6 @@ where
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(new)]
|
|
||||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
|
||||||
struct Expression {
|
|
||||||
instruction: Instruction,
|
|
||||||
slab: Slab,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Expression {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Expression {
|
|
||||||
type Err = fasteval::Error;
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let mut slab = Slab::new();
|
|
||||||
let instruction = fasteval::Parser::new()
|
|
||||||
.parse(s, &mut slab.ps)?
|
|
||||||
.from(&slab.ps)
|
|
||||||
.compile(&slab.ps, &mut slab.cs);
|
|
||||||
Ok(Expression::new(instruction, slab))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Clone)]
|
#[derive(Parser, Clone)]
|
||||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
#[cfg_attr(debug_assertions, derive(Debug))]
|
||||||
#[group(required = false, multiple = false)]
|
#[group(required = false, multiple = false)]
|
||||||
|
@ -293,8 +270,12 @@ impl Default for AudioFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error> {
|
fn audio_format_parser(input: &str) -> Result<AudioFormat, anyhow::Error> {
|
||||||
fn mp3<'a>() -> impl TokenParser<&'a str, AudioFormat> {
|
fn mp3<'a>() -> impl P<
|
||||||
|
LocatedSpan<&'a str>,
|
||||||
|
Output = AudioFormat,
|
||||||
|
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||||
|
> {
|
||||||
preceded(
|
preceded(
|
||||||
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||||
AudioFormatDiscriminants::Mp3.into(),
|
AudioFormatDiscriminants::Mp3.into(),
|
||||||
|
@ -342,7 +323,11 @@ fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error>
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn wav<'a>() -> impl TokenParser<&'a str, AudioFormat> {
|
fn wav<'a>() -> impl P<
|
||||||
|
LocatedSpan<&'a str>,
|
||||||
|
Output = AudioFormat,
|
||||||
|
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||||
|
> {
|
||||||
preceded(
|
preceded(
|
||||||
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||||
AudioFormatDiscriminants::Wav.into(),
|
AudioFormatDiscriminants::Wav.into(),
|
||||||
|
@ -352,7 +337,11 @@ fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error>
|
||||||
.map(|(bps, sample_format)| AudioFormat::Wav { bps, sample_format }),
|
.map(|(bps, sample_format)| AudioFormat::Wav { bps, sample_format }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn flac<'a>() -> impl TokenParser<&'a str, AudioFormat> {
|
fn flac<'a>() -> impl P<
|
||||||
|
LocatedSpan<&'a str>,
|
||||||
|
Output = AudioFormat,
|
||||||
|
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||||
|
> {
|
||||||
preceded(
|
preceded(
|
||||||
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||||
AudioFormatDiscriminants::Flac.into(),
|
AudioFormatDiscriminants::Flac.into(),
|
||||||
|
@ -362,11 +351,15 @@ fn audio_format_parser<'a>(input: &'a str) -> Result<AudioFormat, anyhow::Error>
|
||||||
.map(|bps| AudioFormat::Flac { bps }),
|
.map(|bps| AudioFormat::Flac { bps }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn parser<'a>() -> impl TokenParser<&'a str, AudioFormat> {
|
fn parser<'a>() -> impl P<
|
||||||
|
LocatedSpan<&'a str>,
|
||||||
|
Output = AudioFormat,
|
||||||
|
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||||
|
> {
|
||||||
alt((
|
alt((
|
||||||
mp3(),
|
mp3::<'a>(),
|
||||||
wav(),
|
wav::<'a>(),
|
||||||
flac(),
|
flac::<'a>(),
|
||||||
rest.map_res(|r: LocatedSpan<&'a str>| {
|
rest.map_res(|r: LocatedSpan<&'a str>| {
|
||||||
Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(AudioFormat::Raw(
|
Ok::<AudioFormat, nom::error::Error<LocatedSpan<&'a str>>>(AudioFormat::Raw(
|
||||||
RawAudioFormat::try_from(*r)
|
RawAudioFormat::try_from(*r)
|
||||||
|
|
|
@ -1,42 +1,84 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
use fasteval::Instruction;
|
use derive_new::new;
|
||||||
|
use fasteval::{Compiler, Instruction, Slab};
|
||||||
|
|
||||||
pub type TokenVec = Vec<Box<dyn Token>>;
|
pub type TokenVec = Vec<Box<dyn Token>>;
|
||||||
|
|
||||||
pub trait Token {
|
pub trait Token {
|
||||||
fn apply(&self, context: Context) -> Context;
|
fn apply(&self, context: Context) -> Context {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(debug_assertions, derive(Default))]
|
#[cfg_attr(debug_assertions, derive(Default))]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Silence;
|
pub struct Silence;
|
||||||
|
|
||||||
|
impl Token for Silence {}
|
||||||
|
|
||||||
#[cfg_attr(debug_assertions, derive(Default))]
|
#[cfg_attr(debug_assertions, derive(Default))]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Marker;
|
pub struct Marker;
|
||||||
|
|
||||||
|
impl Token for Marker {}
|
||||||
|
|
||||||
#[cfg_attr(debug_assertions, derive(Default))]
|
#[cfg_attr(debug_assertions, derive(Default))]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Note(pub u8);
|
pub struct Note(pub u8);
|
||||||
|
|
||||||
|
impl Token for Note {}
|
||||||
|
|
||||||
#[cfg_attr(debug_assertions, derive(Default))]
|
#[cfg_attr(debug_assertions, derive(Default))]
|
||||||
pub struct VariableChange(pub char, pub Instruction);
|
pub struct VariableChange(pub char, pub Expression);
|
||||||
|
|
||||||
|
impl Token for VariableChange {}
|
||||||
|
|
||||||
#[cfg_attr(debug_assertions, derive(Default))]
|
#[cfg_attr(debug_assertions, derive(Default))]
|
||||||
pub struct Loop(pub usize, pub TokenVec);
|
pub struct Loop(pub usize, pub TokenVec);
|
||||||
|
|
||||||
|
impl Token for Loop {}
|
||||||
|
|
||||||
#[cfg_attr(debug_assertions, derive(Default))]
|
#[cfg_attr(debug_assertions, derive(Default))]
|
||||||
pub struct Tuplet(pub TokenVec);
|
pub struct Tuplet(pub TokenVec);
|
||||||
|
|
||||||
|
impl Token for Tuplet {}
|
||||||
|
|
||||||
#[cfg_attr(debug_assertions, derive(Default))]
|
#[cfg_attr(debug_assertions, derive(Default))]
|
||||||
pub struct Slope(pub VariableChange, pub TokenVec);
|
pub struct Slope(pub VariableChange, pub TokenVec);
|
||||||
|
|
||||||
|
impl Token for Slope {}
|
||||||
|
|
||||||
|
#[derive(new)]
|
||||||
|
#[cfg_attr(debug_assertions, derive(Debug, Default))]
|
||||||
|
pub struct Expression {
|
||||||
|
pub(crate) instruction: Instruction,
|
||||||
|
pub(crate) slab: Slab,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Expression {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Expression {
|
||||||
|
type Err = fasteval::Error;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut slab = Slab::new();
|
||||||
|
let instruction = fasteval::Parser::new()
|
||||||
|
.parse(s, &mut slab.ps)?
|
||||||
|
.from(&slab.ps)
|
||||||
|
.compile(&slab.ps, &mut slab.cs);
|
||||||
|
Ok(Expression::new(instruction, slab))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub result: Vec<f64>,
|
pub result: Vec<f64>,
|
||||||
pub variables: HashMap<char, f64>,
|
pub variables: HashMap<char, f64>,
|
||||||
pub instrument: Instruction,
|
pub instrument: Expression,
|
||||||
pub slopes: HashMap<char, Instruction>,
|
pub slopes: HashMap<char, Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
203
src/parser.rs
203
src/parser.rs
|
@ -1,20 +1,23 @@
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
fmt::Display,
|
||||||
|
};
|
||||||
|
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use nom::{Compare, Input, combinator::success, error::Error};
|
use fasteval::Evaler;
|
||||||
|
use nom::{
|
||||||
|
AsBytes, AsChar, Compare, FindToken, Finish, IResult, Input, Offset, Parser as NomParser,
|
||||||
|
branch::alt,
|
||||||
|
bytes::complete::{tag, take},
|
||||||
|
character::{complete::char, streaming::one_of},
|
||||||
|
combinator::value,
|
||||||
|
error::{Error, ErrorKind},
|
||||||
|
multi::many0,
|
||||||
|
sequence::preceded,
|
||||||
|
};
|
||||||
use nom_locate::LocatedSpan;
|
use nom_locate::LocatedSpan;
|
||||||
|
|
||||||
use crate::compiler::{Marker, Note, Silence, Token, VariableChange};
|
use crate::compiler::{Expression, Marker, Note, Silence, Token, VariableChange};
|
||||||
|
|
||||||
pub trait TokenParser<I, O>:
|
|
||||||
nom::Parser<LocatedSpan<I>, Output = O, Error = Error<LocatedSpan<I>>>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I, T, O> TokenParser<I, O> for T where
|
|
||||||
T: nom::Parser<LocatedSpan<I>, Output = O, Error = Error<LocatedSpan<I>>>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> {
|
pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> {
|
||||||
pub notes: &'n [I],
|
pub notes: &'n [I],
|
||||||
|
@ -23,33 +26,181 @@ pub struct ParserParametters<'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Builder)]
|
#[derive(Builder)]
|
||||||
pub struct Parser<'i, 'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> {
|
pub struct Parser<'i, 'n, 's, 'v, C: Into<char>> {
|
||||||
input: &'i I,
|
input: &'i str,
|
||||||
notes: &'n [I],
|
notes: &'n [String],
|
||||||
slopes: &'s HashMap<I, VariableChange>,
|
slopes: &'s HashMap<String, VariableChange>,
|
||||||
variables: &'v [C],
|
variables: &'v [C],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i, 'n, 's, 'v, I: Input + Clone + Compare<I>, C: Into<char>> Parser<'i, 'n, 's, 'v, I, C> {
|
impl<'i, 'n, 's, 'v, C> Parser<'i, 'n, 's, 'v, C>
|
||||||
pub fn parse_all() -> Result<Vec<Box<dyn Token>>, Error<nom_locate::LocatedSpan<&'i I>>> {
|
where
|
||||||
todo!()
|
C: Into<char> + Clone + Ord + Display,
|
||||||
|
&'v [C]: FindToken<char>,
|
||||||
|
{
|
||||||
|
pub fn parse_all(
|
||||||
|
&self,
|
||||||
|
) -> Result<Vec<Box<dyn Token>>, Error<nom_locate::LocatedSpan<&'i str>>> {
|
||||||
|
many0(alt((
|
||||||
|
Silence::parser().map(into_box),
|
||||||
|
Marker::parser().map(into_box),
|
||||||
|
Note::parser(self.notes).map(into_box),
|
||||||
|
VariableChange::parser::<LocatedSpan<&'i str>, C>(self.variables).map(into_box),
|
||||||
|
)))
|
||||||
|
.parse_complete(LocatedSpan::new(self.input))
|
||||||
|
.finish()
|
||||||
|
.map(|(_, o)| o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_box(token: impl Token + 'static) -> Box<dyn Token> {
|
||||||
|
Box::new(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TokenResult<'a, T> = IResult<LocatedSpan<&'a str>, T>;
|
||||||
|
|
||||||
impl Silence {
|
impl Silence {
|
||||||
fn parser<I: Input>() -> impl TokenParser<I, Self> {
|
fn parser<'a>() -> impl NomParser<
|
||||||
success(Default::default())
|
LocatedSpan<&'a str>,
|
||||||
|
Output = Self,
|
||||||
|
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||||
|
> {
|
||||||
|
value(Self, char('.'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Marker {
|
impl Marker {
|
||||||
fn parser<I: Input>() -> impl TokenParser<I, Self> {
|
fn parser<'a>() -> impl NomParser<
|
||||||
success(Default::default())
|
LocatedSpan<&'a str>,
|
||||||
|
Output = Self,
|
||||||
|
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||||
|
> {
|
||||||
|
value(Marker, char('%'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Note {
|
impl Note {
|
||||||
fn parser<I: Input>() -> impl TokenParser<I, Self> {
|
fn parser<'a, 'n>(
|
||||||
success(Default::default())
|
notes: &'n [String],
|
||||||
|
) -> impl NomParser<
|
||||||
|
LocatedSpan<&'a str>,
|
||||||
|
Output = Self,
|
||||||
|
Error = nom::error::Error<LocatedSpan<&'a str>>,
|
||||||
|
> {
|
||||||
|
move |input: LocatedSpan<&'a str>| {
|
||||||
|
let mut parsers: Vec<Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>> =
|
||||||
|
notes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, t)| {
|
||||||
|
Box::new(move |input: LocatedSpan<&'a str>| {
|
||||||
|
value(Note(i.clone() as u8), tag(t.clone().as_str())).parse(input)
|
||||||
|
})
|
||||||
|
as Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
alt(parsers.as_mut_slice()).parse(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableChange {
|
||||||
|
fn parser<'c, I: Input + AsBytes + Offset, C: Into<char> + Clone + Ord + Display>(
|
||||||
|
variables: &'c [C],
|
||||||
|
) -> impl for<'a> Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>
|
||||||
|
where
|
||||||
|
<I as Input>::Item: AsChar,
|
||||||
|
&'c [C]: FindToken<char>,
|
||||||
|
{
|
||||||
|
move |i: LocatedSpan<&str>| {
|
||||||
|
preceded(char('$'), one_of(variables))
|
||||||
|
.and(expression_parser(variables))
|
||||||
|
.map(|(name, change)| VariableChange(name, change))
|
||||||
|
.parse(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will return the longest valid fasteval expression
|
||||||
|
fn expression_parser<'c, C: Into<char> + Ord + Display>(
|
||||||
|
variables: &'c [C],
|
||||||
|
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Expression> {
|
||||||
|
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])
|
||||||
|
.parse::<Expression>()
|
||||||
|
.ok()
|
||||||
|
.and_then(|e| {
|
||||||
|
e.instruction
|
||||||
|
.eval(
|
||||||
|
&e.slab,
|
||||||
|
&mut BTreeMap::from_iter(
|
||||||
|
variables.into_iter().map(|v| (v.to_string(), 0.0)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.is_some()
|
||||||
|
.then_some(e)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
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 {
|
||||||
|
return Err(nom::Err::Failure(nom::error::Error::new(
|
||||||
|
input,
|
||||||
|
ErrorKind::Satisfy,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(e) = current_expression {
|
||||||
|
Ok::<(LocatedSpan<&str>, Expression), nom::Err<nom::error::Error<LocatedSpan<&str>>>>((
|
||||||
|
input, e,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(nom::Err::Incomplete(nom::Needed::Unknown))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use nom_locate::LocatedSpan;
|
||||||
|
|
||||||
|
use crate::parser::expression_parser;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expression_parser_test() {
|
||||||
|
let parser = expression_parser::<char>(&['x']);
|
||||||
|
let mut working_test_cases = vec![
|
||||||
|
"1",
|
||||||
|
"1x",
|
||||||
|
"56coucou", // should stop after 56 because c is not a known variable
|
||||||
|
"8x + 1 heille salut ça va ou quoi 46 - 5x",
|
||||||
|
];
|
||||||
|
let mut not_working_test_cases = vec![
|
||||||
|
"",
|
||||||
|
"(",
|
||||||
|
"y",
|
||||||
|
"abcdexx489",
|
||||||
|
" ", // spaces are not expressions
|
||||||
|
" h", // because of previous, this fails
|
||||||
|
" 1", // this too but the parser should remain dumb and not expect spaces before / after
|
||||||
|
];
|
||||||
|
|
||||||
|
for test in working_test_cases.drain(..) {
|
||||||
|
let output = parser(LocatedSpan::new(test));
|
||||||
|
assert!(matches!(output, 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:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue