# Text::KDL::XS A fast Perl XS binding to [ckdl](https://github.com/tjol/ckdl) for parsing and emitting [KDL](https://kdl.dev) documents. Supports KDL 2.0.0 and the legacy KDL 1.0.0 with automatic version detection. The full documentation is in the POD: `perldoc Text::KDL::XS` for the API reference and `perldoc Text::KDL::XS::Cookbook` for a tour of every KDL feature with Perl examples. This file is the short version. ## What is KDL? KDL ("cuddle", the **K**DL **D**ocument **L**anguage) is a small configuration and data language. A document is a tree of *nodes*; each node has a name, optional *arguments*, optional *properties* (`key=value`) and an optional block of child nodes: ```kdl package "kdl-rs" { version "0.4.0" author "Kat Marchán" email="kat@example.com" keywords "config" "data" "structured" license "MIT" { url "https://opensource.org/licenses/MIT" } } ``` Values are strings, numbers, `#true`/`#false`, `#null` or the special numbers `#inf`, `#-inf` and `#nan`, and any value or node can carry a `(type)` annotation. Comments, multi-line and raw strings, hexadecimal/octal/binary numbers and `/-` "slashdash" comments round out the language. The [Cookbook](https://metacpan.org/pod/Text::KDL::XS::Cookbook) shows all of them. ## Synopsis ```perl use Text::KDL::XS qw(parse_kdl emit_kdl); my $doc = parse_kdl(<<'KDL'); server "web-1" port=8080 { tls #true upstream name="app" weight=3 } KDL for my $node (@{ $doc->nodes }) { print $node->name, "\n"; # server print $node->args->[0]->as_string, "\n"; # web-1 print $node->prop('port')->as_number, "\n"; # 8080 for my $child (@{ $node->children }) { print " ", $child->name, "\n"; # tls, upstream } } print emit_kdl($doc); # round trip print emit_kdl({ # plain data server => { host => 'localhost', port => 8080 }, tags => [ 'a', 'b' ], }); # server { # host localhost # port 8080 # } # tags a b ``` `parse_kdl` accepts a Perl character string, a filehandle (any PerlIO layer), or a code reference returning chunks of UTF-8. `emit_kdl` accepts a parsed document, a node, a list of nodes, or plain hashes and arrays, and returns a character string, so `parse_kdl(emit_kdl($data))` round-trips. For SAX-style streaming without building a tree: ```perl use Text::KDL::XS::Parser; open my $fh, '<', 'huge.kdl' or die $!; my $p = Text::KDL::XS::Parser->new($fh); while (my $ev = $p->next_event) { print $ev->{name}, "\n" if $ev->{event} eq 'start_node'; } ``` ## Modules | Module | Role | |------------------------------------------------------------|--------------------------------------------------------| | [`Text::KDL::XS`](https://metacpan.org/pod/Text::KDL::XS) | `parse_kdl`, `emit_kdl`, options, encoding, errors | | [`Text::KDL::XS::Cookbook`](https://metacpan.org/pod/Text::KDL::XS::Cookbook) | Every KDL feature with KDL and Perl examples; recipes | | [`Text::KDL::XS::Parser`](https://metacpan.org/pod/Text::KDL::XS::Parser) | Streaming event parser | | [`Text::KDL::XS::Document`](https://metacpan.org/pod/Text::KDL::XS::Document) | Container for the top-level nodes | | [`Text::KDL::XS::Node`](https://metacpan.org/pod/Text::KDL::XS::Node) | A node: name, type annotation, args, props, children | | [`Text::KDL::XS::Value`](https://metacpan.org/pod/Text::KDL::XS::Value) | A typed value: null, bool, number, string | | [`Text::KDL::XS::Emitter`](https://metacpan.org/pod/Text::KDL::XS::Emitter) | Internal emitter helpers | ## Features - Tree API (`parse_kdl` / `emit_kdl`) with faithful round-tripping of argument order, property order, type annotations and number kinds. Floats are written with the shortest text that reads back as the same float, at the width of the perl's floats; integers exactly over the whole 64-bit signed and unsigned range. - Streaming event API for memory-bounded processing of large documents, with optional reporting of comments (including their text) and slashdashed elements. - Sources: strings, filehandles with any PerlIO layer, IO objects, code references. - Complete value model: distinct null and booleans, integers, floats, arbitrary-precision numbers kept as text (with exact `Math::BigInt` / `Math::BigFloat` conversion), `#inf`/`#-inf`/`#nan`. - KDL 1.0.0 and 2.0.0, detected automatically or pinned with `version => '1' | '2'`; emit in either version. - Plain-Perl data emission for the "just write my config" case. - Safe on untrusted input: strict UTF-8 validation, a nesting limit (`max_depth`, 512 by default), errors with the parser's reason reported at the caller's line, and an emitter that only writes valid KDL. - Passes the upstream KDL test suites for both versions (the only textual differences are the spelling of floats and repeated properties). ## Known issues The remaining limitations come from the underlying ckdl library: parse errors carry a reason but no line or column, detection mode is not a complete KDL v1 parser, a few lenient spots in ckdl's parser (such as `\u{}` escapes without digits), and a small memory leak in ckdl for every parsed property (about 32 bytes, relevant only to long-running processes that parse many documents). Strings that would be written bare but read back as keywords or numbers (`true`, `-1`) make `emit_kdl` quote the whole document. All of them are listed in `perldoc Text::KDL::XS`, section "KNOWN ISSUES AND LIMITATIONS". Upgrading from 0.001: a string passed to `parse_kdl` is now read as Perl characters. Code that passed UTF-8 byte strings must decode them first (`utf8::decode`, `Encode::decode`) or pass the filehandle instead; see `Changes` for the complete list. ## Installation ```sh perl Makefile.PL make make test make install ``` The ckdl sources are bundled in the `ckdl` directory and compiled into the module, so no system package and no Alien module is needed. A C11 compiler and a perl 5.30 or newer with 64-bit integers are. `ckdl/UPSTREAM.md` names the bundled upstream commit. ## Status Version 0.003 bundles the ckdl sources and drops the Alien::ckdl dependency; the API is unchanged. Version 0.002 fixed the defects found in 0.001 (see `Changes`; what remains are the upstream limitations above). The API (`parse_kdl`, `emit_kdl`, `Parser`, `Document`, `Node`, `Value`) is stable. ## See also - [KDL specification](https://github.com/kdl-org/kdl) and [kdl.dev](https://kdl.dev) - [ckdl](https://github.com/tjol/ckdl), the underlying C library ## License Copyright (C) 2026 Davenonymous. This Perl distribution is released under the same terms as Perl itself. The bundled `ckdl` library is MIT-licensed; see `ckdl/COPYING`.