License | BSD-3-Clause |
---|---|
Maintainer | Jamie Willis, Gigaparsec Maintainers |
Stability | stable |
Safe Haskell | Safe |
Language | Haskell2010 |
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.
Terminology
Observably consumes input: 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:
- in
atomic p
,p
can observably consume input even though the wider parser does not consume input due to theatomic
. - in
amend p
,p
can consume input and may not backtrack even though the consumption is not observable in the error message due to theamend
.
Since: 0.2.0.0
Synopsis
- label :: Set String -> Parsec a -> Parsec a
- (<?>) :: Parsec a -> Set String -> Parsec a
- hide :: Parsec a -> Parsec a
- explain :: String -> Parsec a -> Parsec a
- emptyWide :: Word -> Parsec a
- fail :: NonEmpty String -> Parsec a
- failWide :: Word -> NonEmpty String -> Parsec a
- unexpected :: String -> Parsec a
- unexpectedWide :: Word -> String -> Parsec a
- amend :: Parsec a -> Parsec a
- partialAmend :: Parsec a -> Parsec a
- entrench :: Parsec a -> Parsec a
- dislodge :: Parsec a -> Parsec a
- dislodgeBy :: Word -> Parsec a -> Parsec a
- amendThenDislodge :: Parsec a -> Parsec a
- amendThenDislodgeBy :: Word -> Parsec a -> Parsec a
- partialAmendThenDislodge :: Parsec a -> Parsec a
- partialAmendThenDislodgeBy :: Word -> Parsec a -> Parsec a
- markAsToken :: Parsec a -> Parsec a
- filterSWith :: ErrorGen a -> (a -> Bool) -> Parsec a -> Parsec a
- mapMaybeSWith :: ErrorGen a -> (a -> Maybe b) -> Parsec a -> Parsec b
- filterOut :: (a -> Maybe String) -> Parsec a -> Parsec a
- guardAgainst :: (a -> Maybe [String]) -> Parsec a -> Parsec a
- unexpectedWhen :: (a -> Maybe String) -> Parsec a -> Parsec a
- unexpectedWithReasonWhen :: (a -> Maybe (String, String)) -> Parsec a -> Parsec a
- mapEitherS :: (a -> Either (NonEmpty String) b) -> Parsec a -> Parsec b
Error Enrichment Combinators
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 gigaparsec.
:: Set String | the names to give to the expected component of any qualifying errors. |
-> Parsec a | the parser to apply the labels to |
-> Parsec a |
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 items.
(<?>) :: Parsec a -> Set String -> Parsec a infix 0 Source #
This combinator changes the expected component of any errors generated by this parser.
This is just an alias for the label
combinator.
hide :: Parsec a -> Parsec a Source #
This combinator suppresses the entire error message generated by a given parser.
When this parser fails having not observably consumed input, this combinator
replaces any error generated by the given parser to match the empty
combinator.
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.
:: String | reason the reason why a parser failed. |
-> Parsec a | the parser to apply the reason to |
-> Parsec a |
This combinator adds a reason to error messages generated by this parser.
When this parser fails having not observably consumed input, this combinator adds a 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.
Failure Combinators
These combinators immediately fail the parser, with a more bespoke message.
This combinator fails immediately, with a caret of the given width and no other information.
By producing basically no information, this combinator is principally for adjusting the
caret-width of another error, rather than the value empty
, which is used to fail with
no effect on error content.
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.
Examples
>>>
let failing = fail ["hello,", "this is an error message", "broken across multiple lines"]
:: Word | the width of the caret for the error produced by this combinator. |
-> NonEmpty String | the messages that will make up the error message. |
-> Parsec a |
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. The caret width of the message is set to the
given value.
Examples
>>>
let failing = fail 3 ["hello,", "this is an error message", "broken across multiple lines"]
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.
:: Word | the width of the caret for the error produced by this combinator. |
-> String | the unexpected message for the error generated. |
-> Parsec a |
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. The caret width of the message is set to the given value.
Error Adjustment Combinators
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.
amend :: Parsec a -> Parsec a Source #
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
.
Examples
>>>
let greeting = string "hello world" <* char '!'
>>>
parseRepl (greeting <?> ["greeting"]) "hello world."
(line 1, column 12): unexpected "." expected "!" >hello world. ^>>>
parseRepl (amend greeting <?> ["greeting"]) "hello world."
(line 1, column 1): unexpected "h" expected greeting >hello world. ^
partialAmend :: Parsec a -> Parsec a Source #
This combinator adjusts any error messages generated by the given parser so that they occur at the position recorded on entry to this combinator, but retains the original offset.
Similar to amend
, but retains the original offset the error occurred at. This is known
as its underlying offset as opposed to the visual presentation offset. To the reader, the
error messages appears as if no input was consumed, but for the purposes of error message merging
the error is still deeper. A key thing to note is that two errors can only merge if they are at
the same presentation and underlying offsets: if they are not the deeper of the two dominates.
The ability for an error to still dominate others after partial amendment can be useful for allowing it to avoid being lost when merging with errors that are deeper than the presentation offset but shallower than the underlying.
entrench :: Parsec a -> Parsec a Source #
This combinator prevents the action of any enclosing amend
on the errors generated by the given
parser.
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.
dislodge :: Parsec a -> Parsec a Source #
This combinator undoes the action of any entrench
combinators 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 all entrenchment after the critical section has passed.
:: Word | The number of |
-> Parsec a | The parser containing the |
-> Parsec a |
This combinator undoes the action of the given number of entrench
combinators 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 all entrenchment after the critical section has passed.
amendThenDislodge :: Parsec a -> Parsec a Source #
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.
:: Word | The number of |
-> Parsec a | The parser to amend and dislodge. |
-> Parsec a |
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 the given number of times instead.
partialAmendThenDislodge :: Parsec a -> Parsec a Source #
This combinator first tries to partially amend the position of any error generated by the given parser, and if the error was entrenched will dislodge it instead.
partialAmendThenDislodgeBy :: Word -> Parsec a -> Parsec a Source #
This combinator first tries to partially amend the position of any error generated by the given parser, and if the error was entrenched will dislodge it the given number of times instead.
markAsToken :: Parsec a -> Parsec a Source #
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 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.
:: ErrorGen a | the generator for the error message when |
-> (a -> Bool) |
|
-> Parsec a |
|
-> Parsec a | a parser that returns the result of |
This combinator filters the result of this parser with the given predicate,
generating an error with the given error generator if the function returned False
.
Behaves like filterS
, except the error message generated is fine-tuned with
respect to the parser's result and width of input consumed, using the ErrorGen
.
First, parse p
.
If it succeeds then take its result x
and apply it to the predicate pred
.
- If
pred x
is true, then returnx
. - If
pred x
is false, the combinator fails and produces an error message descrbied by the givenErrorGen
.
If p
failed, this combinator fails without using the given ErrorGen
.
Since: 0.2.2.0
:: ErrorGen a | how to generate error messages based on the result of this parser. |
-> (a -> Maybe b) |
|
-> Parsec a |
|
-> Parsec b | a parser which returns the result of |
This combinator applies a function f
to the result of a parser p
: if it returns
, Just
yy
is returned.
If p
succeeds but f
returns Nothing
, then this combinator produces an error message according to the given ErrorGen
.
Behaves like mapMaybeS
with a customised error message.
First, run the given parser p
.
If it succeeds, apply the function f
to the result x
.
- If
f x
returns
, returnJust
yy
. - If
f x
returns
, the combinator fails and produces an error message descrbied by the givenNothing
ErrorGen
.
If p
failed, this combinator fails without using the given ErrorGen
.
This is a more efficient way of performing a filterS
and fmap
at the same time.
Since: 0.2.2.0
:: (a -> Maybe String) |
|
-> Parsec a |
|
-> Parsec a | a parser that returns |
Filters the result of the given parser p
using a given function f
, succeeding only if pred
returns Nothing
.
First, parse p
.
If it succeeds then take its result x
and apply it to the predicate pred
.
- If
pred x
isNothing
, then returnx
. - If
pred x
is
, the combinator fails, and produces a vanilla error with the givenJust
reasonreason
.
If p
failed, this combinator fails in the same way.
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.
Since: 0.2.2.0
:: (a -> Maybe [String]) |
|
-> Parsec a |
|
-> Parsec a | a parser that returns the result of |
This combinator filters the result of the given parser using the given partial-predicate, succeeding only when the predicate returns Nothing
.
First, run the given parser p
.
If it succeeds with result x
, then apply the given partial-predicate pred
:
- If
pred x
isNothing
, the parser succeeds, returningx
. - If
pred x
is
, the parser fails, producing a specialised error only consisting of the messagesJust
msgsmsgs
.
This is useful for performing data validation, but where failure is not tied to the grammar but some other property of the results.
Where guardAgainst
shines, however, is in scenarios where the expected alternatives,
or the unexpected component itself distracts from the cause of the error, or is 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.
Since: 0.2.2.0
:: (a -> Maybe String) |
|
-> Parsec a |
|
-> Parsec a | a parser that returns the result of |
This combinator filters the result of the given parser using the given partial-predicate, succeeding only when the predicate is undefined.
First, run the given parser p
.
If it succeeds with result x
, apply the given partial-predicate, pred
:
- If
pred x
isNothing
, the parser succeeds, returning x. - If
pred x
is
, the parser fails usingJust
lblunexpected
with the labellbl
.
If p
fails, then this combinator also fails.
This is useful for performing data validation where 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.
Since: 0.2.2.0
unexpectedWithReasonWhen Source #
:: (a -> Maybe (String, String)) |
|
-> Parsec a |
|
-> Parsec a | a parser that returns the result of |
This combinator filters the result of the given parser using the given partial-predicate, succeeding only when the predicate is undefined.
First, run the given parser p
.
If it succeeds with result x
, apply the given partial-predicate, pred
:
- If
pred x
isNothing
, the parser succeeds, returning x. - If
pred x
is
, the parser fails using the unexpected labelJust
(lbl, rsn)lbl
and reasonrsn
.
If p
fails, then this combinator also fails.
This is useful for performing data validation where 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.
Since: 0.2.2.0
:: (a -> Either (NonEmpty String) b) |
|
-> Parsec a |
|
-> Parsec b | a parser which returns the result of |
This combinator conditionally transforms the result of this parser with a given function:
if a
is returned, it generates an error with the messages Left
xsxs
; otherwise returns y
for the result
.Right
y
First, run the given parser p
.
If it succeeds, apply the function f
to the result x
.
- If
f x
returns
, returnRight
yy
. - If
f x
returns
, the combinator fails with the error messagesLeft
xsxs
.
If p
failed, this combinator also fails.
Since: 0.2.4.0