CDDL as defined in [
RFC 8610] does not have any mechanisms to compute literals. To cover a large part of the use cases, this specification adds three control operators:
.plus for numeric addition,
.cat for string concatenation, and
.det for string concatenation with dedenting of both sides (target and controller).
For these operators, as with all control operators, targets and controllers are types. The resulting type is therefore formally a function of the elements of the cross-product of the two types. Not all tools may be able to work with non-unique targets or controllers.
In many cases, numbers are needed relative to a base number in a specification. The
.plus control identifies a number that is constructed by adding the numeric values of the target and the controller.
The target and controller both
MUST be numeric. If the target is a floating point number and the controller an integer number, or vice versa, the sum is converted into the type of the target; converting from a floating point number to an integer selects its floor (the largest integer less than or equal to the floating point number, i.e., rounding towards negative infinity).
interval<BASE> = (
BASE => int ; lower bound
(BASE .plus 1) => int ; upper bound
? (BASE .plus 2) => int ; tolerance
)
X = 0
Y = 3
rect = {
interval<X>
interval<Y>
}
The example in
Figure 1 contains the generic definition of a CDDL group
interval that gives a lower and upper bound and, optionally, a tolerance. The parameter BASE allows the non-conflicting use of a multiple of these interval groups in one map by assigning different labels to the entries of the interval. The rule
rect combines two of these interval groups into a map, one group for the X dimension (using 0, 1, and 2 as labels) and one for the Y dimension (using 3, 4, and 5 as labels).
It is often useful to be able to compose string literals out of component literals defined in different places in the specification.
The
.cat control identifies a string that is built from a concatenation of the target and the controller. The target and controller both
MUST be strings. The result of the operation has the same type as the target. The concatenation is performed on the bytes in both strings. If the target is a text string, the result of that concatenation
MUST be valid UTF-8.
c = "foo" .cat '
bar
baz
'
; on a system where the newline is \n, is the same string as:
b = "foo\n bar\n baz\n"
The example in
Figure 2 builds a text string named
c from concatenating the target text string
"foo" and the controller byte string entered in a text form byte string literal. (This particular idiom is useful when the text string contains newlines, which, as shown in the example for
b, may be harder to read when entered in the format that the pure CDDL text string notation inherits from JSON.)
Multi-line string literals for various applications, including embedded ABNF (
Section 3), need to be set flush left, at least partially. Often, having some indentation in the source code for the literal can promote readability, as in
Figure 3.
oid = bytes .abnfb ("oid" .det cbor-tags-oid)
roid = bytes .abnfb ("roid" .det cbor-tags-oid)
cbor-tags-oid = '
oid = 1*arc
roid = *arc
arc = [nlsb] %x00-7f
nlsb = %x81-ff *%x80-ff
'
The control operator
.det works like
.cat, except that both arguments (target and controller) are independently
dedented before the concatenation takes place.
For the first rule in
Figure 3, the result is equivalent to
Figure 4.
oid = bytes .abnfb 'oid
oid = 1*arc
roid = *arc
arc = [nlsb] %x00-7f
nlsb = %x81-ff *%x80-ff
'
For the purposes of this specification, we define "dedenting" as:
-
determining the smallest amount of leftmost blank space (number of leading space characters) present in all the non-blank lines, and
-
removing exactly that number of leading space characters from each line. For blank (blank space only or empty) lines, there may be fewer (or no) leading space characters than this amount, in which case all leading space is removed.
(The name
.det is a shortcut for "dedenting cat". The maybe more obvious name
.dedcat has not been chosen as it is longer and may invoke unpleasant images.)
Occasionally, dedenting of only a single item is needed. This can be achieved by using this operator with an empty string, e.g.,
"" .det rhs or
lhs .det "", which can in turn be combined with a
.cat: in the construct
lhs .cat ("" .det rhs), only
rhs is dedented.