Commit 290dc5d95b
Changed files (5)
lib
test
stage1
behavior
lib/std/zig/ast.zig
@@ -1701,6 +1701,7 @@ pub const Node = struct {
pub const Slice = struct {
start: *Node,
end: ?*Node,
+ sentinel: ?*Node,
};
};
@@ -1732,6 +1733,10 @@ pub const Node = struct {
if (i < 1) return end;
i -= 1;
}
+ if (range.sentinel) |sentinel| {
+ if (i < 1) return sentinel;
+ i -= 1;
+ }
},
.ArrayInitializer => |*exprs| {
if (i < exprs.len) return exprs.at(i).*;
lib/std/zig/parse.zig
@@ -2331,7 +2331,7 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
}
/// SuffixOp
-/// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
+/// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
/// / DOT IDENTIFIER
/// / DOTASTERISK
/// / DOTQUESTIONMARK
@@ -2349,11 +2349,16 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
if (eatToken(it, .Ellipsis2) != null) {
const end_expr = try parseExpr(arena, it, tree);
+ const sentinel: ?*ast.Node = if (eatToken(it, .Colon) != null)
+ try parseExpr(arena, it, tree)
+ else
+ null;
break :blk OpAndToken{
.op = Op{
.Slice = Op.Slice{
.start = index_expr,
.end = end_expr,
+ .sentinel = sentinel,
},
},
.token = try expectToken(it, tree, .RBracket),
lib/std/zig/parser_test.zig
@@ -419,10 +419,13 @@ test "zig fmt: pointer of unknown length" {
test "zig fmt: spaces around slice operator" {
try testCanonical(
\\var a = b[c..d];
+ \\var a = b[c..d :0];
\\var a = b[c + 1 .. d];
\\var a = b[c + 1 ..];
\\var a = b[c .. d + 1];
+ \\var a = b[c .. d + 1 :0];
\\var a = b[c.a..d.e];
+ \\var a = b[c.a..d.e :0];
\\
);
}
lib/std/zig/render.zig
@@ -689,7 +689,13 @@ fn renderExpression(
try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space);
try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..
if (range.end) |end| {
- try renderExpression(allocator, stream, tree, indent, start_col, end, Space.None);
+ const after_end_space = if (range.sentinel != null) Space.Space else Space.None;
+ try renderExpression(allocator, stream, tree, indent, start_col, end, after_end_space);
+ }
+ if (range.sentinel) |sentinel| {
+ const colon = tree.prevToken(sentinel.firstToken());
+ try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
}
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]
},
test/stage1/behavior/slice.zig
@@ -78,3 +78,22 @@ test "access len index of sentinel-terminated slice" {
S.doTheTest();
comptime S.doTheTest();
}
+
+test "obtaining a null terminated slice" {
+ // here we have a normal array
+ var buf: [50]u8 = undefined;
+
+ buf[0] = 'a';
+ buf[1] = 'b';
+ buf[2] = 'c';
+ buf[3] = 0;
+
+ // now we obtain a null terminated slice:
+ const ptr = buf[0..3 :0];
+
+ var runtime_len: usize = 3;
+ const ptr2 = buf[0..runtime_len :0];
+ // ptr2 is a null-terminated slice
+ comptime expect(@TypeOf(ptr2) == [:0]u8);
+ comptime expect(@TypeOf(ptr2[0..2]) == []u8);
+}