feat: rewrite to continuation style

This commit is contained in:
vegowotenks 2025-08-23 19:07:58 +02:00
parent deecf2df4f
commit 7d2259f197

View file

@ -17,12 +17,10 @@
{-# LANGUAGE BangPatterns #-} {-# LANGUAGE BangPatterns #-}
module Pretty.Serialize (Serializer, PrettySerialize(..), ShowPrettySerialize(..), run, emit, KeyValueSerialize(..), customField) where module Pretty.Serialize (Serializer, PrettySerialize(..), ShowPrettySerialize(..), run, emit, KeyValueSerialize(..), customField) where
import Data.Text.Lazy.Builder (Builder) import Data.Text.Lazy (LazyText)
import Data.Text.Lazy (Text)
import GHC.Generics (U1, K1 (K1), Generically (Generically), Generic (Rep), from, M1 (M1), Meta(MetaSel, MetaCons, MetaData), (:+:) (L1, R1), (:*:) ((:*:))) import GHC.Generics (U1, K1 (K1), Generically (Generically), Generic (Rep), from, M1 (M1), Meta(MetaSel, MetaCons, MetaData), (:+:) (L1, R1), (:*:) ((:*:)))
import qualified Data.Text.Lazy.Builder as Builder
import Control.Monad ((<$!>), forM_) import Control.Monad ((<$!>), forM_)
import qualified Data.Text.Lazy as Text import qualified Data.Text as StrictText
import Data.String (IsString(fromString)) import Data.String (IsString(fromString))
import GHC.TypeLits (symbolVal, KnownSymbol) import GHC.TypeLits (symbolVal, KnownSymbol)
import Data.Proxy (Proxy(Proxy)) import Data.Proxy (Proxy(Proxy))
@ -37,7 +35,6 @@ import Data.List (List)
import Data.Typeable (typeOf, Typeable) import Data.Typeable (typeOf, Typeable)
import qualified Data.Array.IArray as Data.IArray import qualified Data.Array.IArray as Data.IArray
import Data.Tuple (Solo) import Data.Tuple (Solo)
import qualified Data.Text as Strict
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as Set import qualified Data.Set as Set
import Data.ByteString.Lazy (LazyByteString) import Data.ByteString.Lazy (LazyByteString)
@ -46,14 +43,21 @@ import Data.Maybe (fromMaybe)
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Ratio (Ratio) import Data.Ratio (Ratio)
import Data.Text (StrictText)
import Data.Bifunctor (Bifunctor(second))
import qualified Data.Text.Lazy as LazyText
type Indentation = Word type Indentation = Word
newtype Serializer a = Serializer (SerializerState -> (SerializerState, a)) newtype Serializer a = Serializer (ContinuationSerializer a)
deriving stock (Functor) deriving stock (Functor)
type ContinuationSerializer a = (SerializerState -> (SerializerState, a))
type Prepends = [StrictText] -> [StrictText]
data SerializerState = SerializerState data SerializerState = SerializerState
{ builder :: !Builder { builder :: Prepends
, indentation :: !Indentation , indentation :: !Indentation
, increaseIndentation :: !(Indentation -> Indentation) , increaseIndentation :: !(Indentation -> Indentation)
, currentFieldIndex :: !(Maybe Word) , currentFieldIndex :: !(Maybe Word)
@ -63,16 +67,15 @@ instance Applicative Serializer where
pure :: a -> Serializer a pure :: a -> Serializer a
pure x = Serializer (, x) pure x = Serializer (, x)
(<*>) :: Serializer (a -> b) -> Serializer a -> Serializer b (<*>) :: Serializer (a -> b) -> Serializer a -> Serializer b
(<*>) (Serializer computeF) (Serializer computeX) = Serializer $ \ state -> let (<*>) (Serializer computeF) (Serializer computeA) = Serializer $ \ state -> let
(state', f) = computeF state (state', f) = computeF state
(state'', x) = computeX state' in second f $ computeA state'
in (state'', f x)
instance Monad Serializer where instance Monad Serializer where
(>>=) :: Serializer a -> (a -> Serializer b) -> Serializer b (>>=) :: Serializer a -> (a -> Serializer b) -> Serializer b
(>>=) (Serializer computeA) f = Serializer $ \ state -> let (>>=) (Serializer computeA) f = Serializer $ \ state -> let
(state', a) = computeA state (state', a) = computeA state
(Serializer computeB) = f a (Serializer computeB) = f a
in computeB state' in computeB state'
getSerializerState :: Serializer SerializerState getSerializerState :: Serializer SerializerState
@ -86,14 +89,14 @@ modifySerializerState f = getSerializerState >>= setSerializerState . f
-- | Append a text to the serializer buffer. Does not check whether the emitted text contains a newline. -- | Append a text to the serializer buffer. Does not check whether the emitted text contains a newline.
emit :: Text -> Serializer () emit :: StrictText -> Serializer ()
emit t = Serializer $ \ state -> (state { builder = state.builder <> Builder.fromLazyText t }, ()) emit t = Serializer $ \ state -> (state { builder = \ suffix -> t : state.builder suffix }, ())
lineBreak :: Serializer () lineBreak :: Serializer ()
lineBreak = do lineBreak = do
spaces <- indentation <$!> getSerializerState spaces <- indentation <$!> getSerializerState
emit "\n" emit "\n"
emit $ Text.replicate (fromIntegral spaces) " " emit $ StrictText.replicate (fromIntegral spaces) " "
recordField :: (Word -> Serializer ()) -> Serializer b -> Serializer b recordField :: (Word -> Serializer ()) -> Serializer b -> Serializer b
recordField labelField dumpField = do recordField labelField dumpField = do
@ -121,7 +124,7 @@ recordField labelField dumpField = do
unnamedField :: Serializer a -> Serializer a unnamedField :: Serializer a -> Serializer a
unnamedField = recordField serializer unnamedField = recordField serializer
namedField :: Text -> Serializer a -> Serializer a namedField :: StrictText -> Serializer a -> Serializer a
namedField name = recordField (const $ emit name) namedField name = recordField (const $ emit name)
customField :: Serializer () -> Serializer a -> Serializer a customField :: Serializer () -> Serializer a -> Serializer a
@ -148,11 +151,11 @@ beginFields name body = do
pure result pure result
inNamedConstructor :: Text -> Serializer a -> Serializer a inNamedConstructor :: StrictText -> Serializer a -> Serializer a
inNamedConstructor name = beginFields $ Just (emit "::" >> emit name) inNamedConstructor name = beginFields $ Just (emit "::" >> emit name)
inDatatype :: Text -> Serializer b -> Serializer b inDatatype :: StrictText -> Serializer b -> Serializer b
inDatatype name body = do inDatatype name body = do
emit name emit name
@ -160,14 +163,17 @@ inDatatype name body = do
inDatatypeOf :: Typeable a => a -> Serializer b -> Serializer b inDatatypeOf :: Typeable a => a -> Serializer b -> Serializer b
inDatatypeOf x s = let inDatatypeOf x s = let
!typeName = Text.pack . show . typeOf $ x !typeName = StrictText.pack . show . typeOf $ x
in inDatatype typeName s in inDatatype typeName s
run :: Serializer () -> Text run :: Serializer () -> LazyText
run (Serializer computeUnit) = Builder.toLazyText . builder . fst . computeUnit $ SerializerState {builder=mempty, indentation=0, increaseIndentation=(+4), currentFieldIndex=Nothing} run (Serializer computeUnit) = let
initialState = SerializerState {builder=mempty, indentation=0, increaseIndentation=(+4), currentFieldIndex=Nothing}
endState = fst (computeUnit initialState)
in LazyText.fromChunks $ endState.builder []
class PrettySerialize a where class PrettySerialize a where
serialize :: a -> Text serialize :: a -> LazyText
serialize = run . serializer serialize = run . serializer
serializer :: a -> Serializer () serializer :: a -> Serializer ()
@ -196,9 +202,9 @@ deriving via ShowPrettySerialize Natural instance PrettySerialize Natural
deriving via ShowPrettySerialize (Ratio a) instance Show a => PrettySerialize (Ratio a) deriving via ShowPrettySerialize (Ratio a) instance Show a => PrettySerialize (Ratio a)
-- text -- text
deriving via ShowPrettySerialize Text instance PrettySerialize Text deriving via ShowPrettySerialize LazyText instance PrettySerialize LazyText
deriving via ShowPrettySerialize ByteString instance PrettySerialize ByteString deriving via ShowPrettySerialize ByteString instance PrettySerialize ByteString
deriving via ShowPrettySerialize Strict.Text instance PrettySerialize Strict.Text deriving via ShowPrettySerialize StrictText instance PrettySerialize StrictText
deriving via ShowPrettySerialize LazyByteString instance PrettySerialize LazyByteString deriving via ShowPrettySerialize LazyByteString instance PrettySerialize LazyByteString
-- tuples -- tuples