fir-0.1.0.0: src/FIR/Syntax/DebugPrintf.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}
{-|
Module: FIR.Syntax.DebugPrintf
Description: @---@ Printf-like formatting functionality (for shader debugging).
-}
module FIR.Syntax.DebugPrintf
( Format, Formatter
, debugPrintf
, (%)
, int32, word32, word32Hex
, float, scientific
, vec
)
where
-- base
import Data.Int
( Int32 )
import Data.Kind
( Type )
import Data.Word
( Word32 )
import Data.String
( IsString(..) )
import GHC.Exts
( proxy#, Proxy# )
import GHC.TypeNats
( Nat, KnownNat
, type (<=)
, natVal'
)
-- text-short
import Data.Text.Short
( ShortText )
import qualified Data.Text.Short as ShortText
( pack )
-- fir
import Data.Constraint.All
( All )
import Data.Type.List
( type (:++:) )
import FIR.AST
( Code, Syntactic(..)
, PrintfAugType, pattern DebugPrintf
)
import FIR.AST.Type
( FunArgs, Nullary )
import FIR.Module
( Program )
import FIR.ProgramState
( ProgramState )
import Math.Linear
( V )
----------------------------------------------------------------------------
-- | A printf format, taking parameters of the given types.
data Format ( as :: [ Type ] ) =
UnsafeFormatFromFormatString { formatString :: !ShortText }
deriving stock ( Eq, Show )
-- | Print to debug output using a printf-style formatter.
--
-- Need to pass the 'Debug' compiler flag (otherwise this instruction will be ignored).
--
-- /Usage/: say we want to print the following values:
--
-- > x :: Code Float
-- > y :: Code Int32
-- > z :: Code (V 2 Float)
--
-- We can do this using the command:
-- > debugPrintf ( "x = " % float % " y = " % int32 % " z = " % vec @2 float ) x y z
--
-- '(%)' denotes composition of formatters.
--
-- /Note/: Enabling the OverloadedStrings extension is necessary.
--
-- To see this output, enable the following layers (off by default):
--
-- > VK_LAYER_PRINTF_ENABLE=1 VK_LAYER_PRINTF_TO_STDOUT=1 \
-- > VK_LAYER_PRINTF_ONLY_PRESET=1 VK_LAYER_PRINTF_BUFFER_SIZE=1048576 <my_exe>
debugPrintf
:: forall
( as :: [Type ] )
( i :: ProgramState )
. ( Syntactic ( ProgPrintf i as )
, Internal ( ProgPrintf i as ) ~ PrintfAugType i as
, All Nullary ( FunArgs ( PrintfAugType i as ) )
)
=> Format as -> ProgPrintf i as
debugPrintf fmt = fromAST ( DebugPrintf ( formatString fmt ) )
type family ProgPrintf ( i :: ProgramState ) ( as :: [ Type ] ) = ( r :: Type ) | r -> i as where
ProgPrintf i '[] = Program i i ( Code () )
ProgPrintf i ( a ': as ) = Code a -> ProgPrintf i as
instance ( as ~ '[] ) => IsString ( Format as ) where
fromString
= UnsafeFormatFromFormatString
. fromString
. escapePercents
escapePercents :: [Char] -> [Char]
escapePercents [] = []
escapePercents ( '%' : as ) = '%' : '%' : escapePercents as
escapePercents ( c : as ) = c : escapePercents as
infixr 9 %
-- | Concatenate two formatters.
--
-- See 'debugPrintf' for example usage.
(%) :: Format as -> Format bs -> Format ( as :++: bs )
( UnsafeFormatFromFormatString a ) % ( UnsafeFormatFromFormatString b ) =
UnsafeFormatFromFormatString ( a <> b )
-- | A class for types which can be formatted.
class Formattable ( a :: Type ) where
data Formatter a :: Type
formatterString :: Formatter a -> ShortText
instance Formattable Int32 where
data Formatter Int32
= FormatInt32
formatterString _ = "i"
instance Formattable Word32 where
data Formatter Word32
= FormatWord32
| FormatWord32Hex
formatterString FormatWord32 = "u"
formatterString FormatWord32Hex = "X"
instance Formattable Float where
data Formatter Float
= FormatFloatDigits
| FormatFloatScientific
| FormatFloatGeneric
formatterString FormatFloatDigits = "f"
formatterString FormatFloatScientific = "e"
formatterString FormatFloatGeneric = "g"
instance ( KnownNat n, 2 <= n, n <= 4, Formattable a ) => Formattable ( V n a ) where
data Formatter ( V n a )
= FormatVec ( Formatter a )
formatterString ( FormatVec fmt ) = "v" <> n <> formatterString fmt
where
n :: ShortText
n = ShortText.pack . show $ natVal' ( proxy# :: Proxy# n )
mkFormatter :: Formattable a => Formatter a -> Format '[ a ]
mkFormatter fmt = UnsafeFormatFromFormatString ( "%" <> formatterString fmt )
class Int32Formatter r where
int32 :: r
instance ( as ~ '[ Int32 ] ) => Int32Formatter ( Format as ) where
int32 = mkFormatter FormatInt32
instance ( a ~ Int32 ) => Int32Formatter ( Formatter a ) where
int32 = FormatInt32
class Word32Formatter r where
word32 :: r
word32Hex :: r
instance ( as ~ '[ Word32 ] ) => Word32Formatter ( Format as ) where
word32 = mkFormatter FormatWord32
word32Hex = mkFormatter FormatWord32Hex
instance ( a ~ Word32 ) => Word32Formatter ( Formatter a ) where
word32 = FormatWord32
word32Hex = FormatWord32Hex
class FloatFormatter r where
float :: r
scientific :: r
instance ( as ~ '[ Float ] ) => FloatFormatter ( Format as ) where
float = mkFormatter FormatFloatDigits
scientific = mkFormatter FormatFloatScientific
instance ( a ~ Float ) => FloatFormatter ( Formatter a ) where
float = FormatFloatDigits
scientific = FormatFloatScientific
class ( KnownNat n, 2 <= n, n <= 4, Formattable a ) => VecFormatter (n :: Nat) a r | r -> n a where
vec :: Formatter a -> r
instance ( KnownNat n, 2 <= n, n <= 4, Formattable a, as ~ '[ V n a ] )
=> VecFormatter n a ( Format as ) where
vec fmt = mkFormatter ( FormatVec fmt )
instance ( KnownNat n, 2 <= n, n <= 4, Formattable a, v ~ V n a )
=> VecFormatter n a ( Formatter v ) where
vec fmt = FormatVec fmt