Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub fn build(b: *std.Build) void {
// LLVM backend with -Duse-llvm=true for the coverage and valgrind CI steps.
// See ziglang/zig#24463 and #25368.
const use_llvm = b.option(bool, "use-llvm", "Build test binaries with the LLVM backend (needed for kcov coverage on Zig 0.16+).");
const coverage_threshold = b.option(u8, "coverage-threshold", "Minimum line coverage percent required by test-coverage.") orelse 100;
const coverage_threshold = b.option(u8, "coverage-threshold", "Minimum line coverage percent required by test-coverage.") orelse 85;
const yaml_test_suite_dir = b.option(
[]const u8,
"yaml-test-suite-dir",
Expand Down
136 changes: 68 additions & 68 deletions src/emitter/block.zig

Large diffs are not rendered by default.

95 changes: 67 additions & 28 deletions src/emitter/emitter.zig
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,41 @@ pub const State = struct {
}
};

pub const OutputBuffer = struct {
const Self = @This();

list: std.ArrayList(u8) = .empty,
max_output_bytes: ?usize = null,
limit_exceeded: bool = false,

pub fn append(self: *Self, allocator: std.mem.Allocator, byte: u8) std.mem.Allocator.Error!void {
try self.checkCanAppend(1);
try self.list.append(allocator, byte);
}

pub fn appendSlice(self: *Self, allocator: std.mem.Allocator, bytes: []const u8) std.mem.Allocator.Error!void {
try self.checkCanAppend(bytes.len);
try self.list.appendSlice(allocator, bytes);
}

fn deinit(self: *Self, allocator: std.mem.Allocator) void {
self.list.deinit(allocator);
self.* = .{};
}

fn toOwnedSlice(self: *Self, allocator: std.mem.Allocator) std.mem.Allocator.Error![]u8 {
return self.list.toOwnedSlice(allocator);
}

fn checkCanAppend(self: *Self, additional_bytes: usize) std.mem.Allocator.Error!void {
const max_output_bytes = self.max_output_bytes orelse return;
if (self.list.items.len > max_output_bytes or additional_bytes > max_output_bytes - self.list.items.len) {
self.limit_exceeded = true;
return error.OutOfMemory;
}
}
};

/// Emits a YAML stream from parser events. Caller owns the returned memory.
pub fn emitEvents(allocator: std.mem.Allocator, events: []const Event) Error![]u8 {
return emitEventsWithOptions(allocator, events, .{});
Expand Down Expand Up @@ -313,9 +348,18 @@ pub fn dumpStreamToWriterWithOptions(
}

pub fn emit(self: *State) Error![]u8 {
var out: std.ArrayList(u8) = .empty;
var out: OutputBuffer = .{ .max_output_bytes = self.max_output_bytes };
errdefer out.deinit(self.allocator);

emitIntoBuffer(self, &out) catch |err| {
if (err == error.OutOfMemory and out.limit_exceeded) return ParseError.Unsupported;
return err;
};

return out.toOwnedSlice(self.allocator);
}

fn emitIntoBuffer(self: *State, out: anytype) Error!void {
try self.expectEvent(.stream_start);

var document_index: usize = 0;
Expand Down Expand Up @@ -347,9 +391,9 @@ pub fn emit(self: *State) Error![]u8 {

if (emit_yaml_version) {
const version = document_start.yaml_version.?;
try out.appendSlice(self.allocator, "%YAML ");
try out.appendSlice(self.allocator, version);
try out.append(self.allocator, '\n');
try out.*.appendSlice(self.allocator, "%YAML ");
try out.*.appendSlice(self.allocator, version);
try out.*.append(self.allocator, '\n');
}

if (emit_tag_directives) {
Expand All @@ -359,21 +403,21 @@ pub fn emit(self: *State) Error![]u8 {
}

if (emit_document_start) {
try out.appendSlice(self.allocator, "---");
try out.*.appendSlice(self.allocator, "---");
}

if (self.index >= self.events.len) return ParseError.InvalidSyntax;
if (self.events[self.index] == .document_end) {
if (emit_document_start) try out.append(self.allocator, '\n');
if (emit_document_start) try out.*.append(self.allocator, '\n');
} else if (shouldEmitEmptyDocument(self)) {
if (emit_document_start) try out.append(self.allocator, '\n');
if (emit_document_start) try out.*.append(self.allocator, '\n');
self.index += 1;
} else if (empty_plain_document and emit_document_start) {
if (self.omit_redundant_document_start) {
try out.append(self.allocator, '\n');
try out.*.append(self.allocator, '\n');
} else {
try out.appendSlice(self.allocator, " null");
try out.append(self.allocator, '\n');
try out.*.appendSlice(self.allocator, " null");
try out.*.append(self.allocator, '\n');
}
self.index += 1;
} else {
Expand All @@ -383,29 +427,24 @@ pub fn emit(self: *State) Error![]u8 {
if (emit_document_start) {
const starts_own_line = self.topLevelEventStartsOwnLine(self.events[self.index]) or
canonicalTopLevelScalarStartsOwnLine(self, document_start);
try out.append(self.allocator, if (starts_own_line) '\n' else ' ');
try out.*.append(self.allocator, if (starts_own_line) '\n' else ' ');
}
try emitTopLevelNode(self, &out);
}
try out.append(self.allocator, '\n');
try out.*.append(self.allocator, '\n');
}

if (self.index >= self.events.len or self.events[self.index] != .document_end) return ParseError.InvalidSyntax;
const document_end = self.events[self.index].document_end;
self.index += 1;

if (document_end.explicit or force_document_end) try out.appendSlice(self.allocator, "...\n");
if (document_end.explicit or force_document_end) try out.*.appendSlice(self.allocator, "...\n");
previous_document_start_explicit = document_start.explicit;
previous_document_empty = empty_plain_document;
}

try self.expectEvent(.stream_end);
if (self.index != self.events.len) return ParseError.InvalidSyntax;
if (self.max_output_bytes) |max_output_bytes| {
if (out.items.len > max_output_bytes) return ParseError.Unsupported;
}

return out.toOwnedSlice(self.allocator);
}

fn validateYamlVersion(version: ?[]const u8) ParseError!void {
Expand Down Expand Up @@ -436,16 +475,16 @@ fn validateDocumentTagsDeclared(self: *const State, tag_directives: []const even
return tag_emit.usesDeclaredTagDirective(document_events, tag_directives);
}

fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: *std.ArrayList(u8)) Error!void {
fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: anytype) Error!void {
const scalar = self.events[self.index].scalar;
if (scalar.anchor) |anchor| {
try validateAnchorName(anchor);
try out.append(self.allocator, ' ');
try out.append(self.allocator, '&');
try out.appendSlice(self.allocator, anchor);
try out.*.append(self.allocator, ' ');
try out.*.append(self.allocator, '&');
try out.*.appendSlice(self.allocator, anchor);
}
if (scalar.tag) |tag| {
try out.append(self.allocator, ' ');
try out.*.append(self.allocator, ' ');
try appendEmittedTag(self.allocator, out, tag);
}

Expand All @@ -459,7 +498,7 @@ fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: *std.Arra
std.mem.indexOfScalar(u8, scalar_without_tag.value, '\n') == null and
(scalar_without_tag.style != .plain or !plainScalarNeedsQuoting(scalar_without_tag.value)))
{
try out.append(self.allocator, ' ');
try out.*.append(self.allocator, ' ');
try appendEmittedScalarNodeIndented(self.allocator, out, scalar_without_tag, .{
.content = 2,
.indicator = 2,
Expand All @@ -468,7 +507,7 @@ fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: *std.Arra
return;
}

try out.append(self.allocator, '\n');
try out.*.append(self.allocator, '\n');
try appendEmittedScalarNodeIndented(self.allocator, out, self.emittedScalar(scalar_without_tag), .{
.content = 2,
.indicator = 2,
Expand All @@ -495,7 +534,7 @@ fn shouldEmitEmptyDocument(self: *const State) bool {
return emptyPlainDocumentHasExplicitEnd(self);
}

fn emitTopLevelNode(self: *State, out: *std.ArrayList(u8)) Error!void {
fn emitTopLevelNode(self: *State, out: anytype) Error!void {
if (self.index >= self.events.len) return ParseError.InvalidSyntax;

switch (self.events[self.index]) {
Expand All @@ -520,7 +559,7 @@ fn emitTopLevelNode(self: *State, out: *std.ArrayList(u8)) Error!void {
const style = self.emittedCollectionStyle(collection.style);
const emit_as_flow = style == .flow and self.preserve_top_level_flow_mapping_style;
if (try appendEmittedCollectionProperties(self.allocator, out, collection)) {
try out.append(self.allocator, if (emit_as_flow) ' ' else '\n');
try out.*.append(self.allocator, if (emit_as_flow) ' ' else '\n');
}
if (emit_as_flow) {
try flow.emitFlowMapping(self, out);
Expand All @@ -534,7 +573,7 @@ fn emitTopLevelNode(self: *State, out: *std.ArrayList(u8)) Error!void {
const style = self.emittedCollectionStyle(collection.style);
const wrote_properties = try appendEmittedCollectionProperties(self.allocator, out, collection);
if (wrote_properties) {
try out.append(self.allocator, switch (style) {
try out.*.append(self.allocator, switch (style) {
.block => '\n',
.flow => ' ',
});
Expand Down
40 changes: 20 additions & 20 deletions src/emitter/flow.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,86 +18,86 @@ const appendEmittedAlias = scalar_emit.appendEmittedAlias;
const appendEmittedCollectionProperties = scalar_emit.appendEmittedCollectionProperties;
const appendEmittedScalarNode = scalar_emit.appendEmittedScalarNode;

pub fn emitFlowSequence(self: *State, out: *std.ArrayList(u8)) Error!void {
pub fn emitFlowSequence(self: *State, out: anytype) Error!void {
try self.enterEmitCollection();
defer self.leaveEmitCollection();

try out.append(self.allocator, '[');
try out.*.append(self.allocator, '[');

var first_item = true;
while (self.index < self.events.len and self.events[self.index] != .sequence_end) {
if (!first_item) try out.appendSlice(self.allocator, ", ");
if (!first_item) try out.*.appendSlice(self.allocator, ", ");
first_item = false;

try emitFlowNode(self, out);
}

if (self.index >= self.events.len) return ParseError.InvalidSyntax;
try out.append(self.allocator, ']');
try out.*.append(self.allocator, ']');
self.index += 1;
}

pub fn emitEmptySequenceIfPresent(
self: *State,
out: *std.ArrayList(u8),
out: anytype,
collection: CollectionStart,
prefix: ?[]const u8,
) Error!bool {
if (self.index >= self.events.len) return ParseError.InvalidSyntax;
if (self.events[self.index] != .sequence_end) return false;

if (prefix) |value| try out.appendSlice(self.allocator, value);
if (prefix) |value| try out.*.appendSlice(self.allocator, value);
if (try appendEmittedCollectionProperties(self.allocator, out, collection)) {
try out.append(self.allocator, ' ');
try out.*.append(self.allocator, ' ');
}
try out.appendSlice(self.allocator, "[]");
try out.*.appendSlice(self.allocator, "[]");
self.index += 1;
return true;
}

pub fn emitEmptyMappingIfPresent(
self: *State,
out: *std.ArrayList(u8),
out: anytype,
collection: CollectionStart,
prefix: ?[]const u8,
) Error!bool {
if (self.index >= self.events.len) return ParseError.InvalidSyntax;
if (self.events[self.index] != .mapping_end) return false;

if (prefix) |value| try out.appendSlice(self.allocator, value);
if (prefix) |value| try out.*.appendSlice(self.allocator, value);
if (try appendEmittedCollectionProperties(self.allocator, out, collection)) {
try out.append(self.allocator, ' ');
try out.*.append(self.allocator, ' ');
}
try out.appendSlice(self.allocator, "{}");
try out.*.appendSlice(self.allocator, "{}");
self.index += 1;
return true;
}

pub fn emitFlowMapping(self: *State, out: *std.ArrayList(u8)) Error!void {
pub fn emitFlowMapping(self: *State, out: anytype) Error!void {
try self.enterEmitCollection();
defer self.leaveEmitCollection();

try out.append(self.allocator, '{');
try out.*.append(self.allocator, '{');

var first_pair = true;
while (self.index < self.events.len and self.events[self.index] != .mapping_end) {
if (!first_pair) try out.appendSlice(self.allocator, ", ");
if (!first_pair) try out.*.appendSlice(self.allocator, ", ");
first_pair = false;

try emitFlowNode(self, out);
if (self.index >= self.events.len or self.events[self.index] == .mapping_end) {
return ParseError.InvalidSyntax;
}
try out.appendSlice(self.allocator, ": ");
try out.*.appendSlice(self.allocator, ": ");
try emitFlowNode(self, out);
}

if (self.index >= self.events.len) return ParseError.InvalidSyntax;
try out.append(self.allocator, '}');
try out.*.append(self.allocator, '}');
self.index += 1;
}

pub fn emitFlowNode(self: *State, out: *std.ArrayList(u8)) Error!void {
pub fn emitFlowNode(self: *State, out: anytype) Error!void {
if (self.index >= self.events.len) return ParseError.InvalidSyntax;

switch (self.events[self.index]) {
Expand All @@ -113,15 +113,15 @@ pub fn emitFlowNode(self: *State, out: *std.ArrayList(u8)) Error!void {
self.index += 1;
if (try emitEmptySequenceIfPresent(self, out, collection, null)) return;
if (try appendEmittedCollectionProperties(self.allocator, out, collection)) {
try out.append(self.allocator, ' ');
try out.*.append(self.allocator, ' ');
}
try emitFlowSequence(self, out);
},
.mapping_start => |collection| {
self.index += 1;
if (try emitEmptyMappingIfPresent(self, out, collection, null)) return;
if (try appendEmittedCollectionProperties(self.allocator, out, collection)) {
try out.append(self.allocator, ' ');
try out.*.append(self.allocator, ' ');
}
try emitFlowMapping(self, out);
},
Expand Down
Loading
Loading