wtk-0.2: Text/ReadEither.hs
---------------------------------------------------------
--
-- Module : ReadEither
-- Copyright : Bartosz Wójcik (2012)
-- License : BSD3
--
-- Maintainer : bartek@sudety.it
-- Stability : Unstable
-- Portability : portable
--
-- Module is part of Wojcik Tool Kit package.
-- | Safe version of Read with human friendly error messages.
---------------------------------------------------------
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
module Text.ReadEither
where
import Control.Monad (liftM)
import Control.Monad.Trans.Error
import Data.Char
import Data.Maybe
import Data.Time
import System.Locale
import Text.WtkParser
import Text.Calculator
-- | Type class providing simple input parsing
class (Show a) => ReadEither a where
readEither :: String -> Either String a -- ^ Parses String to given readEither or returns error message.
instance ReadEither () where
readEither _ = Right ()
instance ReadEither Char where
readEither [] = Left "Empty string"
readEither x = Right $ head x
instance ReadEither String where
readEither = Right
instance ReadEither Bool where
readEither " " = Right False
readEither "0" = Right False
readEither "False" = Right False
readEither _ = Right True
instance ReadEither Integer where
readEither = eval intSigned
instance ReadEither Int where
readEither x = eval intSigned x >>= return . fromIntegral
instance ReadEither Double where
readEither = eval real
instance ReadEither Float where
readEither x = eval real x >>= return . realToFrac
-- | Parsing to Day can be done on many ways. For details see the source code.
instance ReadEither Day where
readEither x = readEither' localeList
where readEither' [] = Left "Date doesn't fit to the expected format"
readEither' (l:ls) = case parseTime defaultTimeLocale l x of
Just d -> Right d
Nothing -> readEither' ls
-- List of all parsable formats.
-- Month name only in english due to usage of @defaultTimeLocale@.
localeList = ["%Y %m %d" -- 2000 11 10
,"%Y %b %d" -- 2000 Nov 10
,"%Y-%m-%d" -- 2000-11-10
,"%d %m %Y" -- 10 11 2000
,"%d %b %Y" -- 10 Nov 2000
,"%d.%m.%Y" -- 10.11.2000
,"%Y.%m.%d" -- 2000.11.10
,"%d-%m-%Y" -- 10-11-2000
,"%Y/%m/%d" -- 2000/11/10
,"%d/%m/%Y" -- 10/11/2000
,"%d/%m/%y" -- 10/11/00
,"%y %m %d" -- 00 11 10
,"%d%m%y" -- 101100
,"%d%m%Y" -- 10112000
,"%y%m%d" -- 0011210
,"%Y%m%d" -- 200011210
]
instance ReadEither a => ReadEither (Maybe a) where
readEither "" = Right Nothing
readEither x = liftM Just $ readEither x
-- | Type class providing input parsing with additional features.
class (ReadEither a) => ReadEitherPlus a where
readEitherPlus :: String -> Either String a -- ^ Parses String to given readEither or returns error message.
-- | Strips out all control charaters
instance ReadEitherPlus String where
readEitherPlus = Right . filter (\x -> ord x >= 32)
-- | Parses arithmetic formula
instance ReadEitherPlus Double where
readEitherPlus = evaluate
-- | Parses arithmetic formula
instance ReadEitherPlus Float where
readEitherPlus = liftM realToFrac . evaluate
-- | Parses arithmetic formula
instance ReadEitherPlus Integer where
readEitherPlus = evaluateInt
-- | Parses arithmetic formula
instance ReadEitherPlus Int where
readEitherPlus = liftM fromIntegral . evaluateInt
instance ReadEitherPlus a => ReadEitherPlus (Maybe a) where
readEitherPlus "" = Right Nothing
readEitherPlus x = liftM Just $ readEitherPlus x