Struct aoc_parse::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, matching4
. -
line(u32)
reports an error to the context (at line 4 column 2) and returnsReported
, becauseu32
didn’t match the entire line. -
line(u32)+
then discards theReported
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>
impl<'parse> ParseContext<'parse>
sourcepub fn new(source: &'parse str) -> Self
pub fn new(source: &'parse str) -> Self
Create a ParseContext
to parse the given input.
Examples found in repository?
54 55 56 57 58 59 60 61 62 63 64 65 66 67
fn parse_raw(&self, s: &str) -> Result<Self::RawOutput> {
let mut ctx = ParseContext::new(s);
let mut it = match self.parse_iter(&mut ctx, 0) {
Ok(iter) => iter,
Err(Reported) => return Err(ctx.into_reported_error()),
};
while it.match_end() != s.len() {
ctx.error_extra(it.match_end());
if it.backtrack(&mut ctx).is_err() {
return Err(ctx.into_reported_error());
}
}
Ok(it.convert())
}
More examples
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
fn match_fully<'parse, R, P>(parser: &'parse P, source: &'parse str) -> Result<P::Iter<'parse>>
where
R: Region,
P: Parser,
{
let mut context = ParseContext::new(source);
let mut iter = match parser.parse_iter(&mut context, 0) {
Ok(iter) => iter,
Err(Reported) => return Err(context.into_reported_error()),
};
while iter.match_end() != source.len() {
R::report_incomplete_match(&mut context, iter.match_end());
if iter.backtrack(&mut context).is_err() {
return Err(context.into_reported_error());
}
}
Ok(iter)
}
sourcepub fn source(&self) -> &'parse str
pub fn source(&self) -> &'parse str
The text being parsed.
Examples found in repository?
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
pub fn error_expected(&mut self, start: usize, expected: &str) -> Reported {
self.report(ParseError::new_expected(self.source(), start, expected))
}
/// Record an error when `FromStr::from_str` fails.
pub fn error_from_str_failed(
&mut self,
start: usize,
end: usize,
type_name: &'static str,
message: String,
) -> Reported {
self.report(ParseError::new_from_str_failed(
self.source(),
start,
end,
type_name,
message,
))
}
/// Record an "extra unparsed text after match" error.
pub fn error_extra(&mut self, location: usize) -> Reported {
self.report(ParseError::new_extra(self.source(), location))
}
More examples
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
let source = context.source();
if start == 0 || source[..start].ends_with('\n') {
Ok(())
} else {
Err(context.report(ParseError::new_bad_line_start(source, start)))
}
}
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
let source = context.source();
match source[start..].find('\n') {
Some(offset) => Ok((start + offset, start + offset + 1)),
None if start != source.len() => Ok((source.len(), source.len())),
None => Err(context.error_expected(source.len(), "line")),
}
}
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
context.report(ParseError::new_line_extra(context.source(), end))
}
}
/// A "section" is a sequence of zero or more nonblank lines, starting either
/// at the beginning of the input or immediately after a newline; followed by
/// either a blank line or the end of input.
#[derive(Debug, Clone, Copy)]
pub struct Section;
impl Region for Section {
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
let source = context.source();
if start == 0 || &source[..start] == "\n" || source[..start].ends_with("\n\n") {
Ok(())
} else {
Err(context.report(ParseError::new_bad_section_start(source, start)))
}
}
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
// FIXME BUG: unclear what this should do when looking at an empty
// section at end of input. presumably not repeat forever. (why does
// this not always hang forever if you try to use `sections`?)
let source = context.source();
match source[start..].find("\n\n") {
// ending at a blank line
Some(index) => Ok((start + index + 1, start + index + 2)),
// ending at the end of `source`
None if start < source.len() => Ok((source.len(), source.len())),
// no end-of-section delimiter found
None => Err(context.error_expected(source.len(), "section")),
}
}
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
context.report(ParseError::new_section_extra(context.source(), end))
}
}
/// Match but don't convert; just return the ParseIter on success. Expects all
/// of `source` to be matched, otherwise it's an error.
fn match_fully<'parse, R, P>(parser: &'parse P, source: &'parse str) -> Result<P::Iter<'parse>>
where
R: Region,
P: Parser,
{
let mut context = ParseContext::new(source);
let mut iter = match parser.parse_iter(&mut context, 0) {
Ok(iter) => iter,
Err(Reported) => return Err(context.into_reported_error()),
};
while iter.match_end() != source.len() {
R::report_incomplete_match(&mut context, iter.match_end());
if iter.backtrack(&mut context).is_err() {
return Err(context.into_reported_error());
}
}
Ok(iter)
}
#[derive(Copy, Clone)]
pub struct RegionParser<R: Region, P> {
parser: P,
phantom: PhantomData<fn() -> R>,
}
impl<R, P> Parser for RegionParser<R, P>
where
R: Region,
P: Parser,
{
type RawOutput = (P::Output,);
type Output = P::Output;
type Iter<'parse> = RegionParseIter<'parse, P>
where
R: 'parse,
P: 'parse;
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
R::check_at_start(context, start)?;
let (inner_end, outer_end) = R::find_end(context, start)?;
let source = context.source();
let iter = match_fully::<R, P>(&self.parser, &source[start..inner_end])
.map_err(|err| context.report(err.adjust_location(source, start)))?;
Ok(RegionParseIter { iter, outer_end })
}
29 30 31 32 33 34 35 36 37 38 39 40
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
let iter = self.parser.parse_iter(context, start)?;
Ok(StringParseIter {
source: context.source(),
start,
iter,
})
}
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<ExactParseIter, Reported> {
if context.source()[start..].starts_with(self) {
Ok(ExactParseIter {
end: start + self.len(),
})
} else {
Err(context.error_expected(start, &format!("{self:?}")))
}
}
}
impl Parser for char {
type Output = ();
type RawOutput = ();
type Iter<'parse> = ExactParseIter;
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<ExactParseIter, Reported> {
if context.source()[start..].starts_with(*self) {
Ok(ExactParseIter {
end: start + self.len_utf8(),
})
} else {
Err(context.error_expected(start, &format!("{self:?}")))
}
}
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
match context.source()[start..].chars().next() {
Some(c) if (self.predicate)(c) => Ok(CharParseIter {
c,
end: start + c.len_utf8(),
}),
_ => Err(context.error_expected(start, self.noun)),
}
}
}
impl<'parse> ParseIter<'parse> for CharParseIter {
type RawOutput = (char,);
fn match_end(&self) -> usize {
self.end
}
fn backtrack(&mut self, _context: &mut ParseContext<'parse>) -> Result<(), Reported> {
Err(Reported)
}
fn convert(&self) -> (char,) {
(self.c,)
}
}
impl Parser for CharOfParser {
type Output = usize;
type RawOutput = (usize,);
type Iter<'parse> = BasicParseIter<usize>;
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
if let Some(c) = context.source()[start..].chars().next() {
// Note: `self.options.find(c)` would give the wrong answer here: we
// want the number of characters before `c`, not bytes.
for (i, x) in self.options.chars().enumerate() {
if c == x {
return Ok(BasicParseIter {
value: i,
end: start + c.len_utf8(),
});
}
}
}
Err(context.error_expected(start, &format!("one of {:?}", self.options)))
}
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
match (self.regex)().find(&context.source()[start..]) {
None => Err(context.error_expected(start, any::type_name::<T>())),
Some(m) => match (self.parse_fn)(m.as_str()) {
Ok(value) => Ok(BasicParseIter {
end: start + m.end(),
value,
}),
Err(err) => Err(context.error_from_str_failed(
start,
start + m.end(),
any::type_name::<T>(),
format!("{err}"),
)),
},
}
}
sourcepub fn into_reported_error(self) -> ParseError
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.
Examples found in repository?
54 55 56 57 58 59 60 61 62 63 64 65 66 67
fn parse_raw(&self, s: &str) -> Result<Self::RawOutput> {
let mut ctx = ParseContext::new(s);
let mut it = match self.parse_iter(&mut ctx, 0) {
Ok(iter) => iter,
Err(Reported) => return Err(ctx.into_reported_error()),
};
while it.match_end() != s.len() {
ctx.error_extra(it.match_end());
if it.backtrack(&mut ctx).is_err() {
return Err(ctx.into_reported_error());
}
}
Ok(it.convert())
}
More examples
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
fn match_fully<'parse, R, P>(parser: &'parse P, source: &'parse str) -> Result<P::Iter<'parse>>
where
R: Region,
P: Parser,
{
let mut context = ParseContext::new(source);
let mut iter = match parser.parse_iter(&mut context, 0) {
Ok(iter) => iter,
Err(Reported) => return Err(context.into_reported_error()),
};
while iter.match_end() != source.len() {
R::report_incomplete_match(&mut context, iter.match_end());
if iter.backtrack(&mut context).is_err() {
return Err(context.into_reported_error());
}
}
Ok(iter)
}
sourcepub fn report(&mut self, err: ParseError) -> Reported
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.
Examples found in repository?
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
pub fn error_expected(&mut self, start: usize, expected: &str) -> Reported {
self.report(ParseError::new_expected(self.source(), start, expected))
}
/// Record an error when `FromStr::from_str` fails.
pub fn error_from_str_failed(
&mut self,
start: usize,
end: usize,
type_name: &'static str,
message: String,
) -> Reported {
self.report(ParseError::new_from_str_failed(
self.source(),
start,
end,
type_name,
message,
))
}
/// Record an "extra unparsed text after match" error.
pub fn error_extra(&mut self, location: usize) -> Reported {
self.report(ParseError::new_extra(self.source(), location))
}
More examples
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
let source = context.source();
if start == 0 || source[..start].ends_with('\n') {
Ok(())
} else {
Err(context.report(ParseError::new_bad_line_start(source, start)))
}
}
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
let source = context.source();
match source[start..].find('\n') {
Some(offset) => Ok((start + offset, start + offset + 1)),
None if start != source.len() => Ok((source.len(), source.len())),
None => Err(context.error_expected(source.len(), "line")),
}
}
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
context.report(ParseError::new_line_extra(context.source(), end))
}
}
/// A "section" is a sequence of zero or more nonblank lines, starting either
/// at the beginning of the input or immediately after a newline; followed by
/// either a blank line or the end of input.
#[derive(Debug, Clone, Copy)]
pub struct Section;
impl Region for Section {
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
let source = context.source();
if start == 0 || &source[..start] == "\n" || source[..start].ends_with("\n\n") {
Ok(())
} else {
Err(context.report(ParseError::new_bad_section_start(source, start)))
}
}
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
// FIXME BUG: unclear what this should do when looking at an empty
// section at end of input. presumably not repeat forever. (why does
// this not always hang forever if you try to use `sections`?)
let source = context.source();
match source[start..].find("\n\n") {
// ending at a blank line
Some(index) => Ok((start + index + 1, start + index + 2)),
// ending at the end of `source`
None if start < source.len() => Ok((source.len(), source.len())),
// no end-of-section delimiter found
None => Err(context.error_expected(source.len(), "section")),
}
}
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
context.report(ParseError::new_section_extra(context.source(), end))
}
}
/// Match but don't convert; just return the ParseIter on success. Expects all
/// of `source` to be matched, otherwise it's an error.
fn match_fully<'parse, R, P>(parser: &'parse P, source: &'parse str) -> Result<P::Iter<'parse>>
where
R: Region,
P: Parser,
{
let mut context = ParseContext::new(source);
let mut iter = match parser.parse_iter(&mut context, 0) {
Ok(iter) => iter,
Err(Reported) => return Err(context.into_reported_error()),
};
while iter.match_end() != source.len() {
R::report_incomplete_match(&mut context, iter.match_end());
if iter.backtrack(&mut context).is_err() {
return Err(context.into_reported_error());
}
}
Ok(iter)
}
#[derive(Copy, Clone)]
pub struct RegionParser<R: Region, P> {
parser: P,
phantom: PhantomData<fn() -> R>,
}
impl<R, P> Parser for RegionParser<R, P>
where
R: Region,
P: Parser,
{
type RawOutput = (P::Output,);
type Output = P::Output;
type Iter<'parse> = RegionParseIter<'parse, P>
where
R: 'parse,
P: 'parse;
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
R::check_at_start(context, start)?;
let (inner_end, outer_end) = R::find_end(context, start)?;
let source = context.source();
let iter = match_fully::<R, P>(&self.parser, &source[start..inner_end])
.map_err(|err| context.report(err.adjust_location(source, start)))?;
Ok(RegionParseIter { iter, outer_end })
}
sourcepub fn error_expected(&mut self, start: usize, expected: &str) -> Reported
pub fn error_expected(&mut self, start: usize, expected: &str) -> Reported
Record a foo expected
error.
Examples found in repository?
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<ExactParseIter, Reported> {
if context.source()[start..].starts_with(self) {
Ok(ExactParseIter {
end: start + self.len(),
})
} else {
Err(context.error_expected(start, &format!("{self:?}")))
}
}
}
impl Parser for char {
type Output = ();
type RawOutput = ();
type Iter<'parse> = ExactParseIter;
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<ExactParseIter, Reported> {
if context.source()[start..].starts_with(*self) {
Ok(ExactParseIter {
end: start + self.len_utf8(),
})
} else {
Err(context.error_expected(start, &format!("{self:?}")))
}
}
More examples
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
let source = context.source();
match source[start..].find('\n') {
Some(offset) => Ok((start + offset, start + offset + 1)),
None if start != source.len() => Ok((source.len(), source.len())),
None => Err(context.error_expected(source.len(), "line")),
}
}
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
context.report(ParseError::new_line_extra(context.source(), end))
}
}
/// A "section" is a sequence of zero or more nonblank lines, starting either
/// at the beginning of the input or immediately after a newline; followed by
/// either a blank line or the end of input.
#[derive(Debug, Clone, Copy)]
pub struct Section;
impl Region for Section {
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
let source = context.source();
if start == 0 || &source[..start] == "\n" || source[..start].ends_with("\n\n") {
Ok(())
} else {
Err(context.report(ParseError::new_bad_section_start(source, start)))
}
}
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
// FIXME BUG: unclear what this should do when looking at an empty
// section at end of input. presumably not repeat forever. (why does
// this not always hang forever if you try to use `sections`?)
let source = context.source();
match source[start..].find("\n\n") {
// ending at a blank line
Some(index) => Ok((start + index + 1, start + index + 2)),
// ending at the end of `source`
None if start < source.len() => Ok((source.len(), source.len())),
// no end-of-section delimiter found
None => Err(context.error_expected(source.len(), "section")),
}
}
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
match context.source()[start..].chars().next() {
Some(c) if (self.predicate)(c) => Ok(CharParseIter {
c,
end: start + c.len_utf8(),
}),
_ => Err(context.error_expected(start, self.noun)),
}
}
}
impl<'parse> ParseIter<'parse> for CharParseIter {
type RawOutput = (char,);
fn match_end(&self) -> usize {
self.end
}
fn backtrack(&mut self, _context: &mut ParseContext<'parse>) -> Result<(), Reported> {
Err(Reported)
}
fn convert(&self) -> (char,) {
(self.c,)
}
}
impl Parser for CharOfParser {
type Output = usize;
type RawOutput = (usize,);
type Iter<'parse> = BasicParseIter<usize>;
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
if let Some(c) = context.source()[start..].chars().next() {
// Note: `self.options.find(c)` would give the wrong answer here: we
// want the number of characters before `c`, not bytes.
for (i, x) in self.options.chars().enumerate() {
if c == x {
return Ok(BasicParseIter {
value: i,
end: start + c.len_utf8(),
});
}
}
}
Err(context.error_expected(start, &format!("one of {:?}", self.options)))
}
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
match (self.regex)().find(&context.source()[start..]) {
None => Err(context.error_expected(start, any::type_name::<T>())),
Some(m) => match (self.parse_fn)(m.as_str()) {
Ok(value) => Ok(BasicParseIter {
end: start + m.end(),
value,
}),
Err(err) => Err(context.error_from_str_failed(
start,
start + m.end(),
any::type_name::<T>(),
format!("{err}"),
)),
},
}
}
sourcepub fn error_from_str_failed(
&mut self,
start: usize,
end: usize,
type_name: &'static str,
message: String
) -> Reported
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.
Examples found in repository?
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
match (self.regex)().find(&context.source()[start..]) {
None => Err(context.error_expected(start, any::type_name::<T>())),
Some(m) => match (self.parse_fn)(m.as_str()) {
Ok(value) => Ok(BasicParseIter {
end: start + m.end(),
value,
}),
Err(err) => Err(context.error_from_str_failed(
start,
start + m.end(),
any::type_name::<T>(),
format!("{err}"),
)),
},
}
}
sourcepub fn error_extra(&mut self, location: usize) -> Reported
pub fn error_extra(&mut self, location: usize) -> Reported
Record an “extra unparsed text after match” error.
Examples found in repository?
54 55 56 57 58 59 60 61 62 63 64 65 66 67
fn parse_raw(&self, s: &str) -> Result<Self::RawOutput> {
let mut ctx = ParseContext::new(s);
let mut it = match self.parse_iter(&mut ctx, 0) {
Ok(iter) => iter,
Err(Reported) => return Err(ctx.into_reported_error()),
};
while it.match_end() != s.len() {
ctx.error_extra(it.match_end());
if it.backtrack(&mut ctx).is_err() {
return Err(ctx.into_reported_error());
}
}
Ok(it.convert())
}