record-dot-preprocessor 0.2.7 → 0.2.8
raw patch · 8 files changed
+88/−15 lines, 8 filesdep ~ghc
Dependency ranges changed: ghc
Files
- CHANGES.txt +3/−0
- LICENSE +1/−1
- README.md +3/−3
- examples/Both.hs +1/−1
- plugin/Compat.hs +50/−1
- plugin/RecordDotPreprocessor.hs +16/−6
- preprocessor/Edit.hs +11/−0
- record-dot-preprocessor.cabal +3/−3
CHANGES.txt view
@@ -1,5 +1,8 @@ Changelog for record-dot-preprocessor +0.2.8, released 2021-02-21+ Support GHC 9.0+ #38, make the preprocessor avoid quasi quotes 0.2.7, released 2020-10-02 #29, deal with records containing type families in field types 0.2.6, released 2020-08-12
LICENSE view
@@ -1,4 +1,4 @@-Copyright Neil Mitchell 2018-2020.+Copyright Neil Mitchell 2018-2021. Licensed under either of:
README.md view
@@ -1,4 +1,4 @@-# record-dot-preprocessor [](https://hackage.haskell.org/package/record-dot-preprocessor) [](https://www.stackage.org/package/record-dot-preprocessor) [](https://travis-ci.org/ndmitchell/record-dot-preprocessor)+# record-dot-preprocessor [](https://hackage.haskell.org/package/record-dot-preprocessor) [](https://www.stackage.org/package/record-dot-preprocessor) [](https://github.com/ndmitchell/record-dot-preprocessor/actions) In almost every programming language `a.b` will get the `b` field from the `a` data type, and many different data types can have a `b` field. The reason this feature is ubiquitous is because it's _useful_. This feature has been [proposed for Haskell](https://github.com/ghc-proposals/ghc-proposals/pull/282) as `RecordDotSyntax`. The `record-dot-preprocessor` brings this feature to Haskell today. Some examples: @@ -20,9 +20,9 @@ First install `record-dot-preprocessor` with either `stack install record-dot-preprocessor` or `cabal update && cabal install record-dot-preprocessor`. Then at the top of the file add: * Either: `{-# OPTIONS_GHC -F -pgmF=record-dot-preprocessor #-}` for the preprocessor.-* Or: `{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}` and `{-# LANGUAGE DuplicateRecordFields, TypeApplications, FlexibleContexts, DataKinds, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}` for the GHC plugin.+* Or: `{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}` and `{-# LANGUAGE DuplicateRecordFields, TypeApplications, FlexibleContexts, DataKinds, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances, UndecidableInstances, GADTs #-}` for the GHC plugin. -The GHC plugin only runs on GHC 8.6 or higher, [doesn't work on Windows](https://gitlab.haskell.org/ghc/ghc/issues/16405) and has much better error messages. In contrast, the preprocessor runs everywhere and has more features.+The GHC plugin only runs on GHC 8.6 or higher, [has some issues on Windows](https://gitlab.haskell.org/ghc/ghc/issues/16405) and has much better error messages. In contrast, the preprocessor runs everywhere and has more features. You must make sure that the `OPTIONS_GHC` is applied both to the file _where your records are defined_, and _where the record syntax is used_. The resulting program will require the [`record-hasfield` library](https://hackage.haskell.org/package/record-hasfield).
examples/Both.hs view
@@ -180,7 +180,7 @@ -- --------------------------------------------------------------------- -- Deal with kind signatures -data UserF (f :: * -> *) = UserF { userf_name :: String }+data UserF (f :: T.Type -> T.Type) = UserF { userf_name :: String } test7 :: IO () test7 = do
plugin/Compat.hs view
@@ -5,7 +5,14 @@ module Compat(module Compat) where import GHC+#if __GLASGOW_HASKELL__ < 900 import BasicTypes+import TcEvidence+#else+import GHC.Types.Basic+import GHC.Unit.Types+import GHC.Parser.Annotation+#endif #if __GLASGOW_HASKELL__ < 810 import HsSyn as Compat #else@@ -26,11 +33,32 @@ noE = noExtField #endif +realSrcLoc :: SrcLoc -> Maybe RealSrcLoc+#if __GLASGOW_HASKELL__ < 811+realSrcLoc (RealSrcLoc x) = Just x+#else+realSrcLoc (RealSrcLoc x _) = Just x+#endif+realSrcLoc _ = Nothing++#if __GLASGOW_HASKELL__ >= 900+hsLTyVarBndrToType :: LHsTyVarBndr flag (GhcPass p) -> LHsType (GhcPass p)+hsLTyVarBndrToType x = noL $ HsTyVar noE NotPromoted $ noL $ hsLTyVarName x+#endif+ --------------------------------------------------------------------- -- COMMON SIGNATURES +#if __GLASGOW_HASKELL__ < 811+type Module = HsModule GhcPs+#else+type Module = HsModule+#endif+ mkAppType :: LHsExpr GhcPs -> LHsType GhcPs -> LHsExpr GhcPs mkTypeAnn :: LHsExpr GhcPs -> LHsType GhcPs -> LHsExpr GhcPs+mkFunTy :: LHsType GhcPs -> LHsType GhcPs -> LHsType GhcPs+newFunBind :: Located RdrName -> MatchGroup GhcPs (LHsExpr GhcPs) -> HsBind GhcPs #if __GLASGOW_HASKELL__ < 807 @@ -46,7 +74,21 @@ #endif +#if __GLASGOW_HASKELL__ < 811 +-- GHC 8.10 and below+mkFunTy a b = noL $ HsFunTy noE a b+newFunBind a b = FunBind noE a b WpHole []++#else++-- GHC 9.0+mkFunTy a b = noL $ HsFunTy noE (HsUnrestrictedArrow NormalSyntax) a b+newFunBind a b = FunBind noE a b []++#endif++ #if __GLASGOW_HASKELL__ < 807 -- GHC 8.6@@ -76,9 +118,16 @@ qualifiedImplicitImport x = noL $ ImportDecl noE NoSourceText (noL x) Nothing False False True {- qualified -} True {- implicit -} Nothing Nothing -#else+#elif __GLASGOW_HASKELL__ < 811 +-- GHC 8.10 qualifiedImplicitImport x = noL $ ImportDecl noE NoSourceText (noL x) Nothing False False+ QualifiedPost {- qualified -} True {- implicit -} Nothing Nothing++#else++-- GHC 9.0+qualifiedImplicitImport x = noL $ ImportDecl noE NoSourceText (noL x) Nothing NotBoot False QualifiedPost {- qualified -} True {- implicit -} Nothing Nothing #endif
plugin/RecordDotPreprocessor.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards, ViewPatterns, NamedFieldPuns, OverloadedStrings #-} {- HLINT ignore "Use camelCase" -} @@ -8,13 +9,22 @@ import Data.List.Extra import Data.Tuple.Extra import Compat-import Bag import qualified GHC+#if __GLASGOW_HASKELL__ < 900+import Bag import qualified GhcPlugins as GHC import qualified PrelNames as GHC import SrcLoc-import TcEvidence+#else+import GHC.Data.Bag+import qualified GHC.Driver.Plugins as GHC+import qualified GHC.Driver.Types as GHC+import qualified GHC.Builtin.Names as GHC+import qualified GHC.Plugins as GHC+import GHC.Types.SrcLoc+#endif + --------------------------------------------------------------------- -- PLUGIN WRAPPER @@ -43,7 +53,7 @@ var_dot = GHC.mkRdrUnqual $ GHC.mkVarOcc "." -onModule :: HsModule GhcPs -> HsModule GhcPs+onModule :: Module -> Module onModule x = x { hsmodImports = onImports $ hsmodImports x , hsmodDecls = concatMap onDecl $ hsmodDecls x }@@ -71,7 +81,7 @@ typ = noL $ makeEqQualTy field (unLoc . typ') has :: LHsBindLR GhcPs GhcPs- has = noL $ FunBind noE (noL var_hasField) (mg1 eqn) WpHole []+ has = noL $ newFunBind (noL var_hasField) (mg1 eqn) where eqn = Match { m_ext = noE@@ -88,7 +98,7 @@ update = RecordUpd noE (noL $ GHC.HsVar noE $ noL vR) [noL $ HsRecField (noL (Unambiguous noE (rdrNameFieldOcc selector))) (noL $ GHC.HsVar noE $ noL vX) False] get = mkApp- (mkParen $ mkTypeAnn (noL $ GHC.HsVar noE $ rdrNameFieldOcc selector) (noL $ HsFunTy noE (noL record) (noL field)))+ (mkParen $ mkTypeAnn (noL $ GHC.HsVar noE $ rdrNameFieldOcc selector) (mkFunTy (noL record) (noL field))) (noL $ GHC.HsVar noE $ noL vR) mg1 :: Match GhcPs (LHsExpr GhcPs) -> MatchGroup GhcPs (LHsExpr GhcPs)@@ -224,7 +234,7 @@ -- | Are the end of a and the start of b next to each other, no white space adjacentBy :: Int -> Located a -> Located b -> Bool-adjacentBy i (L (srcSpanEnd -> RealSrcLoc a) _) (L (srcSpanStart -> RealSrcLoc b) _) =+adjacentBy i (L (realSrcLoc . srcSpanEnd -> Just a) _) (L (realSrcLoc . srcSpanStart -> Just b) _) = srcLocFile a == srcLocFile b && srcLocLine a == srcLocLine b && srcLocCol a + i == srcLocCol b
preprocessor/Edit.hs view
@@ -54,6 +54,14 @@ isCtor (Item x) = any isUpper $ take 1 $ lexeme x isCtor _ = False +-- | This test does not check that the @quoter@ name is a qualified identifier,+-- instead relying on lack of whitespace in the opener and existence of a paired+-- closed (@|]@)+isQuasiQuotation :: PL -> Bool+isQuasiQuotation (Paren (L "[") inner@(Item qq : Item (L "|") : _) (L "]"))+ | Item close@(L "|") <- last inner = null (whitespace qq) && null (whitespace close)+isQuasiQuotation _ = False+ isField (x:_) = x == '_' || isLower x isField _ = False @@ -95,6 +103,9 @@ editLoop :: [PL] -> [PL]++-- Leave quasiquotations alone+editLoop (p : ps) | isQuasiQuotation p = p : editLoop ps -- | a.b.c ==> getField @'(b,c) a editLoop (NoW e : (spanFields -> (fields@(_:_), whitespace, rest)))
record-dot-preprocessor.cabal view
@@ -1,14 +1,14 @@ cabal-version: >= 1.18 build-type: Simple name: record-dot-preprocessor-version: 0.2.7+version: 0.2.8 license: BSD3 x-license: BSD-3-Clause OR Apache-2.0 license-file: LICENSE category: Development author: Neil Mitchell <ndmitchell@gmail.com> maintainer: Neil Mitchell <ndmitchell@gmail.com>-copyright: Neil Mitchell 2018-2020+copyright: Neil Mitchell 2018-2021 synopsis: Preprocessor to allow record.field syntax description: In almost every programming language @a.b@ will get the @b@ field from the @a@ data type, and many different data types can have a @b@ field.@@ -19,7 +19,7 @@ extra-doc-files: README.md CHANGES.txt-tested-with: GHC==8.10, GHC==8.8, GHC==8.6, GHC==8.4+tested-with: GHC==9.0, GHC==8.10, GHC==8.8, GHC==8.6 extra-source-files: examples/Both.hs examples/Both2.hs