attribute-specifier
56 cases. Source: tests/attribute-specifier.json.
#1 canonical
Source
{#id .class .other key=value key2="quoted value"}Parse result
{
"type": "attributes",
"id": "id",
"classes": [
"class",
"other"
],
"attrs": {
"key": "value",
"key2": "quoted value"
}
}#2 empty specifier
Source
{}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}#3 id only
Source
{#main}Parse result
{
"type": "attributes",
"id": "main",
"classes": [],
"attrs": {}
}#4 class only
Source
{.lead}Parse result
{
"type": "attributes",
"id": null,
"classes": [
"lead"
],
"attrs": {}
}#5 several classes keep source order
Source
{.badge .warn .small}Parse result
{
"type": "attributes",
"id": null,
"classes": [
"badge",
"warn",
"small"
],
"attrs": {}
}#6 repeated class is kept, not deduplicated
Source
{.a .a}Parse result
{
"type": "attributes",
"id": null,
"classes": [
"a",
"a"
],
"attrs": {}
}#7 bare value
Source
{tone=info}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"tone": "info"
}
}#8 quoted value with whitespace
Source
{title="Pricing tiers"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"title": "Pricing tiers"
}
}#9 quoted value with escapes
Source
{title="say \"hi\" \\ done"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"title": "say \"hi\" \\ done"
}
}#10 quoted empty string
Source
{alt=""}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"alt": ""
}
}#11 quoted value may contain braces, hashes, dots, and equals
Source
{q="a } b { #c .d e=f"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"q": "a } b { #c .d e=f"
}
}#12 quoted value may contain a single quote
Source
{q="it's"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"q": "it's"
}
}#13 repeated id, last wins
Source
{#a #b}Parse result
{
"type": "attributes",
"id": "b",
"classes": [],
"attrs": {}
}#14 repeated key, last wins
Source
{k=1 k=2}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"k": "2"
}
}#15 leading, trailing, and repeated whitespace
Source
{ #a .b k=v }Parse result
{
"type": "attributes",
"id": "a",
"classes": [
"b"
],
"attrs": {
"k": "v"
}
}#16 tab as the only separator
Source
{#a .b k=v}Parse result
{
"type": "attributes",
"id": "a",
"classes": [
"b"
],
"attrs": {
"k": "v"
}
}#17 identifiers with digits, underscore, and hyphen
Source
{#h2-intro .col_3 data-x=y}Parse result
{
"type": "attributes",
"id": "h2-intro",
"classes": [
"col_3"
],
"attrs": {
"data-x": "y"
}
}#18 single-character identifiers
Source
{#a .b c=d}Parse result
{
"type": "attributes",
"id": "a",
"classes": [
"b"
],
"attrs": {
"c": "d"
}
}#19 bare values with punctuation
Source
{ratio=2:1 width=60% path=/a/b.c eq=a=b hash=#x dot=.y}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"ratio": "2:1",
"width": "60%",
"path": "/a/b.c",
"eq": "a=b",
"hash": "#x",
"dot": ".y"
}
}#20 bare value with a backslash is literal
Source
{path=C:\dir}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"path": "C:\\dir"
}
}#21 non-ASCII in values
Source
{label=Δ title="Δ revenue"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"label": "Δ",
"title": "Δ revenue"
}
}#22 reserved classes alongside a tone key
Source
{.info .badge tone=warn}Parse result
{
"type": "attributes",
"id": null,
"classes": [
"info",
"badge"
],
"attrs": {
"tone": "warn"
}
}#23 key that looks like a reserved word but is not
Source
{ids=1 classes=2 identity=3}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"ids": "1",
"classes": "2",
"identity": "3"
}
}#24 unterminated after items
invalid
Source
{#a .bParse result
{
"type": "attributes",
"id": "a",
"classes": [
"b"
],
"attrs": {}
}Diagnostics: ATTR_UNTERMINATED
#25 unterminated, empty
invalid
Source
{Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_UNTERMINATED
#26 unterminated quoted value swallows the rest, reported once
invalid
Source
{k="abcParse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_UNTERMINATED_QUOTE
#27 unterminated quoted value even when a brace follows
invalid
Source
{k="abc}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_UNTERMINATED_QUOTE
#28 empty id
invalid
Source
{#}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#29 empty class
invalid
Source
{.}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#30 id starting with a digit
invalid
Source
{#1abc}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#31 class starting with a hyphen
invalid
Source
{.-x}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#32 id containing a dot is one bad item, not two
invalid
Source
{#a.b}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#33 class containing an equals sign
invalid
Source
{.a=b}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#34 key starting with a digit
invalid
Source
{9lives=1}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#35 key with an illegal character
invalid
Source
{ke$y=1}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_EXPECTED_EQUALS
#36 non-ASCII letter in an identifier
invalid
Source
{#über}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER
#37 bare word without a value
invalid
Source
{foo}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_EXPECTED_EQUALS
#38 empty bare value
invalid
Source
{key=}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_EMPTY_VALUE
#39 space after equals splits into an empty value and a bare word
invalid
Source
{key= value}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_EMPTY_VALUE ATTR_EXPECTED_EQUALS
#40 space before equals
invalid
Source
{key =value}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_EXPECTED_EQUALS ATTR_UNEXPECTED_CHAR
#41 bare value containing a double quote
invalid
Source
{key=a"b}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_VALUE
#42 bare value containing a single quote
invalid
Source
{key=it's}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_VALUE
#43 single-quoted value is not a quoting form
invalid
Source
{key='v'}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_VALUE
#44 text after the closing quote
invalid
Source
{key="a"b}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_VALUE
#45 unknown escape in a quoted value
invalid
Source
{key="a\nb"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_ESCAPE
#46 escaped brace is not a defined escape
invalid
Source
{key="a\}b"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_ESCAPE
#47 two unknown escapes report twice
invalid
Source
{key="\a\b"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_BAD_ESCAPE ATTR_BAD_ESCAPE
#48 item starting with equals
invalid
Source
{=x}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_UNEXPECTED_CHAR
#49 item starting with a quote
invalid
Source
{"x"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_UNEXPECTED_CHAR
#50 item starting with punctuation
invalid
Source
{@x}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_UNEXPECTED_CHAR
#51 reserved key id
invalid
Source
{id=x}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_RESERVED_KEY
#52 reserved key class
invalid
Source
{class=x}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {}
}Diagnostics: ATTR_RESERVED_KEY
#53 reserved key does not shadow the id item
invalid
Source
{#real id=fake}Parse result
{
"type": "attributes",
"id": "real",
"classes": [],
"attrs": {}
}Diagnostics: ATTR_RESERVED_KEY
#54 several bad items are each reported and the good ones survive
invalid
Source
{# .b bad key= .c}Parse result
{
"type": "attributes",
"id": null,
"classes": [
"b",
"c"
],
"attrs": {}
}Diagnostics: ATTR_BAD_IDENTIFIER ATTR_EXPECTED_EQUALS ATTR_EMPTY_VALUE
#55 newline is not whitespace, so it glues items into one bad identifier
invalid
Source
{#a
.b}Rendered
{#a .b}
Downgrade
{#a
.b}
Diagnostics: ATTR_BAD_IDENTIFIER
#56 bad item followed by good quoted value
invalid
Source
{=x k="a b"}Parse result
{
"type": "attributes",
"id": null,
"classes": [],
"attrs": {
"k": "a b"
}
}Diagnostics: ATTR_UNEXPECTED_CHAR