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

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

The newest version!
package parsley.errors

import parsley.Parsley
import parsley.internal.deepembedding

import parsley.combinator.choice

/** This module contains combinators that can be used to directly influence error messages of parsers.
  * @since 3.0.0
  */
object combinator {
    /**
      * The `fail(msgs)` parser consumes no input and fails with `msg` as the error message
      * @since 3.0.0
      */
    def fail(msgs: String*): Parsley[Nothing] = new Parsley(new deepembedding.Fail(msgs: _*))

    /**
      * The `unexpected(msg)` parser consumes no input and fails with `msg` as an unexpected error
      * @since 3.0.0
      */
    def unexpected(msg: String): Parsley[Nothing] = new Parsley(new deepembedding.Unexpected(msg))

    /**
      * This combinator adjusts the error messages that are generated within its scope so that they
      * happen at the position on entry to the combinator. 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]]`
      *
      * @param p A parser whose error messages should be adjusted
      * @since 3.1.0
      */
    def amend[A](p: =>Parsley[A]): Parsley[A] = new Parsley(new deepembedding.ErrorAmend(p.internal))

    /**
      * Sometimes, the error adjustments performed by `[[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.
      *
      * @param p A parser whose error messages should not be adjusted by any surrounding `[[amend]]`
      * @since 3.1.0
      */
    def entrench[A](p: =>Parsley[A]): Parsley[A] = new Parsley(new deepembedding.ErrorEntrench(p.internal))

    /**
      * This class exposes helpful combinators that are specialised for generating more helpful errors messages.
      * For a description of why the library is designed in this way,
      * see: [[https://github.com/j-mie6/Parsley/wiki/Understanding-the-API the Parsley wiki]]
      *
      * @param p The parser which serves as the method receiver
      * @param con A conversion (if required) to turn `p` into a parser
      * @version 3.0.0
      */
    implicit final class ErrorMethods[P, +A](p: =>P)(implicit con: P => Parsley[A]) {
        /** Filter the value of a parser; if the value returned by the parser is defined for the given partial function, then
          * the `filterOut` fails, using the result of the function as the ''reason'' (see [[explain]]), otherwise the parser
          * succeeds
          * @param pred The predicate that is tested against the parser result
          * @return The result of the invokee if the value failed the predicate
          * @since 3.0.0
          */
        def filterOut(pred: PartialFunction[A, String]): Parsley[A] = new Parsley(new deepembedding.FilterOut(con(p).internal, pred))
        /** Attempts to first filter the parser to ensure that `pf` is defined over it. If it is, then the function `pf`
          * is mapped over its result. Roughly the same as a `guard` then a `map`.
          * @param pf The partial function
          * @param msg The message used for the error if the input failed the check
          * @return The result of applying `pf` to this parsers value (if possible), or fails
          * @since 3.0.0
          */
        def collectMsg[B](msg: String)(pf: PartialFunction[A, B]): Parsley[B] = this.guardAgainst{case x if !pf.isDefinedAt(x) => msg}.map(pf)
        /** Attempts to first filter the parser to ensure that `pf` is defined over it. If it is, then the function `pf`
          * is mapped over its result. Roughly the same as a `guard` then a `map`.
          * @param pf The partial function
          * @param msggen Generator function for error message, generating a message based on the result of the parser
          * @return The result of applying `pf` to this parsers value (if possible), or fails
          * @since 3.0.0
          */
        def collectMsg[B](msggen: A => String)(pf: PartialFunction[A, B]): Parsley[B] = this.guardAgainst{case x if !pf.isDefinedAt(x) => msggen(x)}.map(pf)
        /** Similar to `filterOut`, except the error message generated yields a ''true failure''. This means that it will
          * uses the same mechanism as [[fail]], as opposed to the reason provided by [[filterOut]]
          * @param pred The predicate that is tested against the parser result and produces error messages
          * @return The result of the invokee if it fails the predicate
          * @since 2.8.0
          */
        def guardAgainst(pred: PartialFunction[A, String]): Parsley[A] = new Parsley(new deepembedding.GuardAgainst(con(p).internal, pred))
        /** Alias for `label`
          * @since 3.0.0 */
        def ?(msg: String): Parsley[A] = this.label(msg)
        /** Sets the expected message for a parser. If the parser fails then `expected msg` will added to the error.
          * The label is only applied if the error message does not observe any consumption of input.
          * @since 3.0.0 */
        def label(msg: String): Parsley[A] = new Parsley(new deepembedding.ErrorLabel(con(p).internal, msg))
        /** Similar to `label`, except instead of providing an expected message replacing the original tag, this combinator
          * adds a ''reason'' that the error occurred. This is in complement to the label. The `reason` is only added when
          * the parser fails, and will disappear if any further progress in the parser is made (unlike labels, which may
          * reappear as "hints").
          * @param reason The reason why a parser failed
          * @since 3.0.0
          */
        def explain(reason: String): Parsley[A] = new Parsley(new deepembedding.ErrorExplain(con(p).internal, reason))
        /** Hides the "expected" error message for a parser.
          * @since 3.0.0 */
        def hide: Parsley[A] = this.label("")
        /** Same as `fail`, except allows for a message generated from the result of the failed parser. In essence, this
          * is equivalent to `p >>= (x => fail(msggen(x))` but requires no expensive computations from the use of `>>=`.
          * @param msggen The generator function for error message, creating a message based on the result of invokee
          * @return A parser that fails if it succeeds, with the given generator used to produce the error message
          */
        def !(msggen: A => String): Parsley[Nothing] = new Parsley(new deepembedding.FastFail(con(p).internal, msggen))
        /** Same as `unexpected`, except allows for a message generated from the result of the failed parser. In essence,
          * this is equivalent to `p >>= (x => unexpected(x))` but requires no expensive computations from the use of
          * `>>=`
          * @param msggen The generator function for error message, creating a message based on the result of invokee
          * @return A parser that fails if it succeeds, with the given generator used to produce an unexpected message
          */
        def unexpected(msggen: A => String): Parsley[Nothing] = new Parsley(new deepembedding.FastUnexpected(con(p).internal, msggen))
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy