packages feed

gigaparsec-0.2.2.2: src/Text/Gigaparsec/Errors/ErrorGen.hs

{-# LANGUAGE Safe #-}
{-# LANGUAGE OverloadedLists, RecordWildCards #-}
{-# OPTIONS_GHC -Wno-partial-fields #-}
module Text.Gigaparsec.Errors.ErrorGen (
    ErrorGen(..), UnexpectedItem(..), asFail, asSelect, asErr, vanillaGen, specializedGen
  ) where
import Text.Gigaparsec.Internal (Parsec)
import Text.Gigaparsec.Internal qualified as Internal (Parsec(Parsec), State, specialisedErr, emptyErr, expectedErr, unexpectedErr, raise)
import Text.Gigaparsec.Internal.Errors qualified as Internal (Error, CaretWidth(RigidCaret), addReason)

type ErrorGen :: * -> *
data ErrorGen a = SpecializedGen { messages :: a -> [String]
                                 , adjustWidth :: a -> Word -> Word
                                 }
                | VanillaGen { unexpected :: a -> UnexpectedItem
                             , reason :: a -> Maybe String
                             , adjustWidth :: a -> Word -> Word
                             }

vanillaGen :: ErrorGen a
vanillaGen = VanillaGen { unexpected = const EmptyItem
                        , reason = const Nothing
                        , adjustWidth = const id
                        }

specializedGen :: ErrorGen a
specializedGen = SpecializedGen { messages = const []
                                , adjustWidth = const id
                                }

type UnexpectedItem :: *
data UnexpectedItem = RawItem | EmptyItem | NamedItem String

asErr :: ErrorGen a -> a -> Word -> Parsec b
asErr errGen x w = Internal.raise $ \st -> genErr errGen st x w

asFail :: ErrorGen a -> Parsec (a, Word) -> Parsec b
asFail errGen (Internal.Parsec p) = Internal.Parsec $ \st _ bad ->
  let good (x, w) st' = bad (genErr errGen st' x w) st'
  in  p st good bad

asSelect :: ErrorGen a -> Parsec (Either (a, Word) b) -> Parsec b
asSelect errGen (Internal.Parsec p) = Internal.Parsec $ \st good bad ->
  let good' (Right x) st' = good x st'
      good' (Left (x, w)) st' = bad (genErr errGen st' x w) st'
  in p st good' bad

genErr :: ErrorGen a -> Internal.State -> a -> Word -> Internal.Error
genErr SpecializedGen{..} st x w =
  Internal.specialisedErr st (messages x) (Internal.RigidCaret (adjustWidth x w))
genErr VanillaGen{..} st x w =
  addReason (reason x) (makeError (unexpected x) st (adjustWidth x w))

makeError :: UnexpectedItem -> Internal.State -> Word -> Internal.Error
makeError RawItem st cw = Internal.expectedErr st [] cw
makeError EmptyItem st cw = Internal.emptyErr st cw
makeError (NamedItem name) st cw = Internal.unexpectedErr st [] name (Internal.RigidCaret cw)

-- no fold, unlifed type
addReason :: Maybe String -> Internal.Error -> Internal.Error
addReason Nothing err = err
addReason (Just reason) err = Internal.addReason reason err