diff --git a/bin/Main.hs b/bin/Main.hs index 03d1e2b..9101d5d 100644 --- a/bin/Main.hs +++ b/bin/Main.hs @@ -9,7 +9,7 @@ import Codec.CBOR.Cuddle.CDDL.Postlude (appendPostlude) import Codec.CBOR.Cuddle.CDDL.Resolve ( fullResolveCDDL, ) -import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) +import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..), mapCDDLDropExt) import Codec.CBOR.Cuddle.Parser (pCDDL) import Codec.CBOR.Cuddle.Pretty (PrettyStage) import Codec.CBOR.FlatTerm (toFlatTerm) @@ -192,24 +192,24 @@ run (Opts cmd cddlFile) = do putDocW 80 . pretty $ mapIndex @_ @_ @PrettyStage defs Validate vOpts -> let - res' + cddl | vNoPrelude vOpts = res | otherwise = appendPostlude res in - case fullResolveCDDL $ mapIndex res' of + case fullResolveCDDL $ mapCDDLDropExt cddl of Left err -> putStrLnErr (show err) >> exitFailure Right _ -> exitSuccess (GenerateCBOR gOpts) -> let - res' + cddl | gNoPrelude gOpts = res | otherwise = appendPostlude res in - case fullResolveCDDL $ mapIndex res' of + case fullResolveCDDL $ mapCDDLDropExt cddl of Left err -> putStrLnErr (show err) >> exitFailure Right mt -> do stdGen <- getStdGen - let term = generateCBORTerm mt (Name (itemName gOpts) mempty) stdGen + let term = generateCBORTerm mt (Name $ itemName gOpts) stdGen in case outputFormat gOpts of AsTerm -> print term AsFlatTerm -> print $ toFlatTerm (encodeTerm term) @@ -219,15 +219,15 @@ run (Opts cmd cddlFile) = do AsPrettyCBOR -> putStrLn . prettyHexEnc $ encodeTerm term ValidateCBOR vcOpts -> let - res' + cddl | vcNoPrelude vcOpts = res | otherwise = res in - case fullResolveCDDL $ mapIndex res' of + case fullResolveCDDL $ mapCDDLDropExt cddl of Left err -> putStrLnErr (show err) >> exitFailure Right mt -> do cbor <- BSC.readFile (vcInput vcOpts) - validateCBOR cbor (Name (vcItemName vcOpts) mempty) mt + validateCBOR cbor (Name $ vcItemName vcOpts) (mapIndex mt) putStrLnErr :: String -> IO () putStrLnErr = hPutStrLn stderr diff --git a/cuddle.cabal b/cuddle.cabal index 9ad5eb6..7a4b2ce 100644 --- a/cuddle.cabal +++ b/cuddle.cabal @@ -44,6 +44,7 @@ library Codec.CBOR.Cuddle.CBOR.Gen Codec.CBOR.Cuddle.CBOR.Validator Codec.CBOR.Cuddle.CDDL + Codec.CBOR.Cuddle.CDDL.CBORGenerator Codec.CBOR.Cuddle.CDDL.CTree Codec.CBOR.Cuddle.CDDL.CtlOp Codec.CBOR.Cuddle.CDDL.Postlude @@ -135,6 +136,7 @@ test-suite cuddle-test Paths_cuddle Test.Codec.CBOR.Cuddle.CDDL.Examples Test.Codec.CBOR.Cuddle.CDDL.Gen + Test.Codec.CBOR.Cuddle.CDDL.GeneratorSpec Test.Codec.CBOR.Cuddle.CDDL.Parser Test.Codec.CBOR.Cuddle.CDDL.Pretty Test.Codec.CBOR.Cuddle.Huddle @@ -147,6 +149,8 @@ test-suite cuddle-test QuickCheck >=2.14, base, bytestring, + cborg, + containers, cuddle, data-default-class, generic-random, @@ -154,6 +158,7 @@ test-suite cuddle-test hspec-megaparsec >=2.2, megaparsec, prettyprinter, + random, string-qq >=0.0.6, text, tree-diff, diff --git a/nix/shell.nix b/nix/shell.nix index 21c7618..05e1687 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -36,10 +36,6 @@ let enable = true; package = tools.fourmolu; }; - hlint = { - enable = true; - package = tools.hlint; - }; nixpkgs-fmt = { enable = true; package = pkgs.nixpkgs-fmt; diff --git a/src/Codec/CBOR/Cuddle/CBOR/Gen.hs b/src/Codec/CBOR/Cuddle/CBOR/Gen.hs index 20ae3fc..4e5a615 100644 --- a/src/Codec/CBOR/Cuddle/CBOR/Gen.hs +++ b/src/Codec/CBOR/Cuddle/CBOR/Gen.hs @@ -7,10 +7,13 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeData #-} {-# LANGUAGE ViewPatterns #-} #if MIN_VERSION_random(1,3,0) {-# OPTIONS_GHC -Wno-deprecations #-} -- Due to usage of `split` +{-# LANGUAGE TypeData #-} +{-# LANGUAGE TypeFamilies #-} #endif -- | Generate example CBOR given a CDDL specification module Codec.CBOR.Cuddle.CBOR.Gen (generateCBORTerm, generateCBORTerm') where @@ -25,10 +28,10 @@ import Codec.CBOR.Cuddle.CDDL ( Value (..), ValueVariant (..), ) -import Codec.CBOR.Cuddle.CDDL.CTree (CTree, CTreePhase, CTreeRoot (..), PTerm (..)) +import Codec.CBOR.Cuddle.CDDL.CTree (CTree (..), CTreeRoot (..), PTerm (..), foldCTree) import Codec.CBOR.Cuddle.CDDL.CTree qualified as CTree import Codec.CBOR.Cuddle.CDDL.CtlOp qualified as CtlOp -import Codec.CBOR.Cuddle.CDDL.Resolve (MonoRef (..), MonoReferenced) +import Codec.CBOR.Cuddle.CDDL.Resolve (MonoReferenced, XXCTree (..)) import Codec.CBOR.Term (Term (..)) import Codec.CBOR.Term qualified as CBOR import Codec.CBOR.Write qualified as CBOR @@ -59,8 +62,21 @@ import System.Random.Stateful ( import System.Random.Stateful ( SplitGen (..) ) +import Codec.CBOR.Cuddle.CDDL.CBORGenerator (WrappedTerm (..), CBORGenerator (..)) +import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) #endif +type data MonoDropGen + +newtype instance XXCTree MonoDropGen = MDGRef Name + deriving (Show) + +instance IndexMappable CTree MonoReferenced MonoDropGen where + mapIndex = foldCTree mapExt mapIndex + where + mapExt (MRuleRef n) = CTreeE $ MDGRef n + mapExt (MGenerator _ x) = mapIndex x + -------------------------------------------------------------------------------- -- Generator infrastructure -------------------------------------------------------------------------------- @@ -209,23 +225,14 @@ genPostlude pt = case pt of -- 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 (G 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] @@ -234,9 +241,6 @@ 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)] @@ -244,8 +248,8 @@ pairTermList [] = Just [] pairTermList (P x y : xs) = ((x, y) :) <$> pairTermList xs pairTermList _ = Nothing -pattern G :: [WrappedTerm] -> WrappedTerm -pattern G xs = GroupTerm xs +showDropGen :: CTree MonoReferenced -> String +showDropGen = show . mapIndex @_ @_ @MonoDropGen -------------------------------------------------------------------------------- -- Generator functions @@ -284,9 +288,9 @@ genForCTree (CTree.KV key value _cut) = do _ -> error $ "Non single-term generated outside of group context: " - <> show key + <> showDropGen key <> " => " - <> show value + <> showDropGen value genForCTree (CTree.Occur item occurs) = applyOccurenceIndicator occurs (genForCTree item) genForCTree (CTree.Range from to _bounds) = do @@ -306,11 +310,11 @@ genForCTree (CTree.Control op target controller) = do (CtlOp.Le, CTree.Literal (Value (VUInt n) _)) -> case target of CTree.Postlude PTUInt -> S . TInteger <$> genUniformRM (0, fromIntegral n) _ -> error "Cannot apply le operator to target" - (CtlOp.Le, _) -> error $ "Invalid controller for .le operator: " <> show controller + (CtlOp.Le, _) -> error $ "Invalid controller for .le operator: " <> showDropGen controller (CtlOp.Lt, CTree.Literal (Value (VUInt n) _)) -> case target of CTree.Postlude PTUInt -> S . TInteger <$> genUniformRM (0, fromIntegral n - 1) _ -> error "Cannot apply lt operator to target" - (CtlOp.Lt, _) -> error $ "Invalid controller for .lt operator: " <> show controller + (CtlOp.Lt, _) -> error $ "Invalid controller for .lt operator: " <> showDropGen controller (CtlOp.Size, CTree.Literal (Value (VUInt n) _)) -> case target of CTree.Postlude PTText -> S . TString <$> genText (fromIntegral n) CTree.Postlude PTBytes -> S . TBytes <$> genBytes (fromIntegral n) @@ -328,15 +332,15 @@ genForCTree (CTree.Control op target controller) = do CTree.Postlude PTUInt -> S . TInteger <$> genUniformRM (fromIntegral f1, fromIntegral t1) - _ -> error $ "Cannot apply size operator to target: " <> show target + _ -> error $ "Cannot apply size operator to target: " <> showDropGen target _ -> error $ "Invalid controller for .size operator: " - <> show controller + <> showDropGen controller (CtlOp.Size, _) -> error $ "Invalid controller for .size operator: " - <> show controller + <> showDropGen controller (CtlOp.Cbor, _) -> do enc <- genForCTree controller case enc of @@ -355,15 +359,16 @@ genForCTree (CTree.Tag tag node) = do case enc of S x -> pure $ S $ TTagged tag x _ -> error "Tag controller does not correspond to a single term" -genForCTree (CTree.CTreeE x) = genForNode x +genForCTree (CTree.CTreeE (MRuleRef n)) = genForNode n +genForCTree (CTree.CTreeE (MGenerator (CBORGenerator gen) _)) = gen StateGenM -genForNode :: RandomGen g => CTree.Node MonoReferenced -> M g WrappedTerm +genForNode :: RandomGen g => Name -> M g WrappedTerm genForNode = genForCTree <=< resolveRef -- | Take a reference and resolve it to the relevant Tree, following multiple -- links if necessary. -resolveRef :: RandomGen g => CTree.Node MonoReferenced -> M g (CTree MonoReferenced) -resolveRef (MRuleRef n) = do +resolveRef :: RandomGen g => Name -> M g (CTree MonoReferenced) +resolveRef n = do (CTreeRoot cddl) <- ask @"cddl" -- Since we follow a reference, we increase the 'depth' of the gen monad. modify @"depth" (+ 1) @@ -379,7 +384,7 @@ resolveRef (MRuleRef n) = do -- This will throw an error if the generated item does not correspond to a -- single CBOR term (e.g. if the name resolves to a group, which cannot be -- generated outside a context). -genForName :: RandomGen g => Name CTreePhase -> M g Term +genForName :: RandomGen g => Name -> M g Term genForName n = do (CTreeRoot cddl) <- ask @"cddl" case Map.lookup n cddl of @@ -433,13 +438,13 @@ genValueVariant (VBool b) = pure $ TBool b -- Generator functions -------------------------------------------------------------------------------- -generateCBORTerm :: RandomGen g => CTreeRoot MonoReferenced -> Name CTreePhase -> g -> Term +generateCBORTerm :: RandomGen g => CTreeRoot MonoReferenced -> Name -> g -> Term generateCBORTerm cddl n stdGen = let genEnv = GenEnv {cddl} genState = GenState {randomSeed = stdGen, depth = 1} in evalGen (genForName n) genEnv genState -generateCBORTerm' :: RandomGen g => CTreeRoot MonoReferenced -> Name CTreePhase -> g -> (Term, g) +generateCBORTerm' :: RandomGen g => CTreeRoot MonoReferenced -> Name -> g -> (Term, g) generateCBORTerm' cddl n stdGen = let genEnv = GenEnv {cddl} genState = GenState {randomSeed = stdGen, depth = 1} diff --git a/src/Codec/CBOR/Cuddle/CBOR/Validator.hs b/src/Codec/CBOR/Cuddle/CBOR/Validator.hs index 300f2a1..27baa52 100644 --- a/src/Codec/CBOR/Cuddle/CBOR/Validator.hs +++ b/src/Codec/CBOR/Cuddle/CBOR/Validator.hs @@ -1,5 +1,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeData #-} +{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} module Codec.CBOR.Cuddle.CBOR.Validator ( @@ -12,7 +14,8 @@ module Codec.CBOR.Cuddle.CBOR.Validator ( import Codec.CBOR.Cuddle.CDDL hiding (CDDL, Group, Rule) import Codec.CBOR.Cuddle.CDDL.CTree import Codec.CBOR.Cuddle.CDDL.CtlOp -import Codec.CBOR.Cuddle.CDDL.Resolve +import Codec.CBOR.Cuddle.CDDL.Resolve (MonoReferenced, XXCTree (..)) +import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) import Codec.CBOR.Read import Codec.CBOR.Term import Control.Exception @@ -37,8 +40,25 @@ import System.Exit import System.IO import Text.Regex.TDFA -type CDDL = CTreeRoot MonoReferenced -type Rule = CTree MonoReferenced +type data ValidatorStage + +data instance XTerm ValidatorStage = ValidatorXTerm + deriving (Show) + +newtype instance XXCTree ValidatorStage = VRuleRef Name + deriving (Show) + +instance IndexMappable CTreeRoot MonoReferenced ValidatorStage where + mapIndex (CTreeRoot m) = CTreeRoot $ mapIndex <$> m + +instance IndexMappable CTree MonoReferenced ValidatorStage where + mapIndex = foldCTree mapExt mapIndex + where + mapExt (MRuleRef n) = CTreeE $ VRuleRef n + mapExt (MGenerator _ x) = mapIndex x + +type CDDL = CTreeRoot ValidatorStage +type Rule = CTree ValidatorStage data CBORTermResult = CBORTermResult Term CDDLResult deriving (Show) @@ -114,7 +134,7 @@ data AMatchedItem = AMatchedItem -------------------------------------------------------------------------------- -- Main entry point -validateCBOR :: BS.ByteString -> Name CTreePhase -> CDDL -> IO () +validateCBOR :: BS.ByteString -> Name -> CDDL -> IO () validateCBOR bs rule cddl = ( case validateCBOR' bs rule cddl of ok@(CBORTermResult _ (Valid _)) -> do @@ -131,7 +151,7 @@ validateCBOR bs rule cddl = ) validateCBOR' :: - BS.ByteString -> Name CTreePhase -> CDDL -> CBORTermResult + BS.ByteString -> Name -> CDDL -> CBORTermResult validateCBOR' bs rule cddl@(CTreeRoot tree) = case deserialiseFromBytes decodeTerm (BSL.fromStrict bs) of Left e -> error $ show e @@ -1016,7 +1036,7 @@ validateChoice v rules = go rules . ($ dummyRule) dummyRule :: Rule -dummyRule = CTreeE $ MRuleRef (Name "dummy" mempty) +dummyRule = CTreeE $ VRuleRef "dummy" -------------------------------------------------------------------------------- -- Control helpers @@ -1108,7 +1128,7 @@ getIndicesOfEnum g = -- Resolving rules from the CDDL spec resolveIfRef :: CDDL -> Rule -> Rule -resolveIfRef ct@(CTreeRoot cddl) (CTreeE (MRuleRef n)) = do +resolveIfRef ct@(CTreeRoot cddl) (CTreeE (VRuleRef n)) = do case Map.lookup n cddl of Nothing -> error $ "Unbound reference: " <> show n Just val -> resolveIfRef ct val diff --git a/src/Codec/CBOR/Cuddle/CDDL.hs b/src/Codec/CBOR/Cuddle/CDDL.hs index a06ab8a..8251805 100644 --- a/src/Codec/CBOR/Cuddle/CDDL.hs +++ b/src/Codec/CBOR/Cuddle/CDDL.hs @@ -20,7 +20,8 @@ module Codec.CBOR.Cuddle.CDDL ( TypeOrGroup (..), Assign (..), GenericArg (..), - GenericParam (..), + GenericParameters (..), + GenericParameter (..), Type0 (..), Type1 (..), Type2 (..), @@ -39,10 +40,11 @@ module Codec.CBOR.Cuddle.CDDL ( compareRuleName, -- Extension ForAllExtensions, - XXTopLevel, - XXType2, XCddl, XTerm, + XRule, + XXTopLevel, + XXType2, ) where import Codec.CBOR.Cuddle.CDDL.CtlOp (CtlOp) @@ -68,12 +70,15 @@ data family XCddl i data family XTerm i +data family XRule i + data family XXType2 i type ForAllExtensions i (c :: Type -> Constraint) = ( c (XCddl i) - , c (XXTopLevel i) , c (XTerm i) + , c (XRule i) + , c (XXTopLevel i) , c (XXType2 i) ) @@ -150,30 +155,20 @@ deriving instance ForAllExtensions i ToExpr => ToExpr (TopLevel i) -- -- * Rule names (types or groups) do not appear in the actual CBOR -- encoding, but names used as "barewords" in member keys do. -data Name i = Name - { name :: T.Text - , nameExt :: XTerm i - } +newtype Name = Name {name :: T.Text} deriving (Generic) + deriving (Eq, Ord, Show) + deriving newtype (Semigroup, Monoid) -deriving instance Eq (XTerm i) => Eq (Name i) - -deriving instance Ord (XTerm i) => Ord (Name i) - -deriving instance Show (XTerm i) => Show (Name i) +deriving anyclass instance ToExpr Name -deriving instance ToExpr (XTerm i) => ToExpr (Name i) +instance IsString Name where + fromString = Name . T.pack -instance Monoid (XTerm i) => IsString (Name i) where - fromString x = Name (T.pack x) mempty +instance CollectComments Name where + collectComments _ = [] -instance HasComment (XTerm i) => HasComment (Name i) where - commentL = #nameExt % commentL - -instance CollectComments (XTerm i) => CollectComments (Name i) where - collectComments (Name _ c) = collectComments c - -instance Hashable (XTerm i) => Hashable (Name i) +instance Hashable Name -- | -- assignt = "=" / "/=" @@ -208,15 +203,32 @@ data Assign = AssignEq | AssignExt -- -- Generic rules can be used for establishing names for both types and -- groups. -newtype GenericParam i = GenericParam (NE.NonEmpty (Name i)) +newtype GenericParameters i = GenericParameters (NE.NonEmpty (GenericParameter i)) deriving (Generic) deriving newtype (Semigroup) -deriving instance Eq (XTerm i) => Eq (GenericParam i) +deriving instance Eq (XTerm i) => Eq (GenericParameters i) + +deriving instance Show (XTerm i) => Show (GenericParameters i) + +deriving anyclass instance ToExpr (XTerm i) => ToExpr (GenericParameters i) + +data GenericParameter i = GenericParameter + { gpName :: Name + , gpExt :: XTerm i + } + deriving (Generic) + +deriving instance Eq (XTerm i) => Eq (GenericParameter i) + +deriving instance Show (XTerm i) => Show (GenericParameter i) + +deriving anyclass instance ToExpr (XTerm i) => ToExpr (GenericParameter i) -deriving instance Show (XTerm i) => Show (GenericParam i) +instance CollectComments (XTerm i) => CollectComments (GenericParameter i) -deriving anyclass instance ToExpr (XTerm i) => ToExpr (GenericParam i) +instance HasComment (XTerm i) => HasComment (GenericParameter i) where + commentL = #gpExt % commentL newtype GenericArg i = GenericArg (NE.NonEmpty (Type1 i)) deriving (Generic) @@ -254,11 +266,11 @@ instance ForAllExtensions i CollectComments => CollectComments (GenericArg i) -- this semantic processing may need to span several levels of rule -- definitions before a determination can be made.) data Rule i = Rule - { ruleName :: Name i - , ruleGenParam :: Maybe (GenericParam i) + { ruleName :: Name + , ruleGenParam :: Maybe (GenericParameters i) , ruleAssign :: Assign , ruleTerm :: TypeOrGroup i - , ruleExt :: XTerm i + , ruleExt :: XRule i } deriving (Generic) @@ -268,7 +280,7 @@ deriving instance ForAllExtensions i Show => Show (Rule i) deriving instance ForAllExtensions i ToExpr => ToExpr (Rule i) -instance HasComment (XTerm i) => HasComment (Rule i) where +instance HasComment (XRule i) => HasComment (Rule i) where commentL = #ruleExt % commentL compareRuleName :: Ord (XTerm i) => Rule i -> Rule i -> Ordering @@ -401,7 +413,7 @@ data Type2 i T2Value Value | -- | 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 i) (Maybe (GenericArg i)) + T2Name Name (Maybe (GenericArg i)) | -- | or be defined in a parenthesized type expression (parentheses may be -- necessary to override some operator precedence), T2Group (Type0 i) @@ -415,11 +427,11 @@ data Type2 i 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 i) (Maybe (GenericArg i)) + 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 i) - | T2EnumRef (Name i) (Maybe (GenericArg 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 i) @@ -525,7 +537,7 @@ instance ForAllExtensions i CollectComments => CollectComments (GroupEntry i) wh data GroupEntryVariant i = GEType (Maybe (MemberKey i)) (Type0 i) - | GERef (Name i) (Maybe (GenericArg i)) + | GERef Name (Maybe (GenericArg i)) | GEGroup (Group i) deriving (Generic) @@ -553,7 +565,7 @@ instance ForAllExtensions i CollectComments => CollectComments (GroupEntryVarian -- presence of the cuts denoted by "^" or ":" in previous entries). data MemberKey i = MKType (Type1 i) - | MKBareword (Name i) + | MKBareword Name | MKValue Value deriving (Generic) diff --git a/src/Codec/CBOR/Cuddle/CDDL/CBORGenerator.hs b/src/Codec/CBOR/Cuddle/CDDL/CBORGenerator.hs new file mode 100644 index 0000000..a38d0f3 --- /dev/null +++ b/src/Codec/CBOR/Cuddle/CDDL/CBORGenerator.hs @@ -0,0 +1,23 @@ +module Codec.CBOR.Cuddle.CDDL.CBORGenerator ( + CBORGenerator (..), + HasGenerator (..), + WrappedTerm (..), +) where + +import Codec.CBOR.Term (Term) +import Optics.Core (Lens') +import System.Random.Stateful (StatefulGen) + +data WrappedTerm + = -- | Single term + S Term + | -- | Pair term + P Term Term + | -- | Group term + G [WrappedTerm] + deriving (Eq, Show) + +newtype CBORGenerator = CBORGenerator (forall g m. StatefulGen g m => g -> m WrappedTerm) + +class HasGenerator a where + generatorL :: Lens' a (Maybe CBORGenerator) diff --git a/src/Codec/CBOR/Cuddle/CDDL/CTree.hs b/src/Codec/CBOR/Cuddle/CDDL/CTree.hs index 4fda00d..0f74709 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/CTree.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/CTree.hs @@ -1,4 +1,7 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE TypeData #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -10,12 +13,15 @@ import Codec.CBOR.Cuddle.CDDL ( RangeBound, Value, XCddl, + XRule, XTerm, XXTopLevel, XXType2, ) +import Codec.CBOR.Cuddle.CDDL.CBORGenerator (CBORGenerator) import Codec.CBOR.Cuddle.CDDL.CtlOp -import Codec.CBOR.Cuddle.Comments (Comment) +import Control.Monad.Identity (Identity (..)) +import Data.Default.Class (Default) import Data.Hashable (Hashable) import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as Map @@ -33,21 +39,26 @@ import GHC.Generics (Generic) -- to manipulate. -------------------------------------------------------------------------------- -type family CTreeExt i +data family XXCTree i -data CTreePhase +type data CTreePhase -newtype instance XTerm CTreePhase = CTreeXTerm Comment - deriving (Generic, Show, Eq, Ord, Hashable, Semigroup, Monoid) +data instance XTerm CTreePhase = CTreeXTerm + deriving (Generic, Show, Eq, Ord) + deriving anyclass (Hashable, Default) -newtype instance XXTopLevel CTreePhase = CTreeXXTopLevel Comment - deriving (Generic, Show, Eq, Ord, Hashable) +newtype instance XXTopLevel CTreePhase = CTreeXXTopLevel Void + deriving (Generic, Show, Eq, Ord) -newtype instance XCddl CTreePhase = CTreeXCddl [Comment] - deriving (Generic, Show, Eq, Ord, Hashable) +data instance XCddl CTreePhase = CTreeXCddl + deriving (Generic, Show, Eq, Ord) + +newtype instance XRule CTreePhase = CTreeXRule (Maybe CBORGenerator) + deriving (Generic) newtype instance XXType2 CTreePhase = CTreeXXType2 Void - deriving (Generic, Show, Eq, Ord, Hashable) + deriving (Generic, Show, Eq, Ord) + deriving anyclass (Hashable) data CTree i = Literal Value @@ -63,14 +74,18 @@ data CTree i | Enum (CTree i) | Unwrap (CTree i) | Tag Word64 (CTree i) - | CTreeE (CTreeExt i) + | CTreeE (XXCTree i) deriving (Generic) deriving instance Eq (Node f) => Eq (CTree f) +deriving instance Show (Node f) => Show (CTree f) + +instance Hashable (Node f) => Hashable (CTree f) + -- | Traverse the CTree, carrying out the given operation at each node traverseCTree :: - Monad m => (CTreeExt i -> m (CTree j)) -> (CTree i -> m (CTree j)) -> CTree i -> m (CTree j) + Monad m => (XXCTree i -> m (CTree j)) -> (CTree i -> m (CTree j)) -> CTree i -> m (CTree j) traverseCTree _ _ (Literal a) = pure $ Literal a traverseCTree _ _ (Postlude a) = pure $ Postlude a traverseCTree _ atNode (Map xs) = Map <$> traverse atNode xs @@ -95,9 +110,16 @@ traverseCTree _ atNode (Unwrap ref) = Unwrap <$> atNode ref traverseCTree _ atNode (Tag i ref) = Tag i <$> atNode ref traverseCTree atExt _ (CTreeE x) = atExt x -type Node i = CTreeExt i +foldCTree :: + (XXCTree i -> CTree j) -> + (CTree i -> CTree j) -> + CTree i -> + CTree j +foldCTree atExt atNode x = runIdentity $ traverseCTree (pure . atExt) (pure . atNode) x + +type Node i = XXCTree i -newtype CTreeRoot i = CTreeRoot (Map.Map (Name CTreePhase) (CTree i)) +newtype CTreeRoot i = CTreeRoot (Map.Map Name (CTree i)) deriving (Generic) deriving instance Show (CTree i) => Show (CTreeRoot i) diff --git a/src/Codec/CBOR/Cuddle/CDDL/Postlude.hs b/src/Codec/CBOR/Cuddle/CDDL/Postlude.hs index b242283..a8e2ae8 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/Postlude.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/Postlude.hs @@ -2,7 +2,7 @@ module Codec.CBOR.Cuddle.CDDL.Postlude where -import Codec.CBOR.Cuddle.CDDL (CDDL (..), TopLevel (..), XTerm, XXType2, appendRules) +import Codec.CBOR.Cuddle.CDDL (CDDL (..), TopLevel (..), XRule, XTerm, XXType2, appendRules) import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) import Codec.CBOR.Cuddle.Parser (ParserStage, pCDDL) import Data.Maybe (mapMaybe) @@ -62,6 +62,7 @@ cddlPostlude = appendPostlude :: ( IndexMappable XXType2 ParserStage i , IndexMappable XTerm ParserStage i + , IndexMappable XRule ParserStage i ) => CDDL i -> CDDL i appendPostlude cddl = appendRules cddl $ mapIndex <$> (r : rs) diff --git a/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs b/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs index 6ca2392..c6de72b 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs @@ -4,6 +4,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeData #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} @@ -33,7 +34,7 @@ module Codec.CBOR.Cuddle.CDDL.Resolve ( fullResolveCDDL, NameResolutionFailure (..), MonoReferenced, - MonoRef (..), + XXCTree (..), ) where @@ -47,11 +48,13 @@ import Capability.State (HasState, MonadState (..), modify) import Codec.CBOR.Cuddle.CDDL as CDDL import Codec.CBOR.Cuddle.CDDL.CTree ( CTree (..), - CTreeExt, CTreePhase, CTreeRoot (..), PTerm (..), + XRule (..), + XXCTree, XXType2 (..), + foldCTree, ) import Codec.CBOR.Cuddle.CDDL.CTree qualified as CTree import Control.Monad.Except (ExceptT (..), runExceptT) @@ -63,6 +66,8 @@ import Data.Hashable #if __GLASGOW_HASKELL__ < 910 import Data.List (foldl') #endif +import Codec.CBOR.Cuddle.CDDL.CBORGenerator (CBORGenerator) +import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as Map import Data.Text qualified as T @@ -71,29 +76,29 @@ import GHC.Generics (Generic) import Optics.Core data ProvidedParameters a = ProvidedParameters - { parameters :: [Name CTreePhase] + { parameters :: [Name] , underlying :: a } deriving (Generic, Functor, Show, Eq, Foldable, Traversable) instance Hashable a => Hashable (ProvidedParameters a) -data Parametrised - -type instance CTreeExt Parametrised = ProvidedParameters (CTree Parametrised) - -------------------------------------------------------------------------------- -- 1. Rule extensions -------------------------------------------------------------------------------- -newtype PartialCTreeRoot i = PartialCTreeRoot (Map.Map (Name CTreePhase) (ProvidedParameters (CTree i))) +newtype PartialCTreeRoot i = PartialCTreeRoot (Map.Map Name (ProvidedParameters (CTree i))) deriving (Generic) -type CDDLMap = Map.Map (Name CTreePhase) (ProvidedParameters (TypeOrGroup CTreePhase)) +type CDDLMap = + Map.Map Name (ProvidedParameters (TypeOrGroup CTreePhase), Maybe CBORGenerator) -toParametrised :: a -> Maybe (GenericParam CTreePhase) -> ProvidedParameters a +toParametrised :: + TypeOrGroup CTreePhase -> + Maybe (GenericParameters CTreePhase) -> + ProvidedParameters (TypeOrGroup CTreePhase) toParametrised a Nothing = ProvidedParameters [] a -toParametrised a (Just (GenericParam gps)) = ProvidedParameters (NE.toList gps) a +toParametrised a (Just (GenericParameters gps)) = ProvidedParameters (gpName <$> NE.toList gps) a asMap :: CDDL CTreePhase -> CDDLMap asMap cddl = foldl' go Map.empty rules @@ -103,64 +108,70 @@ asMap cddl = foldl' go Map.empty rules go x (TopLevelRule r) = assignOrExtend x r assignOrExtend :: CDDLMap -> Rule CTreePhase -> CDDLMap - assignOrExtend m (Rule n gps assign tog _) = case assign of + assignOrExtend m (Rule n gps assign tog (CTreeXRule g)) = case assign of -- Equals assignment - AssignEq -> Map.insert n (toParametrised tog gps) m - AssignExt -> Map.alter (extend tog gps) n m + AssignEq -> Map.insert n (toParametrised tog gps, g) m + AssignExt -> + Map.alter (extend tog gps) n m extend :: TypeOrGroup CTreePhase -> - Maybe (GenericParam CTreePhase) -> - Maybe (ProvidedParameters (TypeOrGroup CTreePhase)) -> - Maybe (ProvidedParameters (TypeOrGroup CTreePhase)) - extend tog _gps (Just existing) = case (underlying existing, tog) of + Maybe (GenericParameters CTreePhase) -> + Maybe (ProvidedParameters (TypeOrGroup CTreePhase), Maybe CBORGenerator) -> + Maybe (ProvidedParameters (TypeOrGroup CTreePhase), Maybe CBORGenerator) + extend tog _gps (Just (existing, gen)) = case (underlying existing, tog) of (TOGType _, TOGType (Type0 new)) -> Just $ - existing - & field @"underlying" - % _Ctor @"TOGType" - % _Ctor @"Type0" - %~ (<> new) + ( existing + & field @"underlying" + % _Ctor @"TOGType" + % _Ctor @"Type0" + %~ (<> new) + , gen + ) -- From the CDDL spec, I don't see how one is meant to extend a group. -- According to the description, it's meant to add a group choice, but the -- assignment to a group takes a 'GrpEntry', not a Group, and there is no -- ability to add a choice. For now, we simply ignore attempt at -- extension. - (TOGGroup _, TOGGroup _new) -> Just existing + (TOGGroup _, TOGGroup _new) -> Just (existing, gen) (TOGType _, _) -> error "Attempting to extend a type with a group" (TOGGroup _, _) -> error "Attempting to extend a group with a type" - extend tog gps Nothing = Just $ toParametrised tog gps + extend tog gps Nothing = Just (toParametrised tog gps, Nothing) -------------------------------------------------------------------------------- -- 2. Conversion to CTree -------------------------------------------------------------------------------- -data OrReferenced - -type instance CTreeExt OrReferenced = OrRef +type data OrReferenced --- | Indicates that an item may be referenced rather than defined. -data OrRef +data instance XXCTree OrReferenced = -- | Reference to another node with possible generic arguments supplied - Ref (Name CTreePhase) [CTree OrReferenced] - deriving (Eq, Show) + OrRef Name [CTree OrReferenced] + | OGenerator CBORGenerator (CTree OrReferenced) -type RefCTree = PartialCTreeRoot OrReferenced +type data OrReferencedDropGen -deriving instance Show (CTree OrReferenced) +data instance XXCTree OrReferencedDropGen = DGOrRef Name [CTree OrReferencedDropGen] + deriving (Eq, Show) -deriving instance Show (PartialCTreeRoot OrReferenced) +instance IndexMappable CTree OrReferenced OrReferencedDropGen where + mapIndex = foldCTree mapExt mapIndex + where + mapExt (OrRef n xs) = CTreeE . DGOrRef n $ mapIndex <$> xs + mapExt (OGenerator _ x) = mapIndex x -- | Build a CTree incorporating references. -- -- This translation cannot fail. -buildRefCTree :: CDDLMap -> RefCTree -buildRefCTree rules = PartialCTreeRoot $ toCTreeRule <$> rules +buildRefCTree :: CDDLMap -> PartialCTreeRoot OrReferenced +buildRefCTree rules = PartialCTreeRoot $ uncurry toCTreeRule <$> rules where toCTreeRule :: ProvidedParameters (TypeOrGroup CTreePhase) -> + Maybe CBORGenerator -> ProvidedParameters (CTree OrReferenced) - toCTreeRule = fmap toCTreeTOG + toCTreeRule params gen = fmap (maybe id (\g x -> CTreeE $ OGenerator g x) gen . toCTreeTOG) params toCTreeTOG :: TypeOrGroup CTreePhase -> CTree OrReferenced toCTreeTOG (TOGType t0) = toCTreeT0 t0 @@ -188,7 +199,7 @@ buildRefCTree rules = PartialCTreeRoot $ toCTreeRule <$> rules toCTreeT2 :: Type2 CTreePhase -> CTree OrReferenced toCTreeT2 (T2Value v) = CTree.Literal v - toCTreeT2 (T2Name n garg) = CTreeE $ Ref n (fromGenArgs garg) + toCTreeT2 (T2Name n garg) = CTreeE $ OrRef n (fromGenArgs garg) toCTreeT2 (T2Group t0) = -- This behaviour seems questionable, but I don't really see how better to -- interpret the spec here. @@ -197,9 +208,9 @@ buildRefCTree rules = PartialCTreeRoot $ toCTreeRule <$> rules toCTreeT2 (T2Array g) = toCTreeArray g toCTreeT2 (T2Unwrapped n margs) = CTree.Unwrap . CTreeE $ - Ref n (fromGenArgs margs) + OrRef n (fromGenArgs margs) toCTreeT2 (T2Enum g) = toCTreeEnum g - toCTreeT2 (T2EnumRef n margs) = CTreeE . Ref n $ fromGenArgs margs + toCTreeT2 (T2EnumRef n margs) = CTreeE . OrRef n $ fromGenArgs margs toCTreeT2 (T2Tag Nothing t0) = -- Currently not validating tags toCTreeT0 t0 @@ -237,10 +248,10 @@ buildRefCTree rules = PartialCTreeRoot $ toCTreeRule <$> rules toCTreeGroupEntry (GroupEntry Nothing (GEType mmkey t0) _) = toKVPair mmkey t0 toCTreeGroupEntry (GroupEntry (Just occi) (GERef n margs) _) = CTree.Occur - { CTree.item = CTreeE $ Ref n (fromGenArgs margs) + { CTree.item = CTreeE $ OrRef n (fromGenArgs margs) , CTree.occurs = occi } - toCTreeGroupEntry (GroupEntry Nothing (GERef n margs) _) = CTreeE $ Ref n (fromGenArgs margs) + toCTreeGroupEntry (GroupEntry Nothing (GERef n margs) _) = CTreeE $ OrRef n (fromGenArgs margs) toCTreeGroupEntry (GroupEntry (Just occi) (GEGroup g) _) = CTree.Occur { CTree.item = groupToGroup g @@ -296,7 +307,7 @@ buildRefCTree rules = PartialCTreeRoot $ toCTreeRule <$> rules toCTreeMemberKey :: MemberKey CTreePhase -> CTree OrReferenced toCTreeMemberKey (MKValue v) = CTree.Literal v - toCTreeMemberKey (MKBareword (Name n _)) = CTree.Literal (Value (VText n) mempty) + toCTreeMemberKey (MKBareword n) = CTree.Literal (Value (VText $ name n) mempty) toCTreeMemberKey (MKType t1) = toCTreeT1 t1 -------------------------------------------------------------------------------- @@ -304,82 +315,82 @@ buildRefCTree rules = PartialCTreeRoot $ toCTreeRule <$> rules -------------------------------------------------------------------------------- data NameResolutionFailure - = UnboundReference (Name CTreePhase) - | MismatchingArgs (Name CTreePhase) [Name CTreePhase] - | ArgsToPostlude PTerm [CTree OrReferenced] - deriving (Show) - -deriving instance Eq (CTree.Node OrReferenced) => Eq NameResolutionFailure + = UnboundReference Name + | MismatchingArgs Name [Name] + | ArgsToPostlude PTerm [CTree OrReferencedDropGen] + deriving (Show, Eq) -postludeBinding :: Map.Map (Name CTreePhase) PTerm +postludeBinding :: Map.Map Name PTerm postludeBinding = Map.fromList - [ (Name "bool" mempty, PTBool) - , (Name "uint" mempty, PTUInt) - , (Name "nint" mempty, PTNInt) - , (Name "int" mempty, PTInt) - , (Name "half" mempty, PTHalf) - , (Name "float" mempty, PTFloat) - , (Name "double" mempty, PTDouble) - , (Name "bytes" mempty, PTBytes) - , (Name "bstr" mempty, PTBytes) - , (Name "text" mempty, PTText) - , (Name "tstr" mempty, PTText) - , (Name "any" mempty, PTAny) - , (Name "nil" mempty, PTNil) - , (Name "null" mempty, PTNil) + [ ("bool", PTBool) + , ("uint", PTUInt) + , ("nint", PTNInt) + , ("int", PTInt) + , ("half", PTHalf) + , ("float", PTFloat) + , ("double", PTDouble) + , ("bytes", PTBytes) + , ("bstr", PTBytes) + , ("text", PTText) + , ("tstr", PTText) + , ("any", PTAny) + , ("nil", PTNil) + , ("null", PTNil) ] data BindingEnv i j = BindingEnv - { global :: Map.Map (Name CTreePhase) (ProvidedParameters (CTree i)) + { global :: Map.Map (Name) (ProvidedParameters (CTree i)) -- ^ Global name bindings via 'RuleDef' - , local :: Map.Map (Name CTreePhase) (CTree j) + , local :: Map.Map (Name) (CTree j) -- ^ Local bindings for generic parameters } deriving (Generic) -data DistReferenced +type data DistReferenced -type instance CTreeExt DistReferenced = DistRef - -data DistRef +data DistRef i = -- | Reference to a generic parameter - GenericRef (Name CTreePhase) + GenericRef (Name) | -- | Reference to a rule definition, possibly with generic arguments - RuleRef (Name CTreePhase) [CTree DistReferenced] - deriving (Eq, Generic, Show) + RuleRef (Name) [CTree i] + deriving (Generic) -instance Hashable DistRef +deriving instance Eq (CTree.Node i) => Eq (DistRef i) -deriving instance Show (CTree DistReferenced) +deriving instance Show (CTree.Node i) => Show (DistRef i) -instance Hashable (CTree DistReferenced) +instance Hashable (CTree.Node i) => Hashable (DistRef i) -deriving instance Show (PartialCTreeRoot DistReferenced) +data instance XXCTree DistReferenced + = DRef (DistRef DistReferenced) + | DGenerator CBORGenerator (CTree DistReferenced) -deriving instance Eq (PartialCTreeRoot DistReferenced) +type data DistReferencedNoGen -instance Hashable (PartialCTreeRoot DistReferenced) +newtype instance XXCTree DistReferencedNoGen = DHRef (DistRef DistReferencedNoGen) + deriving (Eq, Hashable) resolveRef :: BindingEnv OrReferenced OrReferenced -> CTree.Node OrReferenced -> Either NameResolutionFailure (CTree DistReferenced) -resolveRef env (Ref n args) = case Map.lookup n postludeBinding of +resolveRef env (OrRef n args) = case Map.lookup n postludeBinding of Just pterm -> case args of [] -> Right $ CTree.Postlude pterm - xs -> Left $ ArgsToPostlude pterm xs + xs -> Left . ArgsToPostlude pterm $ mapIndex <$> xs Nothing -> case Map.lookup n (global env) of Just (parameters -> params') -> if length params' == length args then let localBinds = Map.fromList $ zip params' args newEnv = env & #local %~ Map.union localBinds - in CTreeE . RuleRef n <$> traverse (resolveCTree newEnv) args + in CTreeE . DRef . RuleRef n <$> traverse (resolveCTree newEnv) args else Left $ MismatchingArgs n params' Nothing -> case Map.lookup n (local env) of - Just _ -> Right . CTreeE $ GenericRef n + Just _ -> Right . CTreeE . DRef $ GenericRef n Nothing -> Left $ UnboundReference n +resolveRef env (OGenerator g x) = CTreeE . DGenerator g <$> resolveCTree env x resolveCTree :: BindingEnv OrReferenced OrReferenced -> @@ -394,7 +405,7 @@ buildResolvedCTree (PartialCTreeRoot ct) = PartialCTreeRoot <$> traverse go ct where go pn = let args = parameters pn - localBinds = Map.fromList $ zip args (CTreeE . flip Ref [] <$> args) + localBinds = Map.fromList $ zip args (CTreeE . flip OrRef [] <$> args) env = BindingEnv @OrReferenced @OrReferenced ct localBinds in traverse (resolveCTree env) pn @@ -402,22 +413,16 @@ buildResolvedCTree (PartialCTreeRoot ct) = PartialCTreeRoot <$> traverse go ct -- 4. Monomorphisation -------------------------------------------------------------------------------- -data MonoReferenced - -type instance CTreeExt MonoReferenced = MonoRef (CTree MonoReferenced) - -newtype MonoRef a - = MRuleRef (Name CTreePhase) - deriving (Functor, Show) +type data MonoReferenced -deriving instance Show (CTree MonoReferenced) - -deriving instance Show (PartialCTreeRoot MonoReferenced) +data instance XXCTree MonoReferenced + = MRuleRef Name + | MGenerator CBORGenerator (CTree MonoReferenced) type MonoEnv = BindingEnv DistReferenced MonoReferenced -- | We introduce additional bindings in the state -type MonoState = Map.Map (Name CTreePhase) (CTree MonoReferenced) +type MonoState = Map.Map Name (CTree MonoReferenced) -- | Monad to run the monomorphisation process. We need some additional -- capabilities for this, so 'Either' doesn't fully cut it anymore. @@ -439,10 +444,10 @@ newtype MonoM a = MonoM deriving ( HasSource "local" - (Map.Map (Name CTreePhase) (CTree MonoReferenced)) + (Map.Map (Name) (CTree MonoReferenced)) , HasReader "local" - (Map.Map (Name CTreePhase) (CTree MonoReferenced)) + (Map.Map (Name) (CTree MonoReferenced)) ) via Field "local" @@ -456,10 +461,10 @@ newtype MonoM a = MonoM deriving ( HasSource "global" - (Map.Map (Name CTreePhase) (ProvidedParameters (CTree DistReferenced))) + (Map.Map (Name) (ProvidedParameters (CTree DistReferenced))) , HasReader "global" - (Map.Map (Name CTreePhase) (ProvidedParameters (CTree DistReferenced))) + (Map.Map (Name) (ProvidedParameters (CTree DistReferenced))) ) via Field "global" @@ -485,16 +490,17 @@ throwNR :: NameResolutionFailure -> MonoM a throwNR = throw @"nameResolution" -- | Synthesize a monomorphic rule definition, returning the name -synthMono :: Name CTreePhase -> [CTree DistReferenced] -> MonoM (Name CTreePhase) -synthMono n@(Name origName _) args = - let fresh = +synthMono :: Name -> [CTree DistReferenced] -> MonoM Name +synthMono origName args = + let dropGenerator = fmap $ mapIndex @_ @_ @DistReferencedNoGen + fresh = -- % is not a valid CBOR name, so this should avoid conflict - Name (origName <> "%" <> T.pack (show $ hash args)) mempty + origName <> "%" <> Name (T.pack (show . hash $ dropGenerator args)) in do -- Lookup the original name in the global bindings globalBinds <- ask @"global" - case Map.lookup n globalBinds of - Just (ProvidedParameters [] _) -> throwNR $ MismatchingArgs n [] + case Map.lookup origName globalBinds of + Just (ProvidedParameters [] _) -> throwNR $ MismatchingArgs origName [] Just (ProvidedParameters params' r) -> if length params' == length args then do @@ -503,22 +509,23 @@ synthMono n@(Name origName _) args = Reader.local @"local" (Map.union localBinds) $ do foo <- resolveGenericCTree r modify @"synth" $ Map.insert fresh foo - else throwNR $ MismatchingArgs n params' - Nothing -> throwNR $ UnboundReference n + else throwNR $ MismatchingArgs origName params' + Nothing -> throwNR $ UnboundReference origName pure fresh resolveGenericRef :: CTree.Node DistReferenced -> MonoM (CTree MonoReferenced) -resolveGenericRef (RuleRef n []) = pure . CTreeE $ MRuleRef n -resolveGenericRef (RuleRef n args) = do +resolveGenericRef (DRef (RuleRef n [])) = pure . CTreeE $ MRuleRef n +resolveGenericRef (DRef (RuleRef n args)) = do fresh <- synthMono n args pure . CTreeE $ MRuleRef fresh -resolveGenericRef (GenericRef n) = do +resolveGenericRef (DRef (GenericRef n)) = do localBinds <- ask @"local" case Map.lookup n localBinds of Just node -> pure node Nothing -> throwNR $ UnboundReference n +resolveGenericRef (DGenerator g x) = CTreeE . MGenerator g <$> resolveGenericCTree x resolveGenericCTree :: CTree DistReferenced -> @@ -557,3 +564,13 @@ fullResolveCDDL cddl = do let refCTree = buildRefCTree (asMap cddl) rCTree <- buildResolvedCTree refCTree buildMonoCTree rCTree + +instance IndexMappable CTree DistReferenced DistReferencedNoGen where + mapIndex = foldCTree mapExt mapIndex + where + mapExt (DRef x) = CTreeE . DHRef $ mapIndex x + mapExt (DGenerator _ x) = mapIndex x + +instance IndexMappable DistRef DistReferenced DistReferencedNoGen where + mapIndex (GenericRef n) = GenericRef n + mapIndex (RuleRef n args) = RuleRef n $ mapIndex <$> args diff --git a/src/Codec/CBOR/Cuddle/Huddle.hs b/src/Codec/CBOR/Cuddle/Huddle.hs index e044388..dbb2307 100644 --- a/src/Codec/CBOR/Cuddle/Huddle.hs +++ b/src/Codec/CBOR/Cuddle/Huddle.hs @@ -2,8 +2,10 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeData #-} {-# LANGUAGE TypeFamilies #-} -- | Module for building CDDL in Haskell @@ -17,8 +19,8 @@ module Codec.CBOR.Cuddle.Huddle ( Huddle, HuddleItem (..), huddleAugment, - Rule, - Named, + Rule (..), + Named (..), IsType0 (..), Value (..), @@ -26,6 +28,7 @@ module Codec.CBOR.Cuddle.Huddle ( HuddleStage, C.XCddl (..), C.XTerm (..), + C.XRule (..), C.XXTopLevel (..), C.XXType2 (..), @@ -85,6 +88,9 @@ module Codec.CBOR.Cuddle.Huddle ( binding2, callToDef, + -- * Generators + withGenerator, + -- * Conversion to CDDL collectFrom, collectFromInit, @@ -93,16 +99,18 @@ module Codec.CBOR.Cuddle.Huddle ( ) where -import Codec.CBOR.Cuddle.CDDL (CDDL) +import Codec.CBOR.Cuddle.CDDL (CDDL, GenericParameter (..), XRule) import Codec.CBOR.Cuddle.CDDL qualified as C +import Codec.CBOR.Cuddle.CDDL.CBORGenerator (CBORGenerator (..), HasGenerator (..), WrappedTerm) 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, (|<>)) @@ -115,10 +123,12 @@ 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 Optics.Core qualified as L +import System.Random.Stateful (StatefulGen) import Prelude hiding ((/)) -data HuddleStage +type data HuddleStage newtype instance C.XTerm HuddleStage = HuddleXTerm C.Comment deriving (Generic, Semigroup, Monoid, Show, Eq) @@ -126,6 +136,14 @@ newtype instance C.XTerm HuddleStage = HuddleXTerm C.Comment newtype instance C.XCddl HuddleStage = HuddleXCddl [C.Comment] deriving (Generic, Semigroup, Monoid, Show, Eq) +data instance C.XRule HuddleStage = HuddleXRule + { hxrComment :: C.Comment + , hxrGenerator :: Maybe CBORGenerator + } + deriving (Generic) + +instance Default (XRule HuddleStage) + newtype instance C.XXTopLevel HuddleStage = HuddleXXTopLevel C.Comment deriving (Generic, Semigroup, Monoid, Show, Eq) @@ -140,19 +158,29 @@ data Named a = Named 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 => T.Text -> a -> a +comment desc n = n & commentL %~ (<> Comment desc) instance Show (Named a) where show (Named n _ _) = T.unpack n -type Rule = Named Type0 +data Rule = Rule + { ruleDefinition :: Named Type0 + , ruleExtra :: XRule HuddleStage + } + deriving (Generic) + +instance HasGenerator Rule where + generatorL = #ruleExtra % #hxrGenerator + +instance HasComment Rule where + commentL = #ruleExtra % #hxrComment data HuddleItem = HIRule Rule | HIGRule GRuleDef | HIGroup (Named Group) - deriving (Generic, Show) + deriving (Generic) -- | Top-level Huddle type is a list of rules. data Huddle = Huddle @@ -160,14 +188,14 @@ data Huddle = Huddle -- ^ Root elements , items :: OMap T.Text HuddleItem } - 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 rootsL itemsL) (Huddle rootsR itemsR) = - Huddle (L.nubBy ((==) `on` name) $ rootsL <> rootsR) (itemsL |<> itemsR) + Huddle (L.nubBy ((==) `on` name . ruleDefinition) $ rootsL <> rootsR) (itemsL |<> itemsR) -- | This semigroup instance: -- - Takes takes the roots from the RHS unless they are empty, in which case @@ -192,8 +220,8 @@ instance Semigroup Huddle where instance IsList Huddle where type Item Huddle = Rule fromList [] = Huddle mempty OMap.empty - fromList (x : xs) = - (field @"items" %~ (OMap.|> (x ^. field @"name", HIRule x))) $ fromList xs + fromList (r@(Rule x _) : xs) = + (field @"items" %~ (OMap.|> (x ^. field @"name", HIRule r))) $ fromList xs toList = const [] @@ -216,7 +244,6 @@ choiceToNE (ChoiceOf c cs) = c NE.:| choiceToList cs data Key = LiteralKey Literal | TypeKey Type2 - deriving (Show) -- | Instance for the very general case where we use text keys instance IsString Key where @@ -237,13 +264,12 @@ data MapEntry = MapEntry , quantifier :: Occurs , meDescription :: C.Comment } - deriving (Generic, Show) + deriving (Generic) instance C.HasComment MapEntry where commentL = lens meDescription (\x y -> x {meDescription = y}) newtype MapChoice = MapChoice {unMapChoice :: [MapEntry]} - deriving (Show) instance IsList MapChoice where type Item MapChoice = MapEntry @@ -261,7 +287,7 @@ data ArrayEntry = ArrayEntry , quantifier :: Occurs , aeDescription :: C.Comment } - deriving (Generic, Show) + deriving (Generic) instance C.HasComment ArrayEntry where commentL = lens aeDescription (\x y -> x {aeDescription = y}) @@ -283,7 +309,6 @@ data ArrayChoice = ArrayChoice { unArrayChoice :: [ArrayEntry] , acComment :: C.Comment } - deriving (Show) instance Semigroup ArrayChoice where ArrayChoice x xc <> ArrayChoice y yc = ArrayChoice (x <> y) (xc <> yc) @@ -302,8 +327,8 @@ instance IsList ArrayChoice where type Array = Choice ArrayChoice -newtype Group = Group {unGroup :: [ArrayEntry]} - deriving (Show, Monoid, Semigroup) +newtype Group = Group {_unGroup :: [ArrayEntry]} + deriving (Monoid, Semigroup) instance IsList Group where type Item Group = ArrayEntry @@ -323,7 +348,6 @@ data Type2 T2Generic GRuleCall | -- | Reference to a generic parameter within the body of the definition T2GenericRef GRef - deriving (Show) type Type0 = Choice Type2 @@ -426,17 +450,15 @@ data CGRefType data Constrained where Constrained :: forall a. - { value :: Constrainable a - , constraint :: ValueConstraint a - , refs :: [Rule] + { _value :: Constrainable a + , _constraint :: ValueConstraint a + , _refs :: [Rule] -- ^ Sometimes constraints reference rules. In this case we need to -- collect the references in order to traverse them when collecting all -- relevant rules. } -> Constrained -deriving instance Show Constrained - class IsConstrainable a x | a -> x where toConstrainable :: a -> Constrainable x @@ -539,14 +561,14 @@ instance IsCborable (AnyRef a) instance IsCborable GRef cbor :: (IsCborable b, IsConstrainable c b) => c -> Rule -> Constrained -cbor v r@(Named n _ _) = +cbor v r@(Rule (Named n _ _) _) = Constrained (toConstrainable v) ValueConstraint { applyConstraint = \t2 -> C.Type1 t2 - (Just (C.CtrlOp CtlOp.Cbor, C.T2Name (C.Name n mempty) Nothing)) + (Just (C.CtrlOp CtlOp.Cbor, C.T2Name (C.Name n) Nothing)) mempty , showConstraint = ".cbor " <> T.unpack n } @@ -590,6 +612,9 @@ instance IsRangeBound Integer where instance IsRangeBound (Named Type0) where toRangeBound = RangeBoundRef +instance IsRangeBound Rule where + toRangeBound (Rule x _) = toRangeBound x + data Ranged where Ranged :: { lb :: RangeBound @@ -614,7 +639,7 @@ class IsType0 a where toType0 :: a -> Type0 instance IsType0 Rule where - toType0 = NoChoice . T2Ref + toType0 = NoChoice . T2Ref . ruleDefinition instance IsType0 (Choice Type2) where toType0 = id @@ -743,7 +768,7 @@ infixl 8 ==> -- | Assign a rule (=:=) :: IsType0 a => T.Text -> a -> Rule -n =:= b = Named n (toType0 b) Nothing +n =:= b = Rule (Named n (toType0 b) Nothing) def infixl 1 =:= @@ -790,7 +815,7 @@ instance IsChoosable Type2 Type2 where toChoice = NoChoice instance IsChoosable Rule Type2 where - toChoice = toChoice . T2Ref + toChoice = toChoice . T2Ref . ruleDefinition instance IsChoosable GRuleCall Type2 where toChoice = toChoice . T2Generic @@ -941,7 +966,6 @@ data GRule a = GRule { args :: NE.NonEmpty a , body :: Type0 } - deriving (Show) type GRuleCall = Named (GRule Type2) @@ -963,10 +987,10 @@ callToDef gr = gr {args = refs} binding :: IsType0 t0 => (GRef -> Rule) -> t0 -> GRuleCall binding fRule t0 = Named - (name rule) + (name $ ruleDefinition rule) GRule { args = t2 NE.:| [] - , body = getField @"value" rule + , body = getField @"value" $ ruleDefinition rule } Nothing where @@ -979,10 +1003,10 @@ binding fRule t0 = binding2 :: (IsType0 t0, IsType0 t1) => (GRef -> GRef -> Rule) -> t0 -> t1 -> GRuleCall binding2 fRule t0 t1 = Named - (name rule) + (name $ ruleDefinition rule) GRule { args = t02 NE.:| [t12] - , body = getField @"value" rule + , body = getField @"value" $ ruleDefinition rule } Nothing where @@ -1003,7 +1027,7 @@ hiRule (HIRule r) = [r] hiRule _ = [] hiName :: HuddleItem -> T.Text -hiName (HIRule (Named n _ _)) = n +hiName (HIRule (Rule (Named n _ _) _)) = n hiName (HIGroup (Named n _ _)) = n hiName (HIGRule (Named n _ _)) = n @@ -1025,7 +1049,7 @@ collectFrom topRs = goHuddleItem (HIRule r) = goRule r goHuddleItem (HIGroup g) = goNamedGroup g goHuddleItem (HIGRule (Named _ (GRule _ t0) _)) = goT0 t0 - goRule r@(Named n t0 _) = do + goRule r@(Rule (Named n t0 _) _) = do items <- get when (OMap.notMember n items) $ do modify (OMap.|> (n, HIRule r)) @@ -1050,13 +1074,13 @@ collectFrom topRs = goT2 (T2Map m) = goChoice (mapM_ goMapEntry . unMapChoice) m goT2 (T2Array m) = goChoice (mapM_ goArrayEntry . unArrayChoice) m goT2 (T2Tagged (Tagged _ t0)) = goT0 t0 - goT2 (T2Ref n) = goRule n + goT2 (T2Ref n) = goRule (Rule n $ HuddleXRule mempty Nothing) goT2 (T2Group r) = goNamedGroup r goT2 (T2Generic x) = goGRule x goT2 (T2Constrained (Constrained c _ refs)) = ( case c of CValue _ -> pure () - CRef r -> goRule r + CRef r -> goRule $ Rule r def CGRef _ -> pure () ) >> mapM_ goRule refs @@ -1070,7 +1094,7 @@ collectFrom topRs = goRanged (Unranged _) = pure () goRanged (Ranged lb ub _) = goRangeBound lb >> goRangeBound ub goRangeBound (RangeBoundLiteral _) = pure () - goRangeBound (RangeBoundRef r) = goRule r + goRangeBound (RangeBoundRef r) = goRule . Rule r $ HuddleXRule mempty Nothing -- | 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 @@ -1140,8 +1164,10 @@ toCDDL' HuddleConfig {..} hdl = comment "Pseudo-rule introduced by Cuddle to collect root elements" $ "huddle_root_defs" =:= arr (fromList (fmap a topRs)) toCDDLRule :: Rule -> C.Rule HuddleStage - toCDDLRule (Named n t0 c) = - (\x -> C.Rule (C.Name n mempty) Nothing C.AssignEq x (foldMap (HuddleXTerm . C.Comment) c)) + toCDDLRule (Rule (Named n t0 c) extra) = + ( \x -> + C.Rule (C.Name n) Nothing C.AssignEq x (extra & #hxrComment %~ (<> foldMap Comment c)) + ) . C.TOGType . C.Type0 $ toCDDLType1 <$> choiceToNE t0 @@ -1189,13 +1215,13 @@ toCDDL' HuddleConfig {..} hdl = T2Array x -> C.Type1 (C.T2Array $ arrayToCDDLGroup x) Nothing mempty 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 + T2Ref (Named n _ _) -> C.Type1 (C.T2Name (C.Name n) Nothing) Nothing mempty + T2Group (Named n _ _) -> C.Type1 (C.T2Name (C.Name n) 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 + T2GenericRef (GRef n) -> C.Type1 (C.T2Name (C.Name n) Nothing) Nothing mempty toMemberKey :: Key -> C.MemberKey HuddleStage - toMemberKey (LiteralKey (Literal (LText t) _)) = C.MKBareword (C.Name t mempty) + toMemberKey (LiteralKey (Literal (LText t) _)) = C.MKBareword (C.Name t) toMemberKey (LiteralKey v) = C.MKValue $ toCDDLValue v toMemberKey (TypeKey t) = C.MKType (toCDDLType1 t) @@ -1215,23 +1241,23 @@ toCDDL' HuddleConfig {..} hdl = (C.GEType (fmap toMemberKey k) (toCDDLType0 v)) (HuddleXTerm cmnt) - toCDDLPostlude :: Value a -> C.Name HuddleStage - toCDDLPostlude VBool = C.Name "bool" mempty - toCDDLPostlude VUInt = C.Name "uint" mempty - toCDDLPostlude VNInt = C.Name "nint" mempty - toCDDLPostlude VInt = C.Name "int" mempty - toCDDLPostlude VHalf = C.Name "half" mempty - toCDDLPostlude VFloat = C.Name "float" mempty - toCDDLPostlude VDouble = C.Name "double" mempty - toCDDLPostlude VBytes = C.Name "bytes" mempty - toCDDLPostlude VText = C.Name "text" mempty - toCDDLPostlude VAny = C.Name "any" mempty - toCDDLPostlude VNil = C.Name "nil" mempty + toCDDLPostlude :: Value a -> C.Name + toCDDLPostlude VBool = C.Name "bool" + toCDDLPostlude VUInt = C.Name "uint" + toCDDLPostlude VNInt = C.Name "nint" + toCDDLPostlude VInt = C.Name "int" + toCDDLPostlude VHalf = C.Name "half" + toCDDLPostlude VFloat = C.Name "float" + toCDDLPostlude VDouble = C.Name "double" + toCDDLPostlude VBytes = C.Name "bytes" + toCDDLPostlude VText = C.Name "text" + toCDDLPostlude VAny = C.Name "any" + toCDDLPostlude VNil = C.Name "nil" toCDDLConstrainable c = case c of CValue v -> toCDDLPostlude v - CRef r -> C.Name (name r) mempty - CGRef (GRef n) -> C.Name n mempty + CRef r -> C.Name (name r) + CGRef (GRef n) -> C.Name n toCDDLRanged :: Ranged -> C.Type1 HuddleStage toCDDLRanged (Unranged x) = @@ -1244,12 +1270,12 @@ toCDDL' HuddleConfig {..} hdl = toCDDLRangeBound :: RangeBound -> C.Type2 HuddleStage toCDDLRangeBound (RangeBoundLiteral l) = C.T2Value $ toCDDLValue l - toCDDLRangeBound (RangeBoundRef (Named n _ _)) = C.T2Name (C.Name n mempty) Nothing + toCDDLRangeBound (RangeBoundRef (Named n _ _)) = C.T2Name (C.Name n) Nothing toCDDLGroup :: Named Group -> C.Rule HuddleStage toCDDLGroup (Named n (Group t0s) c) = C.Rule - (C.Name n mempty) + (C.Name n) Nothing C.AssignEq ( C.TOGGroup @@ -1262,25 +1288,29 @@ toCDDL' HuddleConfig {..} hdl = arrayEntryToCDDL t0s ) - (foldMap (HuddleXTerm . C.Comment) c) + (HuddleXRule (foldMap Comment c) Nothing) toGenericCall :: GRuleCall -> C.Type2 HuddleStage toGenericCall (Named n gr _) = C.T2Name - (C.Name n mempty) + (C.Name n) (Just . C.GenericArg $ fmap toCDDLType1 (args gr)) toGenRuleDef :: GRuleDef -> C.Rule HuddleStage toGenRuleDef (Named n gr c) = C.Rule - (C.Name n mempty) + (C.Name n) (Just gps) C.AssignEq ( C.TOGType . C.Type0 $ toCDDLType1 <$> choiceToNE (body gr) ) - (foldMap (HuddleXTerm . C.Comment) c) + (HuddleXRule (foldMap Comment c) Nothing) where gps = - C.GenericParam $ fmap (\(GRef t) -> C.Name t mempty) (args gr) + C.GenericParameters $ + fmap (\(GRef t) -> GenericParameter (C.Name t) $ HuddleXTerm mempty) (args gr) + +withGenerator :: HasGenerator a => (forall g m. StatefulGen g m => g -> m WrappedTerm) -> a -> a +withGenerator f = L.set generatorL (Just $ CBORGenerator f) diff --git a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs index c8be61a..8c6f255 100644 --- a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs +++ b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs @@ -65,8 +65,8 @@ class Includable a where include :: a -> HuddleM a instance Includable Rule where - include r = - modify (field @"items" %~ (OMap.|> (r ^. field @"name", HIRule r))) + include r@(Rule x _) = + modify (field @"items" %~ (OMap.|> (name x, HIRule r))) >> pure r instance Includable (Named Group) where diff --git a/src/Codec/CBOR/Cuddle/IndexMappable.hs b/src/Codec/CBOR/Cuddle/IndexMappable.hs index 70063cf..4e79405 100644 --- a/src/Codec/CBOR/Cuddle/IndexMappable.hs +++ b/src/Codec/CBOR/Cuddle/IndexMappable.hs @@ -1,17 +1,17 @@ {-# LANGUAGE DefaultSignatures #-} -module Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) where +module Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..), mapCDDLDropExt) where import Codec.CBOR.Cuddle.CDDL ( CDDL (..), GenericArg (..), - GenericParam (..), + GenericParameter (..), + GenericParameters (..), Group (..), GroupEntry (..), GroupEntryVariant (..), GrpChoice (..), MemberKey (..), - Name (..), Rule (..), TopLevel (..), Type0 (..), @@ -19,6 +19,7 @@ import Codec.CBOR.Cuddle.CDDL ( Type2 (..), TypeOrGroup (..), XCddl, + XRule, XTerm, XXTopLevel, XXType2, @@ -26,13 +27,27 @@ import Codec.CBOR.Cuddle.CDDL ( import Codec.CBOR.Cuddle.CDDL.CTree ( CTreePhase, XCddl (..), + XRule (..), + XTerm (..), + XXType2 (..), + ) +import Codec.CBOR.Cuddle.Huddle ( + HuddleStage, + XCddl (..), + XRule (..), + XTerm (..), + XXTopLevel (..), + XXType2 (..), + ) +import Codec.CBOR.Cuddle.Parser ( + ParserStage, + XCddl (..), + XRule (..), XTerm (..), XXTopLevel (..), XXType2 (..), ) -import Codec.CBOR.Cuddle.Huddle (HuddleStage, XCddl (..), XTerm (..), XXTopLevel (..), XXType2 (..)) -import Codec.CBOR.Cuddle.Parser (ParserStage, XCddl (..), XTerm (..), XXTopLevel (..), XXType2 (..)) -import Codec.CBOR.Cuddle.Pretty (PrettyStage, XCddl (..), XTerm (..), XXTopLevel (..)) +import Codec.CBOR.Cuddle.Pretty (PrettyStage, XCddl (..), XRule (..), XTerm (..), XXTopLevel (..)) import Data.Bifunctor (Bifunctor (..)) import Data.Coerce (Coercible, coerce) import Data.Void (absurd) @@ -42,11 +57,24 @@ class IndexMappable f i j where default mapIndex :: Coercible (f i) (f j) => f i -> f j mapIndex = coerce +mapCDDLDropExt :: + ( IndexMappable XXType2 i j + , IndexMappable XTerm i j + , IndexMappable XRule i j + ) => + CDDL i -> + CDDL j +mapCDDLDropExt (CDDL r tls _) = CDDL (mapIndex r) (foldMap mapTopLevelDropExt tls) [] + where + mapTopLevelDropExt (TopLevelRule x) = [TopLevelRule $ mapIndex x] + mapTopLevelDropExt (XXTopLevel _) = [] + instance ( IndexMappable XCddl i j , IndexMappable XXTopLevel i j , IndexMappable XXType2 i j , IndexMappable XTerm i j + , IndexMappable XRule i j ) => IndexMappable CDDL i j where @@ -55,26 +83,28 @@ instance instance ( IndexMappable XXType2 i j , IndexMappable XTerm i j + , IndexMappable XRule i j ) => IndexMappable Rule i j where - mapIndex (Rule n mg a t c) = Rule (mapIndex n) (mapIndex <$> mg) a (mapIndex t) (mapIndex c) + mapIndex (Rule n mg a t c) = Rule n (mapIndex <$> mg) a (mapIndex t) (mapIndex c) instance ( IndexMappable XXTopLevel i j , IndexMappable XXType2 i j , IndexMappable XTerm i j + , IndexMappable XRule i j ) => IndexMappable TopLevel i j where mapIndex (TopLevelRule r) = TopLevelRule $ mapIndex r mapIndex (XXTopLevel e) = XXTopLevel $ mapIndex e -instance IndexMappable XTerm i j => IndexMappable Name i j where - mapIndex (Name n e) = Name n $ mapIndex e +instance IndexMappable XTerm i j => IndexMappable GenericParameter i j where + mapIndex (GenericParameter n e) = GenericParameter n $ mapIndex e -instance IndexMappable XTerm i j => IndexMappable GenericParam i j where - mapIndex (GenericParam ns) = GenericParam $ mapIndex <$> ns +instance IndexMappable XTerm i j => IndexMappable GenericParameters i j where + mapIndex (GenericParameters ns) = GenericParameters $ mapIndex <$> ns instance ( IndexMappable XXType2 i j @@ -100,7 +130,7 @@ instance IndexMappable GroupEntryVariant i j where mapIndex (GEType mk t) = GEType (mapIndex <$> mk) $ mapIndex t - mapIndex (GERef n ma) = GERef (mapIndex n) (mapIndex <$> ma) + mapIndex (GERef n ma) = GERef n (mapIndex <$> ma) mapIndex (GEGroup g) = GEGroup (mapIndex g) instance @@ -110,7 +140,7 @@ instance IndexMappable MemberKey i j where mapIndex (MKType t) = MKType $ mapIndex t - mapIndex (MKBareword n) = MKBareword $ mapIndex n + mapIndex (MKBareword n) = MKBareword n mapIndex (MKValue x) = MKValue x instance @@ -136,13 +166,13 @@ instance IndexMappable Type2 i j where mapIndex (T2Value v) = T2Value v - mapIndex (T2Name n mg) = T2Name (mapIndex n) (mapIndex <$> mg) + mapIndex (T2Name n mg) = T2Name n (mapIndex <$> mg) mapIndex (T2Group t) = T2Group $ mapIndex t mapIndex (T2Map g) = T2Map $ mapIndex g mapIndex (T2Array a) = T2Array $ mapIndex a - mapIndex (T2Unwrapped n mg) = T2Unwrapped (mapIndex n) (mapIndex <$> mg) + mapIndex (T2Unwrapped n mg) = T2Unwrapped n (mapIndex <$> mg) mapIndex (T2Enum g) = T2Enum $ mapIndex g - mapIndex (T2EnumRef n mg) = T2EnumRef (mapIndex n) (mapIndex <$> mg) + mapIndex (T2EnumRef n mg) = T2EnumRef n (mapIndex <$> mg) mapIndex (T2Tag mt t) = T2Tag mt $ mapIndex t mapIndex (T2DataItem t mt) = T2DataItem t mt mapIndex T2Any = T2Any @@ -180,6 +210,9 @@ instance IndexMappable XCddl ParserStage PrettyStage where instance IndexMappable XTerm ParserStage PrettyStage where mapIndex (ParserXTerm c) = PrettyXTerm c +instance IndexMappable XRule ParserStage PrettyStage where + mapIndex (ParserXRule c) = PrettyXRule c + instance IndexMappable XXType2 ParserStage PrettyStage where mapIndex (ParserXXType2 v) = absurd v @@ -189,16 +222,16 @@ instance IndexMappable XXTopLevel ParserStage PrettyStage where -- ParserStage -> CTreePhase instance IndexMappable XCddl ParserStage CTreePhase where - mapIndex (ParserXCddl c) = CTreeXCddl c - -instance IndexMappable XXTopLevel ParserStage CTreePhase where - mapIndex (ParserXXTopLevel c) = CTreeXXTopLevel c + mapIndex _ = CTreeXCddl instance IndexMappable XXType2 ParserStage CTreePhase where mapIndex (ParserXXType2 c) = CTreeXXType2 c instance IndexMappable XTerm ParserStage CTreePhase where - mapIndex (ParserXTerm c) = CTreeXTerm c + mapIndex _ = CTreeXTerm + +instance IndexMappable XRule ParserStage CTreePhase where + mapIndex _ = CTreeXRule Nothing -- ParserStage -> HuddleStage @@ -217,16 +250,16 @@ instance IndexMappable XTerm ParserStage HuddleStage where -- HuddleStage -> CTreePhase instance IndexMappable XCddl HuddleStage CTreePhase where - mapIndex (HuddleXCddl c) = CTreeXCddl c - -instance IndexMappable XXTopLevel HuddleStage CTreePhase where - mapIndex (HuddleXXTopLevel c) = CTreeXXTopLevel c + mapIndex _ = CTreeXCddl instance IndexMappable XXType2 HuddleStage CTreePhase where mapIndex (HuddleXXType2 c) = CTreeXXType2 c instance IndexMappable XTerm HuddleStage CTreePhase where - mapIndex (HuddleXTerm c) = CTreeXTerm c + mapIndex _ = CTreeXTerm + +instance IndexMappable XRule HuddleStage CTreePhase where + mapIndex (HuddleXRule _ g) = CTreeXRule g -- HuddleStage -> PrettyStage @@ -251,3 +284,5 @@ instance IndexMappable XXTopLevel ParserStage ParserStage instance IndexMappable XXType2 ParserStage ParserStage instance IndexMappable XTerm ParserStage ParserStage + +instance IndexMappable XRule ParserStage ParserStage diff --git a/src/Codec/CBOR/Cuddle/Parser.hs b/src/Codec/CBOR/Cuddle/Parser.hs index d71c18f..c0598f1 100644 --- a/src/Codec/CBOR/Cuddle/Parser.hs +++ b/src/Codec/CBOR/Cuddle/Parser.hs @@ -1,6 +1,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeData #-} {-# LANGUAGE TypeFamilies #-} module Codec.CBOR.Cuddle.Parser where @@ -42,7 +43,7 @@ import Text.Megaparsec.Char hiding (space) import Text.Megaparsec.Char qualified as C import Text.Megaparsec.Char.Lexer qualified as L -data ParserStage +type data ParserStage newtype instance XXTopLevel ParserStage = ParserXXTopLevel Comment deriving (Generic, Show, Eq, ToExpr) @@ -53,12 +54,18 @@ newtype instance XXType2 ParserStage = ParserXXType2 Void newtype instance XTerm ParserStage = ParserXTerm {unParserXTerm :: Comment} deriving (Generic, Semigroup, Monoid, Show, Eq, ToExpr) +newtype instance XRule ParserStage = ParserXRule {unParserXRule :: Comment} + deriving (Generic, Semigroup, Monoid, Show, Eq, ToExpr) + newtype instance XCddl ParserStage = ParserXCddl [Comment] deriving (Generic, Semigroup, Monoid, Show, Eq, ToExpr) instance HasComment (XTerm ParserStage) where commentL = #unParserXTerm +instance HasComment (XRule ParserStage) where + commentL = #unParserXRule + pCDDL :: Parser (CDDL ParserStage) pCDDL = do initialComments <- many (try $ C.space *> pCommentBlock <* notFollowedBy pRule) @@ -80,7 +87,7 @@ pTopLevel = try tlRule <|> tlComment pRule :: Parser (Rule ParserStage) pRule = do name <- pName - genericParam <- optcomp pGenericParam + genericParam <- optcomp pGenericParameters cmt <- space (assign, typeOrGrp) <- choice @@ -91,13 +98,13 @@ pRule = do <*> (TOGType <$> pType0 <* notFollowedBy (space >> (":" <|> "=>"))) , (,) <$> pAssignG <* space <*> (TOGGroup <$> pGrpEntry) ] - pure $ Rule name genericParam assign typeOrGrp (ParserXTerm cmt) + pure $ Rule name genericParam assign typeOrGrp (ParserXRule cmt) -pName :: Parser (Name ParserStage) +pName :: Parser Name pName = label "name" $ do fc <- firstChar rest <- many midChar - pure $ (`Name` mempty) . T.pack $ (fc : rest) + pure $ Name . T.pack $ (fc : rest) where firstChar = letterChar <|> char '@' <|> char '_' <|> char '$' midChar = @@ -121,10 +128,13 @@ pAssignG = , AssignExt <$ "//=" ] -pGenericParam :: Parser (GenericParam ParserStage) -pGenericParam = - GenericParam - <$> between "<" ">" (NE.sepBy1 (space !*> pName <*! space) ",") +pGenericParameter :: Parser (GenericParameter ParserStage) +pGenericParameter = GenericParameter <$> pName <*> pure mempty + +pGenericParameters :: Parser (GenericParameters ParserStage) +pGenericParameters = + GenericParameters + <$> between "<" ">" (NE.sepBy1 (space !*> pGenericParameter <*! space) ",") pGenericArg :: Parser (GenericArg ParserStage) pGenericArg = @@ -158,7 +168,7 @@ pType2 = , T2Group <$> label "group" ("(" *> pType0Cmt <* ")") , T2Map <$> label "map" ("{" *> pGroup <* "}") , T2Array <$> label "array" ("[" *> space !*> pGroup <*! space <* "]") - , T2Unwrapped <$> ("~" *> space !*> pName) <*> optional pGenericArg + , T2Unwrapped <$> ("~" *> space *> pName) <*> optional pGenericArg , do _ <- "&" cmt <- space diff --git a/src/Codec/CBOR/Cuddle/Pretty.hs b/src/Codec/CBOR/Cuddle/Pretty.hs index 5914fbf..e45e188 100644 --- a/src/Codec/CBOR/Cuddle/Pretty.hs +++ b/src/Codec/CBOR/Cuddle/Pretty.hs @@ -1,6 +1,7 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeData #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-orphans #-} @@ -24,6 +25,7 @@ import Codec.CBOR.Cuddle.Pretty.Columnar ( ) import Codec.CBOR.Cuddle.Pretty.Utils (renderedLen, softspace) import Data.ByteString.Char8 qualified as BS +import Data.Default.Class (Default) import Data.Foldable (Foldable (..)) import Data.List.NonEmpty qualified as NE import Data.String (IsString, fromString) @@ -34,7 +36,7 @@ import GHC.Generics (Generic) import Optics.Core ((^.)) import Prettyprinter -data PrettyStage +type data PrettyStage newtype instance XXTopLevel PrettyStage = PrettyXXTopLevel Comment deriving (Generic, CollectComments, ToExpr, Show, Eq) @@ -48,9 +50,16 @@ newtype instance XTerm PrettyStage = PrettyXTerm {unPrettyXTerm :: Comment} newtype instance XCddl PrettyStage = PrettyXCddl [Comment] deriving (Generic, CollectComments, ToExpr, Show, Eq) +newtype instance XRule PrettyStage = PrettyXRule {unPrettyXRule :: Comment} + deriving (Generic, CollectComments, ToExpr, Show, Eq) + deriving newtype (Default) + instance HasComment (XTerm PrettyStage) where commentL = #unPrettyXTerm +instance HasComment (XRule PrettyStage) where + commentL = #unPrettyXRule + instance Pretty (CDDL PrettyStage) where pretty = vsep . fmap pretty . NE.toList . cddlTopLevel @@ -58,8 +67,8 @@ instance Pretty (TopLevel PrettyStage) where pretty (XXTopLevel (PrettyXXTopLevel cmt)) = pretty cmt pretty (TopLevelRule x) = pretty x <> hardline -instance Pretty (Name PrettyStage) where - pretty (Name name (PrettyXTerm cmt)) = pretty name <> prettyCommentNoBreakWS cmt +instance Pretty Name where + pretty (Name name) = pretty name data CommentRender = PreComment @@ -102,9 +111,12 @@ instance Pretty (GenericArg PrettyStage) where | null (collectComments l) = group . cEncloseSep "<" ">" "," $ fmap pretty l | otherwise = columnarListing "<" ">" "," . Columnar $ singletonRow . pretty <$> l -instance Pretty (GenericParam PrettyStage) where - pretty (GenericParam (NE.toList -> l)) - | null (collectComments l) = group . cEncloseSep "<" ">" "," $ fmap pretty l +instance Pretty (GenericParameter PrettyStage) where + pretty (GenericParameter n (PrettyXTerm c)) = pretty n <> prettyCommentNoBreakWS c + +instance Pretty (GenericParameters PrettyStage) where + pretty (GenericParameters (NE.toList -> l)) + | null (collectComments l) = group . cEncloseSep "<" ">" "," $ pretty <$> l | otherwise = columnarListing "<" ">" "," . Columnar $ singletonRow . pretty <$> l instance Pretty (Type0 PrettyStage) where diff --git a/test/Main.hs b/test/Main.hs index cecc028..bf1809d 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -2,6 +2,7 @@ module Main (main) where import System.IO (BufferMode (..), hSetBuffering, hSetEncoding, stdout, utf8) import Test.Codec.CBOR.Cuddle.CDDL.Examples qualified as Examples +import Test.Codec.CBOR.Cuddle.CDDL.GeneratorSpec qualified as Generator import Test.Codec.CBOR.Cuddle.CDDL.Parser (parserSpec) import Test.Codec.CBOR.Cuddle.Huddle (huddleSpec) import Test.Hspec @@ -18,6 +19,7 @@ main = do hSetBuffering stdout LineBuffering hSetEncoding stdout utf8 hspecWith hspecConfig $ do - describe "cddlParser" parserSpec + describe "Parser" parserSpec describe "Huddle" huddleSpec describe "Examples" Examples.spec + describe "Generator" Generator.spec diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs index c4fe64f..d1d09ad 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs @@ -10,7 +10,7 @@ import Codec.CBOR.Cuddle.CDDL.Resolve ( NameResolutionFailure (..), fullResolveCDDL, ) -import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) +import Codec.CBOR.Cuddle.IndexMappable (mapCDDLDropExt) import Codec.CBOR.Cuddle.Parser (pCDDL) import Data.Text.IO qualified as T import Paths_cuddle (getDataFileName) @@ -26,7 +26,7 @@ tryValidateFile filePath = do cddl <- case parse pCDDL "" contents of Right x -> pure $ appendPostlude x Left x -> fail $ "Failed to parse the file:\n" <> errorBundlePretty x - pure . fullResolveCDDL $ mapIndex cddl + pure . fullResolveCDDL $ mapCDDLDropExt cddl validateExpectSuccess :: FilePath -> Spec validateExpectSuccess filePath = it ("Successfully validates " <> filePath) $ do diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs index dd5ae35..ea63c5e 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Gen.hs @@ -10,7 +10,7 @@ import Codec.CBOR.Cuddle.CDDL import Codec.CBOR.Cuddle.CDDL.CtlOp import Codec.CBOR.Cuddle.Comments (Comment (..)) import Codec.CBOR.Cuddle.Parser (ParserStage, XTerm (..)) -import Codec.CBOR.Cuddle.Pretty (PrettyStage, XTerm (..), XXTopLevel (..)) +import Codec.CBOR.Cuddle.Pretty (PrettyStage, XRule (..), XTerm (..), XXTopLevel (..)) import Data.ByteString (ByteString) import Data.ByteString qualified as BS import Data.List.NonEmpty qualified as NE @@ -27,6 +27,8 @@ deriving newtype instance Arbitrary (XXTopLevel PrettyStage) deriving newtype instance Arbitrary (XTerm PrettyStage) +deriving newtype instance Arbitrary (XRule PrettyStage) + instance Arbitrary (TopLevel PrettyStage) where arbitrary = Gen.oneof @@ -58,19 +60,17 @@ nameMidChars = nameFstChars <> ['1' .. '9'] <> ['-', '.'] nameEndChars :: [Char] nameEndChars = nameFstChars <> ['1' .. '9'] -instance (Monoid (XTerm i), Arbitrary (XTerm i)) => Arbitrary (Name i) where +instance Arbitrary Name where arbitrary = let veryShortListOf = resize 3 . listOf in do fstChar <- elements nameFstChars midChar <- veryShortListOf . elements $ nameMidChars lastChar <- elements nameEndChars - pure $ Name (T.pack $ fstChar : midChar <> [lastChar]) mempty + pure $ Name (T.pack $ fstChar : midChar <> [lastChar]) - shrink (Name xs cmt) = - Name - <$> fmap T.pack (filter isValidName (shrink $ T.unpack xs)) - <*> shrink cmt + shrink (Name xs) = + Name <$> fmap T.pack (filter isValidName (shrink $ T.unpack xs)) where isValidName [] = False isValidName (h : tl) = h `elem` nameFstChars && isValidNameTail tl @@ -83,15 +83,24 @@ instance Arbitrary Assign where arbitrary = Gen.elements [AssignEq, AssignExt] shrink = genericShrink -instance (Monoid (XTerm i), Arbitrary (XTerm i)) => Arbitrary (GenericParam i) where - arbitrary = GenericParam <$> nonEmpty arbitrary - shrink (GenericParam neName) = GenericParam <$> shrinkNE neName +instance Arbitrary (XTerm i) => Arbitrary (GenericParameter i) where + arbitrary = GenericParameter <$> arbitrary <*> arbitrary + +instance (Monoid (XTerm i), Arbitrary (XTerm i)) => Arbitrary (GenericParameters i) where + arbitrary = GenericParameters <$> nonEmpty arbitrary + shrink (GenericParameters neName) = GenericParameters <$> shrinkNE neName instance (Arbitrary (XTerm i), Monoid (XTerm i)) => Arbitrary (GenericArg i) where arbitrary = GenericArg <$> nonEmpty arbitrary shrink (GenericArg neArg) = GenericArg <$> shrinkNE neArg -instance (Monoid (XTerm i), Arbitrary (XTerm i)) => Arbitrary (Rule i) where +instance + ( Monoid (XTerm i) + , Arbitrary (XTerm i) + , Arbitrary (XRule i) + ) => + Arbitrary (Rule i) + where arbitrary = Rule <$> arbitrary diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/GeneratorSpec.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/GeneratorSpec.hs new file mode 100644 index 0000000..16c6577 --- /dev/null +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/GeneratorSpec.hs @@ -0,0 +1,58 @@ +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} + +module Test.Codec.CBOR.Cuddle.CDDL.GeneratorSpec (spec) where + +import Codec.CBOR.Cuddle.CBOR.Gen (generateCBORTerm) +import Codec.CBOR.Cuddle.CDDL.CBORGenerator (WrappedTerm (..)) +import Codec.CBOR.Cuddle.CDDL.Resolve (fullResolveCDDL) +import Codec.CBOR.Cuddle.Huddle ( + Huddle, + HuddleItem (..), + a, + arr, + collectFrom, + toCDDL, + withGenerator, + (=:=), + ) +import Codec.CBOR.Cuddle.Huddle qualified as H +import Codec.CBOR.Cuddle.IndexMappable (mapCDDLDropExt) +import Codec.CBOR.Term (Term) +import Codec.CBOR.Term qualified as C +import System.Random (mkStdGen) +import System.Random.Stateful (UniformRange (..)) +import Test.Hspec (Expectation, Spec, describe, it, shouldBe) + +foo :: H.Rule +foo = withGenerator (fmap (S . C.TInt) . uniformRM (4, 6)) $ "foo" =:= arr [1, 2, 3] + +simpleTermExample :: Huddle +simpleTermExample = + collectFrom + [ HIRule foo + ] + +refTermExample :: Huddle +refTermExample = + collectFrom + [ HIRule foo + , HIRule $ "bar" =:= arr [0, a foo] + ] + +huddleShouldGenerate :: Huddle -> Term -> Expectation +huddleShouldGenerate huddle term = do + let g = mkStdGen 12345 + ct <- case fullResolveCDDL . mapCDDLDropExt $ toCDDL huddle of + Right x -> pure x + Left err -> fail $ "Failed to resolve CDDL: " <> show err + generateCBORTerm ct "foo" g `shouldBe` term + +spec :: Spec +spec = do + describe "Custom generators" $ do + describe "Huddle" $ do + it "If a term has a custom generator then it is used" $ + simpleTermExample `huddleShouldGenerate` C.TInt 5 + it "Custom generator works when called via reference" $ + refTermExample `huddleShouldGenerate` C.TInt 5 diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs index efedf4b..72130fc 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Parser.hs @@ -35,7 +35,7 @@ parserSpec = do roundtripSpec :: Spec roundtripSpec = describe "Roundtripping should be id" $ do - it "Trip Name" $ tripIndexed pName + it "Trip Name" $ trip pName xit "Trip Value" $ trip pValue xit "Trip Type0" $ tripIndexed pType0 xit "Trip GroupEntry" $ tripIndexed pGrpEntry @@ -105,11 +105,11 @@ occurSpec = describe "pOccur" $ do nameSpec :: SpecWith () nameSpec = describe "pName" $ do it "Parses a boring name" $ - parse pName "" "coin" `shouldParse` Name "coin" mempty + parse pName "" "coin" `shouldParse` Name "coin" it "Allows . in the middle" $ - parse pName "" "coin.me" `shouldParse` Name "coin.me" mempty + parse pName "" "coin.me" `shouldParse` Name "coin.me" it "Allows $ as the last character" $ - parse pName "" "coin.me$" `shouldParse` Name "coin.me$" mempty + parse pName "" "coin.me$" `shouldParse` Name "coin.me$" it "Doesn't allow . as a last character" $ parse pName "" "coin." `shouldFailWith` err 5 ueof @@ -118,14 +118,14 @@ genericSpec = describe "generics" $ do it "Parses a simple value generic" $ parse pRule "" "a = b<0>" `shouldParse` Rule - (Name "a" mempty) + (Name "a") Nothing AssignEq ( TOGType ( Type0 ( Type1 ( T2Name - (Name "b" mempty) + (Name "b") ( Just ( GenericArg ( Type1 @@ -147,14 +147,14 @@ genericSpec = describe "generics" $ do it "Parses a range as a generic" $ parse pRule "" "a = b<0 ... 1>" `shouldParse` Rule - (Name "a" mempty) + (Name "a") Nothing AssignEq ( TOGType ( Type0 ( Type1 ( T2Name - (Name "b" mempty) + (Name "b") ( Just ( GenericArg ( Type1 @@ -195,7 +195,7 @@ type2Spec = describe "type2" $ do ( Just ( MKType ( Type1 - { t1Main = T2Name (Name {name = "int", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "int"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -205,7 +205,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "string", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "string"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -234,7 +234,7 @@ type2Spec = describe "type2" $ do ( Just ( MKType ( Type1 - { t1Main = T2Name (Name {name = "int", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "int"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -244,7 +244,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "string", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "string"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -276,7 +276,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "string", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "string"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -295,7 +295,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "int", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "int"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -314,7 +314,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "bytes", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "bytes"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -345,7 +345,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "int", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "int"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -367,7 +367,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "string", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "string"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -477,7 +477,7 @@ type2Spec = describe "type2" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "soon", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "soon"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -505,7 +505,7 @@ grpEntrySpec = describe "GroupEntry" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "int", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "int"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -524,7 +524,7 @@ grpEntrySpec = describe "GroupEntry" $ do ( Type0 { t0Type1 = Type1 - { t1Main = T2Name (Name {name = "int", nameExt = mempty}) Nothing + { t1Main = T2Name (Name {name = "int"}) Nothing , t1TyOp = Nothing , t1Comment = mempty } @@ -545,7 +545,7 @@ grpEntrySpec = describe "GroupEntry" $ do Type1 { t1Main = T2Name - (Name {name = "a", nameExt = mempty}) + (Name {name = "a"}) ( Just ( GenericArg ( Type1 @@ -580,7 +580,7 @@ grpEntrySpec = describe "GroupEntry" $ do (Just (OIBounded (Just 0) Nothing)) ( GEType Nothing - (Type0 (Type1 (T2Name (Name "a" mempty) Nothing) Nothing mempty :| [])) + (Type0 (Type1 (T2Name (Name "a") Nothing) Nothing mempty :| [])) ) mempty @@ -595,7 +595,7 @@ grpChoiceSpec = describe "GroupChoice" $ do Nothing ( Type0 ( Type1 - (T2Name (Name "int" mempty) Nothing) + (T2Name (Name "int") Nothing) Nothing mempty :| [] @@ -612,7 +612,7 @@ type1Spec = describe "Type1" $ do it "Should parse a basic control operator" $ parse pType1 "" "uint .size 3" `shouldParse` Type1 - (T2Name (Name "uint" mempty) Nothing) + (T2Name (Name "uint") Nothing) (Just (CtrlOp CtlOp.Size, T2Value (value $ VUInt 3))) mempty describe "RangeOp" $ do @@ -647,7 +647,7 @@ qcFoundSpec = Just ( CtrlOp CtlOp.Ge , T2EnumRef - (Name {name = "i", nameExt = mempty}) + (Name {name = "i"}) ( Just ( GenericArg ( Type1 @@ -666,7 +666,7 @@ qcFoundSpec = } parseExample "S = 0* ()" pRule $ Rule - (Name "S" mempty) + (Name "S") Nothing AssignEq ( TOGGroup @@ -681,7 +681,7 @@ qcFoundSpec = "W = \"6 ybe2ddl8frq0vqa8zgrk07khrljq7p plrufpd1sff3p95\" : \"u\"" pRule ( Rule - (Name "W" mempty) + (Name "W") Nothing AssignEq ( TOGGroup diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs index 3666418..20644c6 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Pretty.hs @@ -23,6 +23,7 @@ import Codec.CBOR.Cuddle.CDDL ( value, ) import Codec.CBOR.Cuddle.Pretty (PrettyStage) +import Data.Default.Class (Default (..)) import Data.List.NonEmpty (NonEmpty (..)) import Data.Text qualified as T import Data.TreeDiff (ToExpr (..), prettyExpr) @@ -41,7 +42,7 @@ prettyPrintsTo x s = assertEqual (show . prettyExpr $ toExpr x) s rendered rendered = renderString (layoutPretty defaultLayoutOptions (pretty x)) t2Name :: Type2 PrettyStage -t2Name = T2Name (Name "a" mempty) mempty +t2Name = T2Name (Name "a") mempty t1Name :: Type1 PrettyStage t1Name = Type1 t2Name Nothing mempty @@ -120,12 +121,12 @@ drep = ) ) ) - mempty + def unitSpec :: Spec unitSpec = describe "HUnit" $ do describe "Name" $ do - it "names" $ Name @PrettyStage "a" "" `prettyPrintsTo` "a" + it "names" $ Name "a" `prettyPrintsTo` "a" describe "Type0" $ do it "name" $ Type0 @PrettyStage (t1Name :| []) `prettyPrintsTo` "a" describe "Type1" $ do @@ -133,7 +134,7 @@ unitSpec = describe "HUnit" $ do describe "Type2" $ do it "T2Name" $ t2Name `prettyPrintsTo` "a" describe "T2Array" $ do - let groupEntryName = GroupEntry Nothing (GERef (Name "a" mempty) Nothing) "" + let groupEntryName = GroupEntry Nothing (GERef (Name "a") Nothing) "" it "one element" $ T2Array (Group (GrpChoice [groupEntryName] mempty :| [])) `prettyPrintsTo` "[a]" it "two elements" $ @@ -181,11 +182,11 @@ unitSpec = describe "HUnit" $ do describe "Rule" $ do it "simple assignment" $ Rule @PrettyStage - (Name "a" mempty) + (Name "a") Nothing AssignEq - (TOGType (Type0 (Type1 (T2Name (Name "b" mempty) mempty) Nothing mempty :| []))) - mempty + (TOGType (Type0 (Type1 (T2Name (Name "b") mempty) Nothing mempty :| []))) + def `prettyPrintsTo` "a = b" xit "drep" $ drep diff --git a/test/Test/Codec/CBOR/Cuddle/Huddle.hs b/test/Test/Codec/CBOR/Cuddle/Huddle.hs index f0d42b3..3704b8a 100644 --- a/test/Test/Codec/CBOR/Cuddle/Huddle.hs +++ b/test/Test/Codec/CBOR/Cuddle/Huddle.hs @@ -1,21 +1,73 @@ {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeData #-} +{-# LANGUAGE TypeFamilies #-} {- HLINT ignore "Redundant bracket" -} module Test.Codec.CBOR.Cuddle.Huddle where import Codec.CBOR.Cuddle.CDDL (CDDL, fromRules, sortCDDL) +import Codec.CBOR.Cuddle.Comments (Comment) import Codec.CBOR.Cuddle.Huddle import Codec.CBOR.Cuddle.IndexMappable (IndexMappable (..)) import Codec.CBOR.Cuddle.Parser import Data.Text qualified as T +import Data.Void (Void) +import GHC.Generics (Generic) import Test.Codec.CBOR.Cuddle.CDDL.Pretty qualified as Pretty import Test.Hspec import Test.Hspec.Megaparsec import Text.Megaparsec import Prelude hiding ((/)) +type data TestStage + +newtype instance XCddl TestStage = TestXCddl [Comment] + deriving (Generic, Show, Eq) + +instance IndexMappable XCddl ParserStage TestStage where + mapIndex (ParserXCddl x) = TestXCddl x + +instance IndexMappable XCddl HuddleStage TestStage where + mapIndex (HuddleXCddl x) = TestXCddl x + +newtype instance XTerm TestStage = TestXTerm Comment + deriving (Generic, Show, Eq) + +instance IndexMappable XTerm ParserStage TestStage where + mapIndex (ParserXTerm x) = TestXTerm x + +instance IndexMappable XTerm HuddleStage TestStage where + mapIndex (HuddleXTerm x) = TestXTerm x + +newtype instance XRule TestStage = TestXRule Comment + deriving (Generic, Show, Eq) + +instance IndexMappable XRule ParserStage TestStage where + mapIndex (ParserXRule x) = TestXRule x + +instance IndexMappable XRule HuddleStage TestStage where + mapIndex (HuddleXRule x _) = TestXRule x + +newtype instance XXTopLevel TestStage = TestXXTopLevel Comment + deriving (Generic, Show, Eq) + +instance IndexMappable XXTopLevel ParserStage TestStage where + mapIndex (ParserXXTopLevel x) = TestXXTopLevel x + +instance IndexMappable XXTopLevel HuddleStage TestStage where + mapIndex (HuddleXXTopLevel x) = TestXXTopLevel x + +newtype instance XXType2 TestStage = TestXXType2 Void + deriving (Generic, Show, Eq) + +instance IndexMappable XXType2 ParserStage TestStage where + mapIndex (ParserXXType2 x) = TestXXType2 x + +instance IndexMappable XXType2 HuddleStage TestStage where + mapIndex (HuddleXXType2 x) = TestXXType2 x + huddleSpec :: Spec huddleSpec = describe "huddle" $ do basicAssign @@ -156,10 +208,10 @@ shouldMatchParse :: shouldMatchParse x parseFun input = parse parseFun "" (T.pack input) `shouldParse` x shouldMatchParseCDDL :: - CDDL HuddleStage -> + CDDL TestStage -> String -> Expectation shouldMatchParseCDDL x = shouldMatchParse x . fmap mapIndex $ pCDDL -toSortedCDDL :: Huddle -> CDDL HuddleStage -toSortedCDDL = fromRules . sortCDDL . toCDDLNoRoot +toSortedCDDL :: Huddle -> CDDL TestStage +toSortedCDDL = mapIndex . fromRules . sortCDDL . toCDDLNoRoot