feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+185
@@ -0,0 +1,185 @@
|
||||
use crate::combinator::trace;
|
||||
use crate::error::ParserError;
|
||||
use crate::stream::Stream;
|
||||
use crate::{Parser, Result};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::dispatch;
|
||||
|
||||
/// Helper trait for the [`alt()`] combinator.
|
||||
///
|
||||
/// This trait is implemented for tuples of up to 21 elements
|
||||
pub trait Alt<I, O, E> {
|
||||
/// Tests each parser in the tuple and returns the result of the first one that succeeds
|
||||
fn choice(&mut self, input: &mut I) -> Result<O, E>;
|
||||
}
|
||||
|
||||
/// Pick the first successful parser
|
||||
///
|
||||
/// To stop on an error, rather than trying further cases, see
|
||||
/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]).
|
||||
///
|
||||
/// For tight control over the error when no match is found, add a final case using [`fail`][crate::combinator::fail].
|
||||
/// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all
|
||||
/// errors or return the error of the parser that went the farthest in the input data.
|
||||
///
|
||||
/// When the alternative cases have unique prefixes, [`dispatch`] can offer better performance.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::{error::ErrMode, error::Needed};
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::ascii::{alpha1, digit1};
|
||||
/// use winnow::combinator::alt;
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// alt((alpha1, digit1)).parse_next(input)
|
||||
/// };
|
||||
///
|
||||
/// // the first parser, alpha1, takes the input
|
||||
/// assert_eq!(parser.parse_peek("abc"), Ok(("", "abc")));
|
||||
///
|
||||
/// // the first parser returns an error, so alt tries the second one
|
||||
/// assert_eq!(parser.parse_peek("123456"), Ok(("", "123456")));
|
||||
///
|
||||
/// // both parsers failed, and with the default error type, alt will return the last error
|
||||
/// assert!(parser.parse_peek(" ").is_err());
|
||||
/// # }
|
||||
/// ```
|
||||
#[doc(alias = "choice")]
|
||||
#[inline(always)]
|
||||
pub fn alt<Input: Stream, Output, Error, Alternatives>(
|
||||
mut alternatives: Alternatives,
|
||||
) -> impl Parser<Input, Output, Error>
|
||||
where
|
||||
Alternatives: Alt<Input, Output, Error>,
|
||||
Error: ParserError<Input>,
|
||||
{
|
||||
trace("alt", move |i: &mut Input| alternatives.choice(i))
|
||||
}
|
||||
|
||||
impl<const N: usize, I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for [P; N] {
|
||||
fn choice(&mut self, input: &mut I) -> Result<O, E> {
|
||||
let mut error: Option<E> = None;
|
||||
|
||||
let start = input.checkpoint();
|
||||
for branch in self {
|
||||
input.reset(&start);
|
||||
match branch.parse_next(input) {
|
||||
Err(e) if e.is_backtrack() => {
|
||||
error = match error {
|
||||
Some(error) => Some(error.or(e)),
|
||||
None => Some(e),
|
||||
};
|
||||
}
|
||||
res => return res,
|
||||
}
|
||||
}
|
||||
|
||||
match error {
|
||||
Some(e) => Err(e.append(input, &start)),
|
||||
None => Err(ParserError::assert(
|
||||
input,
|
||||
"`alt` needs at least one parser",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for &mut [P] {
|
||||
fn choice(&mut self, input: &mut I) -> Result<O, E> {
|
||||
let mut error: Option<E> = None;
|
||||
|
||||
let start = input.checkpoint();
|
||||
for branch in self.iter_mut() {
|
||||
input.reset(&start);
|
||||
match branch.parse_next(input) {
|
||||
Err(e) if e.is_backtrack() => {
|
||||
error = match error {
|
||||
Some(error) => Some(error.or(e)),
|
||||
None => Some(e),
|
||||
};
|
||||
}
|
||||
res => return res,
|
||||
}
|
||||
}
|
||||
|
||||
match error {
|
||||
Some(e) => Err(e.append(input, &start)),
|
||||
None => Err(ParserError::assert(
|
||||
input,
|
||||
"`alt` needs at least one parser",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! alt_trait(
|
||||
($first:ident $second:ident $($id: ident)+) => (
|
||||
alt_trait!(__impl $first $second; $($id)+);
|
||||
);
|
||||
(__impl $($current:ident)*; $head:ident $($id: ident)+) => (
|
||||
alt_trait_impl!($($current)*);
|
||||
|
||||
alt_trait!(__impl $($current)* $head; $($id)+);
|
||||
);
|
||||
(__impl $($current:ident)*; $head:ident) => (
|
||||
alt_trait_impl!($($current)*);
|
||||
alt_trait_impl!($($current)* $head);
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! alt_trait_impl(
|
||||
($($id:ident)+) => (
|
||||
impl<
|
||||
I: Stream, Output, Error: ParserError<I>,
|
||||
$($id: Parser<I, Output, Error>),+
|
||||
> Alt<I, Output, Error> for ( $($id),+ ) {
|
||||
|
||||
fn choice(&mut self, input: &mut I) -> Result<Output, Error> {
|
||||
let start = input.checkpoint();
|
||||
match self.0.parse_next(input) {
|
||||
Err(e) if e.is_backtrack() => alt_trait_inner!(1, self, input, start, e, $($id)+),
|
||||
res => res,
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! succ (
|
||||
(1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*));
|
||||
(2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*));
|
||||
(3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*));
|
||||
(4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*));
|
||||
(5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*));
|
||||
(6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*));
|
||||
(7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*));
|
||||
(8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*));
|
||||
);
|
||||
|
||||
macro_rules! alt_trait_inner(
|
||||
($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({
|
||||
$input.reset(&$start);
|
||||
match $self.$it.parse_next($input) {
|
||||
Err(e) if e.is_backtrack() => {
|
||||
let err = $err.or(e);
|
||||
succ!($it, alt_trait_inner!($self, $input, $start, err, $($id)+))
|
||||
}
|
||||
res => res,
|
||||
}
|
||||
});
|
||||
($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident) => ({
|
||||
Err($err.append($input, &$start))
|
||||
});
|
||||
);
|
||||
|
||||
alt_trait!(Alt2 Alt3 Alt4 Alt5 Alt6 Alt7 Alt8 Alt9 Alt10);
|
||||
|
||||
// Manually implement Alt for (A,), the 1-tuple type
|
||||
impl<I: Stream, O, E: ParserError<I>, A: Parser<I, O, E>> Alt<I, O, E> for (A,) {
|
||||
fn choice(&mut self, input: &mut I) -> Result<O, E> {
|
||||
self.0.parse_next(input)
|
||||
}
|
||||
}
|
||||
Vendored
+400
@@ -0,0 +1,400 @@
|
||||
use crate::combinator::trace;
|
||||
use crate::error::{ModalError, ParserError};
|
||||
use crate::stream::Stream;
|
||||
use crate::{Parser, Result};
|
||||
|
||||
/// Apply a [`Parser`], producing `None` on [`ErrMode::Backtrack`][crate::error::ErrMode::Backtrack].
|
||||
///
|
||||
/// To chain an error up, see [`cut_err`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::opt;
|
||||
/// use winnow::ascii::alpha1;
|
||||
///
|
||||
/// fn parser<'i>(i: &mut &'i str) -> ModalResult<Option<&'i str>> {
|
||||
/// opt(alpha1).parse_next(i)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("abcd;"), Ok((";", Some("abcd"))));
|
||||
/// assert_eq!(parser.parse_peek("123;"), Ok(("123;", None)));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn opt<Input: Stream, Output, Error, ParseNext>(
|
||||
mut parser: ParseNext,
|
||||
) -> impl Parser<Input, Option<Output>, Error>
|
||||
where
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
Error: ParserError<Input>,
|
||||
{
|
||||
trace("opt", move |input: &mut Input| {
|
||||
let start = input.checkpoint();
|
||||
match parser.parse_next(input) {
|
||||
Ok(o) => Ok(Some(o)),
|
||||
Err(e) if e.is_backtrack() => {
|
||||
input.reset(&start);
|
||||
Ok(None)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Calls the parser if the condition is met.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::prelude::*;
|
||||
/// # use winnow::combinator::opt;
|
||||
/// use winnow::combinator::cond;
|
||||
/// use winnow::ascii::alpha1;
|
||||
///
|
||||
/// fn parser<'i>(i: &mut &'i str) -> ModalResult<Option<&'i str>> {
|
||||
/// let prefix = opt("-").parse_next(i)?;
|
||||
/// let condition = prefix.is_some();
|
||||
/// cond(condition, alpha1).parse_next(i)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("-abcd;"), Ok((";", Some("abcd"))));
|
||||
/// assert_eq!(parser.parse_peek("abcd;"), Ok(("abcd;", None)));
|
||||
/// assert!(parser.parse_peek("-123;").is_err());
|
||||
/// assert_eq!(parser.parse_peek("123;"), Ok(("123;", None)));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn cond<Input, Output, Error, ParseNext>(
|
||||
cond: bool,
|
||||
mut parser: ParseNext,
|
||||
) -> impl Parser<Input, Option<Output>, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
Error: ParserError<Input>,
|
||||
{
|
||||
trace("cond", move |input: &mut Input| {
|
||||
if cond {
|
||||
parser.parse_next(input).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Apply the parser without advancing the input.
|
||||
///
|
||||
/// To lookahead and only advance on success, see [`opt`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::peek;
|
||||
/// use winnow::ascii::alpha1;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// peek(alpha1).parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("abcd;"), Ok(("abcd;", "abcd")));
|
||||
/// assert!(parser.parse_peek("123;").is_err());
|
||||
/// # }
|
||||
/// ```
|
||||
#[doc(alias = "look_ahead")]
|
||||
#[doc(alias = "rewind")]
|
||||
pub fn peek<Input, Output, Error, ParseNext>(
|
||||
mut parser: ParseNext,
|
||||
) -> impl Parser<Input, Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
{
|
||||
trace("peek", move |input: &mut Input| {
|
||||
let start = input.checkpoint();
|
||||
let res = parser.parse_next(input);
|
||||
input.reset(&start);
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
/// Match the end of the [`Stream`]
|
||||
///
|
||||
/// Otherwise, it will error.
|
||||
///
|
||||
/// # Effective Signature
|
||||
///
|
||||
/// Assuming you are parsing a `&str` [Stream]:
|
||||
/// ```rust
|
||||
/// # use winnow::prelude::*;;
|
||||
/// pub fn eof<'i>(input: &mut &'i str) -> ModalResult<&'i str>
|
||||
/// # {
|
||||
/// # winnow::combinator::eof.parse_next(input)
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::str;
|
||||
/// # use winnow::combinator::eof;
|
||||
/// # use winnow::prelude::*;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// eof.parse_next(input)
|
||||
/// }
|
||||
/// assert!(parser.parse_peek("abc").is_err());
|
||||
/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
|
||||
/// ```
|
||||
#[doc(alias = "end")]
|
||||
#[doc(alias = "eoi")]
|
||||
pub fn eof<Input, Error>(input: &mut Input) -> Result<<Input as Stream>::Slice, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
{
|
||||
trace("eof", move |input: &mut Input| {
|
||||
if input.eof_offset() == 0 {
|
||||
Ok(input.next_slice(0))
|
||||
} else {
|
||||
Err(ParserError::from_input(input))
|
||||
}
|
||||
})
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
/// Succeeds if the child parser returns an error.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Note:** This does not advance the [`Stream`]
|
||||
///
|
||||
/// </div>
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::not;
|
||||
/// use winnow::ascii::alpha1;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<()> {
|
||||
/// not(alpha1).parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("123"), Ok(("123", ())));
|
||||
/// assert!(parser.parse_peek("abcd").is_err());
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn not<Input, Output, Error, ParseNext>(mut parser: ParseNext) -> impl Parser<Input, (), Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
{
|
||||
trace("not", move |input: &mut Input| {
|
||||
let start = input.checkpoint();
|
||||
let res = parser.parse_next(input);
|
||||
input.reset(&start);
|
||||
match res {
|
||||
Ok(_) => Err(ParserError::from_input(input)),
|
||||
Err(e) if e.is_backtrack() => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Transforms an [`ErrMode::Backtrack`][crate::error::ErrMode::Backtrack] (recoverable) to [`ErrMode::Cut`][crate::error::ErrMode::Cut] (unrecoverable)
|
||||
///
|
||||
/// This commits the parse result, preventing alternative branch paths like with
|
||||
/// [`winnow::combinator::alt`][crate::combinator::alt].
|
||||
///
|
||||
/// See the [tutorial][crate::_tutorial::chapter_7] for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Without `cut_err`:
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::token::one_of;
|
||||
/// # use winnow::token::rest;
|
||||
/// # use winnow::ascii::digit1;
|
||||
/// # use winnow::combinator::alt;
|
||||
/// # use winnow::combinator::preceded;
|
||||
/// # use winnow::prelude::*;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// alt((
|
||||
/// preceded(one_of(['+', '-']), digit1),
|
||||
/// rest
|
||||
/// )).parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("+10 ab"), Ok((" ab", "10")));
|
||||
/// assert_eq!(parser.parse_peek("ab"), Ok(("", "ab")));
|
||||
/// assert_eq!(parser.parse_peek("+"), Ok(("", "+")));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// With `cut_err`:
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::{error::ErrMode, error::ContextError};
|
||||
/// # use winnow::prelude::*;
|
||||
/// # use winnow::token::one_of;
|
||||
/// # use winnow::token::rest;
|
||||
/// # use winnow::ascii::digit1;
|
||||
/// # use winnow::combinator::alt;
|
||||
/// # use winnow::combinator::preceded;
|
||||
/// use winnow::combinator::cut_err;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// alt((
|
||||
/// preceded(one_of(['+', '-']), cut_err(digit1)),
|
||||
/// rest
|
||||
/// )).parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("+10 ab"), Ok((" ab", "10")));
|
||||
/// assert_eq!(parser.parse_peek("ab"), Ok(("", "ab")));
|
||||
/// assert_eq!(parser.parse_peek("+"), Err(ErrMode::Cut(ContextError::new())));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn cut_err<Input, Output, Error, ParseNext>(
|
||||
mut parser: ParseNext,
|
||||
) -> impl Parser<Input, Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input> + ModalError,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
{
|
||||
trace("cut_err", move |input: &mut Input| {
|
||||
parser.parse_next(input).map_err(|e| e.cut())
|
||||
})
|
||||
}
|
||||
|
||||
/// Transforms an [`ErrMode::Cut`][crate::error::ErrMode::Cut] (unrecoverable) to [`ErrMode::Backtrack`][crate::error::ErrMode::Backtrack] (recoverable)
|
||||
///
|
||||
/// This attempts the parse, allowing other parsers to be tried on failure, like with
|
||||
/// [`winnow::combinator::alt`][crate::combinator::alt].
|
||||
pub fn backtrack_err<Input, Output, Error, ParseNext>(
|
||||
mut parser: ParseNext,
|
||||
) -> impl Parser<Input, Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input> + ModalError,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
{
|
||||
trace("backtrack_err", move |input: &mut Input| {
|
||||
parser.parse_next(input).map_err(|e| e.backtrack())
|
||||
})
|
||||
}
|
||||
|
||||
/// A placeholder for a not-yet-implemented [`Parser`]
|
||||
///
|
||||
/// This is analogous to the [`todo!`] macro and helps with prototyping.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// This will panic when parsing
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::prelude::*;
|
||||
/// # use winnow::combinator::todo;
|
||||
///
|
||||
/// fn parser(input: &mut &str) -> ModalResult<u64> {
|
||||
/// todo(input)
|
||||
/// }
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn todo<Input, Output, Error>(input: &mut Input) -> Result<Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
{
|
||||
#![allow(clippy::todo)]
|
||||
trace("todo", move |_input: &mut Input| {
|
||||
todo!("unimplemented parse")
|
||||
})
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
/// Succeed, consuming no input
|
||||
///
|
||||
/// For example, it can be used as the last alternative in `alt` to
|
||||
/// specify the default case.
|
||||
///
|
||||
/// Useful with:
|
||||
/// - [`Parser::value`]
|
||||
/// - [`Parser::default_value`]
|
||||
/// - [`Parser::map`]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Note:** This never advances the [`Stream`]
|
||||
///
|
||||
/// </div>
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::alt;
|
||||
/// use winnow::combinator::empty;
|
||||
///
|
||||
/// fn sign(input: &mut &str) -> ModalResult<isize> {
|
||||
/// alt((
|
||||
/// '-'.value(-1),
|
||||
/// '+'.value(1),
|
||||
/// empty.value(1)
|
||||
/// )).parse_next(input)
|
||||
/// }
|
||||
/// assert_eq!(sign.parse_peek("+10"), Ok(("10", 1)));
|
||||
/// assert_eq!(sign.parse_peek("-10"), Ok(("10", -1)));
|
||||
/// assert_eq!(sign.parse_peek("10"), Ok(("10", 1)));
|
||||
/// ```
|
||||
#[doc(alias = "value")]
|
||||
#[doc(alias = "success")]
|
||||
#[inline]
|
||||
pub fn empty<Input, Error>(_input: &mut Input) -> Result<(), Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A parser which always fails.
|
||||
///
|
||||
/// For example, it can be used as the last alternative in `alt` to
|
||||
/// control the error message given.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::{error::ErrMode, error::InputError};
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::fail;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<(), InputError<&'i str>> {
|
||||
/// fail.parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("string"), Err(ErrMode::Backtrack(InputError::at("string"))));
|
||||
/// ```
|
||||
#[doc(alias = "unexpected")]
|
||||
#[inline]
|
||||
pub fn fail<Input, Output, Error>(i: &mut Input) -> Result<Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
{
|
||||
trace("fail", |i: &mut Input| Err(ParserError::from_input(i))).parse_next(i)
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
#![cfg(feature = "std")]
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use crate::error::ParserError;
|
||||
use crate::stream::Stream;
|
||||
use crate::{Parser, Result};
|
||||
|
||||
pub(crate) struct Trace<P, D, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
D: std::fmt::Display,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
parser: P,
|
||||
name: D,
|
||||
call_count: usize,
|
||||
marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<P, D, I, O, E> Trace<P, D, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
D: std::fmt::Display,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(parser: P, name: D) -> Self {
|
||||
Self {
|
||||
parser,
|
||||
name,
|
||||
call_count: 0,
|
||||
marker: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, D, I, O, E> Parser<I, O, E> for Trace<P, D, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
D: std::fmt::Display,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
|
||||
let depth = Depth::new();
|
||||
let original = i.checkpoint();
|
||||
start(*depth, &self.name, self.call_count, i);
|
||||
|
||||
let res = self.parser.parse_next(i);
|
||||
|
||||
let consumed = i.offset_from(&original);
|
||||
let severity = Severity::with_result(&res);
|
||||
end(*depth, &self.name, self.call_count, consumed, severity);
|
||||
self.call_count += 1;
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Depth {
|
||||
depth: usize,
|
||||
inc: bool,
|
||||
}
|
||||
|
||||
impl Depth {
|
||||
pub(crate) fn new() -> Self {
|
||||
let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
let inc = true;
|
||||
Self { depth, inc }
|
||||
}
|
||||
|
||||
pub(crate) fn existing() -> Self {
|
||||
let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst);
|
||||
let inc = false;
|
||||
Self { depth, inc }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Depth {
|
||||
fn drop(&mut self) {
|
||||
if self.inc {
|
||||
let _ = DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<usize> for Depth {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &usize {
|
||||
&self.depth
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for Depth {
|
||||
type Target = usize;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.depth
|
||||
}
|
||||
}
|
||||
|
||||
static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
||||
|
||||
pub(crate) enum Severity {
|
||||
Success,
|
||||
Backtrack,
|
||||
Cut,
|
||||
Incomplete,
|
||||
}
|
||||
|
||||
impl Severity {
|
||||
pub(crate) fn with_result<T, I: Stream, E: ParserError<I>>(result: &Result<T, E>) -> Self {
|
||||
match result {
|
||||
Ok(_) => Self::Success,
|
||||
Err(e) if e.is_backtrack() => Self::Backtrack,
|
||||
Err(e) if e.is_incomplete() => Self::Incomplete,
|
||||
_ => Self::Cut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start<I: Stream>(
|
||||
depth: usize,
|
||||
name: &dyn core::fmt::Display,
|
||||
count: usize,
|
||||
input: &I,
|
||||
) {
|
||||
let gutter_style = anstyle::Style::new().bold();
|
||||
let input_style = anstyle::Style::new().underline();
|
||||
let eof_style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Cyan.into()));
|
||||
|
||||
let (call_width, input_width) = column_widths();
|
||||
|
||||
let count = if 0 < count {
|
||||
format!(":{count}")
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
let call_column = format!("{:depth$}> {name}{count}", "");
|
||||
|
||||
// The debug version of `slice` might be wider, either due to rendering one byte as two nibbles or
|
||||
// escaping in strings.
|
||||
let mut debug_slice = format!("{:?}", crate::util::from_fn(|f| input.trace(f)));
|
||||
let (debug_slice, eof) = if let Some(debug_offset) = debug_slice
|
||||
.char_indices()
|
||||
.enumerate()
|
||||
.find_map(|(pos, (offset, _))| (input_width <= pos).then_some(offset))
|
||||
{
|
||||
debug_slice.truncate(debug_offset);
|
||||
let eof = "";
|
||||
(debug_slice, eof)
|
||||
} else {
|
||||
let eof = if debug_slice.chars().count() < input_width {
|
||||
"∅"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
(debug_slice, eof)
|
||||
};
|
||||
|
||||
let writer = anstream::stderr();
|
||||
let mut writer = writer.lock();
|
||||
let _ = writeln!(
|
||||
writer,
|
||||
"{call_column:call_width$} {gutter_style}|{gutter_reset} {input_style}{debug_slice}{input_reset}{eof_style}{eof}{eof_reset}",
|
||||
gutter_style=gutter_style.render(),
|
||||
gutter_reset=gutter_style.render_reset(),
|
||||
input_style=input_style.render(),
|
||||
input_reset=input_style.render_reset(),
|
||||
eof_style=eof_style.render(),
|
||||
eof_reset=eof_style.render_reset(),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn end(
|
||||
depth: usize,
|
||||
name: &dyn core::fmt::Display,
|
||||
count: usize,
|
||||
consumed: usize,
|
||||
severity: Severity,
|
||||
) {
|
||||
let gutter_style = anstyle::Style::new().bold();
|
||||
|
||||
let (call_width, _) = column_widths();
|
||||
|
||||
let count = if 0 < count {
|
||||
format!(":{count}")
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
let call_column = format!("{:depth$}< {name}{count}", "");
|
||||
|
||||
let (status_style, status) = match severity {
|
||||
Severity::Success => {
|
||||
let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into()));
|
||||
let status = format!("+{consumed}");
|
||||
(style, status)
|
||||
}
|
||||
Severity::Backtrack => (
|
||||
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
|
||||
"backtrack".to_owned(),
|
||||
),
|
||||
Severity::Cut => (
|
||||
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
|
||||
"cut".to_owned(),
|
||||
),
|
||||
Severity::Incomplete => (
|
||||
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
|
||||
"incomplete".to_owned(),
|
||||
),
|
||||
};
|
||||
|
||||
let writer = anstream::stderr();
|
||||
let mut writer = writer.lock();
|
||||
let _ = writeln!(
|
||||
writer,
|
||||
"{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
|
||||
gutter_style=gutter_style.render(),
|
||||
gutter_reset=gutter_style.render_reset(),
|
||||
status_style=status_style.render(),
|
||||
status_reset=status_style.render_reset(),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn result(depth: usize, name: &dyn core::fmt::Display, severity: Severity) {
|
||||
let gutter_style = anstyle::Style::new().bold();
|
||||
|
||||
let (call_width, _) = column_widths();
|
||||
|
||||
let call_column = format!("{:depth$}| {name}", "");
|
||||
|
||||
let (status_style, status) = match severity {
|
||||
Severity::Success => (
|
||||
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())),
|
||||
"",
|
||||
),
|
||||
Severity::Backtrack => (
|
||||
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
|
||||
"backtrack",
|
||||
),
|
||||
Severity::Cut => (
|
||||
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
|
||||
"cut",
|
||||
),
|
||||
Severity::Incomplete => (
|
||||
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
|
||||
"incomplete",
|
||||
),
|
||||
};
|
||||
|
||||
let writer = anstream::stderr();
|
||||
let mut writer = writer.lock();
|
||||
let _ = writeln!(
|
||||
writer,
|
||||
"{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
|
||||
gutter_style=gutter_style.render(),
|
||||
gutter_reset=gutter_style.render_reset(),
|
||||
status_style=status_style.render(),
|
||||
status_reset=status_style.render_reset(),
|
||||
);
|
||||
}
|
||||
|
||||
fn column_widths() -> (usize, usize) {
|
||||
let term_width = term_width();
|
||||
|
||||
let min_call_width = 40;
|
||||
let min_input_width = 20;
|
||||
let decor_width = 3;
|
||||
let extra_width = term_width.saturating_sub(min_call_width + min_input_width + decor_width);
|
||||
let call_width = min_call_width + 2 * extra_width / 3;
|
||||
let input_width = min_input_width + extra_width / 3;
|
||||
|
||||
(call_width, input_width)
|
||||
}
|
||||
|
||||
fn term_width() -> usize {
|
||||
columns_env().or_else(query_width).unwrap_or(80)
|
||||
}
|
||||
|
||||
fn query_width() -> Option<usize> {
|
||||
use is_terminal_polyfill::IsTerminal;
|
||||
if std::io::stderr().is_terminal() {
|
||||
terminal_size::terminal_size().map(|(w, _h)| w.0.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn columns_env() -> Option<usize> {
|
||||
std::env::var("COLUMNS")
|
||||
.ok()
|
||||
.and_then(|c| c.parse::<usize>().ok())
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
#![cfg_attr(feature = "debug", allow(clippy::std_instead_of_core))]
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mod internals;
|
||||
|
||||
use crate::error::ParserError;
|
||||
use crate::stream::Stream;
|
||||
use crate::Parser;
|
||||
|
||||
/// Trace the execution of the parser
|
||||
///
|
||||
/// Note that [`Parser::context`] also provides high level trace information.
|
||||
///
|
||||
/// See [tutorial][crate::_tutorial::chapter_8] for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::{error::ErrMode, error::Needed};
|
||||
/// # use winnow::token::take_while;
|
||||
/// # use winnow::stream::AsChar;
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::trace;
|
||||
///
|
||||
/// fn short_alpha<'s>(s: &mut &'s [u8]) -> ModalResult<&'s [u8]> {
|
||||
/// trace("short_alpha",
|
||||
/// take_while(3..=6, AsChar::is_alpha)
|
||||
/// ).parse_next(s)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(short_alpha.parse_peek(b"latin123"), Ok((&b"123"[..], &b"latin"[..])));
|
||||
/// assert_eq!(short_alpha.parse_peek(b"lengthy"), Ok((&b"y"[..], &b"length"[..])));
|
||||
/// assert_eq!(short_alpha.parse_peek(b"latin"), Ok((&b""[..], &b"latin"[..])));
|
||||
/// assert!(short_alpha.parse_peek(b"ed").is_err());
|
||||
/// assert!(short_alpha.parse_peek(b"12345").is_err());
|
||||
/// ```
|
||||
#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
|
||||
#[cfg_attr(not(feature = "debug"), allow(unused_mut))]
|
||||
#[cfg_attr(not(feature = "debug"), inline(always))]
|
||||
pub fn trace<I: Stream, O, E: ParserError<I>>(
|
||||
name: impl core::fmt::Display,
|
||||
parser: impl Parser<I, O, E>,
|
||||
) -> impl Parser<I, O, E> {
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
internals::Trace::new(parser, name)
|
||||
}
|
||||
#[cfg(not(feature = "debug"))]
|
||||
{
|
||||
parser
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
|
||||
pub(crate) fn trace_result<T, I: Stream, E: ParserError<I>>(
|
||||
name: impl core::fmt::Display,
|
||||
res: &Result<T, E>,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
let depth = internals::Depth::existing();
|
||||
let severity = internals::Severity::with_result(res);
|
||||
internals::result(*depth, &name, severity);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DisplayDebug<D>(pub(crate) D);
|
||||
|
||||
impl<D: core::fmt::Debug> core::fmt::Display for DisplayDebug<D> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "debug")]
|
||||
fn example() {
|
||||
use term_transcript::{test::TestConfig, ShellOptions};
|
||||
|
||||
let path = snapbox::cmd::compile_example("string", ["--features=debug"]).unwrap();
|
||||
|
||||
let current_dir = path.parent().unwrap();
|
||||
let cmd = path.file_name().unwrap();
|
||||
// HACK: term_transcript doesn't allow non-UTF8 paths
|
||||
let cmd = format!("./{}", cmd.to_string_lossy());
|
||||
|
||||
TestConfig::new(
|
||||
ShellOptions::default()
|
||||
.with_current_dir(current_dir)
|
||||
.with_env("CLICOLOR_FORCE", "1"),
|
||||
)
|
||||
.test("assets/trace.svg", [format!(r#"{cmd} '"abc"'"#).as_str()]);
|
||||
}
|
||||
+561
@@ -0,0 +1,561 @@
|
||||
use crate::combinator::empty;
|
||||
use crate::combinator::fail;
|
||||
use crate::combinator::opt;
|
||||
use crate::combinator::trace;
|
||||
use crate::error::ParserError;
|
||||
use crate::stream::Stream;
|
||||
use crate::stream::StreamIsPartial;
|
||||
use crate::Parser;
|
||||
use crate::Result;
|
||||
|
||||
/// Parses an expression based on operator precedence.
|
||||
///
|
||||
/// It uses a Pratt parsing algorithm, where operators are
|
||||
/// associated with a binding power. The higher the power,
|
||||
/// the more tightly an operator will bind to its operands.
|
||||
///
|
||||
/// This method returns an [`Expression`], which configures
|
||||
/// the Pratt parser.
|
||||
///
|
||||
/// Each operator type is configured with [`Prefix`], [`Postfix`],
|
||||
/// and [`Infix`]. These describe the operator's binding power,
|
||||
/// a function that applies the operator to its operand, and the
|
||||
/// operator's associativity (infix only).
|
||||
///
|
||||
/// For a more full-featured example, look at the [C-style Expressions][crate::_topic::language#c-style-expressions]
|
||||
/// topic.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Parsing a simple arithmetic expression without parenthesis.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "ascii")] {
|
||||
/// # use winnow::prelude::*;
|
||||
/// # use winnow::error::ContextError;
|
||||
/// # use winnow::ascii::digit1;
|
||||
/// # use winnow::combinator::{dispatch, fail};
|
||||
/// # use winnow::token::any;
|
||||
/// use winnow::combinator::expression;
|
||||
/// use winnow::combinator::{Prefix, Postfix, Infix};
|
||||
///
|
||||
/// fn parser<'i>() -> impl Parser<&'i str, i32, ContextError> {
|
||||
/// move |i: &mut &str| {
|
||||
/// use Infix::*;
|
||||
/// expression(digit1.parse_to::<i32>()) // operands are 32-bit integers
|
||||
/// .prefix(dispatch! {any;
|
||||
/// '-' => Prefix(12, |_, a: i32| Ok(-a)),
|
||||
/// _ => fail,
|
||||
/// })
|
||||
/// .infix(dispatch! {any;
|
||||
/// '+' => Left(5, |_, a, b| Ok(a + b)),
|
||||
/// '-' => Left(5, |_, a, b| Ok(a - b)),
|
||||
/// '*' => Left(7, |_, a, b| Ok(a * b)),
|
||||
/// '/' => Left(7, |_, a: i32, b| Ok(a.checked_div(b).unwrap_or_default())),
|
||||
/// _ => fail,
|
||||
/// })
|
||||
/// .postfix(dispatch! {any;
|
||||
/// '!' => Postfix(15, |_, a| if a < 1 { Ok(1) } else { Ok((1..=a).fold(1, |acc, a| acc*a)) }),
|
||||
/// _ => fail,
|
||||
/// })
|
||||
/// .parse_next(i)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser().parse("1+1"), Ok(2));
|
||||
/// assert_eq!(parser().parse("0!"), Ok(1));
|
||||
/// assert_eq!(parser().parse("-1*5*2*10+30/3!"), Ok(-95));
|
||||
/// # }
|
||||
/// ```
|
||||
#[doc(alias = "pratt")]
|
||||
#[doc(alias = "separated")]
|
||||
#[doc(alias = "shunting_yard")]
|
||||
#[doc(alias = "precedence_climbing")]
|
||||
#[inline(always)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn expression<I, ParseOperand, O, E>(
|
||||
parse_operand: ParseOperand,
|
||||
) -> Expression<
|
||||
I,
|
||||
O,
|
||||
ParseOperand,
|
||||
impl Parser<I, Prefix<I, O, E>, E>,
|
||||
impl Parser<I, Postfix<I, O, E>, E>,
|
||||
impl Parser<I, Infix<I, O, E>, E>,
|
||||
E,
|
||||
>
|
||||
where
|
||||
I: Stream + StreamIsPartial,
|
||||
ParseOperand: Parser<I, O, E>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
Expression {
|
||||
precedence_level: 0,
|
||||
parse_operand,
|
||||
parse_prefix: fail,
|
||||
parse_postfix: fail,
|
||||
parse_infix: fail,
|
||||
marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper struct for [`expression()`].
|
||||
///
|
||||
/// Holds the configuration for the Pratt parser, including
|
||||
/// the operator and operand parsers. A precedence level can
|
||||
/// also be set, which is useful to disambiguate parse trees
|
||||
/// based on the parent operator's precedence.
|
||||
///
|
||||
/// Implements [`Parser`]. When parsing an input, it applies
|
||||
/// the Pratt parser.
|
||||
pub struct Expression<I, O, ParseOperand, Pre, Post, Pix, E>
|
||||
where
|
||||
I: Stream + StreamIsPartial,
|
||||
ParseOperand: Parser<I, O, E>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
precedence_level: i64,
|
||||
parse_operand: ParseOperand,
|
||||
parse_prefix: Pre,
|
||||
parse_postfix: Post,
|
||||
parse_infix: Pix,
|
||||
marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<I, O, ParseOperand, Pre, Post, Pix, E> Expression<I, O, ParseOperand, Pre, Post, Pix, E>
|
||||
where
|
||||
ParseOperand: Parser<I, O, E>,
|
||||
I: Stream + StreamIsPartial,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
/// Sets the prefix operator parser.
|
||||
///
|
||||
/// The parser should parse the input to a [`Prefix`],
|
||||
/// which contains the operator's binding power and
|
||||
/// a fold function which applies the operator to its
|
||||
/// operands.
|
||||
#[inline(always)]
|
||||
pub fn prefix<NewParsePrefix>(
|
||||
self,
|
||||
parser: NewParsePrefix,
|
||||
) -> Expression<I, O, ParseOperand, NewParsePrefix, Post, Pix, E>
|
||||
where
|
||||
NewParsePrefix: Parser<I, Prefix<I, O, E>, E>,
|
||||
{
|
||||
Expression {
|
||||
precedence_level: self.precedence_level,
|
||||
parse_operand: self.parse_operand,
|
||||
parse_prefix: parser,
|
||||
parse_postfix: self.parse_postfix,
|
||||
parse_infix: self.parse_infix,
|
||||
marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the postfix operator parser.
|
||||
///
|
||||
/// The parser should parse the input to a [`Postfix`],
|
||||
/// which contains the operator's binding power and
|
||||
/// a fold function which applies the operator to its
|
||||
/// operands.
|
||||
#[inline(always)]
|
||||
pub fn postfix<NewParsePostfix>(
|
||||
self,
|
||||
parser: NewParsePostfix,
|
||||
) -> Expression<I, O, ParseOperand, Pre, NewParsePostfix, Pix, E>
|
||||
where
|
||||
NewParsePostfix: Parser<I, Postfix<I, O, E>, E>,
|
||||
{
|
||||
Expression {
|
||||
precedence_level: self.precedence_level,
|
||||
parse_operand: self.parse_operand,
|
||||
parse_prefix: self.parse_prefix,
|
||||
parse_postfix: parser,
|
||||
parse_infix: self.parse_infix,
|
||||
marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the infix operator parser.
|
||||
///
|
||||
/// The parser should parse the input to a [`Infix`],
|
||||
/// which contains the operator's binding power and
|
||||
/// a fold function which applies the operator to its
|
||||
/// operands.
|
||||
#[inline(always)]
|
||||
pub fn infix<NewParseInfix>(
|
||||
self,
|
||||
parser: NewParseInfix,
|
||||
) -> Expression<I, O, ParseOperand, Pre, Post, NewParseInfix, E>
|
||||
where
|
||||
NewParseInfix: Parser<I, Infix<I, O, E>, E>,
|
||||
{
|
||||
Expression {
|
||||
precedence_level: self.precedence_level,
|
||||
parse_operand: self.parse_operand,
|
||||
parse_prefix: self.parse_prefix,
|
||||
parse_postfix: self.parse_postfix,
|
||||
parse_infix: parser,
|
||||
marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the precedence level for the current instance of the parser.
|
||||
///
|
||||
/// It defaults to 0, which is traditionally treated as the "lowest"
|
||||
/// possible precedence when parsing an expression.
|
||||
///
|
||||
/// This is useful to disambiguate grammars based on the parent operator's
|
||||
/// precedence. This comes up primarily when parsing recursive expressions.
|
||||
///
|
||||
/// The parsing machinery underpinning [`Expression`] assumes that a "more
|
||||
/// tightly binding" operator is numerically large, while a "more loosely
|
||||
/// binding" operator is numerically small. For example, `13` is a higher
|
||||
/// precedence level than `1` because `13 > 1`.
|
||||
///
|
||||
/// Other ways of describing this relationship:
|
||||
/// - `13` has a higher precedence compared to `1`
|
||||
/// - `13` has a higher binding power compared to `1`
|
||||
///
|
||||
/// Note: Binding power and precedence both refer to the same concept and
|
||||
/// may be used interchangeably.
|
||||
///
|
||||
/// # Motivation
|
||||
///
|
||||
/// If you don't understand why this is useful to have, this section tries
|
||||
/// to explain in more detail.
|
||||
///
|
||||
/// The [C-style Expressions][crate::_topic::arithmetic#c-style-expression]
|
||||
/// example has source code for parsing the expression described below, and
|
||||
/// can provide a clearer usage example.
|
||||
///
|
||||
/// Consider the following expression in the C language:
|
||||
///
|
||||
/// ```c
|
||||
/// int x = (1 == 1 ? 0 : 1, -123); // <-- let's parse this
|
||||
/// printf("%d\n", x); // -123
|
||||
/// ```
|
||||
///
|
||||
/// Let's look at the right-hand side of the expression on the first line,
|
||||
/// and replace some of the sub-expressions with symbols:
|
||||
///
|
||||
/// ```text
|
||||
/// (1 == 1 ? 0 : 1, -123) // rhs
|
||||
/// (a ? b : c, d ) // symbolic
|
||||
/// (a ? b : c, d) // remove whitespace
|
||||
/// (, (? a b c) d) // prefix notation
|
||||
/// ```
|
||||
///
|
||||
/// Written symbolically:
|
||||
/// - `a` is the condition, like `1 == 1`
|
||||
/// - `b` is the value when the condition is true
|
||||
/// - `c` is the value when the condition is false
|
||||
/// - `d` is a secondary expression unrelated to the ternary
|
||||
///
|
||||
/// In prefix notation, it's easier to see the specific operators and what
|
||||
/// they bind to:
|
||||
/// - COMMA (`,`) binds to `(? a b c)` and `d`
|
||||
/// - TERNARY (`?`) binds to `a`, `b`, and `c`
|
||||
///
|
||||
/// ## Parsing `c` and `d`
|
||||
///
|
||||
/// Let's focus on parsing the sub-expressions `c` and `d`, as that
|
||||
/// motivates why a parser precedence level is necessary.
|
||||
///
|
||||
/// To parse `c`, we would really like to re-use the parser produced by
|
||||
/// [`expression()`], because `c` is really *any* valid expression that
|
||||
/// can be parsed by `expression()` already.
|
||||
///
|
||||
/// However, we can't re-use the parser naively. When parsing `c`, we need
|
||||
/// to "escape" from the inner parser when encountering the comma separating
|
||||
/// `c` from `d`.
|
||||
///
|
||||
/// The reason we have to "escape" is because of how operator precedence is
|
||||
/// defined in the C language: the comma operator has the lowest precedence
|
||||
/// among all the operators. When we're parsing `c`, we're in the context of
|
||||
/// the ternary operator. We don't want to parse any valid expression! Just
|
||||
/// what the ternary operator captures.
|
||||
///
|
||||
/// That's where the precedence level comes in: you specify the minimum
|
||||
/// precedence this parser is willing to accept. If you come across an
|
||||
/// expression in the top-level with a lower binding power than the starting
|
||||
/// precedence, you know to stop parsing.
|
||||
///
|
||||
/// The parsing machinery inside of [`Expression`] handles most of this for
|
||||
/// you, but it can't determine what the precedence level should be for a
|
||||
/// given expression. That's a language-specific detail, and it depends on
|
||||
/// what you want to parse.
|
||||
#[inline(always)]
|
||||
pub fn current_precedence_level(
|
||||
mut self,
|
||||
level: i64,
|
||||
) -> Expression<I, O, ParseOperand, Pre, Post, Pix, E> {
|
||||
self.precedence_level = level;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, Pop, Pre, Post, Pix, E> Parser<I, O, E> for Expression<I, O, Pop, Pre, Post, Pix, E>
|
||||
where
|
||||
I: Stream + StreamIsPartial,
|
||||
Pop: Parser<I, O, E>,
|
||||
Pix: Parser<I, Infix<I, O, E>, E>,
|
||||
Pre: Parser<I, Prefix<I, O, E>, E>,
|
||||
Post: Parser<I, Postfix<I, O, E>, E>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<O, E> {
|
||||
trace("expression", move |i: &mut I| {
|
||||
expression_impl(
|
||||
i,
|
||||
&mut self.parse_operand,
|
||||
&mut self.parse_prefix,
|
||||
&mut self.parse_postfix,
|
||||
&mut self.parse_infix,
|
||||
self.precedence_level,
|
||||
)
|
||||
})
|
||||
.parse_next(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque implementation of the Pratt parser.
|
||||
fn expression_impl<I, O, Pop, Pre, Post, Pix, E>(
|
||||
i: &mut I,
|
||||
parse_operand: &mut Pop,
|
||||
prefix: &mut Pre,
|
||||
postfix: &mut Post,
|
||||
infix: &mut Pix,
|
||||
min_power: i64,
|
||||
) -> Result<O, E>
|
||||
where
|
||||
I: Stream + StreamIsPartial,
|
||||
Pop: Parser<I, O, E>,
|
||||
Pix: Parser<I, Infix<I, O, E>, E>,
|
||||
Pre: Parser<I, Prefix<I, O, E>, E>,
|
||||
Post: Parser<I, Postfix<I, O, E>, E>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
let operand = opt(trace("operand", parse_operand.by_ref())).parse_next(i)?;
|
||||
let mut operand = if let Some(operand) = operand {
|
||||
operand
|
||||
} else {
|
||||
// Prefix unary operators
|
||||
let len = i.eof_offset();
|
||||
let Prefix(power, fold_prefix) = trace("prefix", prefix.by_ref()).parse_next(i)?;
|
||||
// infinite loop check: the parser must always consume
|
||||
if i.eof_offset() == len {
|
||||
return Err(E::assert(i, "`prefix` parsers must always consume"));
|
||||
}
|
||||
let operand = expression_impl(i, parse_operand, prefix, postfix, infix, power)?;
|
||||
fold_prefix(i, operand)?
|
||||
};
|
||||
|
||||
// A variable to stop the `'parse` loop when `Assoc::Neither` with the same
|
||||
// precedence is encountered e.g. `a == b == c`. `Assoc::Neither` has similar
|
||||
// associativity rules as `Assoc::Left`, but we stop parsing when the next operator
|
||||
// is the same as the current one.
|
||||
let mut prev_op_is_neither = None;
|
||||
'parse: while i.eof_offset() > 0 {
|
||||
// Postfix unary operators
|
||||
let start = i.checkpoint();
|
||||
if let Some(Postfix(power, fold_postfix)) =
|
||||
opt(trace("postfix", postfix.by_ref())).parse_next(i)?
|
||||
{
|
||||
// control precedence over the prefix e.g.:
|
||||
// `--(i++)` or `(--i)++`
|
||||
if power < min_power {
|
||||
i.reset(&start);
|
||||
break 'parse;
|
||||
}
|
||||
operand = fold_postfix(i, operand)?;
|
||||
|
||||
continue 'parse;
|
||||
}
|
||||
|
||||
// Infix binary operators
|
||||
let start = i.checkpoint();
|
||||
let parse_result = opt(trace("infix", infix.by_ref())).parse_next(i)?;
|
||||
if let Some(infix_op) = parse_result {
|
||||
let mut is_neither = None;
|
||||
let (lpower, rpower, fold_infix) = match infix_op {
|
||||
Infix::Right(p, f) => (p, p - 1, f),
|
||||
Infix::Left(p, f) => (p, p + 1, f),
|
||||
Infix::Neither(p, f) => {
|
||||
is_neither = Some(p);
|
||||
(p, p + 1, f)
|
||||
}
|
||||
};
|
||||
if lpower < min_power
|
||||
// MSRV: `is_some_and`
|
||||
|| match prev_op_is_neither {
|
||||
None => false,
|
||||
Some(p) => lpower == p,
|
||||
}
|
||||
{
|
||||
i.reset(&start);
|
||||
break 'parse;
|
||||
}
|
||||
prev_op_is_neither = is_neither;
|
||||
let rhs = expression_impl(i, parse_operand, prefix, postfix, infix, rpower)?;
|
||||
operand = fold_infix(i, operand, rhs)?;
|
||||
|
||||
continue 'parse;
|
||||
}
|
||||
|
||||
break 'parse;
|
||||
}
|
||||
|
||||
Ok(operand)
|
||||
}
|
||||
|
||||
/// Define an [`expression()`]'s prefix operator
|
||||
///
|
||||
/// It requires an operator binding power, as well as a
|
||||
/// fold function which applies the operator.
|
||||
pub struct Prefix<I, O, E>(
|
||||
/// Binding power
|
||||
pub i64,
|
||||
/// Unary operator
|
||||
pub fn(&mut I, O) -> Result<O, E>,
|
||||
);
|
||||
|
||||
impl<I, O, E> Clone for Prefix<I, O, E> {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Self {
|
||||
Prefix(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Stream, O, E: ParserError<I>> Parser<I, Prefix<I, O, E>, E> for Prefix<I, O, E> {
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<Prefix<I, O, E>, E> {
|
||||
empty.value(self.clone()).parse_next(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Define an [`expression()`]'s postfix operator
|
||||
///
|
||||
/// It requires an operator binding power, as well as a
|
||||
/// fold function which applies the operator.
|
||||
pub struct Postfix<I, O, E>(
|
||||
/// Binding power
|
||||
pub i64,
|
||||
/// Unary operator
|
||||
pub fn(&mut I, O) -> Result<O, E>,
|
||||
);
|
||||
|
||||
impl<I, O, E> Clone for Postfix<I, O, E> {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Self {
|
||||
Postfix(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Stream, O, E: ParserError<I>> Parser<I, Postfix<I, O, E>, E> for Postfix<I, O, E> {
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<Postfix<I, O, E>, E> {
|
||||
empty.value(self.clone()).parse_next(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Define an [`expression()`]'s infix operator
|
||||
///
|
||||
/// It requires an operator binding power, as well as a
|
||||
/// fold function which applies the operator.
|
||||
pub enum Infix<I, O, E> {
|
||||
/// Left-associative operator
|
||||
///
|
||||
/// The operators will bind more tightly to their rightmost operands.
|
||||
///
|
||||
/// e.g `A op B op C` -> `(A op B) op C`
|
||||
Left(
|
||||
/// Binding power
|
||||
i64,
|
||||
/// Binary operator
|
||||
fn(&mut I, O, O) -> Result<O, E>,
|
||||
),
|
||||
/// Right-associative operator
|
||||
///
|
||||
/// The operators will bind more tightly to their leftmost operands.
|
||||
///
|
||||
/// e.g `A op B op C` -> `A op (B op C)`
|
||||
Right(
|
||||
/// Binding power
|
||||
i64,
|
||||
/// Binary operator
|
||||
fn(&mut I, O, O) -> Result<O, E>,
|
||||
),
|
||||
/// Neither left or right associative
|
||||
///
|
||||
/// `Infix::Neither` has similar associativity rules as `Assoc::Left`, but we stop
|
||||
/// parsing when the next operator is the same as the current one.
|
||||
///
|
||||
/// e.g. `a == b == c` -> `(a == b)`, fail: `(== c)`
|
||||
Neither(
|
||||
/// Binding power
|
||||
i64,
|
||||
/// Binary operator
|
||||
fn(&mut I, O, O) -> Result<O, E>,
|
||||
),
|
||||
}
|
||||
|
||||
impl<I, O, E> Clone for Infix<I, O, E> {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Infix::Left(p, f) => Infix::Left(*p, *f),
|
||||
Infix::Right(p, f) => Infix::Right(*p, *f),
|
||||
Infix::Neither(p, f) => Infix::Neither(*p, *f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Stream, O, E: ParserError<I>> Parser<I, Infix<I, O, E>, E> for Infix<I, O, E> {
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<Infix<I, O, E>, E> {
|
||||
empty.value(self.clone()).parse_next(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "ascii"))]
|
||||
mod tests {
|
||||
use crate::ascii::digit1;
|
||||
use crate::combinator::fail;
|
||||
use crate::dispatch;
|
||||
use crate::error::ContextError;
|
||||
use crate::token::any;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn parser<'i>() -> impl Parser<&'i str, i32, ContextError> {
|
||||
move |i: &mut &str| {
|
||||
use Infix::*;
|
||||
expression(digit1.parse_to::<i32>())
|
||||
.current_precedence_level(0)
|
||||
.prefix(dispatch! {any;
|
||||
'+' => Prefix(12, |_, a| Ok(a)),
|
||||
'-' => Prefix(12, |_, a: i32| Ok(-a)),
|
||||
_ => fail
|
||||
})
|
||||
.infix(dispatch! {any;
|
||||
'+' => Left(5, |_, a, b| Ok(a + b)),
|
||||
'-' => Left(5, |_, a, b| Ok(a - b)),
|
||||
'*' => Left(7, |_, a, b| Ok(a * b)),
|
||||
'/' => Left(7, |_, a, b| Ok(a / b)),
|
||||
'%' => Left(7, |_, a, b| Ok(a % b)),
|
||||
'^' => Left(9, |_, a, b| Ok(a ^ b)),
|
||||
_ => fail
|
||||
})
|
||||
.parse_next(i)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression() {
|
||||
assert_eq!(parser().parse("-3+-3*4"), Ok(-15));
|
||||
assert_eq!(parser().parse("+2+3*4"), Ok(14));
|
||||
assert_eq!(parser().parse("2*3+4"), Ok(10));
|
||||
}
|
||||
}
|
||||
+794
@@ -0,0 +1,794 @@
|
||||
//! Opaque implementations of [`Parser`]
|
||||
|
||||
use crate::combinator::trace;
|
||||
use crate::combinator::trace_result;
|
||||
use crate::combinator::DisplayDebug;
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
use crate::error::FromRecoverableError;
|
||||
use crate::error::ParseError;
|
||||
use crate::error::{AddContext, FromExternalError, ParserError};
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
use crate::stream::Recover;
|
||||
use crate::stream::StreamIsPartial;
|
||||
use crate::stream::{Location, Stream};
|
||||
use crate::{Parser, Result};
|
||||
use core::borrow::Borrow;
|
||||
use core::ops::Range;
|
||||
|
||||
/// Iterator implementation for [`Parser::parse_iter`]
|
||||
pub struct ParseIter<'p, P, I, O, E>
|
||||
where
|
||||
I: Stream,
|
||||
{
|
||||
pub(crate) parser: &'p mut P,
|
||||
pub(crate) input: Option<I>,
|
||||
pub(crate) start: Option<I::Checkpoint>,
|
||||
pub(crate) marker: core::marker::PhantomData<(O, E)>,
|
||||
}
|
||||
|
||||
impl<'p, I, O, E, P> Iterator for ParseIter<'p, P, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
// Force users to deal with `Incomplete` when `StreamIsPartial<true>`
|
||||
I: StreamIsPartial,
|
||||
E: ParserError<I>,
|
||||
<E as ParserError<I>>::Inner: ParserError<I>,
|
||||
{
|
||||
type Item = Result<O, ParseError<I, <E as ParserError<I>>::Inner>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let input = self.input.as_mut()?;
|
||||
let len = input.eof_offset();
|
||||
if len == 0 {
|
||||
self.input = None;
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut output = self.parser.parse_next(input);
|
||||
// infinite loop check: the parser must always consume
|
||||
if output.is_ok() && input.eof_offset() == len {
|
||||
let err = <E as ParserError<I>>::assert(
|
||||
input,
|
||||
"`Parser::parse_iter` parsers must always consume",
|
||||
);
|
||||
output = Err(err);
|
||||
}
|
||||
|
||||
match output {
|
||||
Ok(output) => Some(Ok(output)),
|
||||
Err(err) => {
|
||||
let err = err.into_inner().unwrap_or_else(|_err| {
|
||||
panic!("complete parsers should not report `ErrMode::Incomplete(_)`")
|
||||
});
|
||||
let input = self.input.take()?;
|
||||
let start = self.start.take()?;
|
||||
Some(Err(ParseError::new(input, start, err)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::by_ref`]
|
||||
pub struct ByRef<'p, P, I, O, E> {
|
||||
pub(crate) p: &'p mut P,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<I, O, E, P> Parser<I, O, E> for ByRef<'_, P, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
|
||||
self.p.parse_next(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::map`]
|
||||
pub struct Map<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> O2,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) map: G,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E, O2)>,
|
||||
}
|
||||
|
||||
impl<F, G, I, O, O2, E> Parser<I, O2, E> for Map<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> O2,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
|
||||
match self.parser.parse_next(i) {
|
||||
Err(e) => Err(e),
|
||||
Ok(o) => Ok((self.map)(o)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::try_map`]
|
||||
pub struct TryMap<F, G, I, O, O2, E, E2>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> Result<O2, E2>,
|
||||
I: Stream,
|
||||
E: FromExternalError<I, E2>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) map: G,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E, E2)>,
|
||||
}
|
||||
|
||||
impl<F, G, I, O, O2, E, E2> Parser<I, O2, E> for TryMap<F, G, I, O, O2, E, E2>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> Result<O2, E2>,
|
||||
I: Stream,
|
||||
E: FromExternalError<I, E2>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
|
||||
let start = input.checkpoint();
|
||||
let o = self.parser.parse_next(input)?;
|
||||
let res = (self.map)(o).map_err(|err| {
|
||||
input.reset(&start);
|
||||
E::from_external_error(input, err)
|
||||
});
|
||||
trace_result("verify", &res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::verify_map`]
|
||||
pub struct VerifyMap<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> Option<O2>,
|
||||
I: Stream,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) map: G,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E, O2)>,
|
||||
}
|
||||
|
||||
impl<F, G, I, O, O2, E> Parser<I, O2, E> for VerifyMap<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> Option<O2>,
|
||||
I: Stream,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
|
||||
let start = input.checkpoint();
|
||||
let o = self.parser.parse_next(input)?;
|
||||
let res = (self.map)(o).ok_or_else(|| {
|
||||
input.reset(&start);
|
||||
ParserError::from_input(input)
|
||||
});
|
||||
trace_result("verify", &res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::and_then`]
|
||||
pub struct AndThen<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: Parser<O, O2, E>,
|
||||
O: StreamIsPartial,
|
||||
I: Stream,
|
||||
{
|
||||
pub(crate) outer: F,
|
||||
pub(crate) inner: G,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E)>,
|
||||
}
|
||||
|
||||
impl<F, G, I, O, O2, E> Parser<I, O2, E> for AndThen<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: Parser<O, O2, E>,
|
||||
O: StreamIsPartial,
|
||||
I: Stream,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
|
||||
let start = i.checkpoint();
|
||||
let mut o = self.outer.parse_next(i)?;
|
||||
let _ = o.complete();
|
||||
let o2 = self.inner.parse_next(&mut o).map_err(|err| {
|
||||
i.reset(&start);
|
||||
err
|
||||
})?;
|
||||
Ok(o2)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::parse_to`]
|
||||
pub struct ParseTo<P, I, O, O2, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
O: crate::stream::ParseSlice<O2>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
pub(crate) p: P,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E)>,
|
||||
}
|
||||
|
||||
impl<P, I, O, O2, E> Parser<I, O2, E> for ParseTo<P, I, O, O2, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
O: crate::stream::ParseSlice<O2>,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
|
||||
let start = i.checkpoint();
|
||||
let o = self.p.parse_next(i)?;
|
||||
let res = o.parse_slice().ok_or_else(|| {
|
||||
i.reset(&start);
|
||||
ParserError::from_input(i)
|
||||
});
|
||||
trace_result("verify", &res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::flat_map`]
|
||||
pub struct FlatMap<F, G, H, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> H,
|
||||
H: Parser<I, O2, E>,
|
||||
{
|
||||
pub(crate) f: F,
|
||||
pub(crate) g: G,
|
||||
pub(crate) marker: core::marker::PhantomData<(H, I, O, O2, E)>,
|
||||
}
|
||||
|
||||
impl<F, G, H, I, O, O2, E> Parser<I, O2, E> for FlatMap<F, G, H, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(O) -> H,
|
||||
H: Parser<I, O2, E>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
|
||||
let o = self.f.parse_next(i)?;
|
||||
(self.g)(o).parse_next(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::complete_err`]
|
||||
pub struct CompleteErr<P, I, O, E> {
|
||||
pub(crate) p: P,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<P, I, O, E> Parser<I, O, E> for CompleteErr<P, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<O, E> {
|
||||
trace("complete_err", |input: &mut I| {
|
||||
match (self.p).parse_next(input) {
|
||||
Err(err) => match err.needed() {
|
||||
Some(_) => Err(ParserError::from_input(input)),
|
||||
None => Err(err),
|
||||
},
|
||||
rest => rest,
|
||||
}
|
||||
})
|
||||
.parse_next(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::verify`]
|
||||
pub struct Verify<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(&O2) -> bool,
|
||||
I: Stream,
|
||||
O: Borrow<O2>,
|
||||
O2: ?Sized,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) filter: G,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E, O2)>,
|
||||
}
|
||||
|
||||
impl<F, G, I, O, O2, E> Parser<I, O, E> for Verify<F, G, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(&O2) -> bool,
|
||||
I: Stream,
|
||||
O: Borrow<O2>,
|
||||
O2: ?Sized,
|
||||
E: ParserError<I>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<O, E> {
|
||||
let start = input.checkpoint();
|
||||
let o = self.parser.parse_next(input)?;
|
||||
let res = (self.filter)(o.borrow()).then_some(o).ok_or_else(|| {
|
||||
input.reset(&start);
|
||||
ParserError::from_input(input)
|
||||
});
|
||||
trace_result("verify", &res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::value`]
|
||||
pub struct Value<F, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
O2: Clone,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) val: O2,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, O2, E> Parser<I, O2, E> for Value<F, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
O2: Clone,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
|
||||
(self.parser).parse_next(input).map(|_| self.val.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::default_value`]
|
||||
pub struct DefaultValue<F, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
O2: core::default::Default,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(O2, I, O, E)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, O2, E> Parser<I, O2, E> for DefaultValue<F, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
O2: core::default::Default,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
|
||||
(self.parser).parse_next(input).map(|_| O2::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::void`]
|
||||
pub struct Void<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, E> Parser<I, (), E> for Void<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<(), E> {
|
||||
(self.parser).parse_next(input).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::take`]
|
||||
pub struct Take<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<I, O, E, F> Parser<I, <I as Stream>::Slice, E> for Take<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<<I as Stream>::Slice, E> {
|
||||
let checkpoint = input.checkpoint();
|
||||
match (self.parser).parse_next(input) {
|
||||
Ok(_) => {
|
||||
let offset = input.offset_from(&checkpoint);
|
||||
input.reset(&checkpoint);
|
||||
let taken = input.next_slice(offset);
|
||||
Ok(taken)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::with_taken`]
|
||||
pub struct WithTaken<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, E> Parser<I, (O, <I as Stream>::Slice), E> for WithTaken<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<(O, <I as Stream>::Slice), E> {
|
||||
let checkpoint = input.checkpoint();
|
||||
match (self.parser).parse_next(input) {
|
||||
Ok(result) => {
|
||||
let offset = input.offset_from(&checkpoint);
|
||||
input.reset(&checkpoint);
|
||||
let taken = input.next_slice(offset);
|
||||
Ok((result, taken))
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::span`]
|
||||
pub struct Span<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream + Location,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<I, O, E, F> Parser<I, Range<usize>, E> for Span<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream + Location,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<Range<usize>, E> {
|
||||
let start = input.current_token_start();
|
||||
self.parser.parse_next(input).map(move |_| {
|
||||
let end = input.previous_token_end();
|
||||
start..end
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::with_span`]
|
||||
pub struct WithSpan<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream + Location,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, E> Parser<I, (O, Range<usize>), E> for WithSpan<F, I, O, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream + Location,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, input: &mut I) -> Result<(O, Range<usize>), E> {
|
||||
let start = input.current_token_start();
|
||||
self.parser.parse_next(input).map(move |output| {
|
||||
let end = input.previous_token_end();
|
||||
(output, (start..end))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::output_into`]
|
||||
pub struct OutputInto<F, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
O: Into<O2>,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, O2, E> Parser<I, O2, E> for OutputInto<F, I, O, O2, E>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
O: Into<O2>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
|
||||
self.parser.parse_next(i).map(|o| o.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::err_into`]
|
||||
pub struct ErrInto<F, I, O, E, E2>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
E: Into<E2>,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E, E2)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, E, E2> Parser<I, O, E2> for ErrInto<F, I, O, E, E2>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
E: Into<E2>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O, E2> {
|
||||
self.parser.parse_next(i).map_err(|err| err.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::context`]
|
||||
pub struct Context<F, I, O, E, C>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
E: AddContext<I, C>,
|
||||
E: ParserError<I>,
|
||||
C: Clone + core::fmt::Debug,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) context: C,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
impl<F, I, O, E, C> Parser<I, O, E> for Context<F, I, O, E, C>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
E: AddContext<I, C>,
|
||||
E: ParserError<I>,
|
||||
C: Clone + core::fmt::Debug,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
|
||||
let context = self.context.clone();
|
||||
trace(DisplayDebug(self.context.clone()), move |i: &mut I| {
|
||||
let start = i.checkpoint();
|
||||
(self.parser)
|
||||
.parse_next(i)
|
||||
.map_err(|err| err.add_context(i, &start, context.clone()))
|
||||
})
|
||||
.parse_next(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::context`]
|
||||
pub struct ContextWith<P, I, O, E, F, C, FI>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
E: AddContext<I, C>,
|
||||
E: ParserError<I>,
|
||||
F: Fn() -> FI + Clone,
|
||||
C: core::fmt::Debug,
|
||||
FI: Iterator<Item = C>,
|
||||
{
|
||||
pub(crate) parser: P,
|
||||
pub(crate) context: F,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E, C, FI)>,
|
||||
}
|
||||
|
||||
impl<P, I, O, E, F, C, FI> Parser<I, O, E> for ContextWith<P, I, O, E, F, C, FI>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
I: Stream,
|
||||
E: AddContext<I, C>,
|
||||
E: ParserError<I>,
|
||||
F: Fn() -> FI + Clone,
|
||||
C: core::fmt::Debug,
|
||||
FI: Iterator<Item = C>,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
|
||||
let context = self.context.clone();
|
||||
let start = i.checkpoint();
|
||||
(self.parser).parse_next(i).map_err(|mut err| {
|
||||
for context in context() {
|
||||
err = err.add_context(i, &start, context);
|
||||
}
|
||||
err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::map_err`]
|
||||
pub struct MapErr<F, G, I, O, E, E2>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(E) -> E2,
|
||||
{
|
||||
pub(crate) parser: F,
|
||||
pub(crate) map: G,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E, E2)>,
|
||||
}
|
||||
|
||||
impl<F, G, I, O, E, E2> Parser<I, O, E2> for MapErr<F, G, I, O, E, E2>
|
||||
where
|
||||
F: Parser<I, O, E>,
|
||||
G: FnMut(E) -> E2,
|
||||
{
|
||||
#[inline]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O, E2> {
|
||||
match self.parser.parse_next(i) {
|
||||
Err(e) => Err((self.map)(e)),
|
||||
Ok(o) => Ok(o),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::retry_after`]
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
pub struct RetryAfter<P, R, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
R: Parser<I, (), E>,
|
||||
I: Stream,
|
||||
I: Recover<E>,
|
||||
E: ParserError<I> + FromRecoverableError<I, E>,
|
||||
{
|
||||
pub(crate) parser: P,
|
||||
pub(crate) recover: R,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
impl<P, R, I, O, E> Parser<I, O, E> for RetryAfter<P, R, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
R: Parser<I, (), E>,
|
||||
I: Stream,
|
||||
I: Recover<E>,
|
||||
E: ParserError<I> + FromRecoverableError<I, E>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
|
||||
if I::is_recovery_supported() {
|
||||
retry_after_inner(&mut self.parser, &mut self.recover, i)
|
||||
} else {
|
||||
self.parser.parse_next(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
fn retry_after_inner<P, R, I, O, E>(parser: &mut P, recover: &mut R, i: &mut I) -> Result<O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
R: Parser<I, (), E>,
|
||||
I: Stream,
|
||||
I: Recover<E>,
|
||||
E: ParserError<I> + FromRecoverableError<I, E>,
|
||||
{
|
||||
loop {
|
||||
let token_start = i.checkpoint();
|
||||
let mut err = match parser.parse_next(i) {
|
||||
Ok(o) => {
|
||||
return Ok(o);
|
||||
}
|
||||
Err(e) if e.is_incomplete() => return Err(e),
|
||||
Err(err) => err,
|
||||
};
|
||||
let err_start = i.checkpoint();
|
||||
let err_start_eof_offset = i.eof_offset();
|
||||
if recover.parse_next(i).is_ok() {
|
||||
let i_eof_offset = i.eof_offset();
|
||||
if err_start_eof_offset == i_eof_offset {
|
||||
// Didn't advance so bubble the error up
|
||||
} else if let Err(err_) = i.record_err(&token_start, &err_start, err) {
|
||||
err = err_;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
i.reset(&err_start);
|
||||
err = E::from_recoverable_error(&token_start, &err_start, i, err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Parser`] implementation for [`Parser::resume_after`]
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
pub struct ResumeAfter<P, R, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
R: Parser<I, (), E>,
|
||||
I: Stream,
|
||||
I: Recover<E>,
|
||||
E: ParserError<I> + FromRecoverableError<I, E>,
|
||||
{
|
||||
pub(crate) parser: P,
|
||||
pub(crate) recover: R,
|
||||
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
impl<P, R, I, O, E> Parser<I, Option<O>, E> for ResumeAfter<P, R, I, O, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
R: Parser<I, (), E>,
|
||||
I: Stream,
|
||||
I: Recover<E>,
|
||||
E: ParserError<I> + FromRecoverableError<I, E>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn parse_next(&mut self, i: &mut I) -> Result<Option<O>, E> {
|
||||
if I::is_recovery_supported() {
|
||||
resume_after_inner(&mut self.parser, &mut self.recover, i)
|
||||
} else {
|
||||
self.parser.parse_next(i).map(Some)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-recover")]
|
||||
#[cfg(feature = "std")]
|
||||
fn resume_after_inner<P, R, I, O, E>(
|
||||
parser: &mut P,
|
||||
recover: &mut R,
|
||||
i: &mut I,
|
||||
) -> Result<Option<O>, E>
|
||||
where
|
||||
P: Parser<I, O, E>,
|
||||
R: Parser<I, (), E>,
|
||||
I: Stream,
|
||||
I: Recover<E>,
|
||||
E: ParserError<I> + FromRecoverableError<I, E>,
|
||||
{
|
||||
let token_start = i.checkpoint();
|
||||
let mut err = match parser.parse_next(i) {
|
||||
Ok(o) => {
|
||||
return Ok(Some(o));
|
||||
}
|
||||
Err(e) if e.is_incomplete() => return Err(e),
|
||||
Err(err) => err,
|
||||
};
|
||||
let err_start = i.checkpoint();
|
||||
if recover.parse_next(i).is_ok() {
|
||||
if let Err(err_) = i.record_err(&token_start, &err_start, err) {
|
||||
err = err_;
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
i.reset(&err_start);
|
||||
err = FromRecoverableError::from_recoverable_error(&token_start, &err_start, i, err);
|
||||
Err(err)
|
||||
}
|
||||
Vendored
+189
@@ -0,0 +1,189 @@
|
||||
//! # List of parsers and combinators
|
||||
//!
|
||||
//! <div class="warning">
|
||||
//!
|
||||
//! **Note**: this list is meant to provide a nicer way to find a parser than reading through the documentation on docs.rs. Function combinators are organized in module so they are a bit easier to find.
|
||||
//!
|
||||
//! </div>
|
||||
//!
|
||||
//! ## Basic elements
|
||||
//!
|
||||
//! Those are used to take a series of tokens for the lowest level elements of your grammar, like, "here is a dot", or "here is an big endian integer".
|
||||
//!
|
||||
//! | combinator | usage | input | new input | output | comment |
|
||||
//! |---|---|---|---|---|---|
|
||||
//! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` | `"abc"` | `"bc"` | `Ok('a')` |Matches one of the provided [set of tokens][crate::stream::ContainsToken] (works with non ASCII characters too)|
|
||||
//! | [`none_of`][crate::token::none_of] | `none_of(['a', 'b', 'c'])` | `"xyab"` | `"yab"` | `Ok('x')` |Matches anything but one of the provided [set of tokens][crate::stream::ContainsToken]|
|
||||
//! | [`literal`][crate::token::literal] | `"hello"` | `"hello world"` | `" world"` | `Ok("hello")` |Recognizes a specific suite of characters or bytes (see also [`Caseless`][crate::ascii::Caseless])|
|
||||
//! | [`take`][crate::token::take] | `take(4)` | `"hello"` | `"o"` | `Ok("hell")` |Takes a specific number of bytes or characters|
|
||||
//! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` | `"abc123"` | `"123"` | `Ok("abc")` |Returns the longest slice of bytes or characters for which the provided [set of tokens][crate::stream::ContainsToken] matches.|
|
||||
//! | [`take_till`][crate::token::take_till] | `take_till(0.., is_alphabetic)` | `"123abc"` | `"abc"` | `Ok("123")` |Returns a slice of bytes or characters until the provided [set of tokens][crate::stream::ContainsToken] matches. This is the reverse behaviour from `take_while`: `take_till(f)` is equivalent to `take_while(0.., \|c\| !f(c))`|
|
||||
//! | [`take_until`][crate::token::take_until] | `take_until(0.., "world")` | `"Hello world"` | `"world"` | `Ok("Hello ")` |Returns a slice of bytes or characters until the provided [literal][crate::token::literal] is found.|
|
||||
//!
|
||||
//! ## Choice combinators
|
||||
//!
|
||||
//! | combinator | usage | input | new input | output | comment |
|
||||
//! |---|---|---|---|---|---|
|
||||
//! | [`alt`] | `alt(("ab", "cd"))` | `"cdef"` | `"ef"` | `Ok("cd")` |Try a list of parsers and return the result of the first successful one|
|
||||
//! | [`dispatch`] | \- | \- | \- | \- | `match` for parsers |
|
||||
//!
|
||||
//! ## Sequence combinators
|
||||
//!
|
||||
//! | combinator | usage | input | new input | output | comment |
|
||||
//! |---|---|---|---|---|---|
|
||||
//! | [`(...)` (tuples)][crate::Parser] | `("ab", "XY", take(1))` | `"abXYZ!"` | `"!"` | `Ok(("ab", "XY", "Z"))` |Parse a series of values|
|
||||
//! | [`seq!`] | `seq!(_: '(', take(2), _: ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse a series of values, discarding those you specify|
|
||||
//! | [`delimited`] | `delimited('(', take(2), ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse three values, discarding the first and third value|
|
||||
//! | [`preceded`] | `preceded("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("XY")` |Parse two values, discarding the first value|
|
||||
//! | [`terminated`] | `terminated("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("ab")` |Parse two values, discarding the second value|
|
||||
//! | [`separated_pair`] | `separated_pair("hello", ',', "world")` | `"hello,world!"` | `"!"` | `Ok(("hello", "world"))` | Parse three values, discarding the middle value|
|
||||
//!
|
||||
//! ## Applying a parser multiple times
|
||||
//!
|
||||
//! | combinator | usage | input | new input | output | comment |
|
||||
//! |---|---|---|---|---|---|
|
||||
//! | [`repeat`] | `repeat(1..=3, "ab")` | `"ababc"` | `"c"` | `Ok(vec!["ab", "ab"])` |Applies the parser between m and n times (n included) and returns the list of results in a Vec|
|
||||
//! | [`repeat_till`] | `repeat_till(0.., "ab", "ef")` | `"ababefg"` | `"g"` | `Ok((vec!["ab", "ab"], "ef"))` |Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second|
|
||||
//! | [`separated`] | `separated(1..=3, "ab", ",")` | `"ab,ab,ab."` | `"."` | `Ok(vec!["ab", "ab", "ab"])` |Applies the parser and separator between m and n times (n included) and returns the list of results in a Vec|
|
||||
//! | [`Repeat::fold`] | <code>repeat(1..=2, `be_u8`).fold(\|\| 0, \|acc, item\| acc + item)</code> | `[1, 2, 3]` | `[3]` | `Ok(3)` |Applies the parser between m and n times (n included) and folds the list of return value|
|
||||
//!
|
||||
//! ## Partial related
|
||||
//!
|
||||
//! - [`eof`]: Returns its input if it is at the end of input data
|
||||
//! - [`Parser::complete_err`]: Replaces an `Incomplete` returned by the child parser with an `Backtrack`
|
||||
//!
|
||||
//! ## Modifiers
|
||||
//!
|
||||
//! - [`cond`]: Conditional combinator. Wraps another parser and calls it if the condition is met
|
||||
//! - [`Parser::flat_map`]: method to map a new parser from the output of the first parser, then apply that parser over the rest of the input
|
||||
//! - [`Parser::value`]: method to replace the result of a parser
|
||||
//! - [`Parser::default_value`]: method to replace the result of a parser
|
||||
//! - [`Parser::void`]: method to discard the result of a parser
|
||||
//! - [`Parser::map`]: method to map a function on the result of a parser
|
||||
//! - [`Parser::and_then`]: Applies a second parser over the output of the first one
|
||||
//! - [`Parser::verify_map`]: Maps a function returning an `Option` on the output of a parser
|
||||
//! - [`Parser::try_map`]: Maps a function returning a `Result` on the output of a parser
|
||||
//! - [`Parser::parse_to`]: Apply [`std::str::FromStr`] to the output of the parser
|
||||
//! - [`not`]: Returns a result only if the embedded parser returns `Backtrack` or `Incomplete`. Does not consume the input
|
||||
//! - [`opt`]: Make the underlying parser optional
|
||||
//! - [`peek`]: Returns a result without consuming the input
|
||||
//! - [`Parser::take`]: If the child parser was successful, return the consumed input as the produced value
|
||||
//! - [`Parser::with_taken`]: If the child parser was successful, return a tuple of the consumed input and the produced output.
|
||||
//! - [`Parser::span`]: If the child parser was successful, return the location of the consumed input as the produced value
|
||||
//! - [`Parser::with_span`]: If the child parser was successful, return a tuple of the location of the consumed input and the produced output.
|
||||
//! - [`Parser::verify`]: Returns the result of the child parser if it satisfies a verification function
|
||||
//!
|
||||
//! ## Error management and debugging
|
||||
//!
|
||||
//! - [`cut_err`]: Commit the parse result, disallowing alternative parsers from being attempted
|
||||
//! - [`backtrack_err`]: Attempts a parse, allowing alternative parsers to be attempted despite
|
||||
//! use of `cut_err`
|
||||
//! - [`Parser::context`]: Add context to the error if the parser fails
|
||||
//! - [`trace`]: Print the parse state with the `debug` feature flag
|
||||
//! - [`todo()`]: Placeholder parser
|
||||
//!
|
||||
//! ## Remaining combinators
|
||||
//!
|
||||
//! - [`empty`]: Succeed, consuming no input
|
||||
//! - [`fail`]: Inversion of [`empty`]. Always fails.
|
||||
//! - [`Parser::by_ref`]: Allow moving `&mut impl Parser` into other parsers
|
||||
//!
|
||||
//! ## Text parsing
|
||||
//!
|
||||
//! - [`any`][crate::token::any]: Matches one token
|
||||
//! - [`tab`][crate::ascii::tab]: Matches a tab character `\t`
|
||||
//! - [`crlf`][crate::ascii::crlf]: Recognizes the string `\r\n`
|
||||
//! - [`line_ending`][crate::ascii::line_ending]: Recognizes an end of line (both `\n` and `\r\n`)
|
||||
//! - [`newline`][crate::ascii::newline]: Matches a newline character `\n`
|
||||
//! - [`till_line_ending`][crate::ascii::till_line_ending]: Recognizes a string of any char except `\r` or `\n`
|
||||
//! - [`rest`][crate::token::rest]: Return the remaining input
|
||||
//!
|
||||
//! - [`alpha0`][crate::ascii::alpha0]: Recognizes zero or more lowercase and uppercase alphabetic characters: `[a-zA-Z]`. [`alpha1`][crate::ascii::alpha1] does the same but returns at least one character
|
||||
//! - [`alphanumeric0`][crate::ascii::alphanumeric0]: Recognizes zero or more numerical and alphabetic characters: `[0-9a-zA-Z]`. [`alphanumeric1`][crate::ascii::alphanumeric1] does the same but returns at least one character
|
||||
//! - [`space0`][crate::ascii::space0]: Recognizes zero or more spaces and tabs. [`space1`][crate::ascii::space1] does the same but returns at least one character
|
||||
//! - [`multispace0`][crate::ascii::multispace0]: Recognizes zero or more spaces, tabs, carriage returns and line feeds. [`multispace1`][crate::ascii::multispace1] does the same but returns at least one character
|
||||
//! - [`digit0`][crate::ascii::digit0]: Recognizes zero or more numerical characters: `[0-9]`. [`digit1`][crate::ascii::digit1] does the same but returns at least one character
|
||||
//! - [`hex_digit0`][crate::ascii::hex_digit0]: Recognizes zero or more hexadecimal numerical characters: `[0-9A-Fa-f]`. [`hex_digit1`][crate::ascii::hex_digit1] does the same but returns at least one character
|
||||
//! - [`oct_digit0`][crate::ascii::oct_digit0]: Recognizes zero or more octal characters: `[0-7]`. [`oct_digit1`][crate::ascii::oct_digit1] does the same but returns at least one character
|
||||
//!
|
||||
//! - [`float`][crate::ascii::float]: Parse a floating point number in a byte string
|
||||
//! - [`dec_int`][crate::ascii::dec_int]: Decode a variable-width, decimal signed integer
|
||||
//! - [`dec_uint`][crate::ascii::dec_uint]: Decode a variable-width, decimal unsigned integer
|
||||
//! - [`hex_uint`][crate::ascii::hex_uint]: Decode a variable-width, hexadecimal integer
|
||||
//!
|
||||
//! - [`take_escaped`][crate::ascii::take_escaped]: Recognize the input slice with escaped characters
|
||||
//! - [`escaped`][crate::ascii::escaped]: Parse escaped characters, unescaping them
|
||||
//!
|
||||
//! - [`expression()`]: Parse an operator precedence expression with Pratt parsing
|
||||
//!
|
||||
//! ### Character test functions
|
||||
//!
|
||||
//! Use these functions with a combinator like `take_while`:
|
||||
//!
|
||||
//! - [`AsChar::is_alpha`][crate::stream::AsChar::is_alpha]: Tests if byte is ASCII alphabetic: `[A-Za-z]`
|
||||
//! - [`AsChar::is_alphanum`][crate::stream::AsChar::is_alphanum]: Tests if byte is ASCII alphanumeric: `[A-Za-z0-9]`
|
||||
//! - [`AsChar::is_dec_digit`][crate::stream::AsChar::is_dec_digit]: Tests if byte is ASCII digit: `[0-9]`
|
||||
//! - [`AsChar::is_hex_digit`][crate::stream::AsChar::is_hex_digit]: Tests if byte is ASCII hex digit: `[0-9A-Fa-f]`
|
||||
//! - [`AsChar::is_oct_digit`][crate::stream::AsChar::is_oct_digit]: Tests if byte is ASCII octal digit: `[0-7]`
|
||||
//! - [`AsChar::is_space`][crate::stream::AsChar::is_space]: Tests if byte is ASCII space or tab: `[ \t]`
|
||||
//! - [`AsChar::is_newline`][crate::stream::AsChar::is_newline]: Tests if byte is ASCII newline: `[\n]`
|
||||
//!
|
||||
//! ## Binary format parsing
|
||||
//!
|
||||
//! - [`length_repeat`][crate::binary::length_repeat] Gets a number from the first parser, then applies the second parser that many times
|
||||
//! - [`length_take`][crate::binary::length_take]: Gets a number from the first parser, then takes a subslice of the input of that size, and returns that subslice
|
||||
//! - [`length_and_then`][crate::binary::length_and_then]: Gets a number from the first parser, takes a subslice of the input of that size, then applies the second parser on that subslice. If the second parser returns `Incomplete`, `length_value` will return an error
|
||||
//!
|
||||
//! ### Integers
|
||||
//!
|
||||
//! Parsing integers from binary formats can be done in two ways: With parser functions, or combinators with configurable endianness.
|
||||
//!
|
||||
//! - **configurable endianness:** [`i16`][crate::binary::i16], [`i32`][crate::binary::i32],
|
||||
//! [`i64`][crate::binary::i64], [`u16`][crate::binary::u16], [`u32`][crate::binary::u32],
|
||||
//! [`u64`][crate::binary::u64] are combinators that take as argument a
|
||||
//! [`winnow::binary::Endianness`][crate::binary::Endianness], like this: `i16(endianness)`. If the
|
||||
//! parameter is `winnow::binary::Endianness::Big`, parse a big endian `i16` integer, otherwise a
|
||||
//! little endian `i16` integer.
|
||||
//! - **fixed endianness**: The functions are prefixed by `be_` for big endian numbers, and by `le_` for little endian numbers, and the suffix is the type they parse to. As an example, `be_u32` parses a big endian unsigned integer stored in 32 bits.
|
||||
//! - [`be_f32`][crate::binary::be_f32], [`be_f64`][crate::binary::be_f64]: Big endian floating point numbers
|
||||
//! - [`le_f32`][crate::binary::le_f32], [`le_f64`][crate::binary::le_f64]: Little endian floating point numbers
|
||||
//! - [`be_i8`][crate::binary::be_i8], [`be_i16`][crate::binary::be_i16], [`be_i24`][crate::binary::be_i24], [`be_i32`][crate::binary::be_i32], [`be_i64`][crate::binary::be_i64], [`be_i128`][crate::binary::be_i128]: Big endian signed integers
|
||||
//! - [`be_u8`][crate::binary::be_u8], [`be_u16`][crate::binary::be_u16], [`be_u24`][crate::binary::be_u24], [`be_u32`][crate::binary::be_u32], [`be_u64`][crate::binary::be_u64], [`be_u128`][crate::binary::be_u128]: Big endian unsigned integers
|
||||
//! - [`le_i8`][crate::binary::le_i8], [`le_i16`][crate::binary::le_i16], [`le_i24`][crate::binary::le_i24], [`le_i32`][crate::binary::le_i32], [`le_i64`][crate::binary::le_i64], [`le_i128`][crate::binary::le_i128]: Little endian signed integers
|
||||
//! - [`le_u8`][crate::binary::le_u8], [`le_u16`][crate::binary::le_u16], [`le_u24`][crate::binary::le_u24], [`le_u32`][crate::binary::le_u32], [`le_u64`][crate::binary::le_u64], [`le_u128`][crate::binary::le_u128]: Little endian unsigned integers
|
||||
//!
|
||||
//! ### Bit stream parsing
|
||||
//!
|
||||
//! - [`bits`][crate::binary::bits::bits]: Transforms the current input type (byte slice `&[u8]`) to a bit stream on which bit specific parsers and more general combinators can be applied
|
||||
//! - [`bytes`][crate::binary::bits::bytes]: Transforms its bits stream input back into a byte slice for the underlying parser
|
||||
//! - [`take`][crate::binary::bits::take]: Take a set number of bits
|
||||
//! - [`pattern`][crate::binary::bits::pattern]: Check if a set number of bits matches a pattern
|
||||
//! - [`bool`][crate::binary::bits::bool]: Match any one bit
|
||||
|
||||
mod branch;
|
||||
mod core;
|
||||
mod debug;
|
||||
mod expression;
|
||||
mod multi;
|
||||
mod sequence;
|
||||
|
||||
#[cfg(all(test, feature = "ascii", feature = "binary"))]
|
||||
mod tests;
|
||||
|
||||
pub mod impls;
|
||||
|
||||
pub use self::branch::{alt, dispatch, Alt};
|
||||
pub use self::core::{backtrack_err, cond, cut_err, empty, eof, fail, not, opt, peek, todo};
|
||||
pub use self::debug::trace;
|
||||
pub use self::expression::{expression, Expression, Infix, Postfix, Prefix};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use self::multi::separated_foldr1;
|
||||
pub use self::multi::{
|
||||
fill, iterator, repeat, repeat_till, separated, separated_foldl1, ParserIterator, Repeat,
|
||||
};
|
||||
pub use self::sequence::{delimited, preceded, separated_pair, seq, terminated, unordered_seq};
|
||||
|
||||
pub(crate) use self::debug::{trace_result, DisplayDebug};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::Parser;
|
||||
+1644
File diff suppressed because it is too large.
Load diff
+176
@@ -0,0 +1,176 @@
|
||||
use crate::combinator::trace;
|
||||
use crate::error::ParserError;
|
||||
use crate::stream::Stream;
|
||||
use crate::Parser;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::seq;
|
||||
#[doc(inline)]
|
||||
pub use crate::unordered_seq;
|
||||
|
||||
/// Sequence two parsers, only returning the output from the second.
|
||||
///
|
||||
/// See also [`seq`] to generalize this across any number of fields.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::{error::ErrMode, error::Needed};
|
||||
/// # use winnow::prelude::*;
|
||||
/// # use winnow::error::Needed::Size;
|
||||
/// use winnow::combinator::preceded;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// preceded("abc", "efg").parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "efg")));
|
||||
/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "efg")));
|
||||
/// assert!(parser.parse_peek("").is_err());
|
||||
/// assert!(parser.parse_peek("123").is_err());
|
||||
/// ```
|
||||
#[doc(alias = "ignore_then")]
|
||||
pub fn preceded<Input, Ignored, Output, Error, IgnoredParser, ParseNext>(
|
||||
mut ignored: IgnoredParser,
|
||||
mut parser: ParseNext,
|
||||
) -> impl Parser<Input, Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
IgnoredParser: Parser<Input, Ignored, Error>,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
{
|
||||
trace("preceded", move |input: &mut Input| {
|
||||
let _ = ignored.parse_next(input)?;
|
||||
parser.parse_next(input)
|
||||
})
|
||||
}
|
||||
|
||||
/// Sequence two parsers, only returning the output of the first.
|
||||
///
|
||||
/// See also [`seq`] to generalize this across any number of fields.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::{error::ErrMode, error::Needed};
|
||||
/// # use winnow::prelude::*;
|
||||
/// # use winnow::error::Needed::Size;
|
||||
/// use winnow::combinator::terminated;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// terminated("abc", "efg").parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "abc")));
|
||||
/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "abc")));
|
||||
/// assert!(parser.parse_peek("").is_err());
|
||||
/// assert!(parser.parse_peek("123").is_err());
|
||||
/// ```
|
||||
#[doc(alias = "then_ignore")]
|
||||
pub fn terminated<Input, Output, Ignored, Error, ParseNext, IgnoredParser>(
|
||||
mut parser: ParseNext,
|
||||
mut ignored: IgnoredParser,
|
||||
) -> impl Parser<Input, Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
IgnoredParser: Parser<Input, Ignored, Error>,
|
||||
{
|
||||
trace("terminated", move |input: &mut Input| {
|
||||
let o = parser.parse_next(input)?;
|
||||
ignored.parse_next(input).map(|_| o)
|
||||
})
|
||||
}
|
||||
|
||||
/// Sequence three parsers, only returning the values of the first and third.
|
||||
///
|
||||
/// See also [`seq`] to generalize this across any number of fields.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::{error::ErrMode, error::Needed};
|
||||
/// # use winnow::error::Needed::Size;
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::separated_pair;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<(&'i str, &'i str)> {
|
||||
/// separated_pair("abc", "|", "efg").parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("abc|efg"), Ok(("", ("abc", "efg"))));
|
||||
/// assert_eq!(parser.parse_peek("abc|efghij"), Ok(("hij", ("abc", "efg"))));
|
||||
/// assert!(parser.parse_peek("").is_err());
|
||||
/// assert!(parser.parse_peek("123").is_err());
|
||||
/// ```
|
||||
pub fn separated_pair<Input, O1, Sep, O2, Error, P1, SepParser, P2>(
|
||||
mut first: P1,
|
||||
mut sep: SepParser,
|
||||
mut second: P2,
|
||||
) -> impl Parser<Input, (O1, O2), Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
P1: Parser<Input, O1, Error>,
|
||||
SepParser: Parser<Input, Sep, Error>,
|
||||
P2: Parser<Input, O2, Error>,
|
||||
{
|
||||
trace("separated_pair", move |input: &mut Input| {
|
||||
let o1 = first.parse_next(input)?;
|
||||
let _ = sep.parse_next(input)?;
|
||||
second.parse_next(input).map(|o2| (o1, o2))
|
||||
})
|
||||
}
|
||||
|
||||
/// Sequence three parsers, only returning the output of the second.
|
||||
///
|
||||
/// See also [`seq`] to generalize this across any number of fields.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use winnow::{error::ErrMode, error::Needed};
|
||||
/// # use winnow::error::Needed::Size;
|
||||
/// # use winnow::prelude::*;
|
||||
/// use winnow::combinator::delimited;
|
||||
///
|
||||
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
|
||||
/// delimited("(", "abc", ")").parse_next(input)
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(parser.parse_peek("(abc)"), Ok(("", "abc")));
|
||||
/// assert_eq!(parser.parse_peek("(abc)def"), Ok(("def", "abc")));
|
||||
/// assert!(parser.parse_peek("").is_err());
|
||||
/// assert!(parser.parse_peek("123").is_err());
|
||||
/// ```
|
||||
#[doc(alias = "between")]
|
||||
#[doc(alias = "padded")]
|
||||
pub fn delimited<
|
||||
Input,
|
||||
Ignored1,
|
||||
Output,
|
||||
Ignored2,
|
||||
Error,
|
||||
IgnoredParser1,
|
||||
ParseNext,
|
||||
IgnoredParser2,
|
||||
>(
|
||||
mut ignored1: IgnoredParser1,
|
||||
mut parser: ParseNext,
|
||||
mut ignored2: IgnoredParser2,
|
||||
) -> impl Parser<Input, Output, Error>
|
||||
where
|
||||
Input: Stream,
|
||||
Error: ParserError<Input>,
|
||||
IgnoredParser1: Parser<Input, Ignored1, Error>,
|
||||
ParseNext: Parser<Input, Output, Error>,
|
||||
IgnoredParser2: Parser<Input, Ignored2, Error>,
|
||||
{
|
||||
trace("delimited", move |input: &mut Input| {
|
||||
let _ = ignored1.parse_next(input)?;
|
||||
let o2 = parser.parse_next(input)?;
|
||||
ignored2.parse_next(input).map(|_| o2)
|
||||
})
|
||||
}
|
||||
+3854
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user