packages feed

gigaparsec-0.2.4.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 (
    ErrorConfig(
      labelNumericBreakChar, labelIntegerUnsignedDecimal,
      labelIntegerUnsignedHexadecimal, labelIntegerUnsignedOctal,
      labelIntegerUnsignedBinary, labelIntegerUnsignedNumber,
      labelIntegerSignedDecimal,
      labelIntegerSignedHexadecimal, labelIntegerSignedOctal,
      labelIntegerSignedBinary, labelIntegerSignedNumber,
      labelIntegerDecimalEnd,
      labelIntegerHexadecimalEnd, labelIntegerOctalEnd,
      labelIntegerBinaryEnd, labelIntegerNumberEnd,
      filterIntegerOutOfBounds,
      labelNameIdentifier, labelNameOperator,
      unexpectedNameIllegalIdentifier, unexpectedNameIllegalOperator,
      filterNameIllFormedIdentifier, filterNameIllFormedOperator,
      labelCharAscii, labelCharLatin1, labelCharUnicode,
      labelCharAsciiEnd, labelCharLatin1End, labelCharUnicodeEnd,
      labelStringAscii, labelStringLatin1, labelStringUnicode,
      labelStringAsciiEnd, labelStringLatin1End, labelStringUnicodeEnd,
      labelStringCharacter, labelGraphicCharacter, labelEscapeSequence,
      labelEscapeNumeric, labelEscapeNumericEnd, labelEscapeEnd,
      labelStringEscapeEmpty, labelStringEscapeGap, labelStringEscapeGapEnd,
      filterCharNonAscii, filterCharNonLatin1, filterStringNonAscii, filterStringNonLatin1,
      filterEscapeCharRequiresExactDigits, filterEscapeCharNumericSequenceIllegal,
      verifiedCharBadCharsUsedInLiteral, verifiedStringBadCharsUsedInLiteral,
      labelSymbol, labelSymbolEndOfKeyword, labelSymbolEndOfOperator,
      labelSpaceEndOfLineComment, labelSpaceEndOfMultiComment
    ),
    defaultErrorConfig,
    LabelWithExplainConfig, LabelWithExplainConfigurable(..),
    LabelConfig, LabelConfigurable(..),
    ExplainConfig, ExplainConfigurable(..),
    NotConfigurable(..),
    FilterConfig,
    VanillaFilterConfig, VanillaFilterConfigurable(..),
    SpecializedFilterConfig, SpecializedFilterConfigurable(..),
    BasicFilterConfigurable(..),
    VerifiedBadChars, badCharsFail, badCharsReason,
    Unverified(..),
    Bits(B8, B16, B32, B64)
  ) 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)
  )

type ErrorConfig :: *
data ErrorConfig =
  ErrorConfig { labelNumericBreakChar :: !LabelWithExplainConfig
              , labelIntegerUnsignedDecimal :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerUnsignedHexadecimal :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerUnsignedOctal :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerUnsignedBinary :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerUnsignedNumber :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerSignedDecimal :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerSignedHexadecimal :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerSignedOctal :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerSignedBinary :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerSignedNumber :: Maybe Bits -> LabelWithExplainConfig
              , labelIntegerDecimalEnd :: LabelConfig
              , labelIntegerHexadecimalEnd :: LabelConfig
              , labelIntegerOctalEnd :: LabelConfig
              , labelIntegerBinaryEnd :: LabelConfig
              , labelIntegerNumberEnd :: LabelConfig
              , filterIntegerOutOfBounds :: Integer -> Integer -> Int -> FilterConfig Integer
              , labelNameIdentifier :: String
              , labelNameOperator :: String
              , unexpectedNameIllegalIdentifier :: String -> String
              , unexpectedNameIllegalOperator :: String -> String
              , filterNameIllFormedIdentifier :: FilterConfig String
              , filterNameIllFormedOperator :: FilterConfig String
              , labelCharAscii :: LabelWithExplainConfig
              , labelCharLatin1 :: LabelWithExplainConfig
              , labelCharUnicode :: LabelWithExplainConfig
              , labelCharAsciiEnd :: LabelConfig
              , labelCharLatin1End :: LabelConfig
              , labelCharUnicodeEnd :: LabelConfig
              , labelStringAscii :: Bool -> Bool -> LabelWithExplainConfig
              , labelStringLatin1 :: Bool -> Bool -> LabelWithExplainConfig
              , labelStringUnicode :: Bool -> Bool -> LabelWithExplainConfig
              , labelStringAsciiEnd :: Bool -> Bool -> LabelConfig
              , labelStringLatin1End :: Bool -> Bool -> LabelConfig
              , labelStringUnicodeEnd :: Bool -> Bool -> LabelConfig
              , labelStringCharacter :: LabelConfig
              , labelGraphicCharacter :: LabelWithExplainConfig
              , labelEscapeSequence :: LabelWithExplainConfig
              , labelEscapeNumeric :: Int -> LabelWithExplainConfig
              , labelEscapeNumericEnd :: Char -> Int -> LabelWithExplainConfig
              , labelEscapeEnd :: LabelWithExplainConfig
              , labelStringEscapeEmpty :: LabelConfig
              , labelStringEscapeGap :: LabelConfig
              , labelStringEscapeGapEnd :: LabelConfig
              , filterCharNonAscii :: VanillaFilterConfig Char
              , filterCharNonLatin1 :: VanillaFilterConfig Char
              , filterStringNonAscii :: SpecializedFilterConfig String
              , filterStringNonLatin1 :: SpecializedFilterConfig String
              , filterEscapeCharRequiresExactDigits :: Int -> NonEmpty Word -> SpecializedFilterConfig Word
              , filterEscapeCharNumericSequenceIllegal :: Char -> Int -> SpecializedFilterConfig Integer
              , verifiedCharBadCharsUsedInLiteral :: VerifiedBadChars
              , verifiedStringBadCharsUsedInLiteral :: VerifiedBadChars
              , labelSymbol :: Map String LabelWithExplainConfig
              -- don't bother with these until parsley standardises
              --, defaultSymbolKeyword :: Labeller
              --, defaultSymbolOperator :: Labeller
              --, defaultSymbolPunctuaton :: Labeller
              , labelSymbolEndOfKeyword :: String -> String
              , labelSymbolEndOfOperator :: String -> String
              , labelSpaceEndOfLineComment :: LabelWithExplainConfig
              , labelSpaceEndOfMultiComment :: LabelWithExplainConfig
              }

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

type LabelConfigurable :: * -> Constraint
class LabelConfigurable config where
  label :: Set String -> config
  hidden :: config

instance LabelConfigurable LabelConfig where
  label = LLabel
  hidden = LHidden
instance LabelConfigurable LabelWithExplainConfig where
  label = LELabel
  hidden = LEHidden

type ExplainConfigurable :: * -> Constraint
class ExplainConfigurable config where
  reason :: String -> config

instance ExplainConfigurable ExplainConfig where reason = EReason
instance ExplainConfigurable LabelWithExplainConfig where reason = LEReason

type LabelWithExplainConfigurable :: * -> Constraint
class LabelWithExplainConfigurable config where
  labelAndReason :: Set String -> String -> config

instance LabelWithExplainConfigurable LabelWithExplainConfig where labelAndReason = LELabelAndReason

type NotConfigurable :: * -> Constraint
class NotConfigurable config where
  notConfigured :: config

instance NotConfigurable LabelWithExplainConfig where notConfigured = LENotConfigured
instance NotConfigurable LabelConfig where notConfigured = LNotConfigured
instance NotConfigurable ExplainConfig where notConfigured = ENotConfigured

type VanillaFilterConfigurable :: (* -> *) -> Constraint
class VanillaFilterConfigurable config where
  unexpected :: (a -> String) -> config a
  because :: (a -> String) -> config a
  unexpectedBecause :: (a -> String) -> (a -> String) -> config a

instance VanillaFilterConfigurable FilterConfig where
  unexpected = VSUnexpected
  because = VSBecause
  unexpectedBecause = VSUnexpectedBecause

instance VanillaFilterConfigurable VanillaFilterConfig where
  unexpected = VUnexpected
  because = VBecause
  unexpectedBecause = VUnexpectedBecause

type SpecializedFilterConfigurable :: (* -> *) -> Constraint
class SpecializedFilterConfigurable config where
  specializedFilter :: (a -> NonEmpty String) -> config a

instance SpecializedFilterConfigurable FilterConfig where
  specializedFilter = VSSpecializedFilter
instance SpecializedFilterConfigurable SpecializedFilterConfig where
  specializedFilter = SSpecializedFilter

type BasicFilterConfigurable :: (* -> *) -> Constraint
class BasicFilterConfigurable config where
  basicFilter :: config a

instance BasicFilterConfigurable FilterConfig where basicFilter = VSBasicFilter
instance BasicFilterConfigurable VanillaFilterConfig where basicFilter = VBasicFilter
instance BasicFilterConfigurable SpecializedFilterConfig where basicFilter = SBasicFilter

badCharsFail :: Map Char (NonEmpty String) -> VerifiedBadChars
badCharsFail = BadCharsFail
badCharsReason :: Map Char String -> VerifiedBadChars
badCharsReason = BadCharsReason

type Unverified :: * -> Constraint
class Unverified config where
  unverified :: config

instance Unverified VerifiedBadChars where unverified = BadCharsUnverified

singleton :: a -> NonEmpty a
singleton x = x :| []