1101 lines
30 KiB
Rust
1101 lines
30 KiB
Rust
use crate::lib::fmt;
|
|
|
|
#[cfg(feature = "std")]
|
|
use std::error::Error as StdError;
|
|
|
|
use crate::{stream::StreamOnce, ErrorOffset};
|
|
|
|
use self::ParseResult::*;
|
|
|
|
pub(crate) trait ResultExt<E, T> {
|
|
fn committed(self) -> ParseResult<E, T>;
|
|
}
|
|
|
|
impl<E, T> ResultExt<E, T> for Result<E, T> {
|
|
fn committed(self) -> ParseResult<E, T> {
|
|
match self {
|
|
Ok(x) => CommitOk(x),
|
|
Err(x) => CommitErr(x),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
macro_rules! ctry {
|
|
($result:expr) => {
|
|
match $result {
|
|
$crate::error::ParseResult::CommitOk(x) => (x, $crate::error::Commit::Commit(())),
|
|
$crate::error::ParseResult::PeekOk(x) => (x, $crate::error::Commit::Peek(())),
|
|
$crate::error::ParseResult::CommitErr(err) => {
|
|
return $crate::error::ParseResult::CommitErr(err.into())
|
|
}
|
|
$crate::error::ParseResult::PeekErr(err) => {
|
|
return $crate::error::ParseResult::PeekErr(err.into())
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Trait for types which can be used to construct error information.
|
|
///
|
|
/// To call functions expecting this trait, use the wrapper types defined in this module
|
|
/// `Token`, `Range`, `Format` or `Static`/`&'static str`
|
|
pub trait ErrorInfo<'s, T, R> {
|
|
type Format: fmt::Display;
|
|
|
|
#[allow(clippy::wrong_self_convention)]
|
|
fn into_info(&'s self) -> Info<T, R, Self::Format>;
|
|
}
|
|
|
|
impl<'s, 'a, T, R, F> ErrorInfo<'s, T, R> for &'a F
|
|
where
|
|
F: ErrorInfo<'s, T, R>,
|
|
{
|
|
type Format = F::Format;
|
|
fn into_info(&'s self) -> Info<T, R, Self::Format> {
|
|
(**self).into_info()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum Info<T, R, F = &'static str> {
|
|
Token(T),
|
|
Range(R),
|
|
Static(&'static str),
|
|
Format(F),
|
|
}
|
|
|
|
impl<'s, T, R, F> ErrorInfo<'s, T, R> for Info<T, R, F>
|
|
where
|
|
T: Clone,
|
|
R: Clone,
|
|
F: fmt::Display + 's,
|
|
{
|
|
type Format = &'s F;
|
|
fn into_info(&'s self) -> Info<T, R, <Self as ErrorInfo<'_, T, R>>::Format> {
|
|
match self {
|
|
Info::Token(b) => Info::Token(b.clone()),
|
|
Info::Range(b) => Info::Range(b.clone()),
|
|
Info::Static(b) => Info::Static(*b),
|
|
Info::Format(b) => Info::Format(b),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<R, F> From<char> for Info<char, R, F> {
|
|
fn from(s: char) -> Self {
|
|
Info::Token(s)
|
|
}
|
|
}
|
|
|
|
impl<'s, R> ErrorInfo<'s, char, R> for char {
|
|
type Format = &'static str;
|
|
fn into_info(&self) -> Info<char, R, Self::Format> {
|
|
Info::Token(*self)
|
|
}
|
|
}
|
|
|
|
impl<T, R, F> From<&'static str> for Info<T, R, F> {
|
|
fn from(s: &'static str) -> Self {
|
|
Info::Static(s)
|
|
}
|
|
}
|
|
|
|
impl<'s, T, R> ErrorInfo<'s, T, R> for &'static str {
|
|
type Format = &'static str;
|
|
fn into_info(&self) -> Info<T, R, Self::Format> {
|
|
Info::Static(*self)
|
|
}
|
|
}
|
|
|
|
impl<R, F> From<u8> for Info<u8, R, F> {
|
|
fn from(s: u8) -> Self {
|
|
Info::Token(s)
|
|
}
|
|
}
|
|
|
|
impl<R> ErrorInfo<'_, Self, R> for u8 {
|
|
type Format = &'static str;
|
|
fn into_info(&self) -> Info<Self, R, Self::Format> {
|
|
Info::Token(*self)
|
|
}
|
|
}
|
|
|
|
/// Newtype which constructs an `Info::Token` through `ErrorInfo`
|
|
pub struct Token<T>(pub T);
|
|
|
|
impl<T, R> From<Token<T>> for Info<T, R, &'static str> {
|
|
fn from(s: Token<T>) -> Self {
|
|
Info::Token(s.0)
|
|
}
|
|
}
|
|
|
|
impl<'s, T, R> ErrorInfo<'s, T, R> for Token<T>
|
|
where
|
|
T: Clone,
|
|
{
|
|
type Format = &'static str;
|
|
fn into_info(&'s self) -> Info<T, R, Self::Format> {
|
|
Info::Token(self.0.clone())
|
|
}
|
|
}
|
|
|
|
/// Newtype which constructs an `Info::Range` through `ErrorInfo`
|
|
pub struct Range<R>(pub R);
|
|
|
|
impl<T, R> From<Range<R>> for Info<T, R, &'static str> {
|
|
fn from(s: Range<R>) -> Self {
|
|
Info::Range(s.0)
|
|
}
|
|
}
|
|
|
|
impl<'s, T, R> ErrorInfo<'s, T, R> for Range<R>
|
|
where
|
|
R: Clone,
|
|
{
|
|
type Format = &'static str;
|
|
fn into_info(&'s self) -> Info<T, R, Self::Format> {
|
|
Info::Range(self.0.clone())
|
|
}
|
|
}
|
|
|
|
/// Newtype which constructs an `Info::Static` through `ErrorInfo`
|
|
/// A plain `&'static str` can also be used, this exists for consistency.
|
|
pub struct Static(&'static str);
|
|
|
|
impl<T, R, F> From<Static> for Info<T, R, F>
|
|
where
|
|
F: fmt::Display,
|
|
{
|
|
fn from(s: Static) -> Self {
|
|
Info::Static(s.0)
|
|
}
|
|
}
|
|
|
|
impl<'s, T, R> ErrorInfo<'s, T, R> for Static {
|
|
type Format = &'static str;
|
|
fn into_info(&'s self) -> Info<T, R, Self::Format> {
|
|
Info::Static(self.0)
|
|
}
|
|
}
|
|
|
|
/// Newtype which constructs an `Info::Format` through `ErrorInfo`
|
|
pub struct Format<F>(pub F)
|
|
where
|
|
F: fmt::Display;
|
|
|
|
impl<T, R, F> From<Format<F>> for Info<T, R, F>
|
|
where
|
|
F: fmt::Display,
|
|
{
|
|
fn from(s: Format<F>) -> Self {
|
|
Info::Format(s.0)
|
|
}
|
|
}
|
|
|
|
impl<'s, T, R, F> ErrorInfo<'s, T, R> for Format<F>
|
|
where
|
|
F: fmt::Display + 's,
|
|
{
|
|
type Format = &'s F;
|
|
fn into_info(&'s self) -> Info<T, R, Self::Format> {
|
|
Info::Format(&self.0)
|
|
}
|
|
}
|
|
|
|
/// Enum used to indicate if a parser committed any items of the stream it was given as an input.
|
|
///
|
|
/// This is used by parsers such as `or` and `choice` to determine if they should try to parse
|
|
/// with another parser as they will only be able to provide good error reporting if the preceding
|
|
/// parser did not commit to the parse.
|
|
#[derive(Clone, PartialEq, Debug, Copy)]
|
|
pub enum Commit<T> {
|
|
/// Constructor indicating that the parser has committed to this parse. If a parser after this fails,
|
|
/// other parser alternatives will not be attempted (`CommitErr` will be returned)
|
|
Commit(T),
|
|
/// Constructor indicating that the parser has not committed to this parse. If a parser after this fails,
|
|
/// other parser alternatives will be attempted (`EmptyErr` will be returned)
|
|
Peek(T),
|
|
}
|
|
|
|
impl<T> AsMut<T> for Commit<T> {
|
|
fn as_mut(&mut self) -> &mut T {
|
|
match *self {
|
|
Commit::Peek(ref mut t) | Commit::Commit(ref mut t) => t,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> AsRef<T> for Commit<T> {
|
|
fn as_ref(&self) -> &T {
|
|
match *self {
|
|
Commit::Peek(ref t) | Commit::Commit(ref t) => t,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Commit<T> {
|
|
/// Returns true if `self` is peek.
|
|
pub fn is_peek(&self) -> bool {
|
|
match *self {
|
|
Commit::Peek(_) => true,
|
|
Commit::Commit(_) => false,
|
|
}
|
|
}
|
|
|
|
/// Extracts the contained value.
|
|
pub fn into_inner(self) -> T {
|
|
match self {
|
|
Commit::Peek(x) | Commit::Commit(x) => x,
|
|
}
|
|
}
|
|
|
|
/// Converts `self` into the `Commit` state.
|
|
pub fn into_commit(self) -> Commit<T> {
|
|
Commit::Commit(self.into_inner())
|
|
}
|
|
|
|
/// Converts `self` into the `Peek` state.
|
|
pub fn into_peek(self) -> Commit<T> {
|
|
Commit::Peek(self.into_inner())
|
|
}
|
|
|
|
/// Maps over the contained value without changing the committed state.
|
|
pub fn map<F, U>(self, f: F) -> Commit<U>
|
|
where
|
|
F: FnOnce(T) -> U,
|
|
{
|
|
match self {
|
|
Commit::Peek(x) => Commit::Peek(f(x)),
|
|
Commit::Commit(x) => Commit::Commit(f(x)),
|
|
}
|
|
}
|
|
|
|
pub fn merge(&self, current: Commit<T>) -> Commit<T> {
|
|
match *self {
|
|
Commit::Peek(_) => current,
|
|
Commit::Commit(_) => current.into_commit(),
|
|
}
|
|
}
|
|
|
|
/// Combines the `Commit` flags from `self` and the result of `f`.
|
|
///
|
|
/// ```text
|
|
/// Peek <> Peek -> Peek
|
|
/// Commit <> Peek -> Commit
|
|
/// Peek <> Commit -> Commit
|
|
/// Commit <> Commit -> Commit
|
|
/// ```
|
|
///
|
|
/// ```
|
|
/// # extern crate combine as pc;
|
|
/// # use pc::*;
|
|
/// # fn main() {
|
|
/// //Parses a character of string literal and handles the escaped characters \\ and \" as \
|
|
/// //and " respectively
|
|
/// fn char<Input>(input: &mut Input) -> StdParseResult<char, Input>
|
|
/// where Input: Stream<Token = char>,
|
|
/// {
|
|
/// let (c, committed) = satisfy(|c| c != '"').parse_stream(input).into_result()?;
|
|
/// match c {
|
|
/// //Since the `char` parser has already committed some of the input `combine` is used
|
|
/// //propagate the committed state to the next part of the parser
|
|
/// '\\' => committed.combine(|_| {
|
|
/// satisfy(|c| c == '"' || c == '\\')
|
|
/// .map(|c| {
|
|
/// match c {
|
|
/// '"' => '"',
|
|
/// '\\' => '\\',
|
|
/// c => c
|
|
/// }
|
|
/// })
|
|
/// .parse_stream(input)
|
|
/// .into_result()
|
|
/// }),
|
|
/// _ => Ok((c, committed))
|
|
/// }
|
|
/// }
|
|
/// let result = many(parser(char))
|
|
/// .easy_parse(r#"abc\"\\"#);
|
|
/// assert_eq!(result, Ok((r#"abc"\"#.to_string(), "")));
|
|
/// }
|
|
/// ```
|
|
pub fn combine<F, U, E>(self, f: F) -> StdParseResult2<U, E>
|
|
where
|
|
F: FnOnce(T) -> StdParseResult2<U, E>,
|
|
{
|
|
match self {
|
|
Commit::Commit(x) => match f(x) {
|
|
Ok((v, Commit::Peek(()))) => Ok((v, Commit::Commit(()))),
|
|
Err(Commit::Peek(err)) => Err(Commit::Commit(err)),
|
|
y => y,
|
|
},
|
|
Commit::Peek(x) => f(x),
|
|
}
|
|
}
|
|
pub fn combine_commit<F, U, E>(self, f: F) -> ParseResult<U, E>
|
|
where
|
|
F: FnOnce(T) -> ParseResult<U, E>,
|
|
{
|
|
use self::ParseResult::*;
|
|
|
|
match self {
|
|
Commit::Commit(x) => match f(x) {
|
|
PeekOk(v) => CommitOk(v),
|
|
PeekErr(err) => CommitErr(err.error),
|
|
y => y,
|
|
},
|
|
Commit::Peek(x) => f(x),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A type alias over the specific `Result` type used by parsers to indicate whether they were
|
|
/// successful or not.
|
|
/// `O` is the type that is output on success.
|
|
/// `Input` is the specific stream type used in the parser.
|
|
pub type StdParseResult<O, Input> =
|
|
Result<(O, Commit<()>), Commit<Tracked<<Input as StreamOnce>::Error>>>;
|
|
pub type StdParseResult2<O, E> = Result<(O, Commit<()>), Commit<Tracked<E>>>;
|
|
|
|
/// `StreamError` represents a single error returned from a `Stream` or a `Parser`.
|
|
///
|
|
/// Usually multiple instances of `StreamError` is composed into a `ParseError` to build the final
|
|
/// error value.
|
|
pub trait StreamError<Item, Range>: Sized {
|
|
fn unexpected_token(token: Item) -> Self;
|
|
fn unexpected_range(token: Range) -> Self;
|
|
fn unexpected_format<T>(msg: T) -> Self
|
|
where
|
|
T: fmt::Display;
|
|
fn unexpected<E>(info: E) -> Self
|
|
where
|
|
E: for<'s> ErrorInfo<'s, Item, Range>,
|
|
{
|
|
match info.into_info() {
|
|
Info::Token(b) => Self::unexpected_token(b),
|
|
Info::Range(b) => Self::unexpected_range(b),
|
|
Info::Static(b) => Self::unexpected_static_message(b),
|
|
Info::Format(b) => Self::unexpected_format(b),
|
|
}
|
|
}
|
|
fn unexpected_static_message(msg: &'static str) -> Self {
|
|
Self::unexpected_format(msg)
|
|
}
|
|
|
|
fn expected_token(token: Item) -> Self;
|
|
fn expected_range(token: Range) -> Self;
|
|
fn expected_format<T>(msg: T) -> Self
|
|
where
|
|
T: fmt::Display;
|
|
fn expected<E>(info: E) -> Self
|
|
where
|
|
E: for<'s> ErrorInfo<'s, Item, Range>,
|
|
{
|
|
match info.into_info() {
|
|
Info::Token(b) => Self::expected_token(b),
|
|
Info::Range(b) => Self::expected_range(b),
|
|
Info::Static(b) => Self::expected_static_message(b),
|
|
Info::Format(b) => Self::expected_format(b),
|
|
}
|
|
}
|
|
fn expected_static_message(msg: &'static str) -> Self {
|
|
Self::expected_format(msg)
|
|
}
|
|
|
|
fn message_token(token: Item) -> Self;
|
|
fn message_range(token: Range) -> Self;
|
|
fn message_format<T>(msg: T) -> Self
|
|
where
|
|
T: fmt::Display;
|
|
fn message_static_message(msg: &'static str) -> Self {
|
|
Self::message_format(msg)
|
|
}
|
|
fn message<E>(info: E) -> Self
|
|
where
|
|
E: for<'s> ErrorInfo<'s, Item, Range>,
|
|
{
|
|
match info.into_info() {
|
|
Info::Token(b) => Self::message_token(b),
|
|
Info::Range(b) => Self::message_range(b),
|
|
Info::Static(b) => Self::message_static_message(b),
|
|
Info::Format(b) => Self::message_format(b),
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
fn other<E>(err: E) -> Self
|
|
where
|
|
E: StdError + Send + Sync + 'static,
|
|
{
|
|
Self::message_format(err)
|
|
}
|
|
|
|
fn end_of_input() -> Self {
|
|
Self::unexpected_static_message("end of input")
|
|
}
|
|
|
|
fn is_unexpected_end_of_input(&self) -> bool;
|
|
|
|
/// Converts `self` into a different `StreamError` type.
|
|
///
|
|
/// This should aim to preserve as much information as possible into the returned `T` value but
|
|
/// if `Self` ignores some information passed to it using one of the constructors that
|
|
/// information is naturally lost.
|
|
fn into_other<T>(self) -> T
|
|
where
|
|
T: StreamError<Item, Range>;
|
|
}
|
|
|
|
/// Trait which defines a combine parse error.
|
|
///
|
|
/// A parse error is composed of zero or more `StreamError` instances which gets added to it as
|
|
/// errors are encountered during parsing.
|
|
pub trait ParseError<Item, Range, Position>: Sized + PartialEq {
|
|
type StreamError: StreamError<Item, Range>;
|
|
|
|
/// Constructs an empty error.
|
|
///
|
|
/// An empty error is expected to be cheap to create as it is frequently created and discarded.
|
|
fn empty(position: Position) -> Self;
|
|
|
|
/// Creates a `ParseError` from a single `Self::StreamError`
|
|
fn from_error(position: Position, err: Self::StreamError) -> Self {
|
|
let mut errors = Self::empty(position);
|
|
errors.add(err);
|
|
errors
|
|
}
|
|
|
|
fn position(&self) -> Position {
|
|
// TODO Remove the default implementation in a breaking release
|
|
unimplemented!()
|
|
}
|
|
|
|
/// Sets the position of this `ParseError`
|
|
fn set_position(&mut self, position: Position);
|
|
|
|
/// Merges two errors. If they exist at the same position the errors of `other` are
|
|
/// added to `self` (using the semantics of `add`). If they are not at the same
|
|
/// position the error furthest ahead are returned, ignoring the other `ParseError`.
|
|
fn merge(self, other: Self) -> Self {
|
|
other
|
|
}
|
|
|
|
/// Adds a `StreamError` to `self`.
|
|
///
|
|
/// It is up to each individual error type to define what adding an error does, some may push
|
|
/// it to a vector while others may only keep `self` or `err` to avoid allocation
|
|
fn add(&mut self, err: Self::StreamError);
|
|
|
|
fn add_expected<E>(&mut self, info: E)
|
|
where
|
|
E: for<'s> ErrorInfo<'s, Item, Range>,
|
|
{
|
|
self.add(Self::StreamError::expected(info))
|
|
}
|
|
|
|
fn add_unexpected<E>(&mut self, info: E)
|
|
where
|
|
E: for<'s> ErrorInfo<'s, Item, Range>,
|
|
{
|
|
self.add(Self::StreamError::unexpected(info))
|
|
}
|
|
|
|
fn add_message<E>(&mut self, info: E)
|
|
where
|
|
E: for<'s> ErrorInfo<'s, Item, Range>,
|
|
{
|
|
self.add(Self::StreamError::message(info))
|
|
}
|
|
|
|
/// Sets `info` as the *only* `Expected` error of `self`
|
|
fn set_expected<F>(self_: &mut Tracked<Self>, info: Self::StreamError, f: F)
|
|
where
|
|
F: FnOnce(&mut Tracked<Self>);
|
|
|
|
/// Removes any expected errors currently in `self`
|
|
fn clear_expected(&mut self) {}
|
|
|
|
fn is_unexpected_end_of_input(&self) -> bool;
|
|
|
|
/// Does a best-effort conversion of `self` into another `ParseError`
|
|
fn into_other<T>(self) -> T
|
|
where
|
|
T: ParseError<Item, Range, Position>;
|
|
}
|
|
|
|
/// Defines a conversion between two parse error types.
|
|
///
|
|
/// Like `ParseError::into_other` but with a more general signature
|
|
/// (This will take the place of `into_other` on breaking release of combine)
|
|
pub trait ParseErrorInto<Item, Range, Position>: Sized {
|
|
fn into_other_error<T, Item2, Range2, Position2>(self) -> T
|
|
where
|
|
T: ParseError<Item2, Range2, Position2>,
|
|
Item2: From<Item>,
|
|
Range2: From<Range>,
|
|
Position2: From<Position>;
|
|
}
|
|
|
|
/// Defines a conversion between two stream error types.
|
|
///
|
|
/// Like `StreamError::into_other` but with a more general signature
|
|
/// (This will take the place of `into_other` on breaking release of combine)
|
|
pub trait StreamErrorInto<Item, Range>: Sized {
|
|
fn into_other_error<T, Item2, Range2>(self) -> T
|
|
where
|
|
T: StreamError<Item2, Range2>,
|
|
Item2: From<Item>,
|
|
Range2: From<Range>;
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub enum UnexpectedParse {
|
|
Eoi,
|
|
Unexpected,
|
|
}
|
|
|
|
impl fmt::Display for UnexpectedParse {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.as_str())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl StdError for UnexpectedParse {
|
|
fn description(&self) -> &str {
|
|
self.as_str()
|
|
}
|
|
}
|
|
|
|
impl UnexpectedParse {
|
|
fn as_str(&self) -> &str {
|
|
use self::UnexpectedParse::*;
|
|
match *self {
|
|
Unexpected => "unexpected parse",
|
|
Eoi => "unexpected end of input",
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Item, Range> StreamError<Item, Range> for UnexpectedParse {
|
|
#[inline]
|
|
fn unexpected_token(_: Item) -> Self {
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
#[inline]
|
|
fn unexpected_range(_: Range) -> Self {
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
#[inline]
|
|
fn unexpected_format<T>(_: T) -> Self
|
|
where
|
|
T: fmt::Display,
|
|
{
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
|
|
#[inline]
|
|
fn expected_token(_: Item) -> Self {
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
#[inline]
|
|
fn expected_range(_: Range) -> Self {
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
#[inline]
|
|
fn expected_format<T>(_: T) -> Self
|
|
where
|
|
T: fmt::Display,
|
|
{
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
#[inline]
|
|
fn message_format<T>(_: T) -> Self
|
|
where
|
|
T: fmt::Display,
|
|
{
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
#[inline]
|
|
fn message_token(_: Item) -> Self {
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
#[inline]
|
|
fn message_range(_: Range) -> Self {
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
|
|
#[inline]
|
|
fn end_of_input() -> Self {
|
|
UnexpectedParse::Eoi
|
|
}
|
|
|
|
#[inline]
|
|
fn is_unexpected_end_of_input(&self) -> bool {
|
|
*self == UnexpectedParse::Eoi
|
|
}
|
|
|
|
#[inline]
|
|
fn into_other<T>(self) -> T
|
|
where
|
|
T: StreamError<Item, Range>,
|
|
{
|
|
match self {
|
|
UnexpectedParse::Unexpected => T::unexpected_static_message("parse"),
|
|
UnexpectedParse::Eoi => T::end_of_input(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Item, Range, Position> ParseError<Item, Range, Position> for UnexpectedParse
|
|
where
|
|
Position: Default,
|
|
{
|
|
type StreamError = Self;
|
|
#[inline]
|
|
fn empty(_position: Position) -> Self {
|
|
UnexpectedParse::Unexpected
|
|
}
|
|
|
|
#[inline]
|
|
fn from_error(_: Position, err: Self::StreamError) -> Self {
|
|
err
|
|
}
|
|
|
|
fn position(&self) -> Position {
|
|
Position::default()
|
|
}
|
|
|
|
#[inline]
|
|
fn set_position(&mut self, _position: Position) {}
|
|
|
|
#[inline]
|
|
fn add(&mut self, err: Self::StreamError) {
|
|
*self = match (*self, err) {
|
|
(UnexpectedParse::Eoi, _) => UnexpectedParse::Eoi,
|
|
(_, err) => err,
|
|
};
|
|
}
|
|
|
|
#[inline]
|
|
fn set_expected<F>(self_: &mut Tracked<Self>, info: Self::StreamError, f: F)
|
|
where
|
|
F: FnOnce(&mut Tracked<Self>),
|
|
{
|
|
f(self_);
|
|
self_.error = info;
|
|
}
|
|
|
|
fn is_unexpected_end_of_input(&self) -> bool {
|
|
*self == UnexpectedParse::Eoi
|
|
}
|
|
|
|
#[inline]
|
|
fn into_other<T>(self) -> T
|
|
where
|
|
T: ParseError<Item, Range, Position>,
|
|
{
|
|
T::from_error(Position::default(), StreamError::into_other(self))
|
|
}
|
|
}
|
|
|
|
impl<Item, Range, Position> ParseErrorInto<Item, Range, Position> for UnexpectedParse
|
|
where
|
|
Position: Default,
|
|
{
|
|
fn into_other_error<T, Item2, Range2, Position2>(self) -> T
|
|
where
|
|
T: ParseError<Item2, Range2, Position2>,
|
|
Item2: From<Item>,
|
|
Range2: From<Range>,
|
|
Position2: From<Position>,
|
|
{
|
|
T::from_error(
|
|
Position::default().into(),
|
|
StreamErrorInto::<Item, Range>::into_other_error(self),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<Item, Range> StreamErrorInto<Item, Range> for UnexpectedParse {
|
|
fn into_other_error<T, Item2, Range2>(self) -> T
|
|
where
|
|
T: StreamError<Item2, Range2>,
|
|
Item2: From<Item>,
|
|
Range2: From<Range>,
|
|
{
|
|
StreamError::into_other(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub enum StringStreamError {
|
|
UnexpectedParse,
|
|
Eoi,
|
|
CharacterBoundary,
|
|
}
|
|
|
|
pub(crate) const CHAR_BOUNDARY_ERROR_MESSAGE: &str = "unexpected slice on character boundary";
|
|
|
|
impl fmt::Display for StringStreamError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.as_str())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl StdError for StringStreamError {
|
|
fn description(&self) -> &str {
|
|
self.as_str()
|
|
}
|
|
}
|
|
|
|
impl StringStreamError {
|
|
fn as_str(&self) -> &str {
|
|
use self::StringStreamError::*;
|
|
match *self {
|
|
UnexpectedParse => "unexpected parse",
|
|
Eoi => "unexpected end of input",
|
|
CharacterBoundary => CHAR_BOUNDARY_ERROR_MESSAGE,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Item, Range> StreamError<Item, Range> for StringStreamError {
|
|
#[inline]
|
|
fn unexpected_token(_: Item) -> Self {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn unexpected_range(_: Range) -> Self {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn unexpected_format<T>(_msg: T) -> Self
|
|
where
|
|
T: fmt::Display,
|
|
{
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
|
|
#[inline]
|
|
fn expected_token(_: Item) -> Self {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn expected_range(_: Range) -> Self {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn expected_format<T>(_: T) -> Self
|
|
where
|
|
T: fmt::Display,
|
|
{
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn message_format<T>(_: T) -> Self
|
|
where
|
|
T: fmt::Display,
|
|
{
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn message_token(_: Item) -> Self {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn message_range(_: Range) -> Self {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
fn message_static_message(msg: &'static str) -> Self {
|
|
if msg == CHAR_BOUNDARY_ERROR_MESSAGE {
|
|
StringStreamError::CharacterBoundary
|
|
} else {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
}
|
|
#[inline]
|
|
fn end_of_input() -> Self {
|
|
StringStreamError::Eoi
|
|
}
|
|
#[inline]
|
|
fn is_unexpected_end_of_input(&self) -> bool {
|
|
*self == StringStreamError::Eoi
|
|
}
|
|
#[inline]
|
|
fn into_other<T>(self) -> T
|
|
where
|
|
T: StreamError<Item, Range>,
|
|
{
|
|
let msg = match self {
|
|
StringStreamError::CharacterBoundary => CHAR_BOUNDARY_ERROR_MESSAGE,
|
|
StringStreamError::UnexpectedParse => "parse",
|
|
StringStreamError::Eoi => return T::end_of_input(),
|
|
};
|
|
T::unexpected_static_message(msg)
|
|
}
|
|
}
|
|
impl<Item, Range, Position> ParseError<Item, Range, Position> for StringStreamError
|
|
where
|
|
Position: Default,
|
|
{
|
|
type StreamError = Self;
|
|
#[inline]
|
|
fn empty(_position: Position) -> Self {
|
|
StringStreamError::UnexpectedParse
|
|
}
|
|
#[inline]
|
|
fn from_error(_: Position, err: Self::StreamError) -> Self {
|
|
err
|
|
}
|
|
|
|
fn position(&self) -> Position {
|
|
Position::default()
|
|
}
|
|
|
|
#[inline]
|
|
fn set_position(&mut self, _position: Position) {}
|
|
|
|
#[inline]
|
|
fn add(&mut self, err: Self::StreamError) {
|
|
*self = match (*self, err) {
|
|
(StringStreamError::Eoi, _) => StringStreamError::Eoi,
|
|
(_, err) => err,
|
|
};
|
|
}
|
|
|
|
#[inline]
|
|
fn set_expected<F>(self_: &mut Tracked<Self>, info: Self::StreamError, f: F)
|
|
where
|
|
F: FnOnce(&mut Tracked<Self>),
|
|
{
|
|
f(self_);
|
|
self_.error = info;
|
|
}
|
|
|
|
fn is_unexpected_end_of_input(&self) -> bool {
|
|
*self == StringStreamError::Eoi
|
|
}
|
|
|
|
#[inline]
|
|
fn into_other<T>(self) -> T
|
|
where
|
|
T: ParseError<Item, Range, Position>,
|
|
{
|
|
T::from_error(Position::default(), StreamError::into_other(self))
|
|
}
|
|
}
|
|
|
|
impl<Item, Range, Position> ParseErrorInto<Item, Range, Position> for StringStreamError
|
|
where
|
|
Position: Default,
|
|
{
|
|
fn into_other_error<T, Item2, Range2, Position2>(self) -> T
|
|
where
|
|
T: ParseError<Item2, Range2, Position2>,
|
|
Item2: From<Item>,
|
|
Range2: From<Range>,
|
|
Position2: From<Position>,
|
|
{
|
|
T::from_error(
|
|
Position::default().into(),
|
|
StreamErrorInto::<Item, Range>::into_other_error(self),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<Item, Range> StreamErrorInto<Item, Range> for StringStreamError {
|
|
fn into_other_error<T, Item2, Range2>(self) -> T
|
|
where
|
|
T: StreamError<Item2, Range2>,
|
|
Item2: From<Item>,
|
|
Range2: From<Range>,
|
|
{
|
|
StreamError::into_other(self)
|
|
}
|
|
}
|
|
|
|
/// Error wrapper which lets parsers track which parser in a sequence of sub-parsers has emitted
|
|
/// the error. `Tracked::from` can be used to construct this and it should otherwise be
|
|
/// ignored outside of combine.
|
|
#[derive(Clone, PartialEq, Debug, Copy)]
|
|
pub struct Tracked<E> {
|
|
/// The error returned
|
|
pub error: E,
|
|
#[doc(hidden)]
|
|
pub offset: ErrorOffset,
|
|
}
|
|
|
|
impl<E> From<E> for Tracked<E> {
|
|
fn from(error: E) -> Self {
|
|
Tracked {
|
|
error,
|
|
offset: ErrorOffset(1),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A `Result` type which has the committed status flattened into the result.
|
|
/// Conversions to and from `std::result::Result` can be done using `result.into()` or
|
|
/// `From::from(result)`
|
|
#[derive(Clone, PartialEq, Debug, Copy)]
|
|
pub enum ParseResult<T, E> {
|
|
/// The parser has succeeded and has committed to this parse. If a parser after this fails,
|
|
/// other parser alternatives will not be attempted (`CommitErr` will be returned)
|
|
CommitOk(T),
|
|
/// The parser has succeeded and has not committed to this parse. If a parser after this fails,
|
|
/// other parser alternatives will be attempted (`PeekErr` will be returned)
|
|
PeekOk(T),
|
|
/// The parser failed other parse alternatives will not be attempted.
|
|
CommitErr(E),
|
|
/// The parser failed but other parse alternatives may be attempted.
|
|
PeekErr(Tracked<E>),
|
|
}
|
|
|
|
impl<T, E> ParseResult<T, E> {
|
|
#[inline]
|
|
pub fn is_ok(&self) -> bool {
|
|
match *self {
|
|
CommitOk(_) | PeekOk(_) => true,
|
|
CommitErr(_) | PeekErr(_) => false,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_err(&self) -> bool {
|
|
!self.is_ok()
|
|
}
|
|
|
|
pub fn as_ref(&self) -> ParseResult<&T, &E> {
|
|
match *self {
|
|
CommitOk(ref t) => CommitOk(t),
|
|
PeekOk(ref t) => PeekOk(t),
|
|
CommitErr(ref e) => CommitErr(e),
|
|
PeekErr(ref e) => PeekErr(Tracked {
|
|
error: &e.error,
|
|
offset: e.offset,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn and_then<F, T2>(self, f: F) -> F::Output
|
|
where
|
|
F: FnOnce(T) -> ParseResult<T2, E>,
|
|
{
|
|
match self {
|
|
CommitOk(t) => match f(t) {
|
|
CommitOk(t2) | PeekOk(t2) => CommitOk(t2),
|
|
PeekErr(e) => CommitErr(e.error),
|
|
CommitErr(e) => CommitErr(e),
|
|
},
|
|
PeekOk(t) => f(t),
|
|
CommitErr(e) => CommitErr(e),
|
|
PeekErr(e) => PeekErr(e),
|
|
}
|
|
}
|
|
|
|
pub fn map_err<F, E2>(self, f: F) -> ParseResult<T, F::Output>
|
|
where
|
|
F: FnOnce(E) -> E2,
|
|
{
|
|
match self {
|
|
CommitOk(t) => CommitOk(t),
|
|
PeekOk(t) => PeekOk(t),
|
|
CommitErr(e) => CommitErr(f(e)),
|
|
PeekErr(e) => PeekErr(Tracked {
|
|
error: f(e.error),
|
|
offset: e.offset,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn map<F, T2>(self, f: F) -> ParseResult<F::Output, E>
|
|
where
|
|
F: FnOnce(T) -> T2,
|
|
{
|
|
match self {
|
|
CommitOk(t) => CommitOk(f(t)),
|
|
PeekOk(t) => PeekOk(f(t)),
|
|
CommitErr(e) => CommitErr(e),
|
|
PeekErr(e) => PeekErr(e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<O, E> ParseResult<O, E> {
|
|
pub fn into_result(self) -> StdParseResult2<O, E> {
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
impl<T, E> Into<Result<Commit<T>, Commit<Tracked<E>>>> for ParseResult<T, E> {
|
|
#[inline]
|
|
fn into(self) -> Result<Commit<T>, Commit<Tracked<E>>> {
|
|
match self {
|
|
CommitOk(t) => Ok(Commit::Commit(t)),
|
|
PeekOk(t) => Ok(Commit::Peek(t)),
|
|
CommitErr(e) => Err(Commit::Commit(e.into())),
|
|
PeekErr(e) => Err(Commit::Peek(e)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<O, E> Into<StdParseResult2<O, E>> for ParseResult<O, E> {
|
|
#[inline]
|
|
fn into(self) -> StdParseResult2<O, E> {
|
|
use self::ParseResult::*;
|
|
|
|
match self {
|
|
CommitOk(t) => Ok((t, Commit::Commit(()))),
|
|
PeekOk(t) => Ok((t, Commit::Peek(()))),
|
|
CommitErr(e) => Err(Commit::Commit(e.into())),
|
|
PeekErr(e) => Err(Commit::Peek(e)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<O, E> From<StdParseResult2<O, E>> for ParseResult<O, E> {
|
|
#[inline]
|
|
fn from(result: StdParseResult2<O, E>) -> ParseResult<O, E> {
|
|
use self::ParseResult::*;
|
|
|
|
match result {
|
|
Ok((t, Commit::Commit(()))) => CommitOk(t),
|
|
Ok((t, Commit::Peek(()))) => PeekOk(t),
|
|
Err(Commit::Commit(e)) => CommitErr(e.error),
|
|
Err(Commit::Peek(e)) => PeekErr(e),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(all(feature = "std", test))]
|
|
mod tests_std {
|
|
|
|
use crate::Parser;
|
|
|
|
#[derive(Clone, PartialEq, Debug)]
|
|
struct CloneOnly {
|
|
s: String,
|
|
}
|
|
|
|
#[test]
|
|
fn parse_clone_but_not_copy() {
|
|
// This verifies we can parse slice references with an token type that is Clone but not Copy.
|
|
let input = &[
|
|
CloneOnly { s: "x".to_string() },
|
|
CloneOnly { s: "y".to_string() },
|
|
][..];
|
|
let result = crate::parser::range::take_while(|c: CloneOnly| c.s == "x").parse(input);
|
|
assert_eq!(
|
|
result,
|
|
Ok((
|
|
&[CloneOnly { s: "x".to_string() }][..],
|
|
&[CloneOnly { s: "y".to_string() }][..]
|
|
))
|
|
);
|
|
}
|
|
}
|