Commit b988815bf0

Isaac Freund <ifreund@ifreund.xyz>
2021-03-07 12:38:08
parser: fix parsing/rendering of a[b.. :c] slicing
The modification to the grammar in the comment is in line with the grammar in the zig-spec repo. Note: checking if the previous token is a colon is insufficent to tell if a block has a label, the identifier must be checked for as well. This can be seen in sentinel terminated slicing: `foo[0..1:{}]`
1 parent d01bb21
lib/std/zig/ast.zig
@@ -525,7 +525,9 @@ pub const Tree = struct {
             => {
                 // Look for a label.
                 const lbrace = main_tokens[n];
-                if (token_tags[lbrace - 1] == .colon) {
+                if (token_tags[lbrace - 1] == .colon and
+                    token_tags[lbrace - 2] == .identifier)
+                {
                     end_offset += 2;
                 }
                 return lbrace - end_offset;
@@ -989,13 +991,13 @@ pub const Tree = struct {
             },
             .slice => {
                 const extra = tree.extraData(datas[n].rhs, Node.Slice);
-                assert(extra.end != 0); // should have used SliceOpen
+                assert(extra.end != 0); // should have used slice_open
                 end_offset += 1; // rbracket
                 n = extra.end;
             },
             .slice_sentinel => {
                 const extra = tree.extraData(datas[n].rhs, Node.SliceSentinel);
-                assert(extra.sentinel != 0); // should have used Slice
+                assert(extra.sentinel != 0); // should have used slice
                 end_offset += 1; // rbracket
                 n = extra.sentinel;
             },
@@ -2925,6 +2927,7 @@ pub const Node = struct {
 
     pub const SliceSentinel = struct {
         start: Index,
+        /// May be 0 if the slice is "open"
         end: Index,
         sentinel: Index,
     };
lib/std/zig/parse.zig
@@ -3441,7 +3441,7 @@ const Parser = struct {
     }
 
     /// SuffixOp
-    ///     <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
+    ///     <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET
     ///      / DOT IDENTIFIER
     ///      / DOTASTERISK
     ///      / DOTQUESTIONMARK
@@ -3453,17 +3453,6 @@ const Parser = struct {
 
                 if (p.eatToken(.ellipsis2)) |_| {
                     const end_expr = try p.parseExpr();
-                    if (end_expr == 0) {
-                        _ = try p.expectToken(.r_bracket);
-                        return p.addNode(.{
-                            .tag = .slice_open,
-                            .main_token = lbracket,
-                            .data = .{
-                                .lhs = lhs,
-                                .rhs = index_expr,
-                            },
-                        });
-                    }
                     if (p.eatToken(.colon)) |_| {
                         const sentinel = try p.parseExpr();
                         _ = try p.expectToken(.r_bracket);
@@ -3479,20 +3468,29 @@ const Parser = struct {
                                 }),
                             },
                         });
-                    } else {
-                        _ = try p.expectToken(.r_bracket);
+                    }
+                    _ = try p.expectToken(.r_bracket);
+                    if (end_expr == 0) {
                         return p.addNode(.{
-                            .tag = .slice,
+                            .tag = .slice_open,
                             .main_token = lbracket,
                             .data = .{
                                 .lhs = lhs,
-                                .rhs = try p.addExtra(Node.Slice{
-                                    .start = index_expr,
-                                    .end = end_expr,
-                                }),
+                                .rhs = index_expr,
                             },
                         });
                     }
+                    return p.addNode(.{
+                        .tag = .slice,
+                        .main_token = lbracket,
+                        .data = .{
+                            .lhs = lhs,
+                            .rhs = try p.addExtra(Node.Slice{
+                                .start = index_expr,
+                                .end = end_expr,
+                            }),
+                        },
+                    });
                 }
                 _ = try p.expectToken(.r_bracket);
                 return p.addNode(.{
lib/std/zig/parser_test.zig
@@ -852,6 +852,7 @@ test "zig fmt: slices" {
     try testCanonical(
         \\const a = b[0..];
         \\const c = d[0..1];
+        \\const d = f[0.. :0];
         \\const e = f[0..1 :0];
         \\
     );
@@ -861,6 +862,7 @@ test "zig fmt: slices with spaces in bounds" {
     try testCanonical(
         \\const a = b[0 + 0 ..];
         \\const c = d[0 + 0 .. 1];
+        \\const c = d[0 + 0 .. :0];
         \\const e = f[0 .. 1 + 1 :0];
         \\
     );
lib/std/zig/render.zig
@@ -470,9 +470,9 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I
             return renderToken(ais, tree, rbracket, space); // ]
         },
 
-        .slice_open => return renderSlice(gpa, ais, tree, tree.sliceOpen(node), space),
-        .slice => return renderSlice(gpa, ais, tree, tree.slice(node), space),
-        .slice_sentinel => return renderSlice(gpa, ais, tree, tree.sliceSentinel(node), space),
+        .slice_open => return renderSlice(gpa, ais, tree, node, tree.sliceOpen(node), space),
+        .slice => return renderSlice(gpa, ais, tree, node, tree.slice(node), space),
+        .slice_sentinel => return renderSlice(gpa, ais, tree, node, tree.sliceSentinel(node), space),
 
         .deref => {
             try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
@@ -815,6 +815,7 @@ fn renderSlice(
     gpa: *Allocator,
     ais: *Ais,
     tree: ast.Tree,
+    slice_node: ast.Node.Index,
     slice: ast.full.Slice,
     space: Space,
 ) Error!void {
@@ -822,7 +823,9 @@ fn renderSlice(
     const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or
         if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false;
     const after_start_space = if (after_start_space_bool) Space.space else Space.none;
-    const after_dots_space = if (slice.ast.end != 0) after_start_space else Space.none;
+    const after_dots_space = if (slice.ast.end != 0)
+        after_start_space
+    else if (slice.ast.sentinel != 0) Space.space else Space.none;
 
     try renderExpression(gpa, ais, tree, slice.ast.sliced, .none);
     try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket
@@ -830,20 +833,18 @@ fn renderSlice(
     const start_last = tree.lastToken(slice.ast.start);
     try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space);
     try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..")
-    if (slice.ast.end == 0) {
-        return renderToken(ais, tree, start_last + 2, space); // rbracket
+
+    if (slice.ast.end != 0) {
+        const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none;
+        try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space);
     }
 
-    const end_last = tree.lastToken(slice.ast.end);
-    const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none;
-    try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space);
-    if (slice.ast.sentinel == 0) {
-        return renderToken(ais, tree, end_last + 1, space); // rbracket
+    if (slice.ast.sentinel != 0) {
+        try renderToken(ais, tree, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon
+        try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none);
     }
 
-    try renderToken(ais, tree, end_last + 1, .none); // colon
-    try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none);
-    try renderToken(ais, tree, tree.lastToken(slice.ast.sentinel) + 1, space); // rbracket
+    try renderToken(ais, tree, tree.lastToken(slice_node), space); // rbracket
 }
 
 fn renderAsmOutput(
src/astgen.zig
@@ -848,7 +848,9 @@ pub fn blockExpr(
     const token_tags = tree.tokens.items(.tag);
 
     const lbrace = main_tokens[block_node];
-    if (token_tags[lbrace - 1] == .colon) {
+    if (token_tags[lbrace - 1] == .colon and
+        token_tags[lbrace - 2] == .identifier)
+    {
         return labeledBlockExpr(mod, scope, rl, block_node, statements, .block);
     }