Skip to content

Commit

Permalink
patterns documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mie6 committed Jan 22, 2023
1 parent 9654413 commit 517db1d
Showing 1 changed file with 66 additions and 28 deletions.
94 changes: 66 additions & 28 deletions parsley/shared/src/main/scala/parsley/errors/patterns.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,103 @@ import parsley.Parsley

import parsley.internal.deepembedding.frontend

/** TODO:
/** This module contains combinators that help facilitate the error message generational patterns ''Verified Errors'' and ''Preventative Errors''.
*
* In particular, exposes an extension class `VerifiedErrors` that facilitates creating verified errors in many different formats.
*
* @since 4.2.0
*/
object patterns {
/** TODO:
/** This class exposes combinators related to the ''Verified Errors'' parser design pattern.
*
* This extension class operates on values that are convertible to parsers. The combinators it enables
* allow for the parsing of known illegal values, providing richer error messages in case they succeed.
*
* @param p
* @return
* @constructor This constructor should not be called manually, it is designed to be used via Scala's implicit resolution.
* @param p the value that this class is enabling methods on.
* @param con a conversion that allows values convertible to parsers to be used.
* @tparam P the type of base value that this class is used on (the conversion to `Parsley`) is summoned automatically.
* @since 4.2.0
*
* @define autoAmend
* when this combinator fails (and not this parser itself), it will generate errors rooted at the start of the
* parse (as if [[parsley.errors.combinator$.amend `amend`]] had been used) and the caret will span the entire
* successful parse of this parser.
*
* @define attemptNonTerminal
* when this parser is not to be considered as a terminal error, use `attempt` around the ''entire'' combinator to
* allow for backtracking if this parser succeeds (and therefore fails).
*
* @define Ensures this parser does not succeed, failing with a
*/
implicit final class VerifiedErrors[P, A](p: P)(implicit con: P => Parsley[A]) {
private def verified(msggen: Either[A => Seq[String], Option[A => String]]) = new Parsley(new frontend.VerifiedError(con(p).internal, msggen))
private def verifiedUnexpected(reason: Option[A => String]) = verified(Right(reason))

// partial amend semantics are BAD: they render the error in the wrong position unless amended anyway
// Document that `attempt` may be used when this is an informative but not terminal error.
/** TODO:
/** Ensures this parser does not succeed, failing with a specialised error based on this parsers result if it does.
*
* If this parser succeeds, input is consumed and this combinator will fail, producing an error message
* based on the parsed result. However, if this parser fails, no input is consumed and an empty error is generated.
* This parser will produce no labels if it fails.
*
* @param msggen
* @return
* @param msggen the function that generates the error messages from the parsed value.
* @since 4.2.0
* @note $autoAmend
* @note $attemptNonTerminal
*/
def verifiedFail(msggen: A => Seq[String]): Parsley[Nothing] = verified(Left(msggen))

/** TODO:
/** Ensures this parser does not succeed, failing with a specialised error if it does.
*
* @param msg0
* @param msgs
* @return
* If this parser succeeds, input is consumed and this combinator will fail, producing an error message
* based on the given messages. However, if this parser fails, no input is consumed and an empty error is generated.
* This parser will produce no labels if it fails.
*
* @param msg0 the first message in the error message.
* @param msgs the remaining messages that will make up the error message.
* @since 4.2.0
* @note $autoAmend
* @note $attemptNonTerminal
*/
def verifiedFail(msg0: String, msgs: String*): Parsley[Nothing] = this.verifiedFail(_ => msg0 +: msgs)

// TODO: Documentation and testing ahead of future release
// like notFollowedBy, but does consume input on "success" and always fails
// Document that `attempt` may be used when this is an informative but not terminal error.

/** TODO:
/** Ensures this parser does not succeed, failing with a vanilla error with an unexpected message and caret spanning the parse.
*
* If this parser succeeds, input is consumed and this combinator will fail, producing an unexpected message the same width as
* the parse. However, if this parser fails, no input is consumed and an empty error is generated.
* This parser will produce no labels if it fails.
*
* @return
* @since 4.2.0
* @note $autoAmend
* @note $attemptNonTerminal
*/
def verifiedUnexpected: Parsley[Nothing] = this.verifiedUnexpected(None)

/** TODO:
/** Ensures this parser does not succeed, failing with a vanilla error with an unexpected message and caret spanning the parse and a given reason.
*
* If this parser succeeds, input is consumed and this combinator will fail, producing an unexpected message the same width as
* the parse along with the given reason. However, if this parser fails, no input is consumed and an empty error is generated.
* This parser will produce no labels if it fails.
*
* @param reason
* @return
* @param reason the reason that this parser is illegal.
* @since 4.2.0
* @note $autoAmend
* @note $attemptNonTerminal
*/
def verifiedUnexpected(reason: String): Parsley[Nothing] = this.verifiedUnexpected(_ => reason)

/** TODO:
/** Ensures this parser does not succeed, failing with a vanilla error with an unexpected message and caret spanning the parse and a reason generated
* from this parser's result.
*
* @param reason
* @return
* If this parser succeeds, input is consumed and this combinator will fail, producing an unexpected message the same width as
* the parse along with a reason generated from the successful parse. However, if this parser fails, no input is consumed and an empty error
* is generated. This parser will produce no labels if it fails.
*
* @param reason a function that produces a reason for the error given the parsed result.
* @since 4.2.0
* @note $autoAmend
* @note $attemptNonTerminal
*/
def verifiedUnexpected(reason: A => String): Parsley[Nothing] = this.verifiedUnexpected(Some(reason))

private def verified(msggen: Either[A => Seq[String], Option[A => String]]) = new Parsley(new frontend.VerifiedError(con(p).internal, msggen))
private def verifiedUnexpected(reason: Option[A => String]) = verified(Right(reason))
}
}

0 comments on commit 517db1d

Please sign in to comment.