Conformance

250 implementation-independent test cases. The TypeScript implementation is checked against them today; future implementations can use the same corpus.

One of the standing criticisms of YAML is that different implementations routinely disagree with each other on ambiguous edge cases. Lima addresses this directly: a single, implementation-independent conformance corpus , rather than tests written informally by each implementation’s own author. The TypeScript implementation is checked against it today; the corpus is designed so any future implementation — Rust or otherwise — can be checked against the exact same cases.

bun run run

What’s in it

  • 250 cases , split between Lima Core and the References Extension, count pinned by a test so it can’t silently drift.
  • Every case has exactly one expectation kind: a successful result, a thrown error, or warnings alongside a result.
  • Case IDs are permanently stable and follow a fixed scheme:
core.numbers.safe-integer.maximum
core.strings.unknown-escape.strict
references.phases.forward-reference.phase-2
references.interpolation.float.exponent-threshold

Renaming an ID is treated as a corpus change, not a cosmetic edit.

Why JSON, not YAML or Lima itself

The corpus format had to avoid two traps: describing tests in YAML would introduce YAML-native type semantics into what’s supposed to be a language-neutral contract, and describing them in Lima would be circular — the parser under test would be loading its own test definitions. JSON describes the test contract; the Lima input itself stays verbatim, either inline as a JSON string or, when that would hurt readability, in a .lima sidecar file.

Comparing by code, not by message text

Lima exposes a small, structured error API, and the corpus compares against it — not against full English error strings, which would tie every implementation to the exact wording of a TypeScript-first reference implementation.

type LimaDiagnosticCode =
  | "INVALID_ESCAPE"
  | "INVALID_QUOTE"
  | "INVALID_DATE"
  | "INVALID_NUMBER"
  | "INVALID_REFERENCE_SHAPE"
  | "INVALID_INDENTATION"
  | "INVALID_FLOW_SYNTAX"
  | "DUPLICATE_KEY"
  | "RESOURCE_LIMIT"
  | "UNRESOLVED_REFERENCE"
  | "INVALID_INTERPOLATION"
  | "INVALID_PARTIAL"

A diagnostic case compares the fields that are actually normative — code, line, and where relevant column, token, key, partial, path — never the prose message.

Language-neutral values

Host-language values that JSON can’t natively express — a UTC instant, NaN, -0, an intentionally invalid or out-of-range date — are represented as typed corpus values (e.g. { "$type": "instant", "value": "2024-03-01T09:00:00Z" } ) that each runner materializes into its own native representation. This keeps a boundary case reproducible across runners instead of depending on how, say, JavaScript’s Date and a Rust date crate each happen to parse an arbitrary string.

Boundary values are generated, not hand-typed

Cases near a resource limit (§9 of Core, §6.2 of References) are produced by small, deterministic generators rather than pasted in by hand — a 16,385-code-point scalar or a 4,096-node partial is exactly the kind of input nobody should be hand-writing into a fixture file. Every generator is documented and reproducible across runners; generator semantics are part of the corpus contract itself.

Where this is going

The corpus’s normative basis is Lima Core 1.0 and Lima References 1.0. The first implementation checked against it is TypeScript ( @limaformat/lima ); the explicit long-term goal is the same 250 cases validating the Rust implementation — and any future one — against exactly the same expectations. See Packages for current implementation status.

The full corpus, its schema, and the coverage matrix live in the repository: corpus/ , design rationale in docs/corpus-design/.