An attempt combinator reverses a parser’s commitment when that parser fails, allowing an alternative branch to retry the same input. It is useful when several tokens must be examined before a grammar branch becomes unambiguous. section 9.5.3
The combinator coordinates alternatives through three rules:
- A parser normally commits after examining input; a committed failure stops the alternative chain. section 9.6.4
- attempt converts committed failures into uncommitted failures, so the next parser can run on the original input. section 9.6.4
- The implementation can avoid unnecessary branches while still permitting backtracking where grammar ambiguity requires it. section 9.5.3
Choosing between similar prefixes
For input "abra cadabra!", a branch parsing "abra" followed by spaces and another "abra" may fail only after consuming the shared prefix. Wrapping that branch in attempt lets the alternative "abra" followed by spaces and "cadabra!" retry; commitment occurs only after the ambiguous branch succeeds. section 9.5.3
extension [A](p: Parser[A]) def attempt: Parser[A] =
l => p(l).uncommit
enum Result[+A]:
...
def uncommit: Result[A] = this match
case Failure(e, true) => Failure(e, false)
case _ => this
Implement attempt by applying uncommit to the parser result, changing only committed failures while preserving successes and uncommitted failures. section 9.6.4
