All Downloads are FREE. Search and download functionalities are using the official Maven repository.

parsley.errors.combinator.scala Maven / Gradle / Ivy

There is a newer version: 5.0.0-M6
Show newest version
/* SPDX-FileCopyrightText: © 2021 Parsley Contributors 
 * SPDX-License-Identifier: BSD-3-Clause
 */
package parsley.errors

import parsley.Parsley

import parsley.internal.deepembedding.{frontend, singletons}

/** This module contains combinators that can be used to directly influence error messages of parsers.
  *
  * Error messages are, by default, not ''particularly'' descriptive. However, the combinators in this
  * module can be used to improve the generation of error messages by providing labels for expected
  * items, explanations for why things went wrong, custom error messages, custom unexpected error messages,
  * as well as correcting the offsets that error messages actually occurred at.
  *
  * @since 3.0.0
  *
  * @group combinators
  *
  * @groupprio fail 0
  * @groupname fail Failure Combinators
  * @groupdesc fail
  *     These combinator immediately fail the parser, with a more bespoke message.
  *
  * @groupprio adj 10
  * @groupname adj Error Adjustment Combinators
  * @groupdesc adj
  *     These combinators can affect at what position an error is caused at. They are
  *     opposites: where `amend` will ensure an error message is said to have generated
  *     at the position on entry to the combinator, `entrench` will resist these changes.
  *
  * @groupprio ext 5
  * @groupname ext Error Extension Combinators
  * @groupdesc ext
  *     These are implicit classes that, when in scope, enable additional combinators on
  *     parsers that interact with the error system in some way.
  */
object combinator {
    /** This combinator consumes no input and fails immediately with the given error messages.
      *
      * Produces a ''specialised'' error message where all the lines of the error are the
      * given `msgs` in order of appearance.
      *
      * @example {{{
      * val failing = fail("hello,", "this is an error message", "broken across multiple lines")
      * }}}
      *
      * @param msg0 the first message in the error message.
      * @param msgs the remaining messages that will make up the error message.
      * @return a parser that fails producing an error message consisting of all the given messages.
      * @since 3.0.0
      * @group fail
      */
    def fail(msg0: String, msgs: String*): Parsley[Nothing] = fail(1, msg0, msgs: _*)

    /** This combinator consumes no input and fails immediately with the given error messages.
      *
      * Produces a ''specialised'' error message where all the lines of the error are the
      * given `msgs` in order of appearance.
      *
      * @example {{{
      * val failing = fail("hello,", "this is an error message", "broken across multiple lines")
      * }}}
      *
      * @param caretWidth the size of the caret for this error: should ideally match the width of the cause of the error.
      * @param msg0 the first message in the error message.
      * @param msgs the remaining messages that will make up the error message.
      * @return a parser that fails producing an error message consisting of all the given messages.
      * @since 4.0.0
      * @group fail
      */
    def fail(caretWidth: Int, msg0: String, msgs: String*): Parsley[Nothing] = new Parsley(new singletons.Fail(caretWidth, (msg0 +: msgs): _*))

    /** This combinator consumes no input and fails immediately, setting the unexpected component
      * to the given item.
      *
      * Produces a ''trivial'' error message where the unexpected component of the error is
      * replaced with the given item `item`.
      *
      * @since 3.0.0
      * @param item the unexpected message for the error generated.
      * @return a parser that fails producing an error with `item` as the unexpected token.
      * @group fail
      */
    def unexpected(item: String): Parsley[Nothing] = unexpected(1, item)

    /** This combinator consumes no input and fails immediately, setting the unexpected component
      * to the given item.
      *
      * Produces a ''trivial'' error message where the unexpected component of the error is
      * replaced with the given item `item`.
      *
      * @since 4.0.0
      * @param caretWidth the size of the caret for this error: should ideally match the width of the cause of the error (the unexpected item).
      * @param item the unexpected message for the error generated.
      * @return a parser that fails producing an error with `item` as the unexpected token.
      * @group fail
      */
    def unexpected(caretWidth: Int, item: String): Parsley[Nothing] = new Parsley(new singletons.Unexpected(item, caretWidth))

    /** This combinator adjusts any error messages generated by the given parser so that they
      * occur at the position recorded on entry to this combinator (effectively as if no
      * input were consumed).
      *
      * This is useful if validation work is done
      * on the output of a parser that may render it invalid, but the error should point to the
      * beginning of the structure. This combinators effect can be cancelled with [[entrench `entrench`]].
      *
      * @param p a parser whose error messages should be adjusted.
      * @return a parser that parses `p` but ensures any errors generated occur as if no input were consumed.
      * @since 3.1.0
      * @group adj
      */
    def amend[A](p: Parsley[A]): Parsley[A] = new Parsley(new frontend.ErrorAmend(p.internal, partial = false))

    /** This combinator prevents the action of any enclosing `amend` on the errors generated by the given
      * parser.
      *
      * Sometimes, the error adjustments performed by [[amend `amend`]] should only affect errors generated
      * within a certain part of a parser and not the whole thing; in this case, `entrench` can be used
      * to protect sub-parsers from having their errors adjusted, providing a much more fine-grained
      * scope for error adjustment.
      *
      * @example In this example, the `ident` parser should not allow keywords, and these error messages
      * should be generated from the start of the identifier, not the end. However any errors generated
      * ''within'' the identifier itself should remain at their regular offsets.
      *
      * {{{
      * val ident = amend {
      *     entrench(stringOfSome(letter)).filterOut {
      *         case v if keywords.contains(v) => s"keyword $v cannot be an identifier"
      *     }
      * }
      * }}}
      *
      * @param p a parser whose error messages should not be adjusted by any surrounding [[amend `amend`]].
      * @return a parser that parses `p` but ensures any error messages are generated normally.
      * @since 3.1.0
      * @group adj
      */
    def entrench[A](p: Parsley[A]): Parsley[A] = new Parsley(new frontend.ErrorEntrench(p.internal))

    /** This combinator undoes the action of the `entrench` combinator on the given parser.
      *
      * Entrenchment is important for preventing the incorrect amendment of certain parts of sub-errors
      * for a parser, but it may be then undesireable to block further amendments from elsewhere in the
      * parser. This combinator can be used to cancel and entrenchment after the critical section has
      * passed.
      *
      * @param p a parser that should no longer be under the affect of an `entrench` combinator
      * @return a parser that parses `p` and allows its error messages to be amended.
      * @since 4.2.0
      */
    def dislodge[A](p: Parsley[A]): Parsley[A] = new Parsley(new frontend.ErrorDislodge(p.internal))

    /** This combinator first tries to amend the position of any error generated by the given parser,
      * and if the error was entrenched will dislodge it instead.
      *
      * @param p a parser whose error messages should be amended unless its been entrenched.
      * @return a parser that parses `p` but ensures any errors generated occur as if no input were consumed.
      * @since 4.2.0
      * @see [[amend `amend`]] and `[[dislodge `dislodge`]]
      */
    def amendThenDislodge[A](p: Parsley[A]): Parsley[A] = dislodge(amend(p))

    // These aren't going to be exposed and should be removed in 5.0.0 as well!
    @deprecated("this combinator is evil, because it renders the error at the wrong place unless it is amended!", "4.2.0")
    private [parsley] def partialAmend[A](p: Parsley[A]): Parsley[A] = new Parsley(new frontend.ErrorAmend(p.internal, partial = true))
    @deprecated("this combinator is evil, because it renders the error at the wrong place unless it is amended!", "4.2.0")
    private [parsley] def partialAmendThenDislodge[A](p: Parsley[A]): Parsley[A] = dislodge(partialAmend(p))

    /** This combinator marks any errors within the given parser as being ''lexical errors''.
      *
      * When an error is marked as a ''lexical error'', it sets a flag within the error that is
      * passed to [[parsley.errors.ErrorBuilder.unexpectedToken `ErrorBuilder.unexpectedToken`]]: this
      * should be used to prevent `Lexer`-based token extraction from being performed on an error,
      * since lexing errors cannot be the result of unexpected tokens.
      *
      * @param p the parser that serves as a token.
      * @return a parser that parses `p` but ensures any error messages are marked as lexical errors.
      * @since 4.0.0
      * @group adj
      */
    def markAsToken[A](p: Parsley[A]): Parsley[A] = new Parsley(new frontend.ErrorLexical(p.internal))

    /** This class exposes helpful combinators that are specialised for generating more helpful errors messages.
      *
      * This extension class operates on values that are convertible to parsers. It enables the use of
      * error combinators, which can be used for data validation, error annotation, or immediate failing.
      *
      * @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.
      * @version 3.0.0
      * @group ext
      *
      * @groupprio rich 0
      * @groupname rich Error Enrichment Combinators
      * @groupdesc rich
      *     These combinators add additional information - or refine the existing information within - to
      *     an error message that has been generated within the scope of the parser they have been called on.
      *     These are a very basic, but effective, way of improving the quality of error messages generated
      *     by Parsley.
      *
      * @groupprio filter 10
      * @groupname filter Filtering Combinators
      * @groupdesc filter
      *     These combinators perform filtering on a parser, with particular emphasis on generating meaningful
      *     error messages if the filtering fails. This is particularly useful for data validation within the
      *     parser, as very instructive error messages describing what went wrong can be generated. These combinators
      *     often filter using a `PartialFunction`: this may be because they combine filtering with mapping (in which
      *     case, the error message is provided separately), or the function may produce a `String`.
      *
      *     In these cases, the partial function is producing the error messages: if the input to the function is
      *     defined, this means that it is invalid and the filtering will fail using the message obtained from the
      *     succesful partial function invocation.
      *
      * @groupprio fail 20
      *
      * @define observably
      *     *a parser is said to ''observably'' consume input when error messages generated by a parser `p` occur at a deeper
      *     offset than `p` originally started at. While this sounds like it is the same as "having consumed input" for the
      *     purposes of backtracking, they are disjoint concepts:
      *
      *       1. in `attempt(p)`, `p` can ''observably'' consume input even though the wider parser does not consume input due to the `attempt`.
      *       1. in `amend(p)`, `p` can consume input and may not backtrack even though the consumption is not ''observable'' in the error
      *          message due to the `amend`.
      *
      * @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 partialAmend
      *     this combinator will generate error messages rooted at the start of the previously successful parse of this
      *     parser, but only in terms of their position: the actual error is generated at the end of the parse, which
      *     means it takes priority over sibling errors. This is because the error concerns the whole parse (for caret)
      *     and morally starts where this parser started (as it caused the failure), however, if it had full `amend`-like
      *     behaviour these errors would often disappear.
      */
    implicit final class ErrorMethods[P, +A](p: P)(implicit con: P => Parsley[A]) {
        /** This combinator filters the result of this parser using the given partial-predicate, succeeding only when the predicate is undefined.
          *
          * First, parse this parser. If it succeeds then take its result `x` and test if `pred.isDefinedAt(x)` is true. If it is
          * false, the parser succeeds, returning `x`. Otherwise, `pred(x)` will yield a reason `reason` and the parser will
          * fail with `reason` provided to the generated error message à la [[explain `explain`]].
          *
          * This is useful for performing data validation, but where a definitive reason can be given for the failure. In this instance,
          * the rest of the error message is generated as normal, with the expected and unexpected components still given, along with
          * any other generated reasons.
          *
          * @example {{{
          * scala> import parsley.character.letter
          * scala> val keywords = Set("if", "then", "else")
          * scala> val ident = stringOfSome(letter).filterOut {
          *     case v if keywords.contains(v) => s"keyword $v cannot be an identifier"
          * }
          * scala> ident.parse("hello")
          * val res0 = Success("hello")
          * scala> ident.parse("if")
          * val res1 = Failure(..)
          * }}}
          *
          * @since 3.0.0
          * @param pred the predicate that is tested against the parser result, which also generates errors.
          * @return a parser that returns the result of this parser if it fails the predicate.
          * @see [[parsley.Parsley.filterNot `filterNot`]], which is a basic version of this same combinator with no customised reason.
          * @see [[guardAgainst `guardAgainst`]], which is similar to `filterOut`, except it generates a ''specialised'' error as opposed to just a reason.
          * @note $autoAmend
          * @group filter
          */
        def filterOut(pred: PartialFunction[A, String]): Parsley[A] = new Parsley(new frontend.FilterOut(con(p).internal, pred))

        /** This combinator filters the result of this parser using the given partial-predicate, succeeding only when the predicate is undefined.
          *
          * First, parse this parser. If it succeeds then take its result `x` and test of `pred.isDefinedAt(x)` is true. If it is false,
          * the parser succeeds, returning `x`. Otherwise `pred(x)` will yield an error message `msg` and the parser will fail, producing
          * a ''specialised'' error only consisting of the message `msg` à la [[fail(caretWidth:Int,msg0:String,msgs:String*)*  `fail`]].
          *
          * This is useful for performing data validation, but where failure is not tied to the grammar but some other property of
          * the results. For instance, with the identifier example given for `filterOut`, it is reasonable to suggest that an identifier
          * was expected, and a keyword is not a valid identifier: i.e. these components still make sense. Where `guardAgainst` shines,
          * however, is in scenarios where the expected alternatives, or the unexpected component itself distract from the cause of the
          * error, or are irrelevant in some way. This might be because `guardAgainst` is checking some property of the data that is
          * ''possible'' to encode in the grammar, but otherwise ''impractical'', either because it is hard to maintain or generates
          * poor error messages for the user.
          *
          * @example Suppose we are parsing a data-format for graphs, and a restriction has been placed that ensures that the
          *          numeric identifiers of each declared node must be ordered. This has, for whatever reason, been specified
          *          as a syntactic property of the data. This is possible to encode using context-sensitive parsing (since each
          *          new node can only be parsed according to the previous one), but is fairly difficult and impractical. Instead,
          *          when all the declarations have been read, a `guardAgainst` can be used to prevent mis-ordering:
          * {{{
          * val node = integer
          * val nodes = many(node).guardAgainst {
          *     case ns if ns.nonEmpty
          *             && ns.zip(ns.tail).exists { case (x, y) => x == y } =>
          *         val Some((x, _)) = ns.zip(ns.tail).find { case (x, y) => x == y }
          *         Seq(s"node $x has been declared twice")
          *     case ns if ns.nonEmpty
          *             && ns.zip(ns.tail).exists { case (x, y) => x > y } =>
          *         val Some((x, y)) = ns.zip(ns.tail).find { case (x, y) => x > y }
          *         Seq(s"nodes $x and $y are declared in the wrong order", "all nodes should be ordered")
          * }
          * }}}
          *
          * @since 4.0.0
          * @param pred the predicate that is tested against the parser result, which also generates errors.
          * @return a parser that returns the result of this parser if it fails the predicate.
          * @see [[parsley.Parsley.filterNot `filterNot`]], which is a basic version of this same combinator with no customised error message.
          * @see [[filterOut `filterOut`]], which is similar to `guardAgainst`, except it generates a reason for failure and not a ''specialised'' error.
          * @see [[[collectMsg[B](msggen:A=>Seq[String])*  `collectMsg`]]], which is similar to `guardAgainst`, but can also transform the data on success.
          * @note $autoAmend
          * @group filter
          */
        def guardAgainst(pred: PartialFunction[A, Seq[String]]): Parsley[A] = new Parsley(new frontend.GuardAgainst(con(p).internal, pred))

        /** This combinator applies a partial function `pf` to the result of this parser if its result is defined for `pf`, failing if it is not.
          *
          * First, parse this parser. If it succeeds, test whether its result `x` is in the domain of the partial function `pf`. If it is defined for
          * `pf`, return `pf(x)`. Otherwise, if the result was undefined then fail producing a ''specialised'' error message with `msg`. Equivalent
          * to a `guardAgainst` (whose `msggen` ignores its argument) followed by a `map`.
          *
          * @example A good example of this combinator in use is for handling overflow in numeric literals.
          * {{{
          * val integer: Parsley[BigInt] = ...
          * // this should be amended/entrenched for best results
          * val int16: Parsley[Short] =
          *     integer.collectMsg("integer literal should within the range -2^16 to +2^16-1") {
          *         case x if x >= Short.MinValue
          *                && x <= Short.MaxValue => x.toShort
          *     }
          * }}}
          *
          * @since 3.0.0
          * @param msg0 the first error message to use if the filtering fails.
          * @param msgs the remaining error messages to use if the filtering fails.
          * @param pf the partial function used to both filter the result of this parser and transform it.
          * @return a parser which returns the result of this parser applied to pf, if possible.
          * @see [[parsley.Parsley.collect `collect`]], which is a basic version of this same combinator with no customised error message.
          * @see [[guardAgainst `guardAgainst`]], which is similar to `collectMsg`, except it does not transform the data.
          * @note $autoAmend
          * @group filter
          */
        def collectMsg[B](msg0: String, msgs: String*)(pf: PartialFunction[A, B]): Parsley[B] = this.collectMsg(_ => msg0 +: msgs)(pf)

        /** This combinator applies a partial function `pf` to the result of this parser if its result is defined for `pf`, failing if it is not.
          *
          * First, parse this parser. If it succeeds, test whether its result `x` is in the domain of the partial function `pf`. If it is defined for
          * `pf`, return `pf(x)`. Otherwise, if the result was undefined then fail producing a ''specialised'' error message with `msggen(x)`. Equivalent
          * to a `guardAgainst` followed by a `map`.
          *
          * @example A good example of this combinator in use is for handling overflow in numeric literals.
          * {{{
          * val integer: Parsley[BigInt] = ...
          * // this should be amended/entrenched for best results
          * val int16: Parsley[Short] =
          *     integer.collectMsg(n => Seq(s"integer literal $n is not within the range -2^16 to +2^16-1")) {
          *         case x if x >= Short.MinValue
          *                && x <= Short.MaxValue => x.toShort
          *     }
          * }}}
          *
          * @since 4.0.0
          * @param msggen a function that generates the error messages to use if the filtering fails.
          * @param pf the partial function used to both filter the result of this parser and transform it.
          * @return a parser which returns the result of this parser applied to pf, if possible.
          * @see [[parsley.Parsley.collect `collect`]], which is a basic version of this same combinator with no customised error message.
          * @see [[guardAgainst `guardAgainst`]], which is similar to `collectMsg`, except it does not transform the data.
          * @note $autoAmend
          * @group filter
          */
        def collectMsg[B](msggen: A => Seq[String])(pf: PartialFunction[A, B]): Parsley[B] = {
            this.guardAgainst{case x if !pf.isDefinedAt(x) => msggen(x)}.map(pf)
        }

        private def _unexpectedWhen(pred: PartialFunction[A, (String, Option[String])]): Parsley[A] = {
            new Parsley(new frontend.UnexpectedWhen(con(p).internal, pred))
        }

        /** This combinator filters the result of this parser using the given partial-predicate, succeeding only when the predicate is undefined.
          *
          * First, parse this parser. If it succeeds then take its result `x` and test if `pred.isDefinedAt(x)` is true. If it is
          * false, the parser succeeds, returning `x`. Otherwise, `pred(x)` will yield a unexpected label and the parser will
          * fail using [[combinator.unexpected(caretWidth:Int,item:String)* `unexpected`]] and that label.
          *
          * This is useful for performing data validation, but where a the failure results in the entire token being unexpected. In this instance,
          * the rest of the error message is generated as normal, with the expected components still given, along with
          * any generated reasons.
          *
          * @example {{{
          * scala> import parsley.character.letter
          * scala> val keywords = Set("if", "then", "else")
          * scala> val ident = stringOfSome(letter).unexpectedWhen {
          *     case v if keywords.contains(v) => s"keyword $v"
          * }
          * scala> ident.parse("hello")
          * val res0 = Success("hello")
          * scala> ident.parse("if")
          * val res1 = Failure(..)
          * }}}
          *
          * @since 3.0.0
          * @param pred the predicate that is tested against the parser result, which also generates errors.
          * @return a parser that returns the result of this parser if it fails the predicate.
          * @see [[parsley.Parsley.filterNot `filterNot`]], which is a basic version of this same combinator with no unexpected message.
          * @see [[filterOut `filterOut`]], which is a variant that produces a reason for failure as opposed to an unexpected message.
          * @see [[guardAgainst `guardAgainst`]], which is similar to `unexpectedWhen`, except it generates a ''specialised'' error instead.
          * @see [[unexpectedWithReasonWhen `unexpectedWithReasonWhen`]], which is similar, but also has a reason associated.
          * @note $autoAmend
          * @group filter
          */
        def unexpectedWhen(pred: PartialFunction[A, String]): Parsley[A] = this._unexpectedWhen {
            case x if pred.isDefinedAt(x) => (pred(x), None)
        }

        /** This combinator filters the result of this parser using the given partial-predicate, succeeding only when the predicate is undefined.
          *
          * First, parse this parser. If it succeeds then take its result `x` and test if `pred.isDefinedAt(x)` is true. If it is
          * false, the parser succeeds, returning `x`. Otherwise, `pred(x)` will yield a unexpected label and the parser will
          * fail using [[combinator.unexpected(caretWidth:Int,item:String)* `unexpected`]] and that label as well as a reason.
          *
          * This is useful for performing data validation, but where a the failure results in the entire token being unexpected. In this instance,
          * the rest of the error message is generated as normal, with the expected components still given, along with
          * any generated reasons.
          *
          * @example {{{
          * scala> import parsley.character.letter
          * scala> val keywords = Set("if", "then", "else")
          * scala> val ident = stringOfSome(letter).unexpectedWhenWithReason {
          *     case v if keywords.contains(v) => (s"keyword $v", "keywords cannot be identifiers")
          * }
          * scala> ident.parse("hello")
          * val res0 = Success("hello")
          * scala> ident.parse("if")
          * val res1 = Failure(..)
          * }}}
          *
          * @param pred the predicate that is tested against the parser result, which also generates errors.
          * @return a parser that returns the result of this parser if it fails the predicate.
          * @see [[parsley.Parsley.filterNot `filterNot`]], which is a basic version of this same combinator with no unexpected message or reason.
          * @see [[filterOut `filterOut`]], which is a variant that just produces a reason for failure with no unexpected message.
          * @see [[guardAgainst `guardAgainst`]], which is similar to `unexpectedWhen`, except it generates a ''specialised'' error instead.
          * @see [[unexpectedWhen `unexpectedWhen`]], which is similar, but with no associated reason.
            @since 4.2.0
          */
        def unexpectedWithReasonWhen(pred: PartialFunction[A, (String, String)]): Parsley[A] = this._unexpectedWhen {
            case x if pred.isDefinedAt(x) =>
                val (unex, reason) = pred(x)
                (unex, Some(reason))
        }

        /** This combinator changes the expected component of any errors generated by this parser.
          *
          * When this parser fails having not ''observably''* consumed input, the expected component of the generated
          * error message is set to be the given `item`.
          *
          * $observably
          * @param item the name to give to the expected component of any qualifying errors.
          * @return a parser that expects `item` on failure.
          * @since 3.0.0
          * @group rich
          */
        def label(item: String): Parsley[A] = new Parsley(new frontend.ErrorLabel(con(p).internal, item))

        /** This combinator changes the expected component of any errors generated by this parser.
          *
          * This is just an alias for the `label` combinator.
          *
          * ''Known as `<?>` in Haskell.''
          *
          * @since 3.0.0
          * @see [[label `label`]]
          * @group rich
          */
        def ?(item: String): Parsley[A] = this.label(item)

        /** This combinator adds a reason to error messages generated by this parser.
          *
          * When this parser fails having not ''observably''* consumed input, this combinator adds
          * `reason` to the error message, which should justify why the error occured. Unlike error
          * labels, which may persist if more progress is made having not consumed input, reasons
          * are not carried forward in the error message, and are lost.
          *
          * $observably
          * @param reason the reason why a parser failed.
          * @return a parser that produces the given reason for failure if it fails.
          * @since 3.0.0
          * @group rich
          */
        def explain(reason: String): Parsley[A] = new Parsley(new frontend.ErrorExplain(con(p).internal, reason))

        /** This combinator hides the expected component of errors generated by this parser.
          *
          * When this parser fails having not ''observably''* consumed input, this combinator
          * hides any error labels assigned to the expected item by any `label` combinators,
          * or indeed the base raw labels produced by the input consuming combinators themselves.
          *
          * This can be useful, say, for hiding whitespace labels, which are not normally useful
          * information to include in an error message for whitespace insensitive grammars.
          *
          * $observably
          * @since 3.0.0
          * @return a parser that does not produce an expected component on failure.
          * @see [[label `label`]]
          * @group rich
          */
        def hide: Parsley[A] = this.label("")

        // $COVERAGE-OFF$
        /** This combinator parses this parser and then fails, using the result of this parser to customise the error message.
          *
          * Similar to `fail`, but first parses this parser: if it succeeded, then its result `x` is used to form the error
          * message for the `fail` combinator by calling `msggen(x)`. If this parser fails, however, its error message will
          * be generated instead.
          *
          * @param msggen the generator function for error message, creating a message based on the result of this parser.
          * @return a parser that always fails, with the given generator used to produce the error message if this parser succeeded.
          * @note $partialAmend
          * @group fail
          * @deprecated this combinator has not proven to be particularly useful, and will be replaced by a more appropriate,
          *             not exactly the same, `verifiedFail` combinator.
          */
        @deprecated("This combinator will be removed in 5.0.0, without direct replacement", "4.2.0")
        def !(msggen: A => String): Parsley[Nothing] = partialAmendThenDislodge {
            parsley.position.internalOffsetSpan(entrench(con(p))).flatMap { case (os, x, oe) =>
                combinator.fail(oe - os, msggen(x))
            }
        }

        /** This combinator parses this parser and then fails, using the result of this parser to customise the unexpected component
          * of the error message.
          *
          * Similar to `unexpected`, but first parses this parser: if it succeeded, then its result `x` is used to form
          * the unexpected component of the generated error by calling `msggen(x)`. If this parser fails, however,
          * its error message will be returned untouched.
          *
          * @param msggen the generator function for error message, creating a message based on the result of this parser.
          * @return a parser that always fails, with the given generator used to produce an unexpected message if this parser succeeded.
          * @note $partialAmend
          * @group fail
          * @deprecated this combinator has not proven to be particularly useful and will be removed in 5.0.0. There is a similar, but not
          *             exact replacement called `verifiedUnexpected`.
          * @since 4.2.0
          */
        @deprecated("This combinator will be removed in 5.0.0, without direct replacement", "4.2.0")
        def unexpected(msggen: A => String): Parsley[Nothing] = partialAmendThenDislodge {
            parsley.position.internalOffsetSpan(entrench(con(p))).flatMap { case (os, x, oe) =>
                combinator.unexpected(oe - os, msggen(x))
            }
        }
        // $COVERAGE-ON$
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy