use std::{convert::Infallible, str::FromStr};
use thiserror::Error;
use nevermore::FromNever;
use crate::{ParserString, Parser};
pub fn next(s: &mut ParserString) -> Result<char, ()> {
s.try_take(1).ok_or(())?.chars().next().ok_or(())
}
pub fn word(s: &mut ParserString) -> Result<String, WordErr> {
let mut out = String::new();
while let Ok(c) = next(s) {
if !c.is_whitespace() {
out.push(c);
} else {
unsafe { s.give(1) }
break;
}
}
if out.len() == 0 { return Err(WordErr) }
Ok(out)
}
#[derive(Debug, Clone, Copy, Error, FromNever)]
#[error("found no characters")]
pub struct WordErr;
pub fn whitespace(s: &mut ParserString) -> Result<usize, Infallible> {
let mut ctr = 0;
while let Ok(c) = next.parse(s) {
if c != ' ' {
break
}
ctr += 1
}
if !s.get().is_empty() {
unsafe { s.give(1) }
}
Ok(ctr)
}
pub fn take(delim: &'static str) -> impl Parser<&'static str, Err = TakeErr> {
move |s: &mut ParserString| {
let head = s.try_take(delim.len())
.ok_or(TakeErr::NoSpace)?;
if head == delim {
Ok(delim)
} else {
Err(TakeErr::NoMatch)
}
}
}
#[derive(Debug, Clone, Copy, Error, FromNever)]
pub enum TakeErr {
#[error("ran out of space")]
NoSpace,
#[error("did not match delim")]
NoMatch,
}
#[derive(Debug, Clone, Copy, Error, FromNever)]
pub enum IntErr<E: std::error::Error> {
#[error("{0}")]
Word(#[from] WordErr),
#[error("error parsing int: {0}")]
Parse(E)
}
pub fn int<I, E>(s: &mut ParserString) -> Result<I, IntErr<E>>
where I: num_traits::PrimInt + FromStr<Err = E> + 'static, E: std::error::Error + 'static
{
word
.convert_err::<IntErr<E>>()
.and_then(|s| {
s.parse::<I>()
.map_err(|e| IntErr::Parse(e))
})
.parse(s)
}
#[derive(Debug, Clone, Copy, Error, FromNever)]
pub enum FloatErr<E: std::error::Error> {
#[error("{0}")]
Word(#[from] WordErr),
#[error("error parsing int: {0}")]
Parse(E)
}
pub fn float<I, E>(s: &mut ParserString) -> Result<I, FloatErr<E>>
where I: num_traits::Float + FromStr<Err = E> + 'static, E: std::error::Error + 'static
{
word
.convert_err::<FloatErr<E>>()
.and_then(|s| {
s.parse::<I>()
.map_err(|e| FloatErr::Parse(e))
})
.parse(s)
}
#[derive(Debug, Clone, Copy, Error, FromNever)]
pub enum BetweenErr {
#[error("opener was not found")]
NoOpen,
#[error("string ended before closer was found")]
Unmatched,
}
pub fn between(open: &'static str, close: &'static str) -> impl Parser<String, Err = BetweenErr> {
move |s: &mut ParserString| {
let _ = take(open).map_err(|_| BetweenErr::NoOpen).parse(s)?;
let mut out = String::with_capacity(s.len());
while take(close).try_parse(s).is_err() {
out.push(next(s).map_err(|_| BetweenErr::Unmatched)?);
}
Ok(out)
}
}