lang: range bound markers — '=' inclusive / '<' exclusive on either side of '..'
Each side of '..' takes an optional bound marker, defaulting to
start-inclusive, end-exclusive (a..b == a=..<b; a..=b stays the short
end-inclusive spelling):
for 0<..<N (i) { } // 1 .. N-1 (both exclusive)
for 0=..=N (i) { } // 0 .. N (both inclusive)
for 0<..=N (i) { } // 1 .. N
for 0..<N (i) { } // 0 .. N-1 (explicit default)
for xs, 2<.. (x, i) // open range, exclusive start: i = 3, 4, ...
The nine lexemes are single tokens (maximal munch on '<'/'='/'..'), so
expression parsing never sees the leading marker as a comparison; '<',
'<<', '<=', '==', '=>' lex unchanged. An explicit end marker makes the
end expression mandatory; open forms are a.. / a<.. / a=... Works in
runtime, multi-iterable, and inline-for headers.
Regression: examples/0051-basic-for-range-bounds.sx (full matrix, open
start-marked ranges, comptime unroll, runtime bounds, lexer
non-regression); 1152's pinned message generalized.
This commit is contained in:
@@ -315,13 +315,14 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
|
||||
for (fe.iterables, 0..) |it, i| {
|
||||
if (it.is_range) {
|
||||
const start_ref = self.lowerExpr(it.expr);
|
||||
var start_ref = self.lowerExpr(it.expr);
|
||||
if (it.start_exclusive) start_ref = self.builder.add(start_ref, self.builder.constInt(1, .s64), .s64);
|
||||
const slot = self.builder.alloca(.s64);
|
||||
self.builder.store(slot, start_ref);
|
||||
if (i == 0) {
|
||||
// Parser guarantees the first iterable is bounded.
|
||||
var end_ref = self.lowerExpr(it.range_end.?);
|
||||
if (it.inclusive) end_ref = self.builder.add(end_ref, self.builder.constInt(1, .s64), .s64);
|
||||
if (it.end_inclusive) end_ref = self.builder.add(end_ref, self.builder.constInt(1, .s64), .s64);
|
||||
limit = end_ref;
|
||||
}
|
||||
preps.append(self.alloc, .{ .is_range = true, .slot = slot }) catch unreachable;
|
||||
@@ -476,15 +477,16 @@ pub fn lowerInlineRangeFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, it.expr.span, "inline for: a single bounded range is required — `inline for 0..N (i) {{ }}`", .{});
|
||||
return self.builder.constInt(0, .void);
|
||||
}
|
||||
const start = self.evalComptimeInt(it.expr) orelse {
|
||||
var start = self.evalComptimeInt(it.expr) orelse {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, it.expr.span, "inline for: range start is not a compile-time integer", .{});
|
||||
return self.builder.constInt(0, .void);
|
||||
};
|
||||
if (it.start_exclusive) start += 1;
|
||||
var end = self.evalComptimeInt(it.range_end.?) orelse {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, it.range_end.?.span, "inline for: range end is not a compile-time integer", .{});
|
||||
return self.builder.constInt(0, .void);
|
||||
};
|
||||
if (it.inclusive) end += 1;
|
||||
if (it.end_inclusive) end += 1;
|
||||
const capture_name = if (fe.captures.len > 0) fe.captures[0].name else "";
|
||||
|
||||
var i: i64 = start;
|
||||
|
||||
Reference in New Issue
Block a user