clash-prelude-0.11: src/CLaSH/XException.hs
{-|
Copyright : (C) 2016, University of Twente
License : BSD2 (see the file LICENSE)
Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>
'X': An exception for uninitialized values
>>> show (errorX "undefined" :: Integer, 4 :: Int)
"(*** Exception: X: undefined
CallStack (from HasCallStack):
...
>>> showX (errorX "undefined" :: Integer, 4 :: Int)
"(X,4)"
-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE Trustworthy #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module CLaSH.XException
( -- * 'X': An exception for uninitialized values
XException, errorX
-- * Printing 'X' exceptions as \"X\"
, ShowX (..), showsX, printX, showsPrecXWith)
where
import Control.Exception (Exception, catch, evaluate, throw)
import Data.Complex (Complex)
import Data.Int (Int8,Int16,Int32,Int64)
import Data.Ratio (Ratio)
import Data.Word (Word8,Word16,Word32,Word64)
import GHC.Exts (Char (C#), Double (D#), Float (F#), Int (I#), Word (W#))
import GHC.Generics
import GHC.Show (appPrec)
import GHC.Stack (HasCallStack, callStack, prettyCallStack)
import System.IO.Unsafe (unsafeDupablePerformIO)
-- | An exception representing an \"uninitialised\" value.
newtype XException = XException String
instance Show XException where
show (XException s) = s
instance Exception XException
-- | Like 'error', but throwing an 'XException' instead of an 'ErrorCall'
--
-- The 'ShowX' methods print these error-values as \"X\"; instead of error'ing
-- out with an exception.
errorX :: HasCallStack => String -> a
errorX msg = throw (XException ("X: " ++ msg ++ "\n" ++ prettyCallStack callStack))
showXWith :: (a -> ShowS) -> a -> ShowS
showXWith f x =
\s -> unsafeDupablePerformIO (catch (f <$> evaluate x <*> pure s)
(\(XException _) -> return ('X': s)))
-- | Use when you want to create a 'ShowX' instance where:
--
-- - There is no 'Generic' instance for your data type
-- - The 'Generic' derived ShowX method would traverse into the (hidden)
-- implementation details of your data type, and you just want to show the
-- entire value as \"X\".
--
-- Can be used like:
--
-- > data T = ...
-- >
-- > instance Show T where ...
-- >
-- > instance ShowX T where
-- > showsPrecX = showsPrecXWith showsPrec
showsPrecXWith :: (Int -> a -> ShowS) -> Int -> a -> ShowS
showsPrecXWith f n = showXWith (f n)
-- | Like 'shows', but values that normally throw an 'X' exception are
-- converted to \"X\", instead of error'ing out with an exception.
showsX :: ShowX a => a -> ShowS
showsX = showsPrecX 0
-- | Like 'print', but values that normally throw an 'X' exception are
-- converted to \"X\", instead of error'ing out with an exception
printX :: ShowX a => a -> IO ()
printX x = putStrLn $ showX x
-- | Like the 'Show' class, but values that normally throw an 'X' exception are
-- converted to \"X\", instead of error'ing out with an exception.
--
-- >>> show (errorX "undefined" :: Integer, 4 :: Int)
-- "(*** Exception: X: undefined
-- CallStack (from HasCallStack):
-- ...
-- >>> showX (errorX "undefined" :: Integer, 4 :: Int)
-- "(X,4)"
--
-- Can be derived using 'GHC.Generics':
--
-- > {-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
-- >
-- > import CLaSH.Prelude
-- > import GHC.Generics
-- >
-- > data T = MkTA Int | MkTB Bool
-- > deriving (Show,Generic,ShowX)
class ShowX a where
-- | Like 'showsPrec', but values that normally throw an 'X' exception are
-- converted to \"X\", instead of error'ing out with an exception.
showsPrecX :: Int -> a -> ShowS
-- | Like 'show', but values that normally throw an 'X' exception are
-- converted to \"X\", instead of error'ing out with an exception.
showX :: a -> String
showX x = showsX x ""
-- | Like 'showList', but values that normally throw an 'X' exception are
-- converted to \"X\", instead of error'ing out with an exception.
showListX :: [a] -> ShowS
showListX ls s = showListX__ showsX ls s
default showsPrecX :: (Generic a, GShowX (Rep a)) => Int -> a -> ShowS
showsPrecX = genericShowsPrecX
showListX__ :: (a -> ShowS) -> [a] -> ShowS
showListX__ showx = showXWith go
where
go [] s = "[]" ++ s
go (x:xs) s = '[' : showx x (showl xs)
where
showl [] = ']':s
showl (y:ys) = ',' : showx y (showl ys)
data ShowType = Rec -- Record
| Tup -- Tuple
| Pref -- Prefix
| Inf String -- Infix
genericShowsPrecX :: (Generic a, GShowX (Rep a)) => Int -> a -> ShowS
genericShowsPrecX n = gshowsPrecX Pref n . from
instance ShowX ()
instance (ShowX a, ShowX b) => ShowX (a,b)
instance (ShowX a, ShowX b, ShowX c) => ShowX (a,b,c)
instance (ShowX a, ShowX b, ShowX c, ShowX d) => ShowX (a,b,c,d)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e) => ShowX (a,b,c,d,e)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f) => ShowX (a,b,c,d,e,f)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g) => ShowX (a,b,c,d,e,f,g)
-- Show is defined up to 15-tuples, but GHC.Generics only has Generic instances
-- up to 7-tuples, hence we need these orphan instances.
deriving instance Generic ((,,,,,,,) a b c d e f g h)
deriving instance Generic ((,,,,,,,,) a b c d e f g h i)
deriving instance Generic ((,,,,,,,,,) a b c d e f g h i j)
deriving instance Generic ((,,,,,,,,,,) a b c d e f g h i j k)
deriving instance Generic ((,,,,,,,,,,,) a b c d e f g h i j k l)
deriving instance Generic ((,,,,,,,,,,,,) a b c d e f g h i j k l m)
deriving instance Generic ((,,,,,,,,,,,,,) a b c d e f g h i j k l m n)
deriving instance Generic ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m n o)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h) => ShowX (a,b,c,d,e,f,g,h)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h, ShowX i) => ShowX (a,b,c,d,e,f,g,h,i)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h, ShowX i, ShowX j)
=> ShowX (a,b,c,d,e,f,g,h,i,j)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h, ShowX i, ShowX j, ShowX k)
=> ShowX (a,b,c,d,e,f,g,h,i,j,k)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h, ShowX i, ShowX j, ShowX k, ShowX l)
=> ShowX (a,b,c,d,e,f,g,h,i,j,k,l)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h, ShowX i, ShowX j, ShowX k, ShowX l
,ShowX m)
=> ShowX (a,b,c,d,e,f,g,h,i,j,k,l,m)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h, ShowX i, ShowX j, ShowX k, ShowX l
,ShowX m, ShowX n)
=> ShowX (a,b,c,d,e,f,g,h,i,j,k,l,m,n)
instance (ShowX a, ShowX b, ShowX c, ShowX d, ShowX e, ShowX f, ShowX g, ShowX h, ShowX i, ShowX j, ShowX k, ShowX l
,ShowX m, ShowX n, ShowX o)
=> ShowX (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o)
instance {-# OVERLAPPABLE #-} ShowX a => ShowX [a] where
showsPrecX _ = showListX
instance ShowX Bool
instance ShowX Double where
showsPrecX = showsPrecXWith showsPrec
instance (ShowX a, ShowX b) => ShowX (Either a b)
instance ShowX Float where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int8 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int16 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int32 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int64 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Integer where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word8 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word16 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word32 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word64 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX a => ShowX (Maybe a)
instance ShowX a => ShowX (Ratio a) where
showsPrecX = showsPrecXWith showsPrecX
instance ShowX a => ShowX (Complex a)
instance {-# OVERLAPPING #-} ShowX String where
showsPrecX = showsPrecXWith showsPrec
class GShowX f where
gshowsPrecX :: ShowType -> Int -> f a -> ShowS
isNullary :: f a -> Bool
isNullary = error "generic showX (isNullary): unnecessary case"
instance GShowX U1 where
gshowsPrecX _ _ U1 = id
isNullary _ = True
instance (ShowX c) => GShowX (K1 i c) where
gshowsPrecX _ n (K1 a) = showsPrecX n a
isNullary _ = False
instance (GShowX a, Constructor c) => GShowX (M1 C c a) where
gshowsPrecX _ n c@(M1 x) =
case fixity of
Prefix ->
showParen (n > appPrec && not (isNullary x))
( (if conIsTuple c then id else showString (conName c))
. (if isNullary x || conIsTuple c then id else showString " ")
. showBraces t (gshowsPrecX t appPrec x))
Infix _ m -> showParen (n > m) (showBraces t (gshowsPrecX t m x))
where fixity = conFixity c
t = if conIsRecord c then Rec else
case conIsTuple c of
True -> Tup
False -> case fixity of
Prefix -> Pref
Infix _ _ -> Inf (show (conName c))
showBraces :: ShowType -> ShowS -> ShowS
showBraces Rec p = showChar '{' . p . showChar '}'
showBraces Tup p = showChar '(' . p . showChar ')'
showBraces Pref p = p
showBraces (Inf _) p = p
conIsTuple :: C1 c f p -> Bool
conIsTuple y = tupleName (conName y) where
tupleName ('(':',':_) = True
tupleName _ = False
instance (Selector s, GShowX a) => GShowX (M1 S s a) where
gshowsPrecX t n s@(M1 x) | selName s == "" = gshowsPrecX t n x
| otherwise = showString (selName s)
. showString " = "
. gshowsPrecX t 0 x
isNullary (M1 x) = isNullary x
instance (GShowX a) => GShowX (M1 D d a) where
gshowsPrecX t = showsPrecXWith go
where go n (M1 x) = gshowsPrecX t n x
instance (GShowX a, GShowX b) => GShowX (a :+: b) where
gshowsPrecX t n (L1 x) = gshowsPrecX t n x
gshowsPrecX t n (R1 x) = gshowsPrecX t n x
instance (GShowX a, GShowX b) => GShowX (a :*: b) where
gshowsPrecX t@Rec n (a :*: b) =
gshowsPrecX t n a . showString ", " . gshowsPrecX t n b
gshowsPrecX t@(Inf s) n (a :*: b) =
gshowsPrecX t n a . showString s . gshowsPrecX t n b
gshowsPrecX t@Tup n (a :*: b) =
gshowsPrecX t n a . showChar ',' . gshowsPrecX t n b
gshowsPrecX t@Pref n (a :*: b) =
gshowsPrecX t (n+1) a . showChar ' ' . gshowsPrecX t (n+1) b
-- If we have a product then it is not a nullary constructor
isNullary _ = False
-- Unboxed types
instance GShowX UChar where
gshowsPrecX _ _ (UChar c) = showsPrec 0 (C# c) . showChar '#'
instance GShowX UDouble where
gshowsPrecX _ _ (UDouble d) = showsPrec 0 (D# d) . showString "##"
instance GShowX UFloat where
gshowsPrecX _ _ (UFloat f) = showsPrec 0 (F# f) . showChar '#'
instance GShowX UInt where
gshowsPrecX _ _ (UInt i) = showsPrec 0 (I# i) . showChar '#'
instance GShowX UWord where
gshowsPrecX _ _ (UWord w) = showsPrec 0 (W# w) . showString "##"