authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-16 22:06:35+01:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-16 23:20:46+01:00
log070e548acf8b5cb22459b779ce771b42157f49f7
tree2f5fbceac083172b52835db49429567278ca061b
parent4006a3afb31f89be28721bdcd50fa64de63d6cbb
signaturelock-open Commit is signed but in an unrecognized format.

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.

6 files changed, 196 insertions(+), 164 deletions(-)

CMakeLists.txt-1
...@@ -370,7 +370,6 @@ set(ZIG_STAGE2_SOURCES...@@ -370,7 +370,6 @@ set(ZIG_STAGE2_SOURCES
370 "${CMAKE_SOURCE_DIR}/lib/std/heap.zig"370 "${CMAKE_SOURCE_DIR}/lib/std/heap.zig"
371 "${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig"371 "${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig"
372 "${CMAKE_SOURCE_DIR}/lib/std/io.zig"372 "${CMAKE_SOURCE_DIR}/lib/std/io.zig"
373 "${CMAKE_SOURCE_DIR}/lib/std/io/auto_indenting_stream.zig"
374 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig"373 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig"
375 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig"374 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig"
376 "${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig"375 "${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig"
lib/std/io.zig-3
...@@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader;...@@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader;
142pub const BitWriter = @import("io/bit_writer.zig").BitWriter;142pub const BitWriter = @import("io/bit_writer.zig").BitWriter;
143pub const bitWriter = @import("io/bit_writer.zig").bitWriter;143pub const bitWriter = @import("io/bit_writer.zig").bitWriter;
144144
145pub const AutoIndentingStream = @import("io/auto_indenting_stream.zig").AutoIndentingStream;
146pub const autoIndentingStream = @import("io/auto_indenting_stream.zig").autoIndentingStream;
147
148pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;145pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
149pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;146pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;
150147
lib/std/io/auto_indenting_stream.zig deleted-154
...@@ -1,154 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7const std = @import("../std.zig");
8const io = std.io;
9const mem = std.mem;
10const assert = std.debug.assert;
11
12/// Automatically inserts indentation of written data by keeping
13/// track of the current indentation level
14pub fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
15 return struct {
16 const Self = @This();
17 pub const Error = UnderlyingWriter.Error;
18 pub const Writer = io.Writer(*Self, Error, write);
19
20 underlying_writer: UnderlyingWriter,
21
22 indent_count: usize = 0,
23 indent_delta: usize,
24 current_line_empty: bool = true,
25 indent_one_shot_count: usize = 0, // automatically popped when applied
26 applied_indent: usize = 0, // the most recently applied indent
27 indent_next_line: usize = 0, // not used until the next line
28
29 pub fn writer(self: *Self) Writer {
30 return .{ .context = self };
31 }
32
33 pub fn write(self: *Self, bytes: []const u8) Error!usize {
34 if (bytes.len == 0)
35 return @as(usize, 0);
36
37 try self.applyIndent();
38 return self.writeNoIndent(bytes);
39 }
40
41 // Change the indent delta without changing the final indentation level
42 pub fn setIndentDelta(self: *Self, indent_delta: usize) void {
43 if (self.indent_delta == indent_delta) {
44 return;
45 } else if (self.indent_delta > indent_delta) {
46 assert(self.indent_delta % indent_delta == 0);
47 self.indent_count = self.indent_count * (self.indent_delta / indent_delta);
48 } else {
49 // assert that the current indentation (in spaces) in a multiple of the new delta
50 assert((self.indent_count * self.indent_delta) % indent_delta == 0);
51 self.indent_count = self.indent_count / (indent_delta / self.indent_delta);
52 }
53 self.indent_delta = indent_delta;
54 }
55
56 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
57 if (bytes.len == 0)
58 return @as(usize, 0);
59
60 try self.underlying_writer.writeAll(bytes);
61 if (bytes[bytes.len - 1] == '\n')
62 self.resetLine();
63 return bytes.len;
64 }
65
66 pub fn insertNewline(self: *Self) Error!void {
67 _ = try self.writeNoIndent("\n");
68 }
69
70 fn resetLine(self: *Self) void {
71 self.current_line_empty = true;
72 self.indent_next_line = 0;
73 }
74
75 /// Insert a newline unless the current line is blank
76 pub fn maybeInsertNewline(self: *Self) Error!void {
77 if (!self.current_line_empty)
78 try self.insertNewline();
79 }
80
81 /// Push default indentation
82 pub fn pushIndent(self: *Self) void {
83 // Doesn't actually write any indentation.
84 // Just primes the stream to be able to write the correct indentation if it needs to.
85 self.indent_count += 1;
86 }
87
88 /// Push an indent that is automatically popped after being applied
89 pub fn pushIndentOneShot(self: *Self) void {
90 self.indent_one_shot_count += 1;
91 self.pushIndent();
92 }
93
94 /// Turns all one-shot indents into regular indents
95 /// Returns number of indents that must now be manually popped
96 pub fn lockOneShotIndent(self: *Self) usize {
97 var locked_count = self.indent_one_shot_count;
98 self.indent_one_shot_count = 0;
99 return locked_count;
100 }
101
102 /// Push an indent that should not take effect until the next line
103 pub fn pushIndentNextLine(self: *Self) void {
104 self.indent_next_line += 1;
105 self.pushIndent();
106 }
107
108 pub fn popIndent(self: *Self) void {
109 assert(self.indent_count != 0);
110 self.indent_count -= 1;
111
112 if (self.indent_next_line > 0)
113 self.indent_next_line -= 1;
114 }
115
116 /// Writes ' ' bytes if the current line is empty
117 fn applyIndent(self: *Self) Error!void {
118 const current_indent = self.currentIndent();
119 if (self.current_line_empty and current_indent > 0) {
120 try self.underlying_writer.writeByteNTimes(' ', current_indent);
121 self.applied_indent = current_indent;
122 }
123
124 self.indent_count -= self.indent_one_shot_count;
125 self.indent_one_shot_count = 0;
126 self.current_line_empty = false;
127 }
128
129 /// Checks to see if the most recent indentation exceeds the currently pushed indents
130 pub fn isLineOverIndented(self: *Self) bool {
131 if (self.current_line_empty) return false;
132 return self.applied_indent > self.currentIndent();
133 }
134
135 fn currentIndent(self: *Self) usize {
136 var indent_current: usize = 0;
137 if (self.indent_count > 0) {
138 const indent_count = self.indent_count - self.indent_next_line;
139 indent_current = indent_count * self.indent_delta;
140 }
141 return indent_current;
142 }
143 };
144}
145
146pub fn autoIndentingStream(
147 indent_delta: usize,
148 underlying_writer: anytype,
149) AutoIndentingStream(@TypeOf(underlying_writer)) {
150 return AutoIndentingStream(@TypeOf(underlying_writer)){
151 .underlying_writer = underlying_writer,
152 .indent_delta = indent_delta,
153 };
154}
lib/std/zig/render.zig+139-3
...@@ -15,12 +15,14 @@ const asm_indent_delta = 2;...@@ -15,12 +15,14 @@ const asm_indent_delta = 2;
1515
16pub const Error = ast.Tree.RenderError;16pub const Error = ast.Tree.RenderError;
1717
18const Writer = std.ArrayList(u8).Writer;18const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);
19const Ais = std.io.AutoIndentingStream(Writer);
2019
21pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {20pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
22 assert(tree.errors.len == 0); // Cannot render an invalid tree.21 assert(tree.errors.len == 0); // Cannot render an invalid tree.
23 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, buffer.writer());22 var auto_indenting_stream = Ais{
23 .indent_delta = indent_delta,
24 .underlying_writer = buffer.writer(),
25 };
24 const ais = &auto_indenting_stream;26 const ais = &auto_indenting_stream;
2527
26 // Render all the line comments at the beginning of the file.28 // Render all the line comments at the beginning of the file.
...@@ -2132,3 +2134,137 @@ fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {...@@ -2132,3 +2134,137 @@ fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {
2132 else => false,2134 else => false,
2133 };2135 };
2134}2136}
2137
2138/// Automatically inserts indentation of written data by keeping
2139/// track of the current indentation level
2140fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
2141 return struct {
2142 const Self = @This();
2143 pub const Error = UnderlyingWriter.Error;
2144 pub const Writer = std.io.Writer(*Self, Error, write);
2145
2146 underlying_writer: UnderlyingWriter,
2147
2148 indent_count: usize = 0,
2149 indent_delta: usize,
2150 current_line_empty: bool = true,
2151 indent_one_shot_count: usize = 0, // automatically popped when applied
2152 applied_indent: usize = 0, // the most recently applied indent
2153 indent_next_line: usize = 0, // not used until the next line
2154
2155 pub fn writer(self: *Self) Writer {
2156 return .{ .context = self };
2157 }
2158
2159 pub fn write(self: *Self, bytes: []const u8) Error!usize {
2160 if (bytes.len == 0)
2161 return @as(usize, 0);
2162
2163 try self.applyIndent();
2164 return self.writeNoIndent(bytes);
2165 }
2166
2167 // Change the indent delta without changing the final indentation level
2168 pub fn setIndentDelta(self: *Self, new_indent_delta: usize) void {
2169 if (self.indent_delta == new_indent_delta) {
2170 return;
2171 } else if (self.indent_delta > new_indent_delta) {
2172 assert(self.indent_delta % new_indent_delta == 0);
2173 self.indent_count = self.indent_count * (self.indent_delta / new_indent_delta);
2174 } else {
2175 // assert that the current indentation (in spaces) in a multiple of the new delta
2176 assert((self.indent_count * self.indent_delta) % new_indent_delta == 0);
2177 self.indent_count = self.indent_count / (new_indent_delta / self.indent_delta);
2178 }
2179 self.indent_delta = new_indent_delta;
2180 }
2181
2182 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
2183 if (bytes.len == 0)
2184 return @as(usize, 0);
2185
2186 try self.underlying_writer.writeAll(bytes);
2187 if (bytes[bytes.len - 1] == '\n')
2188 self.resetLine();
2189 return bytes.len;
2190 }
2191
2192 pub fn insertNewline(self: *Self) Error!void {
2193 _ = try self.writeNoIndent("\n");
2194 }
2195
2196 fn resetLine(self: *Self) void {
2197 self.current_line_empty = true;
2198 self.indent_next_line = 0;
2199 }
2200
2201 /// Insert a newline unless the current line is blank
2202 pub fn maybeInsertNewline(self: *Self) Error!void {
2203 if (!self.current_line_empty)
2204 try self.insertNewline();
2205 }
2206
2207 /// Push default indentation
2208 pub fn pushIndent(self: *Self) void {
2209 // Doesn't actually write any indentation.
2210 // Just primes the stream to be able to write the correct indentation if it needs to.
2211 self.indent_count += 1;
2212 }
2213
2214 /// Push an indent that is automatically popped after being applied
2215 pub fn pushIndentOneShot(self: *Self) void {
2216 self.indent_one_shot_count += 1;
2217 self.pushIndent();
2218 }
2219
2220 /// Turns all one-shot indents into regular indents
2221 /// Returns number of indents that must now be manually popped
2222 pub fn lockOneShotIndent(self: *Self) usize {
2223 var locked_count = self.indent_one_shot_count;
2224 self.indent_one_shot_count = 0;
2225 return locked_count;
2226 }
2227
2228 /// Push an indent that should not take effect until the next line
2229 pub fn pushIndentNextLine(self: *Self) void {
2230 self.indent_next_line += 1;
2231 self.pushIndent();
2232 }
2233
2234 pub fn popIndent(self: *Self) void {
2235 assert(self.indent_count != 0);
2236 self.indent_count -= 1;
2237
2238 if (self.indent_next_line > 0)
2239 self.indent_next_line -= 1;
2240 }
2241
2242 /// Writes ' ' bytes if the current line is empty
2243 fn applyIndent(self: *Self) Error!void {
2244 const current_indent = self.currentIndent();
2245 if (self.current_line_empty and current_indent > 0) {
2246 try self.underlying_writer.writeByteNTimes(' ', current_indent);
2247 self.applied_indent = current_indent;
2248 }
2249
2250 self.indent_count -= self.indent_one_shot_count;
2251 self.indent_one_shot_count = 0;
2252 self.current_line_empty = false;
2253 }
2254
2255 /// Checks to see if the most recent indentation exceeds the currently pushed indents
2256 pub fn isLineOverIndented(self: *Self) bool {
2257 if (self.current_line_empty) return false;
2258 return self.applied_indent > self.currentIndent();
2259 }
2260
2261 fn currentIndent(self: *Self) usize {
2262 var indent_current: usize = 0;
2263 if (self.indent_count > 0) {
2264 const indent_count = self.indent_count - self.indent_next_line;
2265 indent_current = indent_count * self.indent_delta;
2266 }
2267 return indent_current;
2268 }
2269 };
2270}
src/codegen/c.zig+56-2
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;
2const mem = std.mem;3const mem = std.mem;
3const log = std.log.scoped(.c);4const log = std.log.scoped(.c);
45
...@@ -42,7 +43,7 @@ pub const Object = struct {...@@ -42,7 +43,7 @@ pub const Object = struct {
42 next_arg_index: usize = 0,43 next_arg_index: usize = 0,
43 next_local_index: usize = 0,44 next_local_index: usize = 0,
44 next_block_index: usize = 0,45 next_block_index: usize = 0,
45 indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer),46 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
4647
47 fn resolveInst(o: *Object, inst: *Inst) !CValue {48 fn resolveInst(o: *Object, inst: *Inst) !CValue {
48 if (inst.value()) |_| {49 if (inst.value()) |_| {
...@@ -63,7 +64,7 @@ pub const Object = struct {...@@ -63,7 +64,7 @@ pub const Object = struct {
63 return local_value;64 return local_value;
64 }65 }
6566
66 fn writer(o: *Object) std.io.AutoIndentingStream(std.ArrayList(u8).Writer).Writer {67 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
67 return o.indent_writer.writer();68 return o.indent_writer.writer();
68 }69 }
6970
...@@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {...@@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
796797
797 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});798 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
798}799}
800
801fn IndentWriter(comptime UnderlyingWriter: type) type {
802 return struct {
803 const Self = @This();
804 pub const Error = UnderlyingWriter.Error;
805 pub const Writer = std.io.Writer(*Self, Error, write);
806
807 pub const indent_delta = 4;
808
809 underlying_writer: UnderlyingWriter,
810 indent_count: usize = 0,
811 current_line_empty: bool = true,
812
813 pub fn writer(self: *Self) Writer {
814 return .{ .context = self };
815 }
816
817 pub fn write(self: *Self, bytes: []const u8) Error!usize {
818 if (bytes.len == 0) return @as(usize, 0);
819
820 const current_indent = self.indent_count * Self.indent_delta;
821 if (self.current_line_empty and current_indent > 0) {
822 try self.underlying_writer.writeByteNTimes(' ', current_indent);
823 }
824 self.current_line_empty = false;
825
826 return self.writeNoIndent(bytes);
827 }
828
829 pub fn insertNewline(self: *Self) Error!void {
830 _ = try self.writeNoIndent("\n");
831 }
832
833 pub fn pushIndent(self: *Self) void {
834 self.indent_count += 1;
835 }
836
837 pub fn popIndent(self: *Self) void {
838 assert(self.indent_count != 0);
839 self.indent_count -= 1;
840 }
841
842 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
843 if (bytes.len == 0) return @as(usize, 0);
844
845 try self.underlying_writer.writeAll(bytes);
846 if (bytes[bytes.len - 1] == '\n') {
847 self.current_line_empty = true;
848 }
849 return bytes.len;
850 }
851 };
852}
src/link/C.zig+1-1
...@@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {...@@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
97 .value_map = codegen.CValueMap.init(module.gpa),97 .value_map = codegen.CValueMap.init(module.gpa),
98 .indent_writer = undefined, // set later so we can get a pointer to object.code98 .indent_writer = undefined, // set later so we can get a pointer to object.code
99 };99 };
100 object.indent_writer = std.io.autoIndentingStream(4, object.code.writer());100 object.indent_writer = .{ .underlying_writer = object.code.writer() };
101 defer object.value_map.deinit();101 defer object.value_map.deinit();
102 defer object.code.deinit();102 defer object.code.deinit();
103 defer object.dg.fwd_decl.deinit();103 defer object.dg.fwd_decl.deinit();