Closed AndrewSav closed 3 years ago
Hi @AndrewSav - apologies for the slightly rushed answer, but hopefully some clues:
Backtracking in the presence of alternation means that if the parser takes the first branch, regardless of how well that branch matched, it will discard error information and report a failure in the second branch. This makes producing good error messages hard, especially if alternatives have similar possible prefixes.
This article describes try() and the trade-offs around backtracking, IIRC: https://lorgonblog.wordpress.com/2007/12/06/monadic-parser-combinators-part-four/
HTH!
I would like to get a bit more understanding in how Backtrack and IsPartial works. I'll explain what I understand so far, and then I ask question.
Backtrack
Backtrack
property of theResult
object indicates that the next parser should discard the results of that parser if it failed and try something else, depending on what the next parser is.Or
,Many
andIgnoreMany
currently use itBacktrack
is internal, so onlyOr
,Many
andIgnoreMany
use is it and nothing else.Backtrack
is set byTry
. It passed on byApply
, but only forTokenListParser
, not forTextParser
.Result,
Backtrack
usually do not "flow".IsPartial
IsPartial
property of theResult
, used whenHasValue
is false. It means that the Position has been moved by the previous parser, despite that the parsing failed.IsPartial
is true, we have a combined parser, and some of the parsers in it succeeded, but than a later parser in it failed.Or
,Many
andIgnoreMany
combinators All of them check condition where HasValue is false, and Backtrack is false and IsPartial is true, which means that the previous parser failed, does not have the backtrack, but has moved the current position.Or
parser will NOT process the seconds parser if the above condition is true. It will process the next parser if the above condition is false when the first parser fails.Many
parser keeps applying the previous parser until it fails. Once that has happened, it checks for the condition above. If true, it fails, otherwise it returns the results from all the parsers applied so far.IgnoreMany
returnsUnit.Value
when succeeds. It applies the same logic asMany
combinator above.Usage Practically, you will use the above in code in form of
A.Try().Or(B)
,A.Try().Many(B)
andA.Try().IgnoreMany(B)
Example:
Many
If we only have parser that does not consume any input on failure we do not need backtrack:But once we started combining parsers, we inevitable end up with ones that fail after consuming partial input:
At this point 1 will be consumed and 2 will be not. So:
In the last example, we have a partial parse of "45" and without the backtrack it fails. Let's try the same with backtrack:
In this case, Many has parsed as much as it could and then stopped just before
45
.Example:
Or
Similarly, parser that do not consume any input on failure work work the same with or without backtrack, so I won't repeat it here. Here are a couple of parsers that do consume input on failure:Here we are not using Backtrack and
k3
cannot be matched because the first parser started consuming characters, and "k" is consumed.Question From the examples above it looks to me like Backtracking is a sensible default. I cannot imagine an example where I would like
Many
orOr
to fail, if the previous parser consumed some input. In most cases, I would simply want it to stop and return whatever is consumed. Note, that even without Backtracking,Many
will not fail if it cannot match at all, it will just return empty output.Why does it makes sense to insist on using
Try
, and not making backtracking the default mode? Why does it makes sense for a failed parser to leave the position mid-parse? In which use cases the behaviour of consuming some input for a parser when failing could be desirable?