numerals-0.1: Text/Numeral/Debug.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Text.Numeral.Debug where
import Data.String
import Text.Numeral
import Text.Numeral.Joinable
import Text.Numeral.Misc (const2, withSnd)
import Text.Numeral.Language
import qualified Data.DString as DS
import qualified Data.ByteString.Char8 as B
import qualified Data.Text as T
import qualified Text.PrettyPrint as PP
-------------------------------------------------------------------------------
class Stringable s where
toString :: s -> String
instance Stringable String where
toString = id
instance Stringable B.ByteString where
toString = B.unpack
instance Stringable T.Text where
toString = T.unpack
instance Stringable ShowS where
toString s = s []
instance Stringable DS.DString where
toString = DS.toString
instance Stringable PP.Doc where
toString = PP.render
-------------------------------------------------------------------------------
type Test s = NumConfig s -> Gender -> [Integer] -> IO ()
test :: Stringable s => Test s
test nc g = mapM_ (putStrLn . pretty)
where pretty n = show n ++ " == " ++ (maybe "-" id $ fmap toString $ cardinal nc g n)
testS :: Test String
testS = test
testBS :: Test B.ByteString
testBS = test
testT :: Test T.Text
testT = test
testSS :: Test ShowS
testSS = test
testDS :: Test DS.DString
testDS = test
testDoc :: Test PP.Doc
testDoc = test
testDocWithStyle :: PP.Style -> NumConfig PP.Doc -> Gender -> [Integer] -> IO ()
testDocWithStyle s nc g = mapM_ (putStrLn . pretty)
where pretty n = show n ++ " == " ++ (maybe "-" id $ fmap (PP.renderStyle s) $ cardinal nc g n)
-------------------------------------------------------------------------------
-- | @nummify@ transforms the given NumConfig to a numeric NumConfig
-- such that 'prop_cardinal_nummify' holds. @nummify@ is thus usefull
-- as a testing aid. @nummify@ is also usefull as a debugging aid
-- when you use a suitable numeric type such as 'NS' which renders a
-- numeric expression to a string representing the same expression.
nummify :: Num n => NumConfig s -> NumConfig n
nummify (NumConfig {..}) = NumConfig { ncCardinal = fmap transformSym . ncCardinal
, ncNeg = negate
, ncOne = snd
, ncAdd = withSnd (+)
, ncMul = withSnd (*)
}
where
-- Create a new symbol who's representation is its value.
transformSym :: (Num n) => NumSymbol s -> NumSymbol n
transformSym sym = sym { symRepr = const2 . fromInteger . symVal $ sym}
-- | 'prop_cardinal_nummify' specifies the correctness of 'cardinal'.
prop_cardinal_nummify :: NumConfig String -> Gender -> Integer -> Bool
prop_cardinal_nummify nc g n = maybe True (== n) $ cardinal (nummify nc) g n
-------------------------------------------------------------------------------
-- | 'NS' is used
newtype NS s = NS {unNS :: Precedence -> s}
type Precedence = Int
-- The following bogus Show and Eq instances are needed for Num :-(
instance Show (NS s) where
show _ = "NS <function>"
instance Eq (NS s) where
_ == _ = False
instance (IsString s, Joinable s) => Num (NS s) where
fromInteger = NS . const . fromString . show
(+) = bin "+" 6
(-) = bin "-" 6
(*) = bin "*" 7
negate = un "negate"
abs = un "abs"
signum = un "signum"
un :: (IsString s, Joinable s) => s -> (NS s -> NS s)
un sFun x = NS $ \p -> paren (p > precApp)
(sFun <+> unNS x (precApp+1))
where
precApp = 10
bin :: (IsString s, Joinable s) => s -> Precedence -> (NS s -> NS s -> NS s)
bin sOp d x y = NS $ \p -> paren (p > d) $
let p' = d + 1
in unNS x p' <+> sOp <+> unNS y p'
paren :: (IsString s, Joinable s) => Bool -> s -> s
paren True s = "(" <> s <> ")"
paren False s = s
instance Stringable s => Stringable (NS s) where
toString (NS f) = toString $ f 0
testNSS :: Test (NS String)
testNSS = test
testNumS :: Test String
testNumS = testNSS . nummify
-------------------------------------------------------------------------------
newtype Lst s = Lst {unLst :: [s]}
instance Joinable s => Joinable (Lst s) where
x <> y = Lst $ appendUnionWith (<>) (unLst x) (unLst y)
x <+> y = Lst $ unLst x ++ unLst y
-- | appendUnionWith f [a, b, c] [d, e, f] => [a, b, c `f` d, e, f]
appendUnionWith :: (a -> a -> a) -> [a] -> [a] -> [a]
appendUnionWith _ [] ys = ys
appendUnionWith _ xs [] = xs
appendUnionWith f [x] (y:ys) = x `f` y : ys
appendUnionWith f (x:xs) ys = x : appendUnionWith f xs ys
instance IsString s => IsString (Lst s) where
fromString s = Lst [fromString s]
instance Stringable s => Stringable (Lst s) where
toString = show . map toString . unLst
testLst :: Test (Lst String)
testLst = test