diff --git a/bin/Main.hs b/bin/Main.hs index 4564d36..4da0a3a 100644 --- a/bin/Main.hs +++ b/bin/Main.hs @@ -17,6 +17,7 @@ import Codec.CBOR.Term (encodeTerm) import Codec.CBOR.Write (toStrictByteString) import Data.ByteString.Base16 qualified as Base16 import Data.ByteString.Char8 qualified as BSC +import Data.Functor (($>)) import Data.Text qualified as T import Data.Text.IO qualified as T import Options.Applicative @@ -195,7 +196,7 @@ run (Opts cmd cddlFile) = do | vNoPrelude vOpts = res | otherwise = prependPrelude res in - case fullResolveCDDL res' of + case fullResolveCDDL (res' $> ()) of Left err -> putStrLnErr (show err) >> exitFailure Right _ -> exitSuccess (GenerateCBOR gOpts) -> @@ -204,7 +205,7 @@ run (Opts cmd cddlFile) = do | gNoPrelude gOpts = res | otherwise = prependPrelude res in - case fullResolveCDDL res' of + case fullResolveCDDL (res' $> ()) of Left err -> putStrLnErr (show err) >> exitFailure Right mt -> do stdGen <- getStdGen @@ -222,7 +223,7 @@ run (Opts cmd cddlFile) = do | vcNoPrelude vcOpts = res | otherwise = prependPrelude res in - case fullResolveCDDL res' of + case fullResolveCDDL (res' $> ()) of Left err -> putStrLnErr (show err) >> exitFailure Right mt -> do cbor <- BSC.readFile (vcInput vcOpts) diff --git a/cuddle.cabal b/cuddle.cabal index d3479d4..761d024 100644 --- a/cuddle.cabal +++ b/cuddle.cabal @@ -55,7 +55,6 @@ library Codec.CBOR.Cuddle.Comments Codec.CBOR.Cuddle.Huddle Codec.CBOR.Cuddle.Huddle.HuddleM - Codec.CBOR.Cuddle.Huddle.Optics Codec.CBOR.Cuddle.Parser Codec.CBOR.Cuddle.Parser.Lexer Codec.CBOR.Cuddle.Pretty diff --git a/flake.lock b/flake.lock index cbf5a37..b3e521d 100644 --- a/flake.lock +++ b/flake.lock @@ -6,11 +6,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1745303921, - "narHash": "sha256-zYucemS2QvJUR5GKJ/u3eZAoe82AKhcxMtNVZDERXsw=", + "lastModified": 1757659051, + "narHash": "sha256-pQfaow3cp1YJtV0JZiz8jC4Y8VQjT4CYZ3OAfFe41Zs=", "owner": "nix-community", "repo": "fenix", - "rev": "14850d5984f3696a2972f85f19085e5fb46daa95", + "rev": "9479f6dd16e83add0ef0186662d65e7763b37ebe", "type": "github" }, "original": { @@ -55,11 +55,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1744932701, - "narHash": "sha256-fusHbZCyv126cyArUwwKrLdCkgVAIaa/fQJYFlCEqiU=", + "lastModified": 1757487488, + "narHash": "sha256-zwE/e7CuPJUWKdvvTCB7iunV4E/+G0lKfv4kk/5Izdg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b024ced1aac25639f8ca8fdfc2f8c4fbd66c48ef", + "rev": "ab0f3607a6c7486ea22229b92ed2d355f1482ee0", "type": "github" }, "original": { @@ -71,11 +71,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1744932701, - "narHash": "sha256-fusHbZCyv126cyArUwwKrLdCkgVAIaa/fQJYFlCEqiU=", + "lastModified": 1757487488, + "narHash": "sha256-zwE/e7CuPJUWKdvvTCB7iunV4E/+G0lKfv4kk/5Izdg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b024ced1aac25639f8ca8fdfc2f8c4fbd66c48ef", + "rev": "ab0f3607a6c7486ea22229b92ed2d355f1482ee0", "type": "github" }, "original": { @@ -106,11 +106,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1732527420, - "narHash": "sha256-CODu6b7XDqUr09KCWgG++JtFpy6H2aHVfnKi1SPV/NA=", + "lastModified": 1755004808, + "narHash": "sha256-ivs3qgkRULIF925fJTEJfH85B4f+tl5e2gSrVJH58MU=", "owner": "nickel-lang", "repo": "organist", - "rev": "16afff2ab58d1d72d0bbca6cf37e0e3a541f5b6e", + "rev": "a7e4e638cade5e7c4f36a129b80d91bf3538088e", "type": "github" }, "original": { @@ -129,11 +129,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1745247864, - "narHash": "sha256-QA1Ba8Flz5K+0GbG03HwiX9t46mh/jjKgwavbuKtwMg=", + "lastModified": 1757362324, + "narHash": "sha256-/PAhxheUq4WBrW5i/JHzcCqK5fGWwLKdH6/Lu1tyS18=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "31dbec70c68e97060916d4754c687a3e93c2440f", + "rev": "9edc9cbe5d8e832b5864e09854fa94861697d2fd", "type": "github" }, "original": { diff --git a/nickel.lock.ncl b/nickel.lock.ncl index 813d7fa..e7bf77d 100644 --- a/nickel.lock.ncl +++ b/nickel.lock.ncl @@ -1,3 +1,3 @@ { - organist = import "/nix/store/7zrf2b1ysrgrx7613qlmbz71cfyxgyfb-source/lib/organist.ncl", + organist = import "/nix/store/fjxrgrx0s69m5vkss5ff1i5akjcx39ss-source/lib/organist.ncl", } diff --git a/project.ncl b/project.ncl index 0e16988..7abf7a1 100644 --- a/project.ncl +++ b/project.ncl @@ -1,14 +1,15 @@ let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in -let import_hs = fun ghcver pkgname => - organist.import_nix "nixpkgs#haskell.packages.%{ghcver}.%{pkgname}" +let + import_hs = fun ghcver pkgname => + organist.import_nix "nixpkgs#haskell.packages.%{ghcver}.%{pkgname}" in let shellFor = fun ghcver => let hspkg = import_hs ghcver in { packages = { haskell-language-server = hspkg "haskell-language-server", - fourmolu = hspkg "fourmolu", + fourmolu = organist.import_nix "nixpkgs#haskell.packages.ghc912.fourmolu", ghc = organist.import_nix "nixpkgs#haskell.compiler.%{ghcver}", cabal-install = hspkg "cabal-install", cabal-fmt = hspkg "cabal-fmt", @@ -24,7 +25,7 @@ let shellFor = fun ghcver => packages = {}, }, - shells.dev = shellFor "ghc964", + shells.dev = shellFor "ghc910", } } | organist.OrganistExpression diff --git a/src/Codec/CBOR/Cuddle/CBOR/Gen.hs b/src/Codec/CBOR/Cuddle/CBOR/Gen.hs index c00b0db..6f17a46 100644 --- a/src/Codec/CBOR/Cuddle/CBOR/Gen.hs +++ b/src/Codec/CBOR/Cuddle/CBOR/Gen.hs @@ -24,6 +24,13 @@ import Codec.CBOR.Cuddle.CDDL ( OccurrenceIndicator (..), Value (..), ValueVariant (..), + WrappedTerm, + flattenWrappedList, + pairTermList, + singleTermList, + pattern G, + pattern P, + pattern S, ) import Codec.CBOR.Cuddle.CDDL.CTree (CTree, CTreeRoot' (..)) import Codec.CBOR.Cuddle.CDDL.CTree qualified as CTree @@ -207,48 +214,6 @@ genPostlude pt = case pt of PTNil -> pure TNull PTUndefined -> pure $ TSimple 23 --------------------------------------------------------------------------------- --- Kinds of terms --------------------------------------------------------------------------------- - -data WrappedTerm - = SingleTerm Term - | PairTerm Term Term - | GroupTerm [WrappedTerm] - deriving (Eq, Show) - --- | Recursively flatten wrapped list. That is, expand any groups out to their --- individual entries. -flattenWrappedList :: [WrappedTerm] -> [WrappedTerm] -flattenWrappedList [] = [] -flattenWrappedList (GroupTerm xxs : xs) = - flattenWrappedList xxs <> flattenWrappedList xs -flattenWrappedList (y : xs) = y : flattenWrappedList xs - -pattern S :: Term -> WrappedTerm -pattern S t = SingleTerm t - --- | Convert a list of wrapped terms to a list of terms. If any 'PairTerm's are --- present, we just take their "value" part. -singleTermList :: [WrappedTerm] -> Maybe [Term] -singleTermList [] = Just [] -singleTermList (S x : xs) = (x :) <$> singleTermList xs -singleTermList (P _ y : xs) = (y :) <$> singleTermList xs -singleTermList _ = Nothing - -pattern P :: Term -> Term -> WrappedTerm -pattern P t1 t2 = PairTerm t1 t2 - --- | Convert a list of wrapped terms to a list of pairs of terms, or fail if any --- 'SingleTerm's are present. -pairTermList :: [WrappedTerm] -> Maybe [(Term, Term)] -pairTermList [] = Just [] -pairTermList (P x y : xs) = ((x, y) :) <$> pairTermList xs -pairTermList _ = Nothing - -pattern G :: [WrappedTerm] -> WrappedTerm -pattern G xs = GroupTerm xs - -------------------------------------------------------------------------------- -- Generator functions -------------------------------------------------------------------------------- @@ -420,7 +385,7 @@ applyOccurenceIndicator (OIBounded mlb mub) oldGen = genDepthBiasedRM (fromMaybe 0 mlb :: Word64, fromMaybe 10 mub) >>= \i -> G <$> replicateM (fromIntegral i) oldGen -genValue :: RandomGen g => Value -> M g Term +genValue :: RandomGen g => Value () -> M g Term genValue (Value x _) = genValueVariant x genValueVariant :: RandomGen g => ValueVariant -> M g Term diff --git a/src/Codec/CBOR/Cuddle/CBOR/Validator.hs b/src/Codec/CBOR/Cuddle/CBOR/Validator.hs index f80884a..d5b0b8b 100644 --- a/src/Codec/CBOR/Cuddle/CBOR/Validator.hs +++ b/src/Codec/CBOR/Cuddle/CBOR/Validator.hs @@ -145,7 +145,9 @@ validateCBOR' bs rule cddl@(CTreeRoot tree) = -- spec validateTerm :: MonadReader CDDL m => - Term -> Rule -> m CBORTermResult + Term -> + Rule -> + m CBORTermResult validateTerm term rule = let f = case term of TInt i -> validateInteger (fromIntegral i) @@ -183,7 +185,9 @@ validateTerm term rule = -- Ints, so we convert everything to Integer. validateInteger :: MonadReader CDDL m => - Integer -> Rule -> m CDDLResult + Integer -> + Rule -> + m CDDLResult validateInteger i rule = ($ rule) <$> do getRule rule >>= \case @@ -308,7 +312,9 @@ controlInteger i Ne ctrl = -- | Validating a `Float16` validateHalf :: MonadReader CDDL m => - Float -> Rule -> m CDDLResult + Float -> + Rule -> + m CDDLResult validateHalf f rule = ($ rule) <$> do getRule rule >>= \case @@ -343,7 +349,9 @@ controlHalf f Ne ctrl = -- | Validating a `Float32` validateFloat :: MonadReader CDDL m => - Float -> Rule -> m CDDLResult + Float -> + Rule -> + m CDDLResult validateFloat f rule = ($ rule) <$> do getRule rule >>= \case @@ -383,7 +391,9 @@ controlFloat f Ne ctrl = -- | Validating a `Float64` validateDouble :: MonadReader CDDL m => - Double -> Rule -> m CDDLResult + Double -> + Rule -> + m CDDLResult validateDouble f rule = ($ rule) <$> do getRule rule >>= \case @@ -430,7 +440,9 @@ controlDouble f Ne ctrl = -- | Validating a boolean validateBool :: MonadReader CDDL m => - Bool -> Rule -> m CDDLResult + Bool -> + Rule -> + m CDDLResult validateBool b rule = ($ rule) <$> do getRule rule >>= \case @@ -463,7 +475,9 @@ controlBool b Ne ctrl = -- | Validating a `TSimple`. It is unclear if this is used for anything else than undefined. validateSimple :: MonadReader CDDL m => - Word8 -> Rule -> m CDDLResult + Word8 -> + Rule -> + m CDDLResult validateSimple 23 rule = ($ rule) <$> do getRule rule >>= \case @@ -498,7 +512,9 @@ validateNull rule = -- | Validating a byte sequence validateBytes :: MonadReader CDDL m => - BS.ByteString -> Rule -> m CDDLResult + BS.ByteString -> + Rule -> + m CDDLResult validateBytes bs rule = ($ rule) <$> do getRule rule >>= \case @@ -517,7 +533,11 @@ validateBytes bs rule = -- | Controls for byte strings controlBytes :: forall m. - MonadReader CDDL m => BS.ByteString -> CtlOp -> Rule -> m (Either (Maybe CBORTermResult) ()) + MonadReader CDDL m => + BS.ByteString -> + CtlOp -> + Rule -> + m (Either (Maybe CBORTermResult) ()) controlBytes bs Size ctrl = getRule ctrl >>= \case Literal (Value (VUInt (fromIntegral -> sz)) _) -> pure $ boolCtrl $ BS.length bs == sz @@ -568,7 +588,9 @@ controlBytes bs Cborseq ctrl = -- | Validating text strings validateText :: MonadReader CDDL m => - T.Text -> Rule -> m CDDLResult + T.Text -> + Rule -> + m CDDLResult validateText txt rule = ($ rule) <$> do getRule rule >>= \case @@ -607,7 +629,10 @@ controlText s Regexp ctrl = -- | Validating a `TTagged` validateTagged :: MonadReader CDDL m => - Word64 -> Term -> Rule -> m CDDLResult + Word64 -> + Term -> + Rule -> + m CDDLResult validateTagged tag term rule = ($ rule) <$> do getRule rule >>= \case @@ -725,7 +750,9 @@ isOptional rule = validateListWithExpandedRules :: forall m. MonadReader CDDL m => - [Term] -> [Rule] -> m [(Rule, CBORTermResult)] + [Term] -> + [Rule] -> + m [(Rule, CBORTermResult)] validateListWithExpandedRules terms rules = go (zip terms rules) where @@ -795,7 +822,9 @@ validateList terms rule = validateMapWithExpandedRules :: forall m. MonadReader CDDL m => - [(Term, Term)] -> [Rule] -> m ([AMatchedItem], Maybe ANonMatchedItem) + [(Term, Term)] -> + [Rule] -> + m ([AMatchedItem], Maybe ANonMatchedItem) validateMapWithExpandedRules = go where @@ -853,7 +882,9 @@ validateExpandedMap terms rules = go rules validateMap :: MonadReader CDDL m => - [(Term, Term)] -> Rule -> m CDDLResult + [(Term, Term)] -> + Rule -> + m CDDLResult validateMap terms rule = ($ rule) <$> do getRule rule >>= \case @@ -899,7 +930,10 @@ dummyRule = MRuleRef (Name "dummy" mempty) -- | Validate both rules ctrlAnd :: Monad m => - (Rule -> m CDDLResult) -> Rule -> Rule -> m (Rule -> CDDLResult) + (Rule -> m CDDLResult) -> + Rule -> + Rule -> + m (Rule -> CDDLResult) ctrlAnd v tgt ctrl = v tgt >>= \case Valid _ -> diff --git a/src/Codec/CBOR/Cuddle/CDDL.hs b/src/Codec/CBOR/Cuddle/CDDL.hs index 173dc23..ea734d5 100644 --- a/src/Codec/CBOR/Cuddle/CDDL.hs +++ b/src/Codec/CBOR/Cuddle/CDDL.hs @@ -1,5 +1,10 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} -- | This module defined the data structure of CDDL as specified in -- https://datatracker.ietf.org/doc/rfc8610/ @@ -33,10 +38,19 @@ module Codec.CBOR.Cuddle.CDDL ( GrpChoice (..), unwrap, compareRuleName, + flattenWrappedList, + singleTermList, + pairTermList, + CBORGenerator (..), + WrappedTerm (..), + pattern G, + pattern P, + pattern S, ) where import Codec.CBOR.Cuddle.CDDL.CtlOp (CtlOp) import Codec.CBOR.Cuddle.Comments (CollectComments (..), Comment, HasComment (..)) +import Codec.CBOR.Term (Term) import Data.ByteString qualified as B import Data.Default.Class (Default (..)) import Data.Function (on, (&)) @@ -51,48 +65,106 @@ import GHC.Generics (Generic) import Optics.Core ((%), (.~)) import Optics.Getter (view) import Optics.Lens (lens) +import System.Random.Stateful (StatefulGen) + +-------------------------------------------------------------------------------- +-- Kinds of terms +-------------------------------------------------------------------------------- + +data WrappedTerm + = SingleTerm Term + | PairTerm Term Term + | GroupTerm [WrappedTerm] + deriving (Eq, Show) + +-- | Recursively flatten wrapped list. That is, expand any groups out to their +-- individual entries. +flattenWrappedList :: [WrappedTerm] -> [WrappedTerm] +flattenWrappedList [] = [] +flattenWrappedList (GroupTerm xxs : xs) = + flattenWrappedList xxs <> flattenWrappedList xs +flattenWrappedList (y : xs) = y : flattenWrappedList xs + +pattern S :: Term -> WrappedTerm +pattern S t = SingleTerm t + +-- | Convert a list of wrapped terms to a list of terms. If any 'PairTerm's are +-- present, we just take their "value" part. +singleTermList :: [WrappedTerm] -> Maybe [Term] +singleTermList [] = Just [] +singleTermList (S x : xs) = (x :) <$> singleTermList xs +singleTermList (P _ y : xs) = (y :) <$> singleTermList xs +singleTermList _ = Nothing + +pattern P :: Term -> Term -> WrappedTerm +pattern P t1 t2 = PairTerm t1 t2 + +-- | Convert a list of wrapped terms to a list of pairs of terms, or fail if any +-- 'SingleTerm's are present. +pairTermList :: [WrappedTerm] -> Maybe [(Term, Term)] +pairTermList [] = Just [] +pairTermList (P x y : xs) = ((x, y) :) <$> pairTermList xs +pairTermList _ = Nothing + +pattern G :: [WrappedTerm] -> WrappedTerm +pattern G xs = GroupTerm xs + +newtype CBORGenerator + = CBORGenerator (forall g m. StatefulGen g m => g -> m WrappedTerm) -- | The CDDL constructor takes three arguments: -- 1. Top level comments that precede the first definition -- 2. The root definition -- 3. All the other top level comments and definitions -- This ensures that `CDDL` is correct by construction. -data CDDL = CDDL [Comment] Rule [TopLevel] - deriving (Eq, Generic, Show, ToExpr) +data CDDL i = CDDL [i] (Rule i) [TopLevel i] + deriving (Generic, Functor) + +deriving instance Eq i => Eq (CDDL i) + +deriving instance Show i => Show (CDDL i) + +deriving instance ToExpr i => ToExpr (CDDL i) -- | Sort the CDDL Rules on the basis of their names -- Top level comments will be removed! -sortCDDL :: CDDL -> CDDL +sortCDDL :: CDDL i -> CDDL i sortCDDL = fromRules . NE.sortBy (compare `on` ruleName) . cddlRules -cddlTopLevel :: CDDL -> NonEmpty TopLevel +cddlTopLevel :: CDDL i -> NonEmpty (TopLevel i) cddlTopLevel (CDDL cmts cHead cTail) = prependList (TopLevelComment <$> cmts) $ TopLevelRule cHead :| cTail where prependList [] l = l prependList (x : xs) (y :| ys) = prependList xs $ x :| (y : ys) -cddlRules :: CDDL -> NonEmpty Rule +cddlRules :: CDDL i -> NonEmpty (Rule i) cddlRules (CDDL _ x tls) = x :| concatMap getRule tls where getRule (TopLevelRule r) = [r] getRule _ = mempty -fromRules :: NonEmpty Rule -> CDDL +fromRules :: NonEmpty (Rule i) -> CDDL i fromRules (x :| xs) = CDDL [] x $ TopLevelRule <$> xs -fromRule :: Rule -> CDDL +fromRule :: Rule i -> CDDL i fromRule x = CDDL [] x [] -instance Semigroup CDDL where +instance Semigroup (CDDL i) where CDDL aComments aHead aTail <> CDDL bComments bHead bTail = CDDL aComments aHead $ aTail <> fmap TopLevelComment bComments <> (TopLevelRule bHead : bTail) -data TopLevel - = TopLevelRule Rule - | TopLevelComment Comment - deriving (Eq, Generic, Show, ToExpr) +data TopLevel i + = TopLevelRule (Rule i) + | TopLevelComment i + deriving (Generic, Functor) + +deriving instance Eq i => Eq (TopLevel i) + +deriving instance Show i => Show (TopLevel i) + +deriving instance ToExpr i => ToExpr (TopLevel i) -- | -- A name can consist of any of the characters from the set {"A" to @@ -173,12 +245,17 @@ newtype GenericParam = GenericParam (NE.NonEmpty Name) deriving newtype (Semigroup) deriving anyclass (ToExpr) -newtype GenericArg = GenericArg (NE.NonEmpty Type1) - deriving (Eq, Generic, Show) +newtype GenericArg i = GenericArg (NE.NonEmpty (Type1 i)) + deriving (Generic, Functor) deriving newtype (Semigroup) - deriving anyclass (ToExpr) -instance CollectComments GenericArg +deriving instance Eq i => Eq (GenericArg i) + +deriving instance Show i => Show (GenericArg i) + +deriving anyclass instance ToExpr i => ToExpr (GenericArg i) + +instance CollectComments (GenericArg Comment) -- | -- rule = typename [genericparm] S assignt S type @@ -203,20 +280,25 @@ instance CollectComments GenericArg -- clear immediately either whether "b" stands for a group or a type -- -- this semantic processing may need to span several levels of rule -- definitions before a determination can be made.) -data Rule = Rule +data Rule i = Rule { ruleName :: Name , ruleGenParam :: Maybe GenericParam , ruleAssign :: Assign - , ruleTerm :: TypeOrGroup - , ruleComment :: Comment + , ruleTerm :: TypeOrGroup i + , ruleDecoration :: i } - deriving (Eq, Generic, Show) - deriving anyclass (ToExpr) + deriving (Generic, Functor) + +deriving instance Eq i => Eq (Rule i) + +deriving instance Show i => Show (Rule i) -instance HasComment Rule where - commentL = lens ruleComment (\x y -> x {ruleComment = y}) +deriving instance ToExpr i => ToExpr (Rule i) -compareRuleName :: Rule -> Rule -> Ordering +instance HasComment i => HasComment (Rule i) where + commentL = #ruleDecoration % commentL + +compareRuleName :: Rule i -> Rule i -> Ordering compareRuleName = compare `on` ruleName -- | @@ -235,11 +317,16 @@ data TyOp = RangeOp RangeBound | CtrlOp CtlOp deriving (Eq, Generic, Show) deriving anyclass (ToExpr) -data TypeOrGroup = TOGType Type0 | TOGGroup GroupEntry - deriving (Eq, Generic, Show) - deriving anyclass (ToExpr) +data TypeOrGroup i = TOGType (Type0 i) | TOGGroup (GroupEntry i) + deriving (Generic, Functor) + +deriving instance Eq i => Eq (TypeOrGroup i) -instance CollectComments TypeOrGroup +deriving instance Show i => Show (TypeOrGroup i) + +deriving instance ToExpr i => ToExpr (TypeOrGroup i) + +instance CollectComments (TypeOrGroup Comment) {-- | The group that is used to define a map or an array can often be reused in the @@ -290,7 +377,7 @@ instance CollectComments TypeOrGroup described as "threading in" the group or type inside the referenced type, which suggested the thread-like "~" character.) -} -unwrap :: TypeOrGroup -> Maybe Group +unwrap :: TypeOrGroup i -> Maybe (Group i) unwrap (TOGType (Type0 (Type1 t2 Nothing _ NE.:| []))) = case t2 of T2Map g -> Just g T2Array g -> Just g @@ -301,70 +388,85 @@ unwrap _ = Nothing -- A type can be given as a choice between one or more types. The -- choice matches a data item if the data item matches any one of the -- types given in the choice. -newtype Type0 = Type0 {t0Type1 :: NE.NonEmpty Type1} - deriving (Eq, Generic, Show) +newtype Type0 i = Type0 {t0Type1 :: NE.NonEmpty (Type1 i)} + deriving (Generic, Functor) deriving newtype (Semigroup) - deriving anyclass (ToExpr) -instance HasComment Type0 where +deriving instance Eq i => Eq (Type0 i) + +deriving instance Show i => Show (Type0 i) + +deriving anyclass instance ToExpr i => ToExpr (Type0 i) + +instance HasComment (Type0 Comment) where commentL = lens (view commentL . t0Type1) (\(Type0 x) y -> Type0 $ x & commentL .~ y) -instance CollectComments Type0 +instance CollectComments (Type0 Comment) -- | -- Two types can be combined with a range operator (see below) -data Type1 = Type1 - { t1Main :: Type2 - , t1TyOp :: Maybe (TyOp, Type2) - , t1Comment :: Comment +data Type1 i = Type1 + { t1Main :: Type2 i + , t1TyOp :: Maybe (TyOp, Type2 i) + , t1Decoration :: i } - deriving (Eq, Generic, Show) - deriving anyclass (ToExpr, Default) + deriving (Generic, Functor) + +deriving instance Eq i => Eq (Type1 i) + +deriving instance Show i => Show (Type1 i) -instance HasComment Type1 where - commentL = lens t1Comment (\x y -> x {t1Comment = y}) +deriving instance ToExpr i => ToExpr (Type1 i) -instance CollectComments Type1 where +instance HasComment (Type1 Comment) where + commentL = #t1Decoration + +instance CollectComments (Type1 Comment) where collectComments (Type1 m tyOp c) = c : collectComments m <> collectComments (fmap snd tyOp) -data Type2 +data Type2 i = -- | A type can be just a single value (such as 1 or "icecream" or -- h'0815'), which matches only a data item with that specific value -- (no conversions defined), - T2Value Value + T2Value (Value i) | -- | or be defined by a rule giving a meaning to a name (possibly after -- supplying generic arguments as required by the generic parameters) - T2Name Name (Maybe GenericArg) + T2Name Name (Maybe (GenericArg i)) | -- | or be defined in a parenthesized type expression (parentheses may be -- necessary to override some operator precedence), - T2Group Type0 + T2Group (Type0 i) | -- | a map expression, which matches a valid CBOR map the key/value pairs -- of which can be ordered in such a way that the resulting sequence -- matches the group expression, or - T2Map Group + T2Map (Group i) | -- | an array expression, which matches a CBOR array the elements of which -- when taken as values and complemented by a wildcard (matches -- anything) key each -- match the group, or - T2Array Group + T2Array (Group i) | -- | an "unwrapped" group (see Section 3.7), which matches the group -- inside a type defined as a map or an array by wrapping the group, or - T2Unwrapped Name (Maybe GenericArg) + T2Unwrapped Name (Maybe (GenericArg i)) | -- | an enumeration expression, which matches any value that is within the -- set of values that the values of the group given can take, or - T2Enum Group - | T2EnumRef Name (Maybe GenericArg) + T2Enum (Group i) + | T2EnumRef Name (Maybe (GenericArg i)) | -- | a tagged data item, tagged with the "uint" given and containing the -- type given as the tagged value, or - T2Tag (Maybe Word64) Type0 + T2Tag (Maybe Word64) (Type0 i) | -- | a data item of a major type (given by the DIGIT), optionally -- constrained to the additional information given by the uint, or T2DataItem Word8 (Maybe Word64) | -- | Any data item T2Any - deriving (Eq, Generic, Show, Default) - deriving anyclass (ToExpr) + deriving (Generic, Default, Functor) + +deriving instance Eq i => Eq (Type2 i) -instance CollectComments Type2 +deriving instance Show i => Show (Type2 i) + +deriving instance ToExpr i => ToExpr (Type2 i) + +instance CollectComments (Type2 Comment) -- | -- An optional _occurrence_ indicator can be given in front of a group @@ -393,28 +495,38 @@ instance Hashable OccurrenceIndicator -- | -- A group matches any sequence of key/value pairs that matches any of -- the choices given (again using PEG semantics). -newtype Group = Group {unGroup :: NE.NonEmpty GrpChoice} - deriving (Eq, Generic, Show) +newtype Group i = Group {unGroup :: NE.NonEmpty (GrpChoice i)} + deriving (Generic, Functor) deriving newtype (Semigroup) - deriving anyclass (ToExpr) -instance HasComment Group where +deriving instance Eq i => Eq (Group i) + +deriving instance Show i => Show (Group i) + +deriving anyclass instance ToExpr i => ToExpr (Group i) + +instance HasComment (Group Comment) where commentL = lens unGroup (\x y -> x {unGroup = y}) % commentL -instance CollectComments Group where +instance CollectComments (Group Comment) where collectComments (Group xs) = concatMap collectComments xs -data GrpChoice = GrpChoice - { gcGroupEntries :: [GroupEntry] - , gcComment :: Comment +data GrpChoice i = GrpChoice + { gcGroupEntries :: [GroupEntry i] + , gcDecoration :: i } - deriving (Eq, Generic, Show) - deriving anyclass (ToExpr) + deriving (Generic, Functor) + +deriving instance Eq i => Eq (GrpChoice i) -instance HasComment GrpChoice where - commentL = lens gcComment (\x y -> x {gcComment = y}) +deriving instance Show i => Show (GrpChoice i) -instance CollectComments GrpChoice where +deriving instance ToExpr i => ToExpr (GrpChoice i) + +instance HasComment (GrpChoice Comment) where + commentL = #gcDecoration + +instance CollectComments (GrpChoice Comment) where collectComments (GrpChoice ges c) = c : concatMap collectComments ges -- | @@ -424,26 +536,38 @@ instance CollectComments GrpChoice where -- the memberkey is given. If the memberkey is not given, the entry can -- only be used for matching arrays, not for maps. (See below for how -- that is modified by the occurrence indicator.) -data GroupEntry = GroupEntry +data GroupEntry i = GroupEntry { geOccurrenceIndicator :: Maybe OccurrenceIndicator - , geComment :: Comment - , geVariant :: GroupEntryVariant + , geVariant :: GroupEntryVariant i + , geDecoration :: i } - deriving (Eq, Show, Generic, ToExpr) + deriving (Generic, Functor) + +deriving instance Eq i => Eq (GroupEntry i) + +deriving instance Show i => Show (GroupEntry i) + +deriving instance ToExpr i => ToExpr (GroupEntry i) + +instance CollectComments (GroupEntry Comment) where + collectComments (GroupEntry _ x c) = c : collectComments x + +data GroupEntryVariant i + = GEType (Maybe (MemberKey i)) (Type0 i) + | GERef Name (Maybe (GenericArg i)) + | GEGroup (Group i) + deriving (Generic, Functor) -instance CollectComments GroupEntry where - collectComments (GroupEntry _ c x) = c : collectComments x +deriving instance Eq i => Eq (GroupEntryVariant i) -data GroupEntryVariant - = GEType (Maybe MemberKey) Type0 - | GERef Name (Maybe GenericArg) - | GEGroup Group - deriving (Eq, Show, Generic, ToExpr) +deriving instance Show i => Show (GroupEntryVariant i) -instance HasComment GroupEntry where - commentL = lens geComment (\x y -> x {geComment = y}) +deriving instance ToExpr i => ToExpr (GroupEntryVariant i) -instance CollectComments GroupEntryVariant where +instance HasComment (GroupEntry Comment) where + commentL = #geDecoration + +instance CollectComments (GroupEntryVariant Comment) where collectComments (GEType _ t0) = collectComments t0 collectComments (GERef n mga) = collectComments n <> collectComments mga collectComments (GEGroup g) = collectComments g @@ -456,19 +580,24 @@ instance CollectComments GroupEntryVariant where -- member of the key type, unless a cut preceding it in the group -- applies (see Section 3.5.4 for how map matching is influenced by the -- presence of the cuts denoted by "^" or ":" in previous entries). -data MemberKey - = MKType Type1 +data MemberKey i + = MKType (Type1 i) | MKBareword Name - | MKValue Value - deriving (Eq, Generic, Show) - deriving anyclass (ToExpr) + | MKValue (Value i) + deriving (Generic, Functor) -data Value = Value ValueVariant Comment - deriving (Eq, Generic, Show, Default) +deriving instance Eq i => Eq (MemberKey i) + +deriving instance Show i => Show (MemberKey i) + +deriving instance ToExpr i => ToExpr (MemberKey i) + +data Value i = Value ValueVariant i + deriving (Eq, Generic, Show, Default, Functor) deriving anyclass (ToExpr, Hashable, CollectComments) -value :: ValueVariant -> Value -value x = Value x mempty +value :: Default i => ValueVariant -> Value i +value x = Value x def data ValueVariant = VUInt Word64 diff --git a/src/Codec/CBOR/Cuddle/CDDL/CTree.hs b/src/Codec/CBOR/Cuddle/CDDL/CTree.hs index 176c147..ebfbe67 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/CTree.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/CTree.hs @@ -31,7 +31,7 @@ import GHC.Generics (Generic) -- We principally use this functor to represent references - thus, every 'f a' -- may be either an a or a reference to another CTree. data CTree f - = Literal Value + = Literal (Value ()) | Postlude PTerm | Map [Node f] | Array [Node f] diff --git a/src/Codec/CBOR/Cuddle/CDDL/Prelude.hs b/src/Codec/CBOR/Cuddle/CDDL/Prelude.hs index 3bcbe1a..4bb5776 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/Prelude.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/Prelude.hs @@ -1,13 +1,15 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} module Codec.CBOR.Cuddle.CDDL.Prelude (prependPrelude) where import Codec.CBOR.Cuddle.CDDL (CDDL (..)) +import Codec.CBOR.Cuddle.Comments (Comment) import Codec.CBOR.Cuddle.Parser (pCDDL) import Text.Megaparsec (errorBundlePretty, parse) -- TODO switch to quasiquotes -cddlPrelude :: CDDL +cddlPrelude :: CDDL Comment cddlPrelude = either (error . errorBundlePretty) id $ parse @@ -57,5 +59,5 @@ cddlPrelude = \ null = nil \ \ undefined = #7.23" -prependPrelude :: CDDL -> CDDL +prependPrelude :: CDDL Comment -> CDDL Comment prependPrelude = (cddlPrelude <>) diff --git a/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs b/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs index 96a0f9a..c403f8c 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs @@ -70,7 +70,7 @@ import Optics.Core -- 1. Rule extensions -------------------------------------------------------------------------------- -type CDDLMap = Map.Map Name (Parametrised TypeOrGroup) +type CDDLMap i = Map.Map Name (Parametrised (TypeOrGroup i)) type Parametrised a = ParametrisedWith [Name] a @@ -82,24 +82,24 @@ parameters :: Parametrised a -> [Name] parameters (Unparametrised _) = mempty parameters (Parametrised _ ps) = ps -asMap :: CDDL -> CDDLMap +asMap :: CDDL i -> CDDLMap i asMap cddl = foldl' go Map.empty rules where rules = cddlTopLevel cddl go x (TopLevelComment _) = x go x (TopLevelRule r) = assignOrExtend x r - assignOrExtend :: CDDLMap -> Rule -> CDDLMap + assignOrExtend :: CDDLMap i -> Rule i -> CDDLMap i assignOrExtend m (Rule n gps assign tog _) = case assign of -- Equals assignment AssignEq -> Map.insert n (toParametrised tog gps) m AssignExt -> Map.alter (extend tog gps) n m extend :: - TypeOrGroup -> + TypeOrGroup i -> Maybe GenericParam -> - Maybe (Parametrised TypeOrGroup) -> - Maybe (Parametrised TypeOrGroup) + Maybe (Parametrised (TypeOrGroup i)) -> + Maybe (Parametrised (TypeOrGroup i)) extend tog _gps (Just existing) = case (underlying existing, tog) of (TOGType _, TOGType (Type0 new)) -> Just $ @@ -139,23 +139,23 @@ deriving instance Show (CTreeRoot OrRef) -- | Build a CTree incorporating references. -- -- This translation cannot fail. -buildRefCTree :: CDDLMap -> RefCTree +buildRefCTree :: CDDLMap () -> RefCTree buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules where toCTreeRule :: - Parametrised TypeOrGroup -> + Parametrised (TypeOrGroup ()) -> ParametrisedWith [Name] (CTree.Node OrRef) toCTreeRule = fmap toCTreeTOG - toCTreeTOG :: TypeOrGroup -> CTree.Node OrRef + toCTreeTOG :: TypeOrGroup () -> CTree.Node OrRef toCTreeTOG (TOGType t0) = toCTreeT0 t0 toCTreeTOG (TOGGroup ge) = toCTreeGroupEntry ge - toCTreeT0 :: Type0 -> CTree.Node OrRef + toCTreeT0 :: Type0 () -> CTree.Node OrRef toCTreeT0 (Type0 (t1 NE.:| [])) = toCTreeT1 t1 toCTreeT0 (Type0 xs) = It . CTree.Choice $ toCTreeT1 <$> xs - toCTreeT1 :: Type1 -> CTree.Node OrRef + toCTreeT1 :: Type1 () -> CTree.Node OrRef toCTreeT1 (Type1 t2 Nothing _) = toCTreeT2 t2 toCTreeT1 (Type1 t2 (Just (op, t2')) _) = case op of RangeOp bound -> @@ -173,7 +173,7 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules , CTree.controller = toCTreeT2 t2' } - toCTreeT2 :: Type2 -> CTree.Node OrRef + toCTreeT2 :: Type2 () -> CTree.Node OrRef toCTreeT2 (T2Value v) = It $ CTree.Literal v toCTreeT2 (T2Name n garg) = Ref n (fromGenArgs garg) @@ -215,35 +215,35 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules toCTreeDataItem _ = It $ CTree.Postlude PTAny - toCTreeGroupEntry :: GroupEntry -> CTree.Node OrRef - toCTreeGroupEntry (GroupEntry (Just occi) _ (GEType mmkey t0)) = + toCTreeGroupEntry :: GroupEntry () -> CTree.Node OrRef + toCTreeGroupEntry (GroupEntry (Just occi) (GEType mmkey t0) _) = It $ CTree.Occur { CTree.item = toKVPair mmkey t0 , CTree.occurs = occi } - toCTreeGroupEntry (GroupEntry Nothing _ (GEType mmkey t0)) = toKVPair mmkey t0 - toCTreeGroupEntry (GroupEntry (Just occi) _ (GERef n margs)) = + toCTreeGroupEntry (GroupEntry Nothing (GEType mmkey t0) _) = toKVPair mmkey t0 + toCTreeGroupEntry (GroupEntry (Just occi) (GERef n margs) _) = It $ CTree.Occur { CTree.item = Ref n (fromGenArgs margs) , CTree.occurs = occi } - toCTreeGroupEntry (GroupEntry Nothing _ (GERef n margs)) = Ref n (fromGenArgs margs) - toCTreeGroupEntry (GroupEntry (Just occi) _ (GEGroup g)) = + toCTreeGroupEntry (GroupEntry Nothing (GERef n margs) _) = Ref n (fromGenArgs margs) + toCTreeGroupEntry (GroupEntry (Just occi) (GEGroup g) _) = It $ CTree.Occur { CTree.item = groupToGroup g , CTree.occurs = occi } - toCTreeGroupEntry (GroupEntry Nothing _ (GEGroup g)) = groupToGroup g + toCTreeGroupEntry (GroupEntry Nothing (GEGroup g) _) = groupToGroup g - fromGenArgs :: Maybe GenericArg -> [CTree.Node OrRef] + fromGenArgs :: Maybe (GenericArg ()) -> [CTree.Node OrRef] fromGenArgs = maybe [] (\(GenericArg xs) -> NE.toList $ fmap toCTreeT1 xs) -- Interpret a group as an enumeration. Note that we float out the -- choice options - toCTreeEnum :: Group -> CTree.Node OrRef + toCTreeEnum :: Group () -> CTree.Node OrRef toCTreeEnum (Group (a NE.:| [])) = It . CTree.Enum . It . CTree.Group $ toCTreeGroupEntry <$> gcGroupEntries a toCTreeEnum (Group xs) = @@ -253,14 +253,14 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules groupEntries = fmap gcGroupEntries xs -- Embed a group in another group, again floating out the choice options - groupToGroup :: Group -> CTree.Node OrRef + groupToGroup :: Group () -> CTree.Node OrRef groupToGroup (Group (a NE.:| [])) = It . CTree.Group $ fmap toCTreeGroupEntry (gcGroupEntries a) groupToGroup (Group xs) = It . CTree.Choice $ fmap (It . CTree.Group . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs) - toKVPair :: Maybe MemberKey -> Type0 -> CTree.Node OrRef + toKVPair :: Maybe (MemberKey ()) -> Type0 () -> CTree.Node OrRef toKVPair Nothing t0 = toCTreeT0 t0 toKVPair (Just mkey) t0 = It $ @@ -272,7 +272,7 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules } -- Interpret a group as a map. Note that we float out the choice options - toCTreeMap :: Group -> CTree.Node OrRef + toCTreeMap :: Group () -> CTree.Node OrRef toCTreeMap (Group (a NE.:| [])) = It . CTree.Map $ fmap toCTreeGroupEntry (gcGroupEntries a) toCTreeMap (Group xs) = It @@ -281,14 +281,14 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules -- Interpret a group as an array. Note that we float out the choice -- options - toCTreeArray :: Group -> CTree.Node OrRef + toCTreeArray :: Group () -> CTree.Node OrRef toCTreeArray (Group (a NE.:| [])) = It . CTree.Array $ fmap toCTreeGroupEntry (gcGroupEntries a) toCTreeArray (Group xs) = It . CTree.Choice $ fmap (It . CTree.Array . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs) - toCTreeMemberKey :: MemberKey -> CTree.Node OrRef + toCTreeMemberKey :: MemberKey () -> CTree.Node OrRef toCTreeMemberKey (MKValue v) = It $ CTree.Literal v toCTreeMemberKey (MKBareword (Name n _)) = It $ CTree.Literal (Value (VText n) mempty) toCTreeMemberKey (MKType t1) = toCTreeT1 t1 @@ -556,7 +556,7 @@ buildMonoCTree (CTreeRoot ct) = do -- Combined resolution -------------------------------------------------------------------------------- -fullResolveCDDL :: CDDL -> Either NameResolutionFailure (CTreeRoot' Identity MonoRef) +fullResolveCDDL :: CDDL () -> Either NameResolutionFailure (CTreeRoot' Identity MonoRef) fullResolveCDDL cddl = do let refCTree = buildRefCTree (asMap cddl) rCTree <- buildResolvedCTree refCTree diff --git a/src/Codec/CBOR/Cuddle/Huddle.hs b/src/Codec/CBOR/Cuddle/Huddle.hs index eb4669b..ca47429 100644 --- a/src/Codec/CBOR/Cuddle/Huddle.hs +++ b/src/Codec/CBOR/Cuddle/Huddle.hs @@ -2,6 +2,7 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} @@ -14,6 +15,7 @@ module Codec.CBOR.Cuddle.Huddle ( -- * Core Types Huddle, + DHuddle (..), HuddleItem (..), huddleAugment, Rule, @@ -88,13 +90,14 @@ where import Codec.CBOR.Cuddle.CDDL (CDDL) import Codec.CBOR.Cuddle.CDDL qualified as C import Codec.CBOR.Cuddle.CDDL.CtlOp qualified as CtlOp +import Codec.CBOR.Cuddle.Comments (Comment, HasComment (..)) import Codec.CBOR.Cuddle.Comments qualified as C import Control.Monad (when) import Control.Monad.State (MonadState (get), execState, modify) import Data.ByteString (ByteString) import Data.Default.Class (Default (..)) import Data.Function (on) -import Data.Generics.Product (HasField' (field'), field, getField) +import Data.Generics.Product (field, getField) import Data.List qualified as L import Data.List.NonEmpty qualified as NE import Data.Map.Ordered.Strict (OMap, (|<>)) @@ -106,43 +109,57 @@ import Data.Void (Void) import Data.Word (Word64) import GHC.Exts (IsList (Item, fromList, toList)) import GHC.Generics (Generic) -import Optics.Core (lens, view, (%~), (&), (.~), (^.)) +import Optics.Core (lens, view, (%), (%~), (&), (.~), (^.)) import Prelude hiding ((/)) -data Named a = Named +data DHuddle = DHuddle + { dhComment :: Comment + , dhGenerator :: Maybe C.CBORGenerator + } + deriving (Generic) + +instance C.HasComment DHuddle where + commentL = #dhComment + +instance Default DHuddle + +data Named i a = Named { name :: T.Text , value :: a - , description :: Maybe T.Text + , decoration :: i } deriving (Functor, Generic) -- | Add a description to a rule or group entry, to be included as a comment. -comment :: HasField' "description" a (Maybe T.Text) => T.Text -> a -> a -comment desc n = n & field' @"description" .~ Just desc +comment :: HasComment a => Comment -> a -> a +comment desc = commentL .~ desc -instance Show (Named a) where +instance Show (Named i a) where show (Named n _ _) = T.unpack n -type Rule = Named Type0 +instance HasComment i => HasComment (Named i a) where + commentL = #decoration % commentL -data HuddleItem - = HIRule Rule - | HIGRule GRuleDef - | HIGroup (Named Group) - deriving (Generic, Show) +type Rule i = Named i (Type0 i) + +data HuddleItem i + = HIRule (Rule i) + | HIGRule (GRuleDef i) + | HIGroup (Named i (Group i)) + deriving (Generic) -- | Top-level Huddle type is a list of rules. -data Huddle = Huddle - { roots :: [Rule] +data Huddle i = Huddle + { roots :: [Rule i] -- ^ Root elements - , items :: OMap T.Text HuddleItem + , items :: OMap T.Text (HuddleItem i) } - deriving (Generic, Show) + deriving (Generic) -- | Joins two `Huddle` values with a left-bias. This means that this function -- is not symmetric and that any rules that are present in both prefer the -- definition from the `Huddle` value on the left. -huddleAugment :: Huddle -> Huddle -> Huddle +huddleAugment :: Huddle i -> Huddle i -> Huddle i huddleAugment (Huddle rootsL itemsL) (Huddle rootsR itemsR) = Huddle (L.nubBy ((==) `on` name) $ rootsL <> rootsR) (itemsL |<> itemsR) @@ -156,7 +173,7 @@ huddleAugment (Huddle rootsL itemsL) (Huddle rootsR itemsR) = -- updating higher-level items which make use of them - that is, we do not -- need to "close over" higher-level terms, since by the time they have been -- built into a huddle structure, the references have been converted to keys. -instance Semigroup Huddle where +instance Semigroup (Huddle i) where h1 <> h2 = Huddle { roots = case roots h2 of @@ -166,15 +183,15 @@ instance Semigroup Huddle where } -- | This instance is mostly used for testing -instance IsList Huddle where - type Item Huddle = Rule +instance IsList (Huddle i) where + type Item (Huddle i) = Rule i fromList [] = Huddle mempty OMap.empty fromList (x : xs) = (field @"items" %~ (OMap.|> (x ^. field @"name", HIRule x))) $ fromList xs toList = const [] -instance Default Huddle where +instance Default (Huddle i) where def = Huddle [] OMap.empty data Choice a @@ -190,122 +207,122 @@ choiceToNE :: Choice a -> NE.NonEmpty a choiceToNE (NoChoice c) = c NE.:| [] choiceToNE (ChoiceOf c cs) = c NE.:| choiceToList cs -data Key - = LiteralKey Literal - | TypeKey Type2 +data Key i + = LiteralKey (Literal i) + | TypeKey (Type2 i) deriving (Show) -- | Instance for the very general case where we use text keys -instance IsString Key where - fromString x = LiteralKey $ Literal (LText $ T.pack x) mempty +instance Default i => IsString (Key i) where + fromString x = LiteralKey $ Literal (LText $ T.pack x) def -- | Use a number as a key -idx :: Word64 -> Key -idx x = LiteralKey $ Literal (LInt x) mempty +idx :: Default i => Word64 -> Key i +idx x = LiteralKey $ Literal (LInt x) def -asKey :: IsType0 r => r -> Key +asKey :: IsType0 r => r -> Key DHuddle asKey r = case toType0 r of NoChoice x -> TypeKey x ChoiceOf _ _ -> error "Cannot use a choice of types as a map key" -data MapEntry = MapEntry - { key :: Key - , value :: Type0 +data MapEntry i = MapEntry + { key :: Key i + , value :: Type0 i , quantifier :: Occurs , meDescription :: C.Comment } deriving (Generic, Show) -instance C.HasComment MapEntry where +instance C.HasComment (MapEntry i) where commentL = lens meDescription (\x y -> x {meDescription = y}) -newtype MapChoice = MapChoice {unMapChoice :: [MapEntry]} +newtype MapChoice i = MapChoice {unMapChoice :: [MapEntry i]} deriving (Show) -instance IsList MapChoice where - type Item MapChoice = MapEntry +instance IsList (MapChoice i) where + type Item (MapChoice i) = MapEntry i fromList = MapChoice toList (MapChoice m) = m -type Map = Choice MapChoice +type Map i = Choice (MapChoice i) -data ArrayEntry = ArrayEntry - { key :: Maybe Key +data ArrayEntry i = ArrayEntry + { key :: Maybe (Key i) -- ^ Arrays can have keys, but they have no semantic meaning. We add them -- here because they can be illustrative in the generated CDDL. - , value :: Type0 + , value :: Type0 i , quantifier :: Occurs - , aeDescription :: C.Comment + , aeDecoration :: i } deriving (Generic, Show) -instance C.HasComment ArrayEntry where - commentL = lens aeDescription (\x y -> x {aeDescription = y}) +instance HasComment i => C.HasComment (ArrayEntry i) where + commentL = #aeDecoration % commentL -instance Num ArrayEntry where +instance Default i => Num (ArrayEntry i) where fromInteger i = ArrayEntry Nothing - (NoChoice . T2Range . Unranged $ Literal (LInt (fromIntegral i)) mempty) + (NoChoice . T2Range . Unranged $ Literal (LInt (fromIntegral i)) def) + def def - mempty (+) = error "Cannot treat ArrayEntry as a number" (*) = error "Cannot treat ArrayEntry as a number" abs = error "Cannot treat ArrayEntry as a number" signum = error "Cannot treat ArrayEntry as a number" negate = error "Cannot treat ArrayEntry as a number" -data ArrayChoice = ArrayChoice - { unArrayChoice :: [ArrayEntry] - , acComment :: C.Comment +data ArrayChoice i = ArrayChoice + { unArrayChoice :: [ArrayEntry i] + , acDecoration :: i } - deriving (Show) + deriving (Show, Generic) -instance Semigroup ArrayChoice where +instance Semigroup i => Semigroup (ArrayChoice i) where ArrayChoice x xc <> ArrayChoice y yc = ArrayChoice (x <> y) (xc <> yc) -instance Monoid ArrayChoice where +instance Monoid i => Monoid (ArrayChoice i) where mempty = ArrayChoice mempty mempty -instance C.HasComment ArrayChoice where - commentL = lens acComment (\x y -> x {acComment = y}) +instance HasComment i => C.HasComment (ArrayChoice i) where + commentL = #acDecoration % commentL -instance IsList ArrayChoice where - type Item ArrayChoice = ArrayEntry +instance Default i => IsList (ArrayChoice i) where + type Item (ArrayChoice i) = ArrayEntry i - fromList = (`ArrayChoice` mempty) + fromList = (`ArrayChoice` def) toList (ArrayChoice l _) = l -type Array = Choice ArrayChoice +type Array i = Choice (ArrayChoice i) -newtype Group = Group {unGroup :: [ArrayEntry]} +newtype Group i = Group {unGroup :: [ArrayEntry i]} deriving (Show, Monoid, Semigroup) -instance IsList Group where - type Item Group = ArrayEntry +instance IsList (Group i) where + type Item (Group i) = ArrayEntry i fromList = Group toList (Group l) = l -data Type2 - = T2Constrained Constrained - | T2Range Ranged - | T2Map Map - | T2Array Array - | T2Tagged (Tagged Type0) - | T2Ref (Named Type0) - | T2Group (Named Group) +data Type2 i + = T2Constrained (Constrained i) + | T2Range (Ranged i) + | T2Map (Map i) + | T2Array (Array i) + | T2Tagged (Tagged (Type0 i)) + | T2Ref (Named i (Type0 i)) + | T2Group (Named i (Group i)) | -- | Call to a generic rule, binding arguments - T2Generic GRuleCall + T2Generic (GRuleCall i) | -- | Reference to a generic parameter within the body of the definition T2GenericRef GRef deriving (Show) -type Type0 = Choice Type2 +type Type0 i = Choice (Type2 i) -instance Num Type0 where - fromInteger i = NoChoice . T2Range . Unranged $ Literal (LInt (fromIntegral i)) mempty +instance Default i => Num (Type0 i) where + fromInteger i = NoChoice . T2Range . Unranged $ Literal (LInt (fromIntegral i)) def (+) = error "Cannot treat Type0 as a number" (*) = error "Cannot treat Type0 as a number" abs = error "Cannot treat Type0 as a number" @@ -343,14 +360,14 @@ deriving instance Show (Value a) -- Literals -------------------------------------------------------------------------------- -data Literal = Literal +data Literal i = Literal { litVariant :: LiteralVariant - , litComment :: C.Comment + , litDecoration :: i } - deriving (Show) + deriving (Show, Generic) -instance C.HasComment Literal where - commentL = lens litComment (\x y -> x {litComment = y}) +instance HasComment i => C.HasComment (Literal i) where + commentL = #litDecoration % commentL data LiteralVariant where -- | We store both int and nint as a Word64, since the sign is indicated in @@ -364,31 +381,31 @@ data LiteralVariant where LBytes :: ByteString -> LiteralVariant deriving (Show) -int :: Integer -> Literal +int :: Integer -> Literal DHuddle int = inferInteger -bstr :: ByteString -> Literal -bstr x = Literal (LBytes x) mempty +bstr :: ByteString -> Literal DHuddle +bstr x = Literal (LBytes x) def -text :: T.Text -> Literal -text x = Literal (LText x) mempty +text :: T.Text -> Literal DHuddle +text x = Literal (LText x) def -inferInteger :: Integer -> Literal +inferInteger :: Default i => Integer -> Literal i inferInteger i - | i >= 0 && i < fromIntegral (maxBound @Word64) = Literal (LInt (fromInteger i)) mempty - | i < 0 && (-i) < fromIntegral (maxBound @Word64) = Literal (LNInt (fromInteger (-i))) mempty - | otherwise = Literal (LBignum i) mempty + | i >= 0 && i < fromIntegral (maxBound @Word64) = Literal (LInt (fromInteger i)) def + | i < 0 && (-i) < fromIntegral (maxBound @Word64) = Literal (LNInt (fromInteger (-i))) def + | otherwise = Literal (LBignum i) def -------------------------------------------------------------------------------- -- Constraints and Ranges -------------------------------------------------------------------------------- -- | A reference can be to any type, so we allow it to inhabit all -type AnyRef a = Named Type0 +type AnyRef i a = Named i (Type0 i) -data Constrainable a +data Constrainable i a = CValue (Value a) - | CRef (AnyRef a) + | CRef (AnyRef i a) | CGRef GRef deriving (Show) @@ -400,24 +417,24 @@ data CGRefType -- | We only allow constraining basic values, or references. Of course, we -- can't check what the references refer to. -data Constrained where +data Constrained i where Constrained :: - forall a. - { value :: Constrainable a + forall a i. + { value :: Constrainable i a , constraint :: ValueConstraint a - , refs :: [Rule] + , refs :: [Rule i] -- ^ Sometimes constraints reference rules. In this case we need to -- collect the references in order to traverse them when collecting all -- relevant rules. } -> - Constrained + Constrained i -deriving instance Show Constrained +deriving instance Show (Constrained i) class IsConstrainable a x | a -> x where - toConstrainable :: a -> Constrainable x + toConstrainable :: a -> Constrainable DHuddle x -instance IsConstrainable (AnyRef a) CRefType where +instance IsConstrainable (AnyRef DHuddle a) CRefType where toConstrainable = CRef instance IsConstrainable (Value a) a where @@ -426,13 +443,13 @@ instance IsConstrainable (Value a) a where instance IsConstrainable GRef CGRefType where toConstrainable = CGRef -unconstrained :: Value a -> Constrained +unconstrained :: Value a -> Constrained i unconstrained v = Constrained (CValue v) def [] -- | A constraint on a 'Value' is something applied via CtlOp or RangeOp on a -- Type2, forming a Type1. data ValueConstraint a = ValueConstraint - { applyConstraint :: C.Type2 -> C.Type1 + { applyConstraint :: C.Type2 DHuddle -> C.Type1 DHuddle , showConstraint :: String } @@ -442,7 +459,7 @@ instance Show (ValueConstraint a) where instance Default (ValueConstraint a) where def = ValueConstraint - { applyConstraint = \x -> C.Type1 x Nothing mempty + { applyConstraint = \x -> C.Type1 x Nothing def , showConstraint = "" } @@ -462,15 +479,15 @@ instance IsSizeable CGRefType -- | Things which can be used on the RHS of the '.size' operator. class IsSize a where - sizeAsCDDL :: a -> C.Type2 + sizeAsCDDL :: a -> C.Type2 DHuddle sizeAsString :: a -> String instance IsSize Word where - sizeAsCDDL x = C.T2Value $ C.Value (C.VUInt $ fromIntegral x) mempty + sizeAsCDDL x = C.T2Value $ C.Value (C.VUInt $ fromIntegral x) def sizeAsString = show instance IsSize Word64 where - sizeAsCDDL x = C.T2Value $ C.Value (C.VUInt x) mempty + sizeAsCDDL x = C.T2Value $ C.Value (C.VUInt x) def sizeAsString = show instance IsSize (Word64, Word64) where @@ -478,9 +495,9 @@ instance IsSize (Word64, Word64) where C.T2Group ( C.Type0 ( C.Type1 - (C.T2Value (C.Value (C.VUInt x) mempty)) - (Just (C.RangeOp C.Closed, C.T2Value (C.Value (C.VUInt y) mempty))) - mempty + (C.T2Value (C.Value (C.VUInt x) def)) + (Just (C.RangeOp C.Closed, C.T2Value (C.Value (C.VUInt y) def))) + def NE.:| [] ) ) @@ -496,7 +513,7 @@ sized :: ) => c -> s -> - Constrained + Constrained DHuddle sized v sz = Constrained (toConstrainable @c @a v) @@ -505,17 +522,17 @@ sized v sz = C.Type1 t2 (Just (C.CtrlOp CtlOp.Size, sizeAsCDDL sz)) - mempty + def , showConstraint = ".size " <> sizeAsString sz } [] class IsCborable a instance IsCborable ByteString -instance IsCborable (AnyRef a) +instance IsCborable (AnyRef i a) instance IsCborable GRef -cbor :: (IsCborable b, IsConstrainable c b) => c -> Rule -> Constrained +cbor :: (IsCborable b, IsConstrainable c b) => c -> Rule DHuddle -> Constrained DHuddle cbor v r@(Named n _ _) = Constrained (toConstrainable v) @@ -524,17 +541,17 @@ cbor v r@(Named n _ _) = C.Type1 t2 (Just (C.CtrlOp CtlOp.Cbor, C.T2Name (C.Name n mempty) Nothing)) - mempty + def , showConstraint = ".cbor " <> T.unpack n } [r] class IsComparable a instance IsComparable Int -instance IsComparable (AnyRef a) +instance IsComparable (AnyRef i a) instance IsComparable GRef -le :: (IsComparable a, IsConstrainable c a) => c -> Word64 -> Constrained +le :: (IsComparable a, IsConstrainable c a) => c -> Word64 -> Constrained DHuddle le v bound = Constrained (toConstrainable v) @@ -542,43 +559,43 @@ le v bound = { applyConstraint = \t2 -> C.Type1 t2 - (Just (C.CtrlOp CtlOp.Le, C.T2Value (C.Value (C.VUInt $ fromIntegral bound) mempty))) - mempty + (Just (C.CtrlOp CtlOp.Le, C.T2Value (C.Value (C.VUInt $ fromIntegral bound) def))) + def , showConstraint = ".le " <> show bound } [] -- Ranges -data RangeBound - = RangeBoundLiteral Literal - | RangeBoundRef (Named Type0) +data RangeBound i + = RangeBoundLiteral (Literal i) + | RangeBoundRef (Named i (Type0 i)) deriving (Show) class IsRangeBound a where - toRangeBound :: a -> RangeBound + toRangeBound :: a -> RangeBound DHuddle -instance IsRangeBound Literal where +instance IsRangeBound (Literal DHuddle) where toRangeBound = RangeBoundLiteral instance IsRangeBound Integer where toRangeBound = RangeBoundLiteral . inferInteger -instance IsRangeBound (Named Type0) where +instance IsRangeBound (Named DHuddle (Type0 DHuddle)) where toRangeBound = RangeBoundRef -data Ranged where +data Ranged i where Ranged :: - { lb :: RangeBound - , ub :: RangeBound + { lb :: RangeBound i + , ub :: RangeBound i , bounds :: C.RangeBound } -> - Ranged - Unranged :: Literal -> Ranged + Ranged i + Unranged :: Literal i -> Ranged i deriving (Show) -- | Establish a closed range bound. -(...) :: (IsRangeBound a, IsRangeBound b) => a -> b -> Ranged +(...) :: (IsRangeBound a, IsRangeBound b) => a -> b -> Ranged DHuddle l ... u = Ranged (toRangeBound l) (toRangeBound u) C.Closed infixl 9 ... @@ -588,33 +605,33 @@ infixl 9 ... -------------------------------------------------------------------------------- class IsType0 a where - toType0 :: a -> Type0 + toType0 :: a -> Type0 DHuddle -instance IsType0 Rule where +instance IsType0 (Rule DHuddle) where toType0 = NoChoice . T2Ref -instance IsType0 (Choice Type2) where +instance IsType0 (Choice (Type2 DHuddle)) where toType0 = id -instance IsType0 Constrained where +instance IsType0 (Constrained DHuddle) where toType0 = NoChoice . T2Constrained -instance IsType0 Map where +instance IsType0 (Map DHuddle) where toType0 = NoChoice . T2Map -instance IsType0 MapChoice where +instance IsType0 (MapChoice DHuddle) where toType0 = NoChoice . T2Map . NoChoice -instance IsType0 Array where +instance IsType0 (Array DHuddle) where toType0 = NoChoice . T2Array -instance IsType0 ArrayChoice where +instance IsType0 (ArrayChoice DHuddle) where toType0 = NoChoice . T2Array . NoChoice -instance IsType0 Ranged where +instance IsType0 (Ranged DHuddle) where toType0 = NoChoice . T2Range -instance IsType0 Literal where +instance IsType0 (Literal DHuddle) where toType0 = NoChoice . T2Range . Unranged -- We also allow going directly from primitive types to Type2 @@ -622,25 +639,24 @@ instance IsType0 Integer where toType0 = NoChoice . T2Range . Unranged . inferInteger instance IsType0 T.Text where - toType0 :: T.Text -> Type0 - toType0 x = NoChoice . T2Range . Unranged $ Literal (LText x) mempty + toType0 x = NoChoice . T2Range . Unranged $ Literal (LText x) def instance IsType0 ByteString where - toType0 x = NoChoice . T2Range . Unranged $ Literal (LBytes x) mempty + toType0 x = NoChoice . T2Range . Unranged $ Literal (LBytes x) def instance IsType0 Float where - toType0 x = NoChoice . T2Range . Unranged $ Literal (LFloat x) mempty + toType0 x = NoChoice . T2Range . Unranged $ Literal (LFloat x) def instance IsType0 Double where - toType0 x = NoChoice . T2Range . Unranged $ Literal (LDouble x) mempty + toType0 x = NoChoice . T2Range . Unranged $ Literal (LDouble x) def instance IsType0 (Value a) where toType0 = NoChoice . T2Constrained . unconstrained -instance IsType0 (Named Group) where +instance IsType0 (Named DHuddle (Group DHuddle)) where toType0 = NoChoice . T2Group -instance IsType0 GRuleCall where +instance IsType0 (GRuleCall DHuddle) where toType0 = NoChoice . T2Generic instance IsType0 GRef where @@ -649,7 +665,7 @@ instance IsType0 GRef where instance IsType0 a => IsType0 (Tagged a) where toType0 = NoChoice . T2Tagged . fmap toType0 -instance IsType0 HuddleItem where +instance IsType0 (HuddleItem DHuddle) where toType0 (HIRule r) = toType0 r toType0 (HIGroup g) = toType0 g toType0 (HIGRule g) = @@ -674,11 +690,11 @@ instance CanQuantify Occurs where lb <+ (Occurs _ ub) = Occurs (Just lb) ub (Occurs lb _) +> ub = Occurs lb (Just ub) -instance CanQuantify ArrayEntry where +instance CanQuantify (ArrayEntry i) where lb <+ ae = ae & field @"quantifier" %~ (lb <+) ae +> ub = ae & field @"quantifier" %~ (+> ub) -instance CanQuantify MapEntry where +instance CanQuantify (MapEntry i) where lb <+ ae = ae & field @"quantifier" %~ (lb <+) ae +> ub = ae & field @"quantifier" %~ (+> ub) @@ -688,25 +704,25 @@ instance CanQuantify a => CanQuantify (Choice a) where c +> ub = fmap (+> ub) c class IsEntryLike a where - fromMapEntry :: MapEntry -> a + fromMapEntry :: MapEntry DHuddle -> a -instance IsEntryLike MapEntry where +instance IsEntryLike (MapEntry DHuddle) where fromMapEntry = id -instance IsEntryLike ArrayEntry where +instance IsEntryLike (ArrayEntry DHuddle) where fromMapEntry me = ArrayEntry { key = Just $ getField @"key" me , value = getField @"value" me , quantifier = getField @"quantifier" me - , aeDescription = mempty + , aeDecoration = def } -instance IsEntryLike Type0 where +instance IsEntryLike (Type0 DHuddle) where fromMapEntry = getField @"value" -(==>) :: (IsType0 a, IsEntryLike me) => Key -> a -> me +(==>) :: (IsType0 a, IsEntryLike me) => Key DHuddle -> a -> me k ==> gc = fromMapEntry MapEntry @@ -719,29 +735,29 @@ k ==> gc = infixl 8 ==> -- | Assign a rule -(=:=) :: IsType0 a => T.Text -> a -> Rule -n =:= b = Named n (toType0 b) Nothing +(=:=) :: IsType0 a => T.Text -> a -> Rule DHuddle +n =:= b = Named n (toType0 b) def infixl 1 =:= -(=:~) :: T.Text -> Group -> Named Group -n =:~ b = Named n b Nothing +(=:~) :: Default i => T.Text -> Group i -> Named i (Group i) +n =:~ b = Named n b def infixl 1 =:~ class IsGroupOrArrayEntry a where toGroupOrArrayEntry :: IsType0 x => x -> a -instance IsGroupOrArrayEntry ArrayEntry where +instance IsGroupOrArrayEntry (ArrayEntry DHuddle) where toGroupOrArrayEntry x = ArrayEntry { key = Nothing , value = toType0 x , quantifier = def - , aeDescription = mempty + , aeDecoration = def } -instance IsGroupOrArrayEntry Type0 where +instance IsGroupOrArrayEntry (Type0 DHuddle) where toGroupOrArrayEntry = toType0 -- | Explicitly cast an item in an Array as an ArrayEntry. @@ -757,52 +773,52 @@ class IsChoosable a b | a -> b where instance IsChoosable (Choice a) a where toChoice = id -instance IsChoosable ArrayChoice ArrayChoice where +instance IsChoosable (ArrayChoice i) (ArrayChoice i) where toChoice = NoChoice -instance IsChoosable MapChoice MapChoice where +instance IsChoosable (MapChoice DHuddle) (MapChoice DHuddle) where toChoice = NoChoice -instance IsChoosable Type2 Type2 where +instance IsChoosable (Type2 DHuddle) (Type2 DHuddle) where toChoice = NoChoice -instance IsChoosable Rule Type2 where +instance IsChoosable (Rule DHuddle) (Type2 DHuddle) where toChoice = toChoice . T2Ref -instance IsChoosable GRuleCall Type2 where +instance IsChoosable (GRuleCall DHuddle) (Type2 DHuddle) where toChoice = toChoice . T2Generic -instance IsChoosable GRef Type2 where - toChoice = toChoice . T2GenericRef +instance IsChoosable GRef (Type2 DHuddle) where + toChoice = toChoice . T2GenericRef @DHuddle -instance IsChoosable ByteString Type2 where - toChoice x = toChoice . T2Range . Unranged $ Literal (LBytes x) mempty +instance IsChoosable ByteString (Type2 DHuddle) where + toChoice x = toChoice . T2Range . Unranged . Literal (LBytes x) $ def @DHuddle -instance IsChoosable Constrained Type2 where +instance IsChoosable (Constrained DHuddle) (Type2 DHuddle) where toChoice = toChoice . T2Constrained -instance IsType0 a => IsChoosable (Tagged a) Type2 where +instance IsType0 a => IsChoosable (Tagged a) (Type2 DHuddle) where toChoice = toChoice . T2Tagged . fmap toType0 -instance IsChoosable Literal Type2 where +instance IsChoosable (Literal DHuddle) (Type2 DHuddle) where toChoice = toChoice . T2Range . Unranged -instance IsChoosable (Value a) Type2 where - toChoice = toChoice . T2Constrained . unconstrained +instance IsChoosable (Value a) (Type2 DHuddle) where + toChoice = toChoice . T2Constrained @DHuddle . unconstrained -instance IsChoosable (Named Group) Type2 where +instance IsChoosable (Named DHuddle (Group DHuddle)) (Type2 DHuddle) where toChoice = toChoice . T2Group -instance IsChoosable (Seal Array) Type2 where +instance IsChoosable (Seal (Array DHuddle)) (Type2 DHuddle) where toChoice (Seal x) = NoChoice $ T2Array x -instance IsChoosable (Seal Map) Type2 where +instance IsChoosable (Seal (Map DHuddle)) (Type2 DHuddle) where toChoice (Seal m) = NoChoice $ T2Map m -instance IsChoosable (Seal ArrayChoice) Type2 where +instance IsChoosable (Seal (ArrayChoice DHuddle)) (Type2 DHuddle) where toChoice (Seal m) = NoChoice . T2Array $ NoChoice m -instance IsChoosable (Seal MapChoice) Type2 where +instance IsChoosable (Seal (MapChoice DHuddle)) (Type2 DHuddle) where toChoice (Seal m) = NoChoice . T2Map $ NoChoice m -- | Allow choices between constructions @@ -871,21 +887,21 @@ seal = Seal -- | This function is used solely to resolve type inference by explicitly -- identifying something as an array. -arr :: ArrayChoice -> ArrayChoice +arr :: ArrayChoice DHuddle -> ArrayChoice DHuddle arr = id -- | Create and seal an array, marking it as accepting no additional choices -sarr :: ArrayChoice -> Seal Array +sarr :: ArrayChoice DHuddle -> Seal (Array DHuddle) sarr = seal . NoChoice -mp :: MapChoice -> MapChoice +mp :: MapChoice DHuddle -> MapChoice DHuddle mp = id -- | Create and seal a map, marking it as accepting no additional choices. -smp :: MapChoice -> Seal Map +smp :: MapChoice DHuddle -> Seal (Map DHuddle) smp = seal . NoChoice -grp :: Group -> Group +grp :: Group DHuddle -> Group DHuddle grp = id -------------------------------------------------------------------------------- @@ -914,17 +930,17 @@ freshName ix = T.singleton (['a' .. 'z'] !! (ix `rem` 26)) <> T.pack (show $ ix `quot` 26) -data GRule a = GRule +data GRule i a = GRule { args :: NE.NonEmpty a - , body :: Type0 + , body :: Type0 i } deriving (Show) -type GRuleCall = Named (GRule Type2) +type GRuleCall i = Named i (GRule i (Type2 i)) -type GRuleDef = Named (GRule GRef) +type GRuleDef i = Named i (GRule DHuddle GRef) -callToDef :: GRule Type2 -> GRule GRef +callToDef :: GRule i (Type2 i) -> GRule i GRef callToDef gr = gr {args = refs} where refs = @@ -937,7 +953,7 @@ callToDef gr = gr {args = refs} 0 -- | Bind a single variable into a generic call -binding :: IsType0 t0 => (GRef -> Rule) -> t0 -> GRuleCall +binding :: IsType0 t0 => (GRef -> Rule DHuddle) -> t0 -> GRuleCall DHuddle binding fRule t0 = Named (name rule) @@ -945,7 +961,7 @@ binding fRule t0 = { args = t2 NE.:| [] , body = getField @"value" rule } - Nothing + def where rule = fRule (freshName 0) t2 = case toType0 t0 of @@ -953,7 +969,8 @@ binding fRule t0 = _ -> error "Cannot use a choice of types as a generic argument" -- | Bind two variables as a generic call -binding2 :: (IsType0 t0, IsType0 t1) => (GRef -> GRef -> Rule) -> t0 -> t1 -> GRuleCall +binding2 :: + (IsType0 t0, IsType0 t1) => (GRef -> GRef -> Rule DHuddle) -> t0 -> t1 -> GRuleCall DHuddle binding2 fRule t0 t1 = Named (name rule) @@ -961,7 +978,7 @@ binding2 fRule t0 t1 = { args = t02 NE.:| [t12] , body = getField @"value" rule } - Nothing + def where rule = fRule (freshName 0) (freshName 1) t02 = case toType0 t0 of @@ -975,11 +992,11 @@ binding2 fRule t0 t1 = -- Collecting all top-level rules -------------------------------------------------------------------------------- -hiRule :: HuddleItem -> [Rule] +hiRule :: HuddleItem i -> [Rule i] hiRule (HIRule r) = [r] hiRule _ = [] -hiName :: HuddleItem -> T.Text +hiName :: HuddleItem i -> T.Text hiName (HIRule (Named n _ _)) = n hiName (HIGroup (Named n _ _)) = n hiName (HIGRule (Named n _ _)) = n @@ -987,7 +1004,7 @@ hiName (HIGRule (Named n _ _)) = n -- | Collect all rules starting from a given point. This will also insert a -- single pseudo-rule as the first element which references the specified -- top-level rules. -collectFrom :: [HuddleItem] -> Huddle +collectFrom :: [HuddleItem DHuddle] -> Huddle DHuddle collectFrom topRs = toHuddle $ execState @@ -1052,7 +1069,7 @@ collectFrom topRs = -- | Same as `collectFrom`, but the rules passed into this function will be put -- at the top of the Huddle, and all of their dependencies will be added at -- the end in depth-first order. -collectFromInit :: [HuddleItem] -> Huddle +collectFromInit :: [HuddleItem DHuddle] -> Huddle DHuddle collectFromInit rules = Huddle (concatMap hiRule rules) (OMap.fromList $ (\x -> (hiName x, x)) <$> rules) `huddleAugment` collectFrom rules @@ -1062,15 +1079,15 @@ collectFromInit rules = -------------------------------------------------------------------------------- -- | Convert from Huddle to CDDL, generating a top level root element. -toCDDL :: Huddle -> CDDL +toCDDL :: Huddle DHuddle -> CDDL DHuddle toCDDL = toCDDL' True -- | Convert from Huddle to CDDL, skipping a root element. -toCDDLNoRoot :: Huddle -> CDDL +toCDDLNoRoot :: Huddle DHuddle -> CDDL DHuddle toCDDLNoRoot = toCDDL' False -- | Convert from Huddle to CDDL for the purpose of pretty-printing. -toCDDL' :: Bool -> Huddle -> CDDL +toCDDL' :: Bool -> Huddle DHuddle -> CDDL DHuddle toCDDL' mkPseudoRoot hdl = C.fromRules $ ( if mkPseudoRoot @@ -1082,18 +1099,18 @@ toCDDL' mkPseudoRoot hdl = toCDDLItem (HIRule r) = toCDDLRule r toCDDLItem (HIGroup g) = toCDDLGroup g toCDDLItem (HIGRule g) = toGenRuleDef g - toTopLevelPseudoRoot :: [Rule] -> C.Rule + toTopLevelPseudoRoot :: [Rule DHuddle] -> C.Rule DHuddle toTopLevelPseudoRoot topRs = toCDDLRule $ comment "Pseudo-rule introduced by Cuddle to collect root elements" $ "huddle_root_defs" =:= arr (fromList (fmap a topRs)) - toCDDLRule :: Rule -> C.Rule + toCDDLRule :: Rule DHuddle -> C.Rule DHuddle toCDDLRule (Named n t0 c) = - (\x -> C.Rule (C.Name n mempty) Nothing C.AssignEq x (foldMap C.Comment c)) + (\x -> C.Rule (C.Name n mempty) Nothing C.AssignEq x c) . C.TOGType . C.Type0 $ toCDDLType1 <$> choiceToNE t0 - toCDDLValue :: Literal -> C.Value + toCDDLValue :: Literal DHuddle -> C.Value DHuddle toCDDLValue (Literal x cmt) = C.Value (toCDDLValue' x) cmt toCDDLValue' (LInt i) = C.VUInt i toCDDLValue' (LNInt i) = C.VNInt i @@ -1103,18 +1120,18 @@ toCDDL' mkPseudoRoot hdl = toCDDLValue' (LText t) = C.VText t toCDDLValue' (LBytes b) = C.VBytes b - mapToCDDLGroup :: Map -> C.Group + mapToCDDLGroup :: Map DHuddle -> C.Group DHuddle mapToCDDLGroup xs = C.Group $ mapChoiceToCDDL <$> choiceToNE xs - mapChoiceToCDDL :: MapChoice -> C.GrpChoice - mapChoiceToCDDL (MapChoice entries) = C.GrpChoice (fmap mapEntryToCDDL entries) mempty + mapChoiceToCDDL :: MapChoice DHuddle -> C.GrpChoice DHuddle + mapChoiceToCDDL (MapChoice entries) = C.GrpChoice (fmap mapEntryToCDDL entries) def - mapEntryToCDDL :: MapEntry -> C.GroupEntry + mapEntryToCDDL :: MapEntry DHuddle -> C.GroupEntry DHuddle mapEntryToCDDL (MapEntry k v occ cmnt) = C.GroupEntry (toOccurrenceIndicator occ) - cmnt (C.GEType (Just $ toMemberKey k) (toCDDLType0 v)) + (DHuddle cmnt Nothing) toOccurrenceIndicator :: Occurs -> Maybe C.OccurrenceIndicator toOccurrenceIndicator (Occurs Nothing Nothing) = Nothing @@ -1123,7 +1140,7 @@ toCDDL' mkPseudoRoot hdl = toOccurrenceIndicator (Occurs (Just 1) Nothing) = Just C.OIOneOrMore toOccurrenceIndicator (Occurs lb ub) = Just $ C.OIBounded lb ub - toCDDLType1 :: Type2 -> C.Type1 + toCDDLType1 :: Type2 DHuddle -> C.Type1 DHuddle toCDDLType1 = \case T2Constrained (Constrained x constr _) -> -- TODO Need to handle choices at the top level @@ -1133,35 +1150,35 @@ toCDDL' mkPseudoRoot hdl = C.Type1 (C.T2Map $ mapToCDDLGroup m) Nothing - mempty - T2Array x -> C.Type1 (C.T2Array $ arrayToCDDLGroup x) Nothing mempty + def + T2Array x -> C.Type1 (C.T2Array $ arrayToCDDLGroup x) Nothing def T2Tagged (Tagged mmin x) -> - C.Type1 (C.T2Tag mmin $ toCDDLType0 x) Nothing mempty - T2Ref (Named n _ _) -> C.Type1 (C.T2Name (C.Name n mempty) Nothing) Nothing mempty - T2Group (Named n _ _) -> C.Type1 (C.T2Name (C.Name n mempty) Nothing) Nothing mempty - T2Generic g -> C.Type1 (toGenericCall g) Nothing mempty - T2GenericRef (GRef n) -> C.Type1 (C.T2Name (C.Name n mempty) Nothing) Nothing mempty + C.Type1 (C.T2Tag mmin $ toCDDLType0 x) Nothing def + T2Ref (Named n _ _) -> C.Type1 (C.T2Name (C.Name n mempty) Nothing) Nothing def + T2Group (Named n _ _) -> C.Type1 (C.T2Name (C.Name n mempty) Nothing) Nothing def + T2Generic g -> C.Type1 (toGenericCall g) Nothing def + T2GenericRef (GRef n) -> C.Type1 (C.T2Name (C.Name n mempty) Nothing) Nothing def - toMemberKey :: Key -> C.MemberKey + toMemberKey :: Key DHuddle -> C.MemberKey DHuddle toMemberKey (LiteralKey (Literal (LText t) _)) = C.MKBareword (C.Name t mempty) toMemberKey (LiteralKey v) = C.MKValue $ toCDDLValue v toMemberKey (TypeKey t) = C.MKType (toCDDLType1 t) - toCDDLType0 :: Type0 -> C.Type0 + toCDDLType0 :: Type0 DHuddle -> C.Type0 DHuddle toCDDLType0 = C.Type0 . fmap toCDDLType1 . choiceToNE - arrayToCDDLGroup :: Array -> C.Group + arrayToCDDLGroup :: Array DHuddle -> C.Group DHuddle arrayToCDDLGroup xs = C.Group $ arrayChoiceToCDDL <$> choiceToNE xs - arrayChoiceToCDDL :: ArrayChoice -> C.GrpChoice + arrayChoiceToCDDL :: ArrayChoice DHuddle -> C.GrpChoice DHuddle arrayChoiceToCDDL (ArrayChoice entries cmt) = C.GrpChoice (fmap arrayEntryToCDDL entries) cmt - arrayEntryToCDDL :: ArrayEntry -> C.GroupEntry + arrayEntryToCDDL :: ArrayEntry DHuddle -> C.GroupEntry DHuddle arrayEntryToCDDL (ArrayEntry k v occ cmnt) = C.GroupEntry (toOccurrenceIndicator occ) - cmnt (C.GEType (fmap toMemberKey k) (toCDDLType0 v)) + cmnt toCDDLPostlude :: Value a -> C.Name toCDDLPostlude VBool = C.Name "bool" mempty @@ -1181,44 +1198,44 @@ toCDDL' mkPseudoRoot hdl = CRef r -> C.Name (name r) mempty CGRef (GRef n) -> C.Name n mempty - toCDDLRanged :: Ranged -> C.Type1 + toCDDLRanged :: Ranged DHuddle -> C.Type1 DHuddle toCDDLRanged (Unranged x) = - C.Type1 (C.T2Value $ toCDDLValue x) Nothing mempty + C.Type1 (C.T2Value $ toCDDLValue x) Nothing def toCDDLRanged (Ranged lb ub rop) = C.Type1 (toCDDLRangeBound lb) (Just (C.RangeOp rop, toCDDLRangeBound ub)) - mempty + def - toCDDLRangeBound :: RangeBound -> C.Type2 + toCDDLRangeBound :: RangeBound DHuddle -> C.Type2 DHuddle toCDDLRangeBound (RangeBoundLiteral l) = C.T2Value $ toCDDLValue l toCDDLRangeBound (RangeBoundRef (Named n _ _)) = C.T2Name (C.Name n mempty) Nothing - toCDDLGroup :: Named Group -> C.Rule + toCDDLGroup :: Named DHuddle (Group DHuddle) -> C.Rule DHuddle toCDDLGroup (Named n (Group t0s) c) = C.Rule (C.Name n mempty) Nothing C.AssignEq ( C.TOGGroup - . C.GroupEntry Nothing mempty + . (\x -> C.GroupEntry Nothing x def) . C.GEGroup . C.Group . (NE.:| []) - . (`C.GrpChoice` mempty) + . (`C.GrpChoice` def) $ fmap arrayEntryToCDDL t0s ) - (foldMap C.Comment c) + c - toGenericCall :: GRuleCall -> C.Type2 + toGenericCall :: GRuleCall DHuddle -> C.Type2 DHuddle toGenericCall (Named n gr _) = C.T2Name (C.Name n mempty) (Just . C.GenericArg $ fmap toCDDLType1 (args gr)) - toGenRuleDef :: GRuleDef -> C.Rule + toGenRuleDef :: GRuleDef DHuddle -> C.Rule DHuddle toGenRuleDef (Named n gr c) = C.Rule (C.Name n mempty) @@ -1228,7 +1245,7 @@ toCDDL' mkPseudoRoot hdl = . C.Type0 $ toCDDLType1 <$> choiceToNE (body gr) ) - (foldMap C.Comment c) + c where gps = C.GenericParam $ fmap (\(GRef t) -> C.Name t mempty) (args gr) diff --git a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs index c8be61a..098250e 100644 --- a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs +++ b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs @@ -24,16 +24,16 @@ import Data.Map.Ordered.Strict qualified as OMap import Data.Text qualified as T import Optics.Core (set, (%~), (^.)) -type HuddleM = State Huddle +type HuddleM i = State (Huddle i) -- | Overridden version of assignment which also adds the rule to the state -(=:=) :: IsType0 a => T.Text -> a -> HuddleM Rule +(=:=) :: IsType0 a => T.Text -> a -> HuddleM DHuddle (Rule DHuddle) n =:= b = let r = n Huddle.=:= b in include r infixl 1 =:= -- | Overridden version of group assignment which adds the rule to the state -(=:~) :: T.Text -> Group -> HuddleM (Named Group) +(=:~) :: T.Text -> Group DHuddle -> HuddleM DHuddle (Named DHuddle (Group DHuddle)) n =:~ b = let r = n Huddle.=:~ b in include r infixl 1 =:~ @@ -41,35 +41,35 @@ infixl 1 =:~ binding :: forall t0. IsType0 t0 => - (GRef -> Rule) -> - HuddleM (t0 -> GRuleCall) + (GRef -> Rule DHuddle) -> + HuddleM DHuddle (t0 -> GRuleCall DHuddle) binding fRule = include (Huddle.binding fRule) -- | Renamed version of Huddle's underlying '=:=' for use in generic bindings -(=::=) :: IsType0 a => T.Text -> a -> Rule +(=::=) :: IsType0 a => T.Text -> a -> Rule DHuddle n =::= b = n Huddle.=:= b infixl 1 =::= -setRootRules :: [Rule] -> HuddleM () +setRootRules :: [Rule DHuddle] -> HuddleM DHuddle () setRootRules = modify . set (field @"roots") -huddleDef :: HuddleM a -> Huddle +huddleDef :: HuddleM DHuddle a -> Huddle DHuddle huddleDef = snd . huddleDef' -huddleDef' :: HuddleM a -> (a, Huddle) +huddleDef' :: HuddleM DHuddle a -> (a, Huddle DHuddle) huddleDef' mh = runState mh def class Includable a where -- | Include a rule, group, or generic rule defined elsewhere - include :: a -> HuddleM a + include :: a -> HuddleM DHuddle a -instance Includable Rule where +instance Includable (Rule DHuddle) where include r = modify (field @"items" %~ (OMap.|> (r ^. field @"name", HIRule r))) >> pure r -instance Includable (Named Group) where +instance Includable (Named DHuddle (Group DHuddle)) where include r = modify ( (field @"items") @@ -77,7 +77,7 @@ instance Includable (Named Group) where ) >> pure r -instance IsType0 t0 => Includable (t0 -> GRuleCall) where +instance IsType0 t0 => Includable (t0 -> GRuleCall DHuddle) where include gr = let fakeT0 = error "Attempting to unwrap fake value in generic call" grDef = callToDef <$> gr fakeT0 @@ -86,7 +86,7 @@ instance IsType0 t0 => Includable (t0 -> GRuleCall) where modify (field @"items" %~ (OMap.|> (n, HIGRule grDef))) pure gr -instance Includable HuddleItem where +instance Includable (HuddleItem DHuddle) where include x@(HIRule r) = include r >> pure x include x@(HIGroup g) = include g >> pure x include x@(HIGRule g) = @@ -96,9 +96,9 @@ instance Includable HuddleItem where pure x unsafeIncludeFromHuddle :: - Huddle -> + Huddle DHuddle -> T.Text -> - HuddleM HuddleItem + HuddleM DHuddle (HuddleItem DHuddle) unsafeIncludeFromHuddle h name = let items = h ^. field @"items" in case OMap.lookup name items of diff --git a/src/Codec/CBOR/Cuddle/Huddle/Optics.hs b/src/Codec/CBOR/Cuddle/Huddle/Optics.hs deleted file mode 100644 index 601d95b..0000000 --- a/src/Codec/CBOR/Cuddle/Huddle/Optics.hs +++ /dev/null @@ -1,26 +0,0 @@ -{-# LANGUAGE DataKinds #-} - --- | Optics for mutating Huddle rules -module Codec.CBOR.Cuddle.Huddle.Optics (commentL, nameL) where - -import Codec.CBOR.Cuddle.Huddle -import Data.Generics.Product (HasField' (field')) -import Data.Text qualified as T -import Optics.Core - -mcommentL :: - HasField' "description" a (Maybe T.Text) => - Lens a a (Maybe T.Text) (Maybe T.Text) -mcommentL = field' @"description" - --- | Traversal to the comment field of a description. Using this we can for --- example set the comment with 'a & commentL .~ "This is a comment"' -commentL :: - HasField' "description" a (Maybe T.Text) => - AffineTraversal a a T.Text T.Text -commentL = mcommentL % _Just - --- | Lens to the name of a rule (or other named entity). Using this we can --- for example append to the name with 'a & nameL %~ (<> "_1")' -nameL :: Lens (Named a) (Named a) T.Text T.Text -nameL = field' @"name" diff --git a/src/Codec/CBOR/Cuddle/Parser.hs b/src/Codec/CBOR/Cuddle/Parser.hs index f21f3f3..195d518 100644 --- a/src/Codec/CBOR/Cuddle/Parser.hs +++ b/src/Codec/CBOR/Cuddle/Parser.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} @@ -28,7 +29,7 @@ import Text.Megaparsec.Char hiding (space) import Text.Megaparsec.Char qualified as C import Text.Megaparsec.Char.Lexer qualified as L -pCDDL :: Parser CDDL +pCDDL :: Parser (CDDL Comment) pCDDL = do initialComments <- many (try $ C.space *> pCommentBlock <* notFollowedBy pRule) initialRuleComment <- C.space *> optional pCommentBlock @@ -36,7 +37,7 @@ pCDDL = do cddlTail <- many $ pTopLevel <* C.space eof $> CDDL initialComments (initialRule //- fold initialRuleComment) cddlTail -pTopLevel :: Parser TopLevel +pTopLevel :: Parser (TopLevel Comment) pTopLevel = try tlRule <|> tlComment where tlRule = do @@ -45,7 +46,7 @@ pTopLevel = try tlRule <|> tlComment pure . TopLevelRule $ rule //- fold mCmt tlComment = TopLevelComment <$> pCommentBlock -pRule :: Parser Rule +pRule :: Parser (Rule Comment) pRule = do name <- pName genericParam <- optcomp pGenericParam @@ -94,15 +95,15 @@ pGenericParam = GenericParam <$> between "<" ">" (NE.sepBy1 (space !*> pName <*! space) ",") -pGenericArg :: Parser GenericArg +pGenericArg :: Parser (GenericArg Comment) pGenericArg = GenericArg <$> between "<" ">" (NE.sepBy1 (space !*> pType1 <*! space) ",") -pType0 :: Parser Type0 +pType0 :: Parser (Type0 Comment) pType0 = Type0 <$> sepBy1' (space !*> pType1 <*! space) (try "/") -pType1 :: Parser Type1 +pType1 :: Parser (Type1 Comment) pType1 = do v <- pType2 rest <- optional $ do @@ -118,7 +119,7 @@ pType1 = do pure $ Type1 v (Just (tyOp, w)) $ cmtFst <> cmtSnd Nothing -> pure $ Type1 v Nothing mempty -pType2 :: Parser Type2 +pType2 :: Parser (Type2 Comment) pType2 = choice [ T2Value <$> pValue @@ -176,13 +177,13 @@ pCtlOp = ] ) -pGroup :: Parser Group +pGroup :: Parser (Group Comment) pGroup = Group <$> NE.sepBy1 (space !*> pGrpChoice) "//" -pGrpChoice :: Parser GrpChoice +pGrpChoice :: Parser (GrpChoice Comment) pGrpChoice = GrpChoice <$> many (space !*> pGrpEntry <*! pOptCom) <*> mempty -pGrpEntry :: Parser GroupEntry +pGrpEntry :: Parser (GroupEntry Comment) pGrpEntry = do occur <- optcomp pOccur cmt <- space @@ -195,9 +196,9 @@ pGrpEntry = do , try $ withComment <$> (GERef <$> pName <*> optional pGenericArg) , withComment . GEGroup <$> ("(" *> space !*> pGroup <*! space <* ")") ] - pure $ GroupEntry occur (cmt <> cmt') variant + pure $ GroupEntry occur variant (cmt <> cmt') -pMemberKey :: Parser (WithComment MemberKey) +pMemberKey :: Parser (WithComment (MemberKey Comment)) pMemberKey = choice [ try $ do @@ -229,7 +230,7 @@ pOccur = , pBounded ] -pValue :: Parser Value +pValue :: Parser (Value Comment) pValue = label "value" $ (`Value` mempty) diff --git a/src/Codec/CBOR/Cuddle/Pretty.hs b/src/Codec/CBOR/Cuddle/Pretty.hs index 0e8b1ba..5534ff7 100644 --- a/src/Codec/CBOR/Cuddle/Pretty.hs +++ b/src/Codec/CBOR/Cuddle/Pretty.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} @@ -28,10 +29,10 @@ import Data.String (fromString) import Data.Text qualified as T import Prettyprinter -instance Pretty CDDL where +instance Pretty (CDDL Comment) where pretty = vsep . fmap pretty . NE.toList . cddlTopLevel -instance Pretty TopLevel where +instance Pretty (TopLevel Comment) where pretty (TopLevelComment cmt) = pretty cmt pretty (TopLevelRule x) = pretty x <> hardline @@ -54,10 +55,10 @@ instance Pretty Comment where pretty (Comment "") = mempty pretty c = prettyCommentNoBreak c <> hardline -type0Def :: Type0 -> Doc ann +type0Def :: Type0 Comment -> Doc ann type0Def t = nest 2 $ line' <> pretty t -instance Pretty Rule where +instance Pretty (Rule Comment) where pretty (Rule n mgen assign tog cmt) = pretty cmt <> groupIfNoComments @@ -74,7 +75,7 @@ instance Pretty Rule where AssignEq -> "=" AssignExt -> "//=" -instance Pretty GenericArg where +instance Pretty (GenericArg Comment) where pretty (GenericArg (NE.toList -> l)) | null (collectComments l) = group . cEncloseSep "<" ">" "," $ fmap pretty l | otherwise = columnarListing "<" ">" "," . Columnar $ singletonRow . pretty <$> l @@ -84,7 +85,7 @@ instance Pretty GenericParam where | null (collectComments l) = group . cEncloseSep "<" ">" "," $ fmap pretty l | otherwise = columnarListing "<" ">" "," . Columnar $ singletonRow . pretty <$> l -instance Pretty Type0 where +instance Pretty (Type0 Comment) where pretty t0@(Type0 (NE.toList -> l)) = groupIfNoComments t0 $ columnarSepBy "/" . Columnar $ type1ToRow <$> l where @@ -104,7 +105,7 @@ instance Pretty TyOp where pretty (RangeOp Closed) = ".." pretty (CtrlOp n) = "." <> pretty n -instance Pretty Type1 where +instance Pretty (Type1 Comment) where pretty (Type1 t2 Nothing cmt) = groupIfNoComments t2 (pretty t2) <> prettyCommentNoBreakWS cmt pretty (Type1 t2 (Just (tyop, t2')) cmt) = groupIfNoComments t2 (pretty t2) @@ -112,7 +113,7 @@ instance Pretty Type1 where <+> groupIfNoComments t2' (pretty t2') <> prettyCommentNoBreakWS cmt -instance Pretty Type2 where +instance Pretty (Type2 Comment) where pretty (T2Value v) = pretty v pretty (T2Name n mg) = pretty n <> pretty mg pretty (T2Group g) = cEncloseSep "(" ")" mempty [pretty g] @@ -144,7 +145,7 @@ data GroupRender | AsArray | AsGroup -memberKeySep :: MemberKey -> Doc ann +memberKeySep :: MemberKey Comment -> Doc ann memberKeySep MKType {} = " => " memberKeySep _ = " : " @@ -165,10 +166,10 @@ groupIfNoComments x | not (any (mempty /=) $ collectComments x) = group | otherwise = id -columnarGroupChoice :: GrpChoice -> Columnar ann +columnarGroupChoice :: GrpChoice Comment -> Columnar ann columnarGroupChoice (GrpChoice ges _cmt) = Columnar grpEntryRows where - groupEntryRow (GroupEntry oi cmt gev) = + groupEntryRow (GroupEntry oi gev cmt) = Row $ [maybe emptyCell (\x -> Cell (pretty x <> space) LeftAlign) oi] <> groupEntryVariantCells gev @@ -179,7 +180,7 @@ columnarGroupChoice (GrpChoice ges _cmt) = Columnar grpEntryRows groupEntryVariantCells (GEGroup g) = [Cell (prettyGroup AsGroup g) LeftAlign, emptyCell] grpEntryRows = groupEntryRow <$> ges -prettyGroup :: GroupRender -> Group -> Doc ann +prettyGroup :: GroupRender -> Group Comment -> Doc ann prettyGroup gr g@(Group (toList -> xs)) = groupIfNoComments g . columnarListing (lEnc <> softspace) rEnc "// " . Columnar $ (\x -> singletonRow . groupIfNoComments x . columnarSepBy "," $ columnarGroupChoice x) <$> xs @@ -189,15 +190,15 @@ prettyGroup gr g@(Group (toList -> xs)) = AsArray -> ("[", "]") AsGroup -> ("(", ")") -instance Pretty GroupEntry where +instance Pretty (GroupEntry Comment) where pretty ge = prettyColumnar . columnarGroupChoice $ GrpChoice [ge] mempty -instance Pretty MemberKey where +instance Pretty (MemberKey Comment) where pretty (MKType t1) = pretty t1 pretty (MKBareword n) = pretty n pretty (MKValue v) = pretty v -instance Pretty Value where +instance Pretty (Value Comment) where pretty (Value v cmt) = pretty v <> prettyCommentNoBreakWS cmt instance Pretty ValueVariant where diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs index d0dc8cc..954768c 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs @@ -4,6 +4,7 @@ import Codec.CBOR.Cuddle.CDDL.Prelude (prependPrelude) import Codec.CBOR.Cuddle.CDDL.Resolve (fullResolveCDDL) import Codec.CBOR.Cuddle.Parser (pCDDL) import Data.Either (isRight) +import Data.Functor (($>)) import Data.Text.IO qualified as T import Test.Hspec import Text.Megaparsec (parse) @@ -15,7 +16,7 @@ validateFile filePath = it ("Successfully validates " <> filePath) $ do cddl <- case parse pCDDL "" contents of Right x -> pure $ prependPrelude x Left x -> fail $ "Failed to parse the file:\n" <> errorBundlePretty x - fullResolveCDDL cddl `shouldSatisfy` isRight + fullResolveCDDL (cddl $> ()) `shouldSatisfy` isRight spec :: Spec spec = do diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs index d907b3f..be6079c 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-orphans #-} @@ -15,17 +16,16 @@ import Data.Text qualified as T import Test.QuickCheck import Test.QuickCheck qualified as Gen -instance Arbitrary CDDL where +instance Arbitrary i => Arbitrary (CDDL i) where arbitrary = CDDL <$> arbitrary <*> arbitrary <*> arbitrary shrink = genericShrink -instance Arbitrary TopLevel where +instance Arbitrary i => Arbitrary (TopLevel i) where arbitrary = Gen.oneof [ TopLevelComment <$> arbitrary , TopLevelRule <$> arbitrary ] - shrink = genericShrink instance Arbitrary T.Text where arbitrary = T.pack <$> arbitrary @@ -77,11 +77,11 @@ instance Arbitrary GenericParam where arbitrary = GenericParam <$> nonEmpty arbitrary shrink (GenericParam neName) = GenericParam <$> shrinkNE neName -instance Arbitrary GenericArg where +instance Arbitrary i => Arbitrary (GenericArg i) where arbitrary = GenericArg <$> nonEmpty arbitrary shrink (GenericArg neArg) = GenericArg <$> shrinkNE neArg -instance Arbitrary Rule where +instance Arbitrary i => Arbitrary (Rule i) where arbitrary = Rule <$> arbitrary @@ -89,7 +89,6 @@ instance Arbitrary Rule where <*> arbitrary <*> arbitrary <*> arbitrary - shrink = genericShrink instance Arbitrary RangeBound where arbitrary = Gen.elements [ClOpen, Closed] @@ -103,7 +102,7 @@ instance Arbitrary TyOp where ] shrink = genericShrink -instance Arbitrary TypeOrGroup where +instance Arbitrary i => Arbitrary (TypeOrGroup i) where arbitrary = Gen.oneof [ TOGGroup <$> arbitrary @@ -111,15 +110,14 @@ instance Arbitrary TypeOrGroup where ] shrink = genericShrink -instance Arbitrary Type0 where +instance Arbitrary i => Arbitrary (Type0 i) where arbitrary = Type0 <$> nonEmpty arbitrary shrink (Type0 neType1) = Type0 <$> shrinkNE neType1 -instance Arbitrary Type1 where +instance Arbitrary i => Arbitrary (Type1 i) where arbitrary = Type1 <$> arbitrary <*> arbitrary <*> arbitrary - shrink = genericShrink -instance Arbitrary Type2 where +instance Arbitrary i => Arbitrary (Type2 i) where arbitrary = recursive Gen.oneof @@ -153,15 +151,14 @@ instance Arbitrary OccurrenceIndicator where shrink = genericShrink -instance Arbitrary Group where +instance Arbitrary i => Arbitrary (Group i) where arbitrary = Group <$> nonEmpty arbitrary shrink (Group gr) = Group <$> shrinkNE gr -instance Arbitrary GrpChoice where - arbitrary = GrpChoice <$> listOf' arbitrary <*> pure mempty - shrink = genericShrink +instance Arbitrary i => Arbitrary (GrpChoice i) where + arbitrary = GrpChoice <$> listOf' arbitrary <*> arbitrary -instance Arbitrary GroupEntryVariant where +instance Arbitrary i => Arbitrary (GroupEntryVariant i) where arbitrary = recursive Gen.oneof @@ -176,15 +173,14 @@ instance Arbitrary GroupEntryVariant where ] shrink = genericShrink -instance Arbitrary GroupEntry where +instance Arbitrary i => Arbitrary (GroupEntry i) where arbitrary = GroupEntry <$> arbitrary - <*> pure mempty <*> arbitrary - shrink = genericShrink + <*> arbitrary -instance Arbitrary MemberKey where +instance Arbitrary i => Arbitrary (MemberKey i) where arbitrary = recursive Gen.oneof @@ -196,7 +192,7 @@ instance Arbitrary MemberKey where shrink = genericShrink -instance Arbitrary Value where +instance Arbitrary i => Arbitrary (Value i) where arbitrary = Value <$> arbitrary <*> arbitrary shrink = genericShrink diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs index 53d777e..a7c8b10 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs @@ -179,7 +179,7 @@ type2Spec = describe "type2" $ do { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType ( Just @@ -187,7 +187,7 @@ type2Spec = describe "type2" $ do ( Type1 { t1Main = T2Name (Name {name = "int", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } ) ) @@ -197,14 +197,14 @@ type2Spec = describe "type2" $ do Type1 { t1Main = T2Name (Name {name = "string", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } :| [] } @@ -218,7 +218,7 @@ type2Spec = describe "type2" $ do { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Just OIZeroOrMore - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType ( Just @@ -226,7 +226,7 @@ type2Spec = describe "type2" $ do ( Type1 { t1Main = T2Name (Name {name = "int", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } ) ) @@ -236,14 +236,14 @@ type2Spec = describe "type2" $ do Type1 { t1Main = T2Name (Name {name = "string", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } :| [] } @@ -257,18 +257,18 @@ type2Spec = describe "type2" $ do { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType ( Just - (MKType (Type1 {t1Main = T2Value (value $ VUInt 1), t1TyOp = Nothing, t1Comment = Comment mempty})) + (MKType (Type1 {t1Main = T2Value (value $ VUInt 1), t1TyOp = Nothing, t1Decoration = Comment mempty})) ) ( Type0 { t0Type1 = Type1 { t1Main = T2Name (Name {name = "string", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } @@ -276,18 +276,18 @@ type2Spec = describe "type2" $ do } , GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType ( Just - (MKType (Type1 {t1Main = T2Value (value $ VUInt 2), t1TyOp = Nothing, t1Comment = Comment mempty})) + (MKType (Type1 {t1Main = T2Value (value $ VUInt 2), t1TyOp = Nothing, t1Decoration = Comment mempty})) ) ( Type0 { t0Type1 = Type1 { t1Main = T2Name (Name {name = "int", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } @@ -295,25 +295,25 @@ type2Spec = describe "type2" $ do } , GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType ( Just - (MKType (Type1 {t1Main = T2Value (value $ VUInt 3), t1TyOp = Nothing, t1Comment = Comment mempty})) + (MKType (Type1 {t1Main = T2Value (value $ VUInt 3), t1TyOp = Nothing, t1Decoration = Comment mempty})) ) ( Type0 { t0Type1 = Type1 { t1Main = T2Name (Name {name = "bytes", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } :| [] } @@ -328,7 +328,7 @@ type2Spec = describe "type2" $ do { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing @@ -337,20 +337,20 @@ type2Spec = describe "type2" $ do Type1 { t1Main = T2Name (Name {name = "int", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } :| [ GrpChoice { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing @@ -359,14 +359,14 @@ type2Spec = describe "type2" $ do Type1 { t1Main = T2Name (Name {name = "string", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } ] } @@ -381,35 +381,35 @@ type2Spec = describe "type2" $ do { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing ( Type0 { t0Type1 = - Type1 {t1Main = T2Value (value $ VUInt 0), t1TyOp = Nothing, t1Comment = Comment mempty} :| [] + Type1 {t1Main = T2Value (value $ VUInt 0), t1TyOp = Nothing, t1Decoration = Comment mempty} :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } :| [ GrpChoice { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing ( Type0 { t0Type1 = - Type1 {t1Main = T2Value (value $ VUInt 1), t1TyOp = Nothing, t1Comment = Comment mempty} :| [] + Type1 {t1Main = T2Value (value $ VUInt 1), t1TyOp = Nothing, t1Decoration = Comment mempty} :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } ] } @@ -423,18 +423,18 @@ type2Spec = describe "type2" $ do { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing ( Type0 { t0Type1 = - Type1 {t1Main = T2Value (value $ VUInt 1), t1TyOp = Nothing, t1Comment = Comment mempty} :| [] + Type1 {t1Main = T2Value (value $ VUInt 1), t1TyOp = Nothing, t1Decoration = Comment mempty} :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } :| [] } @@ -448,19 +448,19 @@ type2Spec = describe "type2" $ do { gcGroupEntries = [ GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing ( Type0 { t0Type1 = - Type1 {t1Main = T2Value (value $ VUInt 2), t1TyOp = Nothing, t1Comment = Comment mempty} :| [] + Type1 {t1Main = T2Value (value $ VUInt 2), t1TyOp = Nothing, t1Decoration = Comment mempty} :| [] } ) } , GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing @@ -469,14 +469,14 @@ type2Spec = describe "type2" $ do Type1 { t1Main = T2Name (Name {name = "soon", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } ) } ] - , gcComment = Comment mempty + , gcDecoration = Comment mempty } :| [] } @@ -488,7 +488,7 @@ grpEntrySpec = describe "GroupEntry" $ do parse pGrpEntry "" "int" `shouldParse` GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing @@ -497,7 +497,7 @@ grpEntrySpec = describe "GroupEntry" $ do Type1 { t1Main = T2Name (Name {name = "int", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } @@ -507,7 +507,7 @@ grpEntrySpec = describe "GroupEntry" $ do parse pGrpEntry "" "int // notConsideredHere" `shouldParse` GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing @@ -516,7 +516,7 @@ grpEntrySpec = describe "GroupEntry" $ do Type1 { t1Main = T2Name (Name {name = "int", nameComment = Comment mempty}) Nothing , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } @@ -526,7 +526,7 @@ grpEntrySpec = describe "GroupEntry" $ do parse pGrpEntry "" "a<0 ... #6(0)>" `shouldParse` GroupEntry { geOccurrenceIndicator = Nothing - , geComment = Comment mempty + , geDecoration = Comment mempty , geVariant = GEType Nothing @@ -547,18 +547,18 @@ grpEntrySpec = describe "GroupEntry" $ do Nothing ( Type0 { t0Type1 = - Type1 {t1Main = T2Value (value $ VUInt 0), t1TyOp = Nothing, t1Comment = Comment mempty} :| [] + Type1 {t1Main = T2Value (value $ VUInt 0), t1TyOp = Nothing, t1Decoration = Comment mempty} :| [] } ) ) - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] ) ) ) , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } :| [] } @@ -568,28 +568,31 @@ grpEntrySpec = describe "GroupEntry" $ do parse pGrpEntry "" "0* a" `shouldParse` GroupEntry (Just (OIBounded (Just 0) Nothing)) - def ( GEType Nothing (Type0 (Type1 (T2Name (Name "a" mempty) Nothing) Nothing mempty :| [])) ) + def grpChoiceSpec :: SpecWith () grpChoiceSpec = describe "GroupChoice" $ do it "Should parse part of a group alternative" $ parse pGrpChoice "" "int // string" `shouldParse` GrpChoice - [ GroupEntry Nothing mempty $ - GEType - Nothing - ( Type0 - ( Type1 - (T2Name (Name "int" mempty) Nothing) - Nothing - mempty - :| [] - ) - ) + [ GroupEntry + Nothing + ( GEType + Nothing + ( Type0 + ( Type1 + (T2Name (Name "int" mempty) Nothing) + Nothing + mempty + :| [] + ) + ) + ) + mempty ] mempty @@ -629,7 +632,7 @@ qcFoundSpec = Type1 { t1Main = T2Map - (Group {unGroup = GrpChoice {gcGroupEntries = [], gcComment = Comment mempty} :| []}) + (Group {unGroup = GrpChoice {gcGroupEntries = [], gcDecoration = Comment mempty} :| []}) , t1TyOp = Just ( CtrlOp CtlOp.Ge @@ -640,16 +643,16 @@ qcFoundSpec = ( Type1 { t1Main = T2Map - (Group {unGroup = GrpChoice {gcGroupEntries = [], gcComment = Comment mempty} :| []}) + (Group {unGroup = GrpChoice {gcGroupEntries = [], gcDecoration = Comment mempty} :| []}) , t1TyOp = Nothing - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } - :| [Type1 {t1Main = T2Value (value $ VUInt 3), t1TyOp = Nothing, t1Comment = Comment mempty}] + :| [Type1 {t1Main = T2Value (value $ VUInt 3), t1TyOp = Nothing, t1Decoration = Comment mempty}] ) ) ) ) - , t1Comment = Comment mempty + , t1Decoration = Comment mempty } parseExample "S = 0* ()" pRule $ Rule @@ -657,8 +660,10 @@ qcFoundSpec = Nothing AssignEq ( TOGGroup - ( GroupEntry (Just (OIBounded (Just 0) Nothing)) mempty $ - GEGroup (Group (GrpChoice mempty mempty :| [])) + ( GroupEntry + (Just (OIBounded (Just 0) Nothing)) + (GEGroup (Group (GrpChoice mempty mempty :| []))) + mempty ) ) mempty @@ -672,10 +677,11 @@ qcFoundSpec = ( TOGGroup ( GroupEntry Nothing + ( GEType + (Just (MKValue (value $ VText "6 ybe2ddl8frq0vqa8zgrk07khrljq7p plrufpd1sff3p95"))) + (Type0 (Type1 (T2Value (value $ VText "u")) Nothing mempty :| [])) + ) mempty - $ GEType - (Just (MKValue (value $ VText "6 ybe2ddl8frq0vqa8zgrk07khrljq7p plrufpd1sff3p95"))) - (Type0 (Type1 (T2Value (value $ VText "u")) Nothing mempty :| [])) ) ) mempty diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs index 3a5a354..6d87b10 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoImplicitPrelude #-} @@ -22,6 +23,7 @@ import Codec.CBOR.Cuddle.CDDL ( ValueVariant (..), value, ) +import Codec.CBOR.Cuddle.Comments (Comment) import Codec.CBOR.Cuddle.Pretty () import Data.List.NonEmpty (NonEmpty (..)) import Data.Text qualified as T @@ -40,13 +42,13 @@ prettyPrintsTo x s = assertEqual (show . prettyExpr $ toExpr x) s rendered where rendered = renderString (layoutPretty defaultLayoutOptions (pretty x)) -t2Name :: Type2 +t2Name :: Type2 Comment t2Name = T2Name (Name "a" mempty) mempty -t1Name :: Type1 +t1Name :: Type1 Comment t1Name = Type1 t2Name Nothing mempty -mkType0 :: Type2 -> Type0 +mkType0 :: Type2 Comment -> Type0 Comment mkType0 t2 = Type0 $ Type1 t2 Nothing mempty :| [] spec :: Spec @@ -56,14 +58,14 @@ spec = describe "Pretty printer" $ do qcSpec :: Spec qcSpec = describe "QuickCheck" $ do - xprop "CDDL prettyprinter leaves no trailing spaces" $ \(cddl :: CDDL) -> do + xprop "CDDL prettyprinter leaves no trailing spaces" $ \(cddl :: CDDL Comment) -> do let prettyStr = T.pack . renderString . layoutPretty defaultLayoutOptions $ pretty cddl stripLines = T.unlines . fmap T.stripEnd . T.lines counterexample (show . prettyExpr $ toExpr cddl) $ prettyStr `shouldBe` stripLines prettyStr -drep :: Rule +drep :: Rule Comment drep = Rule "drep" @@ -77,37 +79,37 @@ drep = ( GrpChoice [ GroupEntry Nothing - mempty (GEType Nothing (Type0 $ Type1 (T2Value . value $ VUInt 0) Nothing mempty :| [])) + mempty , GroupEntry Nothing - mempty (GEType Nothing (Type0 $ Type1 (T2Name "addr_keyhash" Nothing) Nothing mempty :| [])) + mempty ] mempty :| [ GrpChoice [ GroupEntry Nothing - mempty (GEType Nothing (Type0 $ Type1 (T2Value . value $ VUInt 1) Nothing mempty :| [])) + mempty , GroupEntry Nothing - mempty (GEType Nothing (Type0 $ Type1 (T2Name "script_hash" Nothing) Nothing mempty :| [])) + mempty ] mempty , GrpChoice [ GroupEntry Nothing - mempty (GEType Nothing (Type0 $ Type1 (T2Value . value $ VUInt 2) Nothing mempty :| [])) + mempty ] mempty , GrpChoice [ GroupEntry Nothing - mempty (GEType Nothing (Type0 $ Type1 (T2Value . value $ VUInt 3) Nothing mempty :| [])) + mempty ] mempty ] @@ -133,14 +135,14 @@ unitSpec = describe "HUnit" $ do describe "Type2" $ do it "T2Name" $ t2Name `prettyPrintsTo` "a" describe "T2Array" $ do - let groupEntryName = GroupEntry Nothing mempty $ GERef (Name "a" mempty) Nothing + let groupEntryName = GroupEntry @Comment Nothing (GERef (Name "a" mempty) Nothing) mempty it "one element" $ T2Array (Group (GrpChoice [groupEntryName] mempty :| [])) `prettyPrintsTo` "[a]" it "two elements" $ T2Array ( Group ( GrpChoice - [ GroupEntry Nothing mempty $ GEType Nothing (mkType0 . T2Value . value $ VUInt 1) + [ GroupEntry Nothing (GEType Nothing (mkType0 . T2Value . value $ VUInt 1)) mempty , groupEntryName ] mempty @@ -152,8 +154,8 @@ unitSpec = describe "HUnit" $ do T2Array ( Group ( GrpChoice - [ GroupEntry Nothing "one" $ GEType Nothing (mkType0 . T2Value . value $ VUInt 1) - , GroupEntry Nothing "two" $ GEType Nothing (mkType0 . T2Value . value $ VUInt 2) + [ GroupEntry @Comment Nothing (GEType Nothing (mkType0 . T2Value . value $ VUInt 1)) "one" + , GroupEntry @Comment Nothing (GEType Nothing (mkType0 . T2Value . value $ VUInt 2)) "two" ] mempty :| [] @@ -164,9 +166,14 @@ unitSpec = describe "HUnit" $ do T2Array ( Group ( GrpChoice - [ GroupEntry Nothing "first\nmultiline comment" $ GEType Nothing (mkType0 . T2Value . value $ VUInt 1) - , GroupEntry Nothing "second\nmultiline comment" $ - GEType Nothing (mkType0 . T2Value . value $ VUInt 2) + [ GroupEntry + Nothing + (GEType Nothing (mkType0 . T2Value . value $ VUInt 1)) + "first\nmultiline comment" + , GroupEntry + Nothing + (GEType Nothing (mkType0 . T2Value . value $ VUInt 2)) + "second\nmultiline comment" ] mempty :| [] @@ -179,7 +186,7 @@ unitSpec = describe "HUnit" $ do (Name "a" mempty) Nothing AssignEq - (TOGType (Type0 (Type1 (T2Name (Name "b" mempty) mempty) Nothing mempty :| []))) + (TOGType (Type0 (Type1 @Comment (T2Name (Name "b" mempty) mempty) Nothing mempty :| []))) mempty `prettyPrintsTo` "a = b" xit "drep" $ diff --git a/test/Test/Codec/CBOR/Cuddle/Huddle.hs b/test/Test/Codec/CBOR/Cuddle/Huddle.hs index 06a0dbf..4c8dccc 100644 --- a/test/Test/Codec/CBOR/Cuddle/Huddle.hs +++ b/test/Test/Codec/CBOR/Cuddle/Huddle.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} @@ -6,6 +7,7 @@ module Test.Codec.CBOR.Cuddle.Huddle where import Codec.CBOR.Cuddle.CDDL (CDDL, sortCDDL) +import Codec.CBOR.Cuddle.Comments (Comment) import Codec.CBOR.Cuddle.Huddle import Codec.CBOR.Cuddle.Parser import Data.Text qualified as T @@ -29,37 +31,37 @@ huddleSpec = describe "huddle" $ do basicAssign :: Spec basicAssign = describe "basic assignment" $ do it "Can assign a primitive" $ - toSortedCDDL ["port" =:= VUInt] + toSortedCDDLPretty ["port" =:= VUInt] `shouldMatchParseCDDL` "port = uint" it "Can assign an int" $ - toSortedCDDL ["one" =:= (int 1)] + toSortedCDDLPretty ["one" =:= (int 1)] `shouldMatchParseCDDL` "one = 1" -- it "Can assign a float" $ - -- toSortedCDDL ["onepointone" =:= (1.1 :: Float)] + -- toSortedCDDLPretty ["onepointone" =:= (1.1 :: Float)] -- `shouldMatchParseCDDL` "onepointone = 1.1" it "Can assign a text string" $ - toSortedCDDL ["hello" =:= ("Hello World" :: T.Text)] + toSortedCDDLPretty ["hello" =:= ("Hello World" :: T.Text)] `shouldMatchParseCDDL` "hello = \"Hello World\"" it "Can handle multiple assignments" $ - toSortedCDDL ["age" =:= VUInt, "location" =:= VText] + toSortedCDDLPretty ["age" =:= VUInt, "location" =:= VText] `shouldMatchParseCDDL` "age = uint\n location = text" arraySpec :: Spec arraySpec = describe "Arrays" $ do it "Can assign a small array" $ - toSortedCDDL ["asl" =:= arr [a VUInt, a VBool, a VText]] + toSortedCDDLPretty ["asl" =:= arr [a VUInt, a VBool, a VText]] `shouldMatchParseCDDL` "asl = [ uint, bool, text ]" it "Can quantify an upper bound" $ - toSortedCDDL ["age" =:= arr [a VUInt +> 64]] + toSortedCDDLPretty ["age" =:= arr [a VUInt +> 64]] `shouldMatchParseCDDL` "age = [ *64 uint ]" it "Can quantify an optional" $ - toSortedCDDL ["age" =:= arr [0 <+ a VUInt +> 1]] + toSortedCDDLPretty ["age" =:= arr [0 <+ a VUInt +> 1]] `shouldMatchParseCDDL` "age = [ ? uint ]" it "Can handle a choice" $ - toSortedCDDL ["ageOrSex" =:= arr [a VUInt] / arr [a VBool]] + toSortedCDDLPretty ["ageOrSex" =:= arr [a VUInt] / arr [a VBool]] `shouldMatchParseCDDL` "ageOrSex = [ uint // bool ]" it "Can handle choices of groups" $ - toSortedCDDL + toSortedCDDLPretty [ "asl" =:= arr [a VUInt, a VBool, a VText] / arr @@ -72,35 +74,35 @@ arraySpec = describe "Arrays" $ do mapSpec :: Spec mapSpec = describe "Maps" $ do it "Can assign a small map" $ - toSortedCDDL ["asl" =:= mp ["age" ==> VUInt, "sex" ==> VBool, "location" ==> VText]] + toSortedCDDLPretty ["asl" =:= mp ["age" ==> VUInt, "sex" ==> VBool, "location" ==> VText]] `shouldMatchParseCDDL` "asl = { age : uint, sex : bool, location : text }" it "Can quantify a lower bound" $ - toSortedCDDL ["age" =:= mp [0 <+ "years" ==> VUInt]] + toSortedCDDLPretty ["age" =:= mp [0 <+ "years" ==> VUInt]] `shouldMatchParseCDDL` "age = { * years : uint }" it "Can quantify an upper bound" $ - toSortedCDDL ["age" =:= mp ["years" ==> VUInt +> 64]] + toSortedCDDLPretty ["age" =:= mp ["years" ==> VUInt +> 64]] `shouldMatchParseCDDL` "age = { *64 years : uint }" it "Can handle a choice" $ - toSortedCDDL ["ageOrSex" =:= mp ["age" ==> VUInt] / mp ["sex" ==> VBool]] + toSortedCDDLPretty ["ageOrSex" =:= mp ["age" ==> VUInt] / mp ["sex" ==> VBool]] `shouldMatchParseCDDL` "ageOrSex = { age : uint // sex : bool }" it "Can handle a choice with an entry" $ - toSortedCDDL ["mir" =:= arr [a (int 0 / int 1), a $ mp [0 <+ "test" ==> VUInt]]] + toSortedCDDLPretty ["mir" =:= arr [a (int 0 / int 1), a $ mp [0 <+ "test" ==> VUInt]]] `shouldMatchParseCDDL` "mir = [ 0 / 1, { * test : uint }]" grpSpec :: Spec grpSpec = describe "Groups" $ do it "Can handle a choice in a group entry" $ let g1 = "g1" =:~ grp [a (VUInt / VBytes), a VUInt] - in toSortedCDDL (collectFrom [HIRule $ "a1" =:= arr [a g1]]) + in toSortedCDDLPretty (collectFrom [HIRule $ "a1" =:= arr [a g1]]) `shouldMatchParseCDDL` "a1 = [g1]\n g1 = ( uint / bytes, uint )" it "Can handle keys in a group entry" $ let g1 = "g1" =:~ grp ["bytes" ==> VBytes] - in toSortedCDDL (collectFrom [HIRule $ "a1" =:= arr [a g1]]) + in toSortedCDDLPretty (collectFrom [HIRule $ "a1" =:= arr [a g1]]) `shouldMatchParseCDDL` "a1 = [g1]\n g1 = (bytes : bytes)" -- it "Can handle a group in a map" $ -- let g1 = "g1" =:~ grp ["bytes"==> VBytes] --- in toSortedCDDL (collectFrom ["a1" =:= mp [g1]]) +-- in toSortedCDDLPretty (collectFrom ["a1" =:= mp [g1]]) -- `shouldMatchParseCDDL` "a1 = [g1]\n g1 = (bytes : bytes)" nestedSpec :: Spec @@ -108,7 +110,7 @@ nestedSpec = describe "Nesting" $ it "Handles references" $ let headerBody = "header_body" =:= arr ["block_number" ==> VUInt, "slot" ==> VUInt] - in toSortedCDDL + in toSortedCDDLPretty [ headerBody , "header" =:= arr [a headerBody, "body_signature" ==> VBytes] ] @@ -117,29 +119,29 @@ nestedSpec = genericSpec :: Spec genericSpec = describe "Generics" $ - let set :: IsType0 t0 => t0 -> GRuleCall + let set :: IsType0 t0 => t0 -> GRuleCall DHuddle set = binding $ \x -> "set" =:= arr [0 <+ a x] - dict :: (IsType0 t0, IsType0 t1) => t0 -> t1 -> GRuleCall + dict :: (IsType0 t0, IsType0 t1) => t0 -> t1 -> GRuleCall DHuddle dict = binding2 $ \k v -> "dict" =:= mp [0 <+ asKey k ==> v] in do it "Should bind a single parameter" $ - toSortedCDDL (collectFrom [HIRule $ "intset" =:= set VUInt]) + toSortedCDDLPretty (collectFrom [HIRule $ "intset" =:= set VUInt]) `shouldMatchParseCDDL` "intset = set\n set = [* a0]" it "Should bind two parameters" $ - toSortedCDDL (collectFrom [HIRule $ "mymap" =:= dict VUInt VText]) + toSortedCDDLPretty (collectFrom [HIRule $ "mymap" =:= dict VUInt VText]) `shouldMatchParseCDDL` "dict = {* a0 => b0}\n mymap = dict" constraintSpec :: Spec constraintSpec = describe "Constraints" $ do it "Size can take a Word" $ - toSortedCDDL (collectFrom [HIRule $ "sz" =:= VUInt `sized` (2 :: Word)]) + toSortedCDDLPretty (collectFrom [HIRule $ "sz" =:= VUInt `sized` (2 :: Word)]) `shouldMatchParseCDDL` "sz = uint .size 2" it "Range bound can take a reference" $ let b = "b" =:= (16 :: Integer) - in toSortedCDDL (collectFrom [HIRule $ "b" =:= (16 :: Integer), HIRule $ "c" =:= int 0 ... b]) + in toSortedCDDLPretty (collectFrom [HIRule $ "b" =:= (16 :: Integer), HIRule $ "c" =:= int 0 ... b]) `shouldMatchParseCDDL` "b = 16\n c = 0 .. b" -------------------------------------------------------------------------------- @@ -155,10 +157,10 @@ shouldMatchParse :: shouldMatchParse x parseFun input = parse parseFun "" (T.pack input) `shouldParse` x shouldMatchParseCDDL :: - CDDL -> + CDDL Comment -> String -> Expectation shouldMatchParseCDDL x = shouldMatchParse x pCDDL -toSortedCDDL :: Huddle -> CDDL -toSortedCDDL = sortCDDL . toCDDLNoRoot +toSortedCDDLPretty :: Huddle DHuddle -> CDDL Comment +toSortedCDDLPretty = sortCDDL . fmap dhComment . toCDDLNoRoot