authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-15 22:27:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-16 17:20:02-07:00
log2d5d2ba4f51fa5300c9807b477cada2c9b3023cd
tree3f554a622d475367895801b96fd2ca3bb2042d73
parent0389b4c7b9b83434daab05e8b94da315c61166ce

std.zig.Render: update it and references


7 files changed, 388 insertions(+), 336 deletions(-)

lib/std/Io/Writer.zig+6
......@@ -2475,6 +2475,12 @@ pub const Allocating = struct {
24752475 return result;
24762476 }
24772477
2478 pub fn ensureUnusedCapacity(a: *Allocating, additional_count: usize) Allocator.Error!void {
2479 var list = a.toArrayList();
2480 defer a.setArrayList(list);
2481 return list.ensureUnusedCapacity(a.allocator, additional_count);
2482 }
2483
24782484 pub fn toOwnedSlice(a: *Allocating) error{OutOfMemory}![]u8 {
24792485 var list = a.toArrayList();
24802486 defer a.setArrayList(list);
lib/std/zig/Ast.zig+2-2
......@@ -207,7 +207,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) error{OutOfMemory}![]u8 {
207207 var aw: std.io.Writer.Allocating = .init(gpa);
208208 defer aw.deinit();
209209 render(tree, gpa, &aw.writer, .{}) catch |err| switch (err) {
210 error.WriteFailed => return error.OutOfMemory,
210 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
211211 };
212212 return aw.toOwnedSlice();
213213}
......@@ -215,7 +215,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) error{OutOfMemory}![]u8 {
215215pub const Render = @import("Ast/Render.zig");
216216
217217pub fn render(tree: Ast, gpa: Allocator, w: *Writer, fixups: Render.Fixups) Render.Error!void {
218 return Render.tree(gpa, w, tree, fixups);
218 return Render.renderTree(gpa, w, tree, fixups);
219219}
220220
221221/// Returns an extra offset for column and byte offset of errors that
lib/std/zig/Ast/Render.zig+278-254
......@@ -1,4 +1,4 @@
1const std = @import("../std.zig");
1const std = @import("../../std.zig");
22const assert = std.debug.assert;
33const mem = std.mem;
44const Allocator = std.mem.Allocator;
......@@ -6,13 +6,24 @@ const meta = std.meta;
66const Ast = std.zig.Ast;
77const Token = std.zig.Token;
88const primitives = std.zig.primitives;
9const Writer = std.io.Writer;
10
11const Render = @This();
12
13gpa: Allocator,
14ais: *AutoIndentingStream,
15tree: Ast,
16fixups: Fixups,
917
1018const indent_delta = 4;
1119const asm_indent_delta = 2;
1220
13pub const Error = Ast.RenderError;
14
15const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);
21pub const Error = error{
22 /// Ran out of memory allocating call stack frames to complete rendering.
23 OutOfMemory,
24 /// Transitive failure from
25 WriteFailed,
26};
1627
1728pub const Fixups = struct {
1829 /// The key is the mut token (`var`/`const`) of the variable declaration
......@@ -72,19 +83,12 @@ pub const Fixups = struct {
7283 }
7384};
7485
75const Render = struct {
76 gpa: Allocator,
77 ais: *Ais,
78 tree: Ast,
79 fixups: Fixups,
80};
81
82pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast, fixups: Fixups) Error!void {
86pub fn renderTree(gpa: Allocator, w: *Writer, tree: Ast, fixups: Fixups) Error!void {
8387 assert(tree.errors.len == 0); // Cannot render an invalid tree.
84 var auto_indenting_stream = Ais.init(buffer, indent_delta);
88 var auto_indenting_stream: AutoIndentingStream = .init(gpa, w, indent_delta);
8589 defer auto_indenting_stream.deinit();
8690 var r: Render = .{
87 .gpa = buffer.allocator,
91 .gpa = gpa,
8892 .ais = &auto_indenting_stream,
8993 .tree = tree,
9094 .fixups = fixups,
......@@ -186,7 +190,7 @@ fn renderMember(
186190 if (opt_callconv_expr.unwrap()) |callconv_expr| {
187191 if (tree.nodeTag(callconv_expr) == .enum_literal) {
188192 if (mem.eql(u8, "@\"inline\"", tree.tokenSlice(tree.nodeMainToken(callconv_expr)))) {
189 try ais.writer().writeAll("inline ");
193 try ais.underlying_writer.writeAll("inline ");
190194 }
191195 }
192196 }
......@@ -200,7 +204,7 @@ fn renderMember(
200204 const lbrace = tree.nodeMainToken(body_node);
201205 try renderToken(r, lbrace, .newline);
202206 try discardAllParams(r, fn_proto);
203 try ais.writer().writeAll("@trap();");
207 try ais.writeAll("@trap();");
204208 ais.popIndent();
205209 try ais.insertNewline();
206210 try renderToken(r, tree.lastToken(body_node), space); // rbrace
......@@ -216,10 +220,9 @@ fn renderMember(
216220 const name_ident = param.name_token.?;
217221 assert(tree.tokenTag(name_ident) == .identifier);
218222 if (r.fixups.unused_var_decls.contains(name_ident)) {
219 const w = ais.writer();
220 try w.writeAll("_ = ");
221 try w.writeAll(tokenSliceForRender(r.tree, name_ident));
222 try w.writeAll(";\n");
223 try ais.writeAll("_ = ");
224 try ais.writeAll(tokenSliceForRender(r.tree, name_ident));
225 try ais.writeAll(";\n");
223226 }
224227 }
225228 var statements_buf: [2]Ast.Node.Index = undefined;
......@@ -312,7 +315,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
312315 const tree = r.tree;
313316 const ais = r.ais;
314317 if (r.fixups.replace_nodes_with_string.get(node)) |replacement| {
315 try ais.writer().writeAll(replacement);
318 try ais.writeAll(replacement);
316319 try renderOnlySpace(r, space);
317320 return;
318321 } else if (r.fixups.replace_nodes_with_node.get(node)) |replacement| {
......@@ -881,7 +884,7 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v
881884 const ais = r.ais;
882885 try renderExpression(r, node, space);
883886 if (r.fixups.append_string_after_node.get(node)) |bytes| {
884 try ais.writer().writeAll(bytes);
887 try ais.writeAll(bytes);
885888 }
886889}
887890
......@@ -1086,10 +1089,10 @@ fn renderVarDecl(
10861089 try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space);
10871090 if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) {
10881091 // Discard the variable like this: `_ = foo;`
1089 const w = r.ais.writer();
1090 try w.writeAll("_ = ");
1091 try w.writeAll(tokenSliceForRender(r.tree, var_decl.ast.mut_token + 1));
1092 try w.writeAll(";\n");
1092 const ais = r.ais;
1093 try ais.writeAll("_ = ");
1094 try ais.writeAll(tokenSliceForRender(r.tree, var_decl.ast.mut_token + 1));
1095 try ais.writeAll(";\n");
10931096 }
10941097}
10951098
......@@ -1567,7 +1570,7 @@ fn renderBuiltinCall(
15671570 defer r.gpa.free(new_string);
15681571
15691572 try renderToken(r, builtin_token + 1, .none); // (
1570 try ais.writer().print("\"{f}\"", .{std.zig.fmtString(new_string)});
1573 try ais.print("\"{f}\"", .{std.zig.fmtString(new_string)});
15711574 return renderToken(r, str_lit_token + 1, space); // )
15721575 }
15731576 }
......@@ -2125,13 +2128,13 @@ fn renderArrayInit(
21252128
21262129 const section_exprs = row_exprs[0..section_end];
21272130
2128 var sub_expr_buffer = std.ArrayList(u8).init(gpa);
2131 var sub_expr_buffer: std.io.Writer.Allocating = .init(gpa);
21292132 defer sub_expr_buffer.deinit();
21302133
21312134 const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);
21322135 defer gpa.free(sub_expr_buffer_starts);
21332136
2134 var auto_indenting_stream = Ais.init(&sub_expr_buffer, indent_delta);
2137 var auto_indenting_stream: AutoIndentingStream = .init(gpa, &sub_expr_buffer.writer, indent_delta);
21352138 defer auto_indenting_stream.deinit();
21362139 var sub_render: Render = .{
21372140 .gpa = r.gpa,
......@@ -2145,13 +2148,14 @@ fn renderArrayInit(
21452148 var single_line = true;
21462149 var contains_newline = false;
21472150 for (section_exprs, 0..) |expr, i| {
2148 const start = sub_expr_buffer.items.len;
2151 const start = sub_expr_buffer.getWritten().len;
21492152 sub_expr_buffer_starts[i] = start;
21502153
21512154 if (i + 1 < section_exprs.len) {
21522155 try renderExpression(&sub_render, expr, .none);
2153 const width = sub_expr_buffer.items.len - start;
2154 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start..], '\n') != null;
2156 const written = sub_expr_buffer.getWritten();
2157 const width = written.len - start;
2158 const this_contains_newline = mem.indexOfScalar(u8, written[start..], '\n') != null;
21552159 contains_newline = contains_newline or this_contains_newline;
21562160 expr_widths[i] = width;
21572161 expr_newlines[i] = this_contains_newline;
......@@ -2173,8 +2177,9 @@ fn renderArrayInit(
21732177 try renderExpression(&sub_render, expr, .comma);
21742178 ais.popSpace();
21752179
2176 const width = sub_expr_buffer.items.len - start - 2;
2177 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start .. sub_expr_buffer.items.len - 1], '\n') != null;
2180 const written = sub_expr_buffer.getWritten();
2181 const width = written.len - start - 2;
2182 const this_contains_newline = mem.indexOfScalar(u8, written[start .. written.len - 1], '\n') != null;
21782183 contains_newline = contains_newline or this_contains_newline;
21792184 expr_widths[i] = width;
21802185 expr_newlines[i] = contains_newline;
......@@ -2185,20 +2190,20 @@ fn renderArrayInit(
21852190 }
21862191 }
21872192 }
2188 sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.items.len;
2193 sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.getWritten().len;
21892194
21902195 // Render exprs in current section.
21912196 column_counter = 0;
21922197 for (section_exprs, 0..) |expr, i| {
21932198 const start = sub_expr_buffer_starts[i];
21942199 const end = sub_expr_buffer_starts[i + 1];
2195 const expr_text = sub_expr_buffer.items[start..end];
2200 const expr_text = sub_expr_buffer.getWritten()[start..end];
21962201 if (!expr_newlines[i]) {
2197 try ais.writer().writeAll(expr_text);
2202 try ais.writeAll(expr_text);
21982203 } else {
21992204 var by_line = std.mem.splitScalar(u8, expr_text, '\n');
22002205 var last_line_was_empty = false;
2201 try ais.writer().writeAll(by_line.first());
2206 try ais.writeAll(by_line.first());
22022207 while (by_line.next()) |line| {
22032208 if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) {
22042209 try ais.insertNewline();
......@@ -2206,7 +2211,7 @@ fn renderArrayInit(
22062211 try ais.maybeInsertNewline();
22072212 }
22082213 last_line_was_empty = (line.len == 0);
2209 try ais.writer().writeAll(line);
2214 try ais.writeAll(line);
22102215 }
22112216 }
22122217
......@@ -2220,7 +2225,7 @@ fn renderArrayInit(
22202225 try renderToken(r, comma, .space); // ,
22212226 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
22222227 const padding = column_widths[column_counter % row_size] - expr_widths[i];
2223 try ais.writer().writeByteNTimes(' ', padding);
2228 try ais.splatByteAll(' ', padding);
22242229
22252230 column_counter += 1;
22262231 continue;
......@@ -2799,7 +2804,7 @@ fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) Error!void
27992804 const tree = r.tree;
28002805 const ais = r.ais;
28012806 const lexeme = tokenSliceForRender(tree, token_index);
2802 try ais.writer().writeAll(lexeme);
2807 try ais.writeAll(lexeme);
28032808 try renderSpace(r, token_index, lexeme.len, space);
28042809}
28052810
......@@ -2807,7 +2812,7 @@ fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space:
28072812 const tree = r.tree;
28082813 const ais = r.ais;
28092814 const lexeme = tokenSliceForRender(tree, token_index);
2810 try ais.writer().writeAll(lexeme);
2815 try ais.writeAll(lexeme);
28112816 ais.enableSpaceMode(override_space);
28122817 defer ais.disableSpaceMode();
28132818 try renderSpace(r, token_index, lexeme.len, space);
......@@ -2822,7 +2827,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
28222827 if (space == .skip) return;
28232828
28242829 if (space == .comma and next_token_tag != .comma) {
2825 try ais.writer().writeByte(',');
2830 try ais.writeByte(',');
28262831 }
28272832 if (space == .semicolon or space == .comma) ais.enableSpaceMode(space);
28282833 defer ais.disableSpaceMode();
......@@ -2833,7 +2838,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
28332838 );
28342839 switch (space) {
28352840 .none => {},
2836 .space => if (!comment) try ais.writer().writeByte(' '),
2841 .space => if (!comment) try ais.writeByte(' '),
28372842 .newline => if (!comment) try ais.insertNewline(),
28382843
28392844 .comma => if (next_token_tag == .comma) {
......@@ -2845,7 +2850,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
28452850 .comma_space => if (next_token_tag == .comma) {
28462851 try renderToken(r, token_index + 1, .space);
28472852 } else if (!comment) {
2848 try ais.writer().writeByte(' ');
2853 try ais.writeByte(' ');
28492854 },
28502855
28512856 .semicolon => if (next_token_tag == .semicolon) {
......@@ -2862,11 +2867,11 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {
28622867 const ais = r.ais;
28632868 switch (space) {
28642869 .none => {},
2865 .space => try ais.writer().writeByte(' '),
2870 .space => try ais.writeByte(' '),
28662871 .newline => try ais.insertNewline(),
2867 .comma => try ais.writer().writeAll(",\n"),
2868 .comma_space => try ais.writer().writeAll(", "),
2869 .semicolon => try ais.writer().writeAll(";\n"),
2872 .comma => try ais.writeAll(",\n"),
2873 .comma_space => try ais.writeAll(", "),
2874 .semicolon => try ais.writeAll(";\n"),
28702875 .skip => unreachable,
28712876 }
28722877}
......@@ -2883,7 +2888,7 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote
28832888 const lexeme = tokenSliceForRender(tree, token_index);
28842889
28852890 if (r.fixups.rename_identifiers.get(lexeme)) |mangled| {
2886 try r.ais.writer().writeAll(mangled);
2891 try r.ais.writeAll(mangled);
28872892 try renderSpace(r, token_index, lexeme.len, space);
28882893 return;
28892894 }
......@@ -2992,15 +2997,15 @@ fn renderQuotedIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space,
29922997 const lexeme = tokenSliceForRender(tree, token_index);
29932998 assert(lexeme.len >= 3 and lexeme[0] == '@');
29942999
2995 if (!unquote) try ais.writer().writeAll("@\"");
3000 if (!unquote) try ais.writeAll("@\"");
29963001 const contents = lexeme[2 .. lexeme.len - 1];
2997 try renderIdentifierContents(ais.writer(), contents);
2998 if (!unquote) try ais.writer().writeByte('\"');
3002 try renderIdentifierContents(ais, contents);
3003 if (!unquote) try ais.writeByte('\"');
29993004
30003005 try renderSpace(r, token_index, lexeme.len, space);
30013006}
30023007
3003fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {
3008fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void {
30043009 var pos: usize = 0;
30053010 while (pos < bytes.len) {
30063011 const byte = bytes[pos];
......@@ -3013,23 +3018,23 @@ fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {
30133018 .success => |codepoint| {
30143019 if (codepoint <= 0x7f) {
30153020 const buf = [1]u8{@as(u8, @intCast(codepoint))};
3016 try std.fmt.format(writer, "{f}", .{std.zig.fmtString(&buf)});
3021 try ais.print("{f}", .{std.zig.fmtString(&buf)});
30173022 } else {
3018 try writer.writeAll(escape_sequence);
3023 try ais.writeAll(escape_sequence);
30193024 }
30203025 },
30213026 .failure => {
3022 try writer.writeAll(escape_sequence);
3027 try ais.writeAll(escape_sequence);
30233028 },
30243029 }
30253030 },
30263031 0x00...('\\' - 1), ('\\' + 1)...0x7f => {
30273032 const buf = [1]u8{byte};
3028 try std.fmt.format(writer, "{f}", .{std.zig.fmtString(&buf)});
3033 try ais.print("{f}", .{std.zig.fmtString(&buf)});
30293034 pos += 1;
30303035 },
30313036 0x80...0xff => {
3032 try writer.writeByte(byte);
3037 try ais.writeByte(byte);
30333038 pos += 1;
30343039 },
30353040 }
......@@ -3091,7 +3096,7 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
30913096 } else if (index == start) {
30923097 // Otherwise if the first comment is on the same line as
30933098 // the token before it, prefix it with a single space.
3094 try ais.writer().writeByte(' ');
3099 try ais.writeByte(' ');
30953100 }
30963101 }
30973102
......@@ -3108,11 +3113,11 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
31083113 ais.disabled_offset = null;
31093114 } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) {
31103115 // Write with the canonical single space.
3111 try ais.writer().writeAll("// zig fmt: off\n");
3116 try ais.writeAll("// zig fmt: off\n");
31123117 ais.disabled_offset = index;
31133118 } else {
31143119 // Write the comment minus trailing whitespace.
3115 try ais.writer().print("{s}\n", .{trimmed_comment});
3120 try ais.print("{s}\n", .{trimmed_comment});
31163121 }
31173122 }
31183123
......@@ -3213,10 +3218,9 @@ fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) Error!void {
32133218 while (it.next()) |param| {
32143219 const name_ident = param.name_token.?;
32153220 assert(tree.tokenTag(name_ident) == .identifier);
3216 const w = ais.writer();
3217 try w.writeAll("_ = ");
3218 try w.writeAll(tokenSliceForRender(r.tree, name_ident));
3219 try w.writeAll(";\n");
3221 try ais.writeAll("_ = ");
3222 try ais.writeAll(tokenSliceForRender(r.tree, name_ident));
3223 try ais.writeAll(";\n");
32203224 }
32213225}
32223226
......@@ -3269,11 +3273,11 @@ fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenI
32693273 return false;
32703274}
32713275
3272fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void {
3276fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {
32733277 for (slice) |byte| switch (byte) {
3274 '\t' => try writer.writeAll(" " ** indent_delta),
3278 '\t' => try w.splatByteAll(' ', indent_delta),
32753279 '\r' => {},
3276 else => try writer.writeByte(byte),
3280 else => try w.writeByte(byte),
32773281 };
32783282}
32793283
......@@ -3398,224 +3402,244 @@ fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usi
33983402/// of the appropriate indentation level for them with pushSpace/popSpace.
33993403/// This should be done whenever a scope that ends in a .semicolon or a
34003404/// .comma is introduced.
3401fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
3402 return struct {
3403 const Self = @This();
3404 pub const WriteError = UnderlyingWriter.Error;
3405 pub const Writer = std.io.GenericWriter(*Self, WriteError, write);
3406
3407 pub const IndentType = enum {
3408 normal,
3409 after_equals,
3410 binop,
3411 field_access,
3412 };
3413 const StackElem = struct {
3414 indent_type: IndentType,
3415 realized: bool,
3416 };
3417 const SpaceElem = struct {
3418 space: Space,
3419 indent_count: usize,
3405const AutoIndentingStream = struct {
3406 underlying_writer: *Writer,
3407
3408 /// Offset into the source at which formatting has been disabled with
3409 /// a `zig fmt: off` comment.
3410 ///
3411 /// If non-null, the AutoIndentingStream will not write any bytes
3412 /// to the underlying writer. It will however continue to track the
3413 /// indentation level.
3414 disabled_offset: ?usize = null,
3415
3416 indent_count: usize = 0,
3417 indent_delta: usize,
3418 indent_stack: std.ArrayList(StackElem),
3419 space_stack: std.ArrayList(SpaceElem),
3420 space_mode: ?usize = null,
3421 disable_indent_committing: usize = 0,
3422 current_line_empty: bool = true,
3423 /// the most recently applied indent
3424 applied_indent: usize = 0,
3425
3426 pub const IndentType = enum {
3427 normal,
3428 after_equals,
3429 binop,
3430 field_access,
3431 };
3432 const StackElem = struct {
3433 indent_type: IndentType,
3434 realized: bool,
3435 };
3436 const SpaceElem = struct {
3437 space: Space,
3438 indent_count: usize,
3439 };
3440
3441 pub fn init(gpa: Allocator, w: *Writer, starting_indent_delta: usize) AutoIndentingStream {
3442 return .{
3443 .underlying_writer = w,
3444 .indent_delta = starting_indent_delta,
3445 .indent_stack = .init(gpa),
3446 .space_stack = .init(gpa),
34203447 };
3448 }
34213449
3422 underlying_writer: UnderlyingWriter,
3423
3424 /// Offset into the source at which formatting has been disabled with
3425 /// a `zig fmt: off` comment.
3426 ///
3427 /// If non-null, the AutoIndentingStream will not write any bytes
3428 /// to the underlying writer. It will however continue to track the
3429 /// indentation level.
3430 disabled_offset: ?usize = null,
3431
3432 indent_count: usize = 0,
3433 indent_delta: usize,
3434 indent_stack: std.ArrayList(StackElem),
3435 space_stack: std.ArrayList(SpaceElem),
3436 space_mode: ?usize = null,
3437 disable_indent_committing: usize = 0,
3438 current_line_empty: bool = true,
3439 /// the most recently applied indent
3440 applied_indent: usize = 0,
3441
3442 pub fn init(buffer: *std.ArrayList(u8), indent_delta_: usize) Self {
3443 return .{
3444 .underlying_writer = buffer.writer(),
3445 .indent_delta = indent_delta_,
3446 .indent_stack = std.ArrayList(StackElem).init(buffer.allocator),
3447 .space_stack = std.ArrayList(SpaceElem).init(buffer.allocator),
3448 };
3449 }
3450 pub fn deinit(self: *AutoIndentingStream) void {
3451 self.indent_stack.deinit();
3452 self.space_stack.deinit();
3453 }
34503454
3451 pub fn deinit(self: *Self) void {
3452 self.indent_stack.deinit();
3453 self.space_stack.deinit();
3454 }
3455 pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) Error!void {
3456 if (bytes.len == 0) return;
3457 try ais.applyIndent();
3458 if (ais.disabled_offset == null) try ais.underlying_writer.writeAll(bytes);
3459 if (bytes[bytes.len - 1] == '\n') ais.resetLine();
3460 }
34553461
3456 pub fn writer(self: *Self) Writer {
3457 return .{ .context = self };
3458 }
3462 /// Assumes that if the printed data ends with a newline, it is directly
3463 /// contained in the format string.
3464 pub fn print(ais: *AutoIndentingStream, comptime format: []const u8, args: anytype) Error!void {
3465 try ais.applyIndent();
3466 if (ais.disabled_offset == null) try ais.underlying_writer.print(format, args);
3467 if (format[format.len - 1] == '\n') ais.resetLine();
3468 }
34593469
3460 pub fn write(self: *Self, bytes: []const u8) WriteError!usize {
3461 if (bytes.len == 0)
3462 return @as(usize, 0);
3470 pub fn writeByte(ais: *AutoIndentingStream, byte: u8) Error!void {
3471 try ais.applyIndent();
3472 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte(byte);
3473 assert(byte != '\n');
3474 }
34633475
3464 try self.applyIndent();
3465 return self.writeNoIndent(bytes);
3466 }
3476 pub fn splatByteAll(ais: *AutoIndentingStream, byte: u8, n: usize) Error!void {
3477 assert(byte != '\n');
3478 try ais.applyIndent();
3479 if (ais.disabled_offset == null) try ais.underlying_writer.splatByteAll(byte, n);
3480 }
34673481
3468 // Change the indent delta without changing the final indentation level
3469 pub fn setIndentDelta(self: *Self, new_indent_delta: usize) void {
3470 if (self.indent_delta == new_indent_delta) {
3471 return;
3472 } else if (self.indent_delta > new_indent_delta) {
3473 assert(self.indent_delta % new_indent_delta == 0);
3474 self.indent_count = self.indent_count * (self.indent_delta / new_indent_delta);
3475 } else {
3476 // assert that the current indentation (in spaces) in a multiple of the new delta
3477 assert((self.indent_count * self.indent_delta) % new_indent_delta == 0);
3478 self.indent_count = self.indent_count / (new_indent_delta / self.indent_delta);
3479 }
3480 self.indent_delta = new_indent_delta;
3482 // Change the indent delta without changing the final indentation level
3483 pub fn setIndentDelta(ais: *AutoIndentingStream, new_indent_delta: usize) void {
3484 if (ais.indent_delta == new_indent_delta) {
3485 return;
3486 } else if (ais.indent_delta > new_indent_delta) {
3487 assert(ais.indent_delta % new_indent_delta == 0);
3488 ais.indent_count = ais.indent_count * (ais.indent_delta / new_indent_delta);
3489 } else {
3490 // assert that the current indentation (in spaces) in a multiple of the new delta
3491 assert((ais.indent_count * ais.indent_delta) % new_indent_delta == 0);
3492 ais.indent_count = ais.indent_count / (new_indent_delta / ais.indent_delta);
34813493 }
3494 ais.indent_delta = new_indent_delta;
3495 }
34823496
3483 fn writeNoIndent(self: *Self, bytes: []const u8) WriteError!usize {
3484 if (bytes.len == 0)
3485 return @as(usize, 0);
3497 pub fn insertNewline(ais: *AutoIndentingStream) Error!void {
3498 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte('\n');
3499 ais.resetLine();
3500 }
34863501
3487 if (self.disabled_offset == null) try self.underlying_writer.writeAll(bytes);
3488 if (bytes[bytes.len - 1] == '\n')
3489 self.resetLine();
3490 return bytes.len;
3491 }
3502 /// Insert a newline unless the current line is blank
3503 pub fn maybeInsertNewline(ais: *AutoIndentingStream) Error!void {
3504 if (!ais.current_line_empty)
3505 try ais.insertNewline();
3506 }
34923507
3493 pub fn insertNewline(self: *Self) WriteError!void {
3494 _ = try self.writeNoIndent("\n");
3495 }
3508 /// Push an indent that is automatically popped after being applied
3509 pub fn pushIndentOneShot(ais: *AutoIndentingStream) void {
3510 ais.indent_one_shot_count += 1;
3511 ais.pushIndent();
3512 }
34963513
3497 fn resetLine(self: *Self) void {
3498 self.current_line_empty = true;
3514 /// Turns all one-shot indents into regular indents
3515 /// Returns number of indents that must now be manually popped
3516 pub fn lockOneShotIndent(ais: *AutoIndentingStream) usize {
3517 const locked_count = ais.indent_one_shot_count;
3518 ais.indent_one_shot_count = 0;
3519 return locked_count;
3520 }
34993521
3500 if (self.disable_indent_committing > 0) return;
3522 /// Push an indent that should not take effect until the next line
3523 pub fn pushIndentNextLine(ais: *AutoIndentingStream) void {
3524 ais.indent_next_line += 1;
3525 ais.pushIndent();
3526 }
35013527
3502 if (self.indent_stack.items.len > 0) {
3503 // By default, we realize the most recent indentation scope.
3504 var to_realize = self.indent_stack.items.len - 1;
3528 /// Checks to see if the most recent indentation exceeds the currently pushed indents
3529 pub fn isLineOverIndented(ais: *AutoIndentingStream) bool {
3530 if (ais.current_line_empty) return false;
3531 return ais.applied_indent > ais.currentIndent();
3532 }
35053533
3506 if (self.indent_stack.items.len >= 2 and
3507 self.indent_stack.items[to_realize - 1].indent_type == .after_equals and
3508 self.indent_stack.items[to_realize - 1].realized and
3509 self.indent_stack.items[to_realize].indent_type == .binop)
3510 {
3511 // If we are in a .binop scope and our direct parent is .after_equals, don't indent.
3512 // This ensures correct indentation in the below example:
3513 //
3514 // const foo =
3515 // (x >= 'a' and x <= 'z') or //<-- we are here
3516 // (x >= 'A' and x <= 'Z');
3517 //
3518 return;
3519 }
3534 fn resetLine(ais: *AutoIndentingStream) void {
3535 ais.current_line_empty = true;
35203536
3521 if (self.indent_stack.items[to_realize].indent_type == .field_access) {
3522 // Only realize the top-most field_access in a chain.
3523 while (to_realize > 0 and self.indent_stack.items[to_realize - 1].indent_type == .field_access)
3524 to_realize -= 1;
3525 }
3537 if (ais.disable_indent_committing > 0) return;
35263538
3527 if (self.indent_stack.items[to_realize].realized) return;
3528 self.indent_stack.items[to_realize].realized = true;
3529 self.indent_count += 1;
3539 if (ais.indent_stack.items.len > 0) {
3540 // By default, we realize the most recent indentation scope.
3541 var to_realize = ais.indent_stack.items.len - 1;
3542
3543 if (ais.indent_stack.items.len >= 2 and
3544 ais.indent_stack.items[to_realize - 1].indent_type == .after_equals and
3545 ais.indent_stack.items[to_realize - 1].realized and
3546 ais.indent_stack.items[to_realize].indent_type == .binop)
3547 {
3548 // If we are in a .binop scope and our direct parent is .after_equals, don't indent.
3549 // This ensures correct indentation in the below example:
3550 //
3551 // const foo =
3552 // (x >= 'a' and x <= 'z') or //<-- we are here
3553 // (x >= 'A' and x <= 'Z');
3554 //
3555 return;
35303556 }
3531 }
35323557
3533 /// Disables indentation level changes during the next newlines until re-enabled.
3534 pub fn disableIndentCommitting(self: *Self) void {
3535 self.disable_indent_committing += 1;
3536 }
3558 if (ais.indent_stack.items[to_realize].indent_type == .field_access) {
3559 // Only realize the top-most field_access in a chain.
3560 while (to_realize > 0 and ais.indent_stack.items[to_realize - 1].indent_type == .field_access)
3561 to_realize -= 1;
3562 }
35373563
3538 pub fn enableIndentCommitting(self: *Self) void {
3539 assert(self.disable_indent_committing > 0);
3540 self.disable_indent_committing -= 1;
3564 if (ais.indent_stack.items[to_realize].realized) return;
3565 ais.indent_stack.items[to_realize].realized = true;
3566 ais.indent_count += 1;
35413567 }
3568 }
35423569
3543 pub fn pushSpace(self: *Self, space: Space) !void {
3544 try self.space_stack.append(.{ .space = space, .indent_count = self.indent_count });
3545 }
3570 /// Disables indentation level changes during the next newlines until re-enabled.
3571 pub fn disableIndentCommitting(ais: *AutoIndentingStream) void {
3572 ais.disable_indent_committing += 1;
3573 }
35463574
3547 pub fn popSpace(self: *Self) void {
3548 _ = self.space_stack.pop();
3549 }
3575 pub fn enableIndentCommitting(ais: *AutoIndentingStream) void {
3576 assert(ais.disable_indent_committing > 0);
3577 ais.disable_indent_committing -= 1;
3578 }
35503579
3551 /// Sets current indentation level to be the same as that of the last pushSpace.
3552 pub fn enableSpaceMode(self: *Self, space: Space) void {
3553 if (self.space_stack.items.len == 0) return;
3554 const curr = self.space_stack.getLast();
3555 if (curr.space != space) return;
3556 self.space_mode = curr.indent_count;
3557 }
3580 pub fn pushSpace(ais: *AutoIndentingStream, space: Space) !void {
3581 try ais.space_stack.append(.{ .space = space, .indent_count = ais.indent_count });
3582 }
35583583
3559 pub fn disableSpaceMode(self: *Self) void {
3560 self.space_mode = null;
3561 }
3584 pub fn popSpace(ais: *AutoIndentingStream) void {
3585 _ = ais.space_stack.pop();
3586 }
35623587
3563 pub fn lastSpaceModeIndent(self: *Self) usize {
3564 if (self.space_stack.items.len == 0) return 0;
3565 return self.space_stack.getLast().indent_count * self.indent_delta;
3566 }
3588 /// Sets current indentation level to be the same as that of the last pushSpace.
3589 pub fn enableSpaceMode(ais: *AutoIndentingStream, space: Space) void {
3590 if (ais.space_stack.items.len == 0) return;
3591 const curr = ais.space_stack.getLast();
3592 if (curr.space != space) return;
3593 ais.space_mode = curr.indent_count;
3594 }
35673595
3568 /// Insert a newline unless the current line is blank
3569 pub fn maybeInsertNewline(self: *Self) WriteError!void {
3570 if (!self.current_line_empty)
3571 try self.insertNewline();
3572 }
3596 pub fn disableSpaceMode(ais: *AutoIndentingStream) void {
3597 ais.space_mode = null;
3598 }
35733599
3574 /// Push default indentation
3575 /// Doesn't actually write any indentation.
3576 /// Just primes the stream to be able to write the correct indentation if it needs to.
3577 pub fn pushIndent(self: *Self, indent_type: IndentType) !void {
3578 try self.indent_stack.append(.{ .indent_type = indent_type, .realized = false });
3579 }
3600 pub fn lastSpaceModeIndent(ais: *AutoIndentingStream) usize {
3601 if (ais.space_stack.items.len == 0) return 0;
3602 return ais.space_stack.getLast().indent_count * ais.indent_delta;
3603 }
35803604
3581 /// Forces an indentation level to be realized.
3582 pub fn forcePushIndent(self: *Self, indent_type: IndentType) !void {
3583 try self.indent_stack.append(.{ .indent_type = indent_type, .realized = true });
3584 self.indent_count += 1;
3585 }
3605 /// Push default indentation
3606 /// Doesn't actually write any indentation.
3607 /// Just primes the stream to be able to write the correct indentation if it needs to.
3608 pub fn pushIndent(ais: *AutoIndentingStream, indent_type: IndentType) !void {
3609 try ais.indent_stack.append(.{ .indent_type = indent_type, .realized = false });
3610 }
35863611
3587 pub fn popIndent(self: *Self) void {
3588 if (self.indent_stack.pop().?.realized) {
3589 assert(self.indent_count > 0);
3590 self.indent_count -= 1;
3591 }
3592 }
3612 /// Forces an indentation level to be realized.
3613 pub fn forcePushIndent(ais: *AutoIndentingStream, indent_type: IndentType) !void {
3614 try ais.indent_stack.append(.{ .indent_type = indent_type, .realized = true });
3615 ais.indent_count += 1;
3616 }
35933617
3594 pub fn indentStackEmpty(self: *Self) bool {
3595 return self.indent_stack.items.len == 0;
3618 pub fn popIndent(ais: *AutoIndentingStream) void {
3619 if (ais.indent_stack.pop().?.realized) {
3620 assert(ais.indent_count > 0);
3621 ais.indent_count -= 1;
35963622 }
3623 }
35973624
3598 /// Writes ' ' bytes if the current line is empty
3599 fn applyIndent(self: *Self) WriteError!void {
3600 const current_indent = self.currentIndent();
3601 if (self.current_line_empty and current_indent > 0) {
3602 if (self.disabled_offset == null) {
3603 try self.underlying_writer.writeByteNTimes(' ', current_indent);
3604 }
3605 self.applied_indent = current_indent;
3606 }
3607 self.current_line_empty = false;
3608 }
3625 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {
3626 return ais.indent_stack.items.len == 0;
3627 }
36093628
3610 /// Checks to see if the most recent indentation exceeds the currently pushed indents
3611 pub fn isLineOverIndented(self: *Self) bool {
3612 if (self.current_line_empty) return false;
3613 return self.applied_indent > self.currentIndent();
3629 /// Writes ' ' bytes if the current line is empty
3630 fn applyIndent(ais: *AutoIndentingStream) Error!void {
3631 const current_indent = ais.currentIndent();
3632 if (ais.current_line_empty and current_indent > 0) {
3633 if (ais.disabled_offset == null) {
3634 try ais.underlying_writer.splatByteAll(' ', current_indent);
3635 }
3636 ais.applied_indent = current_indent;
36143637 }
3638 ais.current_line_empty = false;
3639 }
36153640
3616 fn currentIndent(self: *Self) usize {
3617 const indent_count = self.space_mode orelse self.indent_count;
3618 return indent_count * self.indent_delta;
3619 }
3620 };
3621}
3641 fn currentIndent(ais: *AutoIndentingStream) usize {
3642 const indent_count = ais.space_mode orelse ais.indent_count;
3643 return indent_count * ais.indent_delta;
3644 }
3645};
lib/std/zig/ZonGen.zig+69-59
......@@ -1,5 +1,16 @@
11//! Ingests an `Ast` and produces a `Zoir`.
22
3const std = @import("std");
4const assert = std.debug.assert;
5const mem = std.mem;
6const Allocator = mem.Allocator;
7const StringIndexAdapter = std.hash_map.StringIndexAdapter;
8const StringIndexContext = std.hash_map.StringIndexContext;
9const ZonGen = @This();
10const Zoir = @import("Zoir.zig");
11const Ast = @import("Ast.zig");
12const Writer = std.io.Writer;
13
314gpa: Allocator,
415tree: Ast,
516
......@@ -446,37 +457,44 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
446457 }
447458}
448459
449fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 {
460fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) error{ OutOfMemory, BadString }!u32 {
461 const gpa = zg.gpa;
450462 const tree = zg.tree;
451463 assert(tree.tokenTag(ident_token) == .identifier);
452464 const ident_name = tree.tokenSlice(ident_token);
453465 if (!mem.startsWith(u8, ident_name, "@")) {
454466 const start = zg.string_bytes.items.len;
455 try zg.string_bytes.appendSlice(zg.gpa, ident_name);
467 try zg.string_bytes.appendSlice(gpa, ident_name);
456468 return @intCast(start);
457 } else {
458 const offset = 1;
459 const start: u32 = @intCast(zg.string_bytes.items.len);
460 const raw_string = zg.tree.tokenSlice(ident_token)[offset..];
461 try zg.string_bytes.ensureUnusedCapacity(zg.gpa, raw_string.len);
462 switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) {
463 .success => {},
464 .failure => |err| {
465 try zg.lowerStrLitError(err, ident_token, raw_string, offset);
466 return error.BadString;
467 },
468 }
469
470 const slice = zg.string_bytes.items[start..];
471 if (mem.indexOfScalar(u8, slice, 0) != null) {
472 try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{});
473 return error.BadString;
474 } else if (slice.len == 0) {
475 try zg.addErrorTok(ident_token, "identifier cannot be empty", .{});
469 }
470 const offset = 1;
471 const start: u32 = @intCast(zg.string_bytes.items.len);
472 const raw_string = zg.tree.tokenSlice(ident_token)[offset..];
473 try zg.string_bytes.ensureUnusedCapacity(gpa, raw_string.len);
474 const result = r: {
475 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &zg.string_bytes);
476 defer zg.string_bytes = aw.toArrayList();
477 break :r std.zig.string_literal.parseWrite(&aw.writer, raw_string) catch |err| switch (err) {
478 error.WriteFailed => return error.OutOfMemory,
479 };
480 };
481 switch (result) {
482 .success => {},
483 .failure => |err| {
484 try zg.lowerStrLitError(err, ident_token, raw_string, offset);
476485 return error.BadString;
477 }
478 return start;
486 },
487 }
488
489 const slice = zg.string_bytes.items[start..];
490 if (mem.indexOfScalar(u8, slice, 0) != null) {
491 try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{});
492 return error.BadString;
493 } else if (slice.len == 0) {
494 try zg.addErrorTok(ident_token, "identifier cannot be empty", .{});
495 return error.BadString;
479496 }
497 return start;
480498}
481499
482500/// Estimates the size of a string node without parsing it.
......@@ -507,8 +525,8 @@ pub fn strLitSizeHint(tree: Ast, node: Ast.Node.Index) usize {
507525pub fn parseStrLit(
508526 tree: Ast,
509527 node: Ast.Node.Index,
510 writer: anytype,
511) error{OutOfMemory}!std.zig.string_literal.Result {
528 writer: *Writer,
529) Writer.Error!std.zig.string_literal.Result {
512530 switch (tree.nodeTag(node)) {
513531 .string_literal => {
514532 const token = tree.nodeMainToken(node);
......@@ -543,15 +561,22 @@ const StringLiteralResult = union(enum) {
543561 slice: struct { start: u32, len: u32 },
544562};
545563
546fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) !StringLiteralResult {
564fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) error{ OutOfMemory, BadString }!StringLiteralResult {
547565 if (!zg.options.parse_str_lits) return .{ .slice = .{ .start = 0, .len = 0 } };
548566
549567 const gpa = zg.gpa;
550568 const string_bytes = &zg.string_bytes;
551569 const str_index: u32 = @intCast(zg.string_bytes.items.len);
552570 const size_hint = strLitSizeHint(zg.tree, str_node);
553 try string_bytes.ensureUnusedCapacity(zg.gpa, size_hint);
554 switch (try parseStrLit(zg.tree, str_node, zg.string_bytes.writer(zg.gpa))) {
571 try string_bytes.ensureUnusedCapacity(gpa, size_hint);
572 const result = r: {
573 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &zg.string_bytes);
574 defer zg.string_bytes = aw.toArrayList();
575 break :r parseStrLit(zg.tree, str_node, &aw.writer) catch |err| switch (err) {
576 error.WriteFailed => return error.OutOfMemory,
577 };
578 };
579 switch (result) {
555580 .success => {},
556581 .failure => |err| {
557582 const token = zg.tree.nodeMainToken(str_node);
......@@ -793,10 +818,7 @@ fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.T
793818
794819fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
795820 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
796 const writer = zg.string_bytes.writer(zg.gpa);
797 try writer.print(format, args);
798 try writer.writeByte(0);
799
821 try zg.string_bytes.print(zg.gpa, format ++ "\x00", args);
800822 return .{
801823 .msg = @enumFromInt(message_idx),
802824 .token = .none,
......@@ -806,10 +828,7 @@ fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, a
806828
807829fn errNoteTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
808830 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
809 const writer = zg.string_bytes.writer(zg.gpa);
810 try writer.print(format, args);
811 try writer.writeByte(0);
812
831 try zg.string_bytes.print(zg.gpa, format ++ "\x00", args);
813832 return .{
814833 .msg = @enumFromInt(message_idx),
815834 .token = .fromToken(tok),
......@@ -850,9 +869,7 @@ fn addErrorInner(
850869 try zg.error_notes.appendSlice(gpa, notes);
851870
852871 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
853 const writer = zg.string_bytes.writer(zg.gpa);
854 try writer.print(format, args);
855 try writer.writeByte(0);
872 try zg.string_bytes.print(gpa, format ++ "\x00", args);
856873
857874 try zg.compile_errors.append(gpa, .{
858875 .msg = @enumFromInt(message_idx),
......@@ -868,8 +885,9 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
868885 const tree = zg.tree;
869886 assert(tree.errors.len > 0);
870887
871 var msg: std.ArrayListUnmanaged(u8) = .empty;
872 defer msg.deinit(gpa);
888 var msg: std.io.Writer.Allocating = .init(gpa);
889 defer msg.deinit();
890 const msg_bw = &msg.writer;
873891
874892 var notes: std.ArrayListUnmanaged(Zoir.CompileError.Note) = .empty;
875893 defer notes.deinit(gpa);
......@@ -877,18 +895,20 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
877895 var cur_err = tree.errors[0];
878896 for (tree.errors[1..]) |err| {
879897 if (err.is_note) {
880 try tree.renderError(err, msg.writer(gpa));
881 try notes.append(gpa, try zg.errNoteTok(err.token, "{s}", .{msg.items}));
898 tree.renderError(err, msg_bw) catch return error.OutOfMemory;
899 try notes.append(gpa, try zg.errNoteTok(err.token, "{s}", .{msg.getWritten()}));
882900 } else {
883901 // Flush error
884 try tree.renderError(cur_err, msg.writer(gpa));
902 tree.renderError(cur_err, msg_bw) catch return error.OutOfMemory;
885903 const extra_offset = tree.errorOffset(cur_err);
886 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
904 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items);
887905 notes.clearRetainingCapacity();
888906 cur_err = err;
889907
890 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.
891 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.
908 // TODO: `Parse` currently does not have good error recovery
909 // mechanisms, so the remaining errors could be bogus. As such,
910 // we'll ignore all remaining errors for now. We should improve
911 // `Parse` so that we can report all the errors.
892912 return;
893913 }
894914 msg.clearRetainingCapacity();
......@@ -896,16 +916,6 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
896916
897917 // Flush error
898918 const extra_offset = tree.errorOffset(cur_err);
899 try tree.renderError(cur_err, msg.writer(gpa));
900 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
919 tree.renderError(cur_err, msg_bw) catch return error.OutOfMemory;
920 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items);
901921}
902
903const std = @import("std");
904const assert = std.debug.assert;
905const mem = std.mem;
906const Allocator = mem.Allocator;
907const StringIndexAdapter = std.hash_map.StringIndexAdapter;
908const StringIndexContext = std.hash_map.StringIndexContext;
909const ZonGen = @This();
910const Zoir = @import("Zoir.zig");
911const Ast = @import("Ast.zig");
lib/std/zig/parser_test.zig+4-2
......@@ -6367,7 +6367,9 @@ test "ampersand" {
63676367var fixed_buffer_mem: [100 * 1024]u8 = undefined;
63686368
63696369fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {
6370 const stderr = std.fs.File.stderr().deprecatedWriter();
6370 var buffer: [64]u8 = undefined;
6371 const stderr = std.debug.lockStderrWriter(&buffer);
6372 defer std.debug.unlockStderrWriter();
63716373
63726374 var tree = try std.zig.Ast.parse(allocator, source, .zig);
63736375 defer tree.deinit(allocator);
......@@ -6390,7 +6392,7 @@ fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *
63906392 return error.ParseError;
63916393 }
63926394
6393 const formatted = try tree.render(allocator);
6395 const formatted = try tree.renderAlloc(allocator);
63946396 anything_changed.* = !mem.eql(u8, formatted, source);
63956397 return formatted;
63966398}
lib/std/zig/string_literal.zig+15-11
......@@ -1,6 +1,7 @@
11const std = @import("../std.zig");
22const assert = std.debug.assert;
33const utf8Encode = std.unicode.utf8Encode;
4const Writer = std.io.Writer;
45
56pub const ParseError = error{
67 OutOfMemory,
......@@ -315,9 +316,10 @@ test parseCharLiteral {
315316 );
316317}
317318
318/// Parses `bytes` as a Zig string literal and writes the result to the `std.io.GenericWriter` type.
319/// Parses `bytes` as a Zig string literal and writes the result to the `Writer` type.
320///
319321/// Asserts `bytes` has '"' at beginning and end.
320pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result {
322pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result {
321323 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
322324
323325 var index: usize = 1;
......@@ -333,18 +335,18 @@ pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result
333335 if (bytes[escape_char_index] == 'u') {
334336 var buf: [4]u8 = undefined;
335337 const len = utf8Encode(codepoint, &buf) catch {
336 return Result{ .failure = .{ .invalid_unicode_codepoint = escape_char_index + 1 } };
338 return .{ .failure = .{ .invalid_unicode_codepoint = escape_char_index + 1 } };
337339 };
338340 try writer.writeAll(buf[0..len]);
339341 } else {
340342 try writer.writeByte(@as(u8, @intCast(codepoint)));
341343 }
342344 },
343 .failure => |err| return Result{ .failure = err },
345 .failure => |err| return .{ .failure = err },
344346 }
345347 },
346 '\n' => return Result{ .failure = .{ .invalid_character = index } },
347 '"' => return Result.success,
348 '\n' => return .{ .failure = .{ .invalid_character = index } },
349 '"' => return .success,
348350 else => {
349351 try writer.writeByte(b);
350352 index += 1;
......@@ -356,11 +358,13 @@ pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result
356358/// Higher level API. Does not return extra info about parse errors.
357359/// Caller owns returned memory.
358360pub fn parseAlloc(allocator: std.mem.Allocator, bytes: []const u8) ParseError![]u8 {
359 var buf = std.ArrayList(u8).init(allocator);
360 defer buf.deinit();
361
362 switch (try parseWrite(buf.writer(), bytes)) {
363 .success => return buf.toOwnedSlice(),
361 var aw: std.io.Writer.Allocating = .init(allocator);
362 defer aw.deinit();
363 const result = parseWrite(&aw.writer, bytes) catch |err| switch (err) {
364 error.WriteFailed => return error.OutOfMemory,
365 };
366 switch (result) {
367 .success => return aw.toOwnedSlice(),
364368 .failure => return error.InvalidLiteral,
365369 }
366370}
lib/std/zon/parse.zig+14-8
......@@ -411,18 +411,22 @@ const Parser = struct {
411411 diag: ?*Diagnostics,
412412 options: Options,
413413
414 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) error{ ParseZon, OutOfMemory }!T {
414 const ParseExprError = error{ ParseZon, OutOfMemory };
415
416 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprError!T {
415417 return self.parseExprInner(T, node) catch |err| switch (err) {
416418 error.WrongType => return self.failExpectedType(T, node),
417419 else => |e| return e,
418420 };
419421 }
420422
423 const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType };
424
421425 fn parseExprInner(
422426 self: *@This(),
423427 T: type,
424428 node: Zoir.Node.Index,
425 ) error{ ParseZon, OutOfMemory, WrongType }!T {
429 ) ParseExprInnerError!T {
426430 if (T == Zoir.Node.Index) {
427431 return node;
428432 }
......@@ -611,15 +615,17 @@ const Parser = struct {
611615 }
612616 }
613617
614 fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) !T {
618 fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprInnerError!T {
615619 const ast_node = node.getAstNode(self.zoir);
616620 const pointer = @typeInfo(T).pointer;
617621 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);
618622 if (pointer.sentinel() != null) size_hint += 1;
619623
620 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, size_hint);
621 defer buf.deinit(self.gpa);
622 switch (try ZonGen.parseStrLit(self.ast, ast_node, buf.writer(self.gpa))) {
624 var aw: std.Io.Writer.Allocating = .init(self.gpa);
625 try aw.ensureUnusedCapacity(size_hint);
626 defer aw.deinit();
627 const result = ZonGen.parseStrLit(self.ast, ast_node, &aw.writer) catch return error.OutOfMemory;
628 switch (result) {
623629 .success => {},
624630 .failure => |err| {
625631 const token = self.ast.nodeMainToken(ast_node);
......@@ -638,9 +644,9 @@ const Parser = struct {
638644 }
639645
640646 if (pointer.sentinel() != null) {
641 return buf.toOwnedSliceSentinel(self.gpa, 0);
647 return aw.toOwnedSliceSentinel(0);
642648 } else {
643 return buf.toOwnedSlice(self.gpa);
649 return aw.toOwnedSlice();
644650 }
645651 }
646652