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 | ||