Commit Graph

14 Commits

Author SHA1 Message Date
ae85ec97c9 fix(emitting): quote every string the parser reads as another type
The emitter kept its own list of reserved words and number shapes,
apart from the parser's, and the two disagreed: the string yes was
written plain and read back as a boolean, as were 0x1F, 1_000 and
.inf. The parser's reading of a plain scalar is now one type that
both use, and the emitter quotes what does not read back as a string.

Also quoted: the merge key, the document end marker, and a string
with a CR+LF break, which was written raw.
2026-10-28 23:32:40 +01:00
2fcf54684e fix: correct emitter and tagged round-trip and validation bugs
Seven correctness fixes to the recently landed anchors/styles, canonical,
timestamp/binary, and full-node-key work, each reproduced and covered by a
regression test.

- Stream dump doubled the `---` marker under explicitDocumentStart or
  canonical: the single-document body now renders marker-free and the
  stream path owns marker emission, so a two-document stream reparses as
  two documents, not four.
- Tagged flow-mapping keys used block-context safety, so a key like `a,b`
  emitted bare and reparsed as several entries; flow keys now use the
  flow-aware plain-scalar check.
- Plain scalars ending in `:` reparse as an empty-valued mapping key, so
  both the model and tagged emit paths now fall back to quoting them.
- An alias mapping key pointing at a defined non-scalar anchor raised the
  misleading undefinedAlias; it now raises expectedScalarKey, and a truly
  undefined alias key still raises undefinedAlias.
- Tagged duplicate-key detection keyed on source spelling: it now keys on
  the resolved scalar value plus effective tag, so 0x1 and 1 collide while
  1 and "1" do not and custom-tagged keys stay distinct.
- Dumping a tree nested past the emitter limit overflowed the stack.

- Timestamp now accepts a leap-second value of 60, counted as 60 elapsed
  seconds in the epoch math.
- Tagged.Dumper honors sortKeys in its block and canonical paths for
  all-plain-string-key mappings, matching the model dumper.

Nesting-guard design: dump is public and non-throwing across 90+ call
sites, so making it throwing would be a breaking change. A tree nested
deeper than the parser's own maxNestingDepth cannot round-trip through
PureYAML (the parser rejects it), so no faithful emission exists. Each
dump entry point now measures depth iteratively (no recursion, so the
guard itself cannot overflow) and, past the limit, returns a documented,
reparseable marker document that resolves to null. Under the limit the
existing recursive renderers run byte-identically, so parsed trees and
every realistic hand-built tree are unaffected. This mirrors the parser's
enforceNestingLimit, which bounds depth before any recursive consumer.
2026-07-16 16:29:08 +02:00
8df3f0b8d4 feat(emitting): add canonical mode, document markers, and lineWidth
Completes issue #10. Canonical mode follows libyaml's canonical layout
on both the Model and Tagged dump paths (tags explicit, scalars double
quoted, flow one entry per line, anchors and aliases preserved on the
Tagged path). explicitDocumentStart/End wrap the body in --- and ...
markers. lineWidth wraps overflowing flow collections one entry per
line and folds plain block scalars at single-space boundaries, vetoing
any fold that would manufacture syntax, so values always reparse
unchanged.

Closes #10
2026-07-12 14:13:19 +02:00
ec3642bacc feat(emitting): add indentWidth and sortKeys emitter options
Closes two of the four emitter gaps a feature-parity audit against Yams
found (PureYAMLYamsComparison's FEATURE-PARITY.md): no configurable
indentation and no sorted-key output.

indentWidth replaces every hardcoded indent + 2 in Dumper.swift/
Dumper.Mapping.swift, verified with round-trip tests since indentation is
purely a rendering choice and must never change what a document means.

sortKeys sorts a mapping's pairs by their string key in both block and flow
style, but only when every key in that mapping is a plain string; a mapping
with any complex (mapping/sequence) key stays in insertion order, since
there is no well-defined ordering across mixed key kinds. Off by default,
since insertion order is what makes a duplicate-key-preserving Mapping
round-trip meaningfully in the first place.

width-based line wrapping and canonical mode are each a correctness surface
of their own and are deliberately left for later, not folded into this.
2026-07-10 17:49:44 +02:00
318913baeb fix(emitting): escape CRLF per unicode scalar in double-quoted output
A CR+LF pair forms a single Swift extended grapheme cluster, so quote()'s Character-level switch matched neither '\\r' nor '\\n' and emitted the line break unescaped. The folding parser then read the dumped value back differently, breaking parse/dump/parse stability for any string containing CRLF (surfaced as a round-trip failure on the Zoom OpenAPI corpus seed under PUREYAML_RUN_FULL_CORPUS). Iterating over unicode scalars escapes CR and LF independently. Adds a dumper regression test.
2026-06-23 22:23:40 +02:00
208b892019 feat(parser): harden diagnostics and streaming
Add structured validation diagnostics, JSON and Yams comparison gates, and lazy token/event parsing so production behavior is measured without retaining full intermediate parser arrays.
2026-06-06 07:57:48 +02:00
13183a4353 Add machine-readable validation reports 2026-06-05 20:39:32 +02:00
906f0c454d feat(emitting): dump YAML streams 2026-06-05 17:14:41 +02:00
697b9cc519 feat(model): support complex mapping keys 2026-06-05 16:08:45 +02:00
92b0d7c92d feat(emitting): broaden literal block fixtures 2026-06-05 13:37:33 +02:00
5fe9c1b8d1 feat(emitting): add flow collection output 2026-06-05 11:09:58 +02:00
6197c23e6f feat(emitting): add literal block scalar output 2026-06-05 11:06:19 +02:00
35fb919b74 feat(emitting): add scalar output options 2026-06-05 10:40:28 +02:00
9c718e3303 feat(package): bootstrap pure swift yaml package 2026-06-05 06:34:20 +02:00