Skip to content

Commit 7874af0

Browse files
authored
Merge pull request #42 from input-output-hk/nc/sizedRef
Support constraining reference types
2 parents 2c62cdb + 0be7c54 commit 7874af0

File tree

3 files changed

+95
-28
lines changed

3 files changed

+95
-28
lines changed

cuddle.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 3.4
22
name: cuddle
3-
version: 0.3.3.0
3+
version: 0.3.4.0
44
synopsis: CDDL Generator and test utilities
55

66
-- description:

example/Conway.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -956,13 +956,14 @@ bounded_bytes = "bounded_bytes" =:= VBytes `sized` (0 :: Word64, 64 :: Word64)
956956

957957
-- a type for distinct values.
958958
-- The type parameter must support .size, for example: bytes or uint
959-
distinct :: (IsSizeable s) => Value s -> Rule
960-
distinct x =
961-
"distinct_"
962-
<> T.pack (show x)
959+
960+
distinct :: (IsType0 a, IsConstrainable a b) => a -> GRuleCall
961+
distinct = binding $ \x ->
962+
"distinct"
963963
=:= (x `sized` (8 :: Word64))
964964
/ (x `sized` (16 :: Word64))
965965
/ (x `sized` (20 :: Word64))
966966
/ (x `sized` (24 :: Word64))
967967
/ (x `sized` (30 :: Word64))
968968
/ (x `sized` (32 :: Word64))
969+

src/Codec/CBOR/Cuddle/Huddle.hs

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
{-# LANGUAGE AllowAmbiguousTypes #-}
12
{-# LANGUAGE DuplicateRecordFields #-}
23
{-# LANGUAGE FunctionalDependencies #-}
34
{-# LANGUAGE GADTs #-}
45
{-# LANGUAGE LambdaCase #-}
56
{-# LANGUAGE OverloadedStrings #-}
7+
{-# LANGUAGE PartialTypeSignatures #-}
68
{-# LANGUAGE TypeApplications #-}
79
{-# LANGUAGE TypeFamilies #-}
810
{-# LANGUAGE UndecidableInstances #-}
@@ -59,6 +61,7 @@ module Codec.CBOR.Cuddle.Huddle
5961
text,
6062

6163
-- * Ctl operators
64+
IsConstrainable,
6265
IsSizeable,
6366
sized,
6467
cbor,
@@ -261,7 +264,7 @@ instance IsList Group where
261264
toList (Group l) = l
262265

263266
data Type2
264-
= T2Basic Constrained
267+
= T2Constrained Constrained
265268
| T2Literal Ranged
266269
| T2Map Map
267270
| T2Array Array
@@ -346,11 +349,27 @@ inferInteger i
346349
-- Constraints and Ranges
347350
--------------------------------------------------------------------------------
348351

349-
-- | We only allow constraining basic values.
352+
-- | A reference can be to any type, so we allow it to inhabit all
353+
type AnyRef a = Named Type0
354+
355+
data Constrainable a
356+
= CValue (Value a)
357+
| CRef (AnyRef a)
358+
| CGRef GRef
359+
deriving (Show)
360+
361+
-- | Uninhabited type used as marker for the type of thing a CRef sizes
362+
data CRefType
363+
364+
-- | Uninhabited type used as marker for the type of thing a CGRef sizes
365+
data CGRefType
366+
367+
-- | We only allow constraining basic values, or references. Of course, we
368+
-- can't check what the references refer to.
350369
data Constrained where
351370
Constrained ::
352371
forall a.
353-
{ value :: Value a,
372+
{ value :: Constrainable a,
354373
constraint :: ValueConstraint a,
355374
-- | Sometimes constraints reference rules. In this case we need to
356375
-- collect the references in order to traverse them when collecting all
@@ -361,8 +380,20 @@ data Constrained where
361380

362381
deriving instance Show Constrained
363382

383+
class IsConstrainable a x | a -> x where
384+
toConstrainable :: a -> Constrainable x
385+
386+
instance IsConstrainable (AnyRef a) CRefType where
387+
toConstrainable = CRef
388+
389+
instance IsConstrainable (Value a) a where
390+
toConstrainable = CValue
391+
392+
instance IsConstrainable GRef CGRefType where
393+
toConstrainable = CGRef
394+
364395
unconstrained :: Value a -> Constrained
365-
unconstrained v = Constrained v def []
396+
unconstrained v = Constrained (CValue v) def []
366397

367398
-- | A constraint on a 'Value' is something applied via CtlOp or RangeOp on a
368399
-- Type2, forming a Type1.
@@ -391,6 +422,10 @@ instance IsSizeable ByteString
391422

392423
instance IsSizeable T.Text
393424

425+
instance IsSizeable CRefType
426+
427+
instance IsSizeable CGRefType
428+
394429
-- | Things which can be used on the RHS of the '.size' operator.
395430
class IsSize a where
396431
sizeAsCDDL :: a -> C.Type2
@@ -412,11 +447,20 @@ instance IsSize (Word64, Word64) where
412447
)
413448
sizeAsString (x, y) = show x <> ".." <> show y
414449

415-
-- | Declare a size constraint on an int-style type.
416-
sized :: (IsSizeable a, IsSize s) => Value a -> s -> Constrained
450+
-- | Declare a size constraint on an int-style type or reference.
451+
-- Since 0.3.4 this has worked for reference types as well as values.
452+
sized ::
453+
forall c a s.
454+
( IsSizeable a,
455+
IsSize s,
456+
IsConstrainable c a
457+
) =>
458+
c ->
459+
s ->
460+
Constrained
417461
sized v sz =
418462
Constrained
419-
v
463+
(toConstrainable @c @a v)
420464
ValueConstraint
421465
{ applyConstraint = \t2 ->
422466
C.Type1
@@ -426,10 +470,15 @@ sized v sz =
426470
}
427471
[]
428472

429-
cbor :: Value ByteString -> Rule -> Constrained
473+
class IsCborable a
474+
instance IsCborable ByteString
475+
instance IsCborable CRef
476+
instance IsCborable CGRef
477+
478+
cbor :: (IsCborable b, IsConstrainable c b) => c -> Rule -> Constrained
430479
cbor v r@(Named n _ _) =
431480
Constrained
432-
v
481+
(toConstrainable v)
433482
ValueConstraint
434483
{ applyConstraint = \t2 ->
435484
C.Type1
@@ -439,10 +488,15 @@ cbor v r@(Named n _ _) =
439488
}
440489
[r]
441490

442-
le :: Value Int -> Word64 -> Constrained
491+
class IsComparable a
492+
instance IsComparable Int
493+
instance IsComparable CRef
494+
instance IsComparable CGRef
495+
496+
le :: (IsComparable a, IsConstrainable c a) => c -> Word64 -> Constrained
443497
le v bound =
444498
Constrained
445-
v
499+
(toConstrainable v)
446500
ValueConstraint
447501
{ applyConstraint = \t2 ->
448502
C.Type1
@@ -485,7 +539,7 @@ instance IsType0 (Choice Type2) where
485539
toType0 = id
486540

487541
instance IsType0 Constrained where
488-
toType0 = NoChoice . T2Basic
542+
toType0 = NoChoice . T2Constrained
489543

490544
instance IsType0 Map where
491545
toType0 = NoChoice . T2Map
@@ -523,7 +577,7 @@ instance IsType0 Double where
523577
toType0 = NoChoice . T2Literal . Unranged . LDouble
524578

525579
instance IsType0 (Value a) where
526-
toType0 = NoChoice . T2Basic . unconstrained
580+
toType0 = NoChoice . T2Constrained . unconstrained
527581

528582
instance IsType0 (Named Group) where
529583
toType0 = NoChoice . T2Group
@@ -667,7 +721,7 @@ instance IsChoosable ByteString Type2 where
667721
toChoice = toChoice . T2Literal . Unranged . LBytes
668722

669723
instance IsChoosable Constrained Type2 where
670-
toChoice = toChoice . T2Basic
724+
toChoice = toChoice . T2Constrained
671725

672726
instance (IsType0 a) => IsChoosable (Tagged a) Type2 where
673727
toChoice = toChoice . T2Tagged . fmap toType0
@@ -676,7 +730,7 @@ instance IsChoosable Literal Type2 where
676730
toChoice = toChoice . T2Literal . Unranged
677731

678732
instance IsChoosable (Value a) Type2 where
679-
toChoice = toChoice . T2Basic . unconstrained
733+
toChoice = toChoice . T2Constrained . unconstrained
680734

681735
instance IsChoosable (Named Group) Type2 where
682736
toChoice = toChoice . T2Group
@@ -903,7 +957,13 @@ collectFrom topRs =
903957
-- Note that the parameters here may be different, so this doesn't live
904958
-- under the guard
905959
mapM_ goT2 $ args g
906-
goT2 (T2Basic (Constrained _ _ refs)) = mapM_ goRule refs
960+
goT2 (T2Constrained (Constrained c _ refs)) =
961+
( case c of
962+
CValue _ -> pure ()
963+
CRef r -> goRule r
964+
CGRef _ -> pure ()
965+
)
966+
>> mapM_ goRule refs
907967
goT2 _ = pure ()
908968
goArrayEntry (ArrayEntry (Just k) t0 _ _) = goKey k >> goT0 t0
909969
goArrayEntry (ArrayEntry Nothing t0 _ _) = goT0 t0
@@ -985,9 +1045,9 @@ toCDDL' mkPseudoRoot hdl =
9851045

9861046
toCDDLType1 :: Type2 -> C.Type1
9871047
toCDDLType1 = \case
988-
T2Basic (Constrained x constr _) ->
1048+
T2Constrained (Constrained x constr _) ->
9891049
-- TODO Need to handle choices at the top level
990-
applyConstraint constr (C.T2Name (toCDDLPostlude x) Nothing)
1050+
applyConstraint constr (C.T2Name (toCDDLConstrainable x) Nothing)
9911051
T2Literal l -> toCDDLRanged l
9921052
T2Map m ->
9931053
C.Type1
@@ -1017,13 +1077,14 @@ toCDDL' mkPseudoRoot hdl =
10171077

10181078
arrayEntryToCDDL :: ArrayEntry -> C.WithComments C.GroupEntry
10191079
arrayEntryToCDDL (ArrayEntry k v occ cmnt) =
1020-
C.WithComments
1021-
( C.GEType
1022-
(toOccurrenceIndicator occ)
1023-
(fmap toMemberKey k)
1024-
(toCDDLType0 v)
1080+
C.WithComments
1081+
( C.GEType
1082+
(toOccurrenceIndicator occ)
1083+
(fmap toMemberKey k)
1084+
(toCDDLType0 v)
10251085
)
10261086
(fmap C.Comment cmnt)
1087+
10271088
toCDDLPostlude :: Value a -> C.Name
10281089
toCDDLPostlude VBool = C.Name "bool"
10291090
toCDDLPostlude VUInt = C.Name "uint"
@@ -1037,6 +1098,11 @@ toCDDL' mkPseudoRoot hdl =
10371098
toCDDLPostlude VAny = C.Name "any"
10381099
toCDDLPostlude VNil = C.Name "nil"
10391100

1101+
toCDDLConstrainable c = case c of
1102+
CValue v -> toCDDLPostlude v
1103+
CRef r -> C.Name $ name r
1104+
CGRef (GRef n) -> C.Name n
1105+
10401106
toCDDLRanged :: Ranged -> C.Type1
10411107
toCDDLRanged (Unranged x) =
10421108
C.Type1 (C.T2Value $ toCDDLValue x) Nothing

0 commit comments

Comments
 (0)