diff options
Diffstat (limited to 'lib/Postdelay/TimeSpec/Utils.hs')
| -rw-r--r-- | lib/Postdelay/TimeSpec/Utils.hs | 69 |
1 files changed, 68 insertions, 1 deletions
diff --git a/lib/Postdelay/TimeSpec/Utils.hs b/lib/Postdelay/TimeSpec/Utils.hs index 54b85f7..c10fc4b 100644 --- a/lib/Postdelay/TimeSpec/Utils.hs +++ b/lib/Postdelay/TimeSpec/Utils.hs | |||
| @@ -1,6 +1,17 @@ | |||
| 1 | {-# LANGUAGE RankNTypes, ConstraintKinds, TypeFamilies, FlexibleContexts, ScopedTypeVariables, ViewPatterns, TupleSections #-} | 1 | {-# LANGUAGE RankNTypes, ConstraintKinds, TypeFamilies, FlexibleContexts, ScopedTypeVariables, ViewPatterns, TupleSections #-} |
| 2 | 2 | ||
| 3 | module Postdelay.TimeSpec.Utils where | 3 | module Postdelay.TimeSpec.Utils |
| 4 | ( StringParser | ||
| 5 | , spaceConsumer, spaces | ||
| 6 | , lexeme | ||
| 7 | , signed, optSigned | ||
| 8 | , sign | ||
| 9 | , boundedNatural | ||
| 10 | , boundedRational | ||
| 11 | , module Data.Interval | ||
| 12 | , fromDigit | ||
| 13 | , mkGramSepBy | ||
| 14 | ) where | ||
| 4 | 15 | ||
| 5 | import Control.Applicative | 16 | import Control.Applicative |
| 6 | import Control.Monad | 17 | import Control.Monad |
| @@ -10,8 +21,13 @@ import Data.Time | |||
| 10 | import Data.Time.Lens | 21 | import Data.Time.Lens |
| 11 | import Data.Time.Zones | 22 | import Data.Time.Zones |
| 12 | 23 | ||
| 24 | import Data.Bool | ||
| 25 | import Data.Foldable | ||
| 13 | import Data.Functor | 26 | import Data.Functor |
| 27 | import Data.Function | ||
| 14 | import Data.AdditiveGroup | 28 | import Data.AdditiveGroup |
| 29 | import Data.Interval (Interval, Extended(..), (<=..<=), (<=..<), (<..<=), (<..<), interval) | ||
| 30 | import qualified Data.Interval as I | ||
| 15 | 31 | ||
| 16 | import Text.Megaparsec | 32 | import Text.Megaparsec |
| 17 | import Text.Megaparsec.Prim (MonadParsec) | 33 | import Text.Megaparsec.Prim (MonadParsec) |
| @@ -37,6 +53,57 @@ sign = label "sign" $ choice [ char '+' $> id | |||
| 37 | , char '-' $> negateV | 53 | , char '-' $> negateV |
| 38 | ] | 54 | ] |
| 39 | 55 | ||
| 56 | boundedNatural :: forall s n m. (Show n, Real n, StringParser s m) | ||
| 57 | => Bool -- ^ Require number to be padded with zeroes? | ||
| 58 | -> Interval n -> m n | ||
| 59 | boundedNatural padded (I.intersection (0 <=..< PosInf) -> bounds) = do | ||
| 60 | n <- foldl' (\i l -> i * 10 + l) 0 <$> digitN (fromDigit <$> digitChar) <?> "Natural number cotained in: " ++ show bounds | ||
| 61 | when (not $ n `I.member` bounds) $ fail "Natural number out of bounds" | ||
| 62 | return n | ||
| 63 | where | ||
| 64 | digitN :: m n -> m [n] | ||
| 65 | digitN p | ||
| 66 | | PosInf <- maxDigits | ||
| 67 | , Finite min <- minDigits = (++) <$> count min p <*> many p | ||
| 68 | | Finite max <- maxDigits | ||
| 69 | , Finite min <- minDigits | ||
| 70 | , not padded = count' min max p | ||
| 71 | | Finite max <- maxDigits | ||
| 72 | , Finite _ <- minDigits | ||
| 73 | , padded = count max p | ||
| 74 | | otherwise = error "boundedNatural in undefined state" | ||
| 75 | |||
| 76 | minDigits, maxDigits :: Extended Int | ||
| 77 | (minDigits, maxDigits) = ( fmap digits . I.lowerBound $ close bounds | ||
| 78 | , fmap digits . I.upperBound $ close bounds | ||
| 79 | ) | ||
| 80 | where | ||
| 81 | close int | ||
| 82 | | (Finite min, False) <- I.lowerBound' int = close $ interval (Finite $ min + 1, True) (I.upperBound' int) | ||
| 83 | | (Finite max, False) <- I.upperBound' int = close $ interval (I.lowerBound' int) (Finite $ max - 1, True) | ||
| 84 | | otherwise = int | ||
| 85 | |||
| 86 | digits :: n -> Int | ||
| 87 | digits = ceiling . (logBase 10 :: Double -> Double) . realToFrac . abs | ||
| 88 | |||
| 89 | boundedRational :: forall s m n. (Show n, RealFrac n, StringParser s m) | ||
| 90 | => Bool -- ^ Require number to be padded with zeroes | ||
| 91 | -> Interval n -> m n | ||
| 92 | boundedRational padded (I.intersection (0 <=..< PosInf) -> bounds) | ||
| 93 | = (+) <$> boundedNatural padded wholeBounds <*> fractional <?> "Nonnegative real contained in: " ++ show bounds | ||
| 94 | where | ||
| 95 | fractional :: m n | ||
| 96 | fractional = reqFractional $ char '.' $> (/ 10) . foldr' (\h t -> h + t / 10) 0 <*> some (fromDigit <$> digitChar) | ||
| 97 | |||
| 98 | reqFractional | ||
| 99 | | contained = option 0 | ||
| 100 | | otherwise = id | ||
| 101 | where (_, contained) = I.lowerBound' bounds | ||
| 102 | wholeBounds | ||
| 103 | | (Finite max, False) <- I.upperBound' bounds | ||
| 104 | , max == (fromInteger $ round max) = interval (I.lowerBound' bounds) (Finite $ max - 1, True) | ||
| 105 | | otherwise = bounds | ||
| 106 | |||
| 40 | fromDigit :: Num n => Char -> n | 107 | fromDigit :: Num n => Char -> n |
| 41 | fromDigit c = fromIntegral $ fromEnum c - fromEnum '0' | 108 | fromDigit c = fromIntegral $ fromEnum c - fromEnum '0' |
| 42 | 109 | ||
