lang: slice ranges take the same bound markers as for-header ranges

xs[1..=3] (end inclusive), xs[0<..<4] (both exclusive), xs[..=2]
(prefix form with markers, implicit 0 start), xs[2<..] (open end,
exclusive start), and xs[..] (whole collection) — lowered as lo+1 /
hi+1 on the existing subslice op. Strings slice through the same path.
An explicit end marker requires an end expression, matching the
for-header rule.

Regression: examples/0052-basic-slice-range-bounds.sx.
This commit is contained in:
agra
2026-06-10 22:12:45 +03:00
parent f513c11ea6
commit fea5617e4e
9 changed files with 93 additions and 11 deletions

View File

@@ -2575,26 +2575,45 @@ pub const Parser = struct {
const saved_hdr_idx = self.in_for_header;
self.in_for_header = false;
defer self.in_for_header = saved_hdr_idx;
if (self.current.tag == .dot_dot) {
// [..end]
if (rangeTokenInfo(self.current.tag)) |rt| {
// Prefix form: [..end] / [..=end] / [..] — implicit 0 start
// (a start marker applies to it: [<..end] begins at 1).
self.advance();
const end_expr = try self.parseExpr();
if (rt.end_marked and self.current.tag == .r_bracket) {
return self.fail("a range with an explicit end marker ('..=' / '..<') requires an end expression — the open form is '..'");
}
const end_expr: ?*ast.Node = if (self.current.tag != .r_bracket)
try self.parseExpr()
else
null;
try self.expect(.r_bracket);
expr = try self.createNode(expr.span.start, .{ .slice_expr = .{
.object = expr, .start = null, .end = end_expr,
.object = expr,
.start = null,
.end = end_expr,
.start_exclusive = rt.start_exclusive,
.end_inclusive = rt.end_inclusive,
} });
} else {
const first = try self.parseExpr();
if (self.current.tag == .dot_dot) {
// [start..end] or [start..]
if (rangeTokenInfo(self.current.tag)) |rt| {
// [start..end] or [start..] — same bound-marker matrix
// as the for-header ranges.
self.advance();
if (rt.end_marked and self.current.tag == .r_bracket) {
return self.fail("a range with an explicit end marker ('..=' / '..<') requires an end expression — the open form is 'a..'");
}
const end_expr: ?*ast.Node = if (self.current.tag != .r_bracket)
try self.parseExpr()
else
null;
try self.expect(.r_bracket);
expr = try self.createNode(expr.span.start, .{ .slice_expr = .{
.object = expr, .start = first, .end = end_expr,
.object = expr,
.start = first,
.end = end_expr,
.start_exclusive = rt.start_exclusive,
.end_inclusive = rt.end_inclusive,
} });
} else {
// [index] — normal index access