diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml new file mode 100644 index 0000000..c35832d --- /dev/null +++ b/.github/workflows/haskell.yml @@ -0,0 +1,49 @@ +name: CI +on: + push: + branches: + - master + pull_request: + types: + - opened + - synchronize +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest] + cabal: ["3.8"] + ghc: ["8.4.4", "8.6.5", "8.8.4", "8.10.7", "9.0.2", "9.2.6", "9.4.4"] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + - uses: haskell/actions/setup@v2 + id: setup-haskell-cabal + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: ${{ matrix.cabal }} + - name: Cabal Update + run: | + cabal v2-update + cabal v2-freeze $CONFIG + - uses: actions/cache@v2.1.5 + with: + path: | + ${{ steps.setup-haskell-cabal.outputs.cabal-store }} + dist-newstyle + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.cabal }}- + - name: Build all + run: | + cabal build all + cabal sdist all + - name: Run tests + run: | + cabal test all + - name: Build haddock + run: | + cabal haddock all diff --git a/.gitignore b/.gitignore index 178135c..9531c87 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /dist/ +/dist-newstyle/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0b696e7..0000000 --- a/.travis.yml +++ /dev/null @@ -1,94 +0,0 @@ -# This file has been generated -- see https://github.com/hvr/multi-ghc-travis -language: c -sudo: false - -cache: - directories: - - $HOME/.cabsnap - - $HOME/.cabal/packages - -before_cache: - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar - -matrix: - include: - - env: CABALVER=1.16 GHCVER=7.6.3 - compiler: ": #GHC 7.6.3" - addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3], sources: [hvr-ghc]}} - - env: CABALVER=1.18 GHCVER=7.8.4 - compiler: ": #GHC 7.8.4" - addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}} - - env: CABALVER=1.22 GHCVER=7.10.2 - compiler: ": #GHC 7.10.2" - addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}} - - env: CABALVER=1.22 GHCVER=7.10.3 - compiler: ": #GHC 7.10.3" - addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}} - - env: CABALVER=1.24 GHCVER=8.0.1 - compiler: ": #GHC 8.0.1" - addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], sources: [hvr-ghc]}} - - env: CABALVER=head GHCVER=head - compiler: ": #GHC head" - addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}} - - allow_failures: - - env: CABALVER=head GHCVER=head - -before_install: - - unset CC - - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH - -install: - - cabal --version - - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]" - - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ]; - then - zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz > - $HOME/.cabal/packages/hackage.haskell.org/00-index.tar; - fi - - travis_retry cabal update -v - - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config - - cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt - - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt - -# check whether current requested install-plan matches cached package-db snapshot - - if diff -u installplan.txt $HOME/.cabsnap/installplan.txt; - then - echo "cabal build-cache HIT"; - rm -rfv .ghc; - cp -a $HOME/.cabsnap/ghc $HOME/.ghc; - cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/; - else - echo "cabal build-cache MISS"; - rm -rf $HOME/.cabsnap; - mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin; - cabal install --only-dependencies --enable-tests --enable-benchmarks; - fi - -# snapshot package-db on cache miss - - if [ ! -d $HOME/.cabsnap ]; - then - echo "snapshotting package-db to build-cache"; - mkdir $HOME/.cabsnap; - cp -a $HOME/.ghc $HOME/.cabsnap/ghc; - cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/; - fi - -# Here starts the actual work to be performed for the package under test; -# any command which exits with a non-zero exit code causes the build to fail. -script: - - if [ -f configure.ac ]; then autoreconf -i; fi - - cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging - - cabal build # this builds all libraries and executables (including tests/benchmarks) - - cabal test - - cabal check - - cabal sdist # tests that a source-distribution can be generated - -# Check that the resulting source distribution can be built & installed. -# If there are no other `.tar.gz` files in `dist`, this can be even simpler: -# `cabal install --force-reinstalls dist/*-*.tar.gz` - - SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz && - (cd dist && cabal install --force-reinstalls "$SRC_TGZ") - -# EOF diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7fd0af9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +## unreleased + +## 0.4 + +* Supported GHC 9 and older +* Changed the type of `mkUTCTime :: Day -> DiffTime -> UTCTime` to `mkUTCTime :: Year -> Month -> DayOfMonth -> Hour -> Minute -> Double -> UTCTime`. Use the `UTCTime` pattern synonym instead if needed. +* Miscellaneous API additions and refactors \ No newline at end of file diff --git a/README.md b/README.md index 8b43752..60458c6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # thyme -[![Status](https://travis-ci.org/liyang/thyme.png)][travis] [Hackage][] +[![CI](https://github.com/fumieval/thyme/actions/workflows/haskell.yml/badge.svg)](https://github.com/fumieval/thyme/actions/workflows/haskell.yml) [![Hackage](https://img.shields.io/hackage/v/thyme.svg?logo=haskell)](http://hackage.haskell.org/package/thyme) A faster date and time library based on [time][]. @@ -14,19 +14,17 @@ A faster date and time library based on [time][]. ### Library ``` -stack build --flag thyme:lens +cabal build ``` ### Haddock ``` -stack haddock --flag thyme:lens +cabal haddock ``` -[Hackage]: http://hackage.haskell.org/package/thyme [Iso']: http://hackage.haskell.org/package/lens/docs/Control-Lens-Iso.html#t:Iso-39- [Lens']: http://hackage.haskell.org/package/lens/docs/Control-Lens-Lens.html#t:Lens-39- [lens]: http://hackage.haskell.org/package/lens [time]: http://hackage.haskell.org/package/time -[travis]: https://travis-ci.org/liyang/thyme diff --git a/src/Data/Thyme/Clock/TAI.hs b/src/Data/Thyme/Clock/TAI.hs index 336d81d..23c034c 100644 --- a/src/Data/Thyme/Clock/TAI.hs +++ b/src/Data/Thyme/Clock/TAI.hs @@ -344,7 +344,7 @@ diffAbsoluteTime = (.-.) -- @ {-# INLINE utcToTAITime #-} utcToTAITime :: TAIUTCMap -> UTCTime -> AbsoluteTime -utcToTAITime = view . absoluteTime +utcToTAITime m = view (absoluteTime m) -- | Using a 'TAIUTCMap', convert a 'AbsoluteTime' to 'UTCTime'. -- @@ -353,5 +353,5 @@ utcToTAITime = view . absoluteTime -- @ {-# INLINE taiToUTCTime #-} taiToUTCTime :: TAIUTCMap -> AbsoluteTime -> UTCTime -taiToUTCTime = review . absoluteTime +taiToUTCTime m = review (absoluteTime m) diff --git a/src/Data/Thyme/Format.hs b/src/Data/Thyme/Format.hs index 207dee9..70115b9 100644 --- a/src/Data/Thyme/Format.hs +++ b/src/Data/Thyme/Format.hs @@ -4,6 +4,10 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-orphans #-} +#if __GLASGOW_HASKELL__ >= 810 +{-# OPTIONS_GHC -Wno-unused-record-wildcards #-} +#endif + #include "thyme.h" -- | Formatting and parsing for dates and times. @@ -284,7 +288,7 @@ showsY = showsYear instance FormatTime TimeOfDay where {-# INLINEABLE showsTime #-} - showsTime TimeLocale {..} (TimeOfDay h m (DiffTime s)) = \ def c -> case c of + showsTime TimeLocale{..} (TimeOfDay h m (DiffTime s)) = \ def c -> case c of -- aggregate 'R' -> shows02 h . (:) ':' . shows02 m 'T' -> shows02 h . (:) ':' . shows02 m . (:) ':' . shows02 si @@ -838,7 +842,7 @@ instance ParseTime LocalTime where instance ParseTime Day where {-# INLINE buildTime #-} - buildTime tp@TimeParse {..} + buildTime tp | tp ^. flag IsOrdinalDate = ordinalDate # buildTime tp | tp ^. flag IsGregorian = gregorian # buildTime tp | tp ^. flag IsWeekDate = weekDate # buildTime tp diff --git a/src/Data/Thyme/LocalTime.hs b/src/Data/Thyme/LocalTime.hs index 941b902..c38f777 100644 --- a/src/Data/Thyme/LocalTime.hs +++ b/src/Data/Thyme/LocalTime.hs @@ -620,7 +620,7 @@ timeOfDayToDayFraction = view dayFraction -- @ {-# INLINE utcToLocalTime #-} utcToLocalTime :: TimeZone -> UTCTime -> LocalTime -utcToLocalTime = view . utcLocalTime +utcToLocalTime tz = view (utcLocalTime tz) -- | Convert a 'LocalTime' in the given 'TimeZone' to a 'UTCTime'. -- @@ -629,7 +629,7 @@ utcToLocalTime = view . utcLocalTime -- @ {-# INLINE localTimeToUTC #-} localTimeToUTC :: TimeZone -> LocalTime -> UTCTime -localTimeToUTC = review . utcLocalTime +localTimeToUTC tz = review (utcLocalTime tz) -- | Convert a 'UniversalTime' to a 'LocalTime' at the given medidian in -- degrees East. @@ -639,7 +639,7 @@ localTimeToUTC = review . utcLocalTime -- @ {-# INLINE ut1ToLocalTime #-} ut1ToLocalTime :: Rational -> UniversalTime -> LocalTime -ut1ToLocalTime = view . ut1LocalTime +ut1ToLocalTime l = view (ut1LocalTime l) -- | Convert a 'LocalTime' at the given meridian in degrees East to -- a 'UniversalTime'. @@ -649,7 +649,7 @@ ut1ToLocalTime = view . ut1LocalTime -- @ {-# INLINE localTimeToUT1 #-} localTimeToUT1 :: Rational -> LocalTime -> UniversalTime -localTimeToUT1 = review . ut1LocalTime +localTimeToUT1 l = review (ut1LocalTime l) -- | Convert a 'UTCTime' and the given 'TimeZone' into a 'ZonedTime'. -- diff --git a/src/Data/Thyme/Time/Core.hs b/src/Data/Thyme/Time/Core.hs index b6d7e3d..8dfe60a 100644 --- a/src/Data/Thyme/Time/Core.hs +++ b/src/Data/Thyme/Time/Core.hs @@ -34,7 +34,7 @@ import qualified Data.Time.Calendar as T import qualified Data.Time.Clock as T import qualified Data.Time.Clock.TAI as T import qualified Data.Time.LocalTime as T -import Unsafe.TrueName +import Data.Thyme.TrueName ------------------------------------------------------------------------ -- * Type conversion diff --git a/src/Data/Thyme/TrueName.hs b/src/Data/Thyme/TrueName.hs new file mode 100644 index 0000000..23105b1 --- /dev/null +++ b/src/Data/Thyme/TrueName.hs @@ -0,0 +1,423 @@ +{-# LANGUAGE CPP #-} + +-- | Refer to . + +module Data.Thyme.TrueName (summon, truename) where + +import Prelude +#if !MIN_VERSION_base(4,8,0) +import Control.Applicative +#endif +import Control.Monad +import Data.List (nub) +import Language.Haskell.TH.Ppr +import Language.Haskell.TH.PprLib +import Language.Haskell.TH.Quote +import Language.Haskell.TH.Syntax + +conNames :: Con -> [Name]{- {{{ -} +conNames con = case con of + NormalC name _ -> [name] + RecC name vbts -> name : [ fname | (fname, _, _) <- vbts ] + InfixC _ name _ -> [name] + ForallC _ _ con' -> conNames con' + +#if MIN_VERSION_template_haskell(2,11,0) + GadtC names _ typ -> names ++ typNames typ + RecGadtC names vbts typ -> names ++ typNames typ + ++ [ fname | (fname, _, _) <- vbts] +#endif +{- }}} -} + +decNames :: Dec -> [Name]{- {{{ -} +decNames dec = case dec of + FunD _ _ -> [] + ValD _ _ _ -> [] + TySynD _ _ typ -> typNames typ + ClassD _ _ _ _ decs -> decNames =<< decs +#if MIN_VERSION_template_haskell(2,11,0) + InstanceD _ cxt typ decs -> +#else + InstanceD cxt typ decs -> +#endif + (predNames =<< cxt) ++ typNames typ ++ (decNames =<< decs) + SigD name typ -> name : typNames typ + +#if MIN_VERSION_template_haskell(2,16,0) + KiSigD name kind -> name : typNames kind +#endif + + ForeignD frgn -> case frgn of + ImportF _ _ _ name t -> name : typNames t + ExportF _ _ name t -> name : typNames t + PragmaD _ -> [] + +#if MIN_VERSION_template_haskell(2,11,0) + DataD _ _ _ _ cons _ -> conNames =<< cons + NewtypeD _ _ _ _ con _ -> conNames con +#else + DataD _ _ _ cons _ -> conNames =<< cons + NewtypeD _ _ _ con _ -> conNames con +#endif + +#if MIN_VERSION_template_haskell(2,12,0) + PatSynD _name _args _dir _pat -> [] + PatSynSigD _name typ -> typNames typ +#endif + +#if MIN_VERSION_template_haskell(2,8,0) + InfixD _ _ -> [] +#endif + +#if MIN_VERSION_template_haskell(2,12,0) + DataInstD cxt _name _typs _kind cons derivs -> + datatypeNames cxt cons ++ derivNames derivs + NewtypeInstD cxt _name _typs _kind con derivs -> + datatypeNames cxt [con] ++ derivNames derivs +#elif MIN_VERSION_template_haskell(2,11,0) + DataInstD cxt _ _ _ cons derivs -> + datatypeNames cxt cons ++ (predNames =<< derivs) + NewtypeInstD cxt _ _ _ con derivs -> + datatypeNames cxt [con] ++ (predNames =<< derivs) +#else + DataInstD cxt _ _ cons derivs -> datatypeNames cxt cons ++ derivs + NewtypeInstD cxt _ _ con derivs -> datatypeNames cxt [con] ++ derivs +#endif + +#if MIN_VERSION_template_haskell(2,11,0) + DataFamilyD _ _ _ -> [] + OpenTypeFamilyD _ -> [] +#else + FamilyD _ _ _ _ -> [] +#endif + +#if MIN_VERSION_template_haskell(2,11,0) + ClosedTypeFamilyD _ tses -> tseNames =<< tses +#elif MIN_VERSION_template_haskell(2,9,0) + ClosedTypeFamilyD _ _ _ tses -> tseNames =<< tses +#endif + +#if MIN_VERSION_template_haskell(2,15,0) + TySynInstD tse -> tseNames tse +#elif MIN_VERSION_template_haskell(2,9,0) + TySynInstD _ tse -> tseNames tse +#else + TySynInstD _ ts t -> (typNames =<< ts) ++ typNames t +#endif + +#if MIN_VERSION_template_haskell(2,9,0) + RoleAnnotD _ _ -> [] +#endif + +#if MIN_VERSION_template_haskell(2,12,0) + StandaloneDerivD _strat cxt typ -> (predNames =<< cxt) ++ typNames typ +#elif MIN_VERSION_template_haskell(2,10,0) + StandaloneDerivD cxt typ -> (predNames =<< cxt) ++ typNames typ +#endif + +#if MIN_VERSION_template_haskell(2,10,0) + DefaultSigD _ _ -> [] +#endif + +#if MIN_VERSION_template_haskell(2,15,0) + ImplicitParamBindD _ _ -> [] +#endif + +{- }}} -} + +datatypeNames :: Cxt -> [Con] -> [Name] +datatypeNames cxt cons = (conNames =<< cons) ++ (predNames =<< cxt) + +#if MIN_VERSION_template_haskell(2,12,0) +derivNames :: [DerivClause] -> [Name] +derivNames derivs = predNames =<< + [ p | DerivClause _strat cxt <- derivs, p <- cxt ] +#endif + +tseNames :: TySynEqn -> [Name] +#if MIN_VERSION_template_haskell(2,15,0) +tseNames (TySynEqn _ l r) = typNames l ++ typNames r +#elif MIN_VERSION_template_haskell(2,9,0) +tseNames (TySynEqn ts t) = (typNames =<< ts) ++ typNames t +#endif + +predNames :: Pred -> [Name]{- {{{ -} +#if MIN_VERSION_template_haskell(2,10,0) +predNames = typNames +#else +predNames p = case p of + ClassP n ts -> n : (typNames =<< ts) + EqualP s t -> typNames s ++ typNames t +#endif +{- }}} -} + +typNames :: Type -> [Name]{- {{{ -} +typNames typ = case typ of + ForallT _ c t -> (predNames =<< c) ++ typNames t + AppT s t -> typNames s ++ typNames t + SigT t _ -> typNames t + VarT _ -> [] + ConT name -> [name] + TupleT _ -> [] + UnboxedTupleT _ -> [] + ArrowT -> [] + ListT -> [] + +#if MIN_VERSION_template_haskell(2,8,0) + PromotedT _ -> [] + PromotedTupleT _ -> [] + PromotedNilT -> [] + PromotedConsT -> [] + StarT -> [] + ConstraintT -> [] + LitT _ -> [] +#endif + +#if MIN_VERSION_template_haskell(2,10,0) + EqualityT -> [] +#endif + +#if MIN_VERSION_template_haskell(2,11,0) + InfixT s n t -> n : typNames s ++ typNames t + UInfixT s n t -> n : typNames s ++ typNames t + ParensT t -> typNames t + WildCardT -> [] +#endif + +#if MIN_VERSION_template_haskell(2,12,0) + UnboxedSumT _arity -> [] +#endif + +#if MIN_VERSION_template_haskell(2,15,0) + AppKindT k t -> typNames k ++ typNames t + ImplicitParamT _ t -> typNames t +#endif + +#if MIN_VERSION_template_haskell(2,16,0) + ForallVisT _ t -> typNames t +#endif + +#if MIN_VERSION_template_haskell(2,17,0) + MulArrowT -> [] +#endif + +#if MIN_VERSION_template_haskell(2,19,0) + PromotedInfixT s n t -> n : typNames s ++ typNames t + PromotedUInfixT s n t -> n : typNames s ++ typNames t +#endif +{- }}} -} + +infoNames :: Info -> [Name]{- {{{ -} +infoNames info = case info of + ClassI dec _ -> decNames dec + TyConI dec -> decNames dec + FamilyI _ decs -> decNames =<< decs + PrimTyConI _ _ _ -> [] + TyVarI _ typ -> typNames typ + +#if MIN_VERSION_template_haskell(2,11,0) + ClassOpI _ typ _ -> typNames typ + DataConI _ typ parent -> parent : typNames typ + VarI _ typ _ -> typNames typ +#else + ClassOpI _ typ _ _ -> typNames typ + DataConI _ typ parent _ -> parent : typNames typ + VarI _ typ _ _ -> typNames typ +#endif + +#if MIN_VERSION_template_haskell(2,12,0) + PatSynI _name typ -> typNames typ +#endif +{- }}} -} + +{- {{{ -} +-- | Summons a 'Name' using @template-haskell@'s 'reify' function. +-- +-- The first argument is a 'String' matching the 'Name' we want: either its +-- 'nameBase', or qualified with its module. The second argument gives the +-- 'Name' to 'reify'. +-- +-- If no match is found or there is some ambiguity, 'summon' will fail with +-- a list of 'Name's found, along with the output of 'reify' for reference. +-- +-- Suppose we are given a module @M@ that exports a function @s@, but not +-- the type @T@, the constrcutor @C@, nor the field @f@: +-- +-- > module M (s) where +-- > newtype T = C { f :: Int } +-- > s :: T -> T +-- > s = C . succ . f +-- +-- In our own module we have no legitimate way of passing @s@ an argument of +-- type @T@. We can get around this in a type-safe way with 'summon': +-- +-- >{-# LANGUAGE TemplateHaskell #-} +-- >module Main where +-- >import Language.Haskell.TH.Syntax +-- >import Unsafe.TrueName +-- >import M +-- > +-- >type T = $(fmap ConT $ summon "T" 's) +-- >mkC :: Int -> T; unC :: T -> Int; f :: T -> Int +-- >mkC = $(fmap ConE $ summon "C" =<< summon "T" 's) +-- >unC $(fmap (`ConP` [VarP $ mkName "n"]) $ summon "C" =<< summon "T" 's) = n +-- >f = $(fmap VarE $ summon "f" =<< summon "T" 's) +-- > +-- >main :: IO () +-- >main = print (unC t, n) where +-- > t = s (mkC 42 :: T) +-- > n = f (s t) +-- +-- Note that 'summon' cannot obtain the 'Name' for an unexported function, +-- since GHC . +-- The only workaround is to copypasta the definition. D: +{- }}} -} +summon :: String -> Name -> Q Name{- {{{ -} +summon name thing = do + info <- reify thing + let ns = nub (infoNames info) + case filter (\ n -> name == nameBase n || name == show n) ns of + [n] -> return n + _ -> fail $ "summon: you wanted " ++ show name ++ ", but I have:\n" + ++ unlines ((++) " " . namespace <$> ns) + ++ " reify " ++ show thing ++ " returned:\n" + ++ show (nest 8 $ ppr info) + where + namespace n@(Name _ flavour) = show n ++ case flavour of + NameG VarName _ _ -> " (var)" + NameG DataName _ _ -> " (cons)" + NameG TcClsName _ _ -> " (type)" + _ -> " (?)" +{- }}} -} + +{- {{{ -} +-- | A more convenient 'QuasiQuoter' interface to 'summon'. +-- +-- The first space-delimited token gives the initial 'Name' passed to +-- 'summon': it must be ‘quoted’ with a @'@ or @''@ prefix to indicate +-- whether it should be interpreted in an expression or a type context, +-- as per . +-- Subsequent tokens correspond to the 'String' argument of 'summon', and +-- are iterated over. Thus +-- +-- > [truename| ''A B C D |] +-- +-- is roughly equivalent to: +-- +-- > summon "D" =<< summon "C" =<< summon "B" ''A +-- +-- but with the resulting 'Name' wrapped up in 'ConE', 'VarE', 'ConP', or +-- 'ConT', depending on the context. (There is no 'quoteDec'.) +-- +-- Variable bindings are given after a @|@ token in a 'Pat' context: +-- +-- > [truename| ''Chan Chan | chanR chanW |] <- newChan +-- +-- These may be prefixed with @!@ or @~@ to give the usual semantics. +-- A single @..@ token invokes @RecordWildCards@ in 'Pat' contexts, and for +-- record construction in 'Exp' contexts. +-- Nested or more exotic patterns are not supported. +-- +-- With this, the example from 'summon' may be more succinctly written: +-- +-- >{-# LANGUAGE QuasiQuotes #-} +-- >module Main where +-- >import Unsafe.TrueName +-- >import M +-- > +-- >type T = [truename| 's T |] +-- >mkC :: Int -> T; unC :: T -> Int; f :: T -> Int +-- >mkC = [truename| 's T C |] +-- >unC [truename| 's T C | n |] = n +-- >f = [truename| 's T f |] +-- > +-- >main :: IO () +-- >main = print (unC t, n) where +-- > t = s (mkC 42 :: T) +-- > n = f (s t) +{- }}} -} +truename :: QuasiQuoter{- {{{ -} +truename = QuasiQuoter + { quoteExp = makeE <=< nameVars + , quotePat = makeP <=< nameVars + , quoteType = makeT <=< nameVars + , quoteDec = \ _ -> err "I'm not sure how this would work" + } where + err = fail . (++) "truename: " + noPat = err . (++) "unexpected pattern variables: " . unwords + + makeT (name, vars) = ConT name <$ unless (null vars) (noPat vars) + makeE (name@(Name occ flavour), vars) = case flavour of + NameG VarName _ _ -> VarE name <$ unless (null vars) (noPat vars) + NameG DataName _ _ -> case vars of + [] -> return (ConE name) + [".."] -> RecConE name . capture VarE <$> recFields name + _ -> noPat vars + _ -> err $ occString occ ++ " has a strange flavour" + makeP (name, vars) = if vars == [".."] + then RecP name . capture VarP <$> recFields name + else +#if MIN_VERSION_template_haskell(2,18,0) + return $ ConP name [] (map pat vars) where +#else + return $ ConP name (map pat vars) where +#endif + pat n = case n of + "_" -> WildP + '!' : ns -> BangP (pat ns) + '~' : ns -> TildeP (pat ns) + _ -> VarP (mkName n) + capture v = map $ \ f -> (f, v (mkName $ nameBase f)) + + recFields :: Name -> Q [Name] + recFields name = do + parent <- reify name >>= \ info -> case info of +#if MIN_VERSION_template_haskell(2,11,0) + DataConI _ _ p -> return p +#else + DataConI _ _ p _ -> return p +#endif + _ -> err $ show name ++ " is not a data constructor" + dec <- reify parent >>= \ info -> case info of + TyConI d -> return d + _ -> err $ "parent " ++ show parent ++ " is not a plain type" + case dec of +#if MIN_VERSION_template_haskell(2,11,0) + DataD _ _ _ _ cs _ -> return (fields =<< cs) + NewtypeD _ _ _ _ c _ -> return (fields c) +#else + DataD _ _ _ cs _ -> return (fields =<< cs) + NewtypeD _ _ _ c _ -> return (fields c) +#endif + _ -> err $ "parent " ++ show parent ++ " neither data nor newtype" + where + fields :: Con -> [Name] + fields con = case con of + NormalC _ _ -> [] + RecC n vbts -> if n /= name then [] else [ v | (v, _, _) <- vbts ] + InfixC _ _ _ -> [] + ForallC _ _ c -> fields c +#if MIN_VERSION_template_haskell(2,11,0) + GadtC _ _ _ -> [] + RecGadtC ns vbts _ -> if name `notElem` ns then [] + else [ v | (v, _, _) <- vbts ] +#endif + + lookupThing :: String -> Q Name + lookupThing s0 = case s0 of + '\'' : s1 -> case s1 of + '\'' : s2 -> hmm s2 "lookupTypeName" =<< lookupTypeName s2 + _ -> hmm s1 "lookupValueName" =<< lookupValueName s1 + _ -> err $ "please specify either '" ++ s0 ++ " or ''" ++ s0 + where + hmm s l = maybe (err $ unwords [l, show s, "failed"]) return + + nameVars :: String -> Q (Name, [String]) + nameVars spec = case words spec of + [] -> err "expecting at least one token" + start : rest -> do + thing <- lookupThing start + let (names, vars) = break ("|" ==) rest + name <- foldM (flip summon) thing names + return (name, dropWhile ("|" ==) vars) +{- }}} -} diff --git a/stack.yaml b/stack.yaml deleted file mode 100644 index 40c6d39..0000000 --- a/stack.yaml +++ /dev/null @@ -1,6 +0,0 @@ -flags: {} -extra-package-dbs: [] -packages: -- '.' -extra-deps: [] -resolver: lts-5.11 diff --git a/tests/rewrite.hs b/tests/rewrite.hs index d5dad10..72ba5b0 100644 --- a/tests/rewrite.hs +++ b/tests/rewrite.hs @@ -45,16 +45,15 @@ useless = do wanted :: Set String wanted = Set.fromList - [ "fromSeconds/Float" - , "fromSeconds/Double" - , "fromSeconds/Int" - , "fromSeconds/Int64" - , "fromSeconds/Integer" - , "realToFrac/DiffTime-NominalDiffTime" - , "realToFrac/NominalDiffTime-DiffTime" - , "realToFrac/DiffTime-Fractional" - , "realToFrac/NominalDiffTime-Fractional" - , "realToFrac/Real-DiffTime" - , "realToFrac/Real-NominalDiffTime" - ] - + [ "fromSeconds/Float (Data.Thyme.Clock.Internal)" + , "fromSeconds/Double (Data.Thyme.Clock.Internal)" + , "fromSeconds/Int (Data.Thyme.Clock.Internal)" + , "fromSeconds/Int64 (Data.Thyme.Clock.Internal)" + , "fromSeconds/Integer (Data.Thyme.Clock.Internal)" + , "realToFrac/DiffTime-NominalDiffTime (Data.Thyme.Time)" + , "realToFrac/NominalDiffTime-DiffTime (Data.Thyme.Time)" + , "realToFrac/DiffTime-Fractional (Data.Thyme.Time)" + , "realToFrac/NominalDiffTime-Fractional (Data.Thyme.Time)" + , "realToFrac/Real-DiffTime (Data.Thyme.Time)" + , "realToFrac/Real-NominalDiffTime (Data.Thyme.Time)" + ] \ No newline at end of file diff --git a/thyme.cabal b/thyme.cabal index 893de5f..4736368 100644 --- a/thyme.cabal +++ b/thyme.cabal @@ -1,5 +1,5 @@ name: thyme -version: 0.3.5.5 +version: 0.4 synopsis: A faster time library description: @thyme@ is a performance-optimized rewrite of the excellent @@ -7,26 +7,26 @@ description: . See @@ for a full description. -homepage: https://github.com/liyang/thyme +homepage: https://github.com/fumieval/thyme license: BSD3 license-file: LICENSE author: Liyang HU, Ashley Yakeley -maintainer: thyme@liyang.hu +maintainer: Fumiaki Kinoshita copyright: © 2013−2014 Liyang HU category: Data, System build-type: Simple cabal-version: >= 1.10 stability: experimental extra-source-files: + CHANGELOG.md + README.md include/thyme.h tested-with: - GHC == 7.6.3, GHC == 7.8.4, - GHC == 7.10.2, GHC == 7.10.3, - GHC >= 8.0 && < 8.2 + GHC >= 8.4 && < 9.6 source-repository head type: git - location: https://github.com/liyang/thyme + location: https://github.com/fumieval/thyme flag bug-for-bug description: bug-for-bug compatibility with time @@ -81,6 +81,7 @@ library Data.Thyme.Calendar.Internal Data.Thyme.Clock.Internal Data.Thyme.Format.Internal + Data.Thyme.TrueName if !(flag(lens) || flag(docs)) other-modules: Control.Lens build-depends: @@ -96,16 +97,17 @@ library old-locale >= 1.0, random, text >= 0.11, + template-haskell >= 2.7 && < 2.20, time >= 1.4, - true-name >= 0.1.0.1, vector >= 0.9, vector-th-unbox >= 0.2.1.0, vector-space >= 0.8 + if os(windows) build-depends: Win32 if os(darwin) || os(freebsd) build-tools: cpphs - ghc-options: -pgmP cpphs -optP--cpp + ghc-options: "-pgmP cpphs --cpp" if flag(lens) || flag(docs) build-depends: lens >= 3.9 else