packages feed

spectacle-1.0.0: src/Language/Spectacle/Model/ModelError.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Model checker errors.
--
-- @since 1.0.0
module Language.Spectacle.Model.ModelError
  ( -- * Model Errors
    ModelError (InitialError, RuntimeError, RefutedError),

    -- ** Pretty Printing
    ppModelError,
    ppInitialError,
    ppRuntimeError,

    -- * Property Errors
    TemporalError (TemporalError),
    errorModality,
    errorPropName,
    errorPrimes,
    errorPlains,

    -- ** Pretty Printing
    ppTemporalError,
  )
where

import Data.Kind (Type)
import GHC.TypeLits (Symbol)
import Prettyprinter (Doc, align, indent, line, viaShow, vsep, (<+>))
import Prettyprinter.Render.Terminal (AnsiStyle)

import Data.Type.Rec (Ascribe, HasDict)
import Data.World (World, ppWorldListed)
import Language.Spectacle.Exception.RuntimeException (RuntimeException)
import Language.Spectacle.Specification.Prop (Modality, ppModality)

-- ---------------------------------------------------------------------------------------------------------------------

-- | 'TemporalError' captures information about a temporal property refutation.
--
-- @since 1.0.0
data ModelError :: [Ascribe Symbol Type] -> Type where
  InitialError ::
    ModelError ctx
  RuntimeError ::
    RuntimeException ->
    ModelError ctx
  RefutedError ::
    TemporalError ctx ->
    ModelError ctx

-- | @since 1.0.0
deriving instance HasDict Show ctx => Show (ModelError ctx)

ppModelError :: HasDict Show ctx => ModelError ctx -> Doc AnsiStyle
ppModelError = \case
  InitialError -> ppInitialError
  RuntimeError exc -> ppRuntimeError exc
  RefutedError err -> ppTemporalError err

ppInitialError :: Doc AnsiStyle
ppInitialError = "no initial states resulted from evaluating the initial actions"

ppRuntimeError :: RuntimeException -> Doc AnsiStyle
ppRuntimeError exc = "runtime error:" <+> viaShow exc

-- ---------------------------------------------------------------------------------------------------------------------

-- | 'TemporalError' captures information about a temporal property refutation.
--
-- @since 1.0.0
data TemporalError ctx = TemporalError
  { errorModality :: Modality
  , errorPropName :: String
  , errorPrimes :: Maybe (World ctx)
  , errorPlains :: Maybe (World ctx)
  }

-- | @since 1.0.0
deriving instance HasDict Show ctx => Show (TemporalError ctx)

ppTemporalError :: HasDict Show ctx => TemporalError ctx -> Doc AnsiStyle
ppTemporalError TemporalError {..} =
  "refuted property:"
    <+> ppModality errorModality
    <+> viaShow errorPropName
    <> line
    <> stateList
  where
    stateList =
      let plains = align . mappend "from: " . vsep . ppWorldListed <$> errorPlains
          primes = align . mappend "to: " . vsep . ppWorldListed <$> errorPrimes
          states = foldMap (maybe [] pure) [plains, primes]
       in vsep (map (indent 2 . mappend "* ") states)