summaryrefslogtreecommitdiff
path: root/lib/Postdelay/TimeSpec/Utils.hs
diff options
context:
space:
mode:
authorGregor Kleen <aethoago@141.li>2017-02-16 19:03:30 +0100
committerGregor Kleen <aethoago@141.li>2017-02-16 19:03:30 +0100
commit4343e02ca8431e61e2dc1755d1288dd6c55c9a23 (patch)
tree2f1397eb4c8649dffb7ae2adaedfd101bdda4fab /lib/Postdelay/TimeSpec/Utils.hs
parent333ea946916b005134e7ba249178acad4858a67d (diff)
downloadpostdelay-4343e02ca8431e61e2dc1755d1288dd6c55c9a23.tar
postdelay-4343e02ca8431e61e2dc1755d1288dd6c55c9a23.tar.gz
postdelay-4343e02ca8431e61e2dc1755d1288dd6c55c9a23.tar.bz2
postdelay-4343e02ca8431e61e2dc1755d1288dd6c55c9a23.tar.xz
postdelay-4343e02ca8431e61e2dc1755d1288dd6c55c9a23.zip
Bounded numeric parsers
Diffstat (limited to 'lib/Postdelay/TimeSpec/Utils.hs')
-rw-r--r--lib/Postdelay/TimeSpec/Utils.hs69
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
3module Postdelay.TimeSpec.Utils where 3module 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
5import Control.Applicative 16import Control.Applicative
6import Control.Monad 17import Control.Monad
@@ -10,8 +21,13 @@ import Data.Time
10import Data.Time.Lens 21import Data.Time.Lens
11import Data.Time.Zones 22import Data.Time.Zones
12 23
24import Data.Bool
25import Data.Foldable
13import Data.Functor 26import Data.Functor
27import Data.Function
14import Data.AdditiveGroup 28import Data.AdditiveGroup
29import Data.Interval (Interval, Extended(..), (<=..<=), (<=..<), (<..<=), (<..<), interval)
30import qualified Data.Interval as I
15 31
16import Text.Megaparsec 32import Text.Megaparsec
17import Text.Megaparsec.Prim (MonadParsec) 33import Text.Megaparsec.Prim (MonadParsec)
@@ -37,6 +53,57 @@ sign = label "sign" $ choice [ char '+' $> id
37 , char '-' $> negateV 53 , char '-' $> negateV
38 ] 54 ]
39 55
56boundedNatural :: 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
59boundedNatural 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
89boundedRational :: 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
92boundedRational 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
40fromDigit :: Num n => Char -> n 107fromDigit :: Num n => Char -> n
41fromDigit c = fromIntegral $ fromEnum c - fromEnum '0' 108fromDigit c = fromIntegral $ fromEnum c - fromEnum '0'
42 109