LEPTRIS

libyeptris v0.6.22 — YAML from C

A YAML 1.2 parser, writer and streamer in pure C11 — zero required runtime dependencies, a stable ABI, opaque handles. The conformance bar is the yaml-test-suite (395/395); a libyaml/Psych-compatible YAML 1.1 lenient mode covers the real world.

Build and test

from a checkout
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release   # LTO on by default
cmake --build build
ctest --test-dir build --output-on-failure

scripts/validate.sh is the project’s pre-completion gate: a clean warnings-as-errors build, the full test suite, a CLI smoke, and a leak check. Sanitizer presets exist alongside the default build dir — build-asan, build-ubsan,build-tsan — and a nightly CI lane runs libFuzzer.

Install and link

The install rule ships the public headers (one canonical types header behind yeptris/), the compiled library, and pkg-config + CMake package metadata:

cmake --install
cmake --install build --prefix /usr/local
# headers:   /usr/local/include/yeptris/*.h
# library:   /usr/local/lib/libyeptris.{a,dylib,so}
pkg-config consumers
cc my.c $(pkg-config --cflags --libs yeptris)

The API shape

parse, walk, emit
#include <yeptris/yeptris.h>

YeptrisStatus st;
YeptrisDocument doc = yeptris_parse(data, len, &st);
if (!doc) {
    uint32_t line, col;
    fprintf(stderr, "%s at %u:%u\n",
            yeptris_last_error(&line, &col), line, col);  /* 1-based */
    return 1;
}
size_t n = yeptris_document_count(doc);
YeptrisNode root = yeptris_document_root(doc, 0);

if (yeptris_node_kind(root) == YEPTRIS_NODE_MAPPING) {
    YeptrisNode port = yeptris_node_map_get(root, "port", 4);
    size_t vlen;
    const char *val = yeptris_node_value(port, &vlen);  /* zero-copy */
}

char *out = yeptris_serialize(doc, &(size_t){0});  /* canonical YAML */
yeptris_document_free(doc);   /* ONE call frees everything; free(out) too */

Two schemas, one engine

yeptris_parse applies the YAML 1.2 core schema. The options form switches to the libyaml/Psych-compatible YAML 1.1 lenient mode — yes is a boolean, 017 is octal, sexagesimal integers resolve — for reading the world’s existing YAML:

yeptris_parse_ex
#include <yeptris/parse.h>

yeptris_parse_options opts = {0};
opts.schema = YEPTRIS_SCHEMA_11_COMPAT;   /* libyaml/Psych implicit typing */
YeptrisDocument doc = yeptris_parse_ex(data, len, &opts, &st);

Three consumption models, one engine

The same parse engine feeds three consumption shapes — pick per workload, not per library:

  • DOM — the opaque node handles above; O(1) indexed child access, interned mapping keys.
  • Events — libyaml-compatible push events (libyaml test drivers port directly) and a StAX-style pull feed with zero C→host callbacks.
  • The record tape — one packed 8-byte record per token in a single arena; bulk-drain it in O(1) FFI calls. The binding seam the Ruby and Python packages ride.

This is the curated guide. The repositories are canonical: when this page and the repo disagree, the repo wins. Full documentation lives with the source.