Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
MasonRemaley committed Jan 1, 2024
1 parent c6a90b4 commit 4dcc15e
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 82 deletions.
52 changes: 25 additions & 27 deletions lib/std/zon.zig
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
//! ZON serialization and deserialization.
//!
//! # ZON
//! ZON, or Zig Object Notation, is a subset of Zig use for data representation.
//! ZON, or Zig Object Notation, is a subset* of Zig used for data storage. ZON contains no type
//! names.
//!
//! (Strictly speaking, ZON is not currently a true subset of Zig, solely because of support for
//! `nan` and `inf` literals.)
//!
//! ZON supports the following Zig primitives:
//! Supported Zig primitives:
//! * boolean literals
//! * number literals (including `nan` and `inf`)
//! * character literals
//! * enum literals
//! * the `null` and `void` literals
//! * string literals, multiline string literals
//! * `null` and `void` literals
//! * string literals
//! * multiline string literals
//!
//! In addition, the following container types are supported:
//! Supported Zig containers:
//! * anonymous struct literals
//! * anonymous tuple literals
//! * slices (noted as a reference to a tuple literal)
//! * the reference (`&`) will likely be removed from ZON in the future, at which point ZON will
//! not distinguish between slices and tuples
//!
//! ZON objects do not contain type names.
//! * slices
//! * notated as a reference to a tuple literal
//! * this syntax will likely be removed in the future, at which point ZON will not distinguish
//! between slices and tuples
//!
//! Here is an example ZON object:
//! ```zon
Expand All @@ -38,22 +36,25 @@
//! "This string is a valid ZON object."
//! ```
//!
//! \* ZON is not currently a true subset of Zig, because it supports `nan` and
//! `inf` literals, which Zig does not.
//!
//! # Deserialization
//!
//! The simplest way to deserialize ZON at runtime is to call `parseFromSlice`. (For reading ZON at
//! The simplest way to deserialize ZON at runtime is `parseFromSlice`. (For reading ZON at
//! comptime, you can use `@import`.)
//!
//! If you need lower level control or more detailed diagnostics on failure, you can generate the
//! AST yourself with `std.zig.Ast.parse` and then deserialize it with:
//! If you need lower level control, or more detailed diagnostics, you can generate the AST yourself
//! with `std.zig.Ast.parse` and then deserialize it with:
//! * `parseFromAst`
//! * `parseFromAstNoAlloc`
//!
//! The following functions are also provided if you'd like to deserialize only part of an AST:
//! If you'd like to deserialize just part of an AST, you can use:
//! * `parseFromAstNode`
//! * `parseFromAstNodeNoAlloc`
//!
//! If you want absolute control over deserialization, you can bypass this module completely and
//! operate directly on the results of `std.zig.Ast.parse`.
//! If you need lower level control than provided by this module, you can operate directly on the
//! results of `std.zig.Ast.parse`.
//!
//!
//! # Serialization
Expand All @@ -64,14 +65,11 @@
//! * `stringifyMaxDepth`
//! * `stringifyArbitraryDepth`
//!
//! If you need more control over the serialization process, you can call `stringifier` to create
//! a `Stringifier` instance. This is used under the hood by `stringify` and its companion
//! functions, and allows for writing out values/fields/items individually.
//!
//! This can be used to control which fields are serialized, to configure fields individually, or to
//! stringify a ZON value that does not actually exist in memory.
//! If you need more control over the serialization process, for example to control which fields are
//! serialized, configure fields individually, or to stringify ZON values that do not exist in
//! memory, you can use `Stringifier`.
//!
//! Note that serializing floats with more than 64 bits may result in a loss of precision for now
//! Note that serializing floats with more than 64 bits may result in a loss of precision
//! (see https://github.com/ziglang/zig/issues/1181).

pub const ParseOptions = @import("zon/parse.zig").ParseOptions;
Expand All @@ -91,7 +89,7 @@ pub const Stringifier = @import("zon/stringify.zig").Stringifier;
pub const stringify = @import("zon/stringify.zig").stringify;
pub const stringifyMaxDepth = @import("zon/stringify.zig").stringifyMaxDepth;
pub const stringifyArbitraryDepth = @import("zon/stringify.zig").stringifyArbitraryDepth;
pub const stringier = @import("zon/stringify.zig").stringifier;
pub const stringifier = @import("zon/stringify.zig").stringifier;

test {
_ = @import("zon/parse.zig");
Expand Down
2 changes: 1 addition & 1 deletion lib/std/zon/parse.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub const ParseOptions = struct {
ignore_unknown_fields: bool = false,
/// If true, the parser cleans up partially parsed values on error. This requires some extra
/// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because
/// you're doing arena allocation.)
/// you're using arena allocation.)
free_on_error: bool = true,
};

Expand Down
131 changes: 82 additions & 49 deletions lib/std/zon/stringify.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const std = @import("std");
///
/// See `StringifyOptions` for more details.
pub const StringifierOptions = struct {
/// If false, only syntactically necessary whitespace is emitted.
whitespace: bool = true,
};

Expand Down Expand Up @@ -51,7 +52,6 @@ pub const StringifyContainerOptions = struct {
}
};

// TODO: are pointers to arrays allowed here? what about in the parser?
/// Serialize the given value to ZON.
///
/// It is asserted at comptime that `@TypeOf(val)` is not a recursive type.
Expand Down Expand Up @@ -244,12 +244,39 @@ test "checkValueDepth" {
try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{1, 2, 3}}));
}

/// Lower level control over stringification.
/// Lower level control over stringification, you can create a new instance with `stringifier`.
///
/// Useful when you want control over which fields/items are stringified, how they're represented,
/// or what to write a ZON object that does not exist in memory.
/// or want to write a ZON object that does not exist in memory.
///
/// You can create a new instance with `stringifier`.
/// You can serialize values with `value`. To serialize recursive types, the following are provided:
/// * `valueMaxDepth`
/// * `valueArbitraryDepth`
///
/// You can also serialize values using specific notations:
/// * `int`
/// * `float`
/// * `utf8Codepoint`
/// * `slice`
/// * `sliceMaxDepth`
/// * `sliceArbitraryDepth`
/// * `string`
/// * `multilineString`
///
/// For manual serialization of containers, see:
/// * `startStruct`
/// * `startTuple`
/// * `startSlice`
///
/// # Example
/// ```zig
/// var serializer = stringifier(writer, .{});
/// var vec2 = try serializer.startStruct(.{});
/// try vec2.field("x", 1.5, .{});
/// try vec2.fieldPrefix();
/// try serializer.value(2.5);
/// try vec2.finish();
/// ```
pub fn Stringifier(comptime Writer: type) type {
return struct {
const Self = @This();
Expand All @@ -261,7 +288,7 @@ pub fn Stringifier(comptime Writer: type) type {
writer: Writer,

/// Initialize a stringifier.
pub fn init(writer: Writer, options: StringifierOptions) Self {
fn init(writer: Writer, options: StringifierOptions) Self {
return .{
.options = options,
.writer = writer,
Expand Down Expand Up @@ -410,7 +437,7 @@ pub fn Stringifier(comptime Writer: type) type {
else => {}
}

// TODO: https://github.com/ziglang/zig/issues/1181
// Cast required because of https://github.com/ziglang/zig/issues/1181
try std.fmt.formatFloatDecimal(@as(f64, @floatCast(val)), .{}, self.writer);
}

Expand Down Expand Up @@ -457,16 +484,14 @@ pub fn Stringifier(comptime Writer: type) type {
try self.sliceArbitraryDepth(val, options);
}

// XXX: test this?
/// Like `value`, but recursive types are allowed.
///
/// Returns `error.MaxDepthError` if `depth` is exceeded.
pub fn sliceDepthLimit(self: *Self, val: anytype, options: StringifyValueOptions, depth: usize) MaxDepthError!void {
pub fn sliceMaxDepth(self: *Self, val: anytype, options: StringifyValueOptions, depth: usize) MaxDepthError!void {
try checkValueDepth(val, depth);
try self.sliceArbitraryDepth(val, options);
}

// XXX: test this?
/// Like `value`, but recursive types are allowed.
///
/// It is the caller's responsibility to ensure that `val` does not contain cycles.
Expand Down Expand Up @@ -587,37 +612,31 @@ pub fn Stringifier(comptime Writer: type) type {
};
}

/// Print a trailing comma as configured when appropriate, and the closing bracket.
/// Finishes serializing the tuple.
///
/// Prints a trailing comma as configured when appropriate, and the closing bracket.
pub fn finish(self: *Tuple) Writer.Error!void {
try self.container.finish();
self.* = undefined;
}

/// Serialize a field.
///
/// Equivalent to calling `fieldPrefix` followed by `value`.
/// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
pub fn field(self: *Tuple, val: anytype, options: StringifyValueOptions) Writer.Error!void {
try self.container.field(null, val, options);
}

/// Serialize a field.
///
/// Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
/// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
pub fn fieldMaxDepth(self: *Tuple, val: anytype, options: StringifyValueOptions, depth: usize) MaxDepthError!void {
try self.container.fieldMaxDepth(null, val, options, depth);
}

/// Serialize a field.
///
/// Equivalent to calling `fieldPrefix` followed by `valueArbitraryDepth`.
/// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueArbitraryDepth`.
pub fn fieldArbitraryDepth(self: *Tuple, val: anytype, options: StringifyValueOptions) Writer.Error!void {
try self.container.fieldArbitraryDepth(null, val, options);
}

/// Print a field prefix. This prints any necessary commas, and whitespace as
/// configured.
///
/// Useful if you want to serialize the field value yourself.
/// configured. Useful if you want to serialize the field value yourself.
pub fn fieldPrefix(self: *Tuple) Writer.Error!void {
try self.container.fieldPrefix(null);
}
Expand All @@ -633,37 +652,32 @@ pub fn Stringifier(comptime Writer: type) type {
};
}

/// Print a trailing comma as configured when appropriate, and the closing bracket.
/// Finishes serializing the struct.
///
/// Prints a trailing comma as configured when appropriate, and the closing bracket.
pub fn finish(self: *Struct) Writer.Error!void {
try self.container.finish();
self.* = undefined;
}

/// Serialize a field.
///
/// Equivalent to calling `fieldPrefix` followed by `value`.
/// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
pub fn field(self: *Struct, name: []const u8, val: anytype, options: StringifyValueOptions) Writer.Error!void {
try self.container.field(name, val, options);
}

/// Serialize a field.
///
/// Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
/// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
pub fn fieldMaxDepth(self: *Struct, name: []const u8, val: anytype, options: StringifyValueOptions, depth: usize) MaxDepthError!void {
try self.container.fieldMaxDepth(name, val, options, depth);
}

/// Serialize a field.
///
/// Equivalent to calling `fieldPrefix` followed by `valueArbitraryDepth`.
/// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueArbitraryDepth`.
pub fn fieldArbitraryDepth(self: *Struct, name: []const u8, val: anytype, options: StringifyValueOptions) Writer.Error!void {
try self.container.fieldArbitraryDepth(name, val, options);
}

/// Print a field prefix. This prints any necessary commas, the field name (escaped if
/// necessary) and whitespace as configured.
///
/// Useful if you want to serialize the field value yourself.
/// necessary) and whitespace as configured. Useful if you want to serialize the field
/// value yourself.
pub fn fieldPrefix(self: *Struct, name: []const u8) Writer.Error!void {
try self.container.fieldPrefix(name);
}
Expand All @@ -680,37 +694,31 @@ pub fn Stringifier(comptime Writer: type) type {
};
}

/// Print a trailing comma as configured when appropriate, and the closing bracket.
/// Finishes serializing the slice.
///
/// Prints a trailing comma as configured when appropriate, and the closing bracket.
pub fn finish(self: *Slice) Writer.Error!void {
try self.container.finish();
self.* = undefined;
}

/// Serialize an item.
///
/// Equivalent to calling `itemPrefix` followed by `value`.
/// Serialize an item. Equivalent to calling `itemPrefix` followed by `value`.
pub fn item(self: *Slice, val: anytype, options: StringifyValueOptions) Writer.Error!void {
try self.container.field(null, val, options);
}

/// Serialize an item.
///
/// Equivalent to calling `itemPrefix` followed by `valueMaxDepth`.
/// Serialize an item. Equivalent to calling `itemPrefix` followed by `valueMaxDepth`.
pub fn itemMaxDepth(self: *Slice, val: anytype, options: StringifyValueOptions, depth: usize) MaxDepthError!void {
try self.container.fieldMaxDepth(null, val, options, depth);
}

/// Serialize an item.
///
/// Equivalent to calling `itemPrefix` followed by `valueArbitraryDepth`.
/// Serialize an item. Equivalent to calling `itemPrefix` followed by `valueArbitraryDepth`.
pub fn itemArbitraryDepth(self: *Slice, val: anytype, options: StringifyValueOptions) Writer.Error!void {
try self.container.fieldArbitraryDepth(null, val, options);
}

/// Print a field prefix. This prints any necessary commas, and whitespace as
/// configured.
///
/// Useful if you want to serialize the item value yourself.
/// configured. Useful if you want to serialize the item value yourself.
pub fn itemPrefix(self: *Slice) Writer.Error!void {
try self.container.fieldPrefix(null);
}
Expand Down Expand Up @@ -1662,6 +1670,16 @@ test "depth limits" {
try std.testing.expectError(error.MaxDepth, stringifyMaxDepth(maybe_recurse, .{}, buf.writer(), 2));
try std.testing.expectEqualStrings("", buf.items);
buf.clearRetainingCapacity();

var serializer = stringifier(buf.writer(), .{});

try std.testing.expectError(error.MaxDepth, serializer.sliceMaxDepth(maybe_recurse, .{}, 2));
try std.testing.expectEqualStrings("", buf.items);
buf.clearRetainingCapacity();

try std.testing.expectError(error.MaxDepth, serializer.sliceArbitraryDepth(maybe_recurse, .{}));
try std.testing.expectEqualStrings("", buf.items);
buf.clearRetainingCapacity();
}

// A slice succeeding
Expand All @@ -1672,6 +1690,16 @@ test "depth limits" {
try stringifyMaxDepth(maybe_recurse, .{}, buf.writer(), 3);
try std.testing.expectEqualStrings("&.{.{ .r = &.{} }}", buf.items);
buf.clearRetainingCapacity();

var serializer = stringifier(buf.writer(), .{});

try serializer.sliceMaxDepth(maybe_recurse, .{}, 3);
try std.testing.expectEqualStrings(".{ .{ .r = &.{} } }", buf.items);
buf.clearRetainingCapacity();

try serializer.sliceArbitraryDepth(maybe_recurse, .{});
try std.testing.expectEqualStrings(".{ .{ .r = &.{} } }", buf.items);
buf.clearRetainingCapacity();
}

// Max depth failing on recursive type due to recursion
Expand All @@ -1683,9 +1711,14 @@ test "depth limits" {
try std.testing.expectError(error.MaxDepth, stringifyMaxDepth(maybe_recurse, .{}, buf.writer(), 128));
try std.testing.expectEqualStrings("", buf.items);
buf.clearRetainingCapacity();

var serializer = stringifier(buf.writer(), .{});
try std.testing.expectError(error.MaxDepth, serializer.sliceMaxDepth(maybe_recurse, .{}, 128));
try std.testing.expectEqualStrings("", buf.items);
buf.clearRetainingCapacity();
}

// Max depth on the lower level API
// Max depth on other parts of the lower level API
{
const writer = buf.writer();
var serializer = stringifier(writer, .{});
Expand Down

0 comments on commit 4dcc15e

Please sign in to comment.