diff --git a/bikeshed/cddl.py b/bikeshed/cddl.py
index eb45cd016a..9286387e67 100644
--- a/bikeshed/cddl.py
+++ b/bikeshed/cddl.py
@@ -33,10 +33,11 @@ def serializeValue(self, prefix: str, value: str, suffix: str, node: cddlparser.
# Cannot easily link member key back to a definition
return name
else:
- # Name of created type should not include the quotes
+ # Create a key with and without quotes as linking text
+ lts = [value, name]
return '{}'.format(
h.escapeAttr(forName),
- h.escapeAttr(value),
+ h.escapeAttr("|".join(lts)),
name,
)
elif isinstance(parent, cddlparser.ast.Operator) and parent.controller == node:
@@ -49,9 +50,10 @@ def serializeValue(self, prefix: str, value: str, suffix: str, node: cddlparser.
if forName is None:
return name
else:
+ lts = [value, name]
return '{}'.format(
h.escapeAttr(forName),
- h.escapeAttr(name),
+ h.escapeAttr("|".join(lts)),
name,
)
@@ -88,9 +90,14 @@ def serializeName(self, name: str, node: cddlparser.ast.CDDLNode) -> str:
# Cannot easily link member key back to a definition
return name
else:
+ lts = []
+ if name[0] == '"':
+ lts = [name[1:-1], name]
+ else:
+ lts = [name, '"' + name + '"']
return '{}'.format(
h.escapeAttr(forName),
- h.escapeAttr(name),
+ h.escapeAttr("|".join(lts)),
name,
)
elif isinstance(parent, cddlparser.ast.GenericParameters):
@@ -162,66 +169,96 @@ def markupCDDLBlock(pre: t.ElementT, doc: t.SpecT) -> set[t.ElementT]:
Convert blocks into "dfn" or links.
"""
localDfns = set()
- forcedDfns = []
- for x in (h.treeAttr(pre, "data-dfn-force") or "").split():
- x = x.strip()
- if x.endswith(""):
- x = x[:-11]
- forcedDfns.append(x)
+
+ # A CDDL definition may create duplicates, e.g. argh = [ "dupl", "dupl" ]
+ # To detect these duplicates, let's maintain a list of actual definitions
+ # contained in CDDL blocks.
+ cddlDfns = set()
+
+ def recordDfns(el: t.ElementT) -> bool:
+ cddlType = "cddl-" + (el.get("data-cddl-type") or "")
+ for cddlText in (el.get("data-lt") or "").split("|"):
+ linkFors = t.cast("list[str|None]", config.splitForValues(el.get("data-cddl-for", ""))) or [None]
+ for linkFor in linkFors:
+ dfnText = cddlType + ">" + (linkFor or "") + "/" + cddlText
+ if dfnText in cddlDfns:
+ forText = "" if linkFor is None else f' defined in type "{linkFor}"'
+ m.die(
+ f"CDDL {cddlType[5:]} {cddlText}{forText} creates a duplicate and cannot be referenced.\nPlease create additional CDDL types to disambiguate.",
+ )
+ return False
+ cddlDfns.add(dfnText)
+ if cddlType != "cddl-parameter":
+ warned = False
+ for cddlText in (el.get("data-lt") or "").split("|"):
+ linkFors = t.cast("list[str|None]", config.splitForValues(el.get("data-cddl-for", ""))) or [None]
+ for linkFor in linkFors:
+ dfnText = (linkFor or "") + "/" + cddlText
+ if dfnText in cddlDfns and not warned:
+ warned = True
+ forText = "" if linkFor is None else f' defined in type "{linkFor}"'
+ m.warn(
+ f"CDDL {cddlType[5:]} {cddlText}{forText} creates a duplicate with another CDDL definition.\nLink type needs to be specified to reference the term.\nConsider creating additional CDDL types to disambiguate.",
+ )
+ cddlDfns.add(dfnText)
+ return True
+
for el in h.findAll("cddl", pre):
# Prefix CDDL types with "cddl-" to avoid confusion with other
# types (notably CSS ones such as "value")
cddlType = "cddl-" + (el.get("data-cddl-type") or "")
assert isinstance(cddlType, str)
url = None
- forceDfn = False
ref = None
cddlText = None
- for cddlText in (el.get("data-lt") or "").split("|"):
- if cddlType == "interface" and cddlText in forcedDfns:
- forceDfn = True
- linkFors = t.cast("list[str|None]", config.splitForValues(el.get("data-cddl-for", ""))) or [None]
- for linkFor in linkFors:
- ref = doc.refs.getRef(
- cddlType,
- cddlText,
- linkFor=linkFor,
- status="local",
- el=el,
- error=False,
- )
+ # Record the dfns that the term would create and check whether
+ # it creates a duplicate. If it does, let's not link the term.
+ if not recordDfns(el):
+ el.tag = "span"
+ else:
+ for cddlText in (el.get("data-lt") or "").split("|"):
+ linkFors = t.cast("list[str|None]", config.splitForValues(el.get("data-cddl-for", ""))) or [None]
+ for linkFor in linkFors:
+ ref = doc.refs.getRef(
+ cddlType,
+ cddlText,
+ linkFor=linkFor,
+ status="local",
+ el=el,
+ error=True,
+ )
+ if ref:
+ url = ref.url
+ break
if ref:
- url = ref.url
break
- if ref:
- break
- if url is None or forceDfn:
- el.tag = "dfn"
- el.set("data-dfn-type", cddlType)
- del el.attrib["data-cddl-type"]
- if el.get("data-cddl-for"):
- el.set("data-dfn-for", el.get("data-cddl-for") or "")
- del el.attrib["data-cddl-for"]
- else:
- # Copy over the auto-generated linking text to the manual dfn.
- dfn = h.find(url, doc)
- # How in the hell does this work, the url is not a selector???
- assert dfn is not None
- lts = combineCDDLLinkingTexts(el.get("data-lt"), dfn.get("data-lt"))
- dfn.set("data-lt", lts)
- localDfns.add(dfn)
-
- # Reset the element to be a link to the manual dfn.
- el.tag = "a"
- el.set("data-link-type", cddlType)
- el.set("data-lt", cddlText)
- del el.attrib["data-cddl-type"]
- if el.get("data-cddl-for"):
- el.set("data-link-for", el.get("data-cddl-for") or "")
- del el.attrib["data-cddl-for"]
- if el.get("id"):
- # ID was defensively added by the Marker.
- del el.attrib["id"]
+ if url is None:
+ el.tag = "dfn"
+ el.set("data-dfn-type", cddlType)
+ del el.attrib["data-cddl-type"]
+ if el.get("data-cddl-for"):
+ el.set("data-dfn-for", el.get("data-cddl-for") or "")
+ del el.attrib["data-cddl-for"]
+ else:
+ # Copy over the auto-generated linking text to the manual dfn.
+ dfn = h.find(url, doc)
+ # How in the hell does this work, the url is not a selector???
+ assert dfn is not None
+ lts = combineCDDLLinkingTexts(el.get("data-lt"), dfn.get("data-lt"))
+ dfn.set("data-lt", lts)
+ localDfns.add(dfn)
+
+ # Reset the element to be a link to the manual dfn.
+ el.tag = "a"
+ el.set("data-link-type", cddlType)
+ el.set("data-lt", cddlText)
+ del el.attrib["data-cddl-type"]
+ if el.get("data-cddl-for"):
+ el.set("data-link-for", el.get("data-cddl-for") or "")
+ del el.attrib["data-cddl-for"]
+ if el.get("id"):
+ # ID was defensively added by the Marker.
+ del el.attrib["id"]
return localDfns
diff --git a/bikeshed/refs/utils.py b/bikeshed/refs/utils.py
index d0f29a7460..4b2f7ae782 100644
--- a/bikeshed/refs/utils.py
+++ b/bikeshed/refs/utils.py
@@ -181,13 +181,6 @@ def linkTextVariations(str: str, linkType: str | None) -> t.Generator[str, None,
if str[0] != '"':
yield '"' + str + '"'
- if config.linkTypeIn(linkType, "cddl-key") or config.linkTypeIn(linkType, "cddl-value"):
- # Allow linking to a cddl-key or cddl-value with or without quotes
- if str[0] == '"':
- yield str[1:-1]
- if str[0] != '"':
- yield '"' + str + '"'
-
if t.TYPE_CHECKING:
U = t.TypeVar("U", bound="t.MutableMapping|t.MutableSequence")
diff --git a/tests/cddl001.html b/tests/cddl001.html
index da46e7d2bd..efedf3e5b6 100644
--- a/tests/cddl001.html
+++ b/tests/cddl001.html
@@ -602,15 +602,15 @@ Table of Contents
1. The definitions
- The attire
enumeration lets you wear a "bow tie"
, a "necktie"
, an "Internet attire"
or "swimwear"
.
- The basecolors
construct defines a set of base colors, including black
and white
.
- The extended-color
construct extends basecolors with orange
.
+ The attire
enumeration lets you wear a "bow tie"
, a "necktie"
, an "Internet attire"
or "swimwear"
.
+ The basecolors
construct defines a set of base colors, including black
and white
.
+ The extended-color
construct extends basecolors with orange
.
2. The CDDL
attire = "bow tie" / "necktie" / "Internet attire"
attireBlock
= (
- "bow tie"
/
- "necktie"
/
- "Internet attire"
+ "bow tie"
/
+ "necktie"
/
+ "Internet attire"
)
attireGroup
= (
attire //
@@ -621,21 +621,21 @@ 2. address
= { delivery }
delivery
= (
- street
: tstr,
- ? number
: uint,
- city // po-box
: uint,
- city // foo
: tstr / uint // per-pickup
: true
+ street
: tstr,
+ ? number
: uint,
+ city // po-box
: uint,
+ city // foo
: tstr / uint // per-pickup
: true
)
city
= (
- name
: tstr,
- zip-code
: uint
+ name
: tstr,
+ zip-code
: uint
)
attire /= "swimwear"
delivery //= (
- lat
: float, long
: float, drone-type
: tstr
+ lat
: float, long
: float, drone-type
: tstr
)
device-address
= byte
@@ -645,13 +645,13 @@ 2. terminal-color
= &basecolors
basecolors = (
- black: 0, red
: 1, green
: 2, yellow
: 3,
- blue
: 4, magenta
: 5, cyan
: 6, white: 7,
+ black: 0, red
: 1, green
: 2, yellow
: 3,
+ blue
: 4, magenta
: 5, cyan
: 6, white: 7,
)
extended-color = &(
basecolors,
- orange: 8, pink
: 9, purple
: 10, brown
: 11,
+ orange: 8, pink
: 9, purple
: 10, brown
: 11,
)
my_breakfast
= #6.55799(breakfast) ; cbor-any is too general!
@@ -664,8 +664,8 @@ 2. solid
= tstr
apartment
= {
- kitchen
: size,
- * bedroom
: size,
+ kitchen
: size,
+ * bedroom
: size,
}
size
= float ; in m2
@@ -673,28 +673,28 @@ 2. one-or-two-people
= [1*2 person]
at-least-two-people
= [2* person]
person
= (
- name
: tstr,
- age
: uint,
+ name
: tstr,
+ age
: uint,
)
located-samples
= {
- sample-point
: int,
- samples
: [+ float],
+ sample-point
: int,
+ samples
: [+ float],
* equipment-type => equipment-tolerances,
}
-equipment-type
= [name
: tstr, manufacturer
: tstr]
+equipment-type
= [name
: tstr, manufacturer
: tstr]
equipment-tolerances
= [+ [float, float]]
PersonalData
= {
- ? displayName
: tstr,
+ ? displayName
: tstr,
NameComponents,
- ? age
: uint,
+ ? age
: uint,
* tstr => any
}
NameComponents
= (
- ? firstName
: tstr,
- ? familyName
: tstr,
+ ? firstName
: tstr,
+ ? familyName
: tstr,
)
square-roots
= {* x => y}
@@ -702,32 +702,39 @@ 2. y
= float
extensible-map-example
= {
- ? "optional-key"
: int,
+ ? "optional-key"
: int,
* tstr => any
}
tcpflagbytes
= bstr .bits flags
flags
= &(
- fin
: 8,
- syn
: 9,
- rst
: 10,
- psh
: 11,
- ack
: 12,
- urg
: 13,
- ece
: 14,
- cwr
: 15,
- ns
: 0,
+ fin
: 8,
+ syn
: 9,
+ rst
: 10,
+ psh
: 11,
+ ack
: 12,
+ urg
: 13,
+ ece
: 14,
+ cwr
: 15,
+ ns
: 0,
) / (4..7) ; data offset bits
rwxbits
= uint .bits rwx
-rwx
= &(r
: 2, w
: 1, x
: 0)
+rwx
= &(r
: 2, w
: 1, x
: 0)
Index
Terms defined by this specification
+ - "ack", in § 2
- ack, in § 2
- address, in § 2
+
-
+ "age"
+
-
age
+
-
+ bow tie
+
- breakfast, in § 2
+
- "brown", in § 2
- brown, in § 2
- byte, in § 2
- cereal, in § 2
- city, in § 2
+
- "cwr", in § 2
- cwr, in § 2
+
- "cyan", in § 2
- cyan, in § 2
- delivery, in § 2
- device-address, in § 2
+
- "displayName", in § 2
- displayName, in § 2
+
- "drone-type", in § 2
- drone-type, in § 2
+
- "ece", in § 2
- ece, in § 2
- equipment-tolerances, in § 2
- equipment-type, in § 2
- extended-color, in § 1
- extensible-map-example, in § 2
+
- "familyName", in § 2
- familyName, in § 2
+
- "fin", in § 2
- fin, in § 2
+
- "firstName", in § 2
- firstName, in § 2
- flags, in § 2
+
- "foo", in § 2
- foo, in § 2
+
- "green", in § 2
- green, in § 2
-
"Internet attire"
@@ -777,16 +804,34 @@
cddl-value for attire, in § 1
- cddl-value for attireBlock, in § 2
+
+ Internet attire
+
+ "kitchen", in § 2
kitchen, in § 2
+ "lat", in § 2
lat, in § 2
liquid, in § 2
located-samples, in § 2
+ "long", in § 2
long, in § 2
+ "magenta", in § 2
magenta, in § 2
+ "manufacturer", in § 2
manufacturer, in § 2
max-byte, in § 2
milk, in § 2
my_breakfast, in § 2
+
+ "name"
+
name
+
+ necktie
+
+ "ns", in § 2
ns, in § 2
+ "number", in § 2
number, in § 2
one-or-two-people, in § 2
+ "optional-key", in § 2
optional-key, in § 2
+ "orange", in § 1
orange, in § 1
+ "per-pickup", in § 2
per-pickup, in § 2
person, in § 2
PersonalData, in § 2
+ "pink", in § 2
pink, in § 2
+ "po-box", in § 2
po-box, in § 2
porridge, in § 2
protocol, in § 2
+ "psh", in § 2
psh, in § 2
+ "purple", in § 2
purple, in § 2
+ "r", in § 2
r, in § 2
+ "red", in § 2
red, in § 2
+ "rst", in § 2
rst, in § 2
rwx, in § 2
rwxbits, in § 2
+ "sample-point", in § 2
sample-point, in § 2
+ "samples", in § 2
samples, in § 2
size, in § 2
solid, in § 2
square-roots, in § 2
+ "street", in § 2
street, in § 2
"swimwear", in § 1
+ swimwear, in § 1
+ "syn", in § 2
syn, in § 2
tcpflagbytes, in § 2
terminal-color, in § 2
unlimited-people, in § 2
+ "urg", in § 2
urg, in § 2
+ "w", in § 2
w, in § 2
water, in § 2
+ "white", in § 1
white, in § 1
+ "x", in § 2
x
@@ -842,7 +914,9 @@ cddl-key for rwx, in § 2
y, in § 2
+ "yellow", in § 2
yellow, in § 2
+ "zip-code", in § 2
zip-code, in § 2
CDDL Index
diff --git a/tests/cddl002.html b/tests/cddl002.html
index 3a14c50a84..496be696e0 100644
--- a/tests/cddl002.html
+++ b/tests/cddl002.html
@@ -619,14 +619,14 @@ Table of Contents
request-id
= uint
agent-capability
= &(
- receive-audio
: 1
- receive-video
: 2
- receive-presentation
: 3
- control-presentation
: 4
- receive-remote-playback
: 5
- control-remote-playback
: 6
- receive-streaming
: 7
- send-streaming
: 8
+ receive-audio
: 1
+ receive-video
: 2
+ receive-presentation
: 3
+ control-presentation
: 4
+ receive-remote-playback
: 5
+ control-remote-playback
: 6
+ receive-streaming
: 7
+ send-streaming
: 8
)
@@ -636,15 +636,23 @@ agent-capability, in § Unnumbered section
agent-info, in § Unnumbered section
agent-info-response, in § Unnumbered section
+ "control-presentation", in § Unnumbered section
control-presentation, in § Unnumbered section
+ "control-remote-playback", in § Unnumbered section
control-remote-playback, in § Unnumbered section
+ "receive-audio", in § Unnumbered section
receive-audio, in § Unnumbered section
+ "receive-presentation", in § Unnumbered section
receive-presentation, in § Unnumbered section
+ "receive-remote-playback", in § Unnumbered section
receive-remote-playback, in § Unnumbered section
+ "receive-streaming", in § Unnumbered section
receive-streaming, in § Unnumbered section
+ "receive-video", in § Unnumbered section
receive-video, in § Unnumbered section
request-id, in § Unnumbered section
response, in § Unnumbered section
+ "send-streaming", in § Unnumbered section
send-streaming, in § Unnumbered section
CDDL Index
diff --git a/tests/cddl003.bs b/tests/cddl003.bs
index e47e404409..ffbd78d1f9 100644
--- a/tests/cddl003.bs
+++ b/tests/cddl003.bs
@@ -56,6 +56,8 @@ nested = {
key2: "nested"
}
}
+
+warn = [ (dupl: "val"), "dupl" ]
Linking to the CDDL {#links}
@@ -77,3 +79,5 @@ If there is no ambiguity, a key or value may be referenced directly, for example
Otherwise, full path must be specified, as in `{^anon1/"v1"^}`, `{^anon2/key/"v2"^}`, `{^nested/key1/key2/"nested"^}`: {^anon1/"v1"^}, {^anon2/key/"v2"^}, {^nested/key1/key2/"nested"^}.
In particular, partial paths such as `{^key/"unique"^}` or `{^key2/"nested"^}` do not work: {^key/"unique"^}, {^key2/"nested"^} (Bikeshed reports `No 'cddl' refs found` messages in such cases).
+
+The {^warn^} type defines both a key and a value "dupl". Bikeshed reports a warning to the console to suggest creating additional CDDL types, because the shorthand `{^warn/dupl^}` can no longer be used. It remains possible to reference either construct by expliciting the link type, i.e., `dupl` and `dupl`: dupl, dupl.
diff --git a/tests/cddl003.console.txt b/tests/cddl003.console.txt
index f12b56d7bb..e0cd0ef649 100644
--- a/tests/cddl003.console.txt
+++ b/tests/cddl003.console.txt
@@ -1,6 +1,9 @@
-LINE 79: No 'cddl' refs found for '"unique"'.
+WARNING: CDDL value dupl defined in type "warn" creates a duplicate with another CDDL definition.
+Link type needs to be specified to reference the term.
+Consider creating additional CDDL types to disambiguate.
+LINE 81: No 'cddl' refs found for '"unique"'.
{^key/"unique"^}
-LINE 79: No 'cddl' refs found for '"nested"'.
+LINE 81: No 'cddl' refs found for '"nested"'.
{^key2/"nested"^}
LINE 17: Unexported dfn that's not referenced locally - did you mean to export it?
local end
diff --git a/tests/cddl003.html b/tests/cddl003.html
index 097c614cf2..f02f89bf6b 100644
--- a/tests/cddl003.html
+++ b/tests/cddl003.html
@@ -609,38 +609,40 @@ Table of Contents
1. The CDDL
CDDL specified for the local end:
agent-capability
= &(
- receive-audio
: 1
- receive-video
: 2
- receive-presentation
: 3
- control-presentation
: 4
- receive-remote-playback
: 5
- control-remote-playback
: 6
- receive-streaming
: 7
- send-streaming
: 8
+ receive-audio
: 1
+ receive-video
: 2
+ receive-presentation
: 3
+ control-presentation
: 4
+ receive-remote-playback
: 5
+ control-remote-playback
: 6
+ receive-streaming
: 7
+ send-streaming
: 8
)
CDDL specified for the remote end:
webExtension.ExtensionBase64Encoded
= {
- type
: "base64"
,
- value
: text,
+ type
: "base64"
,
+ value
: text,
}
CDDL specified for all modules:
barewords
= {
- bare
: [+ float],
- "word"
: int,
+ bare
: [+ float],
+ "word"
: int,
}
-anon1
= [("v1"
/ "v2"
)]
+anon1
= [("v1"
/ "v2"
)]
anon2
= {
- key
: "v1"
/ "v2"
/ "unique"
+ key
: "v1"
/ "v2"
/ "unique"
}
nested
= {
- key1
: {
- key2
: "nested"
+ key1
: {
+ key2
: "nested"
}
}
+
+warn
= [ (dupl
: "val"
), "dupl"
]
2. Linking to the CDDL
The agent-capability
production gives information about agent capabilities.
@@ -659,6 +661,7 @@ 2. If there is no ambiguity, a key or value may be referenced directly, for example {^"unique"^}
to target the "unique" value of anon2/key: "unique"
.
Otherwise, full path must be specified, as in {^anon1/"v1"^}
, {^anon2/key/"v2"^}
, {^nested/key1/key2/"nested"^}
: "v1"
, "v2"
, "nested"
.
In particular, partial paths such as {^key/"unique"^}
or {^key2/"nested"^}
do not work: "unique"
, "nested"
(Bikeshed reports No 'cddl' refs found
messages in such cases).
+ The warn
type defines both a key and a value "dupl". Bikeshed reports a warning to the console to suggest creating additional CDDL types, because the shorthand {^warn/dupl^}
can no longer be used. It remains possible to reference either construct by expliciting the link type, i.e., <a cddl-key for=warn>dupl</a>
and <a cddl-value for=warn>dupl</a>
: dupl, dupl.
Index
Terms defined by this specification
@@ -666,40 +669,89 @@ agent-capability, in § 1
anon1, in § 1
anon2, in § 1
+ "bare", in § 1
bare, in § 1
barewords, in § 1
"base64", in § 1
+ base64, in § 1
+ "control-presentation", in § 1
control-presentation, in § 1
+ "control-remote-playback", in § 1
control-remote-playback, in § 1
+
+ "dupl"
+
+
+ dupl
+
+ "key", in § 1
key, in § 1
+ "key1", in § 1
key1, in § 1
+ "key2", in § 1
key2, in § 1
local end, in § 1
"nested", in § 1
- nested, in § 1
+
+ nested
+
+ "receive-audio", in § 1
receive-audio, in § 1
+ "receive-presentation", in § 1
receive-presentation, in § 1
+ "receive-remote-playback", in § 1
receive-remote-playback, in § 1
+ "receive-streaming", in § 1
receive-streaming, in § 1
+ "receive-video", in § 1
receive-video, in § 1
remote end, in § 1
+ "send-streaming", in § 1
send-streaming, in § 1
+ "type", in § 1
type, in § 1
"unique", in § 1
+ unique, in § 1
"v1"
+
+ v1
+
"v2"
+
+ v2
+
+ "val", in § 1
+ val, in § 1
+ "value", in § 1
value, in § 1
+ warn, in § 1
webExtension.ExtensionBase64Encoded, in § 1
+ "word", in § 1
word, in § 1
CDDL Index
@@ -731,6 +783,8 @@ warn
= [ (dupl
: "val"
), "dupl"
]
+
Remote end
webExtension.ExtensionBase64Encoded
= {
@@ -754,6 +808,8 @@ warn
= [ (dupl
: "val"
), "dupl"
]
+
+
+
\ No newline at end of file