std: remove io.AutoIndentingStream

This type is not widely applicable enough to be a public part of the
public interface of the std.

The current implementation in only fully utilized by the zig fmt
implementation, which could benefit by even tighter integration as
will be demonstrated in the next commit. Therefore, move the current
io.AutoIndentingStream to lib/std/zig/render.zig.

The C backend of the self hosted compiler also use this type currently,
but it does not require anywhere near its full complexity. Therefore,
implement a greatly simplified version of this interface in
src/codegen/c.zig.
This commit is contained in:
Isaac Freund 2021-02-16 22:06:35 +01:00
parent 4006a3afb3
commit 070e548acf
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
6 changed files with 196 additions and 164 deletions

View File

@ -370,7 +370,6 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/std/heap.zig"
"${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig"
"${CMAKE_SOURCE_DIR}/lib/std/io.zig"
"${CMAKE_SOURCE_DIR}/lib/std/io/auto_indenting_stream.zig"
"${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig"
"${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig"
"${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig"

View File

@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader;
pub const BitWriter = @import("io/bit_writer.zig").BitWriter;
pub const bitWriter = @import("io/bit_writer.zig").bitWriter;
pub const AutoIndentingStream = @import("io/auto_indenting_stream.zig").AutoIndentingStream;
pub const autoIndentingStream = @import("io/auto_indenting_stream.zig").autoIndentingStream;
pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;

View File

@ -1,154 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("../std.zig");
const io = std.io;
const mem = std.mem;
const assert = std.debug.assert;
/// Automatically inserts indentation of written data by keeping
/// track of the current indentation level
pub fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
return struct {
const Self = @This();
pub const Error = UnderlyingWriter.Error;
pub const Writer = io.Writer(*Self, Error, write);
underlying_writer: UnderlyingWriter,
indent_count: usize = 0,
indent_delta: usize,
current_line_empty: bool = true,
indent_one_shot_count: usize = 0, // automatically popped when applied
applied_indent: usize = 0, // the most recently applied indent
indent_next_line: usize = 0, // not used until the next line
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
pub fn write(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len == 0)
return @as(usize, 0);
try self.applyIndent();
return self.writeNoIndent(bytes);
}
// Change the indent delta without changing the final indentation level
pub fn setIndentDelta(self: *Self, indent_delta: usize) void {
if (self.indent_delta == indent_delta) {
return;
} else if (self.indent_delta > indent_delta) {
assert(self.indent_delta % indent_delta == 0);
self.indent_count = self.indent_count * (self.indent_delta / indent_delta);
} else {
// assert that the current indentation (in spaces) in a multiple of the new delta
assert((self.indent_count * self.indent_delta) % indent_delta == 0);
self.indent_count = self.indent_count / (indent_delta / self.indent_delta);
}
self.indent_delta = indent_delta;
}
fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len == 0)
return @as(usize, 0);
try self.underlying_writer.writeAll(bytes);
if (bytes[bytes.len - 1] == '\n')
self.resetLine();
return bytes.len;
}
pub fn insertNewline(self: *Self) Error!void {
_ = try self.writeNoIndent("\n");
}
fn resetLine(self: *Self) void {
self.current_line_empty = true;
self.indent_next_line = 0;
}
/// Insert a newline unless the current line is blank
pub fn maybeInsertNewline(self: *Self) Error!void {
if (!self.current_line_empty)
try self.insertNewline();
}
/// Push default indentation
pub fn pushIndent(self: *Self) void {
// Doesn't actually write any indentation.
// Just primes the stream to be able to write the correct indentation if it needs to.
self.indent_count += 1;
}
/// Push an indent that is automatically popped after being applied
pub fn pushIndentOneShot(self: *Self) void {
self.indent_one_shot_count += 1;
self.pushIndent();
}
/// Turns all one-shot indents into regular indents
/// Returns number of indents that must now be manually popped
pub fn lockOneShotIndent(self: *Self) usize {
var locked_count = self.indent_one_shot_count;
self.indent_one_shot_count = 0;
return locked_count;
}
/// Push an indent that should not take effect until the next line
pub fn pushIndentNextLine(self: *Self) void {
self.indent_next_line += 1;
self.pushIndent();
}
pub fn popIndent(self: *Self) void {
assert(self.indent_count != 0);
self.indent_count -= 1;
if (self.indent_next_line > 0)
self.indent_next_line -= 1;
}
/// Writes ' ' bytes if the current line is empty
fn applyIndent(self: *Self) Error!void {
const current_indent = self.currentIndent();
if (self.current_line_empty and current_indent > 0) {
try self.underlying_writer.writeByteNTimes(' ', current_indent);
self.applied_indent = current_indent;
}
self.indent_count -= self.indent_one_shot_count;
self.indent_one_shot_count = 0;
self.current_line_empty = false;
}
/// Checks to see if the most recent indentation exceeds the currently pushed indents
pub fn isLineOverIndented(self: *Self) bool {
if (self.current_line_empty) return false;
return self.applied_indent > self.currentIndent();
}
fn currentIndent(self: *Self) usize {
var indent_current: usize = 0;
if (self.indent_count > 0) {
const indent_count = self.indent_count - self.indent_next_line;
indent_current = indent_count * self.indent_delta;
}
return indent_current;
}
};
}
pub fn autoIndentingStream(
indent_delta: usize,
underlying_writer: anytype,
) AutoIndentingStream(@TypeOf(underlying_writer)) {
return AutoIndentingStream(@TypeOf(underlying_writer)){
.underlying_writer = underlying_writer,
.indent_delta = indent_delta,
};
}

View File

@ -15,12 +15,14 @@ const asm_indent_delta = 2;
pub const Error = ast.Tree.RenderError;
const Writer = std.ArrayList(u8).Writer;
const Ais = std.io.AutoIndentingStream(Writer);
const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);
pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
assert(tree.errors.len == 0); // Cannot render an invalid tree.
var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, buffer.writer());
var auto_indenting_stream = Ais{
.indent_delta = indent_delta,
.underlying_writer = buffer.writer(),
};
const ais = &auto_indenting_stream;
// Render all the line comments at the beginning of the file.
@ -2132,3 +2134,137 @@ fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {
else => false,
};
}
/// Automatically inserts indentation of written data by keeping
/// track of the current indentation level
fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
return struct {
const Self = @This();
pub const Error = UnderlyingWriter.Error;
pub const Writer = std.io.Writer(*Self, Error, write);
underlying_writer: UnderlyingWriter,
indent_count: usize = 0,
indent_delta: usize,
current_line_empty: bool = true,
indent_one_shot_count: usize = 0, // automatically popped when applied
applied_indent: usize = 0, // the most recently applied indent
indent_next_line: usize = 0, // not used until the next line
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
pub fn write(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len == 0)
return @as(usize, 0);
try self.applyIndent();
return self.writeNoIndent(bytes);
}
// Change the indent delta without changing the final indentation level
pub fn setIndentDelta(self: *Self, new_indent_delta: usize) void {
if (self.indent_delta == new_indent_delta) {
return;
} else if (self.indent_delta > new_indent_delta) {
assert(self.indent_delta % new_indent_delta == 0);
self.indent_count = self.indent_count * (self.indent_delta / new_indent_delta);
} else {
// assert that the current indentation (in spaces) in a multiple of the new delta
assert((self.indent_count * self.indent_delta) % new_indent_delta == 0);
self.indent_count = self.indent_count / (new_indent_delta / self.indent_delta);
}
self.indent_delta = new_indent_delta;
}
fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len == 0)
return @as(usize, 0);
try self.underlying_writer.writeAll(bytes);
if (bytes[bytes.len - 1] == '\n')
self.resetLine();
return bytes.len;
}
pub fn insertNewline(self: *Self) Error!void {
_ = try self.writeNoIndent("\n");
}
fn resetLine(self: *Self) void {
self.current_line_empty = true;
self.indent_next_line = 0;
}
/// Insert a newline unless the current line is blank
pub fn maybeInsertNewline(self: *Self) Error!void {
if (!self.current_line_empty)
try self.insertNewline();
}
/// Push default indentation
pub fn pushIndent(self: *Self) void {
// Doesn't actually write any indentation.
// Just primes the stream to be able to write the correct indentation if it needs to.
self.indent_count += 1;
}
/// Push an indent that is automatically popped after being applied
pub fn pushIndentOneShot(self: *Self) void {
self.indent_one_shot_count += 1;
self.pushIndent();
}
/// Turns all one-shot indents into regular indents
/// Returns number of indents that must now be manually popped
pub fn lockOneShotIndent(self: *Self) usize {
var locked_count = self.indent_one_shot_count;
self.indent_one_shot_count = 0;
return locked_count;
}
/// Push an indent that should not take effect until the next line
pub fn pushIndentNextLine(self: *Self) void {
self.indent_next_line += 1;
self.pushIndent();
}
pub fn popIndent(self: *Self) void {
assert(self.indent_count != 0);
self.indent_count -= 1;
if (self.indent_next_line > 0)
self.indent_next_line -= 1;
}
/// Writes ' ' bytes if the current line is empty
fn applyIndent(self: *Self) Error!void {
const current_indent = self.currentIndent();
if (self.current_line_empty and current_indent > 0) {
try self.underlying_writer.writeByteNTimes(' ', current_indent);
self.applied_indent = current_indent;
}
self.indent_count -= self.indent_one_shot_count;
self.indent_one_shot_count = 0;
self.current_line_empty = false;
}
/// Checks to see if the most recent indentation exceeds the currently pushed indents
pub fn isLineOverIndented(self: *Self) bool {
if (self.current_line_empty) return false;
return self.applied_indent > self.currentIndent();
}
fn currentIndent(self: *Self) usize {
var indent_current: usize = 0;
if (self.indent_count > 0) {
const indent_count = self.indent_count - self.indent_next_line;
indent_current = indent_count * self.indent_delta;
}
return indent_current;
}
};
}

View File

@ -1,4 +1,5 @@
const std = @import("std");
const assert = std.debug.assert;
const mem = std.mem;
const log = std.log.scoped(.c);
@ -42,7 +43,7 @@ pub const Object = struct {
next_arg_index: usize = 0,
next_local_index: usize = 0,
next_block_index: usize = 0,
indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer),
indent_writer: IndentWriter(std.ArrayList(u8).Writer),
fn resolveInst(o: *Object, inst: *Inst) !CValue {
if (inst.value()) |_| {
@ -63,7 +64,7 @@ pub const Object = struct {
return local_value;
}
fn writer(o: *Object) std.io.AutoIndentingStream(std.ArrayList(u8).Writer).Writer {
fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
return o.indent_writer.writer();
}
@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
}
fn IndentWriter(comptime UnderlyingWriter: type) type {
return struct {
const Self = @This();
pub const Error = UnderlyingWriter.Error;
pub const Writer = std.io.Writer(*Self, Error, write);
pub const indent_delta = 4;
underlying_writer: UnderlyingWriter,
indent_count: usize = 0,
current_line_empty: bool = true,
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
pub fn write(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len == 0) return @as(usize, 0);
const current_indent = self.indent_count * Self.indent_delta;
if (self.current_line_empty and current_indent > 0) {
try self.underlying_writer.writeByteNTimes(' ', current_indent);
}
self.current_line_empty = false;
return self.writeNoIndent(bytes);
}
pub fn insertNewline(self: *Self) Error!void {
_ = try self.writeNoIndent("\n");
}
pub fn pushIndent(self: *Self) void {
self.indent_count += 1;
}
pub fn popIndent(self: *Self) void {
assert(self.indent_count != 0);
self.indent_count -= 1;
}
fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len == 0) return @as(usize, 0);
try self.underlying_writer.writeAll(bytes);
if (bytes[bytes.len - 1] == '\n') {
self.current_line_empty = true;
}
return bytes.len;
}
};
}

View File

@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
.value_map = codegen.CValueMap.init(module.gpa),
.indent_writer = undefined, // set later so we can get a pointer to object.code
};
object.indent_writer = std.io.autoIndentingStream(4, object.code.writer());
object.indent_writer = .{ .underlying_writer = object.code.writer() };
defer object.value_map.deinit();
defer object.code.deinit();
defer object.dg.fwd_decl.deinit();