Skip to content

Zig 0.16#1

Merged
rjosephwright merged 26 commits into
mainfrom
zig-0.16
May 2, 2026
Merged

Zig 0.16#1
rjosephwright merged 26 commits into
mainfrom
zig-0.16

Conversation

@rjosephwright

Copy link
Copy Markdown
Contributor

No description provided.

Migrate std usage to the new APIs introduced in 0.16.
- Replace ArrayListUnmanaged empty-init braces with .empty.
- Switch the encoder output buffer to std.Io.Writer.Allocating.
- Pass std.Io to the file and directory operations in tests.
- Bump zest test runner to its 0.16-compatible commit.
Diagnostics is the conventional name for attached source-position info
on a parse failure. Adds ParseFromValueError, the concrete error set
returned from value-tree decoding paths landing in subsequent commits.
Decodes directly into the caller's allocator without wrapping in
Parsed(T). The intended pairing is an ArenaAllocator passed by the
caller, which gets the same all-or-nothing cleanup as Parsed without
the double indirection.

Factors out parseToNode as the shared scan/parse step between the
two entry points.
Renames variants for std.json parity: boolean->bool, sequence->array,
mapping->object. Array is std.ArrayList(Value); ObjectMap is
std.array_hash_map.Custom keyed by Value, preserving YAML's ability
to use any node as a mapping key (integers, floats, sequences, etc.)
while exposing the std.json-style put/get/iterator interface.

A ValueContext supplies hash and eql for the map; eql is the same
recursive equality previously on Value. Adds an objectGet convenience
method for string-keyed lookup, replacing the old mappingGet.

The decoder, encoder, and tests are all reworked to construct values
through put/append rather than parallel-slice literals.
Decodes a Value tree into a typed T without re-parsing source bytes.
The wrapped form returns Parsed(T) whose arena owns deep-cloned strings
and containers, so the source Value can be released independently. The
leaky form decodes straight into the caller's allocator.

Includes a yamlParseFromValue hook hatch on user types, enabled via
the comptime hasYamlParseFromValue check (parallel to the existing
yamlParse path used by parseFromSlice).
The free functions stringify and stringifyAlloc become Stringify.value
and Stringify.valueAlloc; StringifyOptions becomes Stringify.Options.
yaml.zig drops the free re-exports and pulls Stringify in directly.

This commit is just the rename. The follow-up turns Stringify into a
struct (writer, options, indent_level fields) with the streaming API
methods (beginMapping, mappingField, beginSequence, write, etc.)
needed for yamlStringify hooks. The encodeToNode stub is also dropped
here.
Stringify gains writer/options/indent_level fields, a container stack,
beginObject/endObject/objectField, beginArray/endArray, and write.

Also removes writeSliceTop (unused) and testEncodeWithOptions (a
duplicate of testEncodeOpts).
The writeValue family of helpers used writer: anytype as a holdover
from before std.Io.Writer became a concrete type. Every caller passes
*std.Io.Writer now, so make the type explicit.
The container kind stack is now 1 bit per level (8 bytes for 64
levels) instead of one Container enum per level. endObject and endArray
now read the parent container after popping so the state machine
returns to .in_object or .in_array as appropriate, rather than
unconditionally setting the state from the closed container's kind.
The new third argument matches the shape yamlParseFromValue already
uses and lets hooks forward parser settings into their own decodeNode
calls.
When T declares yamlStringify(self, *Stringify), writeValue builds a
Stringify positioned at the current depth and hands control to the
hook. Replaces the old stub yamlStringify(self, Allocator) !Node
shape, which was never called. Drops the unused Node import.
A custom type with yamlParse, yamlParseFromValue, and yamlStringify
round-trips through the slice, value, and stringify paths. Confirms
the three hook entry points dispatch correctly together on one type.
Frees the ParseError name for the umbrella error matching std.json's
role. ParseFromValueError keeps its existing name and shape;
ParseError is an alias since yaml-zig has no streaming Source variant
that would change the set.
Mirrors std.json's static.zig location for the typed-decoding facade.
yaml.zig re-exports the four functions (parseFromSlice,
parseFromSliceLeaky, parseFromValue, parseFromValueLeaky) directly
from static.zig. decode.zig keeps the type walking; AnchorMap,
parseToNode, decodeNodeInternal, and decodeValueInternal become
public so static.zig can call them.
Custom yamlParse hooks call innerParse to recurse on a sub-node.
Custom yamlParseFromValue hooks call innerParseFromValue to recurse
on a sub-value. Both are re-exported from yaml.zig.
ignore_unknown_fields now defaults to false. duplicate_field_behavior
is new with default .@"error" so a YAML mapping that names the same
key twice is rejected unless the caller opts into .use_first or
.use_last. The existing line-based dedup helper now supports both
keep-the-first and keep-the-last semantics.

A few existing tests relied on the old lenient defaults; they now
opt in via testDecodeLenient or testDecodeUseLast helpers. New tests
cover all three duplicate behaviors.
The Value type now declares the same four methods that other types
can declare to opt into custom parsing and serialization. dump
writes a YAML rendering to a writer. yamlParse builds a Value from
an AST node. yamlParseFromValue clones a Value. yamlStringify
emits a Value through the streaming Stringify writer.

The encoder and decoder still have direct if (T == Value) shortcuts
that run before the hook check. Removing those is remaining work,
which needs new tests first to prove the tag-aware paths still work.

Makes writeValueUnion, writeFlowValueUnion, decodeToValue, and
cloneValue public so the Value methods can call them.
These five paths (!!binary, !!bool, !!float, !!null, !!str into a
Value) were previously uncovered. Locking down current behavior
before reorganizing the tag handling.
The decoder no longer has if (T == Value) shortcuts in its main
dispatch paths. The encoder no longer special-cases Value in its
union arms. Value satisfies the same hook protocol that user types
use, so the same dispatch logic handles it.

Tag-aware decoding into Value (!!binary, !!bool, !!float, !!null,
!!str) moves into decodeToValue's tag arm, which is what
Value.yamlParse calls.

Fixes hasYamlParse / hasYamlParseFromValue / hasYamlStringify so
they recognize unions as well as structs. Without that change the
hooks would not fire for Value at all.
print emits a formatted scalar through the streaming Stringify
state machine. Use it inside a yamlStringify hook when a type
wants to render itself with std.fmt-style formatting rather than
field-by-field dispatch.

Also fixes writeFieldValue so a struct field whose type declares
yamlStringify routes to the hook. Without that change the encoder
would walk the field type's children instead of letting the hook
take over.
Lets callers use the standard {f} formatting verb to print any value
as YAML. yaml.fmt(my_struct, .{}) returns a wrapper whose format
method writes through Stringify.value.
A generic factory: ArrayHashMap(T) wraps a string-keyed hash map of
T values and declares the three hooks. Use it as a struct field to
round-trip a YAML mapping where keys are arbitrary strings and
values share one type.

Tests cover the slice path, the Value path, an empty map, error
cases for non-mapping input and non-string keys, struct values, and
deinit.

Also fixes endObject and endArray so an empty container emits the
inline form "{}" or "[]" instead of nothing. The empty-map test
caught this latent bug in the streaming API.
Comments describing the dynamic Value type and its serialization
options now match the variant names: array and object. YAML 1.2 spec
terminology (mapping, sequence) stays in places that describe the
input grammar or the AST, since those are the spec's own words.
In Zig 0.16 the unmanaged form became the default name and
ArrayListUnmanaged is a deprecated alias for ArrayList.
Per the Zig 0.16 release notes, managed containers are being phased out.
This moves AnchorMap to the unmanaged form internally.
@rjosephwright rjosephwright merged commit 8dd819f into main May 2, 2026
2 checks passed
@rjosephwright rjosephwright deleted the zig-0.16 branch May 2, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant