diff options
-rw-r--r-- | lib/Postdelay/TimeSpec.hs | 28 | ||||
-rw-r--r-- | lib/Postdelay/TimeSpec/Units.hs | 42 | ||||
-rw-r--r-- | lib/Postdelay/TimeSpec/Utils.hs | 8 | ||||
-rw-r--r-- | postdelay.cabal | 2 | ||||
-rw-r--r-- | postdelay.nix | 10 |
5 files changed, 64 insertions, 26 deletions
diff --git a/lib/Postdelay/TimeSpec.hs b/lib/Postdelay/TimeSpec.hs index 5c41180..03baf85 100644 --- a/lib/Postdelay/TimeSpec.hs +++ b/lib/Postdelay/TimeSpec.hs | |||
@@ -14,16 +14,38 @@ import Text.Megaparsec | |||
14 | 14 | ||
15 | import Control.Monad.IO.Class | 15 | import Control.Monad.IO.Class |
16 | import Control.Applicative | 16 | import Control.Applicative |
17 | import Control.Lens hiding ((#)) | ||
17 | 18 | ||
18 | import Data.Semigroup | 19 | import Data.Bool |
20 | import Data.Semigroup hiding (option) | ||
19 | import Data.Monoid (Endo(..)) | 21 | import Data.Monoid (Endo(..)) |
22 | import Data.Foldable | ||
23 | import Data.VectorSpace | ||
20 | 24 | ||
21 | import Data.Time | 25 | import Data.Time |
26 | import Data.Time.Lens | ||
22 | import Data.Time.Zones | 27 | import Data.Time.Zones |
23 | 28 | ||
24 | 29 | ||
30 | seconds' :: Timeable t => Lens' t Time | ||
31 | seconds' = seconds . iso (% Second) (# Second) | ||
32 | |||
33 | |||
25 | pTimeSpec :: StringParser s m => m (Endo LocalTime) | 34 | pTimeSpec :: StringParser s m => m (Endo LocalTime) |
26 | pTimeSpec = empty | 35 | pTimeSpec = label "Relative time specification" $ |
36 | choice [ pOffsets False | ||
37 | ] | ||
38 | |||
39 | pOffsets :: forall s m. StringParser s m | ||
40 | => Bool -- ^ Require sign on first offset? | ||
41 | -> m (Endo LocalTime) | ||
42 | pOffsets (bool optSigned signed -> reqSgn) = fmap fold $ (:) <$> offset reqSgn <*> many (offset optSigned) | ||
43 | where | ||
44 | asOffset :: Time -> Endo LocalTime | ||
45 | asOffset by = Endo $ flexDT.seconds' %~ (^+^ by) | ||
46 | offset :: (m Time -> m Time) -> m (Endo LocalTime) | ||
47 | offset sgn = asOffset <$> lexeme (sgn timeLength) <?> "Time offset" | ||
27 | 48 | ||
28 | pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) | 49 | pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) |
29 | pTimeZone = empty | 50 | pTimeZone = label "Timezone" $ |
51 | empty | ||
diff --git a/lib/Postdelay/TimeSpec/Units.hs b/lib/Postdelay/TimeSpec/Units.hs index 330997a..0062460 100644 --- a/lib/Postdelay/TimeSpec/Units.hs +++ b/lib/Postdelay/TimeSpec/Units.hs | |||
@@ -1,19 +1,21 @@ | |||
1 | {-# LANGUAGE TemplateHaskell, TypeFamilies, GADTs, RankNTypes, DataKinds, ViewPatterns, ScopedTypeVariables #-} | 1 | {-# LANGUAGE TemplateHaskell, GADTs, RankNTypes, DataKinds, ViewPatterns, ScopedTypeVariables, FlexibleContexts, FlexibleInstances, UndecidableInstances, TypeFamilies #-} |
2 | 2 | ||
3 | module Postdelay.TimeSpec.Units | 3 | module Postdelay.TimeSpec.Units |
4 | ( Time | 4 | ( Time |
5 | 5 | ||
6 | , Second, Minute, Hour | 6 | , Second(..), Minute(..), Hour(..) |
7 | , Day, Week, Month, Year | 7 | , Day(..), Week(..), Month(..), Year(..) |
8 | 8 | ||
9 | , timeLength | 9 | , timeLength |
10 | 10 | ||
11 | , module Data.Metrology | ||
11 | , module Data.Units.SI.Prefixes | 12 | , module Data.Units.SI.Prefixes |
12 | ) where | 13 | ) where |
13 | 14 | ||
14 | import Postdelay.TimeSpec.Utils | 15 | import Postdelay.TimeSpec.Utils |
15 | 16 | ||
16 | import Control.Applicative | 17 | import Control.Applicative |
18 | import Control.Monad | ||
17 | 19 | ||
18 | import Data.Metrology | 20 | import Data.Metrology |
19 | import Data.Metrology.TH | 21 | import Data.Metrology.TH |
@@ -42,9 +44,6 @@ declareDerivedUnit "Year" [t| Day |] 365.25 Nothing | |||
42 | 44 | ||
43 | type Time = MkQu_DLN D.Time DefaultLCSU Fixed.Pico | 45 | type Time = MkQu_DLN D.Time DefaultLCSU Fixed.Pico |
44 | 46 | ||
45 | data Prefix where | ||
46 | Prefix :: forall p. (UnitPrefix p, Show p) => p -> Prefix | ||
47 | |||
48 | instance HasResolution p => AdditiveGroup (Fixed p) where | 47 | instance HasResolution p => AdditiveGroup (Fixed p) where |
49 | zeroV = 0 | 48 | zeroV = 0 |
50 | (^+^) = (+) | 49 | (^+^) = (+) |
@@ -57,16 +56,16 @@ instance HasResolution p => VectorSpace (Fixed p) where | |||
57 | 56 | ||
58 | 57 | ||
59 | timeLength :: StringParser s m => m Time | 58 | timeLength :: StringParser s m => m Time |
60 | timeLength = (*^) <$> lexeme rational <*> timeUnit | 59 | timeLength = (*^) <$> lexeme rational <*> timeUnit <?> "Length of time" |
61 | 60 | ||
62 | rational :: forall s m n. (StringParser s m, Fractional n, Num n) => m n | 61 | rational :: forall s m n. (StringParser s m, Fractional n, Num n) => m n |
63 | rational = combine <$> some digitChar <*> option [] (char '.' *> many digitChar) | 62 | rational = combine <$> some digitChar <*> option [] (char '.' *> many digitChar) <?> "Decimal number" |
64 | where | 63 | where |
65 | combine :: [Char] -> [Char] -> n | 64 | combine :: [Char] -> [Char] -> n |
66 | combine (map asN -> whole) (map asN -> fractional) | 65 | combine (map asN -> whole) (map asN -> fractional) |
67 | = foldl' (\i l -> i * 10 + l) 0 whole + (foldr' (\h t -> h + t / 10) 0 fractional) / 10 | 66 | = foldl' (\i l -> i * 10 + l) 0 whole + (foldr' (\h t -> h + t / 10) 0 fractional) / 10 |
68 | asN :: Char -> n | 67 | asN :: Char -> n |
69 | asN c = fromIntegral $ ((-) `on` fromEnum) c '0' | 68 | asN c = fromIntegral $ fromEnum c - fromEnum '0' |
70 | 69 | ||
71 | timeUnit :: StringParser s m => m Time | 70 | timeUnit :: StringParser s m => m Time |
72 | timeUnit = label "Unit of time" . choice $ | 71 | timeUnit = label "Unit of time" . choice $ |
@@ -95,15 +94,28 @@ timeUnit = label "Unit of time" . choice $ | |||
95 | , 1 % Year <$ choice [ string' "years" | 94 | , 1 % Year <$ choice [ string' "years" |
96 | , string' "year" | 95 | , string' "year" |
97 | ] | 96 | ] |
98 | ] ++ | 97 | , option 1 siPrefix <**> choice [ (% Second) <$ string "s" |
99 | [ (% Second) <$> option 1 siPrefix <* string "s" | 98 | , (% Hour ) <$ string "h" |
100 | , (% Hour) <$> option 1 siPrefix <* string "h" | 99 | , (% Day ) <$ string "d" |
101 | , (% Day) <$> option 1 siPrefix <* string "d" | 100 | , (% Year ) <$ choice [ string "a", string' "yr", string' "yrs" ] |
102 | , (% Year) <$> option 1 siPrefix <* choice [ string "a", string "yr", string "yrs" ] | 101 | ] |
103 | ] | 102 | ] |
104 | 103 | ||
104 | |||
105 | data Prefix where | ||
106 | Prefix :: forall p. ParseablePrefix p => p -> Prefix | ||
107 | |||
108 | class UnitPrefix a => ParseablePrefix a where | ||
109 | parser :: StringParser s m => a -> m () | ||
110 | |||
111 | instance {-# OVERLAPPABLE #-} (UnitPrefix a, Show a) => ParseablePrefix a where | ||
112 | parser = void . string . show | ||
113 | |||
114 | instance ParseablePrefix Micro where | ||
115 | parser _ = void $ choice [ string "ยต", string "u" ] | ||
116 | |||
105 | siPrefix :: (StringParser s m, Fractional n) => m n | 117 | siPrefix :: (StringParser s m, Fractional n) => m n |
106 | siPrefix = label "SI prefix" . choice $ map (\(Prefix p) -> multiplier p <$ string (show p)) | 118 | siPrefix = label "SI prefix" . choice $ map (\(Prefix p) -> multiplier p <$ parser p) |
107 | [ Prefix Deca, Prefix Hecto, Prefix Kilo, Prefix Mega, Prefix Giga | 119 | [ Prefix Deca, Prefix Hecto, Prefix Kilo, Prefix Mega, Prefix Giga |
108 | , Prefix Tera, Prefix Peta, Prefix Exa, Prefix Zetta, Prefix Yotta | 120 | , Prefix Tera, Prefix Peta, Prefix Exa, Prefix Zetta, Prefix Yotta |
109 | , Prefix Deci, Prefix Centi, Prefix Milli, Prefix Micro, Prefix Nano | 121 | , Prefix Deci, Prefix Centi, Prefix Milli, Prefix Micro, Prefix Nano |
diff --git a/lib/Postdelay/TimeSpec/Utils.hs b/lib/Postdelay/TimeSpec/Utils.hs index e4ba732..85ac299 100644 --- a/lib/Postdelay/TimeSpec/Utils.hs +++ b/lib/Postdelay/TimeSpec/Utils.hs | |||
@@ -7,8 +7,10 @@ import Control.Monad | |||
7 | import Control.Lens | 7 | import Control.Lens |
8 | 8 | ||
9 | import Data.Time | 9 | import Data.Time |
10 | import Data.Time.Lens | ||
10 | import Data.Time.Zones | 11 | import Data.Time.Zones |
11 | 12 | ||
13 | import Data.Functor | ||
12 | import Data.AdditiveGroup | 14 | import Data.AdditiveGroup |
13 | 15 | ||
14 | import Text.Megaparsec | 16 | import Text.Megaparsec |
@@ -30,6 +32,6 @@ signed = (<*>) (lexeme sign) | |||
30 | optSigned = (<*>) (option id $ lexeme sign) | 32 | optSigned = (<*>) (option id $ lexeme sign) |
31 | 33 | ||
32 | sign :: (StringParser s m, AdditiveGroup n) => m (n -> n) | 34 | sign :: (StringParser s m, AdditiveGroup n) => m (n -> n) |
33 | sign = choice [ id <$ char '+' | 35 | sign = label "sign" $ choice [ char '+' $> id |
34 | , negateV <$ char '-' | 36 | , char '-' $> negateV |
35 | ] | 37 | ] |
diff --git a/postdelay.cabal b/postdelay.cabal index 2e66ace..6d4323a 100644 --- a/postdelay.cabal +++ b/postdelay.cabal | |||
@@ -36,6 +36,8 @@ library | |||
36 | , units >=2.4 && <3 | 36 | , units >=2.4 && <3 |
37 | , units-defs >=2.0 && <3 | 37 | , units-defs >=2.0 && <3 |
38 | , exceptions >=0.8 && <1 | 38 | , exceptions >=0.8 && <1 |
39 | , lens >=4.15 && <5 | ||
40 | , lens-datetime >=0.3 && <1 | ||
39 | hs-source-dirs: lib | 41 | hs-source-dirs: lib |
40 | default-language: Haskell2010 | 42 | default-language: Haskell2010 |
41 | 43 | ||
diff --git a/postdelay.nix b/postdelay.nix index 3bd6184..938557f 100644 --- a/postdelay.nix +++ b/postdelay.nix | |||
@@ -1,6 +1,6 @@ | |||
1 | { mkDerivation, base, case-insensitive, exceptions, hsemail, list-t | 1 | { mkDerivation, base, case-insensitive, exceptions, hsemail, lens |
2 | , megaparsec, mime, mtl, old-time, parsec, stdenv, time | 2 | , lens-datetime, list-t, megaparsec, mime, mtl, old-time, parsec |
3 | , transformers, tz, units, units-defs | 3 | , stdenv, time, transformers, tz, units, units-defs |
4 | }: | 4 | }: |
5 | mkDerivation { | 5 | mkDerivation { |
6 | pname = "postdelay"; | 6 | pname = "postdelay"; |
@@ -9,8 +9,8 @@ mkDerivation { | |||
9 | isLibrary = true; | 9 | isLibrary = true; |
10 | isExecutable = true; | 10 | isExecutable = true; |
11 | libraryHaskellDepends = [ | 11 | libraryHaskellDepends = [ |
12 | base case-insensitive exceptions hsemail list-t megaparsec mime mtl | 12 | base case-insensitive exceptions hsemail lens lens-datetime list-t |
13 | old-time parsec time tz units units-defs | 13 | megaparsec mime mtl old-time parsec time tz units units-defs |
14 | ]; | 14 | ]; |
15 | executableHaskellDepends = [ base transformers ]; | 15 | executableHaskellDepends = [ base transformers ]; |
16 | homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay"; | 16 | homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay"; |