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:
@@ -150,6 +150,10 @@ pub const Lexer = struct {
|
||||
self.index += 1;
|
||||
return self.makeToken(.dot_dot_eq, start, self.index);
|
||||
}
|
||||
if (self.peek() == '<') {
|
||||
self.index += 1;
|
||||
return self.makeToken(.dot_dot_lt, start, self.index);
|
||||
}
|
||||
return self.makeToken(.dot_dot, start, self.index);
|
||||
}
|
||||
return self.makeToken(.dot, start, self.index);
|
||||
@@ -175,6 +179,19 @@ pub const Lexer = struct {
|
||||
self.index += 1;
|
||||
return self.makeToken(.fat_arrow, start, self.index);
|
||||
}
|
||||
// Range with an explicit inclusive start: `=..`, `=..=`, `=..<`.
|
||||
if (self.peek() == '.' and self.peekAt(1) == '.') {
|
||||
self.index += 2;
|
||||
if (self.peek() == '=') {
|
||||
self.index += 1;
|
||||
return self.makeToken(.eq_dot_dot_eq, start, self.index);
|
||||
}
|
||||
if (self.peek() == '<') {
|
||||
self.index += 1;
|
||||
return self.makeToken(.eq_dot_dot_lt, start, self.index);
|
||||
}
|
||||
return self.makeToken(.eq_dot_dot, start, self.index);
|
||||
}
|
||||
return self.makeToken(.equal, start, self.index);
|
||||
},
|
||||
'+' => {
|
||||
@@ -266,6 +283,19 @@ pub const Lexer = struct {
|
||||
return self.makeToken(.bang, start, self.index);
|
||||
},
|
||||
'<' => {
|
||||
// Range with an exclusive start: `<..`, `<..=`, `<..<`.
|
||||
if (self.peek() == '.' and self.peekAt(1) == '.') {
|
||||
self.index += 2;
|
||||
if (self.peek() == '=') {
|
||||
self.index += 1;
|
||||
return self.makeToken(.lt_dot_dot_eq, start, self.index);
|
||||
}
|
||||
if (self.peek() == '<') {
|
||||
self.index += 1;
|
||||
return self.makeToken(.lt_dot_dot_lt, start, self.index);
|
||||
}
|
||||
return self.makeToken(.lt_dot_dot, start, self.index);
|
||||
}
|
||||
if (self.peek() == '<') {
|
||||
self.index += 1;
|
||||
if (self.peek() == '=') {
|
||||
@@ -433,6 +463,14 @@ pub const Lexer = struct {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn peekAt(self: *const Lexer, offset: u32) u8 {
|
||||
const i = self.index + offset;
|
||||
if (i < self.source.len) {
|
||||
return self.source[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn makeToken(_: *const Lexer, tag: Tag, start: u32, end: u32) Token {
|
||||
return .{ .tag = tag, .loc = .{ .start = start, .end = end } };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user