#[must_use]
pub enum Parsed<T, E> {
Res(Result<T, E>),
Fallthrough,
}
pub use Parsed::*;
pub type Result<T, E> = std::result::Result<T, E>;
impl<T, E> Parsed<T, E> {
pub fn err_into<E2: From<E>>(self) -> Parsed<T, E2> {
self.map_err(From::from)
}
#[inline]
pub fn or_give_up(self, err: impl FnOnce() -> E) -> Result<T, E> {
match self {
Res(result) => result,
Fallthrough => Err(err()),
}
}
#[inline]
pub fn optional(self) -> Result<Option<T>, E> {
match self {
Res(Ok(value)) => Ok(Some(value)),
Res(Err(err)) => Err(err),
Fallthrough => Ok(None),
}
}
#[inline]
pub fn matches(self) -> Result<bool, E> {
match self {
Res(Ok(_)) => Ok(true),
Res(Err(err)) => Err(err),
Fallthrough => Ok(false),
}
}
#[inline]
pub fn or_parse(self, parse: impl FnOnce() -> Parsed<T, E>) -> Parsed<T, E> {
match self {
Fallthrough => parse(),
v => v,
}
}
#[inline]
pub fn or_always_parse(self, parse: impl FnOnce() -> Result<T, E>) -> Result<T, E> {
match self {
Fallthrough => parse(),
Res(res) => res,
}
}
#[inline]
pub fn and_then<U>(self, parse: impl FnOnce(T) -> Result<U, E>) -> Parsed<U, E> {
match self {
Res(Ok(value)) => Res(parse(value)),
Res(Err(err)) => Res(Err(err)),
Fallthrough => Fallthrough,
}
}
#[inline]
pub fn and_also(mut self, parse: impl FnOnce(&mut T) -> Result<(), E>) -> Parsed<T, E> {
if let Res(Ok(value)) = &mut self {
let res = parse(value);
if let Err(err) = res {
return Res(Err(err));
}
}
self
}
#[inline]
pub fn and_do(mut self, action: impl FnOnce(&mut T)) -> Parsed<T, E> {
if let Res(Ok(value)) = &mut self {
action(value);
}
self
}
#[inline]
pub fn map<T2>(self, f: impl FnOnce(T) -> T2) -> Parsed<T2, E> {
match self {
Res(Ok(value)) => Res(Ok(f(value))),
Res(Err(err)) => Res(Err(err)),
Fallthrough => Fallthrough,
}
}
#[inline]
pub fn map_err<E2>(self, f: impl FnOnce(E) -> E2) -> Parsed<T, E2> {
match self {
Res(Ok(value)) => Res(Ok(value)),
Res(Err(err)) => Res(Err(f(err))),
Fallthrough => Fallthrough,
}
}
}
impl<T, E> From<Result<T, E>> for Parsed<T, E> {
#[inline]
fn from(res: Result<T, E>) -> Self {
Res(res)
}
}
pub trait ResultExt<T, E> {
fn err_into<E2: From<E>>(self) -> Result<T, E2>;
fn and_also(self, f: impl FnOnce(&mut T) -> Result<(), E>) -> Self;
fn and_do(self, f: impl FnOnce(&mut T)) -> Self;
}
impl<T, E> ResultExt<T, E> for Result<T, E> {
#[inline]
fn err_into<E2: From<E>>(self) -> Result<T, E2> {
self.map_err(From::from)
}
#[inline]
fn and_also(mut self, f: impl FnOnce(&mut T) -> Result<(), E>) -> Result<T, E> {
if let Ok(value) = &mut self {
f(value)?;
}
self
}
#[inline]
fn and_do(mut self, action: impl FnOnce(&mut T)) -> Result<T, E> {
if let Ok(value) = &mut self {
action(value);
}
self
}
}
#[cfg(test)]
mod tests {
use std::{
iter::{Copied, Peekable},
slice,
str::FromStr,
};
use super::*;
struct Stream<'a, 'b> {
tokens: Peekable<Copied<slice::Iter<'b, &'a str>>>,
}
impl<'a, 'b> Stream<'a, 'b> {
fn new(tokens: Peekable<Copied<slice::Iter<'b, &'a str>>>) -> Self {
Self { tokens }
}
fn current(&mut self) -> Option<&'a str> {
self.tokens.peek().copied()
}
}
type Error<'a> = (Option<&'a str>, Vec<&'static str>);
type Parsed<'a, T> = super::Parsed<T, Error<'a>>;
type Result<'a, T> = super::Result<T, Error<'a>>;
fn keyword<'a>(input: &mut Stream, keyword: &'static str) -> Parsed<'a, ()> {
if input.current() == Some(keyword) {
input.tokens.next();
Res(Ok(()))
} else {
Fallthrough
}
}
fn from_str<'a, T: FromStr>(input: &mut Stream) -> Parsed<'a, T> {
if let Some(&str) = input.tokens.peek() {
if let Ok(value) = T::from_str(str) {
input.tokens.next();
return Res(Ok(value));
}
}
Fallthrough
}
fn isize_from_str_radix<'a>(input: &mut Stream, radix: u32) -> Parsed<'a, isize> {
if let Some(&str) = input.tokens.peek() {
if let Ok(value) = isize::from_str_radix(str, radix) {
input.tokens.next();
return Res(Ok(value));
}
}
Fallthrough
}
fn end<'a>(input: &mut Stream) -> Parsed<'a, ()> {
if input.tokens.peek().is_none() {
Res(Ok(()))
} else {
Fallthrough
}
}
fn entry<'a>(input: &mut Stream<'a, '_>) -> Parsed<'a, Option<isize>> {
keyword(input, "value")
.and_then(|_| {
from_str(input)
.map(Some)
.or_parse(|| keyword(input, "none").map(|_| None))
.or_give_up(|| (input.current(), vec!["integer", "none"]))
})
.or_parse(|| {
keyword(input, "radix").and_then(|_| {
let radix =
from_str::<u32>(input).or_give_up(|| (input.current(), vec!["radix"]))?;
isize_from_str_radix(input, radix)
.map(Some)
.or_give_up(|| (input.current(), vec!["radix-integer"]))
})
})
}
fn one_or_more<'a, T>(mut parse: impl FnMut() -> Parsed<'a, T>) -> Parsed<'a, Vec<T>> {
parse().and_then(|initial| {
let mut result = vec![initial];
while let Some(value) = parse().optional()? {
result.push(value);
}
Ok(result)
})
}
fn parse_everything<'a>(input: &mut Stream<'a, '_>) -> Parsed<'a, Vec<Option<isize>>> {
(keyword(input, "begin")).and_then(|_| {
one_or_more(|| entry(input))
.or_give_up(|| (input.current(), vec!["entry"]))
.and_also(|_| {
keyword(input, "end").or_give_up(|| (input.current(), vec!["end", "entry"]))?;
end(input).or_give_up(|| (input.current(), vec!["end of file"]))?;
Ok(())
})
})
}
fn parse_words(input: &str) -> Result<Vec<Option<isize>>> {
let tokens: Vec<_> = input.split_ascii_whitespace().collect();
let mut input = Stream::new(tokens.iter().copied().peekable());
let input = &mut input;
parse_everything(input).or_give_up(|| (input.current(), vec!["begin"]))
}
#[test]
fn parsing() {
assert_eq!(parse_words(""), Err((None, vec!["begin"])));
assert_eq!(parse_words("wrong"), Err((Some("wrong"), vec!["begin"])));
assert_eq!(parse_words("begin"), Err((None, vec!["entry"])));
assert_eq!(
parse_words("begin wrong"),
Err((Some("wrong"), vec!["entry"]))
);
assert_eq!(
parse_words("begin value"),
Err((None, vec!["integer", "none"]))
);
assert_eq!(parse_words("begin radix"), Err((None, vec!["radix"])));
assert_eq!(
parse_words("begin value none"),
Err((None, vec!["end", "entry"]))
);
assert_eq!(parse_words("begin value none end"), Ok(vec![None]));
assert_eq!(
parse_words("begin value none value 3 end"),
Ok(vec![None, Some(3)])
);
assert_eq!(
parse_words("begin value -3 value none end"),
Ok(vec![Some(-3), None])
);
assert_eq!(
parse_words("begin radix 3 value none end"),
Err((Some("value"), vec!["radix-integer"]))
);
assert_eq!(
parse_words("begin value -3 radix 3 1212 value none end"),
Ok(vec![Some(-3), Some(50), None])
);
}
}