mirror of https://github.com/kdl-org/kdl.git
Compare commits
258 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
b8570137b6 | |
|
|
4e4c0af933 | |
|
|
568d103308 | |
|
|
5f73be73c7 | |
|
|
54a49798d9 | |
|
|
0ff5a6ffa3 | |
|
|
3b75764880 | |
|
|
f238372fc9 | |
|
|
d8faf22503 | |
|
|
ebf9ef7649 | |
|
|
5366787152 | |
|
|
85930fd8dd | |
|
|
8e18add9d4 | |
|
|
43d8a5dbf3 | |
|
|
5d82f60407 | |
|
|
ab92232126 | |
|
|
5e5920fd7c | |
|
|
11615b2807 | |
|
|
42ce272508 | |
|
|
a88c450d7d | |
|
|
b942867c58 | |
|
|
ee79f9f755 | |
|
|
84911feb11 | |
|
|
c54ebd9473 | |
|
|
6c55186c03 | |
|
|
21a9eb3f65 | |
|
|
aab44fcd1b | |
|
|
d76063e8e9 | |
|
|
e9e6a844bd | |
|
|
7322f37800 | |
|
|
919e154dcd | |
|
|
53a884c93d | |
|
|
23159696d1 | |
|
|
82a91697a1 | |
|
|
4263a9de3f | |
|
|
0c2dde6d3c | |
|
|
20375a187e | |
|
|
91cd421988 | |
|
|
34e4259dfc | |
|
|
a3b37857e2 | |
|
|
9c9d2b2445 | |
|
|
39a098237d | |
|
|
1147fe965a | |
|
|
72a3c769d7 | |
|
|
757eb5517a | |
|
|
2843744a87 | |
|
|
6d2bc50939 | |
|
|
3b19a761ff | |
|
|
ce3b2eeb7f | |
|
|
353fd85a2b | |
|
|
3c1cf4f2a1 | |
|
|
76dc3e3002 | |
|
|
646dafcd35 | |
|
|
c930f1d124 | |
|
|
0f77ec4f9f | |
|
|
6ceecd85d6 | |
|
|
0e58f61b18 | |
|
|
edbdab2891 | |
|
|
7aa01a21b7 | |
|
|
b82c924013 | |
|
|
d1ceb44f40 | |
|
|
717e86cb1c | |
|
|
ebef8751c0 | |
|
|
ca2bd45a66 | |
|
|
1a6b17b0ae | |
|
|
ace7ec7a96 | |
|
|
171bc71f42 | |
|
|
75a0869a6c | |
|
|
91f7ec05fa | |
|
|
a60d27c99d | |
|
|
ec7d679e88 | |
|
|
4dd6ebbbfe | |
|
|
373e0ed973 | |
|
|
ee41a76357 | |
|
|
831ecc1d52 | |
|
|
29ae90e9f5 | |
|
|
bf40df5581 | |
|
|
eaa6c40693 | |
|
|
5971e61eda | |
|
|
4334a6d552 | |
|
|
e7b5ec6bf9 | |
|
|
e477f32f05 | |
|
|
40aac6602e | |
|
|
073d07748a | |
|
|
1f050633ad | |
|
|
8b2a019998 | |
|
|
8aa4c15758 | |
|
|
76a1de517b | |
|
|
90e22bc789 | |
|
|
1588b1f5fd | |
|
|
fa3050ccc0 | |
|
|
93c4400a96 | |
|
|
d8d583a45e | |
|
|
1e924bcc7f | |
|
|
bcfb3321c4 | |
|
|
6a77436e09 | |
|
|
fa204cec62 | |
|
|
c9134e3c16 | |
|
|
bea0f67685 | |
|
|
fa9d30388c | |
|
|
d064bc9026 | |
|
|
281de7e977 | |
|
|
de1dbd2c33 | |
|
|
4bec08b70d | |
|
|
d0b30c3f35 | |
|
|
aeb41cc7d7 | |
|
|
2de2ddc708 | |
|
|
b294e9cb5a | |
|
|
9e7b958f0c | |
|
|
9132a96e56 | |
|
|
dadcfdf2ae | |
|
|
2fcf6d42d3 | |
|
|
2710c90ff5 | |
|
|
e773747b0b | |
|
|
fa816ca6df | |
|
|
631ec14059 | |
|
|
921211782f | |
|
|
ec7880d4a5 | |
|
|
7ab86588c0 | |
|
|
abae1f9a39 | |
|
|
793a9d4ce7 | |
|
|
f0f9589636 | |
|
|
f81fcfada5 | |
|
|
b1163e1f91 | |
|
|
40d8c83aca | |
|
|
f767472cab | |
|
|
2d4bcd0b51 | |
|
|
35ac19b854 | |
|
|
522ce8591e | |
|
|
172c67b602 | |
|
|
c15b5c2798 | |
|
|
cfd86ce70a | |
|
|
29495006bc | |
|
|
af91cc6319 | |
|
|
1d6809ee46 | |
|
|
1f28fb0e83 | |
|
|
bc2b995bfe | |
|
|
24cd2141d3 | |
|
|
de37e11a29 | |
|
|
c273d249b6 | |
|
|
094a615f82 | |
|
|
1294f9733d | |
|
|
935d054d13 | |
|
|
f02ba59c0c | |
|
|
6d091fd493 | |
|
|
491cc46f89 | |
|
|
b635470ab2 | |
|
|
6d359d2e4c | |
|
|
419995ff19 | |
|
|
057e8c894d | |
|
|
d53d99ff2e | |
|
|
d4333322d9 | |
|
|
511ab6b6ff | |
|
|
055de4e1be | |
|
|
39b9fac0d3 | |
|
|
0022536fc7 | |
|
|
90cd0b1bb9 | |
|
|
50d378f1db | |
|
|
b51859edf3 | |
|
|
56f399bf71 | |
|
|
9f061537c9 | |
|
|
817a7dc0ab | |
|
|
54df7f0cab | |
|
|
a0d5030e3b | |
|
|
8de7df6eaa | |
|
|
7790505bf9 | |
|
|
fc1b59436a | |
|
|
49402ccb7b | |
|
|
13799de32b | |
|
|
c8488db13e | |
|
|
5a7b339ed4 | |
|
|
b42b6c80f0 | |
|
|
31fd7bd00a | |
|
|
63feef70fe | |
|
|
fada1fc1dd | |
|
|
5e89c4550a | |
|
|
2694146af4 | |
|
|
85aa3a09ab | |
|
|
e6356d5a03 | |
|
|
99abeef6d3 | |
|
|
eb55930264 | |
|
|
ef93a6b10c | |
|
|
7b7d57bf29 | |
|
|
9f10522717 | |
|
|
0836df1c19 | |
|
|
568c096465 | |
|
|
3b39e29fee | |
|
|
270c60ca9a | |
|
|
54f5fc8025 | |
|
|
ef1bb689b0 | |
|
|
652590fad3 | |
|
|
6fa99c2586 | |
|
|
11d8e912fc | |
|
|
09801faa93 | |
|
|
f3e5ff6027 | |
|
|
a75ca13c15 | |
|
|
a3d39e7749 | |
|
|
6feeccc491 | |
|
|
6bf9b1c588 | |
|
|
0dc4a92a69 | |
|
|
06d1d67359 | |
|
|
8d252133b7 | |
|
|
5253595c14 | |
|
|
6bab316b0f | |
|
|
20d65edb7d | |
|
|
0b99021180 | |
|
|
c8dd45a0f1 | |
|
|
76d5dd542a | |
|
|
fb80016011 | |
|
|
d437cf228b | |
|
|
0a4a14d87a | |
|
|
825ff2c17d | |
|
|
337bd1bccf | |
|
|
ffeea8e5aa | |
|
|
f38edc765d | |
|
|
78a2d5f5ed | |
|
|
1bf4d740fa | |
|
|
2d5e543bbe | |
|
|
69ac280bf0 | |
|
|
910f6e90a7 | |
|
|
d9459f67c0 | |
|
|
4f739516e6 | |
|
|
e8fe0c38a8 | |
|
|
9daa2019f7 | |
|
|
81fda2455f | |
|
|
b027d3a751 | |
|
|
fcd489afa6 | |
|
|
81a58e64ee | |
|
|
cc1da35435 | |
|
|
168fd6abdc | |
|
|
3e915b5be3 | |
|
|
8976e2da68 | |
|
|
5a566ea5e3 | |
|
|
23bbe1ebec | |
|
|
f82a106814 | |
|
|
a1d85e9fe1 | |
|
|
617ab86e5e | |
|
|
e5e131920f | |
|
|
05d16e0597 | |
|
|
93cc6724f2 | |
|
|
3f293ff01d | |
|
|
b22ed4b1b0 | |
|
|
5a2f4969fe | |
|
|
05af2fb657 | |
|
|
296fe129e1 | |
|
|
99d9168a4a | |
|
|
24faa6b75c | |
|
|
5c4f83b824 | |
|
|
f59ca2476a | |
|
|
8ca18fec0a | |
|
|
0bbe723eb5 | |
|
|
8ae4ae648e | |
|
|
16269d4325 | |
|
|
a2e53990ed | |
|
|
843beb99a5 | |
|
|
785abebfc5 | |
|
|
3df2b719a0 | |
|
|
1351f9fa1b |
|
|
@ -0,0 +1,8 @@
|
|||
# See http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*.{md,xml,org}]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
name: "Update Editor's Copy"
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- CONTRIBUTING.md
|
||||
- LICENSE.md
|
||||
- .gitignore
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- CONTRIBUTING.md
|
||||
- LICENSE.md
|
||||
- .gitignore
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Update Editor's Copy"
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Setup"
|
||||
id: setup
|
||||
run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT"
|
||||
|
||||
- name: "Caching"
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.refcache
|
||||
.venv
|
||||
.gems
|
||||
node_modules
|
||||
.targets.mk
|
||||
key: i-d-${{ steps.setup.outputs.date }}
|
||||
restore-keys: i-d-
|
||||
|
||||
- name: "Build Drafts"
|
||||
uses: martinthomson/i-d-template@v1
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
|
||||
- name: "Update GitHub Pages"
|
||||
uses: martinthomson/i-d-template@v1
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
with:
|
||||
make: gh-pages
|
||||
token: ${{ github.token }}
|
||||
|
||||
- name: "Archive Built Drafts"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: |
|
||||
draft-*.html
|
||||
draft-*.txt
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
name: Lint the test files
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "tests/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "tests/**"
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Verify failing tests and orphaned tests
|
||||
run: |
|
||||
cd tests/test_cases
|
||||
python ../../.github/workflows/lint-tests/lint.py
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import typing
|
||||
|
||||
def findTestFiles(path) -> typing.Generator[str, None, None]:
|
||||
for root, _, filenames in os.walk(path):
|
||||
for filename in filenames:
|
||||
yield os.path.join(root, filename)
|
||||
|
||||
# strip the leading folder name, so they can be directly compared
|
||||
inputFiles = set(x[len("input")+1:] for x in findTestFiles("input"))
|
||||
validFiles = set(x[len("expected_kdl")+1:] for x in findTestFiles("expected_kdl"))
|
||||
|
||||
invalidFiles = inputFiles - validFiles
|
||||
orphanedFiles = validFiles - inputFiles
|
||||
|
||||
SUCCESS = True
|
||||
|
||||
# Check for any expected_kdl files without a corresponding input file.
|
||||
if orphanedFiles:
|
||||
SUCCESS = False
|
||||
print("ERROR: There are outputs in /expected_kdl without corresponding tests in /input:\n" + "\n".join([" "+x for x in orphanedFiles]))
|
||||
|
||||
# Check for any input files lacking an expected_kdl file
|
||||
# (aka inputs expected to generate a parse error)
|
||||
# that don't have a _fail suffix.
|
||||
misnamedFiles: list[str] = []
|
||||
for filepath in invalidFiles:
|
||||
basepath, ext = os.path.splitext(filepath)
|
||||
if not basepath.endswith("_fail"):
|
||||
misnamedFiles.append(filepath)
|
||||
if misnamedFiles:
|
||||
SUCCESS = False
|
||||
print("ERROR: There are tests in /input without corresponding outputs in /expected_kdl, but they don't have a _fail suffix:\n" + "\n".join([" "+x for x in misnamedFiles]))
|
||||
|
||||
# Check for any expected_kdl files that don't end in a newline.
|
||||
noNewlineFiles: list[str] = []
|
||||
for filepath in validFiles:
|
||||
with open("expected_kdl/" + filepath, "r", encoding="utf-8") as fh:
|
||||
text = fh.read()
|
||||
if not text.endswith("\n"):
|
||||
noNewlineFiles.append(filepath)
|
||||
if noNewlineFiles:
|
||||
SUCCESS = False
|
||||
print("ERROR: There are outputs in /expected_kdl that don't end with a newline:\n" + "\n".join([" "+x for x in noNewlineFiles]))
|
||||
|
||||
if not SUCCESS:
|
||||
sys.exit(1)
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
name: "Publish New Draft Version"
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "draft-*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
email:
|
||||
description: "Submitter email"
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Publish New Draft Version"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# See https://github.com/actions/checkout/issues/290
|
||||
- name: "Get Tag Annotations"
|
||||
run: git fetch -f origin ${{ github.ref }}:${{ github.ref }}
|
||||
|
||||
- name: "Setup"
|
||||
id: setup
|
||||
run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT"
|
||||
|
||||
- name: "Caching"
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.refcache
|
||||
.venv
|
||||
.gems
|
||||
node_modules
|
||||
.targets.mk
|
||||
key: i-d-${{ steps.setup.outputs.date }}
|
||||
restore-keys: i-d-
|
||||
|
||||
- name: "Build Drafts"
|
||||
uses: martinthomson/i-d-template@v1
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
|
||||
- name: "Upload to Datatracker"
|
||||
uses: martinthomson/i-d-template@v1
|
||||
with:
|
||||
make: upload
|
||||
env:
|
||||
UPLOAD_EMAIL: ${{ inputs.email }}
|
||||
|
||||
- name: "Archive Submitted Drafts"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: "versioned/draft-*-[0-9][0-9].*"
|
||||
|
|
@ -1,2 +1,25 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
*.html
|
||||
*.pdf
|
||||
*.redxml
|
||||
*.swp
|
||||
*.txt
|
||||
*.upload
|
||||
*~
|
||||
.tags
|
||||
/*-[0-9][0-9].xml
|
||||
/.*.mk
|
||||
/.gems/
|
||||
/.refcache
|
||||
/.venv/
|
||||
/.vscode/
|
||||
/lib
|
||||
/node_modules/
|
||||
/versioned/
|
||||
Gemfile.lock
|
||||
archive.json
|
||||
draft-marchan-kdl2.xml
|
||||
package-lock.json
|
||||
report.xml
|
||||
!requirements.txt
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<note title="Discussion Venues" removeInRFC="true">
|
||||
<t>Source for this draft and an issue tracker can be found at
|
||||
<eref target="https://github.com/kdl-org/kdl"/>.</t>
|
||||
</note>
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
# KDL Changelog
|
||||
|
||||
## 2.0.0 (2024-12-21)
|
||||
|
||||
### Grammar
|
||||
|
||||
* Solidus/Forward slash (`/`) is no longer an escaped character.
|
||||
* Space (`U+0020`) can now be written into quoted strings with the `\s`
|
||||
escape.
|
||||
* Single line comments (`//`) can now be immediately followed by a newline.
|
||||
* All literal whitespace following a `\` in a string is now discarded.
|
||||
* Vertical tabs (`U+000B`) are now considered to be newlines.
|
||||
* The grammar syntax itself has been described, and some confusing definitions
|
||||
in the grammar have been fixed accordingly (mostly related to escaped
|
||||
characters).
|
||||
* `,`, `<`, and `>` are now legal identifier characters. They were previously
|
||||
reserved for KQL but this is no longer necessary.
|
||||
* Code points under `0x20` (except newline and whitespace code points), code
|
||||
points above `0x10FFFF`, Delete control character (`0x7F`), and the [unicode
|
||||
"direction control"
|
||||
characters](https://www.w3.org/International/questions/qa-bidi-unicode-controls)
|
||||
are now completely banned from appearing literally in KDL documents. They
|
||||
can now only be represented in regular strings, and there's no facilities to
|
||||
represent them in raw strings. This should be considered a security
|
||||
improvement.
|
||||
* Raw strings no longer require an `r` prefix: they are now specified by using
|
||||
`#""#`.
|
||||
* Raw string productions are now explicitly non-greedy (and "fallible").
|
||||
* Line continuations can be followed by an EOF now, instead of requiring a
|
||||
newline (or comment). `node \<EOF>` is now a legal KDL document.
|
||||
* `#` is no longer a legal identifier character.
|
||||
* `null`, `true`, and `false` are now `#null`, `#true`, and `#false`. Using
|
||||
the unprefixed versions of these values is a syntax error.
|
||||
* The spec prose has more explicitly stated that whitespace and newlines are
|
||||
not valid identifier characters, even though the grammar already expressed
|
||||
this.
|
||||
* Bare identifiers can now be used as values in Arguments and Properties, and are interpreted as string values.
|
||||
* The spec prose now more explicitly states that strings and raw strings can
|
||||
be used as type annotations.
|
||||
* Removed a statement in the spec prose that said "It is reasonable for an
|
||||
implementation to ignore null values altogether when deserializing". This is
|
||||
no longer encouraged or desired.
|
||||
* Code points have been constrained to [Unicode Scalar
|
||||
Values](https://unicode.org/glossary/#unicode_scalar_value) only, including
|
||||
values used in string escapes (`\u{}`). All KDL documents and string values
|
||||
should be valid UTF-8 now, as was intended.
|
||||
* The last node in a child block no longer needs to be terminated with `;`,
|
||||
even if the closing `}` is on the same line, so this is now a legal node:
|
||||
`node{foo;bar;baz}`
|
||||
* More places allow whitespace (node-spaces, specifically) now. With great
|
||||
power comes great responsibility:
|
||||
* Inside `(foo)` annotations (so, `( foo )` would be legal (`( f oo )` would
|
||||
not be, since it has two identifiers))
|
||||
* Between annotations and the thing they're annotating (`(blah) node (thing)
|
||||
1 y= (who) 2`)
|
||||
* Around `=` for props (`x = 1`)
|
||||
* The BOM is now only allowed as the first character in a document. It was
|
||||
previously treated as generic whitespace.
|
||||
* Multi-line strings must now use `"""` as delimiters. The opening delimiter must be immediately followed by a newline, and the closing delimiter must be on its own line, prefixed by optional whitespace.
|
||||
* Multi-line strings are now automatically dedented, according to the common
|
||||
whitespace matching the whitespace prefix of the closing line.
|
||||
* `.1`, `+.1` etc are no longer valid identifiers, to prevent confusion and
|
||||
conflicts with numbers.
|
||||
* Multi-line strings' literal Newline sequences are now normalized to single
|
||||
`LF`s.
|
||||
* `#inf`, `#-inf`, and `#nan` have been added in order to properly support
|
||||
IEEE floats for implementations that choose to represent their decimals that
|
||||
way.
|
||||
* Correspondingly, the identifiers `inf`, `-inf`, and `nan` are now syntax
|
||||
errors.
|
||||
* `u128` and `i128` have been added as well-known number type annotations.
|
||||
* Slashdash (`/-`) -compatible locations adjusted to be more clear and
|
||||
intuitive. They can now be used in exactly three different places: before nodes,
|
||||
before entire entries, or before entire child blocks.
|
||||
* Furthermore, The ordering of slashdashed elements has been restricted such
|
||||
that a slashdashed child block cannot go before an entry (including slashdashed
|
||||
entries).
|
||||
* Optional version marker `/- kdl-version 2` (or `1`) as the first line in a document, optionally preceded by the BOM.
|
||||
|
||||
### KQL
|
||||
|
||||
> [!INFO] Note: these are provided for convenience, but as of the 2.0.0 KDL spec release,
|
||||
> KQL itself is not finalized and should be considered a separate specification,
|
||||
> alongside the Schema spec and others.
|
||||
|
||||
* There's now a _required_ descendant selector (`>>`), instead of using plain
|
||||
spaces for that purpose.
|
||||
* The "any sibling" selector is now `++` instead of `~`, for consistency with
|
||||
the new descendant selector.
|
||||
* Some parsing logic around the grammar has changed.
|
||||
* Multi- and single-line comments are now supported, as well as line
|
||||
continuations with `\`.
|
||||
* Map operators have been removed entirely.
|
||||
|
||||
---
|
||||
|
||||
## 2.0.0 Draft Changelogs
|
||||
|
||||
### 2.0.0-draft.8 (2024-12-14)
|
||||
|
||||
* Some details have been clarified around the treatment of whitespace in
|
||||
multiline strings.
|
||||
* `raw-string` productions have been updated to be explicitly non-greedy and
|
||||
"fallible".
|
||||
* Some tests have been added, others adjusted, some removed, after a cleanup pass.
|
||||
|
||||
|
||||
### 2.0.0-draft.7 (2024-12-10)
|
||||
|
||||
* `node-space` is now allowed as whitespace after a `slashdash`, meaning line
|
||||
continuations will work now.
|
||||
* One or two consecutive double-quotes are now allowed in the bodies of
|
||||
multi-line quoted strings, without needing to be escaped.
|
||||
* Grammar has been fixed to disallow raw strings like `#"""#`, which are now
|
||||
properly treated as invalid multi-line raw strings (instead of the equivalent of
|
||||
`"\""`).
|
||||
* Test suite has been updated to include a `_fail` suffix in all test cases
|
||||
which are expected to fail.
|
||||
* A slew of additional slashdash and multi-line string compliance tests have
|
||||
been added. Have fun. :)
|
||||
* The organization of string types in the spec prose has been updated to a
|
||||
hopefully more helpful structure.
|
||||
|
||||
|
||||
### 2.0.0-draft.6 (2024-12-04)
|
||||
|
||||
* Multiline strings, both Raw and Quoted, must now use `"""` instead of a single `"`. Using `"""` for a single-line string is a syntax error.
|
||||
* Fixed an issue with the `unicode_silly` test case.
|
||||
* Some rewordings and clarification in the spec prose.
|
||||
* Slight grammar tweak where the pre-terminator `node-space*` for `node` and `final-node` have been moved into `base-node`.
|
||||
|
||||
|
||||
### 2.0.0-draft.5 (2024-11-28)
|
||||
|
||||
* Equals signs other than `=` are no longer supported in properties.
|
||||
* 128-bit integer type annotations have been added to the list of "well-known"
|
||||
type annotations.
|
||||
* Multiline string escape rules have been tweaked significantly.
|
||||
* `\s` is now a valid escape within a string, representing a space character.
|
||||
* Slashdash (`/-`)-compatible locations and related grammar adjusted to be more
|
||||
clear and intuitive. This includes some changes relating to whitespace,
|
||||
including comments and newlines, which are breaking changes.
|
||||
* Various updates to test suite to reflect changes.
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Contributing
|
||||
|
||||
## Mechanics
|
||||
|
||||
Contributions can be made by creating pull requests.
|
||||
The GitHub interface supports creating pull requests using the Edit (✏) button.
|
||||
|
||||
|
||||
## Building the Specification
|
||||
|
||||
The specification is written in
|
||||
[kramdown-rfc](https://github.com/cabo/kramdown-rfc/wiki/Syntax2), which
|
||||
compiles via [RFCXML](https://authors.ietf.org/rfcxml-vocabulary) to text and
|
||||
HTML.
|
||||
|
||||
You can build the formatted versions or the intermediate RFCXML file using
|
||||
https://author-tools.ietf.org/ or locally by running `make`. To preserve the
|
||||
intermediate RFCXML form in a local build, run `make draft-marchan-kdl2.xml`
|
||||
once.
|
||||
|
||||
Command line usage requires that you have the necessary software installed. See
|
||||
[the instructions](https://github.com/martinthomson/i-d-template/blob/main/doc/SETUP.md).
|
||||
146
JSON-IN-KDL.md
146
JSON-IN-KDL.md
|
|
@ -3,79 +3,112 @@ JSON-in-KDL (JiK)
|
|||
|
||||
This specification describes a canonical way to losslessly encode [JSON](https://json.org) in [KDL](https://kdl.dev). While this isn't a very useful thing to want to do on its own, it's occasionally useful when using a KDL toolchain while speaking with a JSON-consuming or -emitting service.
|
||||
|
||||
This is version 2.0.0 of JiK.
|
||||
This is version 4.0.0 of JiK.
|
||||
|
||||
JSON-in-KDL (JiK from now on) is a kdl microsyntax consisting of three types of nodes:
|
||||
|
||||
* literal nodes, with `-` as the nodename
|
||||
* array nodes, with `array` as the nodename
|
||||
* object nodes, with `object` as the nodename
|
||||
JSON-in-KDL (JiK from now on) is a kdl microsyntax consisting of named nodes that represent objects, arrays, or literal values.
|
||||
|
||||
----
|
||||
|
||||
Literal nodes are used to represent a JSON literal, which luckily KDL's literal syntax is a superset of. They contain a single value, the literal they're representing. For example, to represent the JSON literal `true`, you'd write `- true` in JiK.
|
||||
There are two ways to write a JSON literal into JiK:
|
||||
|
||||
(In many cases this isn't necessary, and KDL literals can be directly used instead. Literal nodes are necessary only for a top-level literal, or to intersperse literals with arrays or objects inside an array or object node.)
|
||||
* As a node with any nodename and a single argument, like `- #true` (for the JSON `true`) or `foo 5` (for the JSON `5`).
|
||||
* When nested in arrays or objects, literals can usually be written as arguments (for array nodes) or properties (for object nodes). See below for details.
|
||||
|
||||
----
|
||||
|
||||
Array nodes are used to represent a JSON array. They can contain zero or more unnamed arguments, followed by zero or more child nodes; these are taken as the items of the array, in order of appearance.
|
||||
JSON arrays are represented in JiK as a node with any nodename, with zero or more arguments and/or zero or more children with `-` nodenames.
|
||||
|
||||
This means that simple arrays of literals can be written compactly and simply; a JSON array like `[1,2,3]` can be written in JiK as `array 1 2 3`. When an array contains nested arrays or objects, the child nodes are used; a JSON array like `[1, [true, false], 3]` can be written in JiK as:
|
||||
Arguments can encode literals - for example, the JSON `[1, 2, 3]` can be written in JiK as `- 1 2 3`.
|
||||
|
||||
Children can encode literals and/or nested arrays and objects. For example, the JSON `[1, [true, false], 3]` can be written in JiK as:
|
||||
|
||||
```kdl
|
||||
array {
|
||||
- {
|
||||
- 1
|
||||
array true false
|
||||
- #true #false
|
||||
- 3
|
||||
}
|
||||
```
|
||||
|
||||
The two methods of writing children can be mixed, pulling the prefix of the array that is just literals into the arguments of the node. The preceding example could thus also be written as:
|
||||
The arguments and/or children, taken in order, represent the items of the array.
|
||||
|
||||
Arguments and children can be mixed, if desired. The preceding example could also be written as:
|
||||
|
||||
```kdl
|
||||
array 1 {
|
||||
array true false
|
||||
- 1 {
|
||||
- #true #false
|
||||
- 3
|
||||
}
|
||||
```
|
||||
|
||||
Two otherwise-ambiguous cases must be manually annotated with an `(array)` type annotation:
|
||||
|
||||
* A single-element array (such as `[1]`) written using arguments (as `- 1`) would be ambiguous with a literal node.
|
||||
To indicate this is an array, it must be written as `(array)- 1`
|
||||
(Or rewritten to use child nodes, like `- { - 1 }`.)
|
||||
* An empty array (JSON `[]`) must use the `(array)` type annotation, like `(array)-`.
|
||||
|
||||
The `(array)` type annotation can be used on any other valid array node if desired, but has no effect in such cases.
|
||||
|
||||
----
|
||||
|
||||
Object nodes are used to represent a JSON object. They can contain zero or more named properties, followed by zero or more child nodes; these are taken as the key/value pairs of the object, in order of appearance.
|
||||
JSON objects are represented in JiK as a node with any nodename, with zero or more properties and/or zero or more children with any nodenames.
|
||||
|
||||
If the value of a key/value pair is a literal, it can be encoded as a named property on the object. For example, the JSON object `{"foo": 1, "bar": true}` could be written in JiK as `object foo=1 bar=true`.
|
||||
Properties can encode literals - for example, the JSON `{"foo": 1, "bar": true}` can be written in JiK as `- foo=1 bar=#true`.
|
||||
|
||||
Alternately, key/value pairs can be encoded as child nodes, using a type annotation on the node name to encode the key, and the node itself as the value. The preceding example could instead have been written as:
|
||||
Children can encode literals and/or nested arrays and objects,
|
||||
using the nodename for the item's key.
|
||||
|
||||
For example, the JSON `{"foo": 1, "bar": [2, {"baz": 3}], "qux":4}` can be written in JiK as:
|
||||
|
||||
```kdl
|
||||
object {
|
||||
(foo)- 1
|
||||
(bar)- true
|
||||
}
|
||||
```
|
||||
|
||||
Of course, using children for literals is overly-verbose. It's only necessary when nesting arrays or objects into objects; for example, the JSON object `{"foo": [1, 2, {"bar": 3}], "baz":4}` can be written in JiK as:
|
||||
|
||||
```kdl
|
||||
object {
|
||||
(foo)array 1 2 {
|
||||
object bar=3
|
||||
- {
|
||||
foo 1
|
||||
bar 2 {
|
||||
- baz=3
|
||||
}
|
||||
(baz)- 4
|
||||
qux 4
|
||||
}
|
||||
```
|
||||
|
||||
As with arrays, child nodes and properties can be mixed. The precise order of a JSON object's keys isn't *meant* to be meaningful, so as long as that's true, *all* the keys with literal values can be pulled into the argument list. The preceding example could thus also be written as:
|
||||
As with arrays, child nodes and properties can be mixed, so the preceding example could have been written as:
|
||||
|
||||
```kdl
|
||||
object baz=4 {
|
||||
(foo)array 1 2 {
|
||||
object bar=3
|
||||
- foo=1 {
|
||||
bar 2 {
|
||||
- baz=3
|
||||
}
|
||||
qux 4
|
||||
}
|
||||
```
|
||||
|
||||
Or, so long as the exact order of properties isn't meaningful (it's not *meant* to be in JSON),
|
||||
*all* the literal-valued keys can be pulled up into properties,
|
||||
leaving children nodes solely for nested arrays and objects:
|
||||
|
||||
```kdl
|
||||
- foo=1 qux=4 {
|
||||
bar 2 {
|
||||
- baz=3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The properties and/or children of the node represent the items of the object,
|
||||
with the property names and child nodenames as each item's key.
|
||||
All "keys" in an object node must be unique.
|
||||
|
||||
As with arrays, there are two ambiguous cases that must be manually annotated with the `(object)` type annotation:
|
||||
|
||||
* An object containing a single item whose key is "-" (like `{"-": 1}`) written using children (like `- { - 1 }`)
|
||||
would be ambiguous with an array node.
|
||||
To indicate this is an object, it must be written as `(object)- { - 1 }`.
|
||||
(Or, if the sole item's value is a literal, as in this example,
|
||||
it can be rewritten to use properties, as `- -=1`.)
|
||||
* An empty object (JSON `{}`) must use the `(object)` type annotation, like `(object)-`.
|
||||
|
||||
As with array nodes, `(object)` can be used on any valid object node if desired.
|
||||
|
||||
----
|
||||
|
||||
Converting JiK back to JSON is a trivial process: literal nodes are encoded as their literal value; array nodes are encoded as their items, comma-separated and surrounded with `[]`; object nodes are encoded as their key/value pairs, comma-separated and surrounded with `{}`.
|
||||
|
|
@ -84,6 +117,45 @@ Only valid JiK nodes can be encoded to JSON; if a JiK document contains an inval
|
|||
|
||||
* A literal node is valid if it contains a single unnamed argument.
|
||||
|
||||
* An array node is valid if it contains only unnamed arguments and/or child nodes without type annotations on their node names.
|
||||
* An array node is valid if it contains only unnamed arguments and/or child nodes named "-". If it contains no arguments and no child nodes, its nodename *must* have the `(array)` type annotation.
|
||||
|
||||
* An object node is valid if it contains only named properties and/or child nodes with type annotations on their node names. Additionally, all "keys" must be unique within the node, whether they're encoded as property names or type annotations on node names.
|
||||
* An object node is valid if it contains only named properties and/or child nodes. Additionally, all "keys" must be unique within the node, whether they're encoded as property names or child node names. If it contains no properties and no child nodes, its nodename *must* have the `(object)` type annotation.
|
||||
|
||||
----
|
||||
|
||||
Note that, outside of array/object items, the nodename is not meaningful in JiK.
|
||||
For simplicity, this document uses `-` for all such nodenames
|
||||
(and it is recommended that an automated JSON-to-KDL converter do the same),
|
||||
but this means it is possible to write a JiK object as meaningful KDL
|
||||
and embed it within a larger KDL document.
|
||||
|
||||
Here's a fictitious example describing an HTTP request with a JSON body,
|
||||
where the `body` node is an embedded JiK node
|
||||
that nevertheless reads as fairly natural KDL.
|
||||
|
||||
```kdl
|
||||
request "/api/cart" method="PUT" {
|
||||
body {
|
||||
items {
|
||||
- id=1234 amount=1
|
||||
- id=2341 amount=2 {
|
||||
options {
|
||||
color "red"
|
||||
size "XXL"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `body` node represents the JSON object
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{"id": 1234, "amount": 1},
|
||||
{"id": 2341, "amount": 2, "options": {"color": "red", "size": "XXL"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
LIBDIR := lib
|
||||
include $(LIBDIR)/main.mk
|
||||
|
||||
$(LIBDIR)/main.mk:
|
||||
ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null))
|
||||
git submodule sync
|
||||
git submodule update --init
|
||||
else
|
||||
ifneq (,$(wildcard $(ID_TEMPLATE_HOME)))
|
||||
ln -s "$(ID_TEMPLATE_HOME)" $(LIBDIR)
|
||||
else
|
||||
git clone -q --depth 10 -b main \
|
||||
https://github.com/martinthomson/i-d-template $(LIBDIR)
|
||||
endif
|
||||
endif
|
||||
109
QUERY-SPEC.md
109
QUERY-SPEC.md
|
|
@ -5,35 +5,40 @@ documents to extract nodes and even specific data. It is loosely based on CSS
|
|||
selectors for familiarity and ease of use. Think of it as CSS Selectors or
|
||||
XPath, but for KDL!
|
||||
|
||||
This document describes KQL `1.0.0`. It was released on September 11, 2021.
|
||||
This document describes KQL `next`. It is unreleased.
|
||||
|
||||
## Selectors
|
||||
|
||||
Selectors use selection operators to filter nodes that will be returned by an
|
||||
API using KQL. The main differences between this and CSS selectors are the
|
||||
lack of `*` (use `[]` instead), and the specific syntax for
|
||||
lack of `*` (use `[]` instead), the specific syntax for descendants and siblings, and the specific syntax for
|
||||
[matchers](#matchers) (the stuff between `[` and `]`), which is similar, but not identical to CSS.
|
||||
|
||||
* `a > b`: Selects any `b` element that is a direct child of an `a` element.
|
||||
* `a b`: Selects any `b` element that is a _descendant_ of an `a` element.
|
||||
* `a b || a c`: Selects all `b` and `c` elements that are descendants of an `a` element. Any selector may be on either side of the `||`. Multiple `||` are supported.
|
||||
* `a >> b`: Selects any `b` element that is a _descendant_ of an `a` element.
|
||||
* `a >> b || a >> c`: Selects all `b` and `c` elements that are descendants of an `a` element. Any selector may be on either side of the `||`. Multiple `||` are supported.
|
||||
* `a + b`: Selects any `b` element that is placed immediately after a sibling `a` element.
|
||||
* `a ~ b`: Selects any `b` element that follows an `a` element as a sibling, either immediately or later.
|
||||
* `a ++ b`: Selects any `b` element that follows an `a` element as a sibling, either immediately or later.
|
||||
* `[accessor()]`: Selects any element, filtered by [an accessor](#accessors). (`accessor()` is a placeholder, not an actual accessor)
|
||||
* `a[accessor()]`: Selects any `a` element, filtered by an accessor.
|
||||
* `[]`: Selects any element (a )
|
||||
* `[]`: Selects any element.
|
||||
|
||||
## Matchers
|
||||
|
||||
Matchers are used to filter nodes by their various attributes (such as values,
|
||||
properties, node names, etc). With the exception of `top()` and `()`, they are all
|
||||
used inside a `[ ]` selector. Some matchers are unary, but most of them involve
|
||||
used inside a `[]` selector. Some matchers are unary, but most of them involve
|
||||
binary operators.
|
||||
|
||||
The `top()` matcher can only be used as the first matcher of a selector. This means
|
||||
that it cannot be the right operand of the `>`, `>>`, `+`, or `++` operators. As `||`
|
||||
combines selectors, the `top()` can appear just after it. For instance,
|
||||
`a > b || top() > b` is valid, but `a > top()` is not.
|
||||
|
||||
* `top()`: Returns all toplevel children of the current document.
|
||||
* `top() > []`: Equivalent to `top()` on its own.
|
||||
* `(foo)`: Selects any element with a tag named `foo`.
|
||||
* `()`: Selects any element with any tag.
|
||||
* `(foo)`: Selects any element whose type annotation is `foo`.
|
||||
* `()`: Selects any element with any type annotation.
|
||||
* `[val()]`: Selects any element with a value.
|
||||
* `[val(1)]`: Selects any element with a second value.
|
||||
* `[prop(foo)]`: Selects any element with a property named `foo`.
|
||||
|
|
@ -44,8 +49,8 @@ Attribute matchers support certain binary operators:
|
|||
* `[val() = 1]`: Selects any element whose first value is 1.
|
||||
* `[prop(name) = 1]`: Selects any element with a property `name` whose value is 1.
|
||||
* `[name = 1]`: Equivalent to the above.
|
||||
* `[name() = "hi"]`: Selects any element whose _node name_ is "hi". Equivalent to just `hi`, but more useful when using string operators.
|
||||
* `[tag() = "hi"]`: Selects any element whose tag is "hi". Equivalent to just `(hi)`, but more useful when using string operators.
|
||||
* `[name() = hi]`: Selects any element whose _node name_ is "hi". Equivalent to just `hi`, but more useful when using string operators.
|
||||
* `[tag() = hi]`: Selects any element whose tag is "hi". Equivalent to just `(hi)`, but more useful when using string operators.
|
||||
* `[val() != 1]`: Selects any element whose first value exists, and is not 1.
|
||||
|
||||
The following operators work with any `val()` or `prop()` values.
|
||||
|
|
@ -60,41 +65,14 @@ never coerced to 1, and there is no "universal" ordering across all types.):
|
|||
The following operators work only with string `val()`, `prop()`, `tag()`, or `name()` values.
|
||||
If the value is not a string, the matcher will always fail:
|
||||
|
||||
* `[val() ^= "foo"]`: Selects any element whose first value starts with "foo".
|
||||
* `[val() $= "foo"]`: Selects any element whose first value ends with "foo".
|
||||
* `[val() *= "foo"]`: Selects any element whose first value contains "foo".
|
||||
* `[val() ^= foo]`: Selects any element whose first value starts with "foo".
|
||||
* `[val() $= foo]`: Selects any element whose first value ends with "foo".
|
||||
* `[val() *= foo]`: Selects any element whose first value contains "foo".
|
||||
|
||||
The following operators work only with `val()` or `prop()` values. If the value
|
||||
is not one of those, the matcher will always fail:
|
||||
|
||||
* `[val() = (foo)]`: Selects any element whose tag is "foo".
|
||||
|
||||
## Map Operator
|
||||
|
||||
KQL implementations MAY support a "map operator", `=>`, that allows selection
|
||||
of specific parts of the selected notes, essentially "mapping" over a
|
||||
selector's result set.
|
||||
|
||||
Only a single map operator may be used, and it must be the last element in a
|
||||
selector string.
|
||||
|
||||
The map operator's right hand side is either an [`accessor`](#accessors) on
|
||||
its own, or a tuple of accessors, denoted by a comma-separated list wrapped in
|
||||
`()` (for example, `(a, b, c)`).
|
||||
|
||||
## Accessors
|
||||
|
||||
Accessors access/extract specific parts of a node. They are used with the [map
|
||||
operator](#map-operator), and have syntactic overlap with some
|
||||
[matchers](#matchers).
|
||||
|
||||
* `name()`: Returns the name of the node itself.
|
||||
* `val(2)`: Returns the third value in a node.
|
||||
* `val()`: Equivalent to `val(0)`.
|
||||
* `prop(foo)`: Returns the value of the property `foo` in the node.
|
||||
* `foo`: Equivalent to `prop(foo)`.
|
||||
* `props()`: Returns all properties of the node as an object.
|
||||
* `values()`: Returns all values of the node as an array.
|
||||
* `[val() = (foo)]`: Selects any element whose type annotation is `foo`.
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
@ -102,22 +80,22 @@ Given this document:
|
|||
|
||||
```kdl
|
||||
package {
|
||||
name "foo"
|
||||
name foo
|
||||
version "1.0.0"
|
||||
dependencies platform="windows" {
|
||||
dependencies platform=windows {
|
||||
winapi "1.0.0" path="./crates/my-winapi-fork"
|
||||
}
|
||||
dependencies {
|
||||
miette "2.0.0" dev=true
|
||||
miette "2.0.0" dev=#true integrity=(sri)sha512-deadbeef
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then the following queries are valid:
|
||||
|
||||
* `package name`
|
||||
* `package >> name`
|
||||
* -> fetches the `name` node itself
|
||||
* `top() > package name`
|
||||
* `top() > package >> name`
|
||||
* -> fetches the `name` node, guaranteeing that `package` is in the document root.
|
||||
* `dependencies`
|
||||
* -> deep-fetches both `dependencies` nodes
|
||||
|
|
@ -125,18 +103,29 @@ Then the following queries are valid:
|
|||
* -> fetches any dependencies nodes with a `platform` prop (just the one, in this case)
|
||||
* `dependencies[prop(platform)]`
|
||||
* -> Identical to the above. Plain identifiers are equivalent to `prop(<identifier>)`.
|
||||
* `dependencies > []` ->
|
||||
* fetches all direct-child nodes of any `dependencies` nodes in the
|
||||
document. In this case, it will fetch both `miette` and `winapi` nodes.
|
||||
* `dependencies > []`
|
||||
* -> fetches all direct-child nodes of any `dependencies` nodes in the
|
||||
document. In this case, it will fetch both `miette` and `winapi` nodes.
|
||||
|
||||
If using an API that supports the [map operator](#map-operator), the following
|
||||
are valid queries:
|
||||
## Full Grammar
|
||||
|
||||
* `package name => val()`
|
||||
* -> `["foo"]`.
|
||||
* `dependencies[platform] => platform`
|
||||
* -> `["windows"]`
|
||||
* `dependencies > [] => (name(), val(), path)`
|
||||
* -> `[("winapi", "1.0.0", "./crates/my-winapi-fork"), ("miette", "2.0.0", None)]`
|
||||
* `dependencies > [] => (name(), values(), props())`
|
||||
* -> `[("winapi", ["1.0.0"], {"platform": "windows"}), ("miette", ["2.0.0"], {"dev": true})]`
|
||||
Rules that are not defined in this grammar are prefixed with `$`, see [the KDL
|
||||
grammar](https://kdl.dev/spec/#name-full-grammar) for
|
||||
what they expand to.
|
||||
|
||||
```
|
||||
query-str := $bom? query
|
||||
query := selector q-ws+ "||" q-ws+ query | selector
|
||||
selector := filter q-ws+ selector-operator q-ws+ selector-subsequent | filter
|
||||
selector-subsequent := matchers q-ws+ selector-operator q-ws+ selector-subsequent | matchers
|
||||
selector-operator := ">>" | ">" | "++" | "+"
|
||||
filter := "top(" q-ws* ")" | matchers
|
||||
matchers := type-matcher $string? accessor-matcher* | $string accessor-matcher* | accessor-matcher+
|
||||
type-matcher := "(" q-ws* ")" | $type
|
||||
accessor-matcher := "[" q-ws* (comparison | accessor)? q-ws* "]"
|
||||
comparison := accessor q-ws+ matcher-operator q-ws+ ($type | $string | $number | $keyword)
|
||||
accessor := "val(" q-ws* $integer q-ws* ")" | "prop(" q-ws* $string q-ws* ")" | "name(" q-ws* ")" | "tag(" q-ws* ")" | "values(" q-ws* ")" | "props(" q-ws* ")" | $string
|
||||
matcher-operator := "=" | "!=" | ">" | "<" | ">=" | "<=" | "^=" | "$=" | "*="
|
||||
|
||||
q-ws := $node-space
|
||||
```
|
||||
|
|
|
|||
330
README.md
330
README.md
|
|
@ -1,38 +1,140 @@
|
|||
# The KDL Document Language
|
||||
|
||||
KDL is a document language with xml-like semantics that looks like you're
|
||||
invoking a bunch of CLI commands! It's meant to be used both as a
|
||||
serialization format and a configuration language, much like JSON, YAML, or
|
||||
XML.
|
||||
KDL is a small, pleasant document language with XML-like node semantics that
|
||||
looks like you're invoking a bunch of CLI commands! It's meant to be used both
|
||||
as a serialization format and a configuration language, much like JSON, YAML, or
|
||||
XML. It looks like this:
|
||||
|
||||
There's a living [specification](SPEC.md), as well as various
|
||||
```kdl
|
||||
package {
|
||||
name my-pkg
|
||||
version "1.2.3"
|
||||
|
||||
dependencies {
|
||||
// Nodes can have standalone values as well as
|
||||
// key/value pairs.
|
||||
lodash "^3.2.1" optional=#true alias=underscore
|
||||
}
|
||||
|
||||
scripts {
|
||||
// "Raw" and dedented multi-line strings are supported.
|
||||
message """
|
||||
hello
|
||||
world
|
||||
"""
|
||||
build #"""
|
||||
echo "foo"
|
||||
node -c "console.log('hello, world!');"
|
||||
echo "foo" > some-file.txt
|
||||
"""#
|
||||
}
|
||||
|
||||
// `\` breaks up a single node across multiple lines.
|
||||
the-matrix 1 2 3 \
|
||||
4 5 6 \
|
||||
7 8 9
|
||||
|
||||
// "Slashdash" comments operate at the node level,
|
||||
// with just `/-`.
|
||||
/-this-is-commented {
|
||||
this entire node {
|
||||
is gone
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more details, see the [overview below](#overview).
|
||||
|
||||
There's a living [specification](https://kdl.dev/spec/), as well as various
|
||||
[implementations](#implementations). You can also check out the [FAQ](#faq) to
|
||||
answer all your burning questions!
|
||||
|
||||
In addition to a spec for KDL itself, there are also standard specs for [a KDL
|
||||
Query Language](QUERY-SPEC.md) based on CSS selectors, and [a KDL Schema
|
||||
The current version of the KDL spec is
|
||||
[KDL 2.0.0](https://kdl-org.github.io/kdl/#go.draft-marchan-kdl2.html). For legacy KDL,
|
||||
please refer to the [KDL 1.0.0
|
||||
spec](https://github.com/kdl-org/kdl/blob/2.0.0/SPEC_v1.md). All users are
|
||||
encouraged to migrate. [Migration is forward-and-backward-compatible and
|
||||
safe](https://kdl-org.github.io/kdl/#go.draft-marchan-kdl2.html#compatibility), and can
|
||||
be automated.
|
||||
|
||||
In addition to a spec for KDL itself, there are specifications for [a KDL Query
|
||||
Language](QUERY-SPEC.md) based on CSS selectors, and [a KDL Schema
|
||||
Language](SCHEMA-SPEC.md) loosely based on JSON Schema.
|
||||
|
||||
The language is based on [SDLang](https://sdlang.org), with a number of
|
||||
modifications and clarifications on its syntax and behavior.
|
||||
The language is based on [SDLang](https://sdlang.org), with a [number of
|
||||
modifications and clarifications on its syntax and behavior](#why-not-sdlang).
|
||||
We are grateful for their work as an inspiration to ours.
|
||||
|
||||
The current version of the KDL spec is `1.0.0`.
|
||||
[Play with it in your browser!](https://kdl.dev/play/)
|
||||
|
||||
## Design and Discussion
|
||||
|
||||
KDL is still extremely new, and discussion about the format should happen over
|
||||
on the [discussions page](https://github.com/kdl-org/kdl/discussions). Feel
|
||||
free to jump in and give us your 2 cents!
|
||||
KDL 2.0.0 has been finalized, and no further changes are expected. For questions
|
||||
about KDL and discussions, please see the [discussions
|
||||
page](https://github.com/kdl-org/kdl/discussions). For minor editorial fixes or
|
||||
critical spec errata, please feel free to [file an
|
||||
issue](https://github.com/kdl-org/kdl/issues).
|
||||
|
||||
## Used By
|
||||
|
||||
A lot of folks have started picking up KDL for both personal projects, and
|
||||
larger open source, and even proprietary projects! This section includes a list
|
||||
of some examples of KDL in the wild (either v1, v2, or both):
|
||||
|
||||
* [Zellij](https://zellij.dev) - Terminal workspace/multiplexer
|
||||
* [Niri](https://github.com/YaLTeR/niri) - Scrollable-tiling window manager for Wayland
|
||||
* [Bikeshed](https://github.com/speced/bikeshed) ([here](https://github.com/speced/bikeshed-boilerplate/blob/main/boilerplate/doctypes.kdl) and [here](https://github.com/speced/bikeshed-data/blob/main/data/manifest.txt)) - Specification pre-processor used by CSS, C++, WHATWG, various W3C working groups, and others.
|
||||
* [orogene](https://orogene.dev) - Lightning-fast JavaScript package manager
|
||||
* [Onyx](https://onyxlang.io/) - An efficient, procedural, and pragmatic programming language that compiles to WASM. Used for package manifests.
|
||||
* [Pop!_OS/System76 Scheduler](https://github.com/pop-os/system76-scheduler) - Scheduling service which optimizes Linux's CPU scheduler and makes it go faster.
|
||||
* [ImStyle](https://patitotective.github.io/ImStyle/) - ImGui application styling with Nim and KDL
|
||||
* [fmod-rs](https://github.com/CAD97/fmod-rs) - Rust bindings to FMOD Core and FMOD Studio
|
||||
* [mise](https://mise.jdx.dev/) - dev tools, env vars, task runner
|
||||
* [Camping](https://github.com/camping/camping) - Ruby web microframework
|
||||
* [Iron Vault](https://ironvault.quest) - VTT (Virtual Tabletop) plugin for Obsidian for the Ironsworn family of games
|
||||
* [Microsoft TypeScript DOM Generator](https://github.com/microsoft/TypeScript-DOM-lib-generator) - Tool for generating DOM-related TypeScript and JavaScript library files
|
||||
* [Ferron](https://ferron.sh/) - A fast, memory-safe web server written in Rust
|
||||
* You?
|
||||
|
||||
## Implementations
|
||||
|
||||
* Rust: [kdl-rs](https://github.com/kdl-org/kdl-rs)
|
||||
* JavaScript: [kdljs](https://github.com/kdl-org/kdljs)
|
||||
* Ruby: [kdl-rb](https://github.com/danini-the-panini/kdl-rb)
|
||||
* Dart: [kdl-dart](https://github.com/danini-the-panini/kdl-dart)
|
||||
* Java: [kdl4j](https://github.com/hkolbeck/kdl4j)
|
||||
* PHP: [kdl-php](https://github.com/kdl-org/kdl-php)
|
||||
* Python: [kdl-py](https://github.com/daeken/kdl-py)
|
||||
> [!INFO] There are two major versions of KDL. Different libraries may support one or the
|
||||
> other, or even provide a "hybrid" mode where both versions are attempted, since
|
||||
> there's no data ambiguity between v1 and v2 documents.
|
||||
|
||||
| Language | Implementation | v1 | v2 | Notes |
|
||||
|---|---|---|---|---|
|
||||
| C | [ckdl](https://github.com/tjol/ckdl) | ✅ | ✅ | |
|
||||
| C#/.NET | [Kadlet](https://github.com/oledfish/Kadlet) | ✅ | ✖️ | |
|
||||
| C#/.NET | [KadSharp](https://github.com/AndreyAkinshin/KdlSharp) | ✅ | ✅ | .NET Std: 2.1+, .NET 6+, .NET FW 4.7.2+, Mono, Xamarin |
|
||||
| C++ | [kdlpp](https://github.com/tjol/ckdl) | ✅ | ✅ | part of ckdl, requires C++20 |
|
||||
| Common Lisp | [kdlcl](https://github.com/chee/kdlcl) | ✅ | ✖️ | |
|
||||
| Crystal | [kdl-cr](https://github.com/danini-the-panini/kdl-cr) | ✅ | ✖️ | |
|
||||
| Dart | [kdl-dart](https://github.com/danini-the-panini/kdl-dart) | ✅ | ✅ | |
|
||||
| Elixir | [kuddle](https://github.com/IceDragon200/kuddle) | ✅ | ✅ | |
|
||||
| Go | [gokdl](https://github.com/lunjon/gokdl) | ✅ | ✖️ | |
|
||||
| Go | [kdl-go](https://github.com/sblinch/kdl-go) | ✅ | ✖️ | |
|
||||
| Go | [gokdl2](https://github.com/njreid/gokdl2) | ✅ | ✅ | Friendly errors & arena allocator |
|
||||
| Haskell | [Hustle](https://github.com/fuzzypixelz/Hustle) | ✅ | ✖️ | |
|
||||
| Haskell | [kdl-hs](https://github.com/brandonchinn178/kdl-hs) | ✅ | ✅ | Format/comment-preserving parser |
|
||||
| Java | [kdl4j](https://github.com/kdl-org/kdl4j) | ✅ | ✅ | |
|
||||
| JavaScript | [@bgotink/kdl](https://github.com/bgotink/kdl) | ✅ | ✅ | Format/comment-preserving parser |
|
||||
| JavaScript | [@virtualstate/kdl](https://github.com/virtualstate/kdl) | ✅ | ✖️ | query only, JSX based |
|
||||
| JavaScript | [kdljs](https://github.com/kdl-org/kdljs) | ✅ | ✅ | |
|
||||
| Lua | [kdlua](https://github.com/danini-the-panini/kdlua) | ✅ | ✖️ | |
|
||||
| Nim | [kdl-nim](https://github.com/Patitotective/kdl-nim) | ✅ | ✖️ | |
|
||||
| OCaml | [ocaml-kdl](https://github.com/eilvelia/ocaml-kdl) | ✅ | ✅ | |
|
||||
| PHP | [kdl-php](https://github.com/kdl-org/kdl-php) | ✅ | ✖️ | |
|
||||
| Python | [ckdl](https://github.com/tjol/ckdl) | ✅ | ✅ | |
|
||||
| Python | [cuddle](https://github.com/djmattyg007/python-cuddle) | ✅ | ✖️ | |
|
||||
| Python | [kdl-py](https://github.com/tabatkins/kdlpy) | ✅ | ✅ | |
|
||||
| Ruby | [kdl-rb](https://github.com/danini-the-panini/kdl-rb) | ✅ | ✅ | |
|
||||
| Rust | [kdl-rs](https://github.com/kdl-org/kdl-rs) | ✅ | ✅ | Format/comment-preserving parser |
|
||||
| Rust | [knus](https://crates.io/crates/knus/) | ✅ | ✖️ | Serde-_style_ derive macros (not actual Serde) |
|
||||
| Swift | [kdl-swift](https://github.com/danini-the-panini/kdl-swift) | ✅ | ✖️ | |
|
||||
| XSLT | [xml2kdl](https://github.com/Devasta/XML2KDL) | ✅ | ✖️ | |
|
||||
| Zig | [zig-kdl](https://codeberg.org/desttinghim/zig-kdl) | ✅ | ✅ | Format/comment-preserving parser |
|
||||
|
||||
## Compatibility Test Suite
|
||||
|
||||
|
|
@ -44,13 +146,21 @@ entirety, but in the future, may be required to in order to be included here.
|
|||
|
||||
## Editor Support
|
||||
|
||||
* [VS Code](https://marketplace.visualstudio.com/items?itemName=kdl-org.kdl&ssr=false#review-details)
|
||||
* [Intellij IDEA](https://plugins.jetbrains.com/plugin/20136-kdl-document-language)
|
||||
* [Sublime Text](https://packagecontrol.io/packages/KDL)\*
|
||||
* [TreeSitter](https://github.com/tree-sitter-grammars/tree-sitter-kdl) (neovim, among others)
|
||||
* [VS Code](https://marketplace.visualstudio.com/items?itemName=kdl-org.kdl&ssr=false#review-details)\*
|
||||
* [vim](https://github.com/imsnif/kdl.vim)
|
||||
* [Kate](https://github.com/larsgw/katepart-kdl)\*
|
||||
* [Zed](https://zed.dev/extensions/kdl)
|
||||
|
||||
\* Supports KDL 2.0.0
|
||||
|
||||
## Overview
|
||||
|
||||
### Basics
|
||||
|
||||
A KDL node is a node name, followed by zero or more "arguments", and
|
||||
A KDL node is a node name string, followed by zero or more "arguments", and
|
||||
children.
|
||||
|
||||
```kdl
|
||||
|
|
@ -63,10 +173,10 @@ You can also have multiple values in a single node!
|
|||
bookmarks 12 15 188 1234
|
||||
```
|
||||
|
||||
Nodes can have properties.
|
||||
Nodes can have properties, with string keys.
|
||||
|
||||
```kdl
|
||||
author "Alex Monad" email="alex@example.com" active=true
|
||||
author "Alex Monad" email=alex@example.com active=#true
|
||||
```
|
||||
|
||||
And they can have nested child nodes, too!
|
||||
|
|
@ -84,46 +194,77 @@ Nodes without children are terminated by a newline, a semicolon, or the end of
|
|||
a file stream:
|
||||
|
||||
```kdl
|
||||
node1; node2; node3;
|
||||
node1; node2; node3
|
||||
```
|
||||
|
||||
### Values
|
||||
|
||||
KDL supports 4 data types:
|
||||
|
||||
* Strings: `"hello world"`
|
||||
* Numbers: `123.45`
|
||||
* Booleans: `true` and `false`
|
||||
* Null: `null`
|
||||
* Strings: `unquoted`, `"hello world"`, or `#"hello world"#`
|
||||
* Numbers: `123.45`, `0xdeadbeef`, `#inf`, `#-inf`, `#nan`
|
||||
* Booleans: `#true` and `#false`
|
||||
* Null: `#null`
|
||||
|
||||
#### Strings
|
||||
It supports two different formats for string input: escaped and raw.
|
||||
|
||||
It supports three different formats for string input: unquoted, quoted, and raw.
|
||||
|
||||
```kdl
|
||||
node "this\nhas\tescapes"
|
||||
other r"C:\Users\zkat\"
|
||||
```
|
||||
Both types of string can be multiline as-is, without a different syntax:
|
||||
|
||||
```kdl
|
||||
string "my
|
||||
multiline
|
||||
value"
|
||||
node1 this-is-a-string
|
||||
node2 "this\nhas\tescapes"
|
||||
node3 #"C:\Users\zkat\raw\string"#
|
||||
```
|
||||
|
||||
And for raw strings, you can add any number of # after the r and the last " to
|
||||
disambiguate literal " characters:
|
||||
You don't have to quote strings unless any the following apply:
|
||||
* The string contains whitespace.
|
||||
* The string contains any of `[]{}()\/#";=`.
|
||||
* The string is one of `true`, `false`, `null`, `inf`, `-inf`, or `nan`.
|
||||
* The strings starts with a digit, or `+`/`-`/`.`/`-.`,`+.` and a digit.
|
||||
(aka "looks like a number")
|
||||
|
||||
In essence, if it can get confused for other KDL or KQL syntax, it needs
|
||||
quotes.
|
||||
|
||||
Both types of quoted string can be written across multiple lines by using triple
|
||||
quotes (`"""`) followed immediately by a newline. Additionally, common
|
||||
indentation shared with the line containing the closing quotes will be
|
||||
stripped/dedented:
|
||||
|
||||
```kdl
|
||||
other-raw r#"hello"world"#
|
||||
string """
|
||||
my
|
||||
multiline
|
||||
value
|
||||
"""
|
||||
```
|
||||
|
||||
Raw strings, which do not support `\` escapes and can be used when you want
|
||||
certain kinds of strings to look nicer without having to escape a lot:
|
||||
|
||||
```kdl
|
||||
exec #"""
|
||||
echo "foo"
|
||||
echo "bar"
|
||||
cd C:\path\to\dir
|
||||
"""#
|
||||
|
||||
regex #"\d{3} "[^/"]+""#
|
||||
```
|
||||
|
||||
You can add any number of `#`s before and after the opening and
|
||||
closing `#` to disambiguate literal closing `#"` sequences:
|
||||
|
||||
```kdl
|
||||
other-raw ##"hello#"world"##
|
||||
```
|
||||
|
||||
#### Numbers
|
||||
|
||||
There's 4 ways to represent numbers in KDL. KDL does not prescribe any
|
||||
representation for these numbers, and it's entirely up to individual
|
||||
implementations whether to represent all numbers with a single type, or to
|
||||
have different representations for different forms.
|
||||
There are 4 ways to represent numbers in KDL, plus 3 float keywords. KDL does
|
||||
not prescribe any representation for these numbers, and it's entirely up to
|
||||
individual implementations whether to represent all numbers with a single type,
|
||||
or to have different representations for different forms.
|
||||
|
||||
KDL has regular decimal-radix numbers, with optional decimal part, as well as
|
||||
an optional exponent.
|
||||
|
|
@ -141,6 +282,13 @@ my-octal 0o755
|
|||
my-binary 0b10101101
|
||||
```
|
||||
|
||||
If you're intending to represent IEEE 754 floats, there are three special
|
||||
keywords you can use:
|
||||
|
||||
```kdl
|
||||
special-floats #inf #-inf #nan
|
||||
```
|
||||
|
||||
Finally, all numbers can have underscores to help readability:
|
||||
|
||||
```kdl
|
||||
|
|
@ -159,7 +307,7 @@ comments can be nested.
|
|||
C style multiline
|
||||
*/
|
||||
|
||||
tag /*foo=true*/ bar=false
|
||||
tag /*foo=#true*/ bar=#false
|
||||
|
||||
/*/*
|
||||
hello
|
||||
|
|
@ -167,20 +315,22 @@ hello
|
|||
```
|
||||
|
||||
On top of that, KDL supports `/-` "slashdash" comments, which can be used to
|
||||
comment out individual nodes, arguments, or children:
|
||||
comment out individual nodes, entries, or child blocks:
|
||||
|
||||
```kdl
|
||||
// This entire node and its children are all commented out.
|
||||
/-mynode "foo" key=1 {
|
||||
/-mynode foo key=1 {
|
||||
a
|
||||
b
|
||||
c
|
||||
}
|
||||
|
||||
mynode /-"commented" "not commented" /-key="value" /-{
|
||||
mynode /-commented "not commented" /-key=value /-{
|
||||
a
|
||||
b
|
||||
}
|
||||
// The above is equivalent to:
|
||||
mynode "not commented"
|
||||
```
|
||||
|
||||
### Type Annotations
|
||||
|
|
@ -192,8 +342,8 @@ specific meanings.
|
|||
|
||||
```kdl
|
||||
numbers (u8)10 (i32)20 myfloat=(f32)1.5 {
|
||||
strings (uuid)"123e4567-e89b-12d3-a456-426614174000" (date)"2021-02-03" filter=(regex)r"$\d+"
|
||||
(author)person name="Alex"
|
||||
strings (uuid)"123e4567-e89b-12d3-a456-426614174000" (date)"2021-02-03" filter=(regex)#"$\d+"#
|
||||
(author)person name=Alex
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -206,28 +356,28 @@ title \
|
|||
|
||||
|
||||
// Files must be utf8 encoded!
|
||||
smile "😁"
|
||||
smile 😁
|
||||
|
||||
// Instead of anonymous nodes, nodes and properties can be wrapped
|
||||
// in "" for arbitrary node names.
|
||||
"!@#$@$%Q#$%~@!40" "1.2.3" "!!!!!"=true
|
||||
// Node names and property keys are just strings, so you can write them like
|
||||
// quoted or raw strings, too!
|
||||
"illegal(){}[]/\\=#;identifier" #"1.2.3"# "#false"=#true
|
||||
|
||||
// The following is a legal bare identifier:
|
||||
foo123~!@#$%^&*.:'|/?+ "weeee"
|
||||
// Identifiers are very flexible. The following is a legal bare identifier:
|
||||
-<123~!$@%^&*,.:'`|?+>
|
||||
|
||||
// And you can also use unicode!
|
||||
ノード お名前="☜(゚ヮ゚☜)"
|
||||
// And you can also use non-ASCII unicode!
|
||||
ノード お名前=ฅ^•ﻌ•^ฅ
|
||||
|
||||
// kdl specifically allows properties and values to be
|
||||
// interspersed with each other, much like CLI commands.
|
||||
foo bar=true "baz" quux=false 1 2 3
|
||||
foo bar=#true baz quux=#false 1 2 3
|
||||
```
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. Maintainability
|
||||
1. Human Maintainability
|
||||
1. Flexibility
|
||||
1. Cognitive simplicity and Learnability
|
||||
1. Cognitive Simplicity and Learnability
|
||||
1. Ease of de/serialization
|
||||
1. Ease of implementation
|
||||
|
||||
|
|
@ -252,28 +402,43 @@ Same as "cuddle".
|
|||
Because nothing out there felt quite right. The closest one I found was
|
||||
SDLang, but that had some design choices I disagreed with.
|
||||
|
||||
<a name="why-not-sdlang"></a>
|
||||
#### Ok, then, why not SDLang?
|
||||
|
||||
SDLang is designed for use cases that are not interesting to me, but are very
|
||||
relevant to the D-lang community. KDL is very similar in many ways, but is
|
||||
different in the following ways:
|
||||
SDLang is an excellent base, but I wanted some details ironed out, and some
|
||||
things removed that only really made sense for SDLang's current use-cases, including
|
||||
some restrictions about data representation. KDL is very similar in many ways, except:
|
||||
|
||||
* The grammar and expected semantics are [well-defined and specified](SPEC.md).
|
||||
* There is only one "number" type. KDL does not prescribe representations.
|
||||
* The grammar and expected semantics are [well-defined and specified](https://kdl-org.github.io/kdl/#go.draft-marchan-kdl2.html).
|
||||
This was the original impetus for working on KDL, followed by details that
|
||||
seemed like they could be improved.
|
||||
* There is only one "number" type. KDL does not prescribe representations, but
|
||||
does have keywords for NaN, infinity, and negative infinity if decimal numbers
|
||||
are intended to be represented as IEEE754 floats.
|
||||
* Slashdash (`/-`) comments are great and useful!
|
||||
* I am not interested in having first-class date types, and SDLang's are very
|
||||
non-standard.
|
||||
* Quoteless "identifier" strings (e.g. `node foo=bar`, vs `node foo="bar"`).
|
||||
* KDL does not have first-class date or binary data types. Instead, it
|
||||
supports arbitrary type annotations for any custom data type you might need:
|
||||
`(date)"2021-02-03"`, `(binary)"deadbeefbadc0ffee"`.
|
||||
* Values and properties can be interspersed with each other, rather than one
|
||||
having to follow the other.
|
||||
* KDL does not have a first-class binary data type. Just use strings with base64.
|
||||
* All strings in KDL are multi-line, and raw strings are written with
|
||||
Rust-style syntax (`r"foo"`), instead of backticks.
|
||||
* KDL identifiers can use UTF-8 and are much more lax about symbols than SDLang.
|
||||
* KDL does not support "anonymous" nodes.
|
||||
* Instead, KDL supports arbitrary identifiers for node names and attribute
|
||||
having to follow the other. It was not clear whether this was actually allowed in SDLang.
|
||||
* Multi-line strings are supported using `"""<newline>` and their lines are automatically
|
||||
"dedented" to match their closing quotes' indentation level.
|
||||
* Raw strings are written with `#` (`#"foo\bar"#`), instead of backticks. This,
|
||||
while more verbose, allows embedding of languages, especially scripting
|
||||
languages, that use this syntax on a regular basis, without additional escaping
|
||||
(e.g. bash and JavaScript).
|
||||
* KDL identifiers can use a wide range of UTF-8 and are much more lax about
|
||||
valid characters than SDLang.
|
||||
* KDL does not support "anonymous" nodes. Instead, any string can be used as a
|
||||
node name. For lists of arbitrary values, there is a convention of naming the nodes
|
||||
simply `-`.
|
||||
* Namespaces are not supported, but `:` is a legal identifier character, and applications
|
||||
can choose to implement namespaces as they see fit.
|
||||
* KDL supports arbitrary identifiers for node names and attribute
|
||||
names, meaning you can use arbitrary strings for those: `"123" "value"=1` is
|
||||
a valid node, for example. This makes it easier to use KDL for
|
||||
representing arbitrary key/value pairs.
|
||||
representing arbitrary key/value pairs using child nodes.
|
||||
|
||||
#### Have you seen that one XKCD comic about standards?
|
||||
|
||||
|
|
@ -311,7 +476,10 @@ microsyntax for losslessly encoding JSON](JSON-IN-KDL.md).
|
|||
|
||||
#### What about TOML?
|
||||
|
||||
It nests very poorly. It doesn't fare well with large files.
|
||||
It nests very poorly. It doesn't fare well with large files. Also, I felt some
|
||||
discomfort [continuing to use and promote something by its
|
||||
creator](https://en.wikipedia.org/wiki/Tom_Preston-Werner#Resignation_from_GitHub).
|
||||
|
||||
|
||||
#### What about XML?
|
||||
|
||||
|
|
@ -347,3 +515,7 @@ microsyntax for losslessly encoding XML](XML-IN-KDL.md).
|
|||
This license applies to the text and assets _in this repository_.
|
||||
Implementations of this specification are not "derivative works", and thus are
|
||||
not bound by the restrictions of CC-BY-SA.
|
||||
|
||||
The KDL logo design and files were generously contributed by Timothy Merritt
|
||||
([@timmybytes](https://github.com/timmybytes)), and are also available under
|
||||
the same license.
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ None.
|
|||
* [`node`](#node-node) - zero or more toplevel nodes for the KDL document this schema describes.
|
||||
* [`definitions`](#definitions-node) (optional): Definitions of nodes, values, props, and children block to reference in the toplevel nodes.
|
||||
* `node-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of child nodes.
|
||||
* `other-nodes-allowed` (optional): Whether to allow nodes other than the ones explicitly listed here. Defaults to `false`.
|
||||
* `other-nodes-allowed` (optional): Whether to allow nodes other than the ones explicitly listed here. Defaults to `#false`.
|
||||
* [`tag`](#tag-node) - zero or more toplevel tags for nodes in the KDL document that this schema describes.
|
||||
* `tag-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of tags of child nodes.
|
||||
* `other-tags-allowed` (optional): Whether to allow node tags other than the ones explicitly listed here. Defaults to `false`.
|
||||
* `other-tags-allowed` (optional): Whether to allow node tags other than the ones explicitly listed here. Defaults to `#false`.
|
||||
|
||||
### `info` node
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ Links to the schema itself, and to sources about the schema.
|
|||
|
||||
#### Properties
|
||||
|
||||
* `rel`: what the link is for (`"self"` or `"documentation"`)
|
||||
* `rel`: what the link is for (`self` or `documentation`)
|
||||
* `lang` (optional): An IETF BCP 47 language tag
|
||||
|
||||
### `license` node
|
||||
|
|
@ -268,7 +268,7 @@ and property names when the `node-names` or `prop-names` options are activated.
|
|||
|
||||
* `tag`: [Validations](#validation-nodes) to apply to the tag of the value.
|
||||
* `type`: A string denoting the type of the property value.
|
||||
* `enum`: A specific list of allowed values for this property. May be heterogenous as long as it agrees with the `type`, if specified.
|
||||
* `enum`: A specific list of allowed values for this property. May be heterogeneous as long as it agrees with the `type`, if specified.
|
||||
|
||||
#### String validations
|
||||
|
||||
|
|
@ -287,7 +287,7 @@ and property names when the `node-names` or `prop-names` options are activated.
|
|||
* `country-subdivision`: ISO 3166-2 country subdivision code.
|
||||
* `email`: RFC5302 email address.
|
||||
* `idn-email`: RFC6531 internationalized email address.
|
||||
* `hostname`: RFC1132 internet hostname.
|
||||
* `hostname`: RFC1123 internet hostname.
|
||||
* `idn-hostname`: RFC5890 internationalized internet hostname.
|
||||
* `ipv4`: RFC2673 dotted-quad IPv4 address.
|
||||
* `ipv6`: RFC2373 IPv6 address.
|
||||
|
|
@ -313,10 +313,12 @@ and property names when the `node-names` or `prop-names` options are activated.
|
|||
* `i16`: 16-bit signed integer
|
||||
* `i32`: 32-bit signed integer
|
||||
* `i64`: 64-bit signed integer
|
||||
* `i128`: 128-bit signed integer
|
||||
* `u8`: 8-bit unsigned integer
|
||||
* `u16`: 16-bit unsigned integer
|
||||
* `u32`: 32-bit unsigned integer
|
||||
* `u64`: 64-bit unsigned integer
|
||||
* `u128`: 128-bit unsigned integer
|
||||
* `isize`: Platform-dependent signed integer
|
||||
* `usize`: Platform-dependent unsigned integer
|
||||
* `f32`: IEEE 754 single (32-bit) precision floating point number
|
||||
|
|
|
|||
477
SPEC.md
477
SPEC.md
|
|
@ -1,476 +1 @@
|
|||
# KDL Spec
|
||||
|
||||
This is the semi-formal specification for KDL, including the intended data
|
||||
model and the grammar.
|
||||
|
||||
This document describes KDL version `1.0.0`. It was released on September 11, 2021.
|
||||
|
||||
## Introduction
|
||||
|
||||
KDL is a node-oriented document language. Its niche and purpose overlaps with
|
||||
XML, and as do many of its semantics. You can use KDL both as a configuration
|
||||
language, and a data exchange or storage format, if you so choose.
|
||||
|
||||
The bulk of this document is dedicated to a long-form description of all
|
||||
[Components](#components) of a KDL document. There is also a much more terse
|
||||
[Grammar](#full-grammar) at the end of the document that covers most of the
|
||||
rules, with some semantic exceptions involving the data model.
|
||||
|
||||
KDL is designed to be easy to read _and_ easy to implement.
|
||||
|
||||
## Components
|
||||
|
||||
### Document
|
||||
|
||||
The toplevel concept of KDL is a Document. A Document is composed of zero or
|
||||
more [Nodes](#node), separated by newlines and whitespace, and eventually
|
||||
terminated by an EOF.
|
||||
|
||||
All KDL documents should be UTF-8 encoded and conform to the specifications in
|
||||
this document.
|
||||
|
||||
#### Example
|
||||
|
||||
The following is a document composed of two toplevel nodes:
|
||||
|
||||
```kdl
|
||||
foo {
|
||||
bar
|
||||
}
|
||||
baz
|
||||
```
|
||||
|
||||
### Node
|
||||
|
||||
Being a node-oriented language means that the real core component of any KDL
|
||||
document is the "node". Every node must have a name, which is either a legal
|
||||
[Identifier](#identifier), or a quoted [String](#string).
|
||||
|
||||
The name may be preceded by a [Type Annotation](#type-annotation) to further
|
||||
clarify its type, particularly in relation to its parent node. (For example,
|
||||
clarifying that a particular `date` child node is for the _publication_ date,
|
||||
rather than the last-modified date, with `(published)date`.)
|
||||
|
||||
Following the name are zero or more [Arguments](#argument) or
|
||||
[Properties](#property), separated by either [whitespace](#whitespace) or [a
|
||||
slash-escaped line continuation](#line-continuation). Arguments and Properties
|
||||
may be interspersed in any order, much like is common with positional
|
||||
arguments vs options in command line tools.
|
||||
|
||||
Arguments are ordered relative to each other and that order must be preserved
|
||||
in order to maintain the semantics.
|
||||
|
||||
By contrast, Property order _SHOULD NOT_ matter to implementations.
|
||||
[Children](#children-block) should be used if an order-sensitive key/value
|
||||
data structure must be represented in KDL.
|
||||
|
||||
Nodes _MAY_ be prefixed with `/-` to "comment out" the entire node, including
|
||||
its properties, arguments, and children, and make it act as plain whitespace,
|
||||
even if it spreads across multiple lines.
|
||||
|
||||
Finally, a node is terminated by either a [Newline](#newline), a [Children
|
||||
Block](#children-block), a semicolon (`;`) or the end of the file/stream (an
|
||||
`EOF`).
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
foo 1 key="val" 3 {
|
||||
bar
|
||||
(role)baz 1 2
|
||||
}
|
||||
```
|
||||
|
||||
### Identifier
|
||||
|
||||
A bare Identifier is composed of any Unicode codepoint other than [non-initial
|
||||
characters](#non-initial-characters), followed by any number of Unicode
|
||||
codepoints other than [non-identifier characters](#non-identifier-characters),
|
||||
so long as this doesn't produce something confusable for a [Number](#number),
|
||||
[Boolean](#boolean), or [Null](#null).
|
||||
|
||||
Identifiers are terminated by [Whitespace](#whitespace) or
|
||||
[Newlines](#newline).
|
||||
|
||||
### Non-initial characters
|
||||
|
||||
The following characters cannot be the first character in a bare
|
||||
[Identifier](#identifier):
|
||||
|
||||
* Any decimal digit (0-9)
|
||||
* Any [non-identifier characters](#non-identifier-characters)
|
||||
|
||||
### Non-identifier characters
|
||||
|
||||
The following characters cannot be used anywhere in a bare
|
||||
[Identifier](#identifier):
|
||||
|
||||
* Any codepoint with hexadecimal value `0x20` or below.
|
||||
* Any codepoint with hexadecimal value higher than `0x10FFFF`.
|
||||
* Any of "\\/<>{};[]()=,\""
|
||||
|
||||
### Line Continuation
|
||||
|
||||
Line continuations allow [Nodes](#node) to be spread across multiple lines.
|
||||
|
||||
A line continuation is one or more [whitespace](#whitespace) characters,
|
||||
followed by a `\` character. This character can then be followed by more
|
||||
[whitespace](#whitespace) and must be terminated by a [Newline](#newline)
|
||||
(including the Newline that is part of single-line comments).
|
||||
|
||||
Following a line continuation, processing of a Node can continue as usual.
|
||||
|
||||
#### Example
|
||||
```kdl
|
||||
my-node 1 2 \ // comments are ok after \
|
||||
3 4 // This is the actual end of the Node.
|
||||
```
|
||||
|
||||
### Property
|
||||
|
||||
A Property is a key/value pair attached to a [Node](#node). A Property is
|
||||
composed of an [Identifier](#identifier) or a [String](#string), followed
|
||||
immediately by a `=`, and then a [Value](#value).
|
||||
|
||||
Properties should be interpreted left-to-right, with rightmost properties with
|
||||
identical names overriding earlier properties. That is:
|
||||
|
||||
```kdl
|
||||
node a=1 a=2
|
||||
```
|
||||
|
||||
In this example, the node's `a` value must be `2`, not `1`.
|
||||
|
||||
No other guarantees about order should be expected by implementers.
|
||||
Deserialized representations may iterate over properties in any order and
|
||||
still be spec-compliant.
|
||||
|
||||
Properties _MAY_ be prefixed with `/-` to "comment out" the entire token and
|
||||
make it act as plain whitespace, even if it spreads across multiple lines.
|
||||
|
||||
### Argument
|
||||
|
||||
An Argument is a bare [Value](#value) attached to a [Node](#node), with no
|
||||
associated key. It shares the same space as [Properties](#properties).
|
||||
|
||||
A Node may have any number of Arguments, which should be evaluated left to
|
||||
right. KDL implementations _MUST_ preserve the order of Arguments relative to
|
||||
each other (not counting Properties).
|
||||
|
||||
Arguments _MAY_ be prefixed with `/-` to "comment out" the entire token and
|
||||
make it act as plain whitespace, even if it spreads across multiple lines.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
my-node 1 2 3 "a" "b" "c"
|
||||
```
|
||||
|
||||
### Children Block
|
||||
|
||||
A children block is a block of [Nodes](#node), surrounded by `{` and `}`. They
|
||||
are an optional terminator for nodes, and create a hierarchy of KDL nodes.
|
||||
|
||||
Regular node termination rules apply, which means multiple nodes can be
|
||||
included in a single-line children block, as long as they're all terminated by
|
||||
`;`.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
parent {
|
||||
child1
|
||||
child2
|
||||
}
|
||||
|
||||
parent { child1; child2; }
|
||||
```
|
||||
|
||||
### Value
|
||||
|
||||
A value is either: a [String](#string), a [Raw String](#raw-string), a
|
||||
[Number](#number), a [Boolean](#boolean), or [Null](#null)
|
||||
|
||||
Values _MUST_ be either [Arguments](#argument) or values of
|
||||
[Properties](#property).
|
||||
|
||||
Values _MAY_ be prefixed by a single [Type Annotation](#type-annotation).
|
||||
|
||||
### Type Annotation
|
||||
|
||||
A type annotation is a prefix to any [Node Name](#node) or [Value](#value) that
|
||||
includes a _suggestion_ of what type the value is _intended_ to be treated as,
|
||||
or as a _context-specific elaboration_ of the more generic type the node name
|
||||
indicates.
|
||||
|
||||
Type annotations are written as a set of `(` and `)` with a single
|
||||
[Identifier](#identifier) in it. Any valid identifier is considered a valid
|
||||
type annotation. There must be no whitespace between a type annotation and its
|
||||
associated Node Name or Value.
|
||||
|
||||
KDL does not specify any restrictions on what implementations might do with
|
||||
these annotations. They are free to ignore them, or use them to make decisions
|
||||
about how to interpret a value.
|
||||
|
||||
Additionally, the following type annotations MAY be recognized by KDL parsers
|
||||
and, if used, SHOULD interpret these types as follows:
|
||||
|
||||
#### Reserved Type Annotations for Numbers Without Decimals:
|
||||
|
||||
Signed integers of various sizes (the number is the bit size):
|
||||
|
||||
* `i8`
|
||||
* `i16`
|
||||
* `i32`
|
||||
* `i64`
|
||||
|
||||
Unsigned integers of various sizes (the number is the bit size):
|
||||
|
||||
* `u8`
|
||||
* `u16`
|
||||
* `u32`
|
||||
* `u64`
|
||||
|
||||
Platform-dependent integer types, both signed and unsigned:
|
||||
|
||||
* `isize`
|
||||
* `usize`
|
||||
|
||||
#### Reserved Type Annotations for Numbers With Decimals:
|
||||
|
||||
IEEE 754 floating point numbers, both single (32) and double (64) precision:
|
||||
|
||||
* `f32`
|
||||
* `f64`
|
||||
|
||||
IEEE 754-2008 decimal floating point numbers
|
||||
|
||||
* `decimal64`
|
||||
* `decimal128`
|
||||
|
||||
#### Reserved Type Annotations for Strings:
|
||||
|
||||
* `date-time`: ISO8601 date/time format.
|
||||
* `time`: "Time" section of ISO8601.
|
||||
* `date`: "Date" section of ISO8601.
|
||||
* `duration`: ISO8601 duration format.
|
||||
* `decimal`: IEEE 754-2008 decimal string format.
|
||||
* `currency`: ISO 4217 currency code.
|
||||
* `country-2`: ISO 3166-1 alpha-2 country code.
|
||||
* `country-3`: ISO 3166-1 alpha-3 country code.
|
||||
* `country-subdivision`: ISO 3166-2 country subdivision code.
|
||||
* `email`: RFC5302 email address.
|
||||
* `idn-email`: RFC6531 internationalized email address.
|
||||
* `hostname`: RFC1132 internet hostname.
|
||||
* `idn-hostname`: RFC5890 internationalized internet hostname.
|
||||
* `ipv4`: RFC2673 dotted-quad IPv4 address.
|
||||
* `ipv6`: RFC2373 IPv6 address.
|
||||
* `url`: RFC3986 URI.
|
||||
* `url-reference`: RFC3986 URI Reference.
|
||||
* `irl`: RFC3987 Internationalized Resource Identifier.
|
||||
* `irl-reference`: RFC3987 Internationalized Resource Identifier Reference.
|
||||
* `url-template`: RFC6570 URI Template.
|
||||
* `uuid`: RFC4122 UUID.
|
||||
* `regex`: Regular expression. Specific patterns may be implementation-dependent.
|
||||
* `base64`: A Base64-encoded string, denoting arbitrary binary data.
|
||||
|
||||
#### Examples
|
||||
|
||||
```kdl
|
||||
node (u8)123
|
||||
node prop=(regex)".*"
|
||||
(published)date "1970-01-01"
|
||||
(contributor)person name="Foo McBar"
|
||||
```
|
||||
|
||||
### String
|
||||
|
||||
Strings in KDL represent textual [Values](#value). They are delimited by `"`
|
||||
on either side of any number of literal string characters except unescaped
|
||||
`"` and `\`. This includes literal [Newline](#newline) characters, which means a
|
||||
String Value can encompass multiple lines without behaving like a Newline for
|
||||
[Node](#node) parsing purposes.
|
||||
|
||||
Strings _MUST_ be represented as UTF-8 values.
|
||||
|
||||
In addition to literal code points, a number of "escapes" are supported.
|
||||
"Escapes" are the character `\` followed by another character, and are
|
||||
interpreted as described in the following table:
|
||||
|
||||
| Name | Escape | Code Pt |
|
||||
|-------------------------------|--------|----------|
|
||||
| Line Feed | `\n` | `U+000A` |
|
||||
| Carriage Return | `\r` | `U+000D` |
|
||||
| Character Tabulation (Tab) | `\t` | `U+0009` |
|
||||
| Reverse Solidus (Backslash) | `\\` | `U+005C` |
|
||||
| Solidus (Forwardslash) | `\/` | `U+002F` |
|
||||
| Quotation Mark (Double Quote) | `\"` | `U+0022` |
|
||||
| Backspace | `\b` | `U+0008` |
|
||||
| Form Feed | `\f` | `U+000C` |
|
||||
| Unicode Escape | `\u{(1-6 hex chars)}` | Code point described by hex characters, up to `10FFFF` |
|
||||
|
||||
### Raw String
|
||||
|
||||
Raw Strings in KDL are much like [Strings](#string), except they do not
|
||||
support `\`-escapes. They otherwise share the same properties as far as
|
||||
literal [Newline](#newline) characters go, and the requirement of UTF-8
|
||||
representation.
|
||||
|
||||
Raw String literals are represented as `r`, followed by zero or more `#`
|
||||
characters, followed by `"`, followed by any number of UTF-8 literals. The string is then
|
||||
closed by a `"` followed by a _matching_ number of `#` characters. This means
|
||||
that the string sequence `"` or `"#` and such must not match the closing `"`
|
||||
with the same or more `#` characters as the opening `r`.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
just-escapes r"\n will be literal"
|
||||
quotes-and-escapes r#"hello\n\r\asd"world"#
|
||||
```
|
||||
|
||||
### Number
|
||||
|
||||
Numbers in KDL represent numerical [Values](#value). There is no logical
|
||||
distinction in KDL between real numbers, integers, and floating point numbers.
|
||||
It's up to individual implementations to determine how to represent KDL
|
||||
numbers.
|
||||
|
||||
There are four syntaxes for Numbers: Decimal, Hexadecimal, Octal, and Binary.
|
||||
|
||||
* All numbers may optionally start with one of `-` or `+`, which determine whether they'll be positive or negative.
|
||||
* Binary numbers start with `0b` and only allow `0` and `1` as digits, which may be separated by `_`. They represent numbers in radix 2.
|
||||
* Octal numbers start with `0o` and only allow digits between `0` and `7`, which may be separated by `_`. They represent numbers in radix 8.
|
||||
* Hexadecimal numbers start with `0x` and allow digits between `0` and `9`, as well as letters `A` through `F`, in either lower or upper case, which may be separated by `_`. They represent numbers in radix 16.
|
||||
* Decimal numbers are a bit more special:
|
||||
* They have no radix prefix.
|
||||
* They use digits `0` through `9`, which may be separated by `_`.
|
||||
* They may optionally include a decimal separator `.`, followed by more digits, which may again be separated by `_`.
|
||||
* They may optionally be followed by `E` or `e`, an optional `-` or `+`, and more digits, to represent an exponent value.
|
||||
|
||||
### Boolean
|
||||
|
||||
A boolean [Value](#value) is either the symbol `true` or `false`. These
|
||||
_SHOULD_ be represented by implementation as boolean logical values, or some
|
||||
approximation thereof.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
my-node true value=false
|
||||
```
|
||||
|
||||
### Null
|
||||
|
||||
The symbol `null` represents a null [Value](#value). It's up to the
|
||||
implementation to decide how to represent this, but it generally signals the
|
||||
"absence" of a value. It is reasonable for an implementation to ignore null
|
||||
values altogether when deserializing.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
my-node null key=null
|
||||
```
|
||||
|
||||
### Whitespace
|
||||
|
||||
The following characters should be treated as non-[Newline](#newline) [white
|
||||
space](https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt):
|
||||
|
||||
| Name | Code Pt |
|
||||
|----------------------|---------|
|
||||
| Character Tabulation | `U+0009` |
|
||||
| Space | `U+0020` |
|
||||
| No-Break Space | `U+00A0` |
|
||||
| Ogham Space Mark | `U+1680` |
|
||||
| En Quad | `U+2000` |
|
||||
| Em Quad | `U+2001` |
|
||||
| En Space | `U+2002` |
|
||||
| Em Space | `U+2003` |
|
||||
| Three-Per-Em Space | `U+2004` |
|
||||
| Four-Per-Em Space | `U+2005` |
|
||||
| Six-Per-Em Space | `U+2006` |
|
||||
| Figure Space | `U+2007` |
|
||||
| Punctuation Space | `U+2008` |
|
||||
| Thin Space | `U+2009` |
|
||||
| Hair Space | `U+200A` |
|
||||
| Narrow No-Break Space| `U+202F` |
|
||||
| Medium Mathematical Space | `U+205F` |
|
||||
| Ideographic Space | `U+3000` |
|
||||
|
||||
### Newline
|
||||
|
||||
The following characters [should be treated as new
|
||||
lines](https://www.unicode.org/versions/Unicode13.0.0/ch05.pdf):
|
||||
|
||||
| Acronym | Name | Code Pt |
|
||||
|---------|-----------------|---------|
|
||||
| CR | Carriage Return | `U+000D` |
|
||||
| LF | Line Feed | `U+000A` |
|
||||
| CRLF | Carriage Return and Line Feed | `U+000D` + `U+000A` |
|
||||
| NEL | Next Line | `U+0085` |
|
||||
| FF | Form Feed | `U+000C` |
|
||||
| LS | Line Separator | `U+2028` |
|
||||
| PS | Paragraph Separator | `U+2029` |
|
||||
|
||||
Note that for the purpose of new lines, CRLF is considered _a single newline_.
|
||||
|
||||
## Full Grammar
|
||||
|
||||
```
|
||||
nodes := linespace* (node nodes?)? linespace*
|
||||
|
||||
node := ('/-' node-space*)? type? identifier (node-space node-space* node-props-and-args)* (node-space* node-children ws*)? node-space* node-terminator
|
||||
node-props-and-args := ('/-' node-space*)? (prop | value)
|
||||
node-children := ('/-' node-space*)? '{' nodes '}'
|
||||
node-space := ws* escline ws* | ws+
|
||||
node-terminator := single-line-comment | newline | ';' | eof
|
||||
|
||||
identifier := string | bare-identifier
|
||||
bare-identifier := ((identifier-char - digit - sign) identifier-char* | sign ((identifier-char - digit) identifier-char*)?) - keyword
|
||||
identifier-char := unicode - linespace - [\/(){}<>;[]=,"]
|
||||
keyword := boolean | 'null'
|
||||
prop := identifier '=' value
|
||||
value := type? (string | number | keyword)
|
||||
type := '(' identifier ')'
|
||||
|
||||
string := raw-string | escaped-string
|
||||
escaped-string := '"' character* '"'
|
||||
character := '\' escape | [^\"]
|
||||
escape := ["\\/bfnrt] | 'u{' hex-digit{1, 6} '}'
|
||||
hex-digit := [0-9a-fA-F]
|
||||
|
||||
raw-string := 'r' raw-string-hash
|
||||
raw-string-hash := '#' raw-string-hash '#' | raw-string-quotes
|
||||
raw-string-quotes := '"' .* '"'
|
||||
|
||||
number := decimal | hex | octal | binary
|
||||
|
||||
decimal := integer ('.' [0-9] [0-9_]*)? exponent?
|
||||
exponent := ('e' | 'E') integer
|
||||
integer := sign? [0-9] [0-9_]*
|
||||
sign := '+' | '-'
|
||||
|
||||
hex := sign? '0x' hex-digit (hex-digit | '_')*
|
||||
octal := sign? '0o' [0-7] [0-7_]*
|
||||
binary := sign? '0b' ('0' | '1') ('0' | '1' | '_')*
|
||||
|
||||
boolean := 'true' | 'false'
|
||||
|
||||
escline := '\\' ws* (single-line-comment | newline)
|
||||
|
||||
linespace := newline | ws | single-line-comment
|
||||
|
||||
newline := See Table (All line-break white_space)
|
||||
|
||||
ws := bom | unicode-space | multi-line-comment
|
||||
|
||||
bom := '\u{FFEF}'
|
||||
|
||||
unicode-space := See Table (All White_Space unicode characters which are not `newline`)
|
||||
|
||||
single-line-comment := '//' ^newline+ (newline | eof)
|
||||
multi-line-comment := '/*' commented-block
|
||||
commented-block := '*/' | (multi-line-comment | '*' | '/' | [^*/]+) commented-block
|
||||
```
|
||||
The v2 specification has been moved [here](draft-marchan-kdl2.md).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,544 @@
|
|||
# KDL v1 Spec
|
||||
|
||||
This is the semi-formal specification for the legacy version of KDL, including
|
||||
the intended data model and the grammar.
|
||||
|
||||
This document describes KDL version `1.0.0`. It was released on September 11, 2021.
|
||||
|
||||
Information in this spec is intended as both an accessible historical record,
|
||||
and a reference for KDL implementors who are interested in supporting both major
|
||||
versions of the language.
|
||||
|
||||
The v1 spec will not receive further updates outside of minor, inconsequential
|
||||
rewordings or other superficial fixes and is considered a "legacy" version.
|
||||
|
||||
## Compatibility
|
||||
|
||||
KDL v2 is designed such that for any given KDL document in either v1 or v2, the
|
||||
parse will either fail completely, or, if the parse succeeds, the data
|
||||
represented by a v1 or v2 parser will be identical. This means that it's safe to
|
||||
use a fallback parsing strategy in order to support both v1 and v2
|
||||
simultaneously. For example, `node "foo"` is a valid node in both versions, and
|
||||
should be represented identically by parsers.
|
||||
|
||||
KDL v2 is designed such that for any given KDL document written as KDL
|
||||
1.0 or [KDL 2.0](https://kdl-org.github.io/kdl/#go.draft-marchan-kdl2.html),
|
||||
the parse will either fail completely, or, if the
|
||||
parse succeeds, the data represented by a v1 or v2 parser will be identical.
|
||||
This means that it's safe to use a fallback parsing strategy in order to support
|
||||
both v1 and v2 simultaneously. For example, `node "foo"` is a valid node in both
|
||||
versions, and should be represented identically by parsers.
|
||||
|
||||
A version marker `/- kdl-version 1` (or `2`) _MAY_ be added to the beginning of
|
||||
a KDL document, optionally preceded by the BOM, and parsers _MAY_ use that as a
|
||||
hint as to which version to parse the document as.
|
||||
|
||||
## Introduction
|
||||
|
||||
KDL is a node-oriented document language. Its niche and purpose overlaps with
|
||||
XML, and as do many of its semantics. You can use KDL both as a configuration
|
||||
language, and a data exchange or storage format, if you so choose.
|
||||
|
||||
The bulk of this document is dedicated to a long-form description of all
|
||||
[Components](#components) of a KDL document. There is also a much more terse
|
||||
[Grammar](#full-grammar) at the end of the document that covers most of the
|
||||
rules, with some semantic exceptions involving the data model.
|
||||
|
||||
KDL is designed to be easy to read _and_ easy to implement.
|
||||
|
||||
In this document, references to "left" or "right" refer to directions in the
|
||||
*data stream* towards the beginning or end, respectively; in other words,
|
||||
the directions if the data stream were only ASCII text. They do not refer
|
||||
to the writing direction of text, which can flow in either direction,
|
||||
depending on the characters used.
|
||||
|
||||
## Components
|
||||
|
||||
### Document
|
||||
|
||||
The toplevel concept of KDL is a Document. A Document is composed of zero or
|
||||
more [Nodes](#node), separated by newlines and whitespace, and eventually
|
||||
terminated by an EOF.
|
||||
|
||||
All KDL documents should be UTF-8 encoded and conform to the specifications in
|
||||
this document.
|
||||
|
||||
#### Example
|
||||
|
||||
The following is a document composed of two toplevel nodes:
|
||||
|
||||
```kdl
|
||||
foo {
|
||||
bar
|
||||
}
|
||||
baz
|
||||
```
|
||||
|
||||
### Node
|
||||
|
||||
Being a node-oriented language means that the real core component of any KDL
|
||||
document is the "node". Every node must have a name, which is an
|
||||
[Identifier](#identifier).
|
||||
|
||||
The name may be preceded by a [Type Annotation](#type-annotation) to further
|
||||
clarify its type, particularly in relation to its parent node. (For example,
|
||||
clarifying that a particular `date` child node is for the _publication_ date,
|
||||
rather than the last-modified date, with `(published)date`.)
|
||||
|
||||
Following the name are zero or more [Arguments](#argument) or
|
||||
[Properties](#property), separated by either [whitespace](#whitespace) or [a
|
||||
slash-escaped line continuation](#line-continuation). Arguments and Properties
|
||||
may be interspersed in any order, much like is common with positional
|
||||
arguments vs options in command line tools.
|
||||
|
||||
[Children](#children-block) can be placed after the name and the optional
|
||||
Arguments and Properties, possibly separated by either whitespace or a
|
||||
slash-escaped line continuation.
|
||||
|
||||
Arguments are ordered relative to each other (but not relative to Properties)
|
||||
and that order must be preserved in order to maintain the semantics.
|
||||
|
||||
By contrast, Property order _SHOULD NOT_ matter to implementations.
|
||||
[Children](#children-block) should be used if an order-sensitive key/value
|
||||
data structure must be represented in KDL.
|
||||
|
||||
Nodes _MAY_ be prefixed with `/-` to "comment out" the entire node, including
|
||||
its properties, arguments, and children, and make it act as plain whitespace,
|
||||
even if it spreads across multiple lines.
|
||||
|
||||
Finally, a node is terminated by either a [Newline](#newline), a semicolon (`;`)
|
||||
or the end of the file/stream (an `EOF`).
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
foo 1 key="val" 3 {
|
||||
bar
|
||||
(role)baz 1 2
|
||||
}
|
||||
```
|
||||
|
||||
### Identifier
|
||||
|
||||
An Identifier is either a [Bare Identifier](#bare-identifier), which is an
|
||||
unquoted string like `node` or `item`, or a [String](#string), which is quoted,
|
||||
like `"node"` or `"two words"`. There's no semantic difference between the
|
||||
kinds of identifier; this simply allows for the use of quotes to have unusual
|
||||
identifiers that are inexpressible as bare identifiers.
|
||||
|
||||
### Bare Identifier
|
||||
|
||||
A Bare Identifier is composed of any Unicode codepoint other than [non-initial
|
||||
characters](#non-initial-characters), followed by any number of Unicode
|
||||
codepoints other than [non-identifier characters](#non-identifier-characters),
|
||||
so long as this doesn't produce something confusable for a [Number](#number),
|
||||
[Boolean](#boolean), or [Null](#null). For example, both a [Number](#number)
|
||||
and an Identifier can start with `-`, but when an Identifier starts with `-`
|
||||
the second character cannot be a digit. This is precisely specified in the
|
||||
[Full Grammar](#full-grammar) below.
|
||||
|
||||
Identifiers are terminated by [Whitespace](#whitespace) or
|
||||
[Newlines](#newline).
|
||||
|
||||
### Non-initial characters
|
||||
|
||||
The following characters cannot be the first character in a
|
||||
[Bare Identifier](#identifier):
|
||||
|
||||
* Any decimal digit (0-9)
|
||||
* Any [non-identifier characters](#non-identifier-characters)
|
||||
|
||||
Be aware that the `-` character can only be used as an initial
|
||||
character if the second character is not a digit. This allows
|
||||
identifiers to look like `--this`, and removes the ambiguity
|
||||
of having an identifier look like a negative number.
|
||||
|
||||
### Non-identifier characters
|
||||
|
||||
The following characters cannot be used anywhere in a [Bare Identifier](#identifier):
|
||||
|
||||
* Any codepoint with hexadecimal value `0x20` or below.
|
||||
* Any codepoint with hexadecimal value higher than `0x10FFFF`.
|
||||
* Any of `\/(){}<>;[]=,"`
|
||||
|
||||
### Line Continuation
|
||||
|
||||
Line continuations allow [Nodes](#node) to be spread across multiple lines.
|
||||
|
||||
A line continuation is a `\` character followed by zero or more whitespace
|
||||
characters and an optional single-line comment. It must be terminated by a
|
||||
[Newline](#newline) (including the Newline that is part of single-line comments).
|
||||
|
||||
Following a line continuation, processing of a Node can continue as usual.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
my-node 1 2 \ // comments are ok after \
|
||||
3 4 // This is the actual end of the Node.
|
||||
```
|
||||
|
||||
### Property
|
||||
|
||||
A Property is a key/value pair attached to a [Node](#node). A Property is
|
||||
composed of an [Identifier](#identifier), followed immediately by a `=`, and then a [Value](#value).
|
||||
|
||||
Properties should be interpreted left-to-right, with rightmost properties with
|
||||
identical names overriding earlier properties. That is:
|
||||
|
||||
```kdl
|
||||
node a=1 a=2
|
||||
```
|
||||
|
||||
In this example, the node's `a` value must be `2`, not `1`.
|
||||
|
||||
No other guarantees about order should be expected by implementers.
|
||||
Deserialized representations may iterate over properties in any order and
|
||||
still be spec-compliant.
|
||||
|
||||
Properties _MAY_ be prefixed with `/-` to "comment out" the entire token and
|
||||
make it act as plain whitespace, even if it spreads across multiple lines.
|
||||
|
||||
### Argument
|
||||
|
||||
An Argument is a bare [Value](#value) attached to a [Node](#node), with no
|
||||
associated key. It shares the same space as [Properties](#properties), and may be interleaved with them.
|
||||
|
||||
A Node may have any number of Arguments, which should be evaluated left to
|
||||
right. KDL implementations _MUST_ preserve the order of Arguments relative to
|
||||
each other (not counting Properties).
|
||||
|
||||
Arguments _MAY_ be prefixed with `/-` to "comment out" the entire token and
|
||||
make it act as plain whitespace, even if it spreads across multiple lines.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
my-node 1 2 3 "a" "b" "c"
|
||||
```
|
||||
|
||||
### Children Block
|
||||
|
||||
A children block is a block of [Nodes](#node), surrounded by `{` and `}`. They
|
||||
are an optional part of nodes, and create a hierarchy of KDL nodes.
|
||||
|
||||
Regular node termination rules apply, which means multiple nodes can be
|
||||
included in a single-line children block, as long as they're all terminated by
|
||||
`;`.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
parent {
|
||||
child1
|
||||
child2
|
||||
}
|
||||
|
||||
parent { child1; child2; }
|
||||
```
|
||||
|
||||
### Value
|
||||
|
||||
A value is either: a [String](#string), a [Number](#number), a
|
||||
[Boolean](#boolean), or [Null](#null).
|
||||
|
||||
Values _MUST_ be either [Arguments](#argument) or values of
|
||||
[Properties](#property).
|
||||
|
||||
Values (both as arguments and as properties) _MAY_ be prefixed by a single
|
||||
[Type Annotation](#type-annotation).
|
||||
|
||||
### Type Annotation
|
||||
|
||||
A type annotation is a prefix to any [Node Name](#node) or [Value](#value) that
|
||||
includes a _suggestion_ of what type the value is _intended_ to be treated as,
|
||||
or as a _context-specific elaboration_ of the more generic type the node name
|
||||
indicates.
|
||||
|
||||
Type annotations are written as a set of `(` and `)` with an
|
||||
[Identifier](#identifier) in it. Any valid identifier is considered a valid
|
||||
type annotation. There must be no whitespace between a type annotation and its
|
||||
associated Node Name or Value.
|
||||
|
||||
KDL does not specify any restrictions on what implementations might do with
|
||||
these annotations. They are free to ignore them, or use them to make decisions
|
||||
about how to interpret a value.
|
||||
|
||||
Additionally, the following type annotations MAY be recognized by KDL parsers
|
||||
and, if used, SHOULD interpret these types as follows:
|
||||
|
||||
#### Reserved Type Annotations for Numbers Without Decimals:
|
||||
|
||||
Signed integers of various sizes (the number is the bit size):
|
||||
|
||||
* `i8`
|
||||
* `i16`
|
||||
* `i32`
|
||||
* `i64`
|
||||
|
||||
Unsigned integers of various sizes (the number is the bit size):
|
||||
|
||||
* `u8`
|
||||
* `u16`
|
||||
* `u32`
|
||||
* `u64`
|
||||
|
||||
Platform-dependent integer types, both signed and unsigned:
|
||||
|
||||
* `isize`
|
||||
* `usize`
|
||||
|
||||
#### Reserved Type Annotations for Numbers With Decimals:
|
||||
|
||||
IEEE 754 floating point numbers, both single (32) and double (64) precision:
|
||||
|
||||
* `f32`
|
||||
* `f64`
|
||||
|
||||
IEEE 754-2008 decimal floating point numbers
|
||||
|
||||
* `decimal64`
|
||||
* `decimal128`
|
||||
|
||||
#### Reserved Type Annotations for Strings:
|
||||
|
||||
* `date-time`: ISO8601 date/time format.
|
||||
* `time`: "Time" section of ISO8601.
|
||||
* `date`: "Date" section of ISO8601.
|
||||
* `duration`: ISO8601 duration format.
|
||||
* `decimal`: IEEE 754-2008 decimal string format.
|
||||
* `currency`: ISO 4217 currency code.
|
||||
* `country-2`: ISO 3166-1 alpha-2 country code.
|
||||
* `country-3`: ISO 3166-1 alpha-3 country code.
|
||||
* `country-subdivision`: ISO 3166-2 country subdivision code.
|
||||
* `email`: RFC5322 email address.
|
||||
* `idn-email`: RFC6531 internationalized email address.
|
||||
* `hostname`: RFC1123 internet hostname (only ASCII segments)
|
||||
* `idn-hostname`: RFC5890 internationalized internet hostname (only `xn--`-prefixed ASCII "punycode" segments, or non-ASCII segments)
|
||||
* `ipv4`: RFC2673 dotted-quad IPv4 address.
|
||||
* `ipv6`: RFC2373 IPv6 address.
|
||||
* `url`: RFC3986 URI.
|
||||
* `url-reference`: RFC3986 URI Reference.
|
||||
* `irl`: RFC3987 Internationalized Resource Identifier.
|
||||
* `irl-reference`: RFC3987 Internationalized Resource Identifier Reference.
|
||||
* `url-template`: RFC6570 URI Template.
|
||||
* `uuid`: RFC4122 UUID.
|
||||
* `regex`: Regular expression. Specific patterns may be implementation-dependent.
|
||||
* `base64`: A Base64-encoded string, denoting arbitrary binary data.
|
||||
|
||||
#### Examples
|
||||
|
||||
```kdl
|
||||
node (u8)123
|
||||
node prop=(regex)".*"
|
||||
(published)date "1970-01-01"
|
||||
(contributor)person name="Foo McBar"
|
||||
```
|
||||
|
||||
### String
|
||||
|
||||
Strings in KDL represent textual [Values](#value), or unusual identifiers. A
|
||||
String is either a [Quoted String](#quoted-string) or a
|
||||
[Raw String](#raw-string). Quoted Strings may include escaped characters, while
|
||||
Raw Strings always contain only the literal characters that are present.
|
||||
|
||||
### Quoted String
|
||||
|
||||
A Quoted String is delimited by `"` on either side of any number of literal
|
||||
string characters except unescaped `"` and `\`. This includes literal
|
||||
[Newline](#newline) characters, which means a String Value can encompass
|
||||
multiple lines without behaving like a Newline for [Node](#node) parsing
|
||||
purposes.
|
||||
|
||||
Strings _MUST_ be represented as UTF-8 values.
|
||||
|
||||
In addition to literal code points, a number of "escapes" are supported.
|
||||
"Escapes" are the character `\` followed by another character, and are
|
||||
interpreted as described in the following table:
|
||||
|
||||
| Name | Escape | Code Pt |
|
||||
|-------------------------------|--------|----------|
|
||||
| Line Feed | `\n` | `U+000A` |
|
||||
| Carriage Return | `\r` | `U+000D` |
|
||||
| Character Tabulation (Tab) | `\t` | `U+0009` |
|
||||
| Reverse Solidus (Backslash) | `\\` | `U+005C` |
|
||||
| Solidus (Forwardslash) | `\/` | `U+002F` |
|
||||
| Quotation Mark (Double Quote) | `\"` | `U+0022` |
|
||||
| Backspace | `\b` | `U+0008` |
|
||||
| Form Feed | `\f` | `U+000C` |
|
||||
| Unicode Escape | `\u{(1-6 hex chars)}` | Code point described by hex characters, up to `10FFFF` |
|
||||
|
||||
### Raw String
|
||||
|
||||
Raw Strings in KDL are much like [Quoted Strings](#quoted-string), except they
|
||||
do not support `\`-escapes. They otherwise share the same properties as far as
|
||||
literal [Newline](#newline) characters go, and the requirement of UTF-8
|
||||
representation.
|
||||
|
||||
Raw String literals are represented as `r`, followed by zero or more `#`
|
||||
characters, followed by `"`, followed by any number of UTF-8 literals. The
|
||||
string is then closed by a `"` followed by a _matching_ number of `#`
|
||||
characters. This allows them to contain raw `"` or `#` characters; only the
|
||||
precise terminator (resembling `"##`, for example) ends the raw string. This
|
||||
means that the string sequence `"` or `"#` and such must not match the closing
|
||||
`"` with the same or more `#` characters as the opening `r`.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
just-escapes r"\n will be literal"
|
||||
quotes-and-escapes r#"hello\n\r\asd"world"#
|
||||
```
|
||||
|
||||
### Number
|
||||
|
||||
Numbers in KDL represent numerical [Values](#value). There is no logical distinction in KDL
|
||||
between real numbers, integers, and floating point numbers. It's up to
|
||||
individual implementations to determine how to represent KDL numbers.
|
||||
|
||||
There are four syntaxes for Numbers: Decimal, Hexadecimal, Octal, and Binary.
|
||||
|
||||
* All numbers may optionally start with one of `-` or `+`, which determine whether they'll be positive or negative.
|
||||
* Binary numbers start with `0b` and only allow `0` and `1` as digits, which may be separated by `_`. They represent numbers in radix 2.
|
||||
* Octal numbers start with `0o` and only allow digits between `0` and `7`, which may be separated by `_`. They represent numbers in radix 8.
|
||||
* Hexadecimal numbers start with `0x` and allow digits between `0` and `9`, as well as letters `A` through `F`, in either lower or upper case, which may be separated by `_`. They represent numbers in radix 16.
|
||||
* Decimal numbers are a bit more special:
|
||||
* They have no radix prefix.
|
||||
* They use digits `0` through `9`, which may be separated by `_`.
|
||||
* They may optionally include a decimal separator `.`, followed by more digits, which may again be separated by `_`.
|
||||
* They may optionally be followed by `E` or `e`, an optional `-` or `+`, and more digits, to represent an exponent value.
|
||||
|
||||
### Boolean
|
||||
|
||||
A boolean [Value](#value) is either the symbol `true` or `false`. These
|
||||
_SHOULD_ be represented by implementation as boolean logical values, or some
|
||||
approximation thereof.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
my-node true value=false
|
||||
```
|
||||
|
||||
### Null
|
||||
|
||||
The symbol `null` represents a null [Value](#value). It's up to the
|
||||
implementation to decide how to represent this, but it generally signals the
|
||||
"absence" of a value. It is reasonable for an implementation to ignore null
|
||||
values altogether when deserializing.
|
||||
|
||||
#### Example
|
||||
|
||||
```kdl
|
||||
my-node null key=null
|
||||
```
|
||||
|
||||
### Whitespace
|
||||
|
||||
The following characters should be treated as non-[Newline](#newline) [white
|
||||
space](https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt):
|
||||
|
||||
| Name | Code Pt |
|
||||
|----------------------|---------|
|
||||
| Character Tabulation | `U+0009` |
|
||||
| Space | `U+0020` |
|
||||
| No-Break Space | `U+00A0` |
|
||||
| Ogham Space Mark | `U+1680` |
|
||||
| En Quad | `U+2000` |
|
||||
| Em Quad | `U+2001` |
|
||||
| En Space | `U+2002` |
|
||||
| Em Space | `U+2003` |
|
||||
| Three-Per-Em Space | `U+2004` |
|
||||
| Four-Per-Em Space | `U+2005` |
|
||||
| Six-Per-Em Space | `U+2006` |
|
||||
| Figure Space | `U+2007` |
|
||||
| Punctuation Space | `U+2008` |
|
||||
| Thin Space | `U+2009` |
|
||||
| Hair Space | `U+200A` |
|
||||
| Narrow No-Break Space| `U+202F` |
|
||||
| Medium Mathematical Space | `U+205F` |
|
||||
| Ideographic Space | `U+3000` |
|
||||
|
||||
#### Multi-line comments
|
||||
|
||||
In addition to single-line comments using `//`, comments can also be started
|
||||
with `/*` and ended with `*/`. These comments can span multiple lines. They
|
||||
are allowed in all positions where [Whitespace](#whitespace) is allowed and
|
||||
can be nested.
|
||||
|
||||
### Newline
|
||||
|
||||
The following characters [should be treated as new
|
||||
lines](https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-5/#G41643):
|
||||
|
||||
| Acronym | Name | Code Pt |
|
||||
|---------|-----------------|---------|
|
||||
| CRLF | Carriage Return and Line Feed | `U+000D` + `U+000A` |
|
||||
| CR | Carriage Return | `U+000D` |
|
||||
| LF | Line Feed | `U+000A` |
|
||||
| NEL | Next Line | `U+0085` |
|
||||
| FF | Form Feed | `U+000C` |
|
||||
| LS | Line Separator | `U+2028` |
|
||||
| PS | Paragraph Separator | `U+2029` |
|
||||
|
||||
Note that for the purpose of new lines, CRLF is considered _a single newline_. `VT` `Vertical tab` `U+000B` was mistakenly excluded, but the v1 spec if frozen, so it's left unchanged.
|
||||
|
||||
## Full Grammar
|
||||
|
||||
```
|
||||
nodes := linespace* (node nodes?)? linespace*
|
||||
|
||||
node := ('/-' node-space*)? type? identifier (node-space+ node-prop-or-arg)* (node-space* node-children ws*)? node-space* node-terminator
|
||||
node-prop-or-arg := ('/-' node-space*)? (prop | value)
|
||||
node-children := ('/-' node-space*)? '{' nodes '}'
|
||||
node-space := ws* escline ws* | ws+
|
||||
node-terminator := single-line-comment | newline | ';' | eof
|
||||
|
||||
identifier := string | bare-identifier
|
||||
bare-identifier := ((identifier-char - digit - sign) identifier-char* | sign ((identifier-char - digit) identifier-char*)?) - keyword
|
||||
identifier-char := unicode - linespace - [\/(){}<>;[]=,"]
|
||||
keyword := boolean | 'null'
|
||||
prop := identifier '=' value
|
||||
value := type? (string | number | keyword)
|
||||
type := '(' identifier ')'
|
||||
|
||||
string := raw-string | escaped-string
|
||||
escaped-string := '"' character* '"'
|
||||
character := '\' escape | [^\"]
|
||||
escape := ["\\/bfnrt] | 'u{' hex-digit{1, 6} '}'
|
||||
hex-digit := [0-9a-fA-F]
|
||||
|
||||
raw-string := 'r' raw-string-hash
|
||||
raw-string-hash := '#' raw-string-hash '#' | raw-string-quotes
|
||||
raw-string-quotes := '"' .* '"'
|
||||
|
||||
number := hex | octal | binary | decimal
|
||||
|
||||
decimal := sign? integer ('.' integer)? exponent?
|
||||
exponent := ('e' | 'E') sign? integer
|
||||
integer := digit (digit | '_')*
|
||||
digit := [0-9]
|
||||
sign := '+' | '-'
|
||||
|
||||
hex := sign? '0x' hex-digit (hex-digit | '_')*
|
||||
octal := sign? '0o' [0-7] [0-7_]*
|
||||
binary := sign? '0b' ('0' | '1') ('0' | '1' | '_')*
|
||||
|
||||
boolean := 'true' | 'false'
|
||||
|
||||
escline := '\\' ws* (single-line-comment | newline)
|
||||
|
||||
linespace := newline | ws | single-line-comment
|
||||
|
||||
newline := See Table (All line-break white_space)
|
||||
|
||||
ws := bom | unicode-space | multi-line-comment
|
||||
|
||||
bom := '\u{FEFF}'
|
||||
|
||||
unicode-space := See Table (All White_Space unicode characters which are not `newline`)
|
||||
|
||||
single-line-comment := '//' ^newline+ (newline | eof)
|
||||
multi-line-comment := '/*' commented-block
|
||||
commented-block := '*/' | (multi-line-comment | '*' | '/' | [^*/]+) commented-block
|
||||
```
|
||||
|
|
@ -7,7 +7,7 @@ This is version 1.0.0 of XiK.
|
|||
|
||||
XML-in-KDL (XiK from now on) is a KDL microsyntax for losslessly encoding XML into a KDL document. XML and KDL, luckily, have *very similar* data models (KDL is *almost* a superset of XML), so it's quite straightforward to encode most XML documents into KDL.
|
||||
|
||||
See [the website example](blob/main/examples/website.kdl) for an example of this grammar in use to encode an HTML document.
|
||||
See [the website example](examples/website.kdl) for an example of this grammar in use to encode an HTML document. See [XML2KDL](https://github.com/Devasta/XML2KDL) (third party) to encode your XML in KDL (especially [their online editor](https://xsltfiddle.liberty-development.net/bET2rY5)).
|
||||
|
||||
XML has several types of nodes, corresponding to certain KDL constructs:
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ XML elements and KDL nodes have a direct correspondence. In XiK, an XML element
|
|||
* making the attributes into KDL properties
|
||||
* making the child nodes as KDL child nodes
|
||||
|
||||
For example, the XML `<element foo="bar"><child baz="qux" /></element>` is encoded into XiK as `element foo="bar" { child baz="qux" }`.
|
||||
For example, the XML `<element foo="bar"><child baz="quux" /></element>` is encoded into XiK as `element foo=bar { child baz=quux }`.
|
||||
|
||||
XML namespaces are encoded the same as XML: the node name simply contains a `:` character. Note that KDL identifier syntax allows `:` directly in an ident, so a name like `xml:space` or `xlink:href` is a valid node or property name.
|
||||
|
||||
|
|
@ -35,9 +35,9 @@ Raw text contents of an element can be encoded in two possible ways.
|
|||
|
||||
If the element contains *only* text, it should be encoded as a final string unnamed argument. For example, the XML `<a href="http://example.com">here's a link</a>` can be encoded as `a href="http://example.com" "here's a link"`.
|
||||
|
||||
If the element contains mixed text and element children, the text can be encoded as a KDL node with the name `-` with a single string unnamed argument. For example, the XML `<span>some <b>bold</b> text</span>` can be encoded as `span { - "some "; b "bold"; - " text" }`.
|
||||
If the element contains mixed text and element children, the text can be encoded as a KDL node with the name `-` with a single string unnamed argument. For example, the XML `<span>some <b>bold</b> text</span>` can be encoded as `span { - "some "; b bold; - " text" }`.
|
||||
|
||||
An element that contains only text *is allowed to* encode it as `-` children. For example, `<span>foo</span>` *may* be encoded as `span { - "foo" }` instead of `span "foo"`. However, an element cannot mix the "final string attribute" with child nodes; `span "foo" { b "bar" }` is an **invalid** encoding of `<span>foo<b>bar</b></span>`. (It must be encoded as `span { - "foo"; b "bar" }`.)
|
||||
An element that contains only text *is allowed to* encode it as `-` children. For example, `<span>foo</span>` *may* be encoded as `span { - foo }` instead of `span foo`. However, an element cannot mix the "final string attribute" with child nodes; `span foo { b bar }` is an **invalid** encoding of `<span>foo<b>bar</b></span>`. (It must be encoded as `span { - foo; b bar }`.)
|
||||
|
||||
CDATA sections are not preserved in this encoding, as they are merely a source convenience so you don't have to escape a bunch of characters. They are encoded as normal textual contents would be.
|
||||
|
||||
|
|
@ -53,13 +53,13 @@ Processing instructions and XML declarations (nodes that look like `<?foo ... ?>
|
|||
|
||||
The contents of a PI are technically completely unstructured. However, in practice most PIs' contents look like start-tag attributes. If this is the case, they should be encoded as properties on the node, with string values. For example, `<?xml version="1.0"?>` is encoded as `?xml version="1.0"`.
|
||||
|
||||
If the contents of a PI do *not* look like attributes, then instead the entire contents (from the end of the whitespace following the PI name, to the closing `?>` characters) are encoded as a single unnamed string value. For example, the preceding XML declaration *could* be alternately encoded as `?xml r#"version="1.0""#` (but shouldn't be).
|
||||
If the contents of a PI do *not* look like attributes, then instead the entire contents (from the end of the whitespace following the PI name, to the closing `?>` characters) are encoded as a single unnamed string value. For example, the preceding XML declaration *could* be alternately encoded as `?xml #"version="1.0""#` (but shouldn't be).
|
||||
|
||||
(Note that XML declarations are not needed when writing XiK directly; the version is always 1.0, and the encoding is always UTF-8 since it's KDL.)
|
||||
|
||||
----
|
||||
|
||||
Doctypes (nodes that look like `<!DOCTYPE ...>`) are encoded similarly to unstructured Processing Instructions. They have a node name of `!doctype`, and the entire contents of the node, from the end of the whitespace following the "DOCTYPE" to the closing `>`, are encoded as a single unnamed string value. For example, the HTML doctype `<!DOCTYPE html>` is encoded as `!doctype "html"`, while the XHTML 1 Strict doctype would be encoded as `!doctype r#"html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd""#`
|
||||
Doctypes (nodes that look like `<!DOCTYPE ...>`) are encoded similarly to unstructured Processing Instructions. They have a node name of `!doctype`, and the entire contents of the node, from the end of the whitespace following the "DOCTYPE" to the closing `>`, are encoded as a single unnamed string value. For example, the HTML doctype `<!DOCTYPE html>` is encoded as `!doctype html`, while the XHTML 1 Strict doctype would be encoded as `!doctype #"html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd""#`
|
||||
|
||||
----
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,9 +1,9 @@
|
|||
package {
|
||||
name "kdl"
|
||||
name kdl
|
||||
version "0.0.0"
|
||||
description "kat's document language"
|
||||
description "The kdl document language"
|
||||
authors "Kat Marchán <kzm@zkat.tech>"
|
||||
license-file "LICENSE.md"
|
||||
license-file LICENSE.md
|
||||
edition "2018"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,52 @@
|
|||
// This example is a GitHub Action if it used KDL syntax.
|
||||
// See .github/workflows/ci.yml for the file this was based on.
|
||||
name "CI"
|
||||
name CI
|
||||
|
||||
on "push" "pull_request"
|
||||
on push pull_request
|
||||
|
||||
env {
|
||||
RUSTFLAGS "-Dwarnings"
|
||||
RUSTFLAGS -Dwarnings
|
||||
}
|
||||
|
||||
jobs {
|
||||
fmt_and_docs "Check fmt & build docs" {
|
||||
runs-on "ubuntu-latest"
|
||||
runs-on ubuntu-latest
|
||||
steps {
|
||||
step uses="actions/checkout@v1"
|
||||
step "Install Rust" uses="actions-rs/toolchain@v1" {
|
||||
profile "minimal"
|
||||
toolchain "stable"
|
||||
components "rustfmt"
|
||||
override true
|
||||
profile minimal
|
||||
toolchain stable
|
||||
components rustfmt
|
||||
override #true
|
||||
}
|
||||
step "rustfmt" run="cargo fmt --all -- --check"
|
||||
step "docs" run="cargo doc --no-deps"
|
||||
step rustfmt { run cargo fmt --all -- --check }
|
||||
step docs { run cargo doc --no-deps }
|
||||
}
|
||||
}
|
||||
build_and_test "Build & Test" {
|
||||
runs-on "${{ matrix.os }}"
|
||||
strategy {
|
||||
matrix {
|
||||
rust "1.46.0" "stable"
|
||||
os "ubuntu-latest" "macOS-latest" "windows-latest"
|
||||
rust "1.46.0" stable
|
||||
os ubuntu-latest macOS-latest windows-latest
|
||||
}
|
||||
}
|
||||
|
||||
steps {
|
||||
step uses="actions/checkout@v1"
|
||||
step "Install Rust" uses="actions-rs/toolchain@v1" {
|
||||
profile "minimal"
|
||||
profile minimal
|
||||
toolchain "${{ matrix.rust }}"
|
||||
components "clippy"
|
||||
override true
|
||||
components clippy
|
||||
override #true
|
||||
}
|
||||
step "Clippy" run="cargo clippy --all -- -D warnings"
|
||||
step "Run tests" run="cargo test --all --verbose"
|
||||
step Clippy { run cargo clippy --all -- -D warnings }
|
||||
step "Run tests" { run cargo test --all --verbose }
|
||||
step "Other Stuff" run="""
|
||||
echo foo
|
||||
echo bar
|
||||
echo baz
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,290 +1,293 @@
|
|||
document {
|
||||
info {
|
||||
title "KDL Schema" lang="en"
|
||||
description "KDL Schema KDL schema in KDL" lang="en"
|
||||
title "KDL Schema" lang=en
|
||||
description "KDL Schema KDL schema in KDL" lang=en
|
||||
author "Kat Marchán" {
|
||||
link "https://github.com/zkat" rel="self"
|
||||
link "https://github.com/zkat" rel=self
|
||||
}
|
||||
contributor "Lars Willighagen" {
|
||||
link "https://github.com/larsgw" rel="self"
|
||||
link "https://github.com/larsgw" rel=self
|
||||
}
|
||||
link https://github.com/zkat/kdl rel="documentation"
|
||||
license "Creative Commons Attribution-ShareAlike 4.0 International License" spdx="CC-BY-SA-4.0" {
|
||||
link "https://creativecommons.org/licenses/by-sa/4.0/" lang="en"
|
||||
link "https://github.com/zkat/kdl" rel=documentation
|
||||
license "Creative Commons Attribution-ShareAlike 4.0 International License" spdx=CC-BY-SA-4.0 {
|
||||
link "https://creativecommons.org/licenses/by-sa/4.0/" lang=en
|
||||
}
|
||||
published "2021-08-31"
|
||||
modified "2021-09-01"
|
||||
}
|
||||
node "document" {
|
||||
node document {
|
||||
min 1
|
||||
max 1
|
||||
children id="node-children" {
|
||||
node "node-names" id="node-names-node" description="Validations to apply specifically to arbitrary node names" {
|
||||
children ref=r#"[id="validations"]"#
|
||||
children id=node-children {
|
||||
node node-names id=node-names-node description="Validations to apply specifically to arbitrary node names" {
|
||||
children ref=#"[id="validations"]"#
|
||||
}
|
||||
node "other-nodes-allowed" id="other-nodes-allowed-node" description="Whether to allow child nodes other than the ones explicitly listed. Defaults to 'false'." {
|
||||
node other-nodes-allowed id=other-nodes-allowed-node description="Whether to allow child nodes other than the ones explicitly listed. Defaults to '#false'." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "boolean"
|
||||
type boolean
|
||||
}
|
||||
}
|
||||
node "tag-names" description="Validations to apply specifically to arbitrary type tag names" {
|
||||
children ref=r#"[id="validations"]"#
|
||||
node tag-names description="Validations to apply specifically to arbitrary type tag names" {
|
||||
children ref=#"[id="validations"]"#
|
||||
}
|
||||
node "other-tags-allowed" description="Whether to allow child node tags other than the ones explicitly listed. Defaults to 'false'." {
|
||||
node other-tags-allowed description="Whether to allow child node tags other than the ones explicitly listed. Defaults to '#false'." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "boolean"
|
||||
type boolean
|
||||
}
|
||||
}
|
||||
node "info" description="A child node that describes the schema itself." {
|
||||
node info description="A child node that describes the schema itself." {
|
||||
children {
|
||||
node "title" description="The title of the schema or the format it describes" {
|
||||
node title description="The title of the schema or the format it describes" {
|
||||
value description="The title text" {
|
||||
type "string"
|
||||
type string
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
prop "lang" id="info-lang" description="The language of the text" {
|
||||
type "string"
|
||||
prop lang id=info-lang description="The language of the text" {
|
||||
type string
|
||||
}
|
||||
}
|
||||
node "description" description="A description of the schema or the format it describes" {
|
||||
node description description="A description of the schema or the format it describes" {
|
||||
value description="The description text" {
|
||||
type "string"
|
||||
type string
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
prop ref=r#"[id="info-lang"]"#
|
||||
prop ref=#"[id="info-lang"]"#
|
||||
}
|
||||
node "author" description="Author of the schema" {
|
||||
value id="info-person-name" description="Person name" {
|
||||
type "string"
|
||||
node author description="Author of the schema" {
|
||||
value id=info-person-name description="Person name" {
|
||||
type string
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
prop "orcid" id="info-orcid" description="The ORCID of the person" {
|
||||
type "string"
|
||||
pattern r"\d{4}-\d{4}-\d{4}-\d{4}"
|
||||
prop orcid id=info-orcid description="The ORCID of the person" {
|
||||
type string
|
||||
pattern #"\d{4}-\d{4}-\d{4}-\d{4}"#
|
||||
}
|
||||
children {
|
||||
node ref=r#"[id="info-link"]"#
|
||||
node ref=#"[id="info-link"]"#
|
||||
}
|
||||
}
|
||||
node "contributor" description="Contributor to the schema" {
|
||||
value ref=r#"[id="info-person-name"]"#
|
||||
prop ref=r#"[id="info-orcid"]"#
|
||||
node contributor description="Contributor to the schema" {
|
||||
value ref=#"[id="info-person-name"]"#
|
||||
prop ref=#"[id="info-orcid"]"#
|
||||
children {
|
||||
node ref=#"[id="info-link"]"#
|
||||
}
|
||||
}
|
||||
node "link" id="info-link" description="Links to itself, and to sources describing it" {
|
||||
node link id=info-link description="Links to itself, and to sources describing it" {
|
||||
value description="A URL that the link points to" {
|
||||
type "string"
|
||||
format "url" "irl"
|
||||
type string
|
||||
format url irl
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
prop "rel" description="The relation between the current entity and the URL" {
|
||||
type "string"
|
||||
enum "self" "documentation"
|
||||
prop rel description="The relation between the current entity and the URL" {
|
||||
type string
|
||||
enum self documentation
|
||||
}
|
||||
prop ref=r#"[id="info-lang"]"#
|
||||
prop ref=#"[id="info-lang"]"#
|
||||
}
|
||||
node "license" description="The license(s) that the schema is licensed under" {
|
||||
node license description="The license(s) that the schema is licensed under" {
|
||||
value description="Name of the used license" {
|
||||
type "string"
|
||||
type string
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
prop "spdx" description="An SPDX license identifier" {
|
||||
type "string"
|
||||
prop spdx description="An SPDX license identifier" {
|
||||
type string
|
||||
}
|
||||
children {
|
||||
node ref=r#"[id="info-link"]"#
|
||||
node ref=#"[id="info-link"]"#
|
||||
}
|
||||
}
|
||||
node "published" description="When the schema was published" {
|
||||
node published description="When the schema was published" {
|
||||
value description="Publication date" {
|
||||
type "string"
|
||||
format "date"
|
||||
type string
|
||||
format date
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
prop "time" id="info-time" description="A time to accompany the date" {
|
||||
type "string"
|
||||
format "time"
|
||||
prop time id=info-time description="A time to accompany the date" {
|
||||
type string
|
||||
format time
|
||||
}
|
||||
}
|
||||
node "modified" description="When the schema was last modified" {
|
||||
node modified description="When the schema was last modified" {
|
||||
value description="Modification date" {
|
||||
type "string"
|
||||
format "date"
|
||||
type string
|
||||
format date
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
prop ref=r#"[id="info-time"]"#
|
||||
prop ref=#"[id="info-time"]"#
|
||||
}
|
||||
node "version" description="The version number of this version of the schema" {
|
||||
node version description="The version number of this version of the schema" {
|
||||
value description="Semver version number" {
|
||||
type "string"
|
||||
pattern r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
||||
type string
|
||||
pattern #"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"#
|
||||
min 1
|
||||
max 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node "tag" id="tag-node" description="A tag belonging to a child node of `document` or another node." {
|
||||
node tag id=tag-node description="A tag belonging to a child node of `document` or another node." {
|
||||
value description="The name of the tag. If a tag name is not supplied, the node rules apply to _all_ nodes belonging to the parent." {
|
||||
type "string"
|
||||
type string
|
||||
max 1
|
||||
}
|
||||
prop "description" description="A description of this node's purpose." {
|
||||
type "string"
|
||||
prop description description="A description of this node's purpose." {
|
||||
type string
|
||||
}
|
||||
prop "id" description="A globally-unique ID for this node." {
|
||||
type "string"
|
||||
prop id description="A globally-unique ID for this node." {
|
||||
type string
|
||||
}
|
||||
prop "ref" description="A globally unique reference to another node." {
|
||||
type "string"
|
||||
format "kdl-query"
|
||||
prop ref description="A globally unique reference to another node." {
|
||||
type string
|
||||
format kdl-query
|
||||
}
|
||||
children {
|
||||
node ref=r#"[id="node-names-node"]"#
|
||||
node ref=r#"[id="other-nodes-allowed-node"]"#
|
||||
node ref=r#"[id="node-node"]"#
|
||||
node ref=#"[id="node-names-node"]"#
|
||||
node ref=#"[id="other-nodes-allowed-node"]"#
|
||||
node ref=#"[id="node-node"]"#
|
||||
}
|
||||
}
|
||||
node "node" id="node-node" description="A child node belonging either to `document` or to another `node`. Nodes may be anonymous." {
|
||||
node node id=node-node description="A child node belonging either to `document` or to another `node`. Nodes may be anonymous." {
|
||||
value description="The name of the node. If a node name is not supplied, the node rules apply to _all_ nodes belonging to the parent." {
|
||||
type "string"
|
||||
type string
|
||||
max 1
|
||||
}
|
||||
prop "description" description="A description of this node's purpose." {
|
||||
type "string"
|
||||
prop description description="A description of this node's purpose." {
|
||||
type string
|
||||
}
|
||||
prop "id" description="A globally-unique ID for this node." {
|
||||
type "string"
|
||||
prop id description="A globally-unique ID for this node." {
|
||||
type string
|
||||
}
|
||||
prop "ref" description="A globally unique reference to another node." {
|
||||
type "string"
|
||||
format "kdl-query"
|
||||
prop ref description="A globally unique reference to another node." {
|
||||
type string
|
||||
format kdl-query
|
||||
}
|
||||
children {
|
||||
node "prop-names" description="Validations to apply specifically to arbitrary property names" {
|
||||
children ref=r#"[id="validations"]"#
|
||||
node prop-names description="Validations to apply specifically to arbitrary property names" {
|
||||
children ref=#"[id="validations"]"#
|
||||
}
|
||||
node "other-props-allowed" description="Whether to allow properties other than the ones explicitly listed. Defaults to 'false'." {
|
||||
node other-props-allowed description="Whether to allow properties other than the ones explicitly listed. Defaults to '#false'." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "boolean"
|
||||
type boolean
|
||||
}
|
||||
}
|
||||
node "min" description="minimum number of instances of this node in its parent's children." {
|
||||
node min description="minimum number of instances of this node in its parent's children." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node "max" description="maximum number of instances of this node in its parent's children." {
|
||||
node max description="maximum number of instances of this node in its parent's children." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node ref=r#"[id="value-tag-node"]"#
|
||||
node "prop" id="prop-node" description="A node property key/value pair." {
|
||||
node ref=#"[id="value-tag-node"]"#
|
||||
node prop id="prop-node" description="A node property key/value pair." {
|
||||
value description="The property key." {
|
||||
type "string"
|
||||
type string
|
||||
}
|
||||
prop "id" description="A globally-unique ID of this property." {
|
||||
type "string"
|
||||
prop id description="A globally-unique ID of this property." {
|
||||
type string
|
||||
}
|
||||
prop "ref" description="A globally unique reference to another property node." {
|
||||
type "string"
|
||||
format "kdl-query"
|
||||
prop ref description="A globally unique reference to another property node." {
|
||||
type string
|
||||
format kdl-query
|
||||
}
|
||||
prop "description" description="A description of this property's purpose." {
|
||||
type "string"
|
||||
prop description description="A description of this property's purpose." {
|
||||
type string
|
||||
}
|
||||
children description="Property-specific validations." {
|
||||
node "required" description="Whether this property is required if its parent is present." {
|
||||
node required description="Whether this property is required if its parent is present." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "boolean"
|
||||
type boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
children id="validations" description="General value validations." {
|
||||
node "tag" id="value-tag-node" description="The tags associated with this value" {
|
||||
children id=validations description="General value validations." {
|
||||
node tag id=value-tag-node description="The tags associated with this value" {
|
||||
max 1
|
||||
children ref="[id="validations"]"
|
||||
children ref=#"[id="validations"]"#
|
||||
}
|
||||
node "type" description="The type for this prop's value." {
|
||||
node type description="The type for this prop's value." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
type "string"
|
||||
type string
|
||||
}
|
||||
}
|
||||
node "enum" description="An enumeration of possible values" {
|
||||
node enum description="An enumeration of possible values" {
|
||||
max 1
|
||||
value description="Enumeration choices" {
|
||||
min 1
|
||||
}
|
||||
}
|
||||
node "pattern" description="PCRE (Regex) pattern or patterns to test prop values against." {
|
||||
node pattern description="PCRE (Regex) pattern or patterns to test prop values against." {
|
||||
value {
|
||||
min 1
|
||||
type "string"
|
||||
type string
|
||||
}
|
||||
}
|
||||
node "min-length" description="Minimum length of prop value, if it's a string." {
|
||||
node min-length description="Minimum length of prop value, if it's a string." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node "max-length" description="Maximum length of prop value, if it's a string." {
|
||||
node max-length description="Maximum length of prop value, if it's a string." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node "format" description="Intended data format." {
|
||||
node format description="Intended data format." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
type "string"
|
||||
type string
|
||||
// https://json-schema.org/understanding-json-schema/reference/string.html#format
|
||||
enum "date-time" "date" "time" "duration" "decimal" "currency" "country-2" "country-3" "country-subdivision" "email" "idn-email" "hostname" "idn-hostname" "ipv4" "ipv6" "url" "url-reference" "irl", "irl-reference" "url-template" "regex" "uuid" "kdl-query" "i8" "i16" "i32" "i64" "u8" "u16" "u32" "u64" "isize" "usize" "f32" "f64" "decimal64" "decimal128"
|
||||
enum date-time date time duration decimal currency country-2 country-3 country-subdivision email idn-email hostname idn-hostname ipv4 ipv6 url url-reference irl irl-reference url-template regex uuid kdl-query i8 i16 i32 i64 u8 u16 u32 u64 isize usize f32 f64 decimal64 decimal128
|
||||
}
|
||||
}
|
||||
node "%" description="Only used for numeric values. Constrains them to be multiples of the given number(s)" {
|
||||
node % description="Only used for numeric values. Constrains them to be multiples of the given number(s)" {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node ">" description="Only used for numeric values. Constrains them to be greater than the given number(s)" {
|
||||
node > description="Only used for numeric values. Constrains them to be greater than the given number(s)" {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node ">=" description="Only used for numeric values. Constrains them to be greater than or equal to the given number(s)" {
|
||||
|
|
@ -292,15 +295,15 @@ document {
|
|||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node "<" description="Only used for numeric values. Constrains them to be less than the given number(s)" {
|
||||
node < description="Only used for numeric values. Constrains them to be less than the given number(s)" {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node "<=" description="Only used for numeric values. Constrains them to be less than or equal to the given number(s)" {
|
||||
|
|
@ -308,64 +311,64 @@ document {
|
|||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node "value" id="value-node" description="one or more direct node values" {
|
||||
prop "id" description="A globally-unique ID of this value." {
|
||||
type "string"
|
||||
node value id=value-node description="one or more direct node values" {
|
||||
prop id description="A globally-unique ID of this value." {
|
||||
type string
|
||||
}
|
||||
prop "ref" description="A globally unique reference to another value node." {
|
||||
type "string"
|
||||
format "kdl-query"
|
||||
prop ref description="A globally unique reference to another value node." {
|
||||
type string
|
||||
format kdl-query
|
||||
}
|
||||
prop "description" description="A description of this property's purpose." {
|
||||
type "string"
|
||||
prop description description="A description of this property's purpose." {
|
||||
type string
|
||||
}
|
||||
children ref=r#"[id="validations"]"#
|
||||
children ref=#"[id="validations"]"#
|
||||
children description="Node value-specific validations" {
|
||||
node "min" description="minimum number of values for this node." {
|
||||
node min description="minimum number of values for this node." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
node "max" description="maximum number of values for this node." {
|
||||
node max description="maximum number of values for this node." {
|
||||
max 1
|
||||
value {
|
||||
min 1
|
||||
max 1
|
||||
type "number"
|
||||
type number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node "children" id="children-node" {
|
||||
prop "id" description="A globally-unique ID of this children node." {
|
||||
type "string"
|
||||
node children id=children-node {
|
||||
prop id description="A globally-unique ID of this children node." {
|
||||
type string
|
||||
}
|
||||
prop "ref" description="A globally unique reference to another children node." {
|
||||
type "string"
|
||||
format "kdl-query"
|
||||
prop ref description="A globally unique reference to another children node." {
|
||||
type string
|
||||
format kdl-query
|
||||
}
|
||||
prop "description" description="A description of this these children's purpose." {
|
||||
type "string"
|
||||
prop description description="A description of this these children's purpose." {
|
||||
type string
|
||||
}
|
||||
children ref=r#"[id="node-children"]"#
|
||||
children ref=#"[id="node-children"]"#
|
||||
}
|
||||
}
|
||||
}
|
||||
node "definitions" description="Definitions to reference in parts of the top-level nodes" {
|
||||
node definitions description="Definitions to reference in parts of the top-level nodes" {
|
||||
children {
|
||||
node ref=r#"[id="node-node"]"#
|
||||
node ref=r#"[id="value-node"]"#
|
||||
node ref=r#"[id="prop-node"]"#
|
||||
node ref=r#"[id="children-node"]"#
|
||||
node ref=r#"[id="tag-node"]"#
|
||||
node ref=#"[id="node-node"]"#
|
||||
node ref=#"[id="value-node"]"#
|
||||
node ref=#"[id="prop-node"]"#
|
||||
node ref=#"[id="children-node"]"#
|
||||
node ref=#"[id="tag-node"]"#
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
// Based on https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Clients/NuGet.CommandLine/NuGet.CommandLine.csproj
|
||||
Project {
|
||||
PropertyGroup {
|
||||
IsCommandLinePackage true
|
||||
IsCommandLinePackage #true
|
||||
}
|
||||
|
||||
Import Project=r"$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'README.md'))\build\common.props"
|
||||
Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"
|
||||
Import Project="ilmerge.props"
|
||||
Import Project=#"$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'README.md'))\build\common.props"#
|
||||
Import Project=Sdk.props Sdk=Microsoft.NET.Sdk
|
||||
Import Project=ilmerge.props
|
||||
|
||||
PropertyGroup {
|
||||
RootNamespace "NuGet.CommandLine"
|
||||
AssemblyName "NuGet"
|
||||
RootNamespace NuGet.CommandLine
|
||||
AssemblyName NuGet
|
||||
AssemblyTitle "NuGet Command Line"
|
||||
PackageId "NuGet.CommandLine"
|
||||
PackageId NuGet.CommandLine
|
||||
TargetFramework "$(NETFXTargetFramework)"
|
||||
GenerateDocumentationFile false
|
||||
GenerateDocumentationFile #false
|
||||
Description "NuGet Command Line Interface."
|
||||
ApplicationManifest "app.manifest"
|
||||
Shipping true
|
||||
OutputType "Exe"
|
||||
ComVisible false
|
||||
ApplicationManifest app.manifest
|
||||
Shipping #true
|
||||
OutputType Exe
|
||||
ComVisible #false
|
||||
// Pack properties
|
||||
PackProject true
|
||||
IncludeBuildOutput false
|
||||
PackProject #true
|
||||
IncludeBuildOutput #false
|
||||
TargetsForTfmSpecificContentInPackage "$(TargetsForTfmSpecificContentInPackage)" "CreateCommandlineNupkg"
|
||||
SuppressDependenciesWhenPacking true
|
||||
DevelopmentDependency true
|
||||
PackageRequireLicenseAcceptance false
|
||||
UsePublicApiAnalyzer false
|
||||
SuppressDependenciesWhenPacking #true
|
||||
DevelopmentDependency #true
|
||||
PackageRequireLicenseAcceptance #false
|
||||
UsePublicApiAnalyzer #false
|
||||
}
|
||||
|
||||
Target Name="CreateCommandlineNupkg" {
|
||||
Target Name=CreateCommandlineNupkg {
|
||||
ItemGroup {
|
||||
TfmSpecificPackageFile Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe" {
|
||||
TfmSpecificPackageFile Include=#"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe"# {
|
||||
PackagePath "tools/"
|
||||
}
|
||||
TfmSpecificPackageFile Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.pdb" {
|
||||
TfmSpecificPackageFile Include=#"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.pdb"# {
|
||||
PackagePath "tools/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemGroup Condition="$(DefineConstants.Contains(SIGNED_BUILD))" {
|
||||
AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
|
||||
AssemblyAttribute Include=System.Runtime.CompilerServices.InternalsVisibleTo {
|
||||
_Parameter1 "NuGet.CommandLine.FuncTest, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293"
|
||||
}
|
||||
AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
|
||||
|
|
@ -51,81 +51,81 @@ Project {
|
|||
}
|
||||
|
||||
ItemGroup Condition="!$(DefineConstants.Contains(SIGNED_BUILD))" {
|
||||
AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
|
||||
_Parameter1 "NuGet.CommandLine.FuncTest"
|
||||
AssemblyAttribute Include=System.Runtime.CompilerServices.InternalsVisibleTo {
|
||||
_Parameter1 NuGet.CommandLine.FuncTest
|
||||
}
|
||||
AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
|
||||
_Parameter1 "NuGet.CommandLine.Test"
|
||||
AssemblyAttribute Include=System.Runtime.CompilerServices.InternalsVisibleTo {
|
||||
_Parameter1 NuGet.CommandLine.Test
|
||||
}
|
||||
}
|
||||
|
||||
ItemGroup Condition="$(DefineConstants.Contains(SIGNED_BUILD))" {
|
||||
AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
|
||||
AssemblyAttribute Include=System.Runtime.CompilerServices.InternalsVisibleTo {
|
||||
_Parameter1 "NuGet.CommandLine.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293"
|
||||
}
|
||||
}
|
||||
|
||||
ItemGroup Condition="!$(DefineConstants.Contains(SIGNED_BUILD))" {
|
||||
AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
|
||||
_Parameter1 "NuGet.CommandLine.Test"
|
||||
AssemblyAttribute Include=System.Runtime.CompilerServices.InternalsVisibleTo {
|
||||
_Parameter1 NuGet.CommandLine.Test
|
||||
}
|
||||
}
|
||||
|
||||
ItemGroup {
|
||||
Reference Include="Microsoft.Build.Utilities.v4.0"
|
||||
Reference Include="Microsoft.CSharp"
|
||||
Reference Include="System"
|
||||
Reference Include="System.ComponentModel.Composition"
|
||||
Reference Include="System.ComponentModel.Composition.Registration"
|
||||
Reference Include="System.ComponentModel.DataAnnotations"
|
||||
Reference Include="System.IO.Compression"
|
||||
Reference Include="System.Net.Http"
|
||||
Reference Include="System.Xml"
|
||||
Reference Include="System.Xml.Linq"
|
||||
Reference Include="NuGet.Core" {
|
||||
HintPath r"$(SolutionPackagesFolder)nuget.core\2.14.0-rtm-832\lib\net40-Client\NuGet.Core.dll"
|
||||
Aliases "CoreV2"
|
||||
Reference Include=Microsoft.Build.Utilities.v4.0
|
||||
Reference Include=Microsoft.CSharp
|
||||
Reference Include=System
|
||||
Reference Include=System.ComponentModel.Composition
|
||||
Reference Include=System.ComponentModel.Composition.Registration
|
||||
Reference Include=System.ComponentModel.DataAnnotations
|
||||
Reference Include=System.IO.Compression
|
||||
Reference Include=System.Net.Http
|
||||
Reference Include=System.Xml
|
||||
Reference Include=System.Xml.Linq
|
||||
Reference Include=NuGet.Core {
|
||||
HintPath #"$(SolutionPackagesFolder)nuget.core\2.14.0-rtm-832\lib\net40-Client\NuGet.Core.dll"#
|
||||
Aliases CoreV2
|
||||
}
|
||||
}
|
||||
ItemGroup {
|
||||
PackageReference Include="Microsoft.VisualStudio.Setup.Configuration.Interop"
|
||||
ProjectReference Include=r"$(NuGetCoreSrcDirectory)NuGet.PackageManagement\NuGet.PackageManagement.csproj"
|
||||
ProjectReference Include=r"$(NuGetCoreSrcDirectory)NuGet.Build.Tasks\NuGet.Build.Tasks.csproj"
|
||||
PackageReference Include=Microsoft.VisualStudio.Setup.Configuration.Interop
|
||||
ProjectReference Include=#"$(NuGetCoreSrcDirectory)NuGet.PackageManagement\NuGet.PackageManagement.csproj"#
|
||||
ProjectReference Include=#"$(NuGetCoreSrcDirectory)NuGet.Build.Tasks\NuGet.Build.Tasks.csproj"#
|
||||
}
|
||||
|
||||
ItemGroup {
|
||||
EmbeddedResource Update="NuGetCommand.resx" {
|
||||
Generator "ResXFileCodeGenerator"
|
||||
LastGenOutput "NuGetCommand.Designer.cs"
|
||||
EmbeddedResource Update=NuGetCommand.resx {
|
||||
Generator ResXFileCodeGenerator
|
||||
LastGenOutput NuGetCommand.Designer.cs
|
||||
}
|
||||
Compile Update="NuGetCommand.Designer.cs" {
|
||||
DesignTime true
|
||||
AutoGen true
|
||||
DependentUpon "NuGetCommand.resx"
|
||||
Compile Update=NuGetCommand.Designer.cs {
|
||||
DesignTime #true
|
||||
AutoGen #true
|
||||
DependentUpon NuGetCommand.resx
|
||||
}
|
||||
EmbeddedResource Update="NuGetResources.resx" {
|
||||
EmbeddedResource Update=NuGetResources.resx {
|
||||
// Strings are shared by other projects, use public strings.
|
||||
Generator "PublicResXFileCodeGenerator"
|
||||
LastGenOutput "NuGetResources.Designer.cs"
|
||||
Generator PublicResXFileCodeGenerator
|
||||
LastGenOutput NuGetResources.Designer.cs
|
||||
}
|
||||
Compile Update="NuGetResources.Designer.cs" {
|
||||
DesignTime true
|
||||
AutoGen true
|
||||
DependentUpon "NuGetResources.resx"
|
||||
Compile Update=NuGetResources.Designer.cs {
|
||||
DesignTime #true
|
||||
AutoGen #true
|
||||
DependentUpon NuGetResources.resx
|
||||
}
|
||||
}
|
||||
|
||||
ItemGroup {
|
||||
EmbeddedResource Include=r"$(NuGetCoreSrcDirectory)NuGet.Build.Tasks\NuGet.targets" {
|
||||
Link "NuGet.targets"
|
||||
SubType "Designer"
|
||||
EmbeddedResource Include=#"$(NuGetCoreSrcDirectory)NuGet.Build.Tasks\NuGet.targets"# {
|
||||
Link NuGet.targets
|
||||
SubType Designer
|
||||
}
|
||||
}
|
||||
|
||||
// Since we are moving some code and strings from NuGet.CommandLine to NuGet.Commands, we opted to go through normal localization process (build .resources.dll) and then add them to the ILMerged nuget.exe
|
||||
// This will also be called from CI build, after assemblies are localized, since our test infra takes nuget.exe before Localization
|
||||
Target Name="ILMergeNuGetExe" \
|
||||
AfterTargets="Build" \
|
||||
Target Name=ILMergeNuGetExe \
|
||||
AfterTargets=Build \
|
||||
Condition="'$(BuildingInsideVisualStudio)' != 'true' and '$(SkipILMergeOfNuGetExe)' != 'true'" \
|
||||
{
|
||||
PropertyGroup {
|
||||
|
|
@ -133,9 +133,9 @@ Project {
|
|||
ExpectedLocalizedArtifactCount 0 Condition="'$(ExpectedLocalizedArtifactCount)' == ''"
|
||||
}
|
||||
ItemGroup {
|
||||
BuildArtifacts Include=r"$(OutputPath)\*.dll" Exclude="@(MergeExclude)"
|
||||
BuildArtifacts Include=#"$(OutputPath)\*.dll"# Exclude="@(MergeExclude)"
|
||||
// NuGet.exe needs all NuGet.Commands.resources.dll merged in
|
||||
LocalizedArtifacts Include=r"$(ArtifactsDirectory)\NuGet.Commands\**\$(NETFXTargetFramework)\**\*.resources.dll"
|
||||
LocalizedArtifacts Include=#"$(ArtifactsDirectory)\NuGet.Commands\**\$(NETFXTargetFramework)\**\*.resources.dll"#
|
||||
}
|
||||
Error Text="Build dependencies are inconsistent with mergeinclude specified in ilmerge.props" \
|
||||
Condition="'@(BuildArtifacts->Count())' != '@(MergeInclude->Count())'"
|
||||
|
|
@ -143,36 +143,36 @@ Project {
|
|||
Condition="'@(LocalizedArtifacts->Count())' != '$(ExpectedLocalizedArtifactCount)'"
|
||||
PropertyGroup {
|
||||
PathToBuiltNuGetExe "$(OutputPath)NuGet.exe"
|
||||
IlmergeCommand r"$(ILMergeExePath) /lib:$(OutputPath) /out:$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe @(MergeAllowDup -> '/allowdup:%(Identity)', ' ') /log:$(OutputPath)IlMergeLog.txt"
|
||||
IlmergeCommand #"$(ILMergeExePath) /lib:$(OutputPath) /out:$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe @(MergeAllowDup -> '/allowdup:%(Identity)', ' ') /log:$(OutputPath)IlMergeLog.txt"#
|
||||
IlmergeCommand Condition="Exists($(MS_PFX_PATH))" "$(IlmergeCommand) /delaysign /keyfile:$(MS_PFX_PATH)"
|
||||
// LocalizedArtifacts need fullpath, since there will be duplicate file names
|
||||
IlmergeCommand "$(IlmergeCommand) $(PathToBuiltNuGetExe) @(BuildArtifacts->'%(filename)%(extension)', ' ') @(LocalizedArtifacts->'%(fullpath)', ' ')"
|
||||
}
|
||||
MakeDir Directories="$(ArtifactsDirectory)$(VsixOutputDirName)"
|
||||
Exec Command="$(IlmergeCommand)" ContinueOnError="false"
|
||||
Exec Command="$(IlmergeCommand)" ContinueOnError=#false
|
||||
}
|
||||
|
||||
Import Project="$(BuildCommonDirectory)common.targets"
|
||||
Import Project="$(BuildCommonDirectory)embedinterop.targets"
|
||||
|
||||
// Do nothing. This basically strips away the framework assemblies from the resulting nuspec.
|
||||
Target Name="_GetFrameworkAssemblyReferences" DependsOnTargets="ResolveReferences"
|
||||
Target Name=_GetFrameworkAssemblyReferences DependsOnTargets=ResolveReferences
|
||||
|
||||
Target Name="GetSigningInputs" Returns="@(DllsToSign)" {
|
||||
Target Name=GetSigningInputs Returns="@(DllsToSign)" {
|
||||
ItemGroup {
|
||||
DllsToSign Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe" {
|
||||
StrongName "MsSharedLib72"
|
||||
Authenticode "Microsoft400"
|
||||
DllsToSign Include=#"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe"# {
|
||||
StrongName MsSharedLib72
|
||||
Authenticode Microsoft400
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Target Name="GetSymbolsToIndex" Returns="@(SymbolsToIndex)" {
|
||||
Target Name=GetSymbolsToIndex Returns="@(SymbolsToIndex)" {
|
||||
ItemGroup {
|
||||
SymbolsToIndex Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe"
|
||||
SymbolsToIndex Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.pdb"
|
||||
SymbolsToIndex Include=#"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe"#
|
||||
SymbolsToIndex Include=#"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.pdb"#
|
||||
}
|
||||
}
|
||||
|
||||
Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"
|
||||
Import Project=Sdk.targets Sdk=Microsoft.NET.Sdk
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
!doctype "html"
|
||||
html lang="en" {
|
||||
!doctype html
|
||||
html lang=en {
|
||||
head {
|
||||
meta charset="utf-8"
|
||||
meta name="viewport" content="width=device-width, initial-scale=1.0"
|
||||
meta charset=utf-8
|
||||
meta name=viewport content="width=device-width, initial-scale=1.0"
|
||||
meta \
|
||||
name="description" \
|
||||
name=description \
|
||||
content="kdl is a document language, mostly based on SDLang, with xml-like semantics that looks like you're invoking a bunch of CLI commands!"
|
||||
title "kdl - Kat's Document Language"
|
||||
link rel="stylesheet" href="/styles/global.css"
|
||||
title "kdl - The KDL Document Language"
|
||||
link rel=stylesheet href="/styles/global.css"
|
||||
}
|
||||
body {
|
||||
main {
|
||||
header class="py-10 bg-gray-300" {
|
||||
h1 class="text-4xl text-center" "kdl - Kat's Document Language"
|
||||
h1 class="text-4xl text-center" "kdl - The KDL Document Language"
|
||||
}
|
||||
section class="kdl-section" id="description" {
|
||||
section class=kdl-section id=description {
|
||||
p {
|
||||
- "kdl is a document language, mostly based on "
|
||||
a href="https://sdlang.org" "SDLang"
|
||||
|
|
@ -22,7 +22,7 @@ html lang="en" {
|
|||
}
|
||||
p "It's meant to be used both as a serialization format and a configuration language, and is relatively light on syntax compared to XML."
|
||||
}
|
||||
section class="kdl-section" id="design-and-discussion" {
|
||||
section class=kdl-section id=design-and-discussion {
|
||||
h2 "Design and Discussion"
|
||||
p {
|
||||
- "kdl is still extremely new, and discussion about the format should happen over on the "
|
||||
|
|
@ -32,11 +32,11 @@ html lang="en" {
|
|||
- " page in the Github repo. Feel free to jump in and give us your 2 cents!"
|
||||
}
|
||||
}
|
||||
section class="kdl-section" id="design-principles" {
|
||||
section class=kdl-section id=design-principles {
|
||||
h2 "Design Principles"
|
||||
ol {
|
||||
li "Maintainability"
|
||||
li "Flexibility"
|
||||
li Maintainability
|
||||
li Flexibility
|
||||
li "Cognitive simplicity and Learnability"
|
||||
li "Ease of de/serialization"
|
||||
li "Ease of implementation"
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
The `input` folder contains test cases for KDL parsers. The `expected_kdl`
|
||||
folder contains files with the same name as those in `input` with the expected
|
||||
output after being run through the parser and printed out again. If there's no
|
||||
file in `expected_kdl` with a name corresponding to one in `input` it
|
||||
indicates that parsing for that case should fail.
|
||||
output after being run through the parser and printed out again.
|
||||
|
||||
If a testcase is intended to fail parsing,
|
||||
the `input` file _MUST_ have a `_fail` suffix,
|
||||
and there must be no corresponding file in `expected_kdl`.
|
||||
|
||||
## Translation Rules
|
||||
|
||||
|
|
@ -53,9 +55,6 @@ If you think the disagreement is due to a genuine error or oversight in the
|
|||
KDL specification, please open an issue explaining the matter and the change
|
||||
will be considered for the next version of the KDL spec.
|
||||
|
||||
## Credit
|
||||
# Benchmarks
|
||||
|
||||
This test suite was extracted from
|
||||
[`kdl4j`](https://github.com/hkolbeck/kdl4j), the original Java
|
||||
implementation of KDL, with huge thanks to
|
||||
[@hkolbeck](https://github.com/hkolbeck) for authoring them!
|
||||
The `benchmarks` folder contains some large or gnarly documents intended to be used to stress-test your parser and help with profiling. They are intentionally not part of the testsuite, and just provided for your own personal benefit.
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
node "\"\\\/\b\f\n\r\t"
|
||||
node "\"\\\b\f\n\r\t "
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
node "arg" prop="val" {
|
||||
node arg prop=val {
|
||||
inner_node
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node "arg" arg="val"
|
||||
node arg arg=val
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
node a
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)#false
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)2.5
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)16
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)#null
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)str
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)str
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)#true
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)arg
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)0
|
||||
|
|
@ -1 +1 @@
|
|||
😁 "happy!"
|
||||
😁 happy!
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
node .
|
||||
|
|
@ -0,0 +1 @@
|
|||
node +
|
||||
|
|
@ -0,0 +1 @@
|
|||
node +.
|
||||
|
|
@ -1 +1 @@
|
|||
node 0b10
|
||||
node 2
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node 0b10
|
||||
node 2
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node 0b10
|
||||
node 2
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
node ("")10
|
||||
|
|
@ -0,0 +1 @@
|
|||
("")node
|
||||
|
|
@ -0,0 +1 @@
|
|||
node key=("")#true
|
||||
|
|
@ -1 +1 @@
|
|||
node "arg"
|
||||
node arg
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node "arg"
|
||||
node arg
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
node arg
|
||||
|
|
@ -1 +1 @@
|
|||
node false true
|
||||
node #false #true
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node prop1=true prop2=false
|
||||
node prop1=#true prop2=#false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
foo123 {
|
||||
bar
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
foo123<bar>foo weeee
|
||||
|
|
@ -0,0 +1 @@
|
|||
foo123,bar weeee
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)10
|
||||
|
|
@ -0,0 +1 @@
|
|||
(type)node
|
||||
|
|
@ -0,0 +1 @@
|
|||
node key=(type)10
|
||||
|
|
@ -0,0 +1 @@
|
|||
node (type)10
|
||||
|
|
@ -0,0 +1 @@
|
|||
(type)node
|
||||
|
|
@ -0,0 +1 @@
|
|||
node key=(type)10
|
||||
|
|
@ -1 +1 @@
|
|||
node "arg2"
|
||||
node arg2
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node "arg"
|
||||
node arg
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node "arg"
|
||||
node arg
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
node --
|
||||
|
|
@ -1 +1 @@
|
|||
node "😀"
|
||||
node 😀
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
node {
|
||||
}
|
||||
node
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
node {
|
||||
}
|
||||
node
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
node {
|
||||
}
|
||||
node
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
node {
|
||||
}
|
||||
node
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
node
|
||||
|
|
@ -1 +1 @@
|
|||
"" "arg"
|
||||
"" arg
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node ""="empty"
|
||||
node ""=empty
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
node
|
||||
|
|
@ -0,0 +1 @@
|
|||
node "12"
|
||||
|
|
@ -0,0 +1 @@
|
|||
node "Hello\n\tWorld" "Hello\n\tWorld" "Hello\n\tWorld" "Hello\n\tWorld" "Hello\n\tWorld"
|
||||
|
|
@ -1 +1 @@
|
|||
node "arg"
|
||||
node arg
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
node
|
||||
node
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
node
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
a
|
||||
b
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
parent {
|
||||
child
|
||||
child
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
node "arg" "arg2\n"
|
||||
node arg arg2
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
(type)node
|
||||
|
|
@ -0,0 +1 @@
|
|||
node
|
||||
|
|
@ -0,0 +1 @@
|
|||
false_id
|
||||
|
|
@ -0,0 +1 @@
|
|||
node false_id=1
|
||||
|
|
@ -0,0 +1 @@
|
|||
floats #inf #-inf #nan
|
||||
|
|
@ -1 +1 @@
|
|||
node 0xabcdef1234567890
|
||||
node 12379813812177893520
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node 0xabcdef0123456789abcdef
|
||||
node 207698809136909011942886895
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node 0xabcdef0123
|
||||
node 737894400291
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node 0x1
|
||||
node 1
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
another-node
|
||||
|
|
@ -1 +1 @@
|
|||
node 0b1
|
||||
node 1
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node 0o1
|
||||
node 1
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node "arg"
|
||||
node arg
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
node "arg1" "arg2"
|
||||
node arg1 arg2
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue