diff --git a/CHANGELOG.md b/CHANGELOG.md index 51b9222..fd8a424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog for `cuddle` +## 1.1.0.0 + +* Remove `CTreeRoot'` +* Changed the type in `CTreeRoot` to a map of resolved `CTree`s +* Changed the type of the first argument for `generateCBORTerm` and + `generateCBORTerm'` to `CTreeRoot` +* Removed all exports in `Codec.CBOR.Cuddle.CBOR.Validator` except for + `validateCBOR` and `validateCBOR'` + ## 1.0.0.0 * First official release to Hackage diff --git a/src/Codec/CBOR/Cuddle/CBOR/Gen.hs b/src/Codec/CBOR/Cuddle/CBOR/Gen.hs index c00b0db..69a5154 100644 --- a/src/Codec/CBOR/Cuddle/CBOR/Gen.hs +++ b/src/Codec/CBOR/Cuddle/CBOR/Gen.hs @@ -25,11 +25,11 @@ import Codec.CBOR.Cuddle.CDDL ( Value (..), ValueVariant (..), ) -import Codec.CBOR.Cuddle.CDDL.CTree (CTree, CTreeRoot' (..)) +import Codec.CBOR.Cuddle.CDDL.CTree (CTree, CTreeRoot (..)) import Codec.CBOR.Cuddle.CDDL.CTree qualified as CTree import Codec.CBOR.Cuddle.CDDL.CtlOp qualified as CtlOp import Codec.CBOR.Cuddle.CDDL.Postlude (PTerm (..)) -import Codec.CBOR.Cuddle.CDDL.Resolve (MonoRef (..)) +import Codec.CBOR.Cuddle.CDDL.Resolve (MonoRef (..), MonoReferenced) import Codec.CBOR.Term (Term (..)) import Codec.CBOR.Term qualified as CBOR import Codec.CBOR.Write qualified as CBOR @@ -41,7 +41,6 @@ import Data.Bifunctor (second) import Data.ByteString (ByteString) import Data.ByteString.Base16 qualified as Base16 import Data.Functor ((<&>)) -import Data.Functor.Identity (Identity (runIdentity)) import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as Map import Data.Maybe (fromMaybe) @@ -69,7 +68,7 @@ import System.Random.Stateful ( -- | Generator context, parametrised over the type of the random seed newtype GenEnv = GenEnv - { cddl :: CTreeRoot' Identity MonoRef + { cddl :: CTreeRoot MonoReferenced } deriving (Generic) @@ -121,8 +120,8 @@ newtype M g a = M {runM :: StateT (GenState g) (Reader GenEnv) a} () (MonadState (StateT (GenState g) (Reader GenEnv))) deriving - ( HasSource "cddl" (CTreeRoot' Identity MonoRef) - , HasReader "cddl" (CTreeRoot' Identity MonoRef) + ( HasSource "cddl" (CTreeRoot MonoReferenced) + , HasReader "cddl" (CTreeRoot MonoReferenced) ) via Field "cddl" @@ -253,11 +252,11 @@ pattern G xs = GroupTerm xs -- Generator functions -------------------------------------------------------------------------------- -genForCTree :: RandomGen g => CTree MonoRef -> M g WrappedTerm +genForCTree :: RandomGen g => CTree MonoReferenced -> M g WrappedTerm genForCTree (CTree.Literal v) = S <$> genValue v genForCTree (CTree.Postlude pt) = S <$> genPostlude pt genForCTree (CTree.Map nodes) = do - items <- pairTermList . flattenWrappedList <$> traverse genForNode nodes + items <- pairTermList . flattenWrappedList <$> traverse genForCTree nodes case items of Just ts -> let @@ -270,17 +269,17 @@ genForCTree (CTree.Map nodes) = do pure . S $ TMap tsNodup Nothing -> error "Single terms in map context" genForCTree (CTree.Array nodes) = do - items <- singleTermList . flattenWrappedList <$> traverse genForNode nodes + items <- singleTermList . flattenWrappedList <$> traverse genForCTree nodes case items of Just ts -> pure . S $ TList ts Nothing -> error "Something weird happened which shouldn't be possible" genForCTree (CTree.Choice (NE.toList -> nodes)) = do ix <- genUniformRM (0, length nodes - 1) - genForNode $ nodes !! ix -genForCTree (CTree.Group nodes) = G <$> traverse genForNode nodes + genForCTree $ nodes !! ix +genForCTree (CTree.Group nodes) = G <$> traverse genForCTree nodes genForCTree (CTree.KV key value _cut) = do - kg <- genForNode key - vg <- genForNode value + kg <- genForCTree key + vg <- genForCTree value case (kg, vg) of (S k, S v) -> pure $ P k v _ -> @@ -290,11 +289,11 @@ genForCTree (CTree.KV key value _cut) = do <> " => " <> show value genForCTree (CTree.Occur item occurs) = - applyOccurenceIndicator occurs (genForNode item) + applyOccurenceIndicator occurs (genForCTree item) genForCTree (CTree.Range from to _bounds) = do -- TODO Handle bounds correctly - term1 <- genForNode from - term2 <- genForNode to + term1 <- genForCTree from + term2 <- genForCTree to case (term1, term2) of (S (TInt a), S (TInt b)) -> genUniformRM (a, b) <&> S . TInt (S (TInt a), S (TInteger b)) -> genUniformRM (fromIntegral a, b) <&> S . TInteger @@ -304,27 +303,23 @@ genForCTree (CTree.Range from to _bounds) = do (S (TDouble a), S (TDouble b)) -> genUniformRM (a, b) <&> S . TDouble x -> error $ "Cannot apply range operator to non-numeric types: " <> show x genForCTree (CTree.Control op target controller) = do - tt <- resolveIfRef target - ct <- resolveIfRef controller - case (op, ct) of - (CtlOp.Le, CTree.Literal (Value (VUInt n) _)) -> case tt of + case (op, controller) of + (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.Lt, CTree.Literal (Value (VUInt n) _)) -> case tt of + (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.Size, CTree.Literal (Value (VUInt n) _)) -> case tt of + (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) CTree.Postlude PTUInt -> S . TInteger <$> genUniformRM (0, 2 ^ n - 1) _ -> error "Cannot apply size operator to target " (CtlOp.Size, CTree.Range {CTree.from, CTree.to}) -> do - f <- resolveIfRef from - t <- resolveIfRef to - case (f, t) of - (CTree.Literal (Value (VUInt f1) _), CTree.Literal (Value (VUInt t1) _)) -> case tt of + case (from, to) of + (CTree.Literal (Value (VUInt f1) _), CTree.Literal (Value (VUInt t1) _)) -> case target of CTree.Postlude PTText -> genUniformRM (fromIntegral f1, fromIntegral t1) >>= (fmap (S . TString) . genText) @@ -334,7 +329,7 @@ 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 tt + _ -> error $ "Cannot apply size operator to target: " <> show target _ -> error $ "Invalid controller for .size operator: " @@ -344,39 +339,38 @@ genForCTree (CTree.Control op target controller) = do "Invalid controller for .size operator: " <> show controller (CtlOp.Cbor, _) -> do - enc <- genForCTree ct + enc <- genForCTree controller case enc of S x -> pure . S . TBytes . CBOR.toStrictByteString $ CBOR.encodeTerm x _ -> error "Controller does not correspond to a single term" - _ -> genForNode target -genForCTree (CTree.Enum node) = do - tree <- resolveIfRef node + _ -> genForCTree target +genForCTree (CTree.Enum tree) = do case tree of - CTree.Group nodes -> do - ix <- genUniformRM (0, length nodes) - genForNode $ nodes !! ix + CTree.Group trees -> do + ix <- genUniformRM (0, length trees) + genForCTree $ trees !! ix _ -> error "Attempt to form an enum from something other than a group" -genForCTree (CTree.Unwrap node) = genForCTree =<< resolveIfRef node +genForCTree (CTree.Unwrap node) = genForCTree node genForCTree (CTree.Tag tag node) = do - enc <- genForNode node + enc <- genForCTree node 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 -genForNode :: RandomGen g => CTree.Node MonoRef -> M g WrappedTerm -genForNode = genForCTree <=< resolveIfRef +genForNode :: RandomGen g => CTree.Node MonoReferenced -> M g WrappedTerm +genForNode = genForCTree <=< resolveRef --- | Take something which might be a reference and resolve it to the relevant --- Tree, following multiple links if necessary. -resolveIfRef :: RandomGen g => CTree.Node MonoRef -> M g (CTree MonoRef) -resolveIfRef (MIt a) = pure a -resolveIfRef (MRuleRef n) = do +-- | 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 (CTreeRoot cddl) <- ask @"cddl" -- Since we follow a reference, we increase the 'depth' of the gen monad. modify @"depth" (+ 1) case Map.lookup n cddl of Nothing -> error $ "Unbound reference: " <> show n - Just val -> resolveIfRef $ runIdentity val + Just val -> pure val -- | Generate a CBOR Term corresponding to a top-level name. -- @@ -392,7 +386,7 @@ genForName n = do case Map.lookup n cddl of Nothing -> error $ "Unbound reference: " <> show n Just val -> - genForNode (runIdentity val) >>= \case + genForCTree val >>= \case S x -> pure x _ -> error $ @@ -440,13 +434,13 @@ genValueVariant (VBool b) = pure $ TBool b -- Generator functions -------------------------------------------------------------------------------- -generateCBORTerm :: RandomGen g => CTreeRoot' Identity MonoRef -> Name -> 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' Identity MonoRef -> Name -> 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 d5b0b8b..d3894fb 100644 --- a/src/Codec/CBOR/Cuddle/CBOR/Validator.hs +++ b/src/Codec/CBOR/Cuddle/CBOR/Validator.hs @@ -1,9 +1,11 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} -{-# OPTIONS_GHC -Wno-incomplete-patterns #-} -module Codec.CBOR.Cuddle.CBOR.Validator where +module Codec.CBOR.Cuddle.CBOR.Validator ( + validateCBOR, + validateCBOR', +) where import Codec.CBOR.Cuddle.CDDL hiding (CDDL, Group, Rule) import Codec.CBOR.Cuddle.CDDL.CTree @@ -21,7 +23,6 @@ import Data.ByteString qualified as BS import Data.ByteString.Lazy qualified as BSL import Data.Function ((&)) import Data.Functor ((<&>)) -import Data.Functor.Identity import Data.IntSet qualified as IS import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as Map @@ -30,13 +31,13 @@ import Data.Text qualified as T import Data.Text.Lazy qualified as TL import Data.Word import GHC.Float +import GHC.Stack (HasCallStack) import System.Exit import System.IO import Text.Regex.TDFA -type CDDL = CTreeRoot' Identity MonoRef -type Rule = Node MonoRef -type ResolvedRule = CTree MonoRef +type CDDL = CTreeRoot MonoReferenced +type Rule = CTree MonoReferenced data CBORTermResult = CBORTermResult Term CDDLResult deriving (Show) @@ -135,8 +136,8 @@ validateCBOR' bs rule cddl@(CTreeRoot tree) = Left e -> error $ show e Right (rest, term) -> if BSL.null rest - then runReader (validateTerm term (runIdentity $ tree Map.! rule)) cddl - else runReader (validateTerm (TBytes bs) (runIdentity $ tree Map.! rule)) cddl + then runReader (validateTerm term (tree Map.! rule)) cddl + else runReader (validateTerm (TBytes bs) (tree Map.! rule)) cddl -------------------------------------------------------------------------------- -- Terms @@ -184,7 +185,9 @@ validateTerm term rule = -- For this reason, we cannot assume that bounds or literals are going to be -- Ints, so we convert everything to Integer. validateInteger :: - MonadReader CDDL m => + ( HasCallStack + , MonadReader CDDL m + ) => Integer -> Rule -> m CDDLResult @@ -233,25 +236,33 @@ validateInteger i rule = (Literal (Value (VBignum n) _), Literal (Value (VNInt (fromIntegral -> m)) _)) -> n <= i && range bound i (-m) (Literal (Value (VUInt (fromIntegral -> n)) _), Literal (Value (VBignum m) _)) -> n <= i && range bound i m (Literal (Value (VNInt (fromIntegral -> n)) _), Literal (Value (VBignum m) _)) -> (-n) <= i && range bound i m + _ -> error "Not yet implemented" -- a = &(x, y, z) Enum g -> getRule g >>= \case - Group g' -> validateInteger i (MIt (Choice (NE.fromList g'))) <&> replaceRule + Group g' -> validateInteger i (Choice (NE.fromList g')) <&> replaceRule + _ -> error "Not yet implemented" -- a = x: y -- Note KV cannot appear on its own, but we will use this when validating -- lists. KV _ v _ -> validateInteger i v <&> replaceRule - Tag 2 (MIt (Postlude PTBytes)) -> pure Valid - Tag 3 (MIt (Postlude PTBytes)) -> pure Valid + Tag 2 (Postlude PTBytes) -> pure Valid + Tag 3 (Postlude PTBytes) -> pure Valid _ -> pure UnapplicableRule -- | Controls for an Integer controlInteger :: - forall m. MonadReader CDDL m => Integer -> CtlOp -> Rule -> m (Either (Maybe CBORTermResult) ()) + forall m. + (HasCallStack, MonadReader CDDL m) => + Integer -> + CtlOp -> + Rule -> + m (Either (Maybe CBORTermResult) ()) controlInteger i Size ctrl = getRule ctrl <&> \case Literal (Value (VUInt sz) _) -> boolCtrl $ 0 <= i && i < 256 ^ sz + _ -> error "Not yet implemented" controlInteger i Bits ctrl = do indices <- getRule ctrl >>= \case @@ -259,6 +270,7 @@ controlInteger i Bits ctrl = do Choice nodes -> getIndicesOfChoice nodes Range ff tt incl -> getIndicesOfRange ff tt incl Enum g -> getIndicesOfEnum g + _ -> error "Not yet implemented" pure $ boolCtrl $ go (IS.fromList (map fromIntegral indices)) i 0 where go _ 0 _ = True @@ -272,36 +284,43 @@ controlInteger i Lt ctrl = Literal (Value (VUInt i') _) -> i < fromIntegral i' Literal (Value (VNInt i') _) -> i < -fromIntegral i' Literal (Value (VBignum i') _) -> i < i' + _ -> error "Not yet implemented" controlInteger i Gt ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VUInt i') _) -> i > fromIntegral i' Literal (Value (VNInt i') _) -> i > -fromIntegral i' Literal (Value (VBignum i') _) -> i > i' + _ -> error "Not yet implemented" controlInteger i Le ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VUInt i') _) -> i <= fromIntegral i' Literal (Value (VNInt i') _) -> i <= -fromIntegral i' Literal (Value (VBignum i') _) -> i <= i' + _ -> error "Not yet implemented" controlInteger i Ge ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VUInt i') _) -> i >= fromIntegral i' Literal (Value (VNInt i') _) -> i >= -fromIntegral i' Literal (Value (VBignum i') _) -> i >= i' + _ -> error "Not yet implemented" controlInteger i Eq ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VUInt i') _) -> i == fromIntegral i' Literal (Value (VNInt i') _) -> i == -fromIntegral i' Literal (Value (VBignum i') _) -> i == i' + _ -> error "Not yet implemented" controlInteger i Ne ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VUInt i') _) -> i /= fromIntegral i' Literal (Value (VNInt i') _) -> i /= -fromIntegral i' Literal (Value (VBignum i') _) -> i /= i' + _ -> error "Not yet implemented" +controlInteger _ _ _ = error "Not yet implemented" -------------------------------------------------------------------------------- -- Floating point (Float16, Float32, Float64) @@ -311,7 +330,7 @@ controlInteger i Ne ctrl = -- | Validating a `Float16` validateHalf :: - MonadReader CDDL m => + (HasCallStack, MonadReader CDDL m) => Float -> Rule -> m CDDLResult @@ -333,22 +352,35 @@ validateHalf f rule = ((,) <$> getRule low <*> getRule high) <&> check . \case (Literal (Value (VFloat16 n) _), Literal (Value (VFloat16 m) _)) -> n <= f && range bound f m + _ -> error "Not yet implemented" _ -> pure UnapplicableRule -- | Controls for `Float16` -controlHalf :: MonadReader CDDL m => Float -> CtlOp -> Rule -> m (Either (Maybe CBORTermResult) ()) +controlHalf :: + ( HasCallStack + , MonadReader CDDL m + ) => + Float -> + CtlOp -> + Rule -> + m (Either (Maybe CBORTermResult) ()) controlHalf f Eq ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VFloat16 f') _) -> f == f' + _ -> error "Not yet implemented" controlHalf f Ne ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VFloat16 f') _) -> f /= f' + _ -> error "Not yet implemented" +controlHalf _ _ _ = error "Not yet implemented" -- | Validating a `Float32` validateFloat :: - MonadReader CDDL m => + ( HasCallStack + , MonadReader CDDL m + ) => Float -> Rule -> m CDDLResult @@ -373,24 +405,37 @@ validateFloat f rule = <&> check . \case (Literal (Value (VFloat16 n) _), Literal (Value (VFloat16 m) _)) -> n <= f && range bound f m (Literal (Value (VFloat32 n) _), Literal (Value (VFloat32 m) _)) -> n <= f && range bound f m + _ -> error "Not yet implemented" _ -> pure UnapplicableRule -- | Controls for `Float32` -controlFloat :: MonadReader CDDL m => Float -> CtlOp -> Rule -> m (Either (Maybe CBORTermResult) ()) +controlFloat :: + ( HasCallStack + , MonadReader CDDL m + ) => + Float -> + CtlOp -> + Rule -> + m (Either (Maybe CBORTermResult) ()) controlFloat f Eq ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VFloat16 f') _) -> f == f' Literal (Value (VFloat32 f') _) -> f == f' + _ -> error "Not yet implemented" controlFloat f Ne ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VFloat16 f') _) -> f /= f' Literal (Value (VFloat32 f') _) -> f /= f' + _ -> error "Not yet implemented" +controlFloat _ _ _ = error "Not yet implemented" -- | Validating a `Float64` validateDouble :: - MonadReader CDDL m => + ( HasCallStack + , MonadReader CDDL m + ) => Double -> Rule -> m CDDLResult @@ -416,23 +461,33 @@ validateDouble f rule = (Literal (Value (VFloat16 (float2Double -> n)) _), Literal (Value (VFloat16 (float2Double -> m)) _)) -> n <= f && range bound f m (Literal (Value (VFloat32 (float2Double -> n)) _), Literal (Value (VFloat32 (float2Double -> m)) _)) -> n <= f && range bound f m (Literal (Value (VFloat64 n) _), Literal (Value (VFloat64 m) _)) -> n <= f && range bound f m + _ -> error "Not yet implemented" _ -> pure UnapplicableRule -- | Controls for `Float64` controlDouble :: - MonadReader CDDL m => Double -> CtlOp -> Rule -> m (Either (Maybe CBORTermResult) ()) + ( HasCallStack + , MonadReader CDDL m + ) => + Double -> + CtlOp -> + Rule -> + m (Either (Maybe CBORTermResult) ()) controlDouble f Eq ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VFloat16 f') _) -> f == float2Double f' Literal (Value (VFloat32 f') _) -> f == float2Double f' Literal (Value (VFloat64 f') _) -> f == f' + _ -> error "Not yet implemented" controlDouble f Ne ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VFloat16 f') _) -> f /= float2Double f' Literal (Value (VFloat32 f') _) -> f /= float2Double f' Literal (Value (VFloat64 f') _) -> f /= f' + _ -> error "Not yet implemented" +controlDouble _ _ _ = error "Not yet implmented" -------------------------------------------------------------------------------- -- Bool @@ -459,15 +514,25 @@ validateBool b rule = _ -> pure UnapplicableRule -- | Controls for `Bool` -controlBool :: MonadReader CDDL m => Bool -> CtlOp -> Rule -> m (Either (Maybe CBORTermResult) ()) +controlBool :: + ( HasCallStack + , MonadReader CDDL m + ) => + Bool -> + CtlOp -> + Rule -> + m (Either (Maybe CBORTermResult) ()) controlBool b Eq ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VBool b') _) -> b == b' + _ -> error "Not yet implemented" controlBool b Ne ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VBool b') _) -> b /= b' + _ -> error "Not yet implemented" +controlBool _ _ _ = error "Not yet implemented" -------------------------------------------------------------------------------- -- Simple @@ -533,7 +598,9 @@ validateBytes bs rule = -- | Controls for byte strings controlBytes :: forall m. - MonadReader CDDL m => + ( HasCallStack + , MonadReader CDDL m + ) => BS.ByteString -> CtlOp -> Rule -> @@ -549,6 +616,8 @@ controlBytes bs Size ctrl = (Literal (Value (VNInt (fromIntegral -> n)) _), Literal (Value (VUInt (fromIntegral -> m)) _)) -> -n <= i && range bound i m (Literal (Value (VNInt (fromIntegral -> n)) _), Literal (Value (VNInt (fromIntegral -> m)) _)) -> -n <= i && range bound i (-m) (Literal (Value VUInt {} _), Literal (Value VNInt {} _)) -> False + _ -> error "Not yet implemented" + _ -> error "Not yet implemented" controlBytes bs Bits ctrl = do indices <- getRule ctrl >>= \case @@ -556,6 +625,7 @@ controlBytes bs Bits ctrl = do Choice nodes -> getIndicesOfChoice nodes Range ff tt incl -> getIndicesOfRange ff tt incl Enum g -> getIndicesOfEnum g + _ -> error "Not yet implemented" pure $ boolCtrl $ bitsControlCheck (map fromIntegral indices) where bitsControlCheck :: [Int] -> Bool @@ -575,12 +645,15 @@ controlBytes bs Cbor ctrl = validateTerm term ctrl >>= \case CBORTermResult _ (Valid _) -> pure $ Right () err -> pure $ Left $ Just err + _ -> error "Not yet implemented" controlBytes bs Cborseq ctrl = case deserialiseFromBytes decodeTerm (BSL.fromStrict (BS.snoc (BS.cons 0x9f bs) 0xff)) of Right (BSL.null -> True, TListI terms) -> - validateTerm (TList terms) (MIt (Array [MIt (Occur ctrl OIZeroOrMore)])) >>= \case + validateTerm (TList terms) (Array [Occur ctrl OIZeroOrMore]) >>= \case CBORTermResult _ (Valid _) -> pure $ Right () CBORTermResult _ err -> error $ show err + _ -> error "Not yet implemented" +controlBytes _ _ _ = error "Not yet implmented" -------------------------------------------------------------------------------- -- Text @@ -607,7 +680,14 @@ validateText txt rule = _ -> pure UnapplicableRule -- | Controls for text strings -controlText :: MonadReader CDDL m => T.Text -> CtlOp -> Rule -> m (Either (Maybe CBORTermResult) ()) +controlText :: + ( HasCallStack + , MonadReader CDDL m + ) => + T.Text -> + CtlOp -> + Rule -> + m (Either (Maybe CBORTermResult) ()) controlText bs Size ctrl = getRule ctrl >>= \case Literal (Value (VUInt (fromIntegral -> sz)) _) -> pure $ boolCtrl $ T.length bs == sz @@ -617,11 +697,16 @@ controlText bs Size ctrl = (Literal (Value (VUInt (fromIntegral -> n)) _), Literal (Value (VUInt (fromIntegral -> m)) _)) -> n <= T.length bs && range bound (T.length bs) m (Literal (Value (VNInt (fromIntegral -> n)) _), Literal (Value (VUInt (fromIntegral -> m)) _)) -> -n <= T.length bs && range bound (T.length bs) m (Literal (Value (VNInt (fromIntegral -> n)) _), Literal (Value (VNInt (fromIntegral -> m)) _)) -> -n <= T.length bs && range bound (T.length bs) (-m) + _ -> error "Not yet implemented" + _ -> error "Not yet implemented" controlText s Regexp ctrl = getRule ctrl <&> boolCtrl . \case Literal (Value (VText rxp) _) -> case s =~ rxp :: (T.Text, T.Text, T.Text) of ("", s', "") -> s == s' + _ -> error "Not yet implemented" + _ -> error "Not yet implemented" +controlText _ _ _ = error "Not yet implemented" -------------------------------------------------------------------------------- -- Tagged values @@ -654,7 +739,7 @@ validateTagged tag term rule = -- | Groups might contain enums, or unwraps inside. This resolves all those to -- the top level of the group. -flattenGroup :: CDDL -> [Rule] -> [Rule] +flattenGroup :: HasCallStack => CDDL -> [Rule] -> [Rule] flattenGroup cddl nodes = mconcat [ case resolveIfRef cddl rule of @@ -677,6 +762,7 @@ flattenGroup cddl nodes = _ -> error "Malformed cddl" Tag {} -> [rule] Group g -> flattenGroup cddl g + _ -> error "Not yet implemented" | rule <- nodes ] @@ -714,16 +800,16 @@ expandRule maxLen _ expandRule maxLen rule = getRule rule >>= \case Occur o OIOptional -> pure $ [] : [[o] | maxLen > 0] - Occur o OIZeroOrMore -> ([] :) <$> expandRule maxLen (MIt (Occur o OIOneOrMore)) + Occur o OIZeroOrMore -> ([] :) <$> expandRule maxLen (Occur o OIOneOrMore) Occur o OIOneOrMore -> if maxLen > 0 - then ([o] :) . map (o :) <$> expandRule (maxLen - 1) (MIt (Occur o OIOneOrMore)) + then ([o] :) . map (o :) <$> expandRule (maxLen - 1) (Occur o OIOneOrMore) else pure [] Occur o (OIBounded low high) -> case (low, high) of - (Nothing, Nothing) -> expandRule maxLen (MIt (Occur o OIZeroOrMore)) + (Nothing, Nothing) -> expandRule maxLen (Occur o OIZeroOrMore) (Just (fromIntegral -> low'), Nothing) -> if maxLen >= low' - then map (replicate low' o ++) <$> expandRule (maxLen - low') (MIt (Occur o OIZeroOrMore)) + then map (replicate low' o ++) <$> expandRule (maxLen - low') (Occur o OIZeroOrMore) else pure [] (Nothing, Just (fromIntegral -> high')) -> pure [replicate n o | n <- [0 .. min maxLen high']] @@ -796,6 +882,7 @@ validateExpandedList terms rules = go rules >>= ( \case Valid _ -> pure Valid ListExpansionFail _ _ errors -> pure $ \r -> ListExpansionFail r rules (res : errors) + _ -> error "Not yet implemented" ) . ($ dummyRule) @@ -821,7 +908,9 @@ validateList terms rule = validateMapWithExpandedRules :: forall m. - MonadReader CDDL m => + ( HasCallStack + , MonadReader CDDL m + ) => [(Term, Term)] -> [Rule] -> m ([AMatchedItem], Maybe ANonMatchedItem) @@ -838,6 +927,7 @@ validateMapWithExpandedRules = Left tt -> pure ([], Just tt) Right (res, rs') -> first (res :) <$> go ts rs' + go _ _ = error "Not yet implemented" -- For each pair of terms, try to find some rule that can be applied here, -- and returns the others if there is a succesful match. @@ -856,6 +946,7 @@ validateMapWithExpandedRules = CBORTermResult _ r1 -> bimap (\anmi -> anmi {anmiResults = Left (r, r1) : anmiResults anmi}) (second (r :)) <$> go' tk tv rs + _ -> error "Not yet implemented" validateExpandedMap :: forall m. @@ -877,6 +968,7 @@ validateExpandedMap terms rules = go rules Valid _ -> pure Valid MapExpansionFail _ _ errors -> pure $ \r -> MapExpansionFail r rules ((matches, notMatched) : errors) + _ -> error "Not yet implemented" ) . ($ dummyRule) @@ -918,11 +1010,12 @@ validateChoice v rules = go rules >>= ( \case Valid _ -> pure Valid ChoiceFail _ _ errors -> pure $ \r -> ChoiceFail r rules ((choice, err) NE.<| errors) + _ -> error "Not yet implemented" ) . ($ dummyRule) dummyRule :: Rule -dummyRule = MRuleRef (Name "dummy" mempty) +dummyRule = CTreeE $ MRuleRef (Name "dummy" mempty) -------------------------------------------------------------------------------- -- Control helpers @@ -1013,14 +1106,14 @@ getIndicesOfEnum g = -------------------------------------------------------------------------------- -- Resolving rules from the CDDL spec -resolveIfRef :: CDDL -> Rule -> ResolvedRule -resolveIfRef _ (MIt aa) = aa -resolveIfRef ct@(CTreeRoot cddl) (MRuleRef n) = do +resolveIfRef :: CDDL -> Rule -> Rule +resolveIfRef ct@(CTreeRoot cddl) (CTreeE (MRuleRef n)) = do case Map.lookup n cddl of Nothing -> error $ "Unbound reference: " <> show n - Just val -> resolveIfRef ct $ runIdentity val + Just val -> resolveIfRef ct val +resolveIfRef _ _ = error "Not yet implemented" -getRule :: MonadReader CDDL m => Rule -> m ResolvedRule +getRule :: MonadReader CDDL m => Rule -> m Rule getRule rule = asks (`resolveIfRef` rule) -------------------------------------------------------------------------------- diff --git a/src/Codec/CBOR/Cuddle/CDDL/CTree.hs b/src/Codec/CBOR/Cuddle/CDDL/CTree.hs index 0765771..26963c6 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/CTree.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/CTree.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Codec.CBOR.Cuddle.CDDL.CTree where @@ -11,7 +12,6 @@ import Codec.CBOR.Cuddle.CDDL ( ) import Codec.CBOR.Cuddle.CDDL.CtlOp import Codec.CBOR.Cuddle.CDDL.Postlude (PTerm) -import Data.Hashable (Hashable) import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as Map import Data.Word (Word64) @@ -27,68 +27,57 @@ import GHC.Generics (Generic) -- to manipulate. -------------------------------------------------------------------------------- --- | CDDL Tree, parametrised over a functor --- --- 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 +type family CTreeExt i + +data CTree i = Literal Value | Postlude PTerm - | Map [Node f] - | Array [Node f] - | Choice (NE.NonEmpty (Node f)) - | Group [Node f] - | KV {key :: Node f, value :: Node f, cut :: Bool} - | Occur {item :: Node f, occurs :: OccurrenceIndicator} - | Range {from :: Node f, to :: Node f, inclusive :: RangeBound} - | Control {op :: CtlOp, target :: Node f, controller :: Node f} - | Enum (Node f) - | Unwrap (Node f) - | Tag Word64 (Node f) + | Map [CTree i] + | Array [CTree i] + | Choice (NE.NonEmpty (CTree i)) + | Group [CTree i] + | KV {key :: CTree i, value :: CTree i, cut :: Bool} + | Occur {item :: CTree i, occurs :: OccurrenceIndicator} + | Range {from :: CTree i, to :: CTree i, inclusive :: RangeBound} + | Control {op :: CtlOp, target :: CTree i, controller :: CTree i} + | Enum (CTree i) + | Unwrap (CTree i) + | Tag Word64 (CTree i) + | CTreeE (CTreeExt i) deriving (Generic) deriving instance Eq (Node f) => Eq (CTree f) -- | Traverse the CTree, carrying out the given operation at each node -traverseCTree :: Monad m => (Node f -> m (Node g)) -> CTree f -> m (CTree g) -traverseCTree _ (Literal a) = pure $ Literal a -traverseCTree _ (Postlude a) = pure $ Postlude a -traverseCTree atNode (Map xs) = Map <$> traverse atNode xs -traverseCTree atNode (Array xs) = Array <$> traverse atNode xs -traverseCTree atNode (Group xs) = Group <$> traverse atNode xs -traverseCTree atNode (Choice xs) = Choice <$> traverse atNode xs -traverseCTree atNode (KV k v c) = do +traverseCTree :: + Monad m => (CTreeExt 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 +traverseCTree _ atNode (Array xs) = Array <$> traverse atNode xs +traverseCTree _ atNode (Group xs) = Group <$> traverse atNode xs +traverseCTree _ atNode (Choice xs) = Choice <$> traverse atNode xs +traverseCTree _ atNode (KV k v c) = do k' <- atNode k v' <- atNode v pure $ KV k' v' c -traverseCTree atNode (Occur i occ) = flip Occur occ <$> atNode i -traverseCTree atNode (Range f t inc) = do +traverseCTree _ atNode (Occur i occ) = flip Occur occ <$> atNode i +traverseCTree _ atNode (Range f t inc) = do f' <- atNode f t' <- atNode t pure $ Range f' t' inc -traverseCTree atNode (Control o t c) = do +traverseCTree _ atNode (Control o t c) = do t' <- atNode t c' <- atNode c pure $ Control o t' c' -traverseCTree atNode (Enum ref) = Enum <$> atNode ref -traverseCTree atNode (Unwrap ref) = Unwrap <$> atNode ref -traverseCTree atNode (Tag i ref) = Tag i <$> atNode ref +traverseCTree _ atNode (Enum ref) = Enum <$> atNode ref +traverseCTree _ atNode (Unwrap ref) = Unwrap <$> atNode ref +traverseCTree _ atNode (Tag i ref) = Tag i <$> atNode ref +traverseCTree atExt _ (CTreeE x) = atExt x -type Node f = f (CTree f) +type Node i = CTreeExt i -newtype CTreeRoot' poly f - = CTreeRoot - (Map.Map Name (poly (Node f))) +newtype CTreeRoot i = CTreeRoot (Map.Map Name (CTree i)) deriving (Generic) -type CTreeRoot f = CTreeRoot' (ParametrisedWith [Name]) f - -data ParametrisedWith w a - = Unparametrised {underlying :: a} - | Parametrised - { underlying :: a - , params :: w - } - deriving (Eq, Functor, Generic, Foldable, Traversable, Show) - -instance (Hashable w, Hashable a) => Hashable (ParametrisedWith w a) +deriving instance Show (CTree i) => Show (CTreeRoot i) diff --git a/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs b/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs index 3985c94..d3d6fac 100644 --- a/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs +++ b/src/Codec/CBOR/Cuddle/CDDL/Resolve.hs @@ -2,7 +2,9 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} @@ -29,9 +31,9 @@ module Codec.CBOR.Cuddle.CDDL.Resolve ( asMap, buildMonoCTree, fullResolveCDDL, - MonoRef (..), - OrRef (..), NameResolutionFailure (..), + MonoReferenced, + MonoRef (..), ) where @@ -42,19 +44,17 @@ import Capability.Reader qualified as Reader (local) import Capability.Sink (HasSink) import Capability.Source (HasSource) import Capability.State (HasState, MonadState (..), modify) -import Codec.CBOR.Cuddle.CDDL +import Codec.CBOR.Cuddle.CDDL as CDDL import Codec.CBOR.Cuddle.CDDL.CTree ( - CTree, - CTreeRoot, - CTreeRoot' (CTreeRoot), - ParametrisedWith (..), + CTree (..), + CTreeExt, + CTreeRoot (..), ) import Codec.CBOR.Cuddle.CDDL.CTree qualified as CTree import Codec.CBOR.Cuddle.CDDL.Postlude (PTerm (..)) import Control.Monad.Except (ExceptT (..), runExceptT) import Control.Monad.Reader (Reader, ReaderT (..), runReader) import Control.Monad.State.Strict (StateT (..)) -import Data.Functor.Identity (Identity (..)) import Data.Generics.Product import Data.Generics.Sum import Data.Hashable @@ -67,21 +67,30 @@ import Data.Text qualified as T import GHC.Generics (Generic) import Optics.Core +data ProvidedParameters a = ProvidedParameters + { 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 -------------------------------------------------------------------------------- -type CDDLMap = Map.Map Name (Parametrised TypeOrGroup) - -type Parametrised a = ParametrisedWith [Name] a +newtype PartialCTreeRoot i = PartialCTreeRoot (Map.Map Name (ProvidedParameters (CTree i))) + deriving (Generic) -toParametrised :: a -> Maybe GenericParam -> Parametrised a -toParametrised a Nothing = Unparametrised a -toParametrised a (Just (GenericParam gps)) = Parametrised a (NE.toList gps) +type CDDLMap = Map.Map Name (ProvidedParameters TypeOrGroup) -parameters :: Parametrised a -> [Name] -parameters (Unparametrised _) = mempty -parameters (Parametrised _ ps) = ps +toParametrised :: a -> Maybe GenericParam -> ProvidedParameters a +toParametrised a Nothing = ProvidedParameters [] a +toParametrised a (Just (GenericParam gps)) = ProvidedParameters (NE.toList gps) a asMap :: CDDL -> CDDLMap asMap cddl = foldl' go Map.empty rules @@ -99,8 +108,8 @@ asMap cddl = foldl' go Map.empty rules extend :: TypeOrGroup -> Maybe GenericParam -> - Maybe (Parametrised TypeOrGroup) -> - Maybe (Parametrised TypeOrGroup) + Maybe (ProvidedParameters TypeOrGroup) -> + Maybe (ProvidedParameters TypeOrGroup) extend tog _gps (Just existing) = case (underlying existing, tog) of (TOGType _, TOGType (Type0 new)) -> Just $ @@ -123,61 +132,60 @@ asMap cddl = foldl' go Map.empty rules -- 2. Conversion to CTree -------------------------------------------------------------------------------- +data OrReferenced + +type instance CTreeExt OrReferenced = OrRef (CTree OrReferenced) + -- | Indicates that an item may be referenced rather than defined. data OrRef a - = -- | The item is inlined directly - It a - | -- | Reference to another node with possible generic arguments supplied - Ref Name [CTree.Node OrRef] + = -- | Reference to another node with possible generic arguments supplied + Ref Name [CTree OrReferenced] deriving (Eq, Show, Functor) -type RefCTree = CTreeRoot OrRef +type RefCTree = PartialCTreeRoot OrReferenced -deriving instance Show (CTree OrRef) +deriving instance Show (CTree OrReferenced) -deriving instance Show (CTreeRoot OrRef) +deriving instance Show (PartialCTreeRoot OrReferenced) -- | Build a CTree incorporating references. -- -- This translation cannot fail. buildRefCTree :: CDDLMap -> RefCTree -buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules +buildRefCTree rules = PartialCTreeRoot $ toCTreeRule <$> rules where toCTreeRule :: - Parametrised TypeOrGroup -> - ParametrisedWith [Name] (CTree.Node OrRef) + ProvidedParameters TypeOrGroup -> + ProvidedParameters (CTree OrReferenced) toCTreeRule = fmap toCTreeTOG - toCTreeTOG :: TypeOrGroup -> CTree.Node OrRef + toCTreeTOG :: TypeOrGroup -> CTree OrReferenced toCTreeTOG (TOGType t0) = toCTreeT0 t0 toCTreeTOG (TOGGroup ge) = toCTreeGroupEntry ge - toCTreeT0 :: Type0 -> CTree.Node OrRef + toCTreeT0 :: Type0 -> CTree OrReferenced toCTreeT0 (Type0 (t1 NE.:| [])) = toCTreeT1 t1 - toCTreeT0 (Type0 xs) = It . CTree.Choice $ toCTreeT1 <$> xs + toCTreeT0 (Type0 xs) = CTree.Choice $ toCTreeT1 <$> xs - toCTreeT1 :: Type1 -> CTree.Node OrRef + toCTreeT1 :: Type1 -> CTree OrReferenced toCTreeT1 (Type1 t2 Nothing _) = toCTreeT2 t2 toCTreeT1 (Type1 t2 (Just (op, t2')) _) = case op of RangeOp bound -> - It $ - CTree.Range - { CTree.from = toCTreeT2 t2 - , CTree.to = toCTreeT2 t2' - , CTree.inclusive = bound - } + CTree.Range + { CTree.from = toCTreeT2 t2 + , CTree.to = toCTreeT2 t2' + , CTree.inclusive = bound + } CtrlOp ctlop -> - It $ - CTree.Control - { CTree.op = ctlop - , CTree.target = toCTreeT2 t2 - , CTree.controller = toCTreeT2 t2' - } - - toCTreeT2 :: Type2 -> CTree.Node OrRef - toCTreeT2 (T2Value v) = It $ CTree.Literal v - toCTreeT2 (T2Name n garg) = - Ref n (fromGenArgs garg) + CTree.Control + { CTree.op = ctlop + , CTree.target = toCTreeT2 t2 + , CTree.controller = toCTreeT2 t2' + } + + toCTreeT2 :: Type2 -> CTree OrReferenced + toCTreeT2 (T2Value v) = CTree.Literal v + toCTreeT2 (T2Name n garg) = CTreeE $ Ref n (fromGenArgs garg) toCTreeT2 (T2Group t0) = -- This behaviour seems questionable, but I don't really see how better to -- interpret the spec here. @@ -185,113 +193,106 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules toCTreeT2 (T2Map g) = toCTreeMap g toCTreeT2 (T2Array g) = toCTreeArray g toCTreeT2 (T2Unwrapped n margs) = - It . CTree.Unwrap $ + CTree.Unwrap . CTreeE $ Ref n (fromGenArgs margs) toCTreeT2 (T2Enum g) = toCTreeEnum g - toCTreeT2 (T2EnumRef n margs) = Ref n $ fromGenArgs margs + toCTreeT2 (T2EnumRef n margs) = CTreeE . Ref n $ fromGenArgs margs toCTreeT2 (T2Tag Nothing t0) = -- Currently not validating tags toCTreeT0 t0 toCTreeT2 (T2Tag (Just tag) t0) = - It . CTree.Tag tag $ toCTreeT0 t0 + CTree.Tag tag $ toCTreeT0 t0 toCTreeT2 (T2DataItem 7 (Just mmin)) = toCTreeDataItem mmin toCTreeT2 (T2DataItem _maj _mmin) = -- We don't validate numerical items yet - It $ CTree.Postlude PTAny - toCTreeT2 T2Any = It $ CTree.Postlude PTAny + CTree.Postlude PTAny + toCTreeT2 T2Any = CTree.Postlude PTAny toCTreeDataItem 20 = - It . CTree.Literal $ Value (VBool False) mempty + CTree.Literal $ Value (VBool False) mempty toCTreeDataItem 21 = - It . CTree.Literal $ Value (VBool True) mempty + CTree.Literal $ Value (VBool True) mempty toCTreeDataItem 25 = - It $ CTree.Postlude PTHalf + CTree.Postlude PTHalf toCTreeDataItem 26 = - It $ CTree.Postlude PTFloat + CTree.Postlude PTFloat toCTreeDataItem 27 = - It $ CTree.Postlude PTDouble + CTree.Postlude PTDouble toCTreeDataItem 23 = - It $ CTree.Postlude PTUndefined + CTree.Postlude PTUndefined toCTreeDataItem _ = - It $ CTree.Postlude PTAny + CTree.Postlude PTAny - toCTreeGroupEntry :: GroupEntry -> CTree.Node OrRef + toCTreeGroupEntry :: GroupEntry -> CTree OrReferenced toCTreeGroupEntry (GroupEntry (Just occi) _ (GEType mmkey t0)) = - It $ - CTree.Occur - { CTree.item = toKVPair mmkey t0 - , CTree.occurs = occi - } + 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)) = - It $ - CTree.Occur - { CTree.item = Ref n (fromGenArgs margs) - , CTree.occurs = occi - } - toCTreeGroupEntry (GroupEntry Nothing _ (GERef n margs)) = Ref n (fromGenArgs margs) + CTree.Occur + { CTree.item = CTreeE $ Ref n (fromGenArgs margs) + , CTree.occurs = occi + } + toCTreeGroupEntry (GroupEntry Nothing _ (GERef n margs)) = CTreeE $ Ref n (fromGenArgs margs) toCTreeGroupEntry (GroupEntry (Just occi) _ (GEGroup g)) = - It $ - CTree.Occur - { CTree.item = groupToGroup g - , CTree.occurs = occi - } + CTree.Occur + { CTree.item = groupToGroup g + , CTree.occurs = occi + } toCTreeGroupEntry (GroupEntry Nothing _ (GEGroup g)) = groupToGroup g - fromGenArgs :: Maybe GenericArg -> [CTree.Node OrRef] + fromGenArgs :: Maybe GenericArg -> [CTree OrReferenced] 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 (a NE.:| [])) = - It . CTree.Enum . It . CTree.Group $ toCTreeGroupEntry <$> gcGroupEntries a - toCTreeEnum (Group xs) = - It . CTree.Choice $ - It . CTree.Enum . It . CTree.Group . fmap toCTreeGroupEntry <$> groupEntries + toCTreeEnum :: Group -> CTree OrReferenced + toCTreeEnum (CDDL.Group (a NE.:| [])) = + CTree.Enum . CTree.Group $ toCTreeGroupEntry <$> gcGroupEntries a + toCTreeEnum (CDDL.Group xs) = + CTree.Choice $ CTree.Enum . CTree.Group . fmap toCTreeGroupEntry <$> groupEntries where groupEntries = fmap gcGroupEntries xs -- Embed a group in another group, again floating out the choice options - 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 + groupToGroup :: Group -> CTree OrReferenced + groupToGroup (CDDL.Group (a NE.:| [])) = + CTree.Group $ fmap toCTreeGroupEntry (gcGroupEntries a) + groupToGroup (CDDL.Group xs) = + CTree.Choice $ fmap (CTree.Group . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs) + + toKVPair :: Maybe MemberKey -> Type0 -> CTree OrReferenced toKVPair Nothing t0 = toCTreeT0 t0 toKVPair (Just mkey) t0 = - It $ - CTree.KV - { CTree.key = toCTreeMemberKey mkey - , CTree.value = toCTreeT0 t0 - , -- TODO Handle cut semantics - CTree.cut = False - } + CTree.KV + { CTree.key = toCTreeMemberKey mkey + , CTree.value = toCTreeT0 t0 + , -- TODO Handle cut semantics + CTree.cut = False + } -- Interpret a group as a map. Note that we float out the choice options - toCTreeMap :: Group -> CTree.Node OrRef - toCTreeMap (Group (a NE.:| [])) = It . CTree.Map $ fmap toCTreeGroupEntry (gcGroupEntries a) - toCTreeMap (Group xs) = - It - . CTree.Choice - $ fmap (It . CTree.Map . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs) + toCTreeMap :: Group -> CTree OrReferenced + toCTreeMap (CDDL.Group (a NE.:| [])) = CTree.Map $ fmap toCTreeGroupEntry (gcGroupEntries a) + toCTreeMap (CDDL.Group xs) = + CTree.Choice $ + fmap (CTree.Map . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs) -- Interpret a group as an array. Note that we float out the choice -- options - 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 (MKValue v) = It $ CTree.Literal v - toCTreeMemberKey (MKBareword (Name n _)) = It $ CTree.Literal (Value (VText n) mempty) + toCTreeArray :: Group -> CTree OrReferenced + toCTreeArray (CDDL.Group (a NE.:| [])) = + CTree.Array $ fmap toCTreeGroupEntry (gcGroupEntries a) + toCTreeArray (CDDL.Group xs) = + CTree.Choice $ + fmap (CTree.Array . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs) + + toCTreeMemberKey :: MemberKey -> CTree OrReferenced + toCTreeMemberKey (MKValue v) = CTree.Literal v + toCTreeMemberKey (MKBareword (Name n _)) = CTree.Literal (Value (VText n) mempty) toCTreeMemberKey (MKType t1) = toCTreeT1 t1 -------------------------------------------------------------------------------- @@ -301,10 +302,10 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules data NameResolutionFailure = UnboundReference Name | MismatchingArgs Name [Name] - | ArgsToPostlude PTerm [CTree.Node OrRef] + | ArgsToPostlude PTerm [CTree OrReferenced] deriving (Show) -deriving instance Eq (OrRef (CTree OrRef)) => Eq NameResolutionFailure +deriving instance Eq (CTree.Node OrReferenced) => Eq NameResolutionFailure postludeBinding :: Map.Map Name PTerm postludeBinding = @@ -325,92 +326,94 @@ postludeBinding = , (Name "null" mempty, PTNil) ] -data BindingEnv poly f g = BindingEnv - { global :: Map.Map Name (poly (CTree.Node f)) +data BindingEnv i j = BindingEnv + { global :: Map.Map Name (ProvidedParameters (CTree i)) -- ^ Global name bindings via 'RuleDef' - , local :: Map.Map Name (CTree.Node g) + , local :: Map.Map Name (CTree j) -- ^ Local bindings for generic parameters } deriving (Generic) -data DistRef a - = DIt a - | -- | Reference to a generic parameter +data DistReferenced + +type instance CTreeExt DistReferenced = DistRef + +data DistRef + = -- | Reference to a generic parameter GenericRef Name | -- | Reference to a rule definition, possibly with generic arguments - RuleRef Name [CTree.Node DistRef] - deriving (Eq, Generic, Functor, Show) + RuleRef Name [CTree DistReferenced] + deriving (Eq, Generic, Show) -instance Hashable a => Hashable (DistRef a) +instance Hashable DistRef -deriving instance Show (CTree DistRef) +deriving instance Show (CTree DistReferenced) -instance Hashable (CTree DistRef) +instance Hashable (CTree DistReferenced) -deriving instance Show (CTreeRoot DistRef) +deriving instance Show (PartialCTreeRoot DistReferenced) -deriving instance Eq (CTreeRoot DistRef) +deriving instance Eq (PartialCTreeRoot DistReferenced) -instance Hashable (CTreeRoot DistRef) +instance Hashable (PartialCTreeRoot DistReferenced) resolveRef :: - BindingEnv (ParametrisedWith [Name]) OrRef OrRef -> - CTree.Node OrRef -> - Either NameResolutionFailure (DistRef (CTree DistRef)) -resolveRef env (It a) = DIt <$> resolveCTree env a + BindingEnv OrReferenced OrReferenced -> + CTree.Node OrReferenced -> + Either NameResolutionFailure (CTree DistReferenced) resolveRef env (Ref n args) = case Map.lookup n postludeBinding of Just pterm -> case args of - [] -> Right . DIt $ CTree.Postlude pterm + [] -> Right $ CTree.Postlude pterm xs -> Left $ ArgsToPostlude pterm 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 & field @"local" %~ Map.union localBinds - in RuleRef n <$> traverse (resolveRef newEnv) args + newEnv = env & #local %~ Map.union localBinds + in CTreeE . RuleRef n <$> traverse (resolveCTree newEnv) args else Left $ MismatchingArgs n params' Nothing -> case Map.lookup n (local env) of - Just _ -> Right $ GenericRef n + Just _ -> Right . CTreeE $ GenericRef n Nothing -> Left $ UnboundReference n resolveCTree :: - BindingEnv (ParametrisedWith [Name]) OrRef OrRef -> - CTree OrRef -> - Either NameResolutionFailure (CTree DistRef) -resolveCTree e = CTree.traverseCTree (resolveRef e) + BindingEnv OrReferenced OrReferenced -> + CTree OrReferenced -> + Either NameResolutionFailure (CTree DistReferenced) +resolveCTree e = CTree.traverseCTree (resolveRef e) (resolveCTree e) buildResolvedCTree :: - CTreeRoot OrRef -> - Either NameResolutionFailure (CTreeRoot DistRef) -buildResolvedCTree (CTreeRoot ct) = CTreeRoot <$> traverse go ct + PartialCTreeRoot OrReferenced -> + Either NameResolutionFailure (PartialCTreeRoot DistReferenced) +buildResolvedCTree (PartialCTreeRoot ct) = PartialCTreeRoot <$> traverse go ct where - initBindingEnv = BindingEnv ct mempty go pn = let args = parameters pn - localBinds = Map.fromList $ zip args (flip Ref [] <$> args) - env = initBindingEnv & field @"local" %~ Map.union localBinds - in traverse (resolveRef env) pn + localBinds = Map.fromList $ zip args (CTreeE . flip Ref [] <$> args) + env = BindingEnv @OrReferenced @OrReferenced ct localBinds + in traverse (resolveCTree env) pn -------------------------------------------------------------------------------- -- 4. Monomorphisation -------------------------------------------------------------------------------- -data MonoRef a - = MIt a - | MRuleRef Name +data MonoReferenced + +type instance CTreeExt MonoReferenced = MonoRef (CTree MonoReferenced) + +newtype MonoRef a + = MRuleRef Name deriving (Functor, Show) -deriving instance Show (CTree MonoRef) +deriving instance Show (CTree MonoReferenced) -deriving instance - Show (poly (CTree.Node MonoRef)) => - Show (CTreeRoot' poly MonoRef) +deriving instance Show (PartialCTreeRoot MonoReferenced) -type MonoEnv = BindingEnv (ParametrisedWith [Name]) DistRef MonoRef +type MonoEnv = BindingEnv DistReferenced MonoReferenced -- | We introduce additional bindings in the state -type MonoState = Map.Map Name (CTree.Node MonoRef) +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. @@ -432,10 +435,10 @@ newtype MonoM a = MonoM deriving ( HasSource "local" - (Map.Map Name (CTree.Node MonoRef)) + (Map.Map Name (CTree MonoReferenced)) , HasReader "local" - (Map.Map Name (CTree.Node MonoRef)) + (Map.Map Name (CTree MonoReferenced)) ) via Field "local" @@ -449,10 +452,10 @@ newtype MonoM a = MonoM deriving ( HasSource "global" - (Map.Map Name (ParametrisedWith [Name] (CTree.Node DistRef))) + (Map.Map Name (ProvidedParameters (CTree DistReferenced))) , HasReader "global" - (Map.Map Name (ParametrisedWith [Name] (CTree.Node DistRef))) + (Map.Map Name (ProvidedParameters (CTree DistReferenced))) ) via Field "global" @@ -478,7 +481,7 @@ throwNR :: NameResolutionFailure -> MonoM a throwNR = throw @"nameResolution" -- | Synthesize a monomorphic rule definition, returning the name -synthMono :: Name -> [CTree.Node DistRef] -> MonoM Name +synthMono :: Name -> [CTree DistReferenced] -> MonoM Name synthMono n@(Name origName _) args = let fresh = -- % is not a valid CBOR name, so this should avoid conflict @@ -487,29 +490,26 @@ synthMono n@(Name origName _) args = -- Lookup the original name in the global bindings globalBinds <- ask @"global" case Map.lookup n globalBinds of - Just (Unparametrised _) -> throwNR $ MismatchingArgs n [] - Just (Parametrised r params') -> + Just (ProvidedParameters [] _) -> throwNR $ MismatchingArgs n [] + Just (ProvidedParameters params' r) -> if length params' == length args then do - rargs <- traverse resolveGenericRef args + rargs <- traverse resolveGenericCTree args let localBinds = Map.fromList $ zip params' rargs Reader.local @"local" (Map.union localBinds) $ do - foo <- resolveGenericRef r + foo <- resolveGenericCTree r modify @"synth" $ Map.insert fresh foo else throwNR $ MismatchingArgs n params' Nothing -> throwNR $ UnboundReference n pure fresh resolveGenericRef :: - CTree.Node DistRef -> - MonoM (MonoRef (CTree MonoRef)) -resolveGenericRef (DIt a) = MIt <$> resolveGenericCTree a -resolveGenericRef (RuleRef n margs) = - case margs of - [] -> pure $ MRuleRef n - args -> do - fresh <- synthMono n args - pure $ MRuleRef fresh + CTree.Node DistReferenced -> + MonoM (CTree MonoReferenced) +resolveGenericRef (RuleRef n []) = pure . CTreeE $ MRuleRef n +resolveGenericRef (RuleRef n args) = do + fresh <- synthMono n args + pure . CTreeE $ MRuleRef fresh resolveGenericRef (GenericRef n) = do localBinds <- ask @"local" case Map.lookup n localBinds of @@ -517,47 +517,38 @@ resolveGenericRef (GenericRef n) = do Nothing -> throwNR $ UnboundReference n resolveGenericCTree :: - CTree DistRef -> - MonoM (CTree MonoRef) -resolveGenericCTree = CTree.traverseCTree resolveGenericRef + CTree DistReferenced -> + MonoM (CTree MonoReferenced) +resolveGenericCTree = CTree.traverseCTree resolveGenericRef resolveGenericCTree -- | Monomorphise the CTree -- -- Concretely, for each reference in the tree to a generic rule, we synthesize a -- new monomorphic instance of that rule at top-level with the correct -- parameters applied. -monoCTree :: - CTreeRoot' Identity DistRef -> - MonoM (CTreeRoot' Identity MonoRef) -monoCTree (CTreeRoot ct) = CTreeRoot <$> traverse go ct - where - go = traverse resolveGenericRef - buildMonoCTree :: - CTreeRoot DistRef -> - Either NameResolutionFailure (CTreeRoot' Identity MonoRef) -buildMonoCTree (CTreeRoot ct) = do - let a1 = runExceptT $ runMonoM (monoCTree monoC) + PartialCTreeRoot DistReferenced -> + Either NameResolutionFailure (CTreeRoot MonoReferenced) +buildMonoCTree (PartialCTreeRoot ct) = do + let a1 = runExceptT $ runMonoM (traverse resolveGenericCTree monoC) a2 = runStateT a1 mempty - (er, newBindings) = runReader a2 initBindingEnv - CTreeRoot r <- er - pure . CTreeRoot $ Map.union r $ fmap Identity newBindings + (r, newBindings) = runReader a2 initBindingEnv + CTreeRoot . (`Map.union` newBindings) <$> r where initBindingEnv = BindingEnv ct mempty monoC = - CTreeRoot $ - Map.mapMaybe - ( \case - Unparametrised f -> Just $ Identity f - Parametrised _ _ -> Nothing - ) - ct + Map.mapMaybe + ( \case + ProvidedParameters [] f -> Just f + _ -> Nothing + ) + ct -------------------------------------------------------------------------------- -- Combined resolution -------------------------------------------------------------------------------- -fullResolveCDDL :: CDDL -> Either NameResolutionFailure (CTreeRoot' Identity MonoRef) +fullResolveCDDL :: CDDL -> Either NameResolutionFailure (CTreeRoot MonoReferenced) fullResolveCDDL cddl = do let refCTree = buildRefCTree (asMap cddl) rCTree <- buildResolvedCTree refCTree diff --git a/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs b/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs index 2f53405..6845565 100644 --- a/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs +++ b/test/Test/Codec/CBOR/Cuddle/CDDL/Examples.hs @@ -3,24 +3,22 @@ module Test.Codec.CBOR.Cuddle.CDDL.Examples (spec) where import Codec.CBOR.Cuddle.CDDL (Value (..), ValueVariant (..)) -import Codec.CBOR.Cuddle.CDDL.CTree (CTree (..), CTreeRoot') +import Codec.CBOR.Cuddle.CDDL.CTree (CTree (..), CTreeRoot) import Codec.CBOR.Cuddle.CDDL.Postlude (PTerm (..)) import Codec.CBOR.Cuddle.CDDL.Prelude (prependPrelude) import Codec.CBOR.Cuddle.CDDL.Resolve ( - MonoRef, + MonoReferenced, NameResolutionFailure (..), - OrRef (..), fullResolveCDDL, ) import Codec.CBOR.Cuddle.Parser (pCDDL) -import Data.Functor.Identity (Identity) import Data.Text.IO qualified as T import Test.HUnit (assertFailure) import Test.Hspec import Text.Megaparsec (parse) import Text.Megaparsec.Error (errorBundlePretty) -tryValidateFile :: FilePath -> IO (Either NameResolutionFailure (CTreeRoot' Identity MonoRef)) +tryValidateFile :: FilePath -> IO (Either NameResolutionFailure (CTreeRoot MonoReferenced)) tryValidateFile filePath = do contents <- T.readFile filePath cddl <- case parse pCDDL "" contents of @@ -60,4 +58,4 @@ spec = do validateExpectFailure "example/cddl-files/validator/negative/too-many-args.cddl" $ MismatchingArgs "foo" ["a"] validateExpectFailure "example/cddl-files/validator/negative/args-to-postlude.cddl" $ - ArgsToPostlude PTUInt [It (Literal (Value (VUInt 3) mempty))] + ArgsToPostlude PTUInt [Literal (Value (VUInt 3) mempty)]