----------------------------------------------------------------------------
-- |
-- Module : Prettyprinter.MetaDoc
-- Copyright : (c) Sergey Vinokurov 2018
-- License : Apache-2.0 (see LICENSE)
-- Maintainer : serg.foo@gmail.com
----------------------------------------------------------------------------
module Prettyprinter.MetaDoc
( DocKind(..)
, MetaDoc
, mdPayload
, mdKind
, compositeMetaDoc
, atomicMetaDoc
, metaDocInt
, metaDocFloat
, metaDocDouble
, metaDocInteger
, metaDocNatural
, metaDocWord
, metaDocWord8
, metaDocWord16
, metaDocWord32
, metaDocWord64
, metaDocInt8
, metaDocInt16
, metaDocInt32
, metaDocInt64
, metaDocUnit
, metaDocBool
, metaDocChar
, stringMetaDoc
, strictTextMetaDoc
, lazyTextMetaDoc
, strictByteStringMetaDoc
, lazyByteStringMetaDoc
, shortByteStringMetaDoc
, constructorAppMetaDoc
) where
import Data.ByteString.Char8 qualified as C8
import Data.ByteString.Lazy.Char8 qualified as CL8
import Data.ByteString.Short qualified as ShortBS
import Data.Int
import Data.Semigroup as Semigroup
import Data.Text qualified as T
import Data.Text.Lazy qualified as TL
import Data.Word
import Numeric.Natural
import Prettyprinter
import Prettyprinter qualified as PP
import Prettyprinter.Combinators.Basic
data DocKind = Atomic | Composite
deriving (Eq, Ord, Enum, Bounded)
instance Semigroup DocKind where
(<>) = max
instance Monoid DocKind where
mempty = minBound
mappend = (Semigroup.<>)
data MetaDoc ann = MetaDoc
{ mdPayload :: Doc ann
, mdKind :: DocKind
}
compositeMetaDoc :: Doc ann -> MetaDoc ann
compositeMetaDoc x = MetaDoc
{ mdPayload = x
, mdKind = Composite
}
atomicMetaDoc :: Doc ann -> MetaDoc ann
atomicMetaDoc x = MetaDoc
{ mdPayload = x
, mdKind = Atomic
}
instance Semigroup (MetaDoc ann) where
(<>) (MetaDoc p1 kind1) (MetaDoc p2 kind2) = MetaDoc
{ mdPayload = p1 <> p2
, mdKind = kind1 <> kind2
}
instance Monoid (MetaDoc ann) where
mempty = MetaDoc
{ mdPayload = mempty
, mdKind = mempty
}
mappend = (<>)
metaDocInt :: Int -> MetaDoc ann
metaDocInt = atomicMetaDoc . pretty
metaDocFloat :: Float -> MetaDoc ann
metaDocFloat = atomicMetaDoc . pretty
metaDocDouble :: Double -> MetaDoc ann
metaDocDouble = atomicMetaDoc . pretty
metaDocInteger :: Integer -> MetaDoc ann
metaDocInteger = atomicMetaDoc . pretty
metaDocNatural :: Natural -> MetaDoc ann
metaDocNatural = atomicMetaDoc . pretty
metaDocWord :: Word -> MetaDoc ann
metaDocWord = atomicMetaDoc . pretty
metaDocWord8 :: Word8 -> MetaDoc ann
metaDocWord8 = atomicMetaDoc . pretty
metaDocWord16 :: Word16 -> MetaDoc ann
metaDocWord16 = atomicMetaDoc . pretty
metaDocWord32 :: Word32 -> MetaDoc ann
metaDocWord32 = atomicMetaDoc . pretty
metaDocWord64 :: Word64 -> MetaDoc ann
metaDocWord64 = atomicMetaDoc . pretty
metaDocInt8 :: Int8 -> MetaDoc ann
metaDocInt8 = atomicMetaDoc . pretty
metaDocInt16 :: Int16 -> MetaDoc ann
metaDocInt16 = atomicMetaDoc . pretty
metaDocInt32 :: Int32 -> MetaDoc ann
metaDocInt32 = atomicMetaDoc . pretty
metaDocInt64 :: Int64 -> MetaDoc ann
metaDocInt64 = atomicMetaDoc . pretty
metaDocUnit :: () -> MetaDoc ann
metaDocUnit = atomicMetaDoc . pretty
metaDocBool :: Bool -> MetaDoc ann
metaDocBool = atomicMetaDoc . pretty
metaDocChar :: Char -> MetaDoc ann
metaDocChar = atomicMetaDoc . pretty
stringMetaDoc :: String -> MetaDoc ann
stringMetaDoc str = f $ pretty str
where
f | any (== ' ') str = compositeMetaDoc
| otherwise = atomicMetaDoc
strictTextMetaDoc :: T.Text -> MetaDoc ann
strictTextMetaDoc str = f $ pretty str
where
f | T.any (== ' ') str = compositeMetaDoc
| otherwise = atomicMetaDoc
lazyTextMetaDoc :: TL.Text -> MetaDoc ann
lazyTextMetaDoc str = f $ pretty str
where
f | TL.any (== ' ') str = compositeMetaDoc
| otherwise = atomicMetaDoc
strictByteStringMetaDoc :: C8.ByteString -> MetaDoc ann
strictByteStringMetaDoc str = f $ pretty $ C8.unpack str
where
f | C8.any (== ' ') str = compositeMetaDoc
| otherwise = atomicMetaDoc
lazyByteStringMetaDoc :: CL8.ByteString -> MetaDoc ann
lazyByteStringMetaDoc str = f $ pretty $ CL8.unpack str
where
f | CL8.any (== ' ') str = compositeMetaDoc
| otherwise = atomicMetaDoc
shortByteStringMetaDoc :: ShortBS.ShortByteString -> MetaDoc ann
shortByteStringMetaDoc str = f $ pretty $ C8.unpack str'
where
str' = ShortBS.fromShort str
f | C8.any (== ' ') str' = compositeMetaDoc
| otherwise = atomicMetaDoc
constructorAppMetaDoc :: MetaDoc ann -> [MetaDoc ann] -> MetaDoc ann
constructorAppMetaDoc constructor args =
case map field args of
[] -> constructor
[f] -> compositeMetaDoc $ mdPayload constructor <+> group (mdPayload f)
fs -> compositeMetaDoc $ PP.align $ mdPayload constructor ## PP.vsep (map mdPayload fs)
where
field :: MetaDoc ann -> MetaDoc ann
field md =
case mdKind md of
Atomic -> md
Composite -> compositeMetaDoc $ PP.flatAlt payload (PP.parens payload)
where
payload = mdPayload md