packages feed

prosidy 1.6.0.1 → 1.6.0.2

raw patch · 22 files changed

+617/−486 lines, 22 filesdep +base-compat-batteriesdep +prettyprinterdep −contravariantPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: base-compat-batteries, prettyprinter

Dependencies removed: contravariant

API changes (from Hackage documentation)

- Prosidy.Source: instance Control.DeepSeq.NFData Prosidy.Source.Column
- Prosidy.Source: instance Control.DeepSeq.NFData Prosidy.Source.Line
- Prosidy.Source: instance Control.DeepSeq.NFData Prosidy.Source.LineMap
- Prosidy.Source: instance Control.DeepSeq.NFData Prosidy.Source.Offset
- Prosidy.Source: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Source.Column
- Prosidy.Source: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Source.Line
- Prosidy.Source: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Source.Offset
- Prosidy.Source: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Source.Column
- Prosidy.Source: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Source.Line
- Prosidy.Source: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Source.Offset
- Prosidy.Source: instance Data.Binary.Class.Binary Prosidy.Source.Column
- Prosidy.Source: instance Data.Binary.Class.Binary Prosidy.Source.Line
- Prosidy.Source: instance Data.Binary.Class.Binary Prosidy.Source.LineMap
- Prosidy.Source: instance Data.Binary.Class.Binary Prosidy.Source.Offset
- Prosidy.Source: instance Data.Hashable.Class.Hashable Prosidy.Source.Column
- Prosidy.Source: instance Data.Hashable.Class.Hashable Prosidy.Source.Line
- Prosidy.Source: instance Data.Hashable.Class.Hashable Prosidy.Source.LineMap
- Prosidy.Source: instance Data.Hashable.Class.Hashable Prosidy.Source.Offset
- Prosidy.Source: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Prosidy.Source.Offset
- Prosidy.Source: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Prosidy.Source.Offset
- Prosidy.Source: instance Data.Vector.Unboxed.Base.Unbox Prosidy.Source.Offset
- Prosidy.Source: instance GHC.Classes.Eq Prosidy.Source.Column
- Prosidy.Source: instance GHC.Classes.Eq Prosidy.Source.Line
- Prosidy.Source: instance GHC.Classes.Eq Prosidy.Source.LineMap
- Prosidy.Source: instance GHC.Classes.Eq Prosidy.Source.Offset
- Prosidy.Source: instance GHC.Classes.Ord Prosidy.Source.Column
- Prosidy.Source: instance GHC.Classes.Ord Prosidy.Source.Line
- Prosidy.Source: instance GHC.Classes.Ord Prosidy.Source.Offset
- Prosidy.Source: instance GHC.Enum.Enum Prosidy.Source.Column
- Prosidy.Source: instance GHC.Enum.Enum Prosidy.Source.Line
- Prosidy.Source: instance GHC.Enum.Enum Prosidy.Source.Offset
- Prosidy.Source: instance GHC.Generics.Generic Prosidy.Source.Column
- Prosidy.Source: instance GHC.Generics.Generic Prosidy.Source.Line
- Prosidy.Source: instance GHC.Generics.Generic Prosidy.Source.LineMap
- Prosidy.Source: instance GHC.Generics.Generic Prosidy.Source.Offset
- Prosidy.Source: instance GHC.Show.Show Prosidy.Source.Column
- Prosidy.Source: instance GHC.Show.Show Prosidy.Source.Line
- Prosidy.Source: instance GHC.Show.Show Prosidy.Source.LineMap
- Prosidy.Source: instance GHC.Show.Show Prosidy.Source.Offset
- Prosidy.Types: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Types.Block
- Prosidy.Types: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Types.Document
- Prosidy.Types: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Types.Fragment
- Prosidy.Types: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Types.Inline
- Prosidy.Types: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Types.Metadata
- Prosidy.Types: instance Data.Aeson.Types.FromJSON.FromJSON Prosidy.Types.Paragraph
- Prosidy.Types: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (Prosidy.Types.Tag a)
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Types.Block
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Types.Document
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Types.Fragment
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Types.Inline
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Types.Metadata
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON Prosidy.Types.Paragraph
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (Prosidy.Types.Region a)
- Prosidy.Types: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (Prosidy.Types.Tag a)
- Prosidy.Types.Key: instance Data.Aeson.Types.FromJSON.FromJSONKey Prosidy.Types.Key.Key
- Prosidy.Types.Set: instance (Data.Hashable.Class.Hashable a, GHC.Classes.Eq a, Data.Aeson.Types.FromJSON.FromJSONKey a) => Data.Aeson.Types.FromJSON.FromJSON (Prosidy.Types.Set.Set a)
- Prosidy.Types.Set: instance (Data.Hashable.Class.Hashable a, GHC.Classes.Eq a, Data.Aeson.Types.ToJSON.ToJSONKey a) => Data.Aeson.Types.ToJSON.ToJSON (Prosidy.Types.Set.Set a)
+ Prosidy.Source: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Source.Location
+ Prosidy.Source: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Source.Source
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Types.Block
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Types.Document
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Types.Fragment
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Types.Inline
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Types.Metadata
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Types.Paragraph
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty a => Data.Text.Prettyprint.Doc.Internal.Pretty (Prosidy.Types.Region a)
+ Prosidy.Types: instance Data.Text.Prettyprint.Doc.Internal.Pretty a => Data.Text.Prettyprint.Doc.Internal.Pretty (Prosidy.Types.Tag a)
+ Prosidy.Types.Assoc: instance (Data.Text.Prettyprint.Doc.Internal.Pretty k, Data.Text.Prettyprint.Doc.Internal.Pretty v) => Data.Text.Prettyprint.Doc.Internal.Pretty (Prosidy.Types.Assoc.Assoc k v)
+ Prosidy.Types.Assoc: toEntries :: Assoc k v -> [(k, v)]
+ Prosidy.Types.Key: instance Data.Text.Prettyprint.Doc.Internal.Pretty Prosidy.Types.Key.Key
+ Prosidy.Types.Series: instance Data.Text.Prettyprint.Doc.Internal.Pretty a => Data.Text.Prettyprint.Doc.Internal.Pretty (Prosidy.Types.Series.Series a)
+ Prosidy.Types.Series: instance Data.Text.Prettyprint.Doc.Internal.Pretty a => Data.Text.Prettyprint.Doc.Internal.Pretty (Prosidy.Types.Series.SeriesNE a)
+ Prosidy.Types.Set: instance Data.Text.Prettyprint.Doc.Internal.Pretty a => Data.Text.Prettyprint.Doc.Internal.Pretty (Prosidy.Types.Set.Set a)

Files

prosidy.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                prosidy-version:             1.6.0.1+version:             1.6.0.2 synopsis:            A simple language for writing documents. license:             MPL-2.0 license-file:        LICENSE@@ -13,9 +13,9 @@ extra-source-files:  CHANGELOG.md, README.pro  tested-with:-    GHC == 8.4.4-  , GHC == 8.6.5+    GHC == 8.6.5   , GHC == 8.8.1+  , GHC == 8.8.3  description:     Prosidy is a small language for writing documents.@@ -58,24 +58,28 @@      other-modules:         Prosidy.Optics.Internal-      , Prosidy.Compat+      , Prosidy.Source.LineMap+      , Prosidy.Source.Units+      , Prosidy.Internal.Classes+      , Prosidy.Internal.JSON      build-depends:-        base                 >= 4.11 && < 5-      , aeson                >= 1.4  && < 1.5-      , bytestring           >= 0.10 && < 0.11-      , binary               >= 0.8  && < 0.9-      , containers           >= 0.6  && < 0.7-      , contravariant        >= 1.5  && < 1.6-      , deepseq              >= 1.4  && < 1.5-      , hashable             >= 1.2  && < 1.4-      , megaparsec           >= 7.0  && < 8.1-      , profunctors          >= 5.3  && < 5.6-      , tagged               >= 0.8  && < 0.9-      , text                 >= 1.2  && < 1.3-      , transformers         >= 0.5  && < 0.6-      , vector               >= 0.12 && < 0.13-      , unordered-containers >= 0.2  && < 0.3+        base                  >= 4.11 && < 5+      , base-compat-batteries >= 0.11 && < 0.12+      , aeson                 >= 1.4  && < 1.5+      , bytestring            >= 0.10 && < 0.11+      , binary                >= 0.8  && < 0.9+      , containers            >= 0.6  && < 0.7+      , deepseq               >= 1.4  && < 1.5+      , hashable              >= 1.2  && < 1.4+      , megaparsec            >= 7.0  && < 8.1+      , prettyprinter         >= 1.6  && < 1.7+      , profunctors           >= 5.3  && < 5.6+      , tagged                >= 0.8  && < 0.9+      , text                  >= 1.2  && < 1.3+      , transformers          >= 0.5  && < 0.6+      , vector                >= 0.12 && < 0.13+      , unordered-containers  >= 0.2  && < 0.3  ------------------------------------------------------------------------------- test-suite prosidy-test
src/Prosidy.hs view
@@ -5,6 +5,7 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}+{-# LANGUAGE Safe #-} module Prosidy (module X) where  import           Prosidy.Source                as X@@ -17,3 +18,5 @@ import           Prosidy.Optics                as X import           Prosidy.Parse                 as X import           Prosidy.Types                 as X++import           Prosidy.Internal.JSON          ( )
− src/Prosidy/Compat.hs
@@ -1,8 +0,0 @@-{-# LANGUAGE CPP #-}-module Prosidy.Compat -  ( MonadFail(..) -  ) where--#if __GLASGOW_HASKELL__ < 808-import Control.Monad.Fail (MonadFail(..))-#endif
+ src/Prosidy/Internal/Classes.hs view
@@ -0,0 +1,26 @@+{- |+Module      : Prosidy.Internal.Classes+Description : An internal module exporting common classes.+Copyright   : (c) James Alexander Feldman-Crough, 2019+License     : MPL-2.0+Maintainer  : alex@fldcr.com+-}+{-# LANGUAGE Trustworthy #-}+module Prosidy.Internal.Classes (module X) where++import           Data.Aeson                    as X+                                                ( ToJSON(..)+                                                , FromJSON(..)+                                                , ToJSONKey(..)+                                                , FromJSONKey(..)+                                                )+import           Data.Binary                   as X+                                                ( Binary(..) )+import           GHC.Generics                  as X+                                                ( Generic )+import           Control.DeepSeq               as X+                                                ( NFData(..) )+import           Data.Hashable                 as X+                                                ( Hashable(..) )+import           Data.Text.Prettyprint.Doc     as X+                                                ( Pretty(..) )
+ src/Prosidy/Internal/JSON.hs view
@@ -0,0 +1,154 @@+{- |+Module      : Prosidy.Internal.JSON+Description : Orphan JSON instances to let as many modules be -XSafe as possible.+Copyright   : (c) James Alexander Feldman-Crough, 2019+License     : MPL-2.0+Maintainer  : alex@fldcr.com+-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-}+module Prosidy.Internal.JSON () where++import           Prosidy.Internal.Classes+import           Prosidy.Types+import           Data.Aeson+import qualified Data.HashMap.Strict           as HM+import           Data.Text                      ( Text )+import           Control.Exception              ( displayException )++instance FromJSON Block where+    parseJSON = withObject "block" $ \o -> do+        ty <- o .: "type"+        case ty :: Text of+            "tag" -> do+                subtype <- o .: "subtype"+                case subtype :: Text of+                    "block"   -> BlockTag <$> o .: "value"+                    "literal" -> BlockLiteral <$> o .: "value"+                    _         -> fail $ "unknown tag subtype: " <> show subtype+            "paragraph" -> BlockParagraph <$> o .: "value"+            _           -> fail $ "unknown block type: " <> show ty++instance ToJSON Block where+    toEncoding b = pairs . mconcat $ case b of+        BlockLiteral t ->+            [ "type" .= ("tag" :: Text)+            , "subtype" .= ("literal" :: Text)+            , "value" .= t+            ]+        BlockParagraph p -> ["type" .= ("paragraph" :: Text), "value" .= p]+        BlockTag t ->+            [ "type" .= ("tag" :: Text)+            , "subtype" .= ("block" :: Text)+            , "value" .= t+            ]++    toJSON b = object $ case b of+        BlockLiteral t ->+            [ "type" .= ("tag" :: Text)+            , "subtype" .= ("literal" :: Text)+            , "value" .= t+            ]+        BlockParagraph p -> ["type" .= ("paragraph" :: Text), "value" .= p]+        BlockTag t ->+            [ "type" .= ("tag" :: Text)+            , "subtype" .= ("block" :: Text)+            , "value" .= t+            ]++instance FromJSON Document where+    parseJSON = withObject "Document"+        $ \o -> Document <$> o .: "metadata" <*> o .: "content"++instance ToJSON Document where+    toEncoding (Document md ct) =+        pairs $ mconcat ["metadata" .= md, "content" .= ct]++    toJSON (Document md ct) = object ["metadata" .= md, "content" .= ct]++instance FromJSON Fragment where+    parseJSON = withText "Fragment" $ pure . flip Fragment Nothing++instance ToJSON Fragment where+    toEncoding = toEncoding . fragmentText+    toJSON     = toJSON . fragmentText++instance FromJSON Inline where+    parseJSON = withObject "Inline" $ \o -> do+        ty <- o .: "type"+        case ty :: Text of+            "break" -> pure Break+            "tag"   -> InlineTag <$> o .: "value"+            "text"  -> InlineText <$> o .: "value"+            _       -> fail $ "unknown inline type: " <> show ty++instance ToJSON Inline where+    toEncoding i = pairs . mconcat $ case i of+        Break -> ["type" .= ("break" :: Text), "value" .= Null]+        InlineTag t ->+            [ "type" .= ("tag" :: Text)+            , "subtype" .= ("inline" :: Text)+            , "value" .= t+            ]+        InlineText t -> ["type" .= ("text" :: Text), "value" .= t]++    toJSON i = object $ case i of+        Break -> ["type" .= ("break" :: Text)]+        InlineTag t ->+            [ "type" .= ("tag" :: Text)+            , "subtype" .= ("inline" :: Text)+            , "value" .= t+            ]+        InlineText t -> ["type" .= ("text" :: Text), "value" .= t]++instance FromJSON Metadata where+    parseJSON = withObject "Metadata"+        $ \o -> Metadata <$> o .: "properties" <*> o .: "settings"++instance ToJSON Metadata where+    toEncoding (Metadata ps ss) =+        pairs $ mconcat ["properties" .= ps, "settings" .= ss]++    toJSON (Metadata ps ss) = object ["properties" .= ps, "settings" .= ss]++instance FromJSON Paragraph where+    parseJSON = fmap (flip Paragraph Nothing) . parseJSON++instance ToJSON Paragraph where+    toEncoding (Paragraph s _) = toEncoding s+    toJSON (Paragraph s _) = toJSON s++instance ToJSON a => ToJSON (Region a) where+    toJSON (Region md ct _) = object ["metadata" .= md, "content" .= ct]++instance FromJSON a => FromJSON (Tag a) where+    parseJSON = withObject "Tag" $ \o ->+        Tag+            <$> o+            .:  "name"+            <*> o+            .:  "metadata"+            <*> o+            .:  "content"+            <*> pure Nothing++instance ToJSON a => ToJSON (Tag a) where+    toEncoding (Tag nm md ct _) =+        pairs $ mconcat ["name" .= nm, "metadata" .= md, "content" .= ct]++    toJSON (Tag nm md ct _) =+        object ["name" .= nm, "metadata" .= md, "content" .= ct]++instance FromJSONKey Key where+    fromJSONKey =+        FromJSONKeyTextParser $ either (fail . displayException) pure . makeKey++instance (Hashable a, Eq a, ToJSONKey a) => ToJSON (Set a) where+    toJSON (Set hs) = toJSON $ foldMap (flip HM.singleton True) hs+    toEncoding (Set hs) = toEncoding $ foldMap (flip HM.singleton True) hs++instance (Hashable a, Eq a, FromJSONKey a) => FromJSON (Set a) where+    parseJSON json = do+        m <- parseJSON json+        pure . Set . HM.keysSet $ HM.filter id m
src/Prosidy/Optics.hs view
@@ -5,6 +5,7 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}+{-# LANGUAGE Safe #-} module Prosidy.Optics (module X) where  import           Prosidy.Optics.Source         as X
src/Prosidy/Optics/Internal.hs view
@@ -7,6 +7,7 @@ -} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-}+{-# LANGUAGE Safe #-} module Prosidy.Optics.Internal     ( module Prosidy.Optics.Internal     , Profunctor(..)@@ -26,7 +27,8 @@                                                 ) import           Data.Functor.Identity          ( Identity(..) ) import           Data.Tagged                    ( Tagged(..) )-import           Data.Functor.Contravariant     ( Contravariant(..) )+import           Data.Functor.Contravariant.Compat+                                                ( Contravariant(..) )  type Optic p f s t a b = p a (f b) -> p s (f t) type Iso s t a b = forall p f . (Profunctor p, Functor f) => Optic p f s t a b
src/Prosidy/Optics/Source.hs view
@@ -5,6 +5,7 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}+{-# LANGUAGE Safe #-} module Prosidy.Optics.Source     ( -- * Classy optics; implementable on all types with a location       HasLocation(..)
src/Prosidy/Optics/Types.hs view
@@ -8,6 +8,7 @@ {-# LANGUAGE LambdaCase   #-} {-# LANGUAGE RankNTypes   #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE Safe #-} module Prosidy.Optics.Types     ( -- * Classy optics       -- ** Items with 'Metadata' 
src/Prosidy/Parse.hs view
@@ -5,12 +5,13 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}+{-# LANGUAGE Trustworthy #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE ApplicativeDo     #-} {-# LANGUAGE TypeApplications  #-} {-# LANGUAGE OverloadedStrings #-}-+{-# LANGUAGE NoImplicitPrelude #-} module Prosidy.Parse     ( -- * Parsing Prosidy types from 'Data.Text.Text'       parseDocument@@ -24,7 +25,7 @@     ) where -import           Prosidy.Compat+import           Control.Monad.Fail.Compat      ( MonadFail(..) ) import           Prelude                 hiding ( fail )  import           Prosidy.Types
src/Prosidy/Source.hs view
@@ -5,15 +5,12 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StrictData #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE Safe #-} module Prosidy.Source     ( Source(..)     , Location@@ -37,25 +34,16 @@     ) where -import           Data.Hashable                  ( Hashable(..) )-import           Data.Vector.Unboxed            ( Vector-                                                , MVector-                                                , Unbox-                                                ) import           Data.Text                      ( Text )-import           GHC.Generics                   ( Generic )-import           Control.DeepSeq                ( NFData )-import           Data.Binary                    ( Binary(..) )-import           Data.Aeson                     ( ToJSON(..)-                                                , FromJSON(..)-                                                ) import           Control.Monad                  ( guard )  import qualified Data.Text                     as T-import qualified Data.Vector.Unboxed           as V-import qualified Data.Vector.Generic           as VG-import qualified Data.Vector.Generic.Mutable   as VGM+import qualified Data.Text.Prettyprint.Doc     as PP +import           Prosidy.Internal.Classes+import           Prosidy.Source.LineMap+import           Prosidy.Source.Units+ -- | Information about Prosidy source file. -- -- The 'Show' instance for ths class does not include the 'LineMap' or 'Text'@@ -79,6 +67,9 @@ instance Show Source where     show (Source fp _ _) = "Source " <> show fp +instance Pretty Source where+    pretty = pretty . sourceName+ -- | Create a 'Source' from a descriptive name and a body. The source name is -- typically a 'FilePath', but this is not guarenteed. For instance, when read  -- from standard-input, Prosidy chooses to name the source @\<stdin\>@.@@ -86,7 +77,7 @@ makeSource name body = Source name body lineMap   where     lineMap = case T.foldl' lineMapFold (1, '\0', []) $ body of-        (_, _, acc) -> LineMap . V.fromList . reverse $ acc+        (_, _, acc) -> fromOffsets acc     lineMapFold (ix, prev, acc) ch         | ch == '\n' && prev == '\r' = (succ ix, ch, Offset ix : drop 1 acc)         | ch == '\n' || ch == '\r'   = (succ ix, ch, Offset ix : acc)@@ -132,6 +123,10 @@   deriving stock (Show, Generic, Eq)   deriving anyclass (NFData, Binary, Hashable) +instance Pretty Location where+    pretty loc = pretty (locationSource loc) PP.<+> "@" PP.<+> mconcat+        [pretty (locationLine loc), "×", pretty (locationColumn loc)]+ -- | Add lazily computed line and column number information to a  -- 'SparseLocation'. enrichLocation :: SparseLocation -> Location@@ -154,115 +149,3 @@ stripLocation l = SparseLocation { sparseLocationSource = locationSource l                                  , sparseLocationOffset = locationOffset l                                  }---- | A dense vector containing offsets poiting to the start of each line. That--- is, the starting position of the third line of a file can be found at--- position 2.-newtype LineMap = LineMap (Vector Offset)-  deriving stock (Eq, Generic)-  deriving newtype (Show, NFData)--instance Binary LineMap where-    get = fmap (LineMap . V.fromList) get-    put (LineMap v) = put (V.toList v)--instance Hashable LineMap where-    hashWithSalt salt (LineMap v) = V.foldl' hashWithSalt salt v---- | Convert a 'LineMap' into a list of 'Offset's, corresponding to the first--- character of a line. Note that the initial offset is omitted-- the offset at--- index 0 will be the offset of the /second/ line.-lineOffsets :: LineMap -> [Offset]-lineOffsets (LineMap v) = V.toList v---- | Fetch the 'Offset' for the given 'Line'. Evaluates to 'Nothing' if the--- given 'Line' does not appear in the LineMap-lineToOffset :: Line -> LineMap -> Maybe Offset-lineToOffset (Line 0  ) _            = Just $ Offset 0-lineToOffset (Line nth) (LineMap xs) = xs V.!? fromIntegral (pred nth)---- | Fetch the 'Line' number for a given 'Offset'. Newlines will be attributed--- the line that they terminate, rather than the line started immediately --- afterwards.-offsetToLine :: Offset -> LineMap -> Line-offsetToLine offset (LineMap xs) = Line . fromIntegral $ go Nothing-                                                            0-                                                            (V.length xs)-  where-    go result min max-        | min >= max-        = maybe 0 succ result-        | otherwise-        = let nthIndex  = ((max - min) `div` 2) + min-              nthOffset = xs V.! nthIndex-          in  case nthOffset `compare` offset of-                  EQ -> succ nthIndex-                  LT -> go (Just nthIndex) (nthIndex + 1) max-                  GT -> go result min nthIndex---- | A line number.------ The 'Show' instance for 'Line' counts from one, while the internal--- implementation counts from zero.-newtype Line = Line Word-  deriving stock (Eq, Ord, Generic, Show)-  deriving newtype (ToJSON, FromJSON, Enum)-  deriving anyclass (Hashable, NFData, Binary)---- | A column number.-newtype Column = Column Word-  deriving stock (Eq, Ord, Generic, Show)-  deriving newtype (ToJSON, FromJSON, Enum)-  deriving anyclass (Hashable, NFData, Binary)---- | An offset into a 'Source', counted by UTF-8 codepoint.-newtype Offset = Offset Word-  deriving stock (Eq, Show, Ord, Generic)-  deriving newtype (ToJSON, FromJSON, Enum)-  deriving anyclass (Hashable, NFData, Binary)--newtype instance MVector s Offset = MV_Offset (MVector s Word)--instance VGM.MVector MVector Offset where-    basicLength (MV_Offset m) = VGM.basicLength m-    {-# INLINE basicLength #-}--    basicUnsafeSlice ix len (MV_Offset m) =-        MV_Offset $ VGM.basicUnsafeSlice ix len m-    {-# INLINE basicUnsafeSlice #-}--    basicOverlaps (MV_Offset x) (MV_Offset y) = VGM.basicOverlaps x y-    {-# INLINE basicOverlaps #-}--    basicUnsafeNew len = MV_Offset <$> VGM.basicUnsafeNew len-    {-# INLINE basicUnsafeNew #-}--    basicInitialize (MV_Offset v) = VGM.basicInitialize v-    {-# INLINE basicInitialize #-}--    basicUnsafeRead (MV_Offset v) = fmap Offset <$> VGM.basicUnsafeRead v-    {-# INLINE basicUnsafeRead #-}--    basicUnsafeWrite (MV_Offset v) ix (Offset w) = VGM.basicUnsafeWrite v ix w-    {-# INLINE basicUnsafeWrite #-}--newtype instance Vector Offset = V_Offset (Vector Word)--instance VG.Vector Vector Offset where-    basicUnsafeFreeze (MV_Offset v) = V_Offset <$> VG.basicUnsafeFreeze v-    {-# INLINE basicUnsafeFreeze #-}--    basicUnsafeThaw (V_Offset v) = MV_Offset <$> VG.basicUnsafeThaw v-    {-# INLINE basicUnsafeThaw #-}--    basicLength (V_Offset v) = VG.basicLength v-    {-# INLINE basicLength #-}--    basicUnsafeSlice ix len (V_Offset v) =-        V_Offset $ VG.basicUnsafeSlice ix len v-    {-# INLINE basicUnsafeSlice #-}--    basicUnsafeIndexM (V_Offset v) ix = Offset <$> VG.basicUnsafeIndexM v ix-    {-# INLINE basicUnsafeIndexM #-}--instance Unbox Offset where
+ src/Prosidy/Source/LineMap.hs view
@@ -0,0 +1,130 @@+{- |+Module      : Prosidy.Source.LineMap+Description : Binary-search tree for finding the position of new lines.+Copyright   : (c) James Alexander Feldman-Crough, 2019+License     : MPL-2.0+Maintainer  : alex@fldcr.com+-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE Trustworthy #-}+module Prosidy.Source.LineMap+    ( LineMap+    , lineOffsets+    , lineToOffset+    , offsetToLine+    , fromOffsets+    )+where++import qualified Data.Vector.Unboxed           as V+import qualified Data.Vector.Generic           as VG+import qualified Data.Vector.Generic.Mutable   as VGM+import           Data.Vector.Unboxed            ( Vector+                                                , MVector+                                                , Unbox+                                                )++import           Data.Foldable+import           Data.List                      ( sort )++import           Prosidy.Internal.Classes+import           Prosidy.Source.Units++-- | A dense vector containing offsets poiting to the start of each line. That+-- is, the starting position of the third line of a file can be found at+-- position 2.+newtype LineMap = LineMap (Vector Offset)+  deriving stock (Eq, Generic)+  deriving newtype (Show, NFData)++instance Binary LineMap where+    get = fmap (LineMap . V.fromList) get+    put (LineMap v) = put (V.toList v)++instance Hashable LineMap where+    hashWithSalt salt (LineMap v) = V.foldl' hashWithSalt salt v++fromOffsets :: Foldable f => f Offset -> LineMap+fromOffsets = LineMap . V.fromList . sort . toList++-- | Convert a 'LineMap' into a list of 'Offset's, corresponding to the first+-- character of a line. Note that the initial offset is omitted-- the offset at+-- index 0 will be the offset of the /second/ line.+lineOffsets :: LineMap -> [Offset]+lineOffsets (LineMap v) = V.toList v++-- | Fetch the 'Offset' for the given 'Line'. Evaluates to 'Nothing' if the+-- given 'Line' does not appear in the LineMap+lineToOffset :: Line -> LineMap -> Maybe Offset+lineToOffset (Line 0  ) _            = Just $ Offset 0+lineToOffset (Line nth) (LineMap xs) = xs V.!? fromIntegral (pred nth)++-- | Fetch the 'Line' number for a given 'Offset'. Newlines will be attributed+-- the line that they terminate, rather than the line started immediately +-- afterwards.+offsetToLine :: Offset -> LineMap -> Line+offsetToLine offset (LineMap xs) = Line . fromIntegral $ go Nothing+                                                            0+                                                            (V.length xs)+  where+    go result min max+        | min >= max+        = maybe 0 succ result+        | otherwise+        = let nthIndex  = ((max - min) `div` 2) + min+              nthOffset = xs V.! nthIndex+          in  case nthOffset `compare` offset of+                  EQ -> succ nthIndex+                  LT -> go (Just nthIndex) (nthIndex + 1) max+                  GT -> go result min nthIndex++newtype instance MVector s Offset = MV_Offset (MVector s Word)++instance VGM.MVector MVector Offset where+    basicLength (MV_Offset m) = VGM.basicLength m+    {-# INLINE basicLength #-}++    basicUnsafeSlice ix len (MV_Offset m) =+        MV_Offset $ VGM.basicUnsafeSlice ix len m+    {-# INLINE basicUnsafeSlice #-}++    basicOverlaps (MV_Offset x) (MV_Offset y) = VGM.basicOverlaps x y+    {-# INLINE basicOverlaps #-}++    basicUnsafeNew len = MV_Offset <$> VGM.basicUnsafeNew len+    {-# INLINE basicUnsafeNew #-}++    basicInitialize (MV_Offset v) = VGM.basicInitialize v+    {-# INLINE basicInitialize #-}++    basicUnsafeRead (MV_Offset v) = fmap Offset <$> VGM.basicUnsafeRead v+    {-# INLINE basicUnsafeRead #-}++    basicUnsafeWrite (MV_Offset v) ix (Offset w) = VGM.basicUnsafeWrite v ix w+    {-# INLINE basicUnsafeWrite #-}++newtype instance Vector Offset = V_Offset (Vector Word)++instance VG.Vector Vector Offset where+    basicUnsafeFreeze (MV_Offset v) = V_Offset <$> VG.basicUnsafeFreeze v+    {-# INLINE basicUnsafeFreeze #-}++    basicUnsafeThaw (V_Offset v) = MV_Offset <$> VG.basicUnsafeThaw v+    {-# INLINE basicUnsafeThaw #-}++    basicLength (V_Offset v) = VG.basicLength v+    {-# INLINE basicLength #-}++    basicUnsafeSlice ix len (V_Offset v) =+        V_Offset $ VG.basicUnsafeSlice ix len v+    {-# INLINE basicUnsafeSlice #-}++    basicUnsafeIndexM (V_Offset v) ix = Offset <$> VG.basicUnsafeIndexM v ix+    {-# INLINE basicUnsafeIndexM #-}++instance Unbox Offset where
+ src/Prosidy/Source/Units.hs view
@@ -0,0 +1,46 @@+{- |+Module      : Prosidy.Source.Units+Description : Positional units for marking source-code locations.+Copyright   : (c) James Alexander Feldman-Crough, 2019+License     : MPL-2.0+Maintainer  : alex@fldcr.com+-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Safe #-}+module Prosidy.Source.Units (Line(..), Column(..), Offset(..)) where++import           Prosidy.Internal.Classes++-- | A line number.+--+-- The 'Show' instance for 'Line' counts from one, while the internal+-- implementation counts from zero.+newtype Line = Line Word+  deriving stock (Eq, Ord, Generic, Show)+  deriving anyclass (Hashable, NFData, Binary)+  deriving (ToJSON, FromJSON, Enum) via Word++instance Pretty Line where+    pretty (Line n) = pretty $ succ n++-- | A column number.+newtype Column = Column Word+  deriving stock (Eq, Ord, Generic, Show)+  deriving anyclass (Hashable, NFData, Binary)+  deriving (ToJSON, FromJSON, Enum) via Word++instance Pretty Column where+    pretty (Column n) = pretty $ succ n++-- | An offset into a 'Source', counted by UTF-8 codepoint.+newtype Offset = Offset Word+  deriving stock (Eq, Show, Ord, Generic)+  deriving anyclass (Hashable, NFData, Binary)+  deriving (ToJSON, FromJSON, Enum) via Word++instance Pretty Offset where+    pretty (Offset n) = "+" <> pretty n
src/Prosidy/Types.hs view
@@ -5,15 +5,16 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}+{-# LANGUAGE Safe #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StrictData #-} {-# LANGUAGE TypeApplications #-}@@ -49,6 +50,8 @@     ) where +import           Prosidy.Internal.Classes+ import           Prosidy.Types.Assoc           as X                                                 ( Assoc(..) ) import           Prosidy.Types.Key             as X@@ -67,21 +70,10 @@ import           Prosidy.Source                 ( Location )  import           Data.Text                      ( Text )-import           GHC.Generics                   ( Generic )-import           Control.DeepSeq                ( NFData )-import           Data.Binary                    ( Binary )-import           Data.Hashable                  ( Hashable )-import           Data.Aeson                     ( ToJSON(..)-                                                , FromJSON(..)-                                                , withObject-                                                , withText-                                                , (.:)-                                                , (.=)-                                                , object-                                                , pairs-                                                )--import qualified Data.Aeson                    as Aeson+import           Data.Text.Prettyprint.Doc      ( (<+>) )+import           Data.Foldable                  ( toList )+import           Prosidy.Types.Assoc            ( toEntries )+import qualified Data.Text.Prettyprint.Doc     as PP  ------------------------------------------------------------------------------- -- | A sum type enumerating allowed types inside of a block context.@@ -92,45 +84,10 @@   deriving stock (Eq, Show, Generic)   deriving anyclass (Hashable, Binary, NFData) -instance FromJSON Block where-    parseJSON = withObject "block" $ \o -> do-        ty <- o .: "type"-        case ty :: Text of-            "tag" -> do-                subtype <- o .: "subtype"-                case subtype :: Text of-                    "block"   -> BlockTag <$> o .: "value"-                    "literal" -> BlockLiteral <$> o .: "value"-                    _         -> fail $ "unknown tag subtype: " <> show subtype-            "paragraph" -> BlockParagraph <$> o .: "value"-            _           -> fail $ "unknown block type: " <> show ty--instance ToJSON Block where-    toEncoding b = pairs . mconcat $ case b of-        BlockLiteral t ->-            [ "type" .= ("tag" :: Text)-            , "subtype" .= ("literal" :: Text)-            , "value" .= t-            ]-        BlockParagraph p -> ["type" .= ("paragraph" :: Text), "value" .= p]-        BlockTag t ->-            [ "type" .= ("tag" :: Text)-            , "subtype" .= ("block" :: Text)-            , "value" .= t-            ]--    toJSON b = object $ case b of-        BlockLiteral t ->-            [ "type" .= ("tag" :: Text)-            , "subtype" .= ("literal" :: Text)-            , "value" .= t-            ]-        BlockParagraph p -> ["type" .= ("paragraph" :: Text), "value" .= p]-        BlockTag t ->-            [ "type" .= ("tag" :: Text)-            , "subtype" .= ("block" :: Text)-            , "value" .= t-            ]+instance Pretty Block where+    pretty (BlockLiteral   lit) = pretty lit+    pretty (BlockParagraph pp ) = pretty pp+    pretty (BlockTag       tag) = pretty tag  ------------------------------------------------------------------------------- -- | A full Prosidy document.@@ -141,15 +98,8 @@   deriving stock (Eq, Show, Generic)   deriving anyclass (Hashable, NFData, Binary) -instance FromJSON Document where-    parseJSON = withObject "Document"-        $ \o -> Document <$> o .: "metadata" <*> o .: "content"--instance ToJSON Document where-    toEncoding (Document md ct) =-        pairs $ mconcat ["metadata" .= md, "content" .= ct]--    toJSON (Document md ct) = object ["metadata" .= md, "content" .= ct]+instance Pretty Document where+    pretty (Document md ct) = PP.nest 4 . PP.vsep $ [pretty md, pretty ct]  -- | Convert a 'Document' to a 'Region'. The resulting 'Region' will never have -- a 'Location' attached. @@ -172,12 +122,8 @@   deriving stock (Eq, Show, Generic)   deriving anyclass (Hashable, Binary, NFData) -instance FromJSON Fragment where-    parseJSON = withText "Fragment" $ pure . flip Fragment Nothing--instance ToJSON Fragment where-    toEncoding = toEncoding . fragmentText-    toJSON     = toJSON . fragmentText+instance Pretty Fragment where+    pretty = pretty . fragmentText  ------------------------------------------------------------------------------- -- | A sum type enumerating allowed types inside of an inline context.@@ -196,33 +142,10 @@   deriving stock (Eq, Show, Generic)   deriving anyclass (Hashable, Binary, NFData) -instance FromJSON Inline where-    parseJSON = withObject "Inline" $ \o -> do-        ty <- o .: "type"-        case ty :: Text of-            "break" -> pure Break-            "tag"   -> InlineTag <$> o .: "value"-            "text"  -> InlineText <$> o .: "value"-            _       -> fail $ "unknown inline type: " <> show ty--instance ToJSON Inline where-    toEncoding i = pairs . mconcat $ case i of-        Break -> ["type" .= ("break" :: Text), "value" .= Aeson.Null]-        InlineTag t ->-            [ "type" .= ("tag" :: Text)-            , "subtype" .= ("inline" :: Text)-            , "value" .= t-            ]-        InlineText t -> ["type" .= ("text" :: Text), "value" .= t]--    toJSON i = object $ case i of-        Break -> ["type" .= ("break" :: Text)]-        InlineTag t ->-            [ "type" .= ("tag" :: Text)-            , "subtype" .= ("inline" :: Text)-            , "value" .= t-            ]-        InlineText t -> ["type" .= ("text" :: Text), "value" .= t]+instance Pretty Inline where+    pretty Break            = "\9248"+    pretty (InlineTag  tag) = pretty tag+    pretty (InlineText f  ) = pretty f  ------------------------------------------------------------------------------- -- | A set of properties and settings, associated with a 'Region'. @@ -241,19 +164,21 @@ instance Monoid Metadata where     mempty = Metadata mempty mempty +instance Pretty Metadata where+    pretty (Metadata props sets)+        | null props && null sets+        = "∅"+        | otherwise+        = let props' = fmap pretty . toList $ props+              sets' =+                      fmap (\(k, v) -> pretty k <+> PP.equals <+> pretty v)+                          . toEntries+                          $ sets+          in  PP.list $ props' ++ sets'+ instance Semigroup Metadata where     Metadata p1 s1 <> Metadata p2 s2 = Metadata (p1 <> p2) (s1 <> s2) -instance FromJSON Metadata where-    parseJSON = withObject "Metadata"-        $ \o -> Metadata <$> o .: "properties" <*> o .: "settings"--instance ToJSON Metadata where-    toEncoding (Metadata ps ss) =-        pairs $ mconcat ["properties" .= ps, "settings" .= ss]--    toJSON (Metadata ps ss) = object ["properties" .= ps, "settings" .= ss]- ------------------------------------------------------------------------------- -- | A non-empty collection of 'Inline' items. A 'Paragraph' represents the -- border between block and inline contexts. All ancestors of a paragraph are@@ -265,12 +190,8 @@   deriving stock (Eq, Show, Generic)   deriving anyclass (Hashable, NFData, Binary) -instance FromJSON Paragraph where-    parseJSON = fmap (flip Paragraph Nothing) . parseJSON--instance ToJSON Paragraph where-    toEncoding (Paragraph s _) = toEncoding s-    toJSON (Paragraph s _) = toJSON s+instance Pretty Paragraph where+    pretty pg = "¶" PP.<+> pretty (paragraphContent pg)  ------------------------------------------------------------------------------- -- | An untagged structural grouping of items with type @a@. Regions do not@@ -283,8 +204,8 @@   deriving stock (Eq, Foldable, Functor, Show, Traversable, Generic)   deriving anyclass (Hashable, NFData, Binary) -instance ToJSON a => ToJSON (Region a) where-    toJSON (Region md ct _) = Aeson.object ["metadata" .= md, "content" .= ct]+instance Pretty a => Pretty (Region a) where+    pretty (Region md ct _) = PP.nest 4 $ PP.vsep ["§", pretty md, pretty ct]  ------------------------------------------------------------------------------- -- | A 'Region', annotated with a tag name.@@ -297,23 +218,9 @@   deriving stock (Eq, Foldable, Functor, Show, Traversable, Generic)   deriving anyclass (Hashable, NFData, Binary) -instance FromJSON a => FromJSON (Tag a) where-    parseJSON = withObject "Tag" $ \o ->-        Tag-            <$> o-            .:  "name"-            <*> o-            .:  "metadata"-            <*> o-            .:  "content"-            <*> pure Nothing--instance ToJSON a => ToJSON (Tag a) where-    toEncoding (Tag nm md ct _) =-        pairs $ mconcat ["name" .= nm, "metadata" .= md, "content" .= ct]--    toJSON (Tag nm md ct _) =-        object ["name" .= nm, "metadata" .= md, "content" .= ct]+instance Pretty a => Pretty (Tag a) where+    pretty (Tag name md ct _) =+        PP.nest 4 $ PP.vsep [pretty name, pretty md, pretty ct]  -- | A 'Tag' containing zero or more 'Block' items.  -- Specified in Prosidy source with the @#-@ sigil.
src/Prosidy/Types/Assoc.hs view
@@ -5,21 +5,25 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}-{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Prosidy.Types.Assoc (Assoc(..), asHashMap, fromHashMap, toHashMap) where+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE OverloadedStrings #-}+module Prosidy.Types.Assoc+    ( Assoc(..)+    , asHashMap+    , fromHashMap+    , toHashMap+    , toEntries+    )+where +import           Prosidy.Internal.Classes+ import           Data.HashMap.Strict            ( HashMap )-import           GHC.Generics                   ( Generic )-import           Data.Aeson                     ( ToJSON(..)-                                                , FromJSON(..)-                                                )-import           Control.DeepSeq                ( NFData )-import           Data.Binary                    ( Binary(..) )-import           Data.Hashable                  ( Hashable(..) )  import qualified Data.HashMap.Strict           as HM+import qualified Data.Text.Prettyprint.Doc     as PP  -- | An associative mapping of keys to values. --@@ -28,14 +32,21 @@ -- 1) Add non-orphan instances to the underlying structure. -- 2) Change the underlying type if needed. newtype Assoc k v = Assoc (HashMap k v)-  deriving stock (Generic)-  deriving newtype (Eq, Foldable, Functor, Show, ToJSON, FromJSON, NFData, Semigroup, Monoid, Hashable)+  deriving (Generic)+  deriving (Eq, Show, ToJSON, FromJSON, NFData, Semigroup, Monoid, Hashable) via HashMap k v+  deriving (Foldable, Functor) via HashMap k  instance (Eq k, Hashable k, Binary k, Binary v) => Binary (Assoc k v) where     get = Assoc . HM.fromList <$> get      put (Assoc hm) = put $ HM.toList hm +instance (Pretty k, Pretty v) => Pretty (Assoc k v) where+    pretty (Assoc hm) =+        PP.list+            . map (\(k, v) -> pretty k PP.<+> PP.equals PP.<+> pretty v)+            $ HM.toList hm+ -- | Given a function which operates on a 'HashMap', return a function which -- performs an equivalent transfromation on an 'Assoc'. asHashMap@@ -52,3 +63,7 @@ -- | Convert an 'Assoc' to a 'HashMap'. toHashMap :: Assoc k v -> HashMap k v toHashMap (Assoc hm) = hm++-- | Convert an 'Assoc' into a list of key/value pairs.+toEntries :: Assoc k v -> [(k, v)]+toEntries (Assoc hm) = HM.toList hm
src/Prosidy/Types/Key.hs view
@@ -5,24 +5,10 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE GADTSyntax #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PatternSynonyms #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE StrictData #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE Safe #-} module Prosidy.Types.Key     ( -- * The 'Key' type.       Key@@ -39,16 +25,9 @@     ) where +import           Prosidy.Internal.Classes+ import           Data.Text                      ( Text )-import           Data.Aeson                     ( ToJSON(..)-                                                , ToJSONKey(..)-                                                , FromJSON(..)-                                                , FromJSONKey(..)-                                                )-import           GHC.Generics                   ( Generic )-import           Control.DeepSeq                ( NFData )-import           Data.Binary                    ( Binary )-import           Data.Hashable                  ( Hashable ) import           Data.String                    ( IsString(..) ) import           Data.Foldable                  ( for_ ) import           Control.Monad                  ( unless )@@ -56,15 +35,14 @@                                                 , throw                                                 ) -import qualified Data.Aeson                    as Aeson import qualified Data.Char                     as Char import qualified Data.Set                      as Set import qualified Data.Text                     as Text  -- | A 'Key' is an identifier used in tags, properties, and setting names. newtype Key = Key Text-  deriving stock (Generic)-  deriving newtype (Binary, Eq, Hashable, NFData, Ord, Show, ToJSON, ToJSONKey)+  deriving stock (Show, Generic)+  deriving (Binary, Eq, Hashable, NFData, Ord, ToJSON, ToJSONKey) via Text  -- | 'Key' exposes an 'IsString' instance, but beware! Invalid strings will -- throw a pure exception. @@ -76,11 +54,8 @@         text <- parseJSON json         either (fail . displayException) pure $ makeKey text -instance FromJSONKey Key where-    fromJSONKey =-        Aeson.FromJSONKeyTextParser-            $ either (fail . displayException) pure-            . makeKey+instance Pretty Key where+    pretty = pretty . rawKey  -- | Create a new 'Key', checking its validity. makeKey :: Text -> Either KeyError Key
src/Prosidy/Types/Series.hs view
@@ -5,11 +5,11 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}-{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE Safe #-} module Prosidy.Types.Series     ( -- * Possibly empty collections       Series(..)@@ -30,14 +30,9 @@     ) where +import Prosidy.Internal.Classes+ import           Data.Sequence                  ( Seq )-import           GHC.Generics                   ( Generic )-import           Data.Aeson                     ( ToJSON(..)-                                                , FromJSON(..)-                                                )-import           Control.DeepSeq                ( NFData )-import           Data.Binary                    ( Binary(..) )-import           Data.Hashable                  ( Hashable(..) ) import           Data.Foldable                  ( toList                                                 , foldl'                                                 )@@ -50,8 +45,9 @@ -- Currently, 'Series' is implemented as a 'Seq', but this is not guarenteed to -- be true. newtype Series a = Series (Seq a)-  deriving stock (Generic, Show)-  deriving newtype (Eq, Foldable, Functor, Applicative, ToJSON, FromJSON, NFData, Semigroup, Monoid)+  deriving (Generic, Show)+  deriving (Eq, ToJSON, FromJSON, NFData, Semigroup, Monoid) via Seq a+  deriving (Foldable, Functor, Applicative) via Seq  instance Binary a => Binary (Series a) where     get = Series . Seq.fromList <$> get@@ -63,13 +59,17 @@ instance Hashable a => Hashable (Series a) where     hashWithSalt salt (Series xs) = foldl' hashWithSalt salt xs +instance Pretty a => Pretty (Series a) where+    pretty = pretty . toList+ instance Traversable Series where     traverse f (Series xs) = Series <$> traverse f xs  -- | A non-empty 'Series'. newtype SeriesNE a = SeriesNE (Seq a)-  deriving stock (Generic, Show)-  deriving newtype (Eq, Foldable, Functor, Applicative, ToJSON, NFData, Semigroup)+  deriving (Generic, Show)+  deriving (Eq, ToJSON, NFData, Semigroup) via Seq a+  deriving (Foldable, Functor, Applicative) via Seq  instance Binary a => Binary (SeriesNE a) where     get =@@ -90,6 +90,9 @@  instance Hashable a => Hashable (SeriesNE a) where     hashWithSalt salt (SeriesNE xs) = foldl' hashWithSalt salt xs++instance Pretty a => Pretty (SeriesNE a) where+    pretty = pretty . toList  instance Traversable SeriesNE where     traverse f (SeriesNE xs) = SeriesNE <$> traverse f xs
src/Prosidy/Types/Set.hs view
@@ -5,45 +5,33 @@ License     : MPL-2.0 Maintainer  : alex@fldcr.com -}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DerivingVia #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE Safe #-} module Prosidy.Types.Set (Set(..), asHashSet, fromHashSet, toHashSet) where +import           Prosidy.Internal.Classes+ import           Data.HashSet                   ( HashSet )-import           GHC.Generics                   ( Generic )-import           Data.Aeson                     ( FromJSONKey-                                                , ToJSONKey-                                                , ToJSON(..)-                                                , FromJSON(..)-                                                )-import           Control.DeepSeq                ( NFData )-import           Data.Binary                    ( Binary(..) )-import           Data.Hashable                  ( Hashable(..) ) import qualified Data.HashSet                  as HS-import qualified Data.HashMap.Strict           as HM  -- | A newtype wrapper around an unordered collection of unique elements. -- -- Currently, this is implemented as a wrapper around a 'HashSet'. newtype Set a = Set (HashSet a)   deriving stock (Generic)-  deriving newtype (Eq, Foldable, Show, NFData, Semigroup, Monoid, Hashable)--instance (Hashable a, Eq a, ToJSONKey a) => ToJSON (Set a) where-    toJSON (Set hs) = toJSON $ foldMap (flip HM.singleton True) hs-    toEncoding (Set hs) = toEncoding $ foldMap (flip HM.singleton True) hs--instance (Hashable a, Eq a, FromJSONKey a) => FromJSON (Set a) where-    parseJSON json = do-        m <- parseJSON json-        pure . Set . HM.keysSet $ HM.filter id m+  deriving (Eq, Show, NFData, Semigroup, Monoid, Hashable) via HashSet a+  deriving Foldable via HashSet  instance (Eq a, Hashable a, Binary a) => Binary (Set a) where     get = Set . HS.fromList <$> get      put (Set s) = put $ HS.toList s++instance Pretty a => Pretty (Set a) where+    pretty = pretty . HS.toList . toHashSet  -- | Given a function which operates on 'HashSet's, return a function which -- performs the same operation on a 'Set'.
test/Prosidy/Test.hs view
@@ -1,21 +1,16 @@-import Test.Tasty-import Test.Tasty.Runners.AntXML (antXMLRunner)+import           Test.Tasty+import           Test.Tasty.Runners.AntXML      ( antXMLRunner ) import qualified Prosidy.Test.Source import qualified Prosidy.Test.Types import qualified Prosidy.Test.Parse  main :: IO () main = tests >>= defaultMainWithIngredients ingredients-  where-    ingredients = defaultIngredients ++-        [ antXMLRunner-        ]+    where ingredients = defaultIngredients ++ [antXMLRunner]  tests :: IO TestTree tests = do     parseTests <- Prosidy.Test.Parse.tests-    pure $ testGroup "test"-        [ Prosidy.Test.Source.tests-        , Prosidy.Test.Types.tests-        , parseTests-        ]+    pure $ testGroup+        "test"+        [Prosidy.Test.Source.tests, Prosidy.Test.Types.tests, parseTests]
test/Prosidy/Test/Parse.hs view
@@ -4,21 +4,23 @@  import qualified Prosidy -import Test.Tasty-import Test.Tasty.Golden.Advanced (goldenTest)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BS.Lazy-import qualified System.Directory as Dir-import System.FilePath ((</>))-import Control.Exception (handle, displayException)+import           Test.Tasty+import           Test.Tasty.Golden.Advanced     ( goldenTest )+import qualified Data.ByteString               as BS+import qualified Data.ByteString.Lazy          as BS.Lazy+import qualified System.Directory              as Dir+import           System.FilePath                ( (</>) )+import           Control.Exception              ( handle+                                                , displayException+                                                ) -import qualified Paths_prosidy as Paths-import qualified Data.Aeson as Aeson-import qualified Data.Aeson.Diff as Aeson.Diff-import qualified Data.Aeson.Encode.Pretty as Aeson.Pretty+import qualified Paths_prosidy                 as Paths+import qualified Data.Aeson                    as Aeson+import qualified Data.Aeson.Diff               as Aeson.Diff+import qualified Data.Aeson.Encode.Pretty      as Aeson.Pretty -import qualified Data.Text.Lazy as Text.Lazy-import Data.Text.Lazy.Encoding (decodeUtf8With)+import qualified Data.Text.Lazy                as Text.Lazy+import           Data.Text.Lazy.Encoding        ( decodeUtf8With )  tests :: IO TestTree tests = do@@ -27,38 +29,38 @@     pure . testGroup "parse" $ makeTest goldenDir <$> goldenTests  makeTest :: FilePath -> String -> TestTree-makeTest goldenDir name =-    goldenTest name -        (catchErrors getJSON) -        (catchErrors parsePro)-        compareDocs-        writeGolden+makeTest goldenDir name = goldenTest name+                                     (catchErrors getJSON)+                                     (catchErrors parsePro)+                                     compareDocs+                                     writeGolden   where     getJSON :: IO Aeson.Value     getJSON = do         bytes <- BS.readFile $ goldenDir </> name </> "output.json"         case Aeson.eitherDecode' . BS.Lazy.fromStrict $ bytes of-            Left e   -> fail $ "Failed to parse JSON: " <> e+            Left  e  -> fail $ "Failed to parse JSON: " <> e             Right ok -> pure ok      parsePro :: IO Aeson.Value-    parsePro = Aeson.toJSON <$>-        Prosidy.readDocument (goldenDir </> name </> "input.pro") +    parsePro = Aeson.toJSON+        <$> Prosidy.readDocument (goldenDir </> name </> "input.pro")      compareDocs :: Aeson.Value -> Aeson.Value -> IO (Maybe String)     compareDocs gold test | gold == test = pure Nothing-    compareDocs gold test = -      let-        diff   = Aeson.Diff.diff (Aeson.toJSON gold) (Aeson.toJSON test)-        pretty = Aeson.Pretty.encodePretty diff-      in-        pure . Just . Text.Lazy.unpack $ -            "Golden test failed. The diff is included below:\n" <>-            decodeUtf8With (\_ _ -> Just '\65533') pretty+    compareDocs gold test =+        let diff   = Aeson.Diff.diff (Aeson.toJSON gold) (Aeson.toJSON test)+            pretty = Aeson.Pretty.encodePretty diff+        in  pure+                .  Just+                .  Text.Lazy.unpack+                $  "Golden test failed. The diff is included below:\n"+                <> decodeUtf8With (\_ _ -> Just '\65533') pretty      writeGolden :: Aeson.Value -> IO ()-    writeGolden doc = BS.Lazy.writeFile (goldenDir </> name </> "output.json") $-        Aeson.Pretty.encodePretty doc+    writeGolden doc =+        BS.Lazy.writeFile (goldenDir </> name </> "output.json")+            $ Aeson.Pretty.encodePretty doc  catchErrors :: IO a -> IO a catchErrors = handle $ \(e :: Prosidy.Failure) -> fail $ displayException e
test/Prosidy/Test/Source.hs view
@@ -1,19 +1,22 @@ {-# LANGUAGE OverloadedStrings #-} module Prosidy.Test.Source (tests) where -import Test.Tasty-import Test.Tasty.HUnit-import Test.Tasty.QuickCheck-import Data.Foldable (toList, for_)-import Data.IORef-import System.IO.Unsafe-import Control.Exception (evaluate)+import           Test.Tasty+import           Test.Tasty.HUnit+import           Test.Tasty.QuickCheck+import           Data.Foldable                  ( toList+                                                , for_+                                                )+import           Data.IORef+import           System.IO.Unsafe+import           Control.Exception              ( evaluate ) -import qualified Prosidy.Source as PS-import qualified Data.Text as T+import qualified Prosidy.Source                as PS+import qualified Data.Text                     as T  tests :: TestTree-tests = testGroup "source"+tests = testGroup+    "source"     [ testEmptyLineMap     , testSimpleLineMap     , testLineDelimiters@@ -24,9 +27,9 @@  testEmptyLineMap :: TestTree testEmptyLineMap = testCase "empty" $ do-    let source  = PS.makeSource "<test>" ""-    assertBool "An empty source generates an empty line map." $-        null (PS.lineOffsets $ PS.sourceLineMap source)+    let source = PS.makeSource "<test>" ""+    assertBool "An empty source generates an empty line map."+        $ null (PS.lineOffsets $ PS.sourceLineMap source)  testSimpleLineMap :: TestTree testSimpleLineMap = testCase "simple" $ do@@ -34,14 +37,14 @@             [ "This source file contains a few lines."             , "Some, line the next, are empty."             , ""-            , "Multiple consecutive empty lines are fine as well." +            , "Multiple consecutive empty lines are fine as well."             , ""             , ""             , "終わり"             ]         source = PS.makeSource "<test>" $ T.unlines sourceLines-    toList (PS.lineOffsets $ PS.sourceLineMap source) @?= -        fmap PS.Offset [39, 71, 72, 123, 124, 125, 129]+    toList (PS.lineOffsets $ PS.sourceLineMap source)+        @?= fmap PS.Offset [39, 71, 72, 123, 124, 125, 129]     for_ (zip [PS.Line 0 ..] sourceLines) $ \(lineNumber, line) ->         PS.getSourceLine lineNumber source @?= Just (line <> "\n") @@ -50,13 +53,13 @@     let source = PS.makeSource "<test>" "abc\ndef\rghi\r\njkl"         lines  = PS.sourceLineMap source     PS.getSourceLine (PS.Line 0) source @?= Just "abc\n"-    PS.lineToOffset  (PS.Line 0) lines  @?= Just (PS.Offset 0)+    PS.lineToOffset (PS.Line 0) lines @?= Just (PS.Offset 0)     PS.getSourceLine (PS.Line 1) source @?= Just "def\r"-    PS.lineToOffset  (PS.Line 1) lines  @?= Just (PS.Offset 4)+    PS.lineToOffset (PS.Line 1) lines @?= Just (PS.Offset 4)     PS.getSourceLine (PS.Line 2) source @?= Just "ghi\r\n"-    PS.lineToOffset  (PS.Line 2) lines  @?= Just (PS.Offset 8)+    PS.lineToOffset (PS.Line 2) lines @?= Just (PS.Offset 8)     PS.getSourceLine (PS.Line 3) source @?= Just "jkl"-    PS.lineToOffset  (PS.Line 3) lines  @?= Just (PS.Offset 13)+    PS.lineToOffset (PS.Line 3) lines @?= Just (PS.Offset 13)     PS.getSourceLine (PS.Line 4) source @?= Nothing  testLocation :: TestTree@@ -82,39 +85,38 @@     let source   = PS.makeSource "<test>" "abc\ndef"         Just loc = PS.getLocation (PS.Offset 5) source     (line, checkLine) <- checkEvaluated (PS.locationLine loc)-    (col, checkCol) <- checkEvaluated (PS.locationColumn loc)+    (col , checkCol ) <- checkEvaluated (PS.locationColumn loc)     assertBool "line is unevaluated" . not =<< checkLine-    assertBool "col is unevaluated"  . not =<< checkCol+    assertBool "col is unevaluated" . not =<< checkCol     _ <- evaluate line-    assertBool "line is evaluated"         =<< checkLine-    assertBool "col is unevaluated"  . not =<< checkCol+    assertBool "line is evaluated" =<< checkLine+    assertBool "col is unevaluated" . not =<< checkCol     _ <- evaluate col     assertBool "line is evaluated" =<< checkLine-    assertBool "col is evaluated"  =<< checkCol+    assertBool "col is evaluated" =<< checkCol  propLineOffset :: TestTree-propLineOffset = testProperty "line-and-offset" $ -    forAll gen $ \(source, initialOffset) ->+propLineOffset =+    testProperty "line-and-offset" $ forAll gen $ \(source, initialOffset) ->         let lineMap     = PS.sourceLineMap source             line        = PS.offsetToLine initialOffset lineMap             Just offset = PS.lineToOffset line lineMap             line'       = PS.offsetToLine offset lineMap-        in (initialOffset >= offset) .&&. (line === line')+        in  (initialOffset >= offset) .&&. (line === line')   where     gen = do         text          <- T.pack <$> genChar-        initialOffset <- elements [PS.Offset 0 .. toEnum (if T.null text then 0 else T.length text - 1)]+        initialOffset <-+            elements+                [PS.Offset 0 .. toEnum+                    (if T.null text then 0 else T.length text - 1)]         pure (PS.makeSource "<text>" text, initialOffset)-    genChar = listOf $ frequency-        [ (10, elements ['a' .. 'z'] )-        , (4, pure ' ')-        , (1, pure '\n')-        ]+    genChar =+        listOf $ frequency+            [(10, elements ['a' .. 'z']), (4, pure ' '), (1, pure '\n')]  {-# NOINLINE checkEvaluated #-} checkEvaluated :: a -> IO (a, IO Bool) checkEvaluated val = do     ref <- newIORef False-    pure ( unsafePerformIO (writeIORef ref True) `seq` val-         , readIORef ref-         )+    pure (unsafePerformIO (writeIORef ref True) `seq` val, readIORef ref)
test/Prosidy/Test/Types.hs view
@@ -1,30 +1,29 @@ {-# LANGUAGE OverloadedStrings #-} module Prosidy.Test.Types (tests) where -import Test.Tasty-import Test.Tasty.QuickCheck+import           Test.Tasty+import           Test.Tasty.QuickCheck -import qualified Prosidy.Types.Key as Key-import qualified Prosidy.Types.Series as Series-import qualified Data.Text as Text-import qualified Data.Sequence as Seq+import qualified Prosidy.Types.Key             as Key+import qualified Prosidy.Types.Series          as Series+import qualified Data.Text                     as Text+import qualified Data.Sequence                 as Seq  tests :: TestTree-tests = testGroup "types"-    [ testKey-    , testSeriesNE-    ]+tests = testGroup "types" [testKey, testSeriesNE]  testKey :: TestTree-testKey = testGroup "key" +testKey = testGroup+    "key"     [ testProperty "valid" $ forAll validGen $ \raw ->         fmap Key.rawKey (Key.makeKey raw) === Right raw     , testProperty "invalid-head" $ forAll invalidHeadGen $ \raw ->         case Key.makeKey raw of             Right _ -> label "key creation should fail" False-            Left Key.EmptyKeyError -> label "key should be nonempty" $ property False-            Left (Key.InvalidCharacterError (Key.InvalidCharacter raw' nth ch)) ->-                conjoin+            Left Key.EmptyKeyError ->+                label "key should be nonempty" $ property False+            Left (Key.InvalidCharacterError (Key.InvalidCharacter raw' nth ch))+                -> conjoin                     [ label "error string matches input" $ raw === raw'                     , label "invalid at index 0" $ nth === 0                     , label "char is invalid" $ not (Key.isValidKeyHead ch)@@ -32,22 +31,23 @@     , testProperty "invalid-tail" $ forAll invalidTailGen $ \raw ->         case Key.makeKey raw of             Right _ -> label "key creation should fail" False-            Left Key.EmptyKeyError -> label "key should be nonempty" $ property False-            Left (Key.InvalidCharacterError (Key.InvalidCharacter raw' nth ch)) ->-                conjoin+            Left Key.EmptyKeyError ->+                label "key should be nonempty" $ property False+            Left (Key.InvalidCharacterError (Key.InvalidCharacter raw' nth ch))+                -> conjoin                     [ label "error string matches input" $ raw === raw'                     , label "invalid at index > 0" $ nth =/= 0                     , label "char is invalid" $ not (Key.isValidKeyTail ch)                     ]-    , testProperty "empty" $-        case Key.makeKey "" of-            Right _ -> label "key creation should fail" False-            Left Key.EmptyKeyError -> property ()-            Left (Key.InvalidCharacterError _) -> label "input should be empty" False+    , testProperty "empty" $ case Key.makeKey "" of+        Right _                 -> label "key creation should fail" False+        Left  Key.EmptyKeyError -> property ()+        Left (Key.InvalidCharacterError _) ->+            label "input should be empty" False     ]   where-    arbHead = arbitraryUnicodeChar `suchThat` Key.isValidKeyHead-    arbTail = listOf $ arbitraryUnicodeChar `suchThat` Key.isValidKeyTail+    arbHead  = arbitraryUnicodeChar `suchThat` Key.isValidKeyHead+    arbTail  = listOf $ arbitraryUnicodeChar `suchThat` Key.isValidKeyTail      validGen = do         keyHead <- arbHead@@ -67,12 +67,12 @@         pure . Text.pack $ keyHead : keyTail0 ++ keyTail1 : keyTail2  testSeriesNE :: TestTree-testSeriesNE = testGroup "SeriesNE"-    [ testProperty "nonempty" $-        forAll genSeq1 $ \xs ->-            fmap Series.toSeqNE (Series.fromSeqNE xs) === Just xs-    , testProperty "empty" $-        Series.fromSeqNE (mempty :: Seq.Seq Word) === Nothing+testSeriesNE = testGroup+    "SeriesNE"+    [ testProperty "nonempty" $ forAll genSeq1 $ \xs ->+        fmap Series.toSeqNE (Series.fromSeqNE xs) === Just xs+    , testProperty "empty"+    $   Series.fromSeqNE (mempty :: Seq.Seq Word)+    === Nothing     ]-  where-    genSeq1 = fmap Seq.fromList . listOf1 $ (arbitrary :: Gen Word)+    where genSeq1 = fmap Seq.fromList . listOf1 $ (arbitrary :: Gen Word)