fortran-src-0.13.0: src/Language/Fortran/Repr/Value/Scalar/String.hs
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE QuantifiedConstraints #-}
{- | Fortran CHAR value representation.
Currently only CHARs of known length.
-}
module Language.Fortran.Repr.Value.Scalar.String where
import GHC.TypeNats
import Language.Fortran.Repr.Compat.Natural
import Data.Text ( Text )
import qualified Data.Text as Text
import Language.Fortran.Repr.Util ( natVal'' )
import Data.Proxy
import Unsafe.Coerce
import Text.PrettyPrint.GenericPretty ( Out )
import Text.PrettyPrint.GenericPretty.ViaShow ( OutShowly(..) )
import Data.Binary
import Data.Data
import Data.Singletons
import GHC.TypeLits.Singletons
-- TODO unsafe constructor do not use >:(
-- need context for Reasons(TM)
data FString (l :: NaturalK) = KnownNat l => FString Text
deriving stock instance Show (FString l)
deriving stock instance Eq (FString l)
deriving stock instance Ord (FString l) -- TODO
deriving stock instance KnownNat l => Data (FString l)
{-
instance Data (FString l) where
--gunfold k z c = k (z (\x -> case someFString x of SomeFString y -> y))
gunfold k z c = k (z (FString @l))
-}
eqFString :: FString l -> FString r -> Bool
eqFString (FString l) (FString r) = l == r
-- | This is a painful instance to define. We cheat by leveraging the instance
-- of the length-hiding type 'SomeFString', then asserting length. It's CPU
-- and memory inefficient and has backwards dependencies, but is comfortably
-- safe.
instance KnownNat l => Binary (FString l) where
put t = put (SomeFString t)
get =
get @SomeFString >>= \case
SomeFString (FString t) ->
case fString @l t of
Just t' -> pure t'
Nothing -> fail "FString had incorrect length"
-- | Attempt to a 'Text' into an 'FString' of the given length.
fString :: forall l. KnownNat l => Text -> Maybe (FString l)
fString s =
if Text.length s == fromIntegral (natVal'' @l)
then Just $ FString s
else Nothing
fStringLen :: forall l. KnownNat l => FString l -> Natural
fStringLen _ = natVal'' @l
data SomeFString = forall (l :: NaturalK). KnownNat l => SomeFString (FString l)
deriving stock instance Show SomeFString
deriving via (OutShowly SomeFString) instance Out SomeFString
instance Eq SomeFString where
(SomeFString l) == (SomeFString r) = l `eqFString` r
-- TODO impossible??
instance Data SomeFString where
{-
dataSomeFStringT = mkDataType "TODO" [dataSomeFStringC1]
dataSomeFStringC1 = mkConstr dataSomeFStringT "SomeFString" [] Prefix
instance Data SomeFString where
dataTypeOf _ = dataSomeFStringT
toConstr = \case
SomeFString{} -> dataSomeFStringC1
--gunfold k z c = k (z SomeFString)
gunfold k z c = k (z (\(FString fstr :: FString l) -> SomeFString @l (FString fstr)))
-}
instance Binary SomeFString where
put (SomeFString (FString t)) = put t
get = someFString <$> get @Text
-- | Lift a 'Text' into 'SomeFString'.
someFString :: Text -> SomeFString
someFString t =
case someNatVal (fromIntegral (Text.length t)) of
SomeNat (_ :: Proxy l) -> SomeFString $ FString @l t
someFStringLen :: SomeFString -> Natural
someFStringLen (SomeFString s) = fStringLen s
-- TODO dunno how to do this without unsafeCoerce because of the type-level nat
-- addition >:( -- oh actually seems this is an expected usage of it. ok
concatFString
:: forall ll lr. (KnownNat ll, KnownNat lr)
=> FString ll
-> FString lr
-> FString (ll + lr)
concatFString (FString sl) (FString sr) =
unsafeCoerce $ FString @ll $ sl <> sr
concatSomeFString :: SomeFString -> SomeFString -> SomeFString
concatSomeFString (SomeFString l) (SomeFString r) =
case concatFString l r of s@FString{} -> SomeFString s
fStringBOp :: (Text -> Text -> r) -> FString ll -> FString lr -> r
fStringBOp f (FString l) (FString r) = f l r
someFStringBOp :: (Text -> Text -> r) -> SomeFString -> SomeFString -> r
someFStringBOp f (SomeFString l) (SomeFString r) = fStringBOp f l r