#![cfg_attr(not(any(doc, feature = "std", test)), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::all))]
#![cfg_attr(
feature = "nightly",
feature(never_type, fn_traits, tuple_trait, unboxed_closures, specialization)
)]
#![cfg_attr(feature = "nightly", allow(incomplete_features))]
#![doc = include_str!("../README.md")]
#![deny(missing_docs, clippy::undocumented_unsafe_blocks)]
#![allow(
clippy::style,
clippy::useless_format,
clippy::should_implement_trait,
clippy::type_complexity,
clippy::result_unit_err
)]
extern crate alloc;
extern crate core;
macro_rules! go_extra {
( $O :ty ) => {
#[inline(always)]
fn go_emit(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<Emit, $O> {
Parser::<I, $O, E>::go::<Emit>(self, inp)
}
#[inline(always)]
fn go_check(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<Check, $O> {
Parser::<I, $O, E>::go::<Check>(self, inp)
}
};
}
mod blanket;
#[cfg(feature = "unstable")]
pub mod cache;
pub mod combinator;
pub mod container;
#[cfg(feature = "debug")]
pub mod debug;
#[cfg(feature = "either")]
mod either;
pub mod error;
#[cfg(feature = "extension")]
pub mod extension;
pub mod extra;
#[cfg(docsrs)]
pub mod guide;
pub mod input;
pub mod inspector;
pub mod label;
#[cfg(feature = "lexical-numbers")]
pub mod number;
#[cfg(feature = "pratt")]
pub mod pratt;
pub mod primitive;
mod private;
pub mod recovery;
pub mod recursive;
#[cfg(feature = "regex")]
pub mod regex;
pub mod span;
mod stream;
pub mod text;
#[cfg(feature = "bytes")]
mod tokio;
pub mod util;
pub mod prelude {
#[cfg(feature = "lexical-numbers")]
pub use super::number::number;
#[cfg(feature = "regex")]
pub use super::regex::regex;
pub use super::{
error::{Cheap, EmptyErr, Error as _, Rich, Simple},
extra,
input::Input,
primitive::{
any, any_ref, choice, custom, empty, end, group, just, map_ctx, none_of, one_of, set,
todo,
},
recovery::{nested_delimiters, skip_then_retry_until, skip_until, via_parser},
recursive::{recursive, Recursive},
span::{SimpleSpan, Span as _, SpanWrap as _, Spanned},
text, Boxed, ConfigIterParser, ConfigParser, IterParser, ParseResult, Parser,
};
pub use crate::{select, select_ref};
}
use crate::input::InputOwn;
use alloc::{
boxed::Box,
rc::{self, Rc},
string::String,
vec,
vec::Vec,
};
#[cfg(feature = "nightly")]
use core::marker::Tuple;
use core::{
borrow::Borrow,
cell::{Cell, RefCell},
cmp::{Eq, Ord, Ordering},
fmt,
hash::Hash,
marker::PhantomData,
mem::MaybeUninit,
ops::{Deref, DerefMut, Range, RangeFrom},
panic::Location,
str::FromStr,
};
use hashbrown::HashMap;
#[cfg(feature = "serde")]
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use self::{
combinator::*,
container::*,
error::Error,
extra::ParserExtra,
input::{
BorrowInput, Emitter, ExactSizeInput, InputRef, MapExtra, SliceInput, StrInput, ValueInput,
},
inspector::Inspector,
label::{LabelError, Labelled, LabelledWith},
prelude::*,
primitive::Any,
private::{Check, Emit, IPResult, Located, MaybeUninitExt, Mode, PResult, Sealed},
recovery::{RecoverWith, Strategy},
span::{Span, WrappingSpan},
text::*,
util::{IntoMaybe, MaybeMut, MaybeRef},
};
#[cfg(all(feature = "extension", doc))]
use self::{extension::v1::*, primitive::custom, stream::Stream};
struct EmptyPhantom<T>(core::marker::PhantomData<T>);
impl<T> EmptyPhantom<T> {
const fn new() -> Self {
Self(core::marker::PhantomData)
}
}
impl<T> Copy for EmptyPhantom<T> {}
impl<T> Clone for EmptyPhantom<T> {
fn clone(&self) -> Self {
*self
}
}
unsafe impl<T> Send for EmptyPhantom<T> {}
unsafe impl<T> Sync for EmptyPhantom<T> {}
impl<T> Unpin for EmptyPhantom<T> {}
impl<T> core::panic::UnwindSafe for EmptyPhantom<T> {}
impl<T> core::panic::RefUnwindSafe for EmptyPhantom<T> {}
pub(crate) type DynParser<'src, 'b, I, O, E> = dyn Parser<'src, I, O, E> + 'b;
#[cfg(feature = "pratt")]
pub(crate) type DynOperator<'src, 'b, I, O, E> = dyn pratt::Operator<'src, I, O, E> + 'b;
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum DefaultExpected<'a, T> {
Token(MaybeRef<'a, T>),
Any,
SomethingElse,
EndOfInput,
}
impl<T> DefaultExpected<'_, T> {
#[inline]
pub fn into_owned(self) -> DefaultExpected<'static, T>
where
T: Clone,
{
match self {
Self::Token(tok) => DefaultExpected::Token(tok.into_owned()),
Self::Any => DefaultExpected::Any,
Self::SomethingElse => DefaultExpected::SomethingElse,
Self::EndOfInput => DefaultExpected::EndOfInput,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ParseResult<T, E> {
output: Option<T>,
errs: Vec<E>,
}
impl<T, E> ParseResult<T, E> {
pub(crate) fn new(output: Option<T>, errs: Vec<E>) -> ParseResult<T, E> {
ParseResult { output, errs }
}
pub fn has_output(&self) -> bool {
self.output.is_some()
}
pub fn has_errors(&self) -> bool {
!self.errs.is_empty()
}
pub fn output(&self) -> Option<&T> {
self.output.as_ref()
}
pub fn errors(&self) -> impl ExactSizeIterator<Item = &E> + DoubleEndedIterator {
self.errs.iter()
}
pub fn into_output(self) -> Option<T> {
self.output
}
pub fn into_errors(self) -> Vec<E> {
self.errs
}
pub fn into_output_errors(self) -> (Option<T>, Vec<E>) {
(self.output, self.errs)
}
pub fn into_result(self) -> Result<T, Vec<E>> {
if self.errs.is_empty() {
self.output.ok_or(self.errs)
} else {
Err(self.errs)
}
}
#[track_caller]
pub fn unwrap(self) -> T
where
E: fmt::Debug,
{
if self.has_errors() {
panic!(
"called `ParseResult::unwrap` on a parse result containing errors: {:?}",
&self.errs
)
} else {
self.output.expect("parser generated no errors or output")
}
}
}
pub trait Parser<'src, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Default> {
#[cfg(feature = "debug")]
fn debug(&self) -> debug::DebugInfo<'_> {
debug::DebugInfo {
node_info: self.node_info(&mut Default::default()),
phantom: PhantomData,
}
}
#[doc(hidden)]
#[cfg(feature = "debug")]
fn node_info(&self, _scope: &mut debug::NodeScope) -> debug::NodeInfo {
let ty = core::any::type_name::<Self>();
debug::NodeInfo::Unknown(ty.split_once('<').map_or(ty, |(ty, _)| ty).to_string())
}
#[doc(hidden)]
fn go<M: Mode>(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O>
where
Self: Sized;
#[doc(hidden)]
fn go_emit(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<Emit, O>;
#[doc(hidden)]
fn go_check(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<Check, O>;
fn parse(&self, input: I) -> ParseResult<O, E::Error>
where
I: Input<'src>,
E::State: Default,
E::Context: Default,
{
self.parse_with_state(input, &mut E::State::default())
}
fn parse_with_state(&self, input: I, state: &mut E::State) -> ParseResult<O, E::Error>
where
I: Input<'src>,
E::Context: Default,
{
let mut own = InputOwn::new_state(input, state);
let mut inp = own.as_ref_start();
let res = self.then_ignore(end()).go::<Emit>(&mut inp);
let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| {
let fake_span = inp.span_since(&inp.cursor());
E::Error::expected_found([], None, fake_span)
});
let mut errs = own.into_errs();
let out = match res {
Ok(out) => Some(out),
Err(()) => {
errs.push(alt);
None
}
};
ParseResult::new(out, errs)
}
fn check(&self, input: I) -> ParseResult<(), E::Error>
where
Self: Sized,
I: Input<'src>,
E::State: Default,
E::Context: Default,
{
self.check_with_state(input, &mut E::State::default())
}
fn check_with_state(&self, input: I, state: &mut E::State) -> ParseResult<(), E::Error>
where
Self: Sized,
I: Input<'src>,
E::Context: Default,
{
let mut own = InputOwn::new_state(input, state);
let mut inp = own.as_ref_start();
let res = self.then_ignore(end()).go::<Check>(&mut inp);
let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| {
let fake_span = inp.span_since(&inp.cursor());
E::Error::expected_found([], None, fake_span)
});
let mut errs = own.into_errs();
let out = match res {
Ok(()) => Some(()),
Err(()) => {
errs.push(alt);
None
}
};
ParseResult::new(out, errs)
}
fn to_slice(self) -> ToSlice<Self, O>
where
Self: Sized,
{
ToSlice {
parser: self,
phantom: EmptyPhantom::new(),
}
}
fn filter<F: Fn(&O) -> bool>(self, f: F) -> Filter<Self, F>
where
Self: Sized,
{
Filter {
parser: self,
filter: f,
}
}
fn map<U, F: Fn(O) -> U>(self, f: F) -> Map<Self, O, F>
where
Self: Sized,
{
Map {
parser: self,
mapper: f,
phantom: EmptyPhantom::new(),
}
}
fn map_with<U, F: Fn(O, &mut MapExtra<'src, '_, I, E>) -> U>(self, f: F) -> MapWith<Self, O, F>
where
Self: Sized,
{
MapWith {
parser: self,
mapper: f,
phantom: EmptyPhantom::new(),
}
}
#[cfg(feature = "nightly")]
fn map_group<F: Fn<O>>(self, f: F) -> MapGroup<Self, O, F>
where
Self: Sized,
O: Tuple,
{
MapGroup {
parser: self,
mapper: f,
phantom: EmptyPhantom::new(),
}
}
fn to_span(self) -> ToSpan<Self, O>
where
Self: Sized,
{
ToSpan {
parser: self,
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn try_foldl<B, F, OB>(self, other: B, f: F) -> TryFoldl<F, Self, B, OB, E>
where
F: Fn(O, OB, &mut MapExtra<'src, '_, I, E>) -> Result<O, E::Error>,
B: IterParser<'src, I, OB, E>,
Self: Sized,
{
TryFoldl {
parser_a: self,
parser_b: other,
folder: f,
phantom: EmptyPhantom::new(),
}
}
fn spanned(self) -> combinator::Spanned<Self, O>
where
Self: Sized,
{
combinator::Spanned {
parser: self,
phantom: EmptyPhantom::new(),
}
}
#[doc(alias = "filter_map")]
fn try_map<U, F: Fn(O, I::Span) -> Result<U, E::Error>>(self, f: F) -> TryMap<Self, O, F>
where
Self: Sized,
{
TryMap {
parser: self,
mapper: f,
phantom: EmptyPhantom::new(),
}
}
fn try_map_with<U, F: Fn(O, &mut MapExtra<'src, '_, I, E>) -> Result<U, E::Error>>(
self,
f: F,
) -> TryMapWith<Self, O, F>
where
Self: Sized,
{
TryMapWith {
parser: self,
mapper: f,
phantom: EmptyPhantom::new(),
}
}
fn ignored(self) -> Ignored<Self, O>
where
Self: Sized,
{
Ignored {
parser: self,
phantom: EmptyPhantom::new(),
}
}
#[cfg(feature = "memoization")]
fn memoized(self) -> Memoized<Self>
where
Self: Sized,
{
Memoized { parser: self }
}
fn to<U: Clone>(self, to: U) -> To<Self, O, U>
where
Self: Sized,
{
To {
parser: self,
to,
phantom: EmptyPhantom::new(),
}
}
fn labelled<L>(self, label: L) -> Labelled<Self, L>
where
Self: Sized,
E::Error: LabelError<'src, I, L>,
{
Labelled {
parser: self,
label,
is_context: false,
is_builtin: false,
}
}
fn labelled_with<L, F>(self, label: F) -> LabelledWith<Self, L, F>
where
Self: Sized,
E::Error: LabelError<'src, I, L>,
F: Fn() -> L,
{
LabelledWith {
parser: self,
label,
is_context: false,
is_builtin: false,
phantom: PhantomData,
}
}
fn then<U, B: Parser<'src, I, U, E>>(self, other: B) -> Then<Self, B, O, U, E>
where
Self: Sized,
{
Then {
parser_a: self,
parser_b: other,
phantom: EmptyPhantom::new(),
}
}
fn ignore_then<U, B: Parser<'src, I, U, E>>(self, other: B) -> IgnoreThen<Self, B, O, E>
where
Self: Sized,
{
IgnoreThen {
parser_a: self,
parser_b: other,
phantom: EmptyPhantom::new(),
}
}
fn then_ignore<U, B: Parser<'src, I, U, E>>(self, other: B) -> ThenIgnore<Self, B, U, E>
where
Self: Sized,
{
ThenIgnore {
parser_a: self,
parser_b: other,
phantom: EmptyPhantom::new(),
}
}
fn nested_in<B: Parser<'src, J, I, F>, J, F>(self, other: B) -> NestedIn<Self, B, I, O, F>
where
Self: Sized,
I: 'src,
J: Input<'src>,
F: ParserExtra<'src, J>,
{
NestedIn {
parser_a: self,
parser_b: other,
phantom: EmptyPhantom::new(),
}
}
fn ignore_with_ctx<U, P>(
self,
then: P,
) -> IgnoreWithCtx<Self, P, O, I, extra::Full<E::Error, E::State, O>>
where
Self: Sized,
O: 'src,
P: Parser<'src, I, U, extra::Full<E::Error, E::State, O>>,
{
IgnoreWithCtx {
parser: self,
then,
phantom: EmptyPhantom::new(),
}
}
fn then_with_ctx<U, P>(
self,
then: P,
) -> ThenWithCtx<Self, P, O, I, extra::Full<E::Error, E::State, O>>
where
Self: Sized,
O: 'src,
P: Parser<'src, I, U, extra::Full<E::Error, E::State, O>>,
{
ThenWithCtx {
parser: self,
then,
phantom: EmptyPhantom::new(),
}
}
fn with_ctx(self, ctx: E::Context) -> WithCtx<Self, E::Context>
where
Self: Sized,
E::Context: Clone,
{
WithCtx { parser: self, ctx }
}
fn with_state<State>(self, state: State) -> WithState<Self, State>
where
Self: Sized,
State: 'src + Clone,
{
WithState {
parser: self,
state,
}
}
fn and_is<U, B>(self, other: B) -> AndIs<Self, B, U>
where
Self: Sized,
B: Parser<'src, I, U, E>,
{
AndIs {
parser_a: self,
parser_b: other,
phantom: EmptyPhantom::new(),
}
}
fn delimited_by<U, V, B, C>(self, start: B, end: C) -> DelimitedBy<Self, B, C, U, V>
where
Self: Sized,
B: Parser<'src, I, U, E>,
C: Parser<'src, I, V, E>,
{
DelimitedBy {
parser: self,
start,
end,
phantom: EmptyPhantom::new(),
}
}
fn padded_by<U, B>(self, padding: B) -> PaddedBy<Self, B, U>
where
Self: Sized,
B: Parser<'src, I, U, E>,
{
PaddedBy {
parser: self,
padding,
phantom: EmptyPhantom::new(),
}
}
fn or<B>(self, other: B) -> Or<Self, B>
where
Self: Sized,
B: Parser<'src, I, O, E>,
{
Or {
choice: choice((self, other)),
}
}
fn or_not(self) -> OrNot<Self>
where
Self: Sized,
{
OrNot { parser: self }
}
fn not(self) -> Not<Self, O>
where
Self: Sized,
{
Not {
parser: self,
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn repeated(self) -> Repeated<Self, O, I, E>
where
Self: Sized,
{
Repeated {
parser: self,
at_least: 0,
at_most: !0,
#[cfg(debug_assertions)]
location: *Location::caller(),
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn separated_by<U, B>(self, separator: B) -> SeparatedBy<Self, B, O, U, I, E>
where
Self: Sized,
B: Parser<'src, I, U, E>,
{
SeparatedBy {
parser: self,
separator,
at_least: 0,
at_most: !0,
allow_leading: false,
allow_trailing: false,
#[cfg(debug_assertions)]
location: *Location::caller(),
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn foldl<B, F, OB>(self, other: B, f: F) -> Foldl<F, Self, B, OB, E>
where
F: Fn(O, OB) -> O,
B: IterParser<'src, I, OB, E>,
Self: Sized,
{
Foldl {
parser_a: self,
parser_b: other,
folder: f,
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn foldl_with<B, F, OB>(self, other: B, f: F) -> FoldlWith<F, Self, B, OB, E>
where
F: Fn(O, OB, &mut MapExtra<'src, '_, I, E>) -> O,
B: IterParser<'src, I, OB, E>,
Self: Sized,
{
FoldlWith {
parser_a: self,
parser_b: other,
folder: f,
phantom: EmptyPhantom::new(),
}
}
fn rewind(self) -> Rewind<Self>
where
Self: Sized,
{
Rewind { parser: self }
}
fn lazy(self) -> Lazy<'src, Self, I, E>
where
Self: Sized,
I: ValueInput<'src>,
{
self.then_ignore(any().repeated())
}
fn padded(self) -> Padded<Self>
where
Self: Sized,
I: Input<'src>,
I::Token: Char,
{
Padded { parser: self }
}
fn recover_with<S: Strategy<'src, I, O, E>>(self, strategy: S) -> RecoverWith<Self, S>
where
Self: Sized,
{
RecoverWith {
parser: self,
strategy,
}
}
fn map_err<F>(self, f: F) -> MapErr<Self, F>
where
Self: Sized,
F: Fn(E::Error) -> E::Error,
{
MapErr {
parser: self,
mapper: f,
}
}
fn map_err_with_state<F>(self, f: F) -> MapErrWithState<Self, F>
where
Self: Sized,
F: Fn(E::Error, I::Span, &mut E::State) -> E::Error,
{
MapErrWithState {
parser: self,
mapper: f,
}
}
fn validate<U, F>(self, f: F) -> Validate<Self, O, F>
where
Self: Sized,
F: Fn(O, &mut MapExtra<'src, '_, I, E>, &mut Emitter<E::Error>) -> U,
{
Validate {
parser: self,
validator: f,
phantom: EmptyPhantom::new(),
}
}
#[allow(clippy::wrong_self_convention)]
fn from_str<U>(self) -> Map<Self, O, fn(O) -> Result<U, U::Err>>
where
Self: Sized,
U: FromStr,
O: AsRef<str>,
{
self.map(|o| o.as_ref().parse())
}
#[track_caller]
fn unwrapped(self) -> Unwrapped<Self, O>
where
Self: Sized,
{
Unwrapped {
parser: self,
#[cfg(debug_assertions)]
location: *Location::caller(),
phantom: EmptyPhantom::new(),
}
}
fn into_iter(self) -> IntoIter<Self, O>
where
Self: Sized,
O: IntoIterator,
{
IntoIter {
parser: self,
phantom: EmptyPhantom::new(),
}
}
fn boxed<'b>(self) -> Boxed<'src, 'b, I, O, E>
where
Self: Sized + 'b,
{
Boxed {
inner: Rc::new(self),
}
}
#[cfg(feature = "nightly")]
fn simplify(self) -> impl Parser<'src, I, O, E>
where
Self: Sized + 'src,
{
self
}
fn contextual(self) -> Contextual<Self>
where
Self: Sized,
{
Contextual { inner: self }
}
#[cfg(feature = "pratt")]
fn pratt<Ops>(self, ops: Ops) -> pratt::Pratt<Self, Ops>
where
Self: Sized,
{
pratt::Pratt { atom: self, ops }
}
}
#[cfg(feature = "nightly")]
impl<'src, I, O, E> Parser<'src, I, O, E> for !
where
I: Input<'src>,
E: ParserExtra<'src, I>,
{
fn go<M: Mode>(&self, _inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O> {
*self
}
go_extra!(O);
}
pub trait ConfigParser<'src, I, O, E>: Parser<'src, I, O, E>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
{
type Config: Default;
#[doc(hidden)]
fn go_cfg<M: Mode>(
&self,
inp: &mut InputRef<'src, '_, I, E>,
cfg: Self::Config,
) -> PResult<M, O>;
#[doc(hidden)]
#[inline(always)]
fn go_emit_cfg(
&self,
inp: &mut InputRef<'src, '_, I, E>,
cfg: Self::Config,
) -> PResult<Emit, O> {
self.go_cfg::<Emit>(inp, cfg)
}
#[doc(hidden)]
#[inline(always)]
fn go_check_cfg(
&self,
inp: &mut InputRef<'src, '_, I, E>,
cfg: Self::Config,
) -> PResult<Check, O> {
self.go_cfg::<Check>(inp, cfg)
}
fn configure<F>(self, cfg: F) -> Configure<Self, F>
where
Self: Sized,
F: Fn(Self::Config, &E::Context) -> Self::Config,
{
Configure { parser: self, cfg }
}
}
#[derive(Clone, Copy)]
pub struct IterParserDebug {
#[cfg(debug_assertions)]
pub(crate) nonconsumption_is_ok: bool,
}
impl IterParserDebug {
#[inline(always)]
pub(crate) fn new(nonconsumption_is_ok: bool) -> Self {
Self {
#[cfg(debug_assertions)]
nonconsumption_is_ok,
}
}
}
#[cfg(feature = "unstable")]
pub struct ParseIter<
'a,
'src,
'iter,
P: IterParser<'src, I, O, E>,
I: Input<'src>,
O,
E: ParserExtra<'src, I>,
> {
parser: &'a mut P,
own: InputOwn<'src, 'iter, I, E>,
iter_state: Option<P::IterState<Emit>>,
#[allow(dead_code)]
phantom: EmptyPhantom<(&'src (), O)>,
}
#[cfg(feature = "unstable")]
impl<'a, 'src, P, I: Input<'src>, O, E: ParserExtra<'src, I>> Iterator
for ParseIter<'a, 'src, '_, P, I, O, E>
where
P: IterParser<'src, I, O, E>,
{
type Item = O;
fn next(&mut self) -> Option<Self::Item> {
let mut inp = self.own.as_ref_start();
let parser = &self.parser;
let iter_state = match &mut self.iter_state {
Some(state) => state,
None => {
let state = parser.make_iter::<Emit>(&mut inp).ok()?;
self.iter_state = Some(state);
self.iter_state.as_mut().unwrap()
}
};
let res = parser.next::<Emit>(&mut inp, iter_state, IterParserDebug::new(true));
self.own.start = inp.cursor().inner;
res.ok().and_then(|res| res)
}
}
pub trait IterParser<'src, I, O, E = extra::Default>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
{
#[doc(hidden)]
type IterState<M: Mode>
where
I: 'src;
#[doc(hidden)]
fn make_iter<M: Mode>(
&self,
inp: &mut InputRef<'src, '_, I, E>,
) -> PResult<Emit, Self::IterState<M>>;
#[doc(hidden)]
fn next<M: Mode>(
&self,
inp: &mut InputRef<'src, '_, I, E>,
state: &mut Self::IterState<M>,
debug: IterParserDebug,
) -> IPResult<M, O>;
#[doc(hidden)]
#[cfg(feature = "debug")]
fn node_info(&self, _scope: &mut debug::NodeScope) -> debug::NodeInfo {
let ty = core::any::type_name::<Self>();
debug::NodeInfo::Unknown(ty.split_once('<').map_or(ty, |(ty, _)| ty).to_string())
}
#[cfg_attr(debug_assertions, track_caller)]
fn collect<C: Container<O>>(self) -> Collect<Self, O, C>
where
Self: Sized,
{
Collect {
parser: self,
phantom: EmptyPhantom::new(),
}
}
fn collect_exactly<C: ContainerExactly<O>>(self) -> CollectExactly<Self, O, C>
where
Self: Sized,
{
CollectExactly {
parser: self,
phantom: EmptyPhantom::new(),
}
}
fn count(self) -> Collect<Self, O, usize>
where
Self: Sized,
{
self.collect()
}
fn enumerate(self) -> Enumerate<Self, O>
where
Self: Sized,
{
Enumerate {
parser: self,
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn fold<B, F>(self, init: B, f: F) -> Fold<F, Self, B, O, E>
where
B: Clone,
F: Fn(B, O) -> B,
Self: Sized,
{
Fold {
parser: self,
init,
folder: f,
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn foldr<B, F, OA>(self, other: B, f: F) -> Foldr<F, Self, B, O, E>
where
F: Fn(O, OA) -> OA,
B: Parser<'src, I, OA, E>,
Self: Sized,
{
Foldr {
parser_a: self,
parser_b: other,
folder: f,
phantom: EmptyPhantom::new(),
}
}
#[cfg_attr(debug_assertions, track_caller)]
fn foldr_with<B, F, OA>(self, other: B, f: F) -> FoldrWith<F, Self, B, O, E>
where
F: Fn(O, OA, &mut MapExtra<'src, '_, I, E>) -> OA,
B: Parser<'src, I, OA, E>,
Self: Sized,
{
FoldrWith {
parser_a: self,
parser_b: other,
folder: f,
phantom: EmptyPhantom::new(),
}
}
#[cfg(feature = "nightly")]
fn flatten(self) -> Flatten<Self, O>
where
O: IntoIterator,
Self: Sized,
{
Flatten {
parser: self,
phantom: EmptyPhantom::new(),
}
}
#[cfg(feature = "unstable")]
fn parse_iter<F, R>(&mut self, input: I, f: F) -> ParseResult<R, E::Error>
where
Self: IterParser<'src, I, O, E> + Sized,
I: Input<'src>,
E::State: Default,
E::Context: Default,
F: FnOnce(&mut ParseIter<'_, 'src, '_, Self, I, O, E>) -> R,
{
self.parse_iter_with_state(input, &mut Default::default(), f)
}
#[cfg(feature = "unstable")]
fn parse_iter_with_state<F, R>(
&mut self,
input: I,
state: &mut E::State,
f: F,
) -> ParseResult<R, E::Error>
where
Self: IterParser<'src, I, O, E> + Sized,
I: Input<'src>,
E::Context: Default,
F: FnOnce(&mut ParseIter<'_, 'src, '_, Self, I, O, E>) -> R,
{
let mut iter = ParseIter {
parser: self,
own: InputOwn::new_state(input, state),
iter_state: None,
phantom: EmptyPhantom::new(),
};
let out = f(&mut iter);
let mut inp = iter.own.as_ref_start();
let res = end().go::<Emit>(&mut inp);
let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| {
let fake_span = inp.span_since(&inp.cursor());
E::Error::expected_found([], None, fake_span)
});
let mut errs = iter.own.into_errs();
if res.is_err() {
errs.push(alt);
}
ParseResult::new(Some(out), errs)
}
}
pub trait ConfigIterParser<'src, I, O, E = extra::Default>: IterParser<'src, I, O, E>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
{
type Config: Default;
#[doc(hidden)]
fn next_cfg<M: Mode>(
&self,
inp: &mut InputRef<'src, '_, I, E>,
state: &mut Self::IterState<M>,
cfg: &Self::Config,
debug: IterParserDebug,
) -> IPResult<M, O>;
fn configure<F>(self, cfg: F) -> IterConfigure<Self, F, O>
where
Self: Sized,
F: Fn(Self::Config, &E::Context) -> Self::Config,
{
IterConfigure {
parser: self,
cfg,
phantom: EmptyPhantom::new(),
}
}
fn try_configure<F>(self, cfg: F) -> TryIterConfigure<Self, F, O>
where
Self: Sized,
F: Fn(Self::Config, &E::Context, I::Span) -> Result<Self::Config, E::Error>,
{
TryIterConfigure {
parser: self,
cfg,
phantom: EmptyPhantom::new(),
}
}
}
pub struct Boxed<'src, 'b, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Default> {
inner: Rc<DynParser<'src, 'b, I, O, E>>,
}
impl<'src, I: Input<'src>, O, E: ParserExtra<'src, I>> Clone for Boxed<'src, '_, I, O, E> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<'src, I, O, E> Parser<'src, I, O, E> for Boxed<'src, '_, I, O, E>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
{
#[doc(hidden)]
#[cfg(feature = "debug")]
fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo {
self.inner.node_info(scope)
}
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O> {
M::invoke(&*self.inner, inp)
}
fn boxed<'c>(self) -> Boxed<'src, 'c, I, O, E>
where
Self: Sized + 'c,
{
self
}
go_extra!(O);
}
impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::boxed::Box<T>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
T: Parser<'src, I, O, E>,
{
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O>
where
Self: Sized,
{
T::go::<M>(self, inp)
}
go_extra!(O);
}
impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::rc::Rc<T>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
T: Parser<'src, I, O, E>,
{
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O>
where
Self: Sized,
{
T::go::<M>(self, inp)
}
go_extra!(O);
}
impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::sync::Arc<T>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
T: Parser<'src, I, O, E>,
{
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O>
where
Self: Sized,
{
T::go::<M>(self, inp)
}
go_extra!(O);
}
#[macro_export]
macro_rules! select {
($($p:pat $(= $extra:ident)? $(if $guard:expr)? $(=> $out:expr)?),+ $(,)?) => ({
$crate::primitive::select(
move |x, extra| match (x, extra) {
$(($p $(,$extra)?, ..) $(if $guard)? => ::core::option::Option::Some({ () $(;$out)? })),+,
_ => ::core::option::Option::None,
}
)
});
}
#[macro_export]
macro_rules! select_ref {
($($p:pat $(= $extra:ident)? $(if $guard:expr)? $(=> $out:expr)?),+ $(,)?) => ({
$crate::primitive::select_ref(
move |x, extra| match (x, extra) {
$(($p $(,$extra)?, ..) $(if $guard)? => ::core::option::Option::Some({ () $(;$out)? })),+,
_ => ::core::option::Option::None,
}
)
});
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn zero_copy() {
use crate::input::WithContext;
use crate::prelude::*;
#[derive(PartialEq, Debug)]
enum Token<'src> {
Ident(&'src str),
String(&'src str),
}
type FileId = u32;
type Span = SimpleSpan<usize, FileId>;
fn parser<'src>(
) -> impl Parser<'src, WithContext<Span, &'src str>, [(Span, Token<'src>); 6]> {
let ident = any()
.filter(|c: &char| c.is_alphanumeric())
.repeated()
.at_least(1)
.to_slice()
.map(Token::Ident);
let string = just('"')
.then(any().filter(|c: &char| *c != '"').repeated())
.then(just('"'))
.to_slice()
.map(Token::String);
ident
.or(string)
.map_with(|token, e| (e.span(), token))
.padded()
.repeated()
.collect_exactly()
}
assert_eq!(
parser()
.parse(r#"hello "world" these are "test" tokens"#.with_context(42))
.into_result(),
Ok([
(Span::new(42, 0..5), Token::Ident("hello")),
(Span::new(42, 6..13), Token::String("\"world\"")),
(Span::new(42, 14..19), Token::Ident("these")),
(Span::new(42, 20..23), Token::Ident("are")),
(Span::new(42, 24..30), Token::String("\"test\"")),
(Span::new(42, 31..37), Token::Ident("tokens")),
]),
);
}
#[test]
fn zero_copy_map_span() {
use crate::{
input::{SliceInput, ValueInput},
prelude::*,
};
#[derive(PartialEq, Debug)]
enum Token<'src> {
Ident(&'src str),
String(&'src str),
}
type FileId<'src> = &'src str;
type Span<'src> = SimpleSpan<usize, FileId<'src>>;
fn parser<'src, I>() -> impl Parser<'src, I, [(Span<'src>, Token<'src>); 6]>
where
I: ValueInput<'src, Token = char, Span = Span<'src>>
+ SliceInput<'src, Slice = &'src str>,
{
let ident = any()
.filter(|c: &char| c.is_alphanumeric())
.repeated()
.at_least(1)
.to_slice()
.map(Token::Ident);
let string = just('"')
.then(any().filter(|c: &char| *c != '"').repeated())
.then(just('"'))
.to_slice()
.map(Token::String);
ident
.or(string)
.map_with(|token, e| (e.span(), token))
.padded()
.repeated()
.collect_exactly()
}
let filename = "file.txt".to_string();
let fstr = filename.as_str();
assert_eq!(
parser()
.parse(
r#"hello "world" these are "test" tokens"#
.map_span(|span| Span::new(fstr, span.start()..span.end()))
)
.into_result(),
Ok([
(Span::new("file.txt", 0..5), Token::Ident("hello")),
(Span::new("file.txt", 6..13), Token::String("\"world\"")),
(Span::new("file.txt", 14..19), Token::Ident("these")),
(Span::new("file.txt", 20..23), Token::Ident("are")),
(Span::new("file.txt", 24..30), Token::String("\"test\"")),
(Span::new("file.txt", 31..37), Token::Ident("tokens")),
]),
);
}
#[test]
fn zero_copy_repetition() {
use crate::prelude::*;
fn parser<'src>() -> impl Parser<'src, &'src str, Vec<u64>> {
any()
.filter(|c: &char| c.is_ascii_digit())
.repeated()
.at_least(1)
.at_most(3)
.to_slice()
.map(|b: &str| b.parse::<u64>().unwrap())
.padded()
.separated_by(just(',').padded())
.allow_trailing()
.collect()
.delimited_by(just('['), just(']'))
}
assert_eq!(
parser().parse("[122 , 23,43, 4, ]").into_result(),
Ok(vec![122, 23, 43, 4]),
);
assert_eq!(
parser().parse("[0, 3, 6, 900,120]").into_result(),
Ok(vec![0, 3, 6, 900, 120]),
);
assert_eq!(
parser().parse("[200,400,50 ,0,0, ]").into_result(),
Ok(vec![200, 400, 50, 0, 0]),
);
assert!(parser().parse("[1234,123,12,1]").has_errors());
assert!(parser().parse("[,0, 1, 456]").has_errors());
assert!(parser().parse("[3, 4, 5, 67 89,]").has_errors());
}
#[test]
fn zero_copy_group() {
use crate::prelude::*;
fn parser<'src>() -> impl Parser<'src, &'src str, (&'src str, u64, char)> {
group((
any()
.filter(|c: &char| c.is_ascii_alphabetic())
.repeated()
.at_least(1)
.to_slice()
.padded(),
any()
.filter(|c: &char| c.is_ascii_digit())
.repeated()
.at_least(1)
.to_slice()
.map(|s: &str| s.parse::<u64>().unwrap())
.padded(),
any().filter(|c: &char| !c.is_whitespace()).padded(),
))
}
assert_eq!(
parser().parse("abc 123 [").into_result(),
Ok(("abc", 123, '[')),
);
assert_eq!(
parser().parse("among3d").into_result(),
Ok(("among", 3, 'd')),
);
assert_eq!(
parser().parse("cba321,").into_result(),
Ok(("cba", 321, ',')),
);
assert!(parser().parse("abc 123 ").has_errors());
assert!(parser().parse("123abc ]").has_errors());
assert!(parser().parse("and one &").has_errors());
}
#[test]
fn zero_copy_group_array() {
use crate::prelude::*;
fn parser<'src>() -> impl Parser<'src, &'src str, [char; 3]> {
group([just('a'), just('b'), just('c')])
}
assert_eq!(parser().parse("abc").into_result(), Ok(['a', 'b', 'c']));
assert!(parser().parse("abd").has_errors());
}
#[test]
fn unicode_str() {
let input = "🄯🄚🄐🝋🄂🬯🈦g🍩🕔🈳2🬙🨞🅢🭳🎅h🧿🏩k🠡🀔🤟📵🤿🝜🙘5🠻🠓";
let mut own = crate::input::InputOwn::<_, extra::Default>::new(input);
let mut inp = own.as_ref_start();
while let Some(_c) = inp.next() {}
}
#[test]
#[cfg(feature = "unstable")]
fn iter() {
use crate::prelude::*;
fn many_letters<'src>() -> impl IterParser<'src, &'src str, char> {
any().filter(char::is_ascii_alphabetic).repeated()
}
let res = many_letters().parse_iter("abcdef", |iter| iter.collect::<String>());
assert_eq!(res.into_result().unwrap(), "abcdef");
let res = many_letters().parse_iter("123456", |iter| iter.collect::<String>());
assert!(res.has_errors());
}
#[test]
#[cfg(feature = "memoization")]
fn exponential() {
use crate::prelude::*;
fn parser<'src>() -> impl Parser<'src, &'src str, String> {
recursive(|expr| {
let atom = any()
.filter(|c: &char| c.is_alphabetic())
.repeated()
.at_least(1)
.collect()
.or(expr.delimited_by(just('('), just(')')));
atom.clone()
.then_ignore(just('+'))
.then(atom.clone())
.map(|(a, b)| format!("{a}{b}"))
.memoized()
.or(atom)
})
.then_ignore(end())
}
parser()
.parse("((((((((((((((((((((((((((((((a+b))))))))))))))))))))))))))))))")
.into_result()
.unwrap();
}
#[test]
#[cfg(feature = "memoization")]
fn left_recursive() {
use crate::prelude::*;
fn parser<'src>() -> impl Parser<'src, &'src str, String> {
recursive(|expr| {
let atom = any()
.filter(|c: &char| c.is_alphabetic())
.repeated()
.at_least(1)
.collect();
let sum = expr
.clone()
.then_ignore(just('+'))
.then(expr)
.map(|(a, b)| format!("{a}{b}"))
.memoized();
sum.or(atom)
})
.then_ignore(end())
}
assert_eq!(parser().parse("a+b+c").into_result().unwrap(), "abc");
}
#[cfg(debug_assertions)]
mod debug_asserts {
use crate::prelude::*;
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn debug_assert_collect() {
empty::<&str, extra::Default>()
.to(())
.repeated()
.collect::<()>()
.parse("a+b+c")
.unwrap();
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn debug_assert_separated_by() {
empty::<&str, extra::Default>()
.to(())
.separated_by(empty())
.collect::<()>()
.parse("a+b+c");
}
#[test]
fn debug_assert_separated_by2() {
assert_eq!(
empty::<&str, extra::Default>()
.to(())
.separated_by(just(','))
.count()
.parse(",")
.unwrap(),
2
);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn debug_assert_foldl() {
assert_eq!(
empty::<&str, extra::Default>()
.to(1)
.foldl(empty().repeated(), |n, ()| n + 1)
.parse("a+b+c")
.unwrap(),
3
);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn debug_assert_foldl_with() {
use extra::SimpleState;
let state = 100;
empty::<&str, extra::Full<EmptyErr, SimpleState<i32>, ()>>()
.foldl_with(empty().to(()).repeated(), |_, _, _| ())
.parse_with_state("a+b+c", &mut state.into());
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn debug_assert_foldr() {
empty::<&str, extra::Default>()
.to(())
.repeated()
.foldr(empty(), |_, _| ())
.parse("a+b+c");
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn debug_assert_foldr_with_state() {
empty::<&str, extra::Default>()
.to(())
.repeated()
.foldr_with(empty(), |_, _, _| ())
.parse_with_state("a+b+c", &mut ());
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn debug_assert_repeated() {
empty::<&str, extra::Default>()
.to(())
.repeated()
.parse("a+b+c");
}
}
#[test]
#[should_panic]
fn recursive_define_twice() {
let mut expr = Recursive::declare();
expr.define({
let atom = any::<&str, extra::Default>()
.filter(|c: &char| c.is_alphabetic())
.repeated()
.at_least(1)
.collect();
let sum = expr
.clone()
.then_ignore(just('+'))
.then(expr.clone())
.map(|(a, b)| format!("{a}{b}"));
sum.or(atom)
});
expr.define(expr.clone());
expr.then_ignore(end()).parse("a+b+c");
}
#[test]
#[should_panic]
fn todo_err() {
let expr = todo::<&str, String, extra::Default>();
expr.then_ignore(end()).parse("a+b+c");
}
#[test]
fn box_impl() {
fn parser<'src>() -> impl Parser<'src, &'src str, Vec<u64>> {
Box::new(
any()
.filter(|c: &char| c.is_ascii_digit())
.repeated()
.at_least(1)
.at_most(3)
.to_slice()
.map(|b: &str| b.parse::<u64>().unwrap())
.padded()
.separated_by(just(',').padded())
.allow_trailing()
.collect()
.delimited_by(just('['), just(']')),
)
}
assert_eq!(
parser().parse("[122 , 23,43, 4, ]").into_result(),
Ok(vec![122, 23, 43, 4]),
);
assert_eq!(
parser().parse("[0, 3, 6, 900,120]").into_result(),
Ok(vec![0, 3, 6, 900, 120]),
);
assert_eq!(
parser().parse("[200,400,50 ,0,0, ]").into_result(),
Ok(vec![200, 400, 50, 0, 0]),
);
}
#[test]
fn rc_impl() {
use alloc::rc::Rc;
fn parser<'src>() -> impl Parser<'src, &'src str, Vec<u64>> {
Rc::new(
any()
.filter(|c: &char| c.is_ascii_digit())
.repeated()
.at_least(1)
.at_most(3)
.to_slice()
.map(|b: &str| b.parse::<u64>().unwrap())
.padded()
.separated_by(just(',').padded())
.allow_trailing()
.collect()
.delimited_by(just('['), just(']')),
)
}
assert_eq!(
parser().parse("[122 , 23,43, 4, ]").into_result(),
Ok(vec![122, 23, 43, 4]),
);
assert_eq!(
parser().parse("[0, 3, 6, 900,120]").into_result(),
Ok(vec![0, 3, 6, 900, 120]),
);
assert_eq!(
parser().parse("[200,400,50 ,0,0, ]").into_result(),
Ok(vec![200, 400, 50, 0, 0]),
);
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct MyErr(&'static str);
impl<'src, I: Input<'src>> crate::Error<'src, I> for MyErr {
fn merge(self, other: Self) -> Self {
if other == MyErr("special") {
MyErr("special")
} else {
self
}
}
}
impl<'src, I> crate::LabelError<'src, I, crate::DefaultExpected<'src, I::Token>> for MyErr
where
I: Input<'src>,
{
fn expected_found<E: IntoIterator<Item = crate::DefaultExpected<'src, I::Token>>>(
_expected: E,
_found: Option<crate::MaybeRef<'src, I::Token>>,
_span: I::Span,
) -> Self {
MyErr("expected found")
}
}
#[test]
fn err_prio_0() {
#[allow(dead_code)]
fn always_err<'src>() -> impl Parser<'src, &'src str, (), extra::Err<MyErr>> {
empty().try_map(|_, _| Err(MyErr("special")))
}
assert_eq!(
always_err().parse("test").into_result().unwrap_err(),
vec![MyErr("special")]
)
}
#[test]
fn err_prio_1() {
#[allow(dead_code)]
fn always_err_choice<'src>() -> impl Parser<'src, &'src str, (), extra::Err<MyErr>> {
choice((just("something").ignored(), empty())).try_map(|_, _| Err(MyErr("special")))
}
assert_eq!(
always_err_choice().parse("test").into_result().unwrap_err(),
vec![MyErr("special")]
)
}
#[test]
fn into_iter_no_error() {
fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err<MyErr>> {
let many_as = just('a')
.ignored()
.repeated()
.at_least(1)
.collect::<Vec<_>>();
many_as.into_iter().collect()
}
assert_eq!(parser().parse("aaa").into_result(), Ok(()));
}
#[cfg(feature = "nightly")]
#[test]
fn flatten() {
fn parser<'src>() -> impl Parser<'src, &'src str, Vec<char>, extra::Err<MyErr>> {
let many_as = just('a')
.map(Some)
.or(any().to(None))
.repeated()
.flatten()
.collect::<Vec<_>>();
many_as.into_iter().collect()
}
assert_eq!(
parser().parse("abracadabra").into_result(),
Ok(vec!['a', 'a', 'a', 'a', 'a'])
);
}
#[test]
fn iterable_then() {
fn parser<'src>() -> impl Parser<'src, &'src str, Vec<char>> {
just('a')
.map(Some)
.into_iter()
.then(just('b').repeated())
.then(just('c').repeated())
.collect()
}
assert_eq!(
parser().parse("abbcc").into_result(),
Ok(vec!['a', 'b', 'b', 'c', 'c'])
);
assert_eq!(parser().parse("acc").into_result(), Ok(vec!['a', 'c', 'c']));
assert!(parser().parse("bbc").has_errors());
}
#[test]
#[cfg(feature = "unstable")]
fn cached() {
fn my_parser<'src>() -> impl Parser<'src, &'src str, &'src str, extra::Default> {
any().repeated().exactly(5).to_slice()
}
struct MyCache;
impl crate::cache::Cached for MyCache {
type Parser<'src> = Boxed<'src, 'src, &'src str, &'src str, extra::Default>;
fn make_parser<'src>(self) -> Self::Parser<'src> {
Parser::boxed(my_parser())
}
}
{
let parser = crate::cache::Cache::new(MyCache);
for _ in 0..2 {
let s = "hello".to_string();
assert_eq!(parser.get().parse(&s).into_result(), Ok("hello"));
assert!(parser.get().parse("goodbye").into_result().is_err());
}
}
{
let s = "hello".to_string();
for _ in 0..2 {
let parser = crate::cache::Cache::new(MyCache);
assert_eq!(parser.get().parse(&s).into_result(), Ok("hello"));
assert!(parser.get().parse("goodbye").into_result().is_err());
}
}
}
#[test]
#[allow(dead_code)]
fn map_with_compiles() {
enum Token {}
enum Expr {}
fn expr<'src, I>() -> impl Parser<'src, I, (Expr, SimpleSpan)> + 'src
where
I: Input<'src, Token = Token, Span = SimpleSpan> + 'src,
{
todo().map_with(|expr, e| (expr, e.span()))
}
}
#[test]
fn label() {
use crate::label::LabelError;
fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err<Rich<'src, char>>> {
just("hello").labelled("greeting").as_context().ignored()
}
let mut err = <Rich<_> as crate::LabelError<&str, char>>::expected_found(
['h'],
Some('b'.into()),
(0..1).into(),
);
<Rich<_, _> as LabelError<&str, _>>::label_with(&mut err, "greeting");
assert_eq!(parser().parse("bye").into_errors(), vec![err]);
let mut err = <Rich<_> as crate::LabelError<&str, char>>::expected_found(
['l'],
Some('p'.into()),
(3..4).into(),
);
<Rich<_, _> as LabelError<&str, _>>::in_context(&mut err, "greeting", (0..3).into());
assert_eq!(parser().parse("help").into_errors(), vec![err]);
fn parser2<'src>() -> impl Parser<'src, &'src str, (), extra::Err<Rich<'src, char>>> {
text::keyword("hello")
.labelled("greeting")
.as_context()
.ignored()
}
let mut err =
<Rich<_> as crate::LabelError<&str, char>>::expected_found(['h'], None, (0..7).into());
<Rich<_, _> as LabelError<&str, _>>::label_with(&mut err, "greeting");
assert_eq!(parser2().parse("goodbye").into_errors(), vec![err]);
}
#[test]
fn labelled_with() {
use crate::label::LabelError;
fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err<Rich<'src, char>>> {
just("hello")
.ignored()
.recover_with(via_parser(empty()))
.labelled_with(|| "greeting")
.as_context()
}
let mut err =
<Rich<_> as LabelError<&str, char>>::expected_found(['h'], None, (0..0).into());
<Rich<_, _> as LabelError<&str, _>>::in_context(&mut err, "greeting", (0..0).into());
assert_eq!(parser().parse("").into_errors(), vec![err]);
}
#[test]
#[allow(dead_code)]
fn invalid_escape() {
use crate::LabelError;
fn string<'src>() -> impl Parser<'src, &'src str, &'src str, extra::Err<Rich<'src, char>>> {
let quote = just("\"");
let escaped = just("\\").then(just("n"));
let unescaped = none_of("\\\"");
unescaped
.ignored()
.or(escaped.ignored())
.repeated()
.to_slice()
.delimited_by(quote, quote)
}
assert_eq!(
string().parse(r#""Hello\m""#).into_result(),
Err(vec![
<Rich<char> as LabelError::<&str, char>>::expected_found(
['n'],
Some('m'.into()),
(7..8).into(),
)
]),
);
}
#[test]
#[allow(dead_code)]
fn map_err_missed_info() {
use crate::{extra::Err, LabelError};
fn erroneous_map_err<'src>() -> impl Parser<'src, &'src str, (), Err<Rich<'src, char>>> {
group((
just("a").or_not(),
just("b").map_err(|mut err| {
LabelError::<&str, _>::label_with(&mut err, 'l');
err
}),
))
.ignored()
}
assert_eq!(
erroneous_map_err().parse("_").into_output_errors(),
(
None,
vec![LabelError::<&str, _>::expected_found(
['a', 'l'],
Some('_'.into()),
SimpleSpan::new((), 0..1),
)]
),
);
fn erroneous_then<'src>() -> impl Parser<'src, &'src str, (), Err<Rich<'src, char>>> {
group((
just("a").or_not(),
empty().map_err(|mut err| {
LabelError::<&str, _>::label_with(&mut err, 'l');
err
}),
just("c"),
))
.ignored()
}
assert_eq!(
erroneous_then().parse("_").into_output_errors(),
(
None,
vec![LabelError::<&str, _>::expected_found(
['a', 'c'],
Some('_'.into()),
SimpleSpan::new((), 0..1),
)]
),
);
}
#[test]
fn map_err() {
use crate::LabelError;
let parser = just::<char, &str, extra::Err<_>>('"').map_err(move |e: Rich<char>| {
println!("Found = {:?}", e.found());
println!("Expected = {:?}", e.expected().collect::<Vec<_>>());
println!("Span = {:?}", e.span());
LabelError::<&str, char>::expected_found(
['"'],
e.found().copied().map(Into::into),
*e.span(),
)
});
assert_eq!(
parser.parse(r#"H"#).into_result(),
Err(vec![LabelError::<&str, char>::expected_found(
['"'],
Some('H'.into()),
(0..1).into()
)])
);
}
#[test]
fn try_map() {
use crate::{DefaultExpected, LabelError};
let parser = group((
just("a").or_not(),
just("b").try_map(|_, _| Ok(())).or_not(),
just::<_, &str, extra::Err<Rich<_>>>("c"),
))
.ignored();
assert_eq!(
parser.parse("").into_output_errors(),
(
None,
vec![LabelError::<&str, _>::expected_found(
vec![
DefaultExpected::Token('a'.into()),
DefaultExpected::Token('b'.into()),
DefaultExpected::Token('c'.into()),
],
None,
SimpleSpan::new((), 0..0)
)]
)
);
}
#[test]
fn try_map_with() {
use crate::{DefaultExpected, LabelError};
let parser = group((
just("a").or_not(),
just("b").try_map_with(|_, _| Ok(())).or_not(),
just::<_, &str, extra::Err<Rich<_>>>("c"),
))
.ignored();
assert_eq!(
parser.parse("").into_output_errors(),
(
None,
vec![LabelError::<&str, _>::expected_found(
vec![
DefaultExpected::Token('a'.into()),
DefaultExpected::Token('b'.into()),
DefaultExpected::Token('c'.into()),
],
None,
SimpleSpan::new((), 0..0)
)]
)
);
}
#[test]
fn filter() {
use crate::{DefaultExpected, LabelError};
let parser = just::<_, _, extra::Err<Rich<_>>>("a").filter(|_| false);
assert_eq!(
parser.parse("a").into_result(),
Err(vec![LabelError::<&str, _>::expected_found(
[DefaultExpected::SomethingElse],
Some('a'.into()),
SimpleSpan::new((), 0..1)
),])
);
let parser = group((
just("a").or_not(),
just("b").filter(|_| false).or_not(),
just::<_, &str, extra::Err<Rich<_>>>("c"),
));
assert_eq!(
parser.parse("b").into_output_errors(),
(
None,
vec![LabelError::<&str, _>::expected_found(
vec![
DefaultExpected::Token('a'.into()),
DefaultExpected::SomethingElse,
DefaultExpected::Token('c'.into()),
],
Some('b'.into()),
SimpleSpan::new((), 0..1)
)]
)
);
}
#[test]
fn rewind() {
use crate::{DefaultExpected, LabelError};
let parser = group((just("a"), any(), just("b").or_not()))
.rewind()
.then(just::<_, _, extra::Err<Rich<_>>>("ac"));
assert_eq!(
parser.parse("ad").into_output_errors(),
(
None,
vec![LabelError::<&str, _>::expected_found(
[DefaultExpected::Token('c'.into())],
Some('d'.into()),
SimpleSpan::new((), 1..2)
)]
)
)
}
#[test]
fn separated_by() {
use crate::{error::Simple, extra};
let parser = just::<_, &str, extra::Err<Simple<_>>>("a")
.or_not()
.separated_by(just("b"));
assert_eq!(parser.parse("bba").into_result(), Ok(()));
}
#[test]
fn zero_size_custom_failure() {
fn my_custom<'src>() -> impl Parser<'src, &'src str, ()> {
custom(|inp| {
let check = inp.save();
if inp.parse(just("foo")).is_err() {
inp.rewind(check);
}
Ok(())
})
}
assert!(my_custom().parse("not foo").has_errors());
}
#[test]
fn labels() {
use crate::{DefaultExpected, Error, LabelError, TextExpected};
let parser = just("a")
.or_not()
.then(text::whitespace::<&str, extra::Err<Rich<_>>>());
assert_eq!(
parser.parse("b").into_output_errors(),
(
None,
vec![Error::<&str>::merge(
Error::<&str>::merge(
LabelError::<&str, _>::expected_found(
vec![DefaultExpected::Token('a'.into())],
Some('b'.into()),
SimpleSpan::new((), 0..1)
),
LabelError::<&str, _>::expected_found(
vec![TextExpected::<&str>::Whitespace],
Some('b'.into()),
SimpleSpan::new((), 0..1)
),
),
LabelError::<&str, _>::expected_found(
vec![DefaultExpected::EndOfInput],
Some('b'.into()),
SimpleSpan::new((), 0..1)
),
)]
)
);
}
#[test]
fn labelled_not() {
use crate::{DefaultExpected, LabelError};
let parser = any::<_, extra::Err<Rich<_>>>().not().labelled("label");
let mut err = LabelError::<&str, _>::expected_found(
[DefaultExpected::SomethingElse],
Some('b'.into()),
SimpleSpan::new((), 0..1),
);
LabelError::<&str, _>::label_with(&mut err, "label");
assert_eq!(parser.parse("b").into_output_errors(), (None, vec![err]));
}
#[test]
fn state_rewind() {
use crate::{extra::Full, inspector::TruncateState};
let parser = any::<_, Full<EmptyErr, TruncateState<char>, ()>>()
.map_with(|out, extra| {
extra.state().0.push(out);
extra.state().0.len() - 1
})
.rewind()
.then_ignore(any());
let mut state = TruncateState::default();
let res = parser.parse_with_state("a", &mut state).unwrap();
assert_eq!(res, 0);
assert_eq!(state.0.as_slice(), ['a']);
}
#[test]
fn error_rewind() {
let parser = any::<_, extra::Default>()
.validate(|out, _, emitter| {
emitter.emit(EmptyErr::default());
out
})
.rewind()
.then_ignore(any());
assert_eq!(
parser.parse("a").into_output_errors(),
(Some('a'), vec![EmptyErr::default()])
);
}
#[test]
fn labelled_recovery_dont_panic() {
fn parser<'i>() -> impl Parser<'i, &'i str, SimpleSpan> {
choice((choice((just("true"), just("false")))
.labelled("boolean")
.to_span(),))
.recover_with(via_parser(any().and_is(text::newline().not()).to_span()))
}
let _ = parser().parse("tru");
}
}