Kdl
the kdl document language specifications
Install / Use
/learn @kdl-org/KdlREADME
The KDL Document Language
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:
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.
There's a living specification, as well as various implementations. You can also check out the FAQ to answer all your burning questions!
The current version of the KDL spec is KDL 2.0.0. For legacy KDL, please refer to the KDL 1.0.0 spec. All users are encouraged to migrate. Migration is forward-and-backward-compatible and safe, and can be automated.
In addition to a spec for KDL itself, there are specifications for a KDL Query Language based on CSS selectors, and a KDL Schema Language loosely based on JSON Schema.
The language is based on SDLang, with a number of modifications and clarifications on its syntax and behavior. We are grateful for their work as an inspiration to ours.
Design and Discussion
KDL 2.0.0 has been finalized, and no further changes are expected. For questions about KDL and discussions, please see the discussions page. For minor editorial fixes or critical spec errata, please feel free to file an issue.
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 - Terminal workspace/multiplexer
- Niri - Scrollable-tiling window manager for Wayland
- Bikeshed (here and here) - Specification pre-processor used by CSS, C++, WHATWG, various W3C working groups, and others.
- orogene - Lightning-fast JavaScript package manager
- Onyx - An efficient, procedural, and pragmatic programming language that compiles to WASM. Used for package manifests.
- Pop!_OS/System76 Scheduler - Scheduling service which optimizes Linux's CPU scheduler and makes it go faster.
- ImStyle - ImGui application styling with Nim and KDL
- fmod-rs - Rust bindings to FMOD Core and FMOD Studio
- mise - dev tools, env vars, task runner
- Camping - Ruby web microframework
- Iron Vault - VTT (Virtual Tabletop) plugin for Obsidian for the Ironsworn family of games
- Microsoft TypeScript DOM Generator - Tool for generating DOM-related TypeScript and JavaScript library files
- Ferron - A fast, memory-safe web server written in Rust
- You?
Implementations
[!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 | ✅ | ✅ | | | C#/.NET | Kadlet | ✅ | ✖️ | | | C#/.NET | KadSharp | ✅ | ✅ | .NET Std: 2.1+, .NET 6+, .NET FW 4.7.2+, Mono, Xamarin | | C++ | kdlpp | ✅ | ✅ | part of ckdl, requires C++20 | | Common Lisp | kdlcl | ✅ | ✖️ | | | Crystal | kdl-cr | ✅ | ✖️ | | | Dart | kdl-dart | ✅ | ✅ | | | Elixir | kuddle | ✅ | ✅ | | | Go | gokdl | ✅ | ✖️ | | | Go | kdl-go | ✅ | ✖️ | | | Haskell | Hustle | ✅ | ✖️ | | | Haskell | kdl-hs | ✅ | ✅ | Format/comment-preserving parser | | Java | kdl4j | ✅ | ✅ | | | JavaScript | @bgotink/kdl | ✅ | ✅ | Format/comment-preserving parser | | JavaScript | @virtualstate/kdl | ✅ | ✖️ | query only, JSX based | | JavaScript | kdljs | ✅ | ✅ | | | Lua | kdlua | ✅ | ✖️ | | | Nim | kdl-nim | ✅ | ✖️ | | | OCaml | ocaml-kdl | ✅ | ✅ | | | PHP | kdl-php | ✅ | ✖️ | | | Python | ckdl | ✅ | ✅ | | | Python | cuddle | ✅ | ✖️ | | | Python | kdl-py | ✅ | ✅ | | | Ruby | kdl-rb | ✅ | ✅ | | | Rust | kdl-rs | ✅ | ✅ | Format/comment-preserving parser | | Rust | knus | ✅ | ✖️ | Serde-style derive macros (not actual Serde) | | Swift | kdl-swift | ✅ | ✖️ | | | XSLT | xml2kdl | ✅ | ✖️ | | | Zig | zig-kdl | ✅ | ✅ | Format/comment-preserving parser |
Compatibility Test Suite
There is a compatibility test suite available for KDL implementors to check that their implementations are actually spec-compatible.
The implementations above are not guaranteed to pass this test suite in its entirety, but in the future, may be required to in order to be included here.
Editor Support
- Intellij IDEA
- Sublime Text*
- TreeSitter (neovim, among others)
- VS Code*
- vim
- Kate*
- Zed
* Supports KDL 2.0.0
Overview
Basics
A KDL node is a node name string, followed by zero or more "arguments", and children.
title "Hello, World"
You can also have multiple values in a single node!
bookmarks 12 15 188 1234
Nodes can have properties, with string keys.
author "Alex Monad" email=alex@example.com active=#true
And they can have nested child nodes, too!
contents {
section "First section" {
paragraph "This is the first paragraph"
paragraph "This is the second paragraph"
}
}
Nodes without children are terminated by a newline, a semicolon, or the end of a file stream:
node1; node2; node3
Values
KDL supports 4 data types:
- Strings:
unquoted,"hello world", or#"hello world"# - Numbers:
123.45,0xdeadbeef,#inf,#-inf,#nan - Booleans:
#trueand#false - Null:
#null
Strings
It supports three different formats for string input: unquoted, quoted, and raw.
node1 this-is-a-string
node2 "this\nhas\tescapes"
node3 #"C:\Users\zkat\raw\string"#
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, ornan. - 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:
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:
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:
other-raw ##"hello#"world"##
Numbers
There are 4 ways t
