authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-05 15:47:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-05 15:47:18-07:00
log16a2562c3f12a5a4fe9875644b593bd571c2734c
tree13158f34a7389ae37f66398a30e6e3a8f3c04ffc
parentcf42ae178deae475c4fdc2d927f91b4980ec8be5

zig fmt: implement container decls


4 files changed, 762 insertions(+), 364 deletions(-)

lib/std/zig/ast.zig+243-17
......@@ -365,6 +365,26 @@ pub const Tree = struct {
365365 }
366366 },
367367
368 .ContainerDecl,
369 .ContainerDeclComma,
370 .ContainerDeclTwo,
371 .ContainerDeclTwoComma,
372 .ContainerDeclArg,
373 .ContainerDeclArgComma,
374 .TaggedUnion,
375 .TaggedUnionComma,
376 .TaggedUnionTwo,
377 .TaggedUnionTwoComma,
378 .TaggedUnionEnumTag,
379 .TaggedUnionEnumTagComma,
380 => {
381 const main_token = main_tokens[n];
382 switch (token_tags[main_token - 1]) {
383 .Keyword_packed, .Keyword_extern => return main_token - 1,
384 else => return main_token,
385 }
386 },
387
368388 .PtrTypeAligned => unreachable, // TODO
369389 .PtrTypeSentinel => unreachable, // TODO
370390 .PtrType => unreachable, // TODO
......@@ -375,10 +395,6 @@ pub const Tree = struct {
375395 .While => unreachable, // TODO
376396 .ForSimple => unreachable, // TODO
377397 .For => unreachable, // TODO
378 .ContainerDecl => unreachable, // TODO
379 .ContainerDeclArg => unreachable, // TODO
380 .TaggedUnion => unreachable, // TODO
381 .TaggedUnionEnumTag => unreachable, // TODO
382398 .AsmOutput => unreachable, // TODO
383399 .AsmInput => unreachable, // TODO
384400 .ErrorValue => unreachable, // TODO
......@@ -408,6 +424,7 @@ pub const Tree = struct {
408424 .Break,
409425 .Return,
410426 .Nosuspend,
427 .Comptime,
411428 => n = datas[n].lhs,
412429
413430 .TestDecl,
......@@ -455,7 +472,6 @@ pub const Tree = struct {
455472 .BoolOr,
456473 .AnyFrameType,
457474 .ErrorUnion,
458 .Comptime,
459475 .IfSimple,
460476 .WhileSimple,
461477 => n = datas[n].rhs,
......@@ -490,13 +506,37 @@ pub const Tree = struct {
490506 }
491507 n = tree.extra_data[params.end - 1]; // last parameter
492508 },
493 .Block => {
509 .ContainerDeclArg => {
510 const members = tree.extraData(datas[n].rhs, Node.SubRange);
511 if (members.end - members.start == 0) {
512 end_offset += 1; // for the rparen
513 n = datas[n].lhs;
514 } else {
515 end_offset += 1; // for the rbrace
516 n = tree.extra_data[members.end - 1]; // last parameter
517 }
518 },
519 .ContainerDeclArgComma => {
520 const members = tree.extraData(datas[n].rhs, Node.SubRange);
521 assert(members.end - members.start > 0);
522 end_offset += 2; // for the comma + rbrace
523 n = tree.extra_data[members.end - 1]; // last parameter
524 },
525 .Block,
526 .ContainerDecl,
527 .TaggedUnion,
528 => {
494529 end_offset += 1; // for the rbrace
495530 if (datas[n].rhs - datas[n].lhs == 0) {
496531 return main_tokens[n] + end_offset;
497532 }
498533 n = tree.extra_data[datas[n].rhs - 1]; // last statement
499534 },
535 .ContainerDeclComma, .TaggedUnionComma => {
536 assert(datas[n].rhs - datas[n].lhs > 0);
537 end_offset += 2; // for the comma + rbrace
538 n = tree.extra_data[datas[n].rhs - 1]; // last member
539 },
500540 .CallOne,
501541 .ArrayAccess,
502542 => {
......@@ -511,6 +551,8 @@ pub const Tree = struct {
511551 .BuiltinCallTwo,
512552 .BlockTwo,
513553 .StructInitDotTwo,
554 .ContainerDeclTwo,
555 .TaggedUnionTwo,
514556 => {
515557 end_offset += 1; // for the rparen/rbrace
516558 if (datas[n].rhs != 0) {
......@@ -523,6 +565,8 @@ pub const Tree = struct {
523565 },
524566 .ArrayInitDotTwoComma,
525567 .StructInitDotTwoComma,
568 .ContainerDeclTwoComma,
569 .TaggedUnionTwoComma,
526570 => {
527571 end_offset += 2; // for the comma + rbrace
528572 if (datas[n].rhs != 0) {
......@@ -589,6 +633,38 @@ pub const Tree = struct {
589633 }
590634 }
591635 },
636 .ContainerFieldInit => {
637 if (datas[n].rhs != 0) {
638 n = datas[n].rhs;
639 } else if (datas[n].lhs != 0) {
640 n = datas[n].lhs;
641 } else {
642 return main_tokens[n] + end_offset;
643 }
644 },
645 .ContainerFieldAlign => {
646 if (datas[n].rhs != 0) {
647 end_offset += 1; // for the rparen
648 n = datas[n].rhs;
649 } else if (datas[n].lhs != 0) {
650 n = datas[n].lhs;
651 } else {
652 return main_tokens[n] + end_offset;
653 }
654 },
655 .ContainerField => {
656 const extra = tree.extraData(datas[n].rhs, Node.ContainerField);
657 if (extra.value_expr != 0) {
658 n = extra.value_expr;
659 } else if (extra.align_expr != 0) {
660 end_offset += 1; // for the rparen
661 n = extra.align_expr;
662 } else if (datas[n].lhs != 0) {
663 n = datas[n].lhs;
664 } else {
665 return main_tokens[n] + end_offset;
666 }
667 },
592668
593669 // These are not supported by lastToken() because implementation would
594670 // require recursion due to the optional comma followed by rbrace.
......@@ -600,10 +676,9 @@ pub const Tree = struct {
600676 .StructInit => unreachable,
601677 .StructInitOne => unreachable,
602678 .StructInitDot => unreachable,
603 .ContainerFieldInit => unreachable,
604 .ContainerFieldAlign => unreachable,
605 .ContainerField => unreachable,
606679
680 .TaggedUnionEnumTag => unreachable, // TODO
681 .TaggedUnionEnumTagComma => unreachable, // TODO
607682 .Switch => unreachable, // TODO
608683 .If => unreachable, // TODO
609684 .Continue => unreachable, // TODO
......@@ -631,10 +706,6 @@ pub const Tree = struct {
631706 .FnProtoMulti => unreachable, // TODO
632707 .FnProtoOne => unreachable, // TODO
633708 .FnProto => unreachable, // TODO
634 .ContainerDecl => unreachable, // TODO
635 .ContainerDeclArg => unreachable, // TODO
636 .TaggedUnion => unreachable, // TODO
637 .TaggedUnionEnumTag => unreachable, // TODO
638709 .AsmOutput => unreachable, // TODO
639710 .AsmInput => unreachable, // TODO
640711 .ErrorValue => unreachable, // TODO
......@@ -952,6 +1023,93 @@ pub const Tree = struct {
9521023 };
9531024 }
9541025
1026 pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ContainerDecl {
1027 assert(tree.nodes.items(.tag)[node] == .ContainerDeclTwo or
1028 tree.nodes.items(.tag)[node] == .ContainerDeclTwoComma);
1029 const data = tree.nodes.items(.data)[node];
1030 buffer.* = .{ data.lhs, data.rhs };
1031 const members = if (data.rhs != 0)
1032 buffer[0..2]
1033 else if (data.lhs != 0)
1034 buffer[0..1]
1035 else
1036 buffer[0..0];
1037 return tree.fullContainerDecl(.{
1038 .main_token = tree.nodes.items(.main_token)[node],
1039 .enum_token = null,
1040 .members = members,
1041 .arg = 0,
1042 });
1043 }
1044
1045 pub fn containerDecl(tree: Tree, node: Node.Index) Full.ContainerDecl {
1046 assert(tree.nodes.items(.tag)[node] == .ContainerDecl or
1047 tree.nodes.items(.tag)[node] == .ContainerDeclComma);
1048 const data = tree.nodes.items(.data)[node];
1049 return tree.fullContainerDecl(.{
1050 .main_token = tree.nodes.items(.main_token)[node],
1051 .enum_token = null,
1052 .members = tree.extra_data[data.lhs..data.rhs],
1053 .arg = 0,
1054 });
1055 }
1056
1057 pub fn containerDeclArg(tree: Tree, node: Node.Index) Full.ContainerDecl {
1058 assert(tree.nodes.items(.tag)[node] == .ContainerDeclArg);
1059 const data = tree.nodes.items(.data)[node];
1060 const members_range = tree.extraData(data.rhs, Node.SubRange);
1061 return tree.fullContainerDecl(.{
1062 .main_token = tree.nodes.items(.main_token)[node],
1063 .enum_token = null,
1064 .members = tree.extra_data[members_range.start..members_range.end],
1065 .arg = data.lhs,
1066 });
1067 }
1068
1069 pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ContainerDecl {
1070 assert(tree.nodes.items(.tag)[node] == .TaggedUnionTwo);
1071 const data = tree.nodes.items(.data)[node];
1072 buffer.* = .{ data.lhs, data.rhs };
1073 const members = if (data.rhs != 0)
1074 buffer[0..2]
1075 else if (data.lhs != 0)
1076 buffer[0..1]
1077 else
1078 buffer[0..0];
1079 const main_token = tree.nodes.items(.main_token)[node];
1080 return tree.fullContainerDecl(.{
1081 .main_token = main_token,
1082 .enum_token = main_token + 2, // union lparen enum
1083 .members = members,
1084 .arg = 0,
1085 });
1086 }
1087
1088 pub fn taggedUnion(tree: Tree, node: Node.Index) Full.ContainerDecl {
1089 assert(tree.nodes.items(.tag)[node] == .TaggedUnion);
1090 const data = tree.nodes.items(.data)[node];
1091 const main_token = tree.nodes.items(.main_token)[node];
1092 return tree.fullContainerDecl(.{
1093 .main_token = main_token,
1094 .enum_token = main_token + 2, // union lparen enum
1095 .members = tree.extra_data[data.lhs..data.rhs],
1096 .arg = 0,
1097 });
1098 }
1099
1100 pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) Full.ContainerDecl {
1101 assert(tree.nodes.items(.tag)[node] == .TaggedUnionEnumTag);
1102 const data = tree.nodes.items(.data)[node];
1103 const members_range = tree.extraData(data.rhs, Node.SubRange);
1104 const main_token = tree.nodes.items(.main_token)[node];
1105 return tree.fullContainerDecl(.{
1106 .main_token = main_token,
1107 .enum_token = main_token + 2, // union lparen enum
1108 .members = tree.extra_data[data.lhs..data.rhs],
1109 .arg = data.lhs,
1110 });
1111 }
1112
9551113 fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl {
9561114 const token_tags = tree.tokens.items(.tag);
9571115 var result: Full.VarDecl = .{
......@@ -1031,6 +1189,19 @@ pub const Tree = struct {
10311189 };
10321190 return result;
10331191 }
1192
1193 fn fullContainerDecl(tree: Tree, info: Full.ContainerDecl.Ast) Full.ContainerDecl {
1194 const token_tags = tree.tokens.items(.tag);
1195 var result: Full.ContainerDecl = .{
1196 .ast = info,
1197 .layout_token = null,
1198 };
1199 switch (token_tags[info.main_token - 1]) {
1200 .Keyword_extern, .Keyword_packed => result.layout_token = info.main_token - 1,
1201 else => {},
1202 }
1203 return result;
1204 }
10341205};
10351206
10361207/// Fully assembled AST node information.
......@@ -1125,6 +1296,19 @@ pub const Full = struct {
11251296 elem_type: Node.Index,
11261297 };
11271298 };
1299
1300 pub const ContainerDecl = struct {
1301 layout_token: ?TokenIndex,
1302 ast: Ast,
1303
1304 pub const Ast = struct {
1305 main_token: TokenIndex,
1306 /// Populated when main_token is Keyword_union.
1307 enum_token: ?TokenIndex,
1308 members: []const Node.Index,
1309 arg: Node.Index,
1310 };
1311 };
11281312};
11291313
11301314pub const Error = union(enum) {
......@@ -1543,9 +1727,11 @@ pub const Node = struct {
15431727 StructInitOne,
15441728 /// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted.
15451729 /// main_token is the lbrace.
1730 /// No trailing comma before the rbrace.
15461731 StructInitDotTwo,
15471732 /// Same as `StructInitDotTwo` except there is known to be a trailing comma
1548 /// before the final rbrace.
1733 /// before the final rbrace. This tag exists to facilitate lastToken() implemented
1734 /// without recursion.
15491735 StructInitDotTwoComma,
15501736 /// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`.
15511737 /// main_token is the lbrace.
......@@ -1655,21 +1841,50 @@ pub const Node = struct {
16551841 /// `error{a, b}`.
16561842 /// lhs and rhs both unused.
16571843 ErrorSetDecl,
1658 /// `struct {}`, `union {}`, etc. `sub_list[lhs..rhs]`.
1844 /// `struct {}`, `union {}`, `opaque {}`, `enum {}`. `extra_data[lhs..rhs]`.
1845 /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
16591846 ContainerDecl,
1660 /// `union(lhs)` / `enum(lhs)`. `sub_range_list[rhs]`.
1847 /// Same as ContainerDecl but there is known to be a trailing comma before the rbrace.
1848 ContainerDeclComma,
1849 /// `struct {lhs, rhs}`, `union {lhs, rhs}`, `opaque {lhs, rhs}`, `enum {lhs, rhs}`.
1850 /// lhs or rhs can be omitted.
1851 /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
1852 ContainerDeclTwo,
1853 /// Same as ContainerDeclTwo except there is known to be a trailing comma
1854 /// before the rbrace.
1855 ContainerDeclTwoComma,
1856 /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
16611857 ContainerDeclArg,
1858 /// Same as ContainerDeclArg but there is known to be a trailing comma before the rbrace.
1859 ContainerDeclArgComma,
16621860 /// `union(enum) {}`. `sub_list[lhs..rhs]`.
16631861 /// Note that tagged unions with explicitly provided enums are represented
16641862 /// by `ContainerDeclArg`.
16651863 TaggedUnion,
1666 /// `union(enum(lhs)) {}`. `sub_list_range[rhs]`.
1864 /// Same as TaggedUnion but there is known to be a trailing comma before the rbrace.
1865 TaggedUnionComma,
1866 /// `union(enum) {lhs, rhs}`. lhs or rhs may be omitted.
1867 /// Note that tagged unions with explicitly provided enums are represented
1868 /// by `ContainerDeclArg`.
1869 TaggedUnionTwo,
1870 /// Same as TaggedUnionTwo but there is known to be a trailing comma before the rbrace.
1871 TaggedUnionTwoComma,
1872 /// `union(enum(lhs)) {}`. `SubRange[rhs]`.
16671873 TaggedUnionEnumTag,
1874 /// Same as TaggedUnionEnumTag but there is known to be a trailing comma
1875 /// before the rbrace.
1876 TaggedUnionEnumTagComma,
16681877 /// `a: lhs = rhs,`. lhs and rhs can be omitted.
1878 /// main_token is the field name identifier.
1879 /// lastToken() does not include the possible trailing comma.
16691880 ContainerFieldInit,
16701881 /// `a: lhs align(rhs),`. rhs can be omitted.
1882 /// main_token is the field name identifier.
1883 /// lastToken() does not include the possible trailing comma.
16711884 ContainerFieldAlign,
16721885 /// `a: lhs align(c) = d,`. `container_field_list[rhs]`.
1886 /// main_token is the field name identifier.
1887 /// lastToken() does not include the possible trailing comma.
16731888 ContainerField,
16741889 /// `anytype`. both lhs and rhs unused.
16751890 /// Used by `ContainerField`.
......@@ -1699,6 +1914,17 @@ pub const Node = struct {
16991914 ErrorValue,
17001915 /// `lhs!rhs`. main_token is the `!`.
17011916 ErrorUnion,
1917
1918 pub fn isContainerField(tag: Tag) bool {
1919 return switch (tag) {
1920 .ContainerFieldInit,
1921 .ContainerFieldAlign,
1922 .ContainerField,
1923 => true,
1924
1925 else => false,
1926 };
1927 }
17021928 };
17031929
17041930 pub const Data = struct {
lib/std/zig/parse.zig+330-190
......@@ -64,9 +64,10 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
6464 .rhs = undefined,
6565 },
6666 });
67 const root_decls = try parser.parseContainerMembers(true);
68 // parseContainerMembers will try to skip as much
69 // invalid tokens as it can, so we are now at EOF.
67 const root_members = try parser.parseContainerMembers();
68 const root_decls = try root_members.toSpan(&parser);
69 // parseContainerMembers will try to skip as much invalid tokens as
70 // it can, so we are now at EOF.
7071 assert(parser.token_tags[parser.tok_i] == .Eof);
7172 parser.nodes.items(.data)[0] = .{
7273 .lhs = root_decls.start,
......@@ -108,6 +109,22 @@ const Parser = struct {
108109 }
109110 };
110111
112 const Members = struct {
113 len: usize,
114 lhs: Node.Index,
115 rhs: Node.Index,
116 trailing_comma: bool,
117
118 fn toSpan(self: Members, p: *Parser) !Node.SubRange {
119 if (self.len <= 2) {
120 const nodes = [2]Node.Index{ self.lhs, self.rhs };
121 return p.listToSpan(nodes[0..self.len]);
122 } else {
123 return Node.SubRange{ .start = self.lhs, .end = self.rhs };
124 }
125 }
126 };
127
111128 fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange {
112129 try p.extra_data.appendSlice(p.gpa, list);
113130 return Node.SubRange{
......@@ -151,169 +168,225 @@ const Parser = struct {
151168 /// / ContainerField COMMA ContainerMembers
152169 /// / ContainerField
153170 /// /
154 fn parseContainerMembers(p: *Parser, top_level: bool) !Node.SubRange {
171 /// TopLevelComptime <- KEYWORD_comptime BlockExpr
172 fn parseContainerMembers(p: *Parser) !Members {
155173 var list = std.ArrayList(Node.Index).init(p.gpa);
156174 defer list.deinit();
157175
158176 var field_state: union(enum) {
159 /// no fields have been seen
177 /// No fields have been seen.
160178 none,
161 /// currently parsing fields
179 /// Currently parsing fields.
162180 seen,
163 /// saw fields and then a declaration after them.
164 /// payload is first token of previous declaration.
165 end: TokenIndex,
166 /// ther was a declaration between fields, don't report more errors
181 /// Saw fields and then a declaration after them.
182 /// Payload is first token of previous declaration.
183 end: Node.Index,
184 /// There was a declaration between fields, don't report more errors.
167185 err,
168186 } = .none;
169187
170188 // Skip container doc comments.
171189 while (p.eatToken(.ContainerDocComment)) |_| {}
172190
191 var trailing_comma = false;
173192 while (true) {
174193 const doc_comment = p.eatDocComments();
175194
176 const test_decl_node = p.parseTestDecl() catch |err| switch (err) {
177 error.OutOfMemory => return error.OutOfMemory,
178 error.ParseError => {
179 p.findNextContainerMember();
180 continue;
195 switch (p.token_tags[p.tok_i]) {
196 .Keyword_test => {
197 const test_decl_node = try p.expectTestDeclRecoverable();
198 if (test_decl_node != 0) {
199 if (field_state == .seen) {
200 field_state = .{ .end = test_decl_node };
201 }
202 try list.append(test_decl_node);
203 }
204 trailing_comma = false;
181205 },
182 };
183 if (test_decl_node != 0) {
184 if (field_state == .seen) {
185 field_state = .{ .end = p.nodes.items(.main_token)[test_decl_node] };
186 }
187 try list.append(test_decl_node);
188 continue;
189 }
190
191 const comptime_node = p.parseTopLevelComptime() catch |err| switch (err) {
192 error.OutOfMemory => return error.OutOfMemory,
193 error.ParseError => {
194 p.findNextContainerMember();
195 continue;
206 .Keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
207 .Identifier => {
208 p.tok_i += 1;
209 const container_field = try p.expectContainerFieldRecoverable();
210 if (container_field != 0) {
211 switch (field_state) {
212 .none => field_state = .seen,
213 .err, .seen => {},
214 .end => |node| {
215 try p.warn(.{
216 .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] },
217 });
218 // Continue parsing; error will be reported later.
219 field_state = .err;
220 },
221 }
222 try list.append(container_field);
223 switch (p.token_tags[p.tok_i]) {
224 .Comma => {
225 p.tok_i += 1;
226 trailing_comma = true;
227 continue;
228 },
229 .RBrace, .Eof => {
230 trailing_comma = false;
231 break;
232 },
233 else => {},
234 }
235 // There is not allowed to be a decl after a field with no comma.
236 // Report error but recover parser.
237 try p.warn(.{
238 .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
239 });
240 p.findNextContainerMember();
241 }
242 },
243 .LBrace => {
244 const comptime_token = p.nextToken();
245 const block = p.parseBlock() catch |err| switch (err) {
246 error.OutOfMemory => return error.OutOfMemory,
247 error.ParseError => blk: {
248 p.findNextContainerMember();
249 break :blk null_node;
250 },
251 };
252 if (block != 0) {
253 const comptime_node = try p.addNode(.{
254 .tag = .Comptime,
255 .main_token = comptime_token,
256 .data = .{
257 .lhs = block,
258 .rhs = undefined,
259 },
260 });
261 if (field_state == .seen) {
262 field_state = .{ .end = comptime_node };
263 }
264 try list.append(comptime_node);
265 }
266 trailing_comma = false;
267 },
268 else => {
269 p.tok_i += 1;
270 try p.warn(.{ .ExpectedBlockOrField = .{ .token = p.tok_i } });
271 },
196272 },
197 };
198 if (comptime_node != 0) {
199 if (field_state == .seen) {
200 field_state = .{ .end = p.nodes.items(.main_token)[comptime_node] };
201 }
202 try list.append(comptime_node);
203 continue;
204 }
205
206 const visib_token = p.eatToken(.Keyword_pub);
207
208 const top_level_decl = p.parseTopLevelDecl() catch |err| switch (err) {
209 error.OutOfMemory => return error.OutOfMemory,
210 error.ParseError => {
211 p.findNextContainerMember();
212 continue;
273 .Keyword_pub => {
274 p.tok_i += 1;
275 const top_level_decl = try p.expectTopLevelDeclRecoverable();
276 if (top_level_decl != 0) {
277 if (field_state == .seen) {
278 field_state = .{ .end = top_level_decl };
279 }
280 try list.append(top_level_decl);
281 }
282 trailing_comma = false;
213283 },
214 };
215 if (top_level_decl != 0) {
216 if (field_state == .seen) {
217 field_state = .{
218 .end = visib_token orelse p.nodes.items(.main_token)[top_level_decl],
219 };
220 }
221 try list.append(top_level_decl);
222 continue;
223 }
224
225 if (visib_token != null) {
226 try p.warn(.{ .ExpectedPubItem = .{ .token = p.tok_i } });
227 // ignore this pub
228 continue;
229 }
230
231 const container_field = p.parseContainerField() catch |err| switch (err) {
232 error.OutOfMemory => return error.OutOfMemory,
233 error.ParseError => {
234 // attempt to recover
235 p.findNextContainerMember();
236 continue;
284 .Keyword_usingnamespace => {
285 const node = try p.expectUsingNamespaceRecoverable();
286 if (node != 0) {
287 if (field_state == .seen) {
288 field_state = .{ .end = node };
289 }
290 try list.append(node);
291 }
292 trailing_comma = false;
237293 },
238 };
239 if (container_field != 0) {
240 switch (field_state) {
241 .none => field_state = .seen,
242 .err, .seen => {},
243 .end => |tok| {
244 try p.warn(.{ .DeclBetweenFields = .{ .token = tok } });
245 // continue parsing, error will be reported later
246 field_state = .err;
247 },
248 }
249 try list.append(container_field);
250 const comma = p.eatToken(.Comma) orelse {
251 // try to continue parsing
252 const index = p.tok_i;
253 p.findNextContainerMember();
254 const next = p.token_tags[p.tok_i];
255 switch (next) {
256 .Eof => {
257 // no invalid tokens were found
258 if (index == p.tok_i) break;
259
260 // Invalid tokens, add error and exit
261 try p.warn(.{
262 .ExpectedToken = .{ .token = index, .expected_id = .Comma },
263 });
264 break;
265 },
266 else => {
267 if (next == .RBrace) {
268 if (!top_level) break;
294 .Keyword_const,
295 .Keyword_var,
296 .Keyword_threadlocal,
297 .Keyword_export,
298 .Keyword_extern,
299 .Keyword_inline,
300 .Keyword_noinline,
301 .Keyword_fn,
302 => {
303 const top_level_decl = try p.expectTopLevelDeclRecoverable();
304 if (top_level_decl != 0) {
305 if (field_state == .seen) {
306 field_state = .{ .end = top_level_decl };
307 }
308 try list.append(top_level_decl);
309 }
310 trailing_comma = false;
311 },
312 .Identifier => {
313 const container_field = try p.expectContainerFieldRecoverable();
314 if (container_field != 0) {
315 switch (field_state) {
316 .none => field_state = .seen,
317 .err, .seen => {},
318 .end => |node| {
319 try p.warn(.{
320 .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] },
321 });
322 // Continue parsing; error will be reported later.
323 field_state = .err;
324 },
325 }
326 try list.append(container_field);
327 switch (p.token_tags[p.tok_i]) {
328 .Comma => {
269329 p.tok_i += 1;
270 }
271
272 // add error and continue
273 try p.warn(.{
274 .ExpectedToken = .{ .token = index, .expected_id = .Comma },
275 });
276 continue;
277 },
330 trailing_comma = true;
331 continue;
332 },
333 .RBrace, .Eof => {
334 trailing_comma = false;
335 break;
336 },
337 else => {},
338 }
339 // There is not allowed to be a decl after a field with no comma.
340 // Report error but recover parser.
341 try p.warn(.{
342 .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
343 });
344 p.findNextContainerMember();
278345 }
279 };
280 continue;
281 }
282
283 // Dangling doc comment
284 if (doc_comment) |tok| {
285 try p.warn(.{
286 .UnattachedDocComment = .{ .token = tok },
287 });
288 }
289
290 const next = p.token_tags[p.tok_i];
291 switch (next) {
292 .Eof => break,
293 .Keyword_comptime => {
294 p.tok_i += 1;
295 try p.warn(.{
296 .ExpectedBlockOrField = .{ .token = p.tok_i },
297 });
298346 },
299 else => {
300 const index = p.tok_i;
301 if (next == .RBrace) {
302 if (!top_level) break;
303 p.tok_i += 1;
347 .Eof, .RBrace => {
348 if (doc_comment) |tok| {
349 try p.warn(.{ .UnattachedDocComment = .{ .token = tok } });
304350 }
305
306 // this was likely not supposed to end yet,
307 // try to find the next declaration
351 break;
352 },
353 else => {
354 try p.warn(.{ .ExpectedContainerMembers = .{ .token = p.tok_i } });
355 // This was likely not supposed to end yet; try to find the next declaration.
308356 p.findNextContainerMember();
309 try p.warn(.{
310 .ExpectedContainerMembers = .{ .token = index },
311 });
312357 },
313358 }
314359 }
315360
316 return p.listToSpan(list.items);
361 switch (list.items.len) {
362 0 => return Members{
363 .len = 0,
364 .lhs = 0,
365 .rhs = 0,
366 .trailing_comma = trailing_comma,
367 },
368 1 => return Members{
369 .len = 1,
370 .lhs = list.items[0],
371 .rhs = 0,
372 .trailing_comma = trailing_comma,
373 },
374 2 => return Members{
375 .len = 2,
376 .lhs = list.items[0],
377 .rhs = list.items[1],
378 .trailing_comma = trailing_comma,
379 },
380 else => {
381 const span = try p.listToSpan(list.items);
382 return Members{
383 .len = list.items.len,
384 .lhs = span.start,
385 .rhs = span.end,
386 .trailing_comma = trailing_comma,
387 };
388 },
389 }
317390 }
318391
319392 /// Attempts to find next container member by searching for certain tokens
......@@ -398,44 +471,36 @@ const Parser = struct {
398471 }
399472
400473 /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block
401 fn parseTestDecl(p: *Parser) !Node.Index {
402 const test_token = p.eatToken(.Keyword_test) orelse return null_node;
403 const name_token = try p.expectToken(.StringLiteral);
474 fn expectTestDecl(p: *Parser) !Node.Index {
475 const test_token = try p.expectToken(.Keyword_test);
476 const name_token = p.eatToken(.StringLiteral);
404477 const block_node = try p.parseBlock();
405478 if (block_node == 0) return p.fail(.{ .ExpectedLBrace = .{ .token = p.tok_i } });
406479 return p.addNode(.{
407480 .tag = .TestDecl,
408481 .main_token = test_token,
409482 .data = .{
410 .lhs = name_token,
483 .lhs = name_token orelse 0,
411484 .rhs = block_node,
412485 },
413486 });
414487 }
415488
416 /// TopLevelComptime <- KEYWORD_comptime BlockExpr
417 fn parseTopLevelComptime(p: *Parser) !Node.Index {
418 if (p.token_tags[p.tok_i] == .Keyword_comptime and
419 p.token_tags[p.tok_i + 1] == .LBrace)
420 {
421 return p.addNode(.{
422 .tag = .Comptime,
423 .main_token = p.nextToken(),
424 .data = .{
425 .lhs = try p.parseBlock(),
426 .rhs = undefined,
427 },
428 });
429 } else {
430 return null_node;
431 }
489 fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
490 return p.expectTestDecl() catch |err| switch (err) {
491 error.OutOfMemory => return error.OutOfMemory,
492 error.ParseError => {
493 p.findNextContainerMember();
494 return null_node;
495 },
496 };
432497 }
433498
434499 /// TopLevelDecl
435500 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
436501 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
437502 /// / KEYWORD_usingnamespace Expr SEMICOLON
438 fn parseTopLevelDecl(p: *Parser) !Node.Index {
503 fn expectTopLevelDecl(p: *Parser) !Node.Index {
439504 const extern_export_inline_token = p.nextToken();
440505 var expect_fn: bool = false;
441506 var exported: bool = false;
......@@ -496,7 +561,21 @@ const Parser = struct {
496561 return p.fail(.{ .ExpectedVarDeclOrFn = .{ .token = p.tok_i } });
497562 }
498563
499 const usingnamespace_token = p.eatToken(.Keyword_usingnamespace) orelse return null_node;
564 return p.expectUsingNamespace();
565 }
566
567 fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
568 return p.expectTopLevelDecl() catch |err| switch (err) {
569 error.OutOfMemory => return error.OutOfMemory,
570 error.ParseError => {
571 p.findNextContainerMember();
572 return null_node;
573 },
574 };
575 }
576
577 fn expectUsingNamespace(p: *Parser) !Node.Index {
578 const usingnamespace_token = try p.expectToken(.Keyword_usingnamespace);
500579 const expr = try p.expectExpr();
501580 const semicolon_token = try p.expectToken(.Semicolon);
502581 try p.parseAppendedDocComment(semicolon_token);
......@@ -510,6 +589,16 @@ const Parser = struct {
510589 });
511590 }
512591
592 fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
593 return p.expectUsingNamespace() catch |err| switch (err) {
594 error.OutOfMemory => return error.OutOfMemory,
595 error.ParseError => {
596 p.findNextContainerMember();
597 return null_node;
598 },
599 };
600 }
601
513602 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
514603 fn parseFnProto(p: *Parser) !Node.Index {
515604 const fn_token = p.eatToken(.Keyword_fn) orelse return null_node;
......@@ -648,12 +737,9 @@ const Parser = struct {
648737 }
649738
650739 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
651 fn parseContainerField(p: *Parser) !Node.Index {
740 fn expectContainerField(p: *Parser) !Node.Index {
652741 const comptime_token = p.eatToken(.Keyword_comptime);
653 const name_token = p.eatToken(.Identifier) orelse {
654 if (comptime_token) |_| p.tok_i -= 1;
655 return null_node;
656 };
742 const name_token = try p.expectToken(.Identifier);
657743
658744 var align_expr: Node.Index = 0;
659745 var type_expr: Node.Index = 0;
......@@ -708,6 +794,16 @@ const Parser = struct {
708794 }
709795 }
710796
797 fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
798 return p.expectContainerField() catch |err| switch (err) {
799 error.OutOfMemory => return error.OutOfMemory,
800 error.ParseError => {
801 p.findNextContainerMember();
802 return null_node;
803 },
804 };
805 }
806
711807 /// Statement
712808 /// <- KEYWORD_comptime? VarDecl
713809 /// / KEYWORD_comptime BlockExprStatement
......@@ -3333,16 +3429,20 @@ const Parser = struct {
33333429 _ = try p.expectToken(.RParen);
33343430
33353431 _ = try p.expectToken(.LBrace);
3336 const members = try p.parseContainerMembers(false);
3432 const members = try p.parseContainerMembers();
3433 const members_span = try members.toSpan(p);
33373434 _ = try p.expectToken(.RBrace);
33383435 return p.addNode(.{
3339 .tag = .TaggedUnionEnumTag,
3436 .tag = switch (members.trailing_comma) {
3437 true => .TaggedUnionEnumTagComma,
3438 false => .TaggedUnionEnumTag,
3439 },
33403440 .main_token = main_token,
33413441 .data = .{
33423442 .lhs = enum_tag_expr,
33433443 .rhs = try p.addExtra(Node.SubRange{
3344 .start = members.start,
3345 .end = members.end,
3444 .start = members_span.start,
3445 .end = members_span.end,
33463446 }),
33473447 },
33483448 });
......@@ -3350,16 +3450,34 @@ const Parser = struct {
33503450 _ = try p.expectToken(.RParen);
33513451
33523452 _ = try p.expectToken(.LBrace);
3353 const members = try p.parseContainerMembers(false);
3453 const members = try p.parseContainerMembers();
33543454 _ = try p.expectToken(.RBrace);
3355 return p.addNode(.{
3356 .tag = .TaggedUnion,
3357 .main_token = main_token,
3358 .data = .{
3359 .lhs = members.start,
3360 .rhs = members.end,
3361 },
3362 });
3455 if (members.len <= 2) {
3456 return p.addNode(.{
3457 .tag = switch (members.trailing_comma) {
3458 true => .TaggedUnionTwoComma,
3459 false => .TaggedUnionTwo,
3460 },
3461 .main_token = main_token,
3462 .data = .{
3463 .lhs = members.lhs,
3464 .rhs = members.rhs,
3465 },
3466 });
3467 } else {
3468 const span = try members.toSpan(p);
3469 return p.addNode(.{
3470 .tag = switch (members.trailing_comma) {
3471 true => .TaggedUnionComma,
3472 false => .TaggedUnion,
3473 },
3474 .main_token = main_token,
3475 .data = .{
3476 .lhs = span.start,
3477 .rhs = span.end,
3478 },
3479 });
3480 }
33633481 }
33643482 } else {
33653483 const expr = try p.expectExpr();
......@@ -3373,26 +3491,48 @@ const Parser = struct {
33733491 else => unreachable,
33743492 };
33753493 _ = try p.expectToken(.LBrace);
3376 const members = try p.parseContainerMembers(false);
3494 const members = try p.parseContainerMembers();
33773495 _ = try p.expectToken(.RBrace);
33783496 if (arg_expr == 0) {
3379 return p.addNode(.{
3380 .tag = .ContainerDecl,
3381 .main_token = main_token,
3382 .data = .{
3383 .lhs = members.start,
3384 .rhs = members.end,
3385 },
3386 });
3497 if (members.len <= 2) {
3498 return p.addNode(.{
3499 .tag = switch (members.trailing_comma) {
3500 true => .ContainerDeclTwoComma,
3501 false => .ContainerDeclTwo,
3502 },
3503 .main_token = main_token,
3504 .data = .{
3505 .lhs = members.lhs,
3506 .rhs = members.rhs,
3507 },
3508 });
3509 } else {
3510 const span = try members.toSpan(p);
3511 return p.addNode(.{
3512 .tag = switch (members.trailing_comma) {
3513 true => .ContainerDeclComma,
3514 false => .ContainerDecl,
3515 },
3516 .main_token = main_token,
3517 .data = .{
3518 .lhs = span.start,
3519 .rhs = span.end,
3520 },
3521 });
3522 }
33873523 } else {
3524 const span = try members.toSpan(p);
33883525 return p.addNode(.{
3389 .tag = .ContainerDeclArg,
3526 .tag = switch (members.trailing_comma) {
3527 true => .ContainerDeclArgComma,
3528 false => .ContainerDeclArg,
3529 },
33903530 .main_token = main_token,
33913531 .data = .{
33923532 .lhs = arg_expr,
33933533 .rhs = try p.addExtra(Node.SubRange{
3394 .start = members.start,
3395 .end = members.end,
3534 .start = span.start,
3535 .end = span.end,
33963536 }),
33973537 },
33983538 });
lib/std/zig/parser_test.zig+76-39
......@@ -149,45 +149,82 @@ test "zig fmt: nosuspend block" {
149149 );
150150}
151151
152//test "zig fmt: nosuspend await" {
153// try testCanonical(
154// \\fn foo() void {
155// \\ x = nosuspend await y;
156// \\}
157// \\
158// );
159//}
160//
161//test "zig fmt: trailing comma in container declaration" {
162// try testCanonical(
163// \\const X = struct { foo: i32 };
164// \\const X = struct { foo: i32, bar: i32 };
165// \\const X = struct { foo: i32 = 1, bar: i32 = 2 };
166// \\const X = struct { foo: i32 align(4), bar: i32 align(4) };
167// \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };
168// \\
169// );
170// try testCanonical(
171// \\test "" {
172// \\ comptime {
173// \\ const X = struct {
174// \\ x: i32
175// \\ };
176// \\ }
177// \\}
178// \\
179// );
180// try testTransform(
181// \\const X = struct {
182// \\ foo: i32, bar: i8 };
183// ,
184// \\const X = struct {
185// \\ foo: i32, bar: i8
186// \\};
187// \\
188// );
189//}
190//
152test "zig fmt: nosuspend await" {
153 try testCanonical(
154 \\fn foo() void {
155 \\ x = nosuspend await y;
156 \\}
157 \\
158 );
159}
160
161test "zig fmt: container declaration, single line" {
162 try testCanonical(
163 \\const X = struct { foo: i32 };
164 \\const X = struct { foo: i32, bar: i32 };
165 \\const X = struct { foo: i32 = 1, bar: i32 = 2 };
166 \\const X = struct { foo: i32 align(4), bar: i32 align(4) };
167 \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };
168 \\
169 );
170}
171
172test "zig fmt: container declaration, one item, multi line trailing comma" {
173 try testCanonical(
174 \\test "" {
175 \\ comptime {
176 \\ const X = struct {
177 \\ x: i32,
178 \\ };
179 \\ }
180 \\}
181 \\
182 );
183}
184
185test "zig fmt: container declaration, no trailing comma on separate line" {
186 try testTransform(
187 \\test "" {
188 \\ comptime {
189 \\ const X = struct {
190 \\ x: i32
191 \\ };
192 \\ }
193 \\}
194 \\
195 ,
196 \\test "" {
197 \\ comptime {
198 \\ const X = struct { x: i32 };
199 \\ }
200 \\}
201 \\
202 );
203}
204
205test "zig fmt: container declaration, line break, no trailing comma" {
206 try testTransform(
207 \\const X = struct {
208 \\ foo: i32, bar: i8 };
209 ,
210 \\const X = struct { foo: i32, bar: i8 };
211 \\
212 );
213}
214
215test "zig fmt: container declaration, transform trailing comma" {
216 try testTransform(
217 \\const X = struct {
218 \\ foo: i32, bar: i8, };
219 ,
220 \\const X = struct {
221 \\ foo: i32,
222 \\ bar: i8,
223 \\};
224 \\
225 );
226}
227
191228//test "zig fmt: trailing comma in fn parameter list" {
192229// try testCanonical(
193230// \\pub fn f(
lib/std/zig/render.zig+113-118
......@@ -68,7 +68,7 @@ fn renderRoot(ais: *Ais, tree: ast.Tree) Error!void {
6868 const root_decls = tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
6969
7070 for (root_decls) |decl| {
71 try renderContainerDecl(ais, tree, decl, .Newline);
71 try renderMember(ais, tree, decl, .Newline);
7272 }
7373}
7474
......@@ -84,7 +84,7 @@ fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, first_token: ast.TokenInde
8484 }
8585}
8686
87fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
87fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
8888 const token_tags = tree.tokens.items(.tag);
8989 const main_tokens = tree.nodes.items(.main_token);
9090 const datas = tree.nodes.items(.data);
......@@ -158,6 +158,8 @@ fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: S
158158 .ContainerFieldAlign => return renderContainerField(ais, tree, tree.containerFieldAlign(decl), space),
159159 .ContainerField => return renderContainerField(ais, tree, tree.containerField(decl), space),
160160 .Comptime => return renderExpression(ais, tree, decl, space),
161
162 .Root => unreachable,
161163 else => unreachable,
162164 }
163165}
......@@ -195,7 +197,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
195197 // return renderToken(ais, tree, any_type.token, space);
196198 //},
197199 .BlockTwo => {
198 var statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
200 const statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
199201 if (datas[node].lhs == 0) {
200202 return renderBlock(ais, tree, main_tokens[node], statements[0..0], space);
201203 } else if (datas[node].rhs == 0) {
......@@ -667,124 +669,29 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
667669 // return renderToken(ais, tree, grouped_expr.rparen, space);
668670 //},
669671
670 .ContainerDecl => unreachable, // TODO
671 .ContainerDeclArg => unreachable, // TODO
672 .TaggedUnion => unreachable, // TODO
673 .TaggedUnionEnumTag => unreachable, // TODO
674 //.ContainerDecl => {
675 // const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
676
677 // if (container_decl.layout_token) |layout_token| {
678 // try renderToken(ais, tree, layout_token, Space.Space);
679 // }
680
681 // switch (container_decl.init_arg_expr) {
682 // .None => {
683 // try renderToken(ais, tree, container_decl.kind_token, Space.Space); // union
684 // },
685 // .Enum => |enum_tag_type| {
686 // try renderToken(ais, tree, container_decl.kind_token, Space.None); // union
687
688 // const lparen = tree.nextToken(container_decl.kind_token);
689 // const enum_token = tree.nextToken(lparen);
690
691 // try renderToken(ais, tree, lparen, Space.None); // (
692 // try renderToken(ais, tree, enum_token, Space.None); // enum
693
694 // if (enum_tag_type) |expr| {
695 // try renderToken(ais, tree, tree.nextToken(enum_token), Space.None); // (
696 // try renderExpression(ais, tree, expr, Space.None);
697
698 // const rparen = tree.nextToken(expr.lastToken());
699 // try renderToken(ais, tree, rparen, Space.None); // )
700 // try renderToken(ais, tree, tree.nextToken(rparen), Space.Space); // )
701 // } else {
702 // try renderToken(ais, tree, tree.nextToken(enum_token), Space.Space); // )
703 // }
704 // },
705 // .Type => |type_expr| {
706 // try renderToken(ais, tree, container_decl.kind_token, Space.None); // union
707
708 // const lparen = tree.nextToken(container_decl.kind_token);
709 // const rparen = tree.nextToken(type_expr.lastToken());
710
711 // try renderToken(ais, tree, lparen, Space.None); // (
712 // try renderExpression(ais, tree, type_expr, Space.None);
713 // try renderToken(ais, tree, rparen, Space.Space); // )
714 // },
715 // }
716
717 // if (container_decl.fields_and_decls_len == 0) {
718 // {
719 // ais.pushIndentNextLine();
720 // defer ais.popIndent();
721 // try renderToken(ais, tree, container_decl.lbrace_token, Space.None); // lbrace
722 // }
723 // return renderToken(ais, tree, container_decl.rbrace_token, space); // rbrace
724 // }
672 .ContainerDecl,
673 .ContainerDeclComma,
674 => return renderContainerDecl(ais, tree, tree.containerDecl(node), space),
725675
726 // const src_has_trailing_comma = blk: {
727 // var maybe_comma = tree.prevToken(container_decl.lastToken());
728 // // Doc comments for a field may also appear after the comma, eg.
729 // // field_name: T, // comment attached to field_name
730 // if (tree.token_tags[maybe_comma] == .DocComment)
731 // maybe_comma = tree.prevToken(maybe_comma);
732 // break :blk tree.token_tags[maybe_comma] == .Comma;
733 // };
734
735 // const fields_and_decls = container_decl.fieldsAndDecls();
736
737 // // Check if the first declaration and the { are on the same line
738 // const src_has_newline = !tree.tokensOnSameLine(
739 // container_decl.lbrace_token,
740 // fields_and_decls[0].firstToken(),
741 // );
742
743 // // We can only print all the elements in-line if all the
744 // // declarations inside are fields
745 // const src_has_only_fields = blk: {
746 // for (fields_and_decls) |decl| {
747 // if (decl.tag != .ContainerField) break :blk false;
748 // }
749 // break :blk true;
750 // };
751
752 // if (src_has_trailing_comma or !src_has_only_fields) {
753 // // One declaration per line
754 // ais.pushIndentNextLine();
755 // defer ais.popIndent();
756 // try renderToken(ais, tree, container_decl.lbrace_token, .Newline); // lbrace
757
758 // for (fields_and_decls) |decl, i| {
759 // try renderContainerDecl(allocator, ais, tree, decl, .Newline);
760
761 // if (i + 1 < fields_and_decls.len) {
762 // try renderExtraNewline(ais, tree, fields_and_decls[i + 1]);
763 // }
764 // }
765 // } else if (src_has_newline) {
766 // // All the declarations on the same line, but place the items on
767 // // their own line
768 // try renderToken(ais, tree, container_decl.lbrace_token, .Newline); // lbrace
769
770 // ais.pushIndent();
771 // defer ais.popIndent();
772
773 // for (fields_and_decls) |decl, i| {
774 // const space_after_decl: Space = if (i + 1 >= fields_and_decls.len) .Newline else .Space;
775 // try renderContainerDecl(allocator, ais, tree, decl, space_after_decl);
776 // }
777 // } else {
778 // // All the declarations on the same line
779 // try renderToken(ais, tree, container_decl.lbrace_token, .Space); // lbrace
676 .ContainerDeclTwo, .ContainerDeclTwoComma => {
677 var buffer: [2]ast.Node.Index = undefined;
678 return renderContainerDecl(ais, tree, tree.containerDeclTwo(&buffer, node), space);
679 },
680 .ContainerDeclArg,
681 .ContainerDeclArgComma,
682 => return renderContainerDecl(ais, tree, tree.containerDeclArg(node), space),
780683
781 // for (fields_and_decls) |decl| {
782 // try renderContainerDecl(allocator, ais, tree, decl, .Space);
783 // }
784 // }
684 .TaggedUnion,
685 .TaggedUnionComma,
686 => return renderContainerDecl(ais, tree, tree.taggedUnion(node), space),
785687
786 // return renderToken(ais, tree, container_decl.rbrace_token, space); // rbrace
787 //},
688 .TaggedUnionTwo, .TaggedUnionTwoComma => {
689 var buffer: [2]ast.Node.Index = undefined;
690 return renderContainerDecl(ais, tree, tree.taggedUnionTwo(&buffer, node), space);
691 },
692 .TaggedUnionEnumTag,
693 .TaggedUnionEnumTagComma,
694 => return renderContainerDecl(ais, tree, tree.taggedUnionEnumTag(node), space),
788695
789696 .ErrorSetDecl => unreachable, // TODO
790697 //.ErrorSetDecl => {
......@@ -1949,6 +1856,94 @@ fn renderArrayInit(
19491856 }
19501857}
19511858
1859fn renderContainerDecl(
1860 ais: *Ais,
1861 tree: ast.Tree,
1862 container_decl: ast.Full.ContainerDecl,
1863 space: Space,
1864) Error!void {
1865 const token_tags = tree.tokens.items(.tag);
1866 const node_tags = tree.nodes.items(.tag);
1867
1868 if (container_decl.layout_token) |layout_token| {
1869 try renderToken(ais, tree, layout_token, .Space);
1870 }
1871
1872 var lbrace: ast.TokenIndex = undefined;
1873 if (container_decl.ast.enum_token) |enum_token| {
1874 try renderToken(ais, tree, container_decl.ast.main_token, .None); // union
1875 try renderToken(ais, tree, enum_token - 1, .None); // lparen
1876 try renderToken(ais, tree, enum_token, .None); // enum
1877 if (container_decl.ast.arg != 0) {
1878 try renderToken(ais, tree, enum_token + 1, .None); // lparen
1879 try renderExpression(ais, tree, container_decl.ast.arg, .None);
1880 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
1881 try renderToken(ais, tree, rparen, .None); // rparen
1882 try renderToken(ais, tree, rparen + 1, .Space); // rparen
1883 lbrace = rparen + 2;
1884 } else {
1885 try renderToken(ais, tree, enum_token + 1, .Space); // rparen
1886 lbrace = enum_token + 2;
1887 }
1888 } else if (container_decl.ast.arg != 0) {
1889 try renderToken(ais, tree, container_decl.ast.main_token, .None); // union
1890 try renderToken(ais, tree, container_decl.ast.main_token + 1, .None); // lparen
1891 try renderExpression(ais, tree, container_decl.ast.arg, .None);
1892 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
1893 try renderToken(ais, tree, rparen, .Space); // rparen
1894 lbrace = rparen + 1;
1895 } else {
1896 try renderToken(ais, tree, container_decl.ast.main_token, .Space); // union
1897 lbrace = container_decl.ast.main_token + 1;
1898 }
1899
1900 if (container_decl.ast.members.len == 0) {
1901 try renderToken(ais, tree, lbrace, Space.None); // lbrace
1902 return renderToken(ais, tree, lbrace + 1, space); // rbrace
1903 }
1904
1905 const last_member = container_decl.ast.members[container_decl.ast.members.len - 1];
1906 const last_member_token = tree.lastToken(last_member);
1907 const rbrace = switch (token_tags[last_member_token + 1]) {
1908 .DocComment => last_member_token + 2,
1909 .Comma => switch (token_tags[last_member_token + 2]) {
1910 .DocComment => last_member_token + 3,
1911 .RBrace => last_member_token + 2,
1912 else => unreachable,
1913 },
1914 .RBrace => last_member_token + 1,
1915 else => unreachable,
1916 };
1917 const src_has_trailing_comma = token_tags[last_member_token + 1] == .Comma;
1918
1919 if (!src_has_trailing_comma) one_line: {
1920 // We can only print all the members in-line if all the members are fields.
1921 for (container_decl.ast.members) |member| {
1922 if (!node_tags[member].isContainerField()) break :one_line;
1923 }
1924 // All the declarations on the same line.
1925 try renderToken(ais, tree, lbrace, .Space); // lbrace
1926 for (container_decl.ast.members) |member| {
1927 try renderMember(ais, tree, member, .Space);
1928 }
1929 return renderToken(ais, tree, rbrace, space); // rbrace
1930 }
1931
1932 // One member per line.
1933 ais.pushIndent();
1934 try renderToken(ais, tree, lbrace, .Newline); // lbrace
1935 for (container_decl.ast.members) |member, i| {
1936 try renderMember(ais, tree, member, .Newline);
1937
1938 if (i + 1 < container_decl.ast.members.len) {
1939 try renderExtraNewline(ais, tree, container_decl.ast.members[i + 1]);
1940 }
1941 }
1942 ais.popIndent();
1943
1944 return renderToken(ais, tree, rbrace, space); // rbrace
1945}
1946
19521947/// Render an expression, and the comma that follows it, if it is present in the source.
19531948fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
19541949 const token_tags = tree.tokens.items(.tag);