diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index c6d477cc5f..96a60614bf 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -212,8 +212,6 @@ pub const Tree = struct { .Try, .Await, .OptionalType, - .ArrayInitDotTwo, - .ArrayInitDot, .Switch, .IfSimple, .If, @@ -250,9 +248,12 @@ pub const Tree = struct { .FnProto, => return main_tokens[n], + .ArrayInitDot, + .ArrayInitDotTwo, + .ArrayInitDotTwoComma, + .StructInitDot, .StructInitDotTwo, .StructInitDotTwoComma, - .StructInitDot, => return main_tokens[n] - 1, .Catch, @@ -304,6 +305,7 @@ pub const Tree = struct { .ArrayInitOne, .ArrayInit, .StructInitOne, + .StructInit, .CallOne, .Call, .SwitchCaseOne, @@ -367,7 +369,6 @@ pub const Tree = struct { .PtrTypeSentinel => unreachable, // TODO .PtrType => unreachable, // TODO .SliceType => unreachable, // TODO - .StructInit => unreachable, // TODO .SwitchCaseMulti => unreachable, // TODO .WhileSimple => unreachable, // TODO .WhileCont => unreachable, // TODO @@ -506,6 +507,7 @@ pub const Tree = struct { n = datas[n].rhs; }, + .ArrayInitDotTwo, .BuiltinCallTwo, .BlockTwo, .StructInitDotTwo, @@ -519,7 +521,9 @@ pub const Tree = struct { return main_tokens[n] + end_offset; } }, - .StructInitDotTwoComma => { + .ArrayInitDotTwoComma, + .StructInitDotTwoComma, + => { end_offset += 2; // for the comma + rbrace if (datas[n].rhs != 0) { n = datas[n].rhs; @@ -590,13 +594,16 @@ pub const Tree = struct { // require recursion due to the optional comma followed by rbrace. // TODO follow the pattern set by StructInitDotTwoComma which will allow // lastToken to work for all of these. + .ArrayInit => unreachable, + .ArrayInitOne => unreachable, + .ArrayInitDot => unreachable, + .StructInit => unreachable, + .StructInitOne => unreachable, .StructInitDot => unreachable, .ContainerFieldInit => unreachable, .ContainerFieldAlign => unreachable, .ContainerField => unreachable, - .ArrayInitDotTwo => unreachable, // TODO - .ArrayInitDot => unreachable, // TODO .Switch => unreachable, // TODO .If => unreachable, // TODO .Continue => unreachable, // TODO @@ -606,9 +613,6 @@ pub const Tree = struct { .Asm => unreachable, // TODO .SliceOpen => unreachable, // TODO .Slice => unreachable, // TODO - .ArrayInitOne => unreachable, // TODO - .ArrayInit => unreachable, // TODO - .StructInitOne => unreachable, // TODO .SwitchCaseOne => unreachable, // TODO .SwitchRange => unreachable, // TODO .FnDecl => unreachable, // TODO @@ -618,7 +622,6 @@ pub const Tree = struct { .PtrTypeSentinel => unreachable, // TODO .PtrType => unreachable, // TODO .SliceType => unreachable, // TODO - .StructInit => unreachable, // TODO .SwitchCaseMulti => unreachable, // TODO .WhileCont => unreachable, // TODO .While => unreachable, // TODO @@ -863,6 +866,65 @@ pub const Tree = struct { }); } + pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.ArrayInit { + assert(tree.nodes.items(.tag)[node] == .ArrayInitOne); + const data = tree.nodes.items(.data)[node]; + buffer[0] = data.rhs; + const elements = if (data.rhs == 0) buffer[0..0] else buffer[0..1]; + return .{ + .ast = .{ + .lbrace = tree.nodes.items(.main_token)[node], + .elements = elements, + .type_expr = data.lhs, + }, + }; + } + + pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ArrayInit { + assert(tree.nodes.items(.tag)[node] == .ArrayInitDotTwo or + tree.nodes.items(.tag)[node] == .ArrayInitDotTwoComma); + const data = tree.nodes.items(.data)[node]; + buffer.* = .{ data.lhs, data.rhs }; + const elements = if (data.rhs != 0) + buffer[0..2] + else if (data.lhs != 0) + buffer[0..1] + else + buffer[0..0]; + return .{ + .ast = .{ + .lbrace = tree.nodes.items(.main_token)[node], + .elements = elements, + .type_expr = 0, + }, + }; + } + + pub fn arrayInitDot(tree: Tree, node: Node.Index) Full.ArrayInit { + assert(tree.nodes.items(.tag)[node] == .ArrayInitDot); + const data = tree.nodes.items(.data)[node]; + return .{ + .ast = .{ + .lbrace = tree.nodes.items(.main_token)[node], + .elements = tree.extra_data[data.lhs..data.rhs], + .type_expr = 0, + }, + }; + } + + pub fn arrayInit(tree: Tree, node: Node.Index) Full.ArrayInit { + assert(tree.nodes.items(.tag)[node] == .ArrayInit); + const data = tree.nodes.items(.data)[node]; + const elem_range = tree.extraData(data.rhs, Node.SubRange); + return .{ + .ast = .{ + .lbrace = tree.nodes.items(.main_token)[node], + .elements = tree.extra_data[elem_range.start..elem_range.end], + .type_expr = data.lhs, + }, + }; + } + fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl { const token_tags = tree.tokens.items(.tag); var result: Full.VarDecl = .{ @@ -1015,6 +1077,16 @@ pub const Full = struct { type_expr: Node.Index, }; }; + + pub const ArrayInit = struct { + ast: Ast, + + pub const Ast = struct { + lbrace: TokenIndex, + elements: []const Node.Index, + type_expr: Node.Index, + }; + }; }; pub const Error = union(enum) { @@ -1421,6 +1493,9 @@ pub const Node = struct { ArrayInitOne, /// `.{lhs, rhs}`. lhs and rhs can be omitted. ArrayInitDotTwo, + /// Same as `ArrayInitDotTwo` except there is known to be a trailing comma + /// before the final rbrace. + ArrayInitDotTwoComma, /// `.{a, b}`. `sub_list[lhs..rhs]`. ArrayInitDot, /// `lhs{a, b}`. `sub_range_list[rhs]`. lhs can be omitted which means `.{a, b}`. diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index deff7fdf98..394c36fcb5 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -2536,7 +2536,7 @@ const Parser = struct { const comma_one = p.eatToken(.Comma); if (p.eatToken(.RBrace)) |_| { return p.addNode(.{ - .tag = .ArrayInitDotTwo, + .tag = if (comma_one != null) .ArrayInitDotTwoComma else .ArrayInitDotTwo, .main_token = lbrace, .data = .{ .lhs = elem_init_one, @@ -2553,7 +2553,7 @@ const Parser = struct { const comma_two = p.eatToken(.Comma); if (p.eatToken(.RBrace)) |_| { return p.addNode(.{ - .tag = .ArrayInitDotTwo, + .tag = if (comma_one != null) .ArrayInitDotTwoComma else .ArrayInitDotTwo, .main_token = lbrace, .data = .{ .lhs = elem_init_one, @@ -2576,7 +2576,10 @@ const Parser = struct { if (next == 0) break; try init_list.append(next); switch (p.token_tags[p.nextToken()]) { - .Comma => continue, + .Comma => { + if (p.eatToken(.RBrace)) |_| break; + continue; + }, .RBrace => break, .Colon, .RParen, .RBracket => { p.tok_i -= 1; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 9174e3b1bd..d155c2ee5e 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -336,24 +336,160 @@ test "zig fmt: nosuspend block" { // \\ // ); //} -// -//test "zig fmt: anon struct literal syntax" { -// try testCanonical( -// \\const x = .{ -// \\ .a = b, -// \\ .c = d, -// \\}; -// \\ -// ); -//} -// -//test "zig fmt: anon list literal syntax" { -// try testCanonical( -// \\const x = .{ a, b, c }; -// \\ -// ); -//} -// + +test "zig fmt: anon struct literal 1 element" { + try testCanonical( + \\const x = .{ .a = b }; + \\ + ); +} + +test "zig fmt: anon struct literal 1 element comma" { + try testCanonical( + \\const x = .{ + \\ .a = b, + \\}; + \\ + ); +} + +test "zig fmt: anon struct literal 2 element" { + try testCanonical( + \\const x = .{ .a = b, .c = d }; + \\ + ); +} + +test "zig fmt: anon struct literal 2 element comma" { + try testCanonical( + \\const x = .{ + \\ .a = b, + \\ .c = d, + \\}; + \\ + ); +} + +test "zig fmt: anon struct literal 3 element" { + try testCanonical( + \\const x = .{ .a = b, .c = d, .e = f }; + \\ + ); +} + +test "zig fmt: anon struct literal 3 element comma" { + try testCanonical( + \\const x = .{ + \\ .a = b, + \\ .c = d, + \\ .e = f, + \\}; + \\ + ); +} + +test "zig fmt: struct literal 1 element" { + try testCanonical( + \\const x = X{ .a = b }; + \\ + ); +} + +test "zig fmt: struct literal 1 element comma" { + try testCanonical( + \\const x = X{ + \\ .a = b, + \\}; + \\ + ); +} + +test "zig fmt: struct literal 2 element" { + try testCanonical( + \\const x = X{ .a = b, .c = d }; + \\ + ); +} + +test "zig fmt: struct literal 2 element comma" { + try testCanonical( + \\const x = X{ + \\ .a = b, + \\ .c = d, + \\}; + \\ + ); +} + +test "zig fmt: struct literal 3 element" { + try testCanonical( + \\const x = X{ .a = b, .c = d, .e = f }; + \\ + ); +} + +test "zig fmt: struct literal 3 element comma" { + try testCanonical( + \\const x = X{ + \\ .a = b, + \\ .c = d, + \\ .e = f, + \\}; + \\ + ); +} + +test "zig fmt: anon list literal 1 element" { + try testCanonical( + \\const x = .{a}; + \\ + ); +} + +test "zig fmt: anon list literal 1 element comma" { + try testCanonical( + \\const x = .{ + \\ a, + \\}; + \\ + ); +} + +test "zig fmt: anon list literal 2 element" { + try testCanonical( + \\const x = .{ a, b }; + \\ + ); +} + +test "zig fmt: anon list literal 2 element comma" { + try testCanonical( + \\const x = .{ + \\ a, + \\ b, + \\}; + \\ + ); +} + +test "zig fmt: anon list literal 3 element" { + try testCanonical( + \\const x = .{ a, b, c }; + \\ + ); +} + +test "zig fmt: anon list literal 3 element comma" { + try testCanonical( + \\const x = .{ + \\ a, + \\ b, + \\ c, + \\}; + \\ + ); +} + //test "zig fmt: async function" { // try testCanonical( // \\pub const Server = struct { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 5ebed640e7..deeae58e7f 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -503,221 +503,16 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac // return renderExpression(ais, tree, slice_type.rhs, space); //}, - .ArrayInitOne => unreachable, // TODO - .ArrayInitDotTwo => unreachable, // TODO - .ArrayInitDot => unreachable, // TODO - .ArrayInit => unreachable, // TODO - //.ArrayInitializer, .ArrayInitializerDot => { - // var rtoken: ast.TokenIndex = undefined; - // var exprs: []ast.Node.Index = undefined; - // const lhs: union(enum) { dot: ast.TokenIndex, node: ast.Node.Index } = switch (base.tag) { - // .ArrayInitializerDot => blk: { - // const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base); - // rtoken = casted.rtoken; - // exprs = casted.list(); - // break :blk .{ .dot = casted.dot }; - // }, - // .ArrayInitializer => blk: { - // const casted = @fieldParentPtr(ast.Node.ArrayInitializer, "base", base); - // rtoken = casted.rtoken; - // exprs = casted.list(); - // break :blk .{ .node = casted.lhs }; - // }, - // else => unreachable, - // }; - - // const lbrace = switch (lhs) { - // .dot => |dot| tree.nextToken(dot), - // .node => |node| tree.nextToken(node.lastToken()), - // }; - - // switch (lhs) { - // .dot => |dot| try renderToken(ais, tree, dot, Space.None), - // .node => |node| try renderExpression(ais, tree, node, Space.None), - // } - - // if (exprs.len == 0) { - // try renderToken(ais, tree, lbrace, Space.None); - // return renderToken(ais, tree, rtoken, space); - // } - - // if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_tags[exprs[0].*.lastToken() + 1] == .RBrace) { - // const expr = exprs[0]; - - // try renderToken(ais, tree, lbrace, Space.None); - // try renderExpression(ais, tree, expr, Space.None); - // return renderToken(ais, tree, rtoken, space); - // } - - // // scan to find row size - // if (rowSize(tree, exprs, rtoken) != null) { - // { - // ais.pushIndentNextLine(); - // defer ais.popIndent(); - // try renderToken(ais, tree, lbrace, Space.Newline); - - // var expr_index: usize = 0; - // while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| { - // const row_exprs = exprs[expr_index..]; - // // A place to store the width of each expression and its column's maximum - // var widths = try allocator.alloc(usize, row_exprs.len + row_size); - // defer allocator.free(widths); - // mem.set(usize, widths, 0); - - // var expr_newlines = try allocator.alloc(bool, row_exprs.len); - // defer allocator.free(expr_newlines); - // mem.set(bool, expr_newlines, false); - - // var expr_widths = widths[0 .. widths.len - row_size]; - // var column_widths = widths[widths.len - row_size ..]; - - // // Find next row with trailing comment (if any) to end the current section - // var section_end = sec_end: { - // var this_line_first_expr: usize = 0; - // var this_line_size = rowSize(tree, row_exprs, rtoken); - // for (row_exprs) |expr, i| { - // // Ignore comment on first line of this section - // if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue; - // // Track start of line containing comment - // if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) { - // this_line_first_expr = i; - // this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken); - // } - - // const maybe_comma = expr.lastToken() + 1; - // const maybe_comment = expr.lastToken() + 2; - // if (maybe_comment < tree.token_tags.len) { - // if (tree.token_tags[maybe_comma] == .Comma and - // tree.token_tags[maybe_comment] == .LineComment and - // tree.tokensOnSameLine(expr.lastToken(), maybe_comment)) - // { - // var comment_token_loc = tree.token_locs[maybe_comment]; - // const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2; - // if (!comment_is_empty) { - // // Found row ending in comment - // break :sec_end i - this_line_size.? + 1; - // } - // } - // } - // } - // break :sec_end row_exprs.len; - // }; - // expr_index += section_end; - - // const section_exprs = row_exprs[0..section_end]; - - // // Null stream for counting the printed length of each expression - // var line_find_stream = std.io.findByteWriter('\n', std.io.null_writer); - // var counting_stream = std.io.countingWriter(line_find_stream.writer()); - // var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer()); - - // // Calculate size of columns in current section - // var column_counter: usize = 0; - // var single_line = true; - // for (section_exprs) |expr, i| { - // if (i + 1 < section_exprs.len) { - // counting_stream.bytes_written = 0; - // line_find_stream.byte_found = false; - // try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None); - // const width = @intCast(usize, counting_stream.bytes_written); - // expr_widths[i] = width; - // expr_newlines[i] = line_find_stream.byte_found; - - // if (!line_find_stream.byte_found) { - // const column = column_counter % row_size; - // column_widths[column] = std.math.max(column_widths[column], width); - - // const expr_last_token = expr.*.lastToken() + 1; - // const next_expr = section_exprs[i + 1]; - // const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, next_expr.*.firstToken()); - - // column_counter += 1; - - // if (loc.line != 0) single_line = false; - // } else { - // single_line = false; - // column_counter = 0; - // } - // } else { - // counting_stream.bytes_written = 0; - // try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None); - // const width = @intCast(usize, counting_stream.bytes_written); - // expr_widths[i] = width; - // expr_newlines[i] = line_find_stream.byte_found; - - // if (!line_find_stream.byte_found) { - // const column = column_counter % row_size; - // column_widths[column] = std.math.max(column_widths[column], width); - // } - // break; - // } - // } - - // // Render exprs in current section - // column_counter = 0; - // var last_col_index: usize = row_size - 1; - // for (section_exprs) |expr, i| { - // if (i + 1 < section_exprs.len) { - // const next_expr = section_exprs[i + 1]; - // try renderExpression(ais, tree, expr, Space.None); - - // const comma = tree.nextToken(expr.*.lastToken()); - - // if (column_counter != last_col_index) { - // if (!expr_newlines[i] and !expr_newlines[i + 1]) { - // // Neither the current or next expression is multiline - // try renderToken(ais, tree, comma, Space.Space); // , - // assert(column_widths[column_counter % row_size] >= expr_widths[i]); - // const padding = column_widths[column_counter % row_size] - expr_widths[i]; - // try ais.writer().writeByteNTimes(' ', padding); - - // column_counter += 1; - // continue; - // } - // } - // if (single_line and row_size != 1) { - // try renderToken(ais, tree, comma, Space.Space); // , - // continue; - // } - - // column_counter = 0; - // try renderToken(ais, tree, comma, Space.Newline); // , - // try renderExtraNewline(ais, tree, next_expr); - // } else { - // const maybe_comma = tree.nextToken(expr.*.lastToken()); - // if (tree.token_tags[maybe_comma] == .Comma) { - // try renderExpression(ais, tree, expr, Space.None); // , - // try renderToken(ais, tree, maybe_comma, Space.Newline); // , - // } else { - // try renderExpression(ais, tree, expr, Space.Comma); // , - // } - // } - // } - - // if (expr_index == exprs.len) { - // break; - // } - // } - // } - - // return renderToken(ais, tree, rtoken, space); - // } - - // // Single line - // try renderToken(ais, tree, lbrace, Space.Space); - // for (exprs) |expr, i| { - // if (i + 1 < exprs.len) { - // const next_expr = exprs[i + 1]; - // try renderExpression(ais, tree, expr, Space.None); - // const comma = tree.nextToken(expr.*.lastToken()); - // try renderToken(ais, tree, comma, Space.Space); // , - // } else { - // try renderExpression(ais, tree, expr, Space.Space); - // } - // } - - // return renderToken(ais, tree, rtoken, space); - //}, + .ArrayInitOne => { + var elements: [1]ast.Node.Index = undefined; + return renderArrayInit(ais, tree, tree.arrayInitOne(&elements, node), space); + }, + .ArrayInitDotTwo, .ArrayInitDotTwoComma => { + var elements: [2]ast.Node.Index = undefined; + return renderArrayInit(ais, tree, tree.arrayInitDotTwo(&elements, node), space); + }, + .ArrayInitDot => return renderArrayInit(ais, tree, tree.arrayInitDot(node), space), + .ArrayInit => return renderArrayInit(ais, tree, tree.arrayInit(node), space), .StructInitOne => { var fields: [1]ast.Node.Index = undefined; @@ -2158,6 +1953,52 @@ fn renderStructInit( } } +fn renderArrayInit( + ais: *Ais, + tree: ast.Tree, + array_init: ast.Full.ArrayInit, + space: Space, +) Error!void { + const token_tags = tree.tokens.items(.tag); + if (array_init.ast.type_expr == 0) { + try renderToken(ais, tree, array_init.ast.lbrace - 1, .None); // . + } else { + try renderExpression(ais, tree, array_init.ast.type_expr, .None); // T + } + if (array_init.ast.elements.len == 0) { + try renderToken(ais, tree, array_init.ast.lbrace, .None); // lbrace + return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace + } + const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1]; + const last_elem_token = tree.lastToken(last_elem); + if (token_tags[last_elem_token + 1] == .Comma) { + // Render one element per line. + ais.pushIndent(); + try renderToken(ais, tree, array_init.ast.lbrace, .Newline); + + try renderExpression(ais, tree, array_init.ast.elements[0], .Comma); + for (array_init.ast.elements[1..]) |elem| { + try renderExpressionNewlined(ais, tree, elem, .Comma); + } + + ais.popIndent(); + return renderToken(ais, tree, last_elem_token + 2, space); // rbrace + } else { + // Render all on one line, no trailing comma. + if (array_init.ast.elements.len == 1) { + // If there is only one element, we don't use spaces + try renderToken(ais, tree, array_init.ast.lbrace, .None); + try renderExpression(ais, tree, array_init.ast.elements[0], .None); + } else { + try renderToken(ais, tree, array_init.ast.lbrace, .Space); + for (array_init.ast.elements) |elem| { + try renderExpression(ais, tree, elem, .CommaSpace); + } + } + return renderToken(ais, tree, last_elem_token + 1, space); // rbrace + } +} + /// Render an expression, and the comma that follows it, if it is present in the source. fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void { const token_tags = tree.tokens.items(.tag);