use nom::{
bytes::complete::take_while_m_n,
combinator::{not, peek},
number::complete::{double, float},
sequence::terminated,
};
use num_traits::{Num, Pow};
use core::{f32, f64, fmt, marker::PhantomData, ops};
use crate::{Features, Grammar, NomResult, Span};
#[derive(Debug)]
pub struct NumGrammar<T>(PhantomData<T>);
pub type F32Grammar = NumGrammar<f32>;
pub type F64Grammar = NumGrammar<f64>;
impl<T: NumLiteral> Grammar for NumGrammar<T> {
type Lit = T;
type Type = ();
const FEATURES: Features = Features {
type_annotations: false,
..Features::all()
};
fn parse_literal(input: Span<'_>) -> NomResult<'_, Self::Lit> {
T::parse(input)
}
fn parse_type(_input: Span<'_>) -> NomResult<'_, Self::Type> {
unimplemented!()
}
}
pub trait NumLiteral:
'static + Copy + Num + fmt::Debug + ops::Neg<Output = Self> + Pow<Self, Output = Self>
{
fn parse(input: Span<'_>) -> NomResult<'_, Self>;
}
pub fn ensure_no_overlap<'a, T>(
parser: impl Fn(Span<'a>) -> NomResult<'a, T>,
) -> impl Fn(Span<'a>) -> NomResult<'a, T> {
terminated(
parser,
peek(not(take_while_m_n(1, 1, |c: char| {
c.is_ascii_alphabetic() || c == '_'
}))),
)
}
impl NumLiteral for f32 {
fn parse(input: Span<'_>) -> NomResult<'_, Self> {
ensure_no_overlap(float)(input)
}
}
impl NumLiteral for f64 {
fn parse(input: Span<'_>) -> NomResult<'_, Self> {
ensure_no_overlap(double)(input)
}
}
#[cfg(feature = "num-complex")]
mod complex {
use super::*;
use nom::{
character::complete::one_of,
combinator::{map, opt},
sequence::tuple,
};
use num_complex::Complex;
fn complex_parser<'a, T: Num>(
num_parser: impl Fn(Span<'a>) -> NomResult<'a, T>,
) -> impl Fn(Span<'a>) -> NomResult<'a, Complex<T>> {
let parser = tuple((num_parser, opt(one_of("ij"))));
map(parser, |(value, maybe_imag)| {
if maybe_imag.is_some() {
Complex::new(T::zero(), value)
} else {
Complex::new(value, T::zero())
}
})
}
impl NumLiteral for num_complex::Complex32 {
fn parse(input: Span<'_>) -> NomResult<'_, Self> {
ensure_no_overlap(complex_parser(float))(input)
}
}
impl NumLiteral for num_complex::Complex64 {
fn parse(input: Span<'_>) -> NomResult<'_, Self> {
ensure_no_overlap(complex_parser(double))(input)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Expr, GrammarExt};
use assert_matches::assert_matches;
use core::f32::INFINITY;
#[test]
fn parsing_infinity() {
let parsed = F32Grammar::parse_statements(Span::new("Inf")).unwrap();
let ret = parsed.return_value.unwrap().extra;
assert_matches!(ret, Expr::Literal(lit) if lit == INFINITY);
let parsed = F32Grammar::parse_statements(Span::new("-Inf")).unwrap();
let ret = parsed.return_value.unwrap().extra;
assert_matches!(ret, Expr::Literal(lit) if lit == -INFINITY);
let parsed = F32Grammar::parse_statements(Span::new("Infty")).unwrap();
let ret = parsed.return_value.unwrap().extra;
assert_matches!(ret, Expr::Variable);
let parsed = F32Grammar::parse_statements(Span::new("Infer(1)")).unwrap();
let ret = parsed.return_value.unwrap().extra;
assert_matches!(ret, Expr::Function { .. });
let parsed = F32Grammar::parse_statements(Span::new("-Infty")).unwrap();
let ret = parsed.return_value.unwrap().extra;
assert_matches!(ret, Expr::Unary { .. });
let parsed = F32Grammar::parse_statements(Span::new("-Infer(2, 3)")).unwrap();
let ret = parsed.return_value.unwrap().extra;
assert_matches!(ret, Expr::Unary { .. });
}
}