gigaparsec-0.3.1.0: src/Text/Gigaparsec/Token/Errors.hs
{-# LANGUAGE Safe #-}
{-# LANGUAGE NoMonomorphismRestriction, BlockArguments, OverloadedLists, OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
{-|
Module : Text.Gigaparsec.Token.Errors
Description : This module contains the relevant functionality for configuring the error messages generated by the parsers provided by the Lexer.
License : BSD-3-Clause
Maintainer : Jamie Willis, Gigaparsec Maintainers
Stability : experimental
This module contains the relevant functionality for configuring the error messages generated by the parsers provided by the 'Text.Gigaparsec.Token.Lexer.Lexer'.
@since 0.2.2.0
-}
module Text.Gigaparsec.Token.Errors (
-- ** Error Configuration
-- | This is the main type that defines the configuration for errors from the Lexer.
ErrorConfig,
-- *** Labelling and Explanation Configuration
{-|
Labels and Explanations make parser errors more descriptive, by giving a name to the parser that failed, or providing a reason a particular parser failed.
These help make error messages more descriptive than the usual 'unexpected symbol' messages.
'LabelConfigurable' types describe errors that can provide labels upon failure,
'ExplainConfigurable' types those that can providing reaons for failure,
and 'LabelWithExplainConfigurable' types those that can provide both.
These configs are used by the 'ErrorConfig', whose fields include combinators that configure both labels and/or explains for simple description configurations.
-}
-- **** Label Configurations
{-|
Labels provide names to a parser to be used upon failure.
A 'LabelConfig' or 'LabelConfigurable' type is able to produce these sorts of errors.
-}
LabelConfig, LabelConfigurable(..),
-- **** Explain Configurations
{-|
Explains provide a reason that a parser has failed.
An 'ExplainConfig' or 'ExplainConfigurable' type is able to produce these sorts of errors.
-}
ExplainConfig, ExplainConfigurable(..),
-- **** Label-with-Explain Configurations
{-|
'LabelWithExplainConfig' combines 'LabelConfig' and 'ExplainConfig',
describing configs that are able to give labels and/or reasons to errors.
'LabelWithExplainConfigurable' types are those able to produce an error providing both labels and a reason.
-}
LabelWithExplainConfig, LabelWithExplainConfigurable(..),
-- **** No-special-error-generating Configs
{-|
Sometimes it is best to defer to the default errors, instead of generating specialised ones.
Configs that allow this are 'NotConfigurable'.
-}
NotConfigurable(..),
-- *** Numeric Errors
{-|
These control the errors generated with the numeric ('Text.Gigaparsec.Token.Descriptions.NumericDesc') component of the 'Text.Gigaparsec.Token.Lexer.Lexer'.
-}
labelNumericBreakChar, labelIntegerUnsignedDecimal,
labelIntegerUnsignedHexadecimal, labelIntegerUnsignedOctal,
labelIntegerUnsignedBinary, labelIntegerUnsignedNumber,
labelIntegerSignedDecimal,
labelIntegerSignedHexadecimal, labelIntegerSignedOctal,
labelIntegerSignedBinary, labelIntegerSignedNumber,
labelIntegerDecimalEnd,
labelIntegerHexadecimalEnd, labelIntegerOctalEnd,
labelIntegerBinaryEnd, labelIntegerNumberEnd,
filterIntegerOutOfBounds,
-- **** Bit-Widths
{-|
Some of the numeric errors can take into account the supposed bit-width of the parsed data.
Using 'Bits' achieves this.
-}
Bits(B8, B16, B32, B64),
-- *** Name Errors
{-|
These control the errors generated with the names ('Text.Gigaparsec.Token.Descriptions.NameDesc') component of the 'Text.Gigaparsec.Token.Lexer.Lexer'.
-}
labelNameIdentifier, labelNameOperator,
unexpectedNameIllegalIdentifier, unexpectedNameIllegalOperator,
filterNameIllFormedIdentifier, filterNameIllFormedOperator,
-- *** Text Errors
{-|
These control the errors generated with the text ('Text.Gigaparsec.Token.Descriptions.TextDesc') component of the 'Text.Gigaparsec.Token.Lexer.Lexer'.
-}
labelCharAscii, labelCharLatin1, labelCharUnicode,
labelCharAsciiEnd, labelCharLatin1End, labelCharUnicodeEnd,
labelStringAscii, labelStringLatin1, labelStringUnicode,
labelStringAsciiEnd, labelStringLatin1End, labelStringUnicodeEnd,
labelStringCharacter, labelGraphicCharacter, labelEscapeSequence,
labelEscapeNumeric, labelEscapeNumericEnd, labelEscapeEnd,
labelStringEscapeEmpty, labelStringEscapeGap, labelStringEscapeGapEnd,
-- *** Filtering Errors
{-|
These configs and combinators describe how to generate the filters to rule out specific parses.
They can generate types of vanilla error or specialised errors.
-}
-- **** Filtering Configs
FilterConfig,
BasicFilterConfigurable(..),
VanillaFilterConfig, VanillaFilterConfigurable(..),
SpecializedFilterConfig, SpecializedFilterConfigurable(..),
-- **** Filtering Combinators
filterCharNonAscii, filterCharNonLatin1, filterStringNonAscii, filterStringNonLatin1,
filterEscapeCharRequiresExactDigits, filterEscapeCharNumericSequenceIllegal,
-- *** Verifying Bad Characters
{-|
These types and combinators help implement the Verified Error™ pattern for illegal string and literal characters.
-}
-- **** Configs
{-|
These types help configure the Verified Error pattern for illegal string and character literal characters,
used by 'verifiedCharBadCharsUsedInLiteral' and 'verifiedStringBadCharsUsedInLiteral'.
-}
VerifiedBadChars,
Unverified(..),
-- **** Combinators
badCharsFail, badCharsReason,
verifiedCharBadCharsUsedInLiteral, verifiedStringBadCharsUsedInLiteral,
-- *** Symbol Errors
{-|
These control the errors generated with the symbol ('Text.Gigaparsec.Token.Descriptions.SymbolDesc') component of the 'Text.Gigaparsec.Token.Lexer.Lexer'
-}
labelSymbol, labelSymbolEndOfKeyword, labelSymbolEndOfOperator,
-- *** Space Errors
{-|
These control the errors generated with the space ('Text.Gigaparsec.Token.Descriptions.SpaceDesc') component of the 'Text.Gigaparsec.Token.Lexer.Lexer'.
-}
labelSpaceEndOfLineComment, labelSpaceEndOfMultiComment,
-- ** The Default Configuration
defaultErrorConfig,
) where
import Data.Set (Set)
import Data.Map (Map)
import Data.Map qualified as Map (empty)
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.List.NonEmpty qualified as NonEmpty (toList)
import Data.Kind (Constraint)
import Text.Gigaparsec.Internal.Token.BitBounds (Bits(B8, B16, B32, B64))
import Numeric (showIntAtBase)
import Data.Char (intToDigit, ord)
import Text.Gigaparsec.Errors.DefaultErrorBuilder (from, disjunct, toString)
import Text.Gigaparsec.Internal.Token.Errors (
LabelWithExplainConfig(LELabelAndReason, LELabel, LEHidden, LEReason, LENotConfigured),
LabelConfig(LLabel, LHidden, LNotConfigured), ExplainConfig(EReason, ENotConfigured),
FilterConfig(VSBecause, VSUnexpected, VSUnexpectedBecause, VSBasicFilter, VSSpecializedFilter),
SpecializedFilterConfig(SSpecializedFilter, SBasicFilter),
VanillaFilterConfig(VBecause, VUnexpected, VUnexpectedBecause, VBasicFilter),
VerifiedBadChars(BadCharsUnverified, BadCharsFail, BadCharsReason)
)
{-|
An 'ErrorConfig' specifies how errors should be produced by the 'Text.Gigaparsec.Token.Lexer.Lexer'.
The Lexer is set up to produce a variety of different errors via @label@-ing, @explain@-ing, and @filter@-ing,
and some applications of the Verified and Preventative error patterns.
The exact content of those errors can be configured here.
Errors can be suppressed or specified with different levels of detail,
or even switching between vanilla or specialised errors.
A custom 'ErrorConfig' should be created by extending the 'defaultErrorConfig'
with record updates to override the relevant default fields.
Not configuring something does not mean it will not appear in the message,
but will mean it uses the underlying base errors.
-}
type ErrorConfig :: *
data ErrorConfig =
ErrorConfig {
-- | How a numeric break character should (like @_@) be referred to or explained within an error.
labelNumericBreakChar :: !LabelWithExplainConfig
-- | How unsigned decimal integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerUnsignedDecimal :: Maybe Bits -> LabelWithExplainConfig
-- | How unsigned hexadecimal integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerUnsignedHexadecimal :: Maybe Bits -> LabelWithExplainConfig
-- | How unsigned octal integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerUnsignedOctal :: Maybe Bits -> LabelWithExplainConfig
-- | How unsigned binary integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerUnsignedBinary :: Maybe Bits -> LabelWithExplainConfig
-- | How generic unsigned integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerUnsignedNumber :: Maybe Bits -> LabelWithExplainConfig
-- | How signed decimal integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerSignedDecimal :: Maybe Bits -> LabelWithExplainConfig
-- | How signed hexadecimal integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerSignedHexadecimal :: Maybe Bits -> LabelWithExplainConfig
-- | How signed octal integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerSignedOctal :: Maybe Bits -> LabelWithExplainConfig
-- | How signed binary integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerSignedBinary :: Maybe Bits -> LabelWithExplainConfig
-- | How generic signed integers (of a possibly given bit-width) should be referred to or explained within an error.
, labelIntegerSignedNumber :: Maybe Bits -> LabelWithExplainConfig
-- | How the fact that the end of a decimal integer literal is expected should be referred to within an error.
, labelIntegerDecimalEnd :: LabelConfig
-- | How the fact that the end of a hexadecimal integer literal is expected should be referred to within an error.
, labelIntegerHexadecimalEnd :: LabelConfig
-- | How the fact that the end of an octal integer literal is expected should be referred to within an error.
, labelIntegerOctalEnd :: LabelConfig
-- | How the fact that the end of a binary integer literal is expected should be referred to within an error.
, labelIntegerBinaryEnd :: LabelConfig
-- | How the fact that the end of a generic integer literal is expected should be referred to within an error.
, labelIntegerNumberEnd :: LabelConfig
-- | Describes the content of the error when an integer literal is parsed and it is not within the required bit-width.
--
-- In @'filterIntegerOutOfBounds' x y r@:
--
-- - @x@ is the smallest value the integer could have taken,
-- - @y@ is the largest value the integer could have taken, and
-- - @r@ is the radix that the integer was parsed using
, filterIntegerOutOfBounds :: Integer
-> Integer
-> Int
-> FilterConfig Integer
-- | How an identifier should be referred to in an error message.
, labelNameIdentifier :: String
-- | How a user-defined operator should be referred to in an error message.
, labelNameOperator :: String
-- | How an illegally parsed hard keyword should be referred to as an unexpected component.
, unexpectedNameIllegalIdentifier :: String -> String
-- | How an illegally parsed hard operator should be referred to as an unexpected component.
, unexpectedNameIllegalOperator :: String -> String
-- | When parsing identifiers that are required to have specific start characters, how bad identifiers should be reported.
, filterNameIllFormedIdentifier :: FilterConfig String
-- | When parsing operators that are required to have specific start/end characters, how bad operators should be reported.
, filterNameIllFormedOperator :: FilterConfig String
-- | How an ASCII character literal should be referred to or explained in error messages.
, labelCharAscii :: LabelWithExplainConfig
-- | How a Latin1 (extended ASCII) character literal should be referred to or explained in error messages.
, labelCharLatin1 :: LabelWithExplainConfig
-- | How a UTF-16 character literal should be referred to or explained in error messages.
, labelCharUnicode :: LabelWithExplainConfig
-- | How the closing quote of an ASCII character literal should be referred to in error messages.
, labelCharAsciiEnd :: LabelConfig
-- | How the closing quote of a Latin1 (extended ASCII) character literal should be referred to in error messages.
, labelCharLatin1End :: LabelConfig
-- | How the closing quote of a UTF-16 character literal should be referred to in error messages.
, labelCharUnicodeEnd :: LabelConfig
{-| How an ASCII-only string should literal be referred to or explained in error messages.
With the arguments @'labelStringAscii' multi raw@:
- @multi@: whether this is for a multi-line string
- @raw@: whether this is for a raw string
-}
, labelStringAscii :: Bool -> Bool -> LabelWithExplainConfig
{-| How a Latin1-only string should literal be referred to or explained in error messages.
With the arguments @'labelStringLatin1' multi raw@:
- @multi@: whether this is for a multi-line string
- @raw@: whether this is for a raw string
-}
, labelStringLatin1 :: Bool -> Bool -> LabelWithExplainConfig
{-| How a UTF-16-only string should literal be referred to or explained in error messages.
With the arguments @'labelStringUnicode' multi raw@:
- @multi@: whether this is for a multi-line string
- @raw@: whether this is for a raw string
-}
, labelStringUnicode :: Bool -> Bool -> LabelWithExplainConfig
{- | How the closing quote(s) of an ASCII string literal should be referred to in error messages.
With the arguments @'labelStringAsciiEnd' multi raw@:
- @multi@: whether this is for a multi-line string
- @raw@: whether this is for a raw string
-}
, labelStringAsciiEnd :: Bool -> Bool -> LabelConfig
{-| How the closing quote(s) of a Latin1 string literal should be referred to in error messages.
With the arguments @'labelStringLatin1End' multi raw@:
- @multi@: whether this is for a multi-line string
- @raw@: whether this is for a raw string
-}
, labelStringLatin1End :: Bool -> Bool -> LabelConfig
{-| How the closing quote(s) of a UTF-16 string literal should be referred to in error messages.
With the arguments @'labelStringUnicodeEnd' multi raw@:
- @multi@: whether this is for a multi-line string
- @raw@: whether this is for a raw string
-}
, labelStringUnicodeEnd :: Bool -> Bool -> LabelConfig
-- | How general string characters should be referred to in error messages.
, labelStringCharacter :: LabelConfig
-- | How a graphic character (a regular character in the literal) should be referred to or explained in error messages.
, labelGraphicCharacter :: LabelWithExplainConfig
-- | How an escape sequence should be referred to or explained in error messages.
, labelEscapeSequence :: LabelWithExplainConfig
{- | How a numeric escape sequence (after the opening character) should be referred to or explained in error messages.
In @'labelEscapeNumeric' radix@:
- @radix@: the radix this specific configuration applies to.
-}
, labelEscapeNumeric :: Int -> LabelWithExplainConfig
{-| How the end of a numeric escape sequence (after a prefix) should be referred to or explained in error messages.
In @'labelEscapeNumericEnd' prefix radix@:
- @prefix@: the character that started this sequence
- @radix@: the radix this specific configuration applies to.
-}
, labelEscapeNumericEnd :: Char -> Int -> LabelWithExplainConfig
-- | How the end of an escape sequence (anything past the opening character) should be referred to or explained within an error message.
, labelEscapeEnd :: LabelWithExplainConfig
-- | How zero-width escape characters should be referred to within error messages.
, labelStringEscapeEmpty :: LabelConfig
-- | How string gaps should be referred to within error messages.
, labelStringEscapeGap :: LabelConfig
-- | How the end of a string gap (the closing slash) should be referred to within error messages.
, labelStringEscapeGapEnd :: LabelConfig
-- | When a non-ASCII character is found in a ASCII-only character literal, specifies how this should be reported.
, filterCharNonAscii :: VanillaFilterConfig Char
-- | When a non-Latin1 character is found in a Latin1-only character literal, specifies how this should be reported.
, filterCharNonLatin1 :: VanillaFilterConfig Char
-- | When a non-ASCII character is found in a ASCII-only string literal, specifies how this should be reported.
, filterStringNonAscii :: SpecializedFilterConfig String
-- | When a non-Latin1 character is found in a Latin1-only string literal, specifies how this should be reported.
, filterStringNonLatin1 :: SpecializedFilterConfig String
{-| When a numeric escape sequence requires a specific number of digits but this was not successfully parsed,
this describes how to report that error given the number of successfully parsed digits up this point.
In @'filterEscapeCharRequiresExactDigits' radix needed@:
- @radix@: the radix used for this numeric escape sequence.
- @needed@: the possible numbers of digits required.
-}
, filterEscapeCharRequiresExactDigits :: Int -> NonEmpty Word -> SpecializedFilterConfig Word
{-| When a numeric escape sequence is not legal, this describes how to report that error, given the original illegal character.
In @'filterEscapeCharNumericSequenceIllegal' maxEscape radix@:
- @maxEscape@: the largest legal escape character.
- @radix@: the radix used for this numeric escape sequence.
-}
, filterEscapeCharNumericSequenceIllegal :: Char -> Int -> SpecializedFilterConfig Integer
-- | Character literals parse either graphic characters or escape characters.
, verifiedCharBadCharsUsedInLiteral :: VerifiedBadChars
-- | String literals parse either graphic characters or escape characters.
, verifiedStringBadCharsUsedInLiteral :: VerifiedBadChars
{-|
Gives names and/or reasons to symbols.
Symbols that do not appear in the map are assumed to be 'NotConfigurable'.
-}
, labelSymbol :: Map String LabelWithExplainConfig
-- don't bother with these until parsley standardises
--, defaultSymbolKeyword :: Labeller
--, defaultSymbolOperator :: Labeller
--, defaultSymbolPunctuaton :: Labeller
-- | How the required end of a given keyword should be specified in an error.
, labelSymbolEndOfKeyword :: String -> String
-- | How the required end of a given operator should be specified in an error.
, labelSymbolEndOfOperator :: String -> String
-- | How the end of a single-line comment should be described or explained.
, labelSpaceEndOfLineComment :: LabelWithExplainConfig
-- | How the end of a multi-line comment should be described or explained.
, labelSpaceEndOfMultiComment :: LabelWithExplainConfig
}
{-|
The default configuration.
This serves as the base configuration, to be customised using record field updates.
-}
defaultErrorConfig :: ErrorConfig
defaultErrorConfig = ErrorConfig {..}
where labelNumericBreakChar = notConfigured
labelIntegerUnsignedDecimal = const notConfigured
labelIntegerUnsignedHexadecimal = const notConfigured
labelIntegerUnsignedOctal = const notConfigured
labelIntegerUnsignedBinary = const notConfigured
labelIntegerUnsignedNumber = const notConfigured
labelIntegerSignedDecimal = const notConfigured
labelIntegerSignedHexadecimal = const notConfigured
labelIntegerSignedOctal = const notConfigured
labelIntegerSignedBinary = const notConfigured
labelIntegerSignedNumber = const notConfigured
labelIntegerDecimalEnd = notConfigured
labelIntegerHexadecimalEnd = notConfigured
labelIntegerOctalEnd = notConfigured
labelIntegerBinaryEnd = notConfigured
labelIntegerNumberEnd = notConfigured
filterIntegerOutOfBounds small big nativeRadix = specializedFilter
(outOfBounds small big nativeRadix)
labelNameIdentifier = "identifier"
labelNameOperator = "operator"
unexpectedNameIllegalIdentifier = ("keyword " ++)
unexpectedNameIllegalOperator = ("reserved operator " ++)
filterNameIllFormedIdentifier = unexpected ("identifier " ++)
filterNameIllFormedOperator = unexpected ("operator " ++)
labelCharAscii = notConfigured
labelCharLatin1 = notConfigured
labelCharUnicode = notConfigured
labelCharAsciiEnd = notConfigured
labelCharLatin1End = notConfigured
labelCharUnicodeEnd = notConfigured
labelStringAscii _ _ = notConfigured
labelStringLatin1 _ _ = notConfigured
labelStringUnicode _ _ = notConfigured
labelStringAsciiEnd _ _ = notConfigured
labelStringLatin1End _ _ = notConfigured
labelStringUnicodeEnd _ _ = notConfigured
labelStringCharacter = label ["string character"]
labelGraphicCharacter = label ["graphic character"]
labelEscapeSequence = label ["escape sequence"]
labelEscapeNumeric _ = notConfigured
labelEscapeNumericEnd _ _ = notConfigured
labelEscapeEnd = labelAndReason ["end of escape sequence"] "invalid escape sequence"
labelStringEscapeEmpty = notConfigured
labelStringEscapeGap = label ["string gap"]
labelStringEscapeGapEnd = label ["end of string gap"]
filterCharNonAscii = because (const "non-ascii character")
filterCharNonLatin1 = because (const "non-latin1 character")
filterStringNonAscii =
specializedFilter (const ["non-ascii characters in string literal, this is not allowed"])
filterStringNonLatin1 =
specializedFilter (const ["non-latin1 characters in string literal, this is not allowed"])
filterEscapeCharRequiresExactDigits _ needed = specializedFilter \got ->
let ~(Just formatted) = disjunct True (map show (NonEmpty.toList needed))
in [toString ("numeric escape requires " <> formatted <> "digits, but only got" <> from got)]
filterEscapeCharNumericSequenceIllegal maxEscape radix =
let messages :: Integer -> NonEmpty String
messages c
| c > toInteger (ord maxEscape) = singleton $
showIntAtBase (toInteger radix) intToDigit c
(" is greater than the maximum character value of "
++ showIntAtBase (toInteger radix) intToDigit (toInteger (ord maxEscape)) "")
| otherwise = singleton $ "illegal unicode character: "
++ showIntAtBase (toInteger radix) intToDigit c ""
in specializedFilter messages
verifiedCharBadCharsUsedInLiteral = unverified
verifiedStringBadCharsUsedInLiteral = unverified
labelSymbol = Map.empty
-- defaultSymbolKeyword = Label
-- defaultSymbolOperator = Label
-- defaultSymbolOperator = NotConfigured
labelSymbolEndOfKeyword = ("end of " ++)
labelSymbolEndOfOperator = ("end of " ++)
labelSpaceEndOfLineComment = label ["end of comment"]
labelSpaceEndOfMultiComment = label ["end of comment"]
outOfBounds :: Integer -> Integer -> Int -> Integer -> NonEmpty String
outOfBounds small big radix _n = singleton $
"literal is not within the range " ++ resign small (" to " ++ resign big "")
where resign n
| n < 0 = ('-' :) . showIntAtBase (toInteger radix) intToDigit (abs n)
| otherwise = showIntAtBase (toInteger radix) intToDigit n
{-|
A type @config@ is 'ExplainConfigurable' when it is able to configure errors that make labels.
This means @config@ is able to behave like a 'LabelConfig'
-}
type LabelConfigurable :: * -> Constraint
class LabelConfigurable config where
-- | The configuration produces the labels in the given set, which should not be empty.
label :: Set String -- ^ The labels to produce.
-> config
-- | Configure a label by stating it must be hidden.
hidden :: config
instance LabelConfigurable LabelConfig where
label = LLabel
hidden = LHidden
instance LabelConfigurable LabelWithExplainConfig where
label = LELabel
hidden = LEHidden
{-|
A type @config@ is 'ExplainConfigurable' when it is able to configure an error which provides a reason.
This means @config@ is able to behave like an 'ExplainConfig'
-}
type ExplainConfigurable :: * -> Constraint
class ExplainConfigurable config where
-- | The error should be displayed using the given reason.
reason :: String -- ^ The reason a parser failed.
-> config
instance ExplainConfigurable ExplainConfig where reason = EReason
instance ExplainConfigurable LabelWithExplainConfig where reason = LEReason
{-|
A type @config@ is 'LabelWithExplainConfigurable' when it is able to configure an error which provides both a reason and labels.
-}
type LabelWithExplainConfigurable :: * -> Constraint
class LabelWithExplainConfigurable config where
-- | The configuration produces the labels in the given set, and provides the given reason.
labelAndReason :: Set String -- ^ The labels to produce
-> String -- ^ The reason for the error.
-> config
instance LabelWithExplainConfigurable LabelWithExplainConfig where labelAndReason = LELabelAndReason
{-|
A type @config@ is 'NotConfigurable' when it is able to specify that no special error should be generated,
and instead use default errors.
-}
type NotConfigurable :: * -> Constraint
class NotConfigurable config where
-- | No special error should be generated, and default errors should be used instead.
notConfigured :: config
instance NotConfigurable LabelWithExplainConfig where notConfigured = LENotConfigured
instance NotConfigurable LabelConfig where notConfigured = LNotConfigured
instance NotConfigurable ExplainConfig where notConfigured = ENotConfigured
{-|
A type @config@ is 'VanillaFilterConfigurable' when it is able to generate vanilla errors.
This means @config@ is able to behave like a 'VanillaFilterConfig'
-}
type VanillaFilterConfigurable :: (* -> *) -> Constraint
class VanillaFilterConfigurable config where
-- | Ensure the filter generates a /vanilla/ unexpected item for the given failing parse.
unexpected :: (a -> String) -- ^ a function producing the unexpected label for the given value.
-> config a
-- | Ensure the filter generates a /vanilla/ reason for the given failing parse.
because :: (a -> String) -- ^ a function producing the reason for the given value.
-> config a
-- | Ensure the filter generates a /vanilla/ unexpected item and a reason for the given failing parse.
unexpectedBecause
:: (a -> String) -- ^ @reason@, a function producing the reason for the given value.
-> (a -> String) -- ^ @unexpected@, a function producing the unexpected label for the given value.
-> config a
instance VanillaFilterConfigurable FilterConfig where
unexpected = VSUnexpected
because = VSBecause
unexpectedBecause = VSUnexpectedBecause
instance VanillaFilterConfigurable VanillaFilterConfig where
unexpected = VUnexpected
because = VBecause
unexpectedBecause = VUnexpectedBecause
{-|
A type @config@ is 'SpecializedFilterConfigurable' when it is able to generate specialised errors.
This means @config@ is able to behave like a 'SpecializedFilterConfig'
-}
type SpecializedFilterConfigurable :: (* -> *) -> Constraint
class SpecializedFilterConfigurable config where
-- | Ensure the filter generates /specialised/ messages for the given failing parse.
specializedFilter
:: (a -> NonEmpty String) -- ^ @message@: a function producing the message for the given value.
-> config a
instance SpecializedFilterConfigurable FilterConfig where
specializedFilter = VSSpecializedFilter
instance SpecializedFilterConfigurable SpecializedFilterConfig where
specializedFilter = SSpecializedFilter
{-|
A type @config@ is 'BasicFilterConfigurable' when it is able to provide /no/ error configuration,
and instead defer to a regular filter.
-}
type BasicFilterConfigurable :: (* -> *) -> Constraint
class BasicFilterConfigurable config where
-- | No error configuration for the filter is specified; a regular filter is used instead.
basicFilter :: config a
instance BasicFilterConfigurable FilterConfig where basicFilter = VSBasicFilter
instance BasicFilterConfigurable VanillaFilterConfig where basicFilter = VBasicFilter
instance BasicFilterConfigurable SpecializedFilterConfig where basicFilter = SBasicFilter
-- | Makes "bad literal chars" generate a bunch of given messages in a specialised error.
-- Requires a map from bad characters to their messages.
badCharsFail :: Map Char (NonEmpty String) -> VerifiedBadChars
badCharsFail = BadCharsFail
-- | Makes "bad literal chars" generate a reason in a vanilla error.
-- Requires a map from bad characters to their reasons.
badCharsReason :: Map Char String -> VerifiedBadChars
badCharsReason = BadCharsReason
{-|
A type @config@ is 'Unverified' when it can disable the verified error for bad characters:
this may improve parsing performance slightly on the failure case.
-}
type Unverified :: * -> Constraint
class Unverified config where
-- | A configuration which disables the verified error for bad characters.
unverified :: config
instance Unverified VerifiedBadChars where unverified = BadCharsUnverified
singleton :: a -> NonEmpty a
singleton x = x :| []