Struct ParseContext

Source
pub struct ParseContext<'parse> { /* private fields */ }
Expand description

Contains the source text we’re parsing and tracks errors.

We track errors in the ParseContext, not Results, because often a parser produces both a successful match and the error that will later prove to be the best error message for the overall parse attempt.

§The :wq example

For example, consider parser!(line(u32)+) parsing the following input:

1
2
3
4:wq
5

Clearly, someone accidentally typed :wq on line 4. But what will happen here is that line(u32)+ successfully matches the first 3 lines. If we don’t track the error we encountered when trying to parse 5:wq as a u32, but content ourselves with the successful match line(u32)+ produces, the best error message we can ultimately produce is something like “found extra text after a successful match at line 4, column 1”.

Here’s how we now handle these success-failures:

  • u32 returns success, matching 4.

  • line(u32) reports an error to the context (at line 4 column 2) and returns Reported, because u32 didn’t match the entire line.

  • line(u32)+ then discards the Reported error, backtracks, and returns a successful match for the first 3 lines.

  • The top-level parser sees that line(u32)+ didn’t match the entire input, and reports an error to the context at line 4 column 1. But we already have a previous error where we had got further, so this error is ignored.

  • The error at line 4 column 2 is taken from the context and returned to the user.

§Rationale: Design alternatives

To implement this without ParseContext, we could have implemented a TigerResult<T, E> type that can be Ok(T), Err(E), or OkBut(T, E), the last containing both a success value and an excuse explaining why we did not succeed even more. The forwardmost error would be propagated there instead of being stored in the context. We would use TigerResult<T, ParseError> instead of Result<T, Reported> everywhere. Both ways have advantages. Both are pretty weird for Rust. The way of the context is something I’ve wanted to explore in Rust; and it lets us keep using the ? try operator.

Implementations§

Source§

impl<'parse> ParseContext<'parse>

Source

pub fn new(source: &'parse str) -> Self

Create a ParseContext to parse the given input.

Source

pub fn source(&self) -> &'parse str

The text being parsed.

Source

pub fn into_reported_error(self) -> ParseError

Extract the error. Use this only after receiving Reported from an operation on the context.

§Panics

If no error has been reported on this context.

Source

pub fn report(&mut self, err: ParseError) -> Reported

Record an error.

Currently a ParseContext only tracks the foremost error. That is, if err.location is farther forward than any other error we’ve encountered, we store it. Otherwise discard it.

Nontrivial patterns try several different things. If anything succeeds, we get a match. We only fail if every branch leads to failure. This means that by the time matching fails, we have an abundance of different error messages. Generally the error we want is the one where we progressed as far as possible through the input string before failing.

Source

pub fn error_expected(&mut self, start: usize, expected: &str) -> Reported

Record a foo expected error.

Source

pub fn error_from_str_failed( &mut self, start: usize, end: usize, type_name: &'static str, message: String, ) -> Reported

Record an error when FromStr::from_str fails.

Source

pub fn error_extra(&mut self, location: usize) -> Reported

Record an “extra unparsed text after match” error.

Auto Trait Implementations§

§

impl<'parse> Freeze for ParseContext<'parse>

§

impl<'parse> !RefUnwindSafe for ParseContext<'parse>

§

impl<'parse> !Send for ParseContext<'parse>

§

impl<'parse> !Sync for ParseContext<'parse>

§

impl<'parse> Unpin for ParseContext<'parse>

§

impl<'parse> !UnwindSafe for ParseContext<'parse>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.