packages feed

grisette-0.4.0.0: src/Grisette/Core/Data/Class/GPretty.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

module Grisette.Core.Data.Class.GPretty
  ( GPretty (..),
    groupedEnclose,
    condEnclose,
  )
where

import Control.Monad.Except (ExceptT (ExceptT))
import Control.Monad.Identity
  ( Identity (Identity),
    IdentityT (IdentityT),
  )
import Control.Monad.Trans.Maybe (MaybeT (MaybeT))
import qualified Control.Monad.Writer.Lazy as WriterLazy
import qualified Control.Monad.Writer.Strict as WriterStrict
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as C
import Data.Functor.Sum (Sum)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.String (IsString (fromString))
import qualified Data.Text as T
import Data.Word (Word16, Word32, Word64, Word8)
import GHC.Generics
  ( C,
    C1,
    Constructor (conFixity, conIsRecord, conName),
    D,
    Fixity (Infix, Prefix),
    Generic (Rep, from),
    K1 (K1),
    M1 (M1),
    S,
    Selector (selName),
    U1 (U1),
    V1,
    type (:*:) ((:*:)),
    type (:+:) (L1, R1),
  )
import GHC.TypeLits (KnownNat, type (<=))
import Generics.Deriving (Default (Default, unDefault))
import Grisette.Core.Data.BV (IntN, SomeIntN, SomeWordN, WordN)
import Grisette.IR.SymPrim.Data.Prim.InternedTerm.Term
  ( LinkedRep,
    SupportedPrim,
    prettyPrintTerm,
  )
import Grisette.IR.SymPrim.Data.SymPrim
  ( SomeSymIntN (SomeSymIntN),
    SomeSymWordN (SomeSymWordN),
    SymBool (SymBool),
    SymIntN (SymIntN),
    SymInteger (SymInteger),
    SymWordN (SymWordN),
    type (-~>) (SymGeneralFun),
    type (=~>) (SymTabularFun),
  )

#if MIN_VERSION_prettyprinter(1,7,0)
import Prettyprinter
  ( (<+>),
    align,
    encloseSep,
    flatAlt,
    group,
    nest,
    vcat,
    viaShow,
    vsep,
    Doc,
    Pretty(pretty),
  )
#else
import Data.Text.Prettyprint.Doc
  ( (<+>),
    align,
    encloseSep,
    flatAlt,
    group,
    nest,
    vcat,
    viaShow,
    vsep,
    Doc,
    Pretty(pretty),
  )
#endif

glist :: [Doc ann] -> Doc ann
glist l
  | null l = "[]"
  | length l == 1 = "[" <> head l <> "]"
  | otherwise = groupedEnclose "[" "]" $ encloseSep "" "" (flatAlt ", " ",") l

class GPretty a where
  gpretty :: a -> Doc ann
  gprettyPrec :: Int -> a -> Doc ann
  gprettyList :: [a] -> Doc ann
  gprettyList = align . glist . map gpretty

  gpretty = gprettyPrec 0
  gprettyPrec _ = gpretty

  {-# MINIMAL gpretty | gprettyPrec #-}

#define GPRETTY_SIMPLE(type) \
instance GPretty type where gprettyPrec = viaShowsPrec showsPrec

instance GPretty Char where
  gpretty = viaShow
  gprettyList v = pretty (fromString v :: T.Text)

#if 1
GPRETTY_SIMPLE(Bool)
GPRETTY_SIMPLE(Integer)
GPRETTY_SIMPLE(Int)
GPRETTY_SIMPLE(Int8)
GPRETTY_SIMPLE(Int16)
GPRETTY_SIMPLE(Int32)
GPRETTY_SIMPLE(Int64)
GPRETTY_SIMPLE(Word)
GPRETTY_SIMPLE(Word8)
GPRETTY_SIMPLE(Word16)
GPRETTY_SIMPLE(Word32)
GPRETTY_SIMPLE(Word64)
GPRETTY_SIMPLE(SomeIntN)
GPRETTY_SIMPLE(SomeWordN)
#endif

instance GPretty B.ByteString where
  gpretty = pretty . C.unpack

instance GPretty T.Text where
  gpretty = pretty

instance (KnownNat n, 1 <= n) => GPretty (IntN n) where
  gpretty = viaShow

instance (KnownNat n, 1 <= n) => GPretty (WordN n) where
  gpretty = viaShow

-- ()
instance GPretty () where
  gpretty = viaShow

-- Either
deriving via
  (Default (Either a b))
  instance
    (GPretty a, GPretty b) => GPretty (Either a b)

-- Maybe
deriving via
  (Default (Maybe a))
  instance
    (GPretty a) => GPretty (Maybe a)

-- List
instance (GPretty a) => GPretty [a] where
  gpretty = gprettyList

-- (,)
deriving via
  (Default (a, b))
  instance
    (GPretty a, GPretty b) => GPretty (a, b)

-- (,,)
deriving via
  (Default (a, b, c))
  instance
    (GPretty a, GPretty b, GPretty c) => GPretty (a, b, c)

-- (,,,)
deriving via
  (Default (a, b, c, d))
  instance
    ( GPretty a,
      GPretty b,
      GPretty c,
      GPretty d
    ) =>
    GPretty (a, b, c, d)

-- (,,,,)
deriving via
  (Default (a, b, c, d, e))
  instance
    ( GPretty a,
      GPretty b,
      GPretty c,
      GPretty d,
      GPretty e
    ) =>
    GPretty (a, b, c, d, e)

-- (,,,,,)
deriving via
  (Default (a, b, c, d, e, f))
  instance
    ( GPretty a,
      GPretty b,
      GPretty c,
      GPretty d,
      GPretty e,
      GPretty f
    ) =>
    GPretty (a, b, c, d, e, f)

-- (,,,,,,)
deriving via
  (Default (a, b, c, d, e, f, g))
  instance
    ( GPretty a,
      GPretty b,
      GPretty c,
      GPretty d,
      GPretty e,
      GPretty f,
      GPretty g
    ) =>
    GPretty (a, b, c, d, e, f, g)

-- (,,,,,,,)
deriving via
  (Default (a, b, c, d, e, f, g, h))
  instance
    ( GPretty a,
      GPretty b,
      GPretty c,
      GPretty d,
      GPretty e,
      GPretty f,
      GPretty g,
      GPretty h
    ) =>
    GPretty (a, b, c, d, e, f, g, h)

-- Sum
deriving via
  (Default (Sum f g a))
  instance
    (GPretty (f a), GPretty (g a)) =>
    GPretty (Sum f g a)

-- MaybeT
instance
  (GPretty (m (Maybe a))) =>
  GPretty (MaybeT m a)
  where
  gprettyPrec _ (MaybeT a) =
    group $
      nest 2 $
        vsep
          [ "MaybeT",
            gprettyPrec 11 a
          ]

-- ExceptT
instance
  (GPretty (m (Either e a))) =>
  GPretty (ExceptT e m a)
  where
  gprettyPrec _ (ExceptT a) =
    group $
      nest 2 $
        vsep
          [ "ExceptT",
            gprettyPrec 11 a
          ]

-- WriterT
instance
  (GPretty (m (a, w))) =>
  GPretty (WriterLazy.WriterT w m a)
  where
  gprettyPrec _ (WriterLazy.WriterT a) =
    group $
      nest 2 $
        vsep
          [ "WriterT",
            gprettyPrec 11 a
          ]

instance
  (GPretty (m (a, w))) =>
  GPretty (WriterStrict.WriterT w m a)
  where
  gprettyPrec _ (WriterStrict.WriterT a) =
    group $
      nest 2 $
        vsep
          [ "WriterT",
            gprettyPrec 11 a
          ]

-- Identity
instance (GPretty a) => GPretty (Identity a) where
  gprettyPrec _ (Identity a) =
    group $
      nest 2 $
        vsep
          [ "Identity",
            gprettyPrec 11 a
          ]

-- IdentityT
instance (GPretty (m a)) => GPretty (IdentityT m a) where
  gprettyPrec _ (IdentityT a) =
    group $
      nest 2 $
        vsep
          [ "IdentityT",
            gprettyPrec 11 a
          ]

-- Prettyprint
#define GPRETTY_SYM_SIMPLE(symtype) \
instance GPretty symtype where \
  gpretty (symtype t) = prettyPrintTerm t

#define GPRETTY_SYM_BV(symtype) \
instance (KnownNat n, 1 <= n) => GPretty (symtype n) where \
  gpretty (symtype t) = prettyPrintTerm t

#define GPRETTY_SYM_FUN(op, cons) \
instance (SupportedPrim ca, SupportedPrim cb, LinkedRep ca sa, LinkedRep cb sb)\
  => GPretty (sa op sb) where \
  gpretty (cons t) = prettyPrintTerm t

#define GPRETTY_SYM_SOME_BV(symtype) \
instance GPretty symtype where \
  gpretty (symtype t) = gpretty t

#if 1
GPRETTY_SYM_SIMPLE(SymBool)
GPRETTY_SYM_SIMPLE(SymInteger)
GPRETTY_SYM_BV(SymIntN)
GPRETTY_SYM_BV(SymWordN)
GPRETTY_SYM_FUN(=~>, SymTabularFun)
GPRETTY_SYM_FUN(-~>, SymGeneralFun)
GPRETTY_SYM_SOME_BV(SomeSymIntN)
GPRETTY_SYM_SOME_BV(SomeSymWordN)
#endif

instance (Generic a, GPretty' (Rep a)) => GPretty (Default a) where
  gprettyPrec i v = gprettyPrec' Pref i $ from $ unDefault v

data Type = Rec | Tup | Pref | Inf String Int

class GPretty' a where
  gprettyPrec' :: Type -> Int -> a c -> Doc ann
  isNullary :: a c -> Bool
  isNullary = error "generic gpretty (isNullary): unnecessary case"

instance GPretty' V1 where
  gprettyPrec' _ _ x = case x of {}

instance GPretty' U1 where
  gprettyPrec' _ _ U1 = ""
  isNullary _ = True

instance (GPretty c) => GPretty' (K1 i c) where
  gprettyPrec' _ n (K1 a) = gprettyPrec n a
  isNullary _ = False

groupedEnclose :: Doc ann -> Doc ann -> Doc ann -> Doc ann
groupedEnclose l r d = group $ align $ vcat [l <> flatAlt " " "" <> d, r]

condEnclose :: Bool -> Doc ann -> Doc ann -> Doc ann -> Doc ann
condEnclose b = if b then groupedEnclose else const $ const id

instance (GPretty' a, Constructor c) => GPretty' (M1 C c a) where
  gprettyPrec' _ n c@(M1 x) =
    case t of
      Tup ->
        prettyBraces t (gprettyPrec' t 0 x)
      Inf _ m ->
        group $ condEnclose (n > m) "(" ")" $ gprettyPrec' t m x
      _ ->
        if isNullary x
          then pretty (conName c)
          else
            group $
              condEnclose (n > 10) "(" ")" $
                align $
                  nest 2 $
                    vsep
                      [ pretty (conName c),
                        prettyBraces t (gprettyPrec' t 11 x)
                      ]
    where
      prettyBraces :: Type -> Doc ann -> Doc ann
      prettyBraces Rec = groupedEnclose "{" "}"
      prettyBraces Tup = groupedEnclose "(" ")"
      prettyBraces Pref = id
      prettyBraces (Inf _ _) = id
      fixity = conFixity c
      t
        | conIsRecord c = Rec
        | conIsTuple c = Tup
        | otherwise = case fixity of
            Prefix -> Pref
            Infix _ i -> Inf (conName c) i
      conIsTuple :: C1 c f p -> Bool
      conIsTuple y = tupleName (conName y)
        where
          tupleName ('(' : ',' : _) = True
          tupleName _ = False

instance (Selector s, GPretty' a) => GPretty' (M1 S s a) where
  gprettyPrec' t n s@(M1 x)
    | selName s == "" =
        case t of
          Pref -> gprettyPrec' t (n + 1) x
          _ -> gprettyPrec' t (n + 1) x
    | otherwise =
        pretty (selName s) <+> "=" <+> gprettyPrec' t 0 x
  isNullary (M1 x) = isNullary x

instance (GPretty' a) => GPretty' (M1 D d a) where
  gprettyPrec' t n (M1 x) = gprettyPrec' t n x

instance (GPretty' a, GPretty' b) => GPretty' (a :+: b) where
  gprettyPrec' t n (L1 x) = gprettyPrec' t n x
  gprettyPrec' t n (R1 x) = gprettyPrec' t n x

instance (GPretty' a, GPretty' b) => GPretty' (a :*: b) where
  gprettyPrec' t@Rec n (a :*: b) =
    vcat
      [ gprettyPrec' t n a,
        "," <+> gprettyPrec' t n b
      ]
  gprettyPrec' t@(Inf s _) n (a :*: b) =
    align $
      nest 2 $
        vsep
          [ gprettyPrec' t n a,
            pretty s <+> gprettyPrec' t n b
          ]
  gprettyPrec' t@Tup _ (a :*: b) =
    vcat
      [ gprettyPrec' t 0 a,
        "," <> flatAlt " " "" <> gprettyPrec' t 0 b
      ]
  gprettyPrec' t@Pref n (a :*: b) =
    vsep
      [ gprettyPrec' t (n + 1) a,
        gprettyPrec' t (n + 1) b
      ]
  isNullary _ = False

viaShowsPrec :: (Int -> a -> ShowS) -> Int -> a -> Doc ann
viaShowsPrec f n a = pretty (f n a "")