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:
48
examples/0051-basic-for-range-bounds.sx
Normal file
48
examples/0051-basic-for-range-bounds.sx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Range bound markers: each side of `..` takes `=` (inclusive) or `<`
|
||||||
|
// (exclusive); defaults are start-inclusive, end-exclusive (`a..b` == `a=..<b`).
|
||||||
|
// Covers the full matrix, open ranges with start markers, comptime unrolling,
|
||||||
|
// runtime bounds, and that `<` / `<<` comparisons still lex normally.
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
for 0<..<5 (i) { print("{} ", i); }
|
||||||
|
print("| 0<..<5\n");
|
||||||
|
for 0=..=5 (i) { print("{} ", i); }
|
||||||
|
print("| 0=..=5\n");
|
||||||
|
for 0<..=5 (i) { print("{} ", i); }
|
||||||
|
print("| 0<..=5\n");
|
||||||
|
for 0=..<5 (i) { print("{} ", i); }
|
||||||
|
print("| 0=..<5\n");
|
||||||
|
for 0..<5 (i) { print("{} ", i); }
|
||||||
|
print("| 0..<5\n");
|
||||||
|
for 0..=5 (i) { print("{} ", i); }
|
||||||
|
print("| 0..=5\n");
|
||||||
|
|
||||||
|
// Exclusive-start open range following a bounded first iterable.
|
||||||
|
xs : [3]s64 = .[10, 20, 30];
|
||||||
|
for xs, 2<.. (x, i) { print("{}@{} ", x, i); }
|
||||||
|
print("| xs, 2<..\n");
|
||||||
|
|
||||||
|
// Explicit inclusive-start open form (synonym of `5..`).
|
||||||
|
for xs, 5=.. (x, i) { print("{}@{} ", x, i); }
|
||||||
|
print("| xs, 5=..\n");
|
||||||
|
|
||||||
|
// Comptime-unrolled with markers.
|
||||||
|
s := 0;
|
||||||
|
inline for 0<..=3 (i) { s += i; }
|
||||||
|
print("inline 0<..=3 sum={}\n", s);
|
||||||
|
|
||||||
|
// Runtime bounds with markers.
|
||||||
|
lo := 1;
|
||||||
|
hi := 4;
|
||||||
|
for lo<..=hi (i) { print("{} ", i); }
|
||||||
|
print("| lo<..=hi\n");
|
||||||
|
|
||||||
|
// Comparison operators still lex normally.
|
||||||
|
a := 3;
|
||||||
|
if a < 5 { print("cmp ok\n"); }
|
||||||
|
b := a << 1;
|
||||||
|
print("shl={}\n", b);
|
||||||
|
0
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// `..=` is the inclusive bounded form — it requires an end expression.
|
// A range with an explicit end marker (`..=` / `..<`) is the bounded form —
|
||||||
|
// it requires an end expression; the open form is `a..`.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
|||||||
1
examples/expected/0051-basic-for-range-bounds.exit
Normal file
1
examples/expected/0051-basic-for-range-bounds.exit
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
1
examples/expected/0051-basic-for-range-bounds.stderr
Normal file
1
examples/expected/0051-basic-for-range-bounds.stderr
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
12
examples/expected/0051-basic-for-range-bounds.stdout
Normal file
12
examples/expected/0051-basic-for-range-bounds.stdout
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
1 2 3 4 | 0<..<5
|
||||||
|
0 1 2 3 4 5 | 0=..=5
|
||||||
|
1 2 3 4 5 | 0<..=5
|
||||||
|
0 1 2 3 4 | 0=..<5
|
||||||
|
0 1 2 3 4 | 0..<5
|
||||||
|
0 1 2 3 4 5 | 0..=5
|
||||||
|
10@3 20@4 30@5 | xs, 2<..
|
||||||
|
10@5 20@6 30@7 | xs, 5=..
|
||||||
|
inline 0<..=3 sum=6
|
||||||
|
2 3 4 | lo<..=hi
|
||||||
|
cmp ok
|
||||||
|
shl=6
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
error: '..=' requires an end expression — the open form is 'a..'
|
error: a range with an explicit end marker ('..=' / '..<') requires an end expression — the open form is 'a..'
|
||||||
--> examples/1152-diagnostics-for-inclusive-open.sx:6:14
|
--> examples/1152-diagnostics-for-inclusive-open.sx:7:14
|
||||||
|
|
|
|
||||||
6 | for 0..= (i) { }
|
7 | for 0..= (i) { }
|
||||||
| ^
|
| ^
|
||||||
|
|||||||
@@ -345,6 +345,8 @@ for items (val) { print("{}\n", val); }
|
|||||||
for items, 0.. (val, idx) { print("[{}] = {}\n", idx, val); }
|
for items, 0.. (val, idx) { print("[{}] = {}\n", idx, val); }
|
||||||
for 1..=5, 0.. (a, b) { print("{}:{}\n", a, b); } // a: 1..5, b follows
|
for 1..=5, 0.. (a, b) { print("{}:{}\n", a, b); } // a: 1..5, b follows
|
||||||
for items (val) => total += val; // arrow body
|
for items (val) => total += val; // arrow body
|
||||||
|
for 0<..<n (i) { } // bound markers: 1 .. n-1
|
||||||
|
for 0=..=n (i) { } // 0 .. n
|
||||||
|
|
||||||
// Defer
|
// Defer
|
||||||
f := open("file.txt");
|
f := open("file.txt");
|
||||||
|
|||||||
20
specs.md
20
specs.md
@@ -1963,6 +1963,23 @@ for xs (x) => sum += x; // arrow body
|
|||||||
inline for 0..n (i) { } // comptime-unrolled single bounded range
|
inline for 0..n (i) { } // comptime-unrolled single bounded range
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Range bound markers.** Each side of `..` takes an optional marker — `=`
|
||||||
|
inclusive, `<` exclusive — with defaults start-inclusive, end-exclusive
|
||||||
|
(`a..b` ≡ `a=..<b`; `a..=b` is the short end-inclusive spelling):
|
||||||
|
|
||||||
|
```sx
|
||||||
|
for 0<..<5 (i) { } // 1 2 3 4 — both ends exclusive
|
||||||
|
for 0=..=5 (i) { } // 0 1 2 3 4 5 — both ends inclusive
|
||||||
|
for 0<..=5 (i) { } // 1 2 3 4 5
|
||||||
|
for 0=..<5 (i) { } // 0 1 2 3 4 — explicit spelling of `0..5`
|
||||||
|
for 0..<5 (i) { } // 0 1 2 3 4 — explicit spelling of `0..5`
|
||||||
|
for xs, 2<.. (x, i) { } // open range with an exclusive start: i = 3, 4, …
|
||||||
|
```
|
||||||
|
|
||||||
|
A marker after the dots (`..=` / `..<`) makes the end expression mandatory;
|
||||||
|
the open form is `a..` (or `a<..` / `a=..`). The lexemes are single tokens —
|
||||||
|
no whitespace inside (`0 <..< N` is fine, `0 < ..` is not a range).
|
||||||
|
|
||||||
**First-iterable-wins.** The FIRST iterable's length drives the loop: a
|
**First-iterable-wins.** The FIRST iterable's length drives the loop: a
|
||||||
bounded range runs `end - start` times (`..=`: `end - start + 1`), a
|
bounded range runs `end - start` times (`..=`: `end - start + 1`), a
|
||||||
collection runs `len` times. The first iterable must be bounded — an open
|
collection runs `len` times. The first iterable must be bounded — an open
|
||||||
@@ -3069,7 +3086,8 @@ lvalue = IDENT | postfix '.' IDENT
|
|||||||
expr = if_expr | match_expr | while_expr | for_expr | lambda | binary
|
expr = if_expr | match_expr | while_expr | for_expr | lambda | binary
|
||||||
while_expr = 'while' expr block
|
while_expr = 'while' expr block
|
||||||
for_expr = 'for' for_iter (',' for_iter)* [for_capture] (block | '=>' stmt)
|
for_expr = 'for' for_iter (',' for_iter)* [for_capture] (block | '=>' stmt)
|
||||||
for_iter = expr [('..' | '..=') [expr]]
|
for_iter = expr [range_op [expr]]
|
||||||
|
range_op = '..' | '..=' | '..<' | '<..' | '<..=' | '<..<' | '=..' | '=..=' | '=..<'
|
||||||
for_capture = '(' ['*'] IDENT (',' ['*'] IDENT)* ')'
|
for_capture = '(' ['*'] IDENT (',' ['*'] IDENT)* ')'
|
||||||
binary = catch_expr (binop catch_expr)* // binop includes `or` (fallback / chain)
|
binary = catch_expr (binop catch_expr)* // binop includes `or` (fallback / chain)
|
||||||
catch_expr = unary ('catch' IDENT? (block | '==' '{' case_arm* else_arm? '}' | unary))?
|
catch_expr = unary ('catch' IDENT? (block | '==' '{' case_arm* else_arm? '}' | unary))?
|
||||||
|
|||||||
16
src/ast.zig
16
src/ast.zig
@@ -646,13 +646,19 @@ pub const WhileExpr = struct {
|
|||||||
pub const ForIterable = struct {
|
pub const ForIterable = struct {
|
||||||
/// Collection expression, or the range START for the range forms.
|
/// Collection expression, or the range START for the range forms.
|
||||||
expr: *Node,
|
expr: *Node,
|
||||||
/// `a..b` / `a..=b` end. Null for a plain collection AND for the
|
/// Range end. Null for a plain collection AND for the open-ended range
|
||||||
/// open-ended range `a..` (distinguished by `is_range`).
|
/// `a..` (distinguished by `is_range`).
|
||||||
range_end: ?*Node = null,
|
range_end: ?*Node = null,
|
||||||
/// True for any range form (`a..`, `a..b`, `a..=b`).
|
/// True for any range form. Each side of `..` takes an optional bound
|
||||||
|
/// marker — `=` inclusive, `<` exclusive — with defaults start-inclusive,
|
||||||
|
/// end-exclusive: `a..b` ≡ `a=..<b`; `a<..<b` is 1-past-start to
|
||||||
|
/// end-1; `a=..=b` includes both ends; `a..=b` keeps the short
|
||||||
|
/// end-inclusive spelling.
|
||||||
is_range: bool = false,
|
is_range: bool = false,
|
||||||
/// `a..=b` — end is inclusive.
|
/// `<..` family — start is exclusive (cursor begins at start+1).
|
||||||
inclusive: bool = false,
|
start_exclusive: bool = false,
|
||||||
|
/// `..=` family — end is inclusive.
|
||||||
|
end_inclusive: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// One capture of a `for` header: `(x)`, `(*x)`, `(x, y, ...)`.
|
/// One capture of a `for` header: `(x)`, `(*x)`, `(x, y, ...)`.
|
||||||
|
|||||||
@@ -315,13 +315,14 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
|||||||
|
|
||||||
for (fe.iterables, 0..) |it, i| {
|
for (fe.iterables, 0..) |it, i| {
|
||||||
if (it.is_range) {
|
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);
|
const slot = self.builder.alloca(.s64);
|
||||||
self.builder.store(slot, start_ref);
|
self.builder.store(slot, start_ref);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
// Parser guarantees the first iterable is bounded.
|
// Parser guarantees the first iterable is bounded.
|
||||||
var end_ref = self.lowerExpr(it.range_end.?);
|
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;
|
limit = end_ref;
|
||||||
}
|
}
|
||||||
preps.append(self.alloc, .{ .is_range = true, .slot = slot }) catch unreachable;
|
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) {{ }}`", .{});
|
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);
|
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", .{});
|
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);
|
return self.builder.constInt(0, .void);
|
||||||
};
|
};
|
||||||
|
if (it.start_exclusive) start += 1;
|
||||||
var end = self.evalComptimeInt(it.range_end.?) orelse {
|
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", .{});
|
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);
|
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 "";
|
const capture_name = if (fe.captures.len > 0) fe.captures[0].name else "";
|
||||||
|
|
||||||
var i: i64 = start;
|
var i: i64 = start;
|
||||||
|
|||||||
@@ -150,6 +150,10 @@ pub const Lexer = struct {
|
|||||||
self.index += 1;
|
self.index += 1;
|
||||||
return self.makeToken(.dot_dot_eq, start, self.index);
|
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_dot, start, self.index);
|
||||||
}
|
}
|
||||||
return self.makeToken(.dot, start, self.index);
|
return self.makeToken(.dot, start, self.index);
|
||||||
@@ -175,6 +179,19 @@ pub const Lexer = struct {
|
|||||||
self.index += 1;
|
self.index += 1;
|
||||||
return self.makeToken(.fat_arrow, start, self.index);
|
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);
|
return self.makeToken(.equal, start, self.index);
|
||||||
},
|
},
|
||||||
'+' => {
|
'+' => {
|
||||||
@@ -266,6 +283,19 @@ pub const Lexer = struct {
|
|||||||
return self.makeToken(.bang, start, self.index);
|
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() == '<') {
|
if (self.peek() == '<') {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
if (self.peek() == '=') {
|
if (self.peek() == '=') {
|
||||||
@@ -433,6 +463,14 @@ pub const Lexer = struct {
|
|||||||
return 0;
|
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 {
|
fn makeToken(_: *const Lexer, tag: Tag, start: u32, end: u32) Token {
|
||||||
return .{ .tag = tag, .loc = .{ .start = start, .end = end } };
|
return .{ .tag = tag, .loc = .{ .start = start, .end = end } };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1771,6 +1771,13 @@ pub const Server = struct {
|
|||||||
.dot,
|
.dot,
|
||||||
.dot_dot,
|
.dot_dot,
|
||||||
.dot_dot_eq,
|
.dot_dot_eq,
|
||||||
|
.dot_dot_lt,
|
||||||
|
.lt_dot_dot,
|
||||||
|
.lt_dot_dot_eq,
|
||||||
|
.lt_dot_dot_lt,
|
||||||
|
.eq_dot_dot,
|
||||||
|
.eq_dot_dot_eq,
|
||||||
|
.eq_dot_dot_lt,
|
||||||
.dollar,
|
.dollar,
|
||||||
.l_paren,
|
.l_paren,
|
||||||
.r_paren,
|
.r_paren,
|
||||||
|
|||||||
@@ -3181,9 +3181,10 @@ pub const Parser = struct {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const expr = try self.parseExpr();
|
const expr = try self.parseExpr();
|
||||||
var it = ast.ForIterable{ .expr = expr };
|
var it = ast.ForIterable{ .expr = expr };
|
||||||
if (self.current.tag == .dot_dot or self.current.tag == .dot_dot_eq) {
|
if (rangeTokenInfo(self.current.tag)) |rt| {
|
||||||
it.is_range = true;
|
it.is_range = true;
|
||||||
it.inclusive = self.current.tag == .dot_dot_eq;
|
it.start_exclusive = rt.start_exclusive;
|
||||||
|
it.end_inclusive = rt.end_inclusive;
|
||||||
self.advance();
|
self.advance();
|
||||||
// End expression — absent for the open range `a..`, i.e. when
|
// End expression — absent for the open range `a..`, i.e. when
|
||||||
// the header continues (`,`), the body starts (`{` / `=>`),
|
// the header continues (`,`), the body starts (`{` / `=>`),
|
||||||
@@ -3194,7 +3195,7 @@ pub const Parser = struct {
|
|||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
if (open) {
|
if (open) {
|
||||||
if (it.inclusive) return self.fail("'..=' requires an end expression — the open form is 'a..'");
|
if (rt.end_marked) return self.fail("a range with an explicit end marker ('..=' / '..<') requires an end expression — the open form is 'a..'");
|
||||||
} else {
|
} else {
|
||||||
it.range_end = try self.parseExpr();
|
it.range_end = try self.parseExpr();
|
||||||
}
|
}
|
||||||
@@ -3831,6 +3832,32 @@ pub const Parser = struct {
|
|||||||
return after == .l_brace or after == .fat_arrow;
|
return after == .l_brace or after == .fat_arrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RangeTokenInfo = struct {
|
||||||
|
start_exclusive: bool,
|
||||||
|
end_inclusive: bool,
|
||||||
|
/// True when the lexeme carries an explicit end marker (`=` / `<`
|
||||||
|
/// after the dots) — the end expression is then mandatory.
|
||||||
|
end_marked: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Range lexemes: each side of `..` takes an optional bound marker, `=`
|
||||||
|
/// inclusive / `<` exclusive, defaulting to start-inclusive,
|
||||||
|
/// end-exclusive (`a..b` ≡ `a=..<b`).
|
||||||
|
fn rangeTokenInfo(tag: Tag) ?RangeTokenInfo {
|
||||||
|
return switch (tag) {
|
||||||
|
.dot_dot => .{ .start_exclusive = false, .end_inclusive = false, .end_marked = false },
|
||||||
|
.dot_dot_eq => .{ .start_exclusive = false, .end_inclusive = true, .end_marked = true },
|
||||||
|
.dot_dot_lt => .{ .start_exclusive = false, .end_inclusive = false, .end_marked = true },
|
||||||
|
.lt_dot_dot => .{ .start_exclusive = true, .end_inclusive = false, .end_marked = false },
|
||||||
|
.lt_dot_dot_eq => .{ .start_exclusive = true, .end_inclusive = true, .end_marked = true },
|
||||||
|
.lt_dot_dot_lt => .{ .start_exclusive = true, .end_inclusive = false, .end_marked = true },
|
||||||
|
.eq_dot_dot => .{ .start_exclusive = false, .end_inclusive = false, .end_marked = false },
|
||||||
|
.eq_dot_dot_eq => .{ .start_exclusive = false, .end_inclusive = true, .end_marked = true },
|
||||||
|
.eq_dot_dot_lt => .{ .start_exclusive = false, .end_inclusive = false, .end_marked = true },
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn advance(self: *Parser) void {
|
fn advance(self: *Parser) void {
|
||||||
self.prev_end = self.current.loc.end;
|
self.prev_end = self.current.loc.end;
|
||||||
self.current = self.lexer.next();
|
self.current = self.lexer.next();
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ pub const Tag = enum {
|
|||||||
dot, // .
|
dot, // .
|
||||||
dot_dot, // ..
|
dot_dot, // ..
|
||||||
dot_dot_eq, // ..=
|
dot_dot_eq, // ..=
|
||||||
|
dot_dot_lt, // ..<
|
||||||
|
lt_dot_dot, // <..
|
||||||
|
lt_dot_dot_eq, // <..=
|
||||||
|
lt_dot_dot_lt, // <..<
|
||||||
|
eq_dot_dot, // =..
|
||||||
|
eq_dot_dot_eq, // =..=
|
||||||
|
eq_dot_dot_lt, // =..<
|
||||||
dollar, // $
|
dollar, // $
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
@@ -152,6 +159,13 @@ pub const Tag = enum {
|
|||||||
.dot => ".",
|
.dot => ".",
|
||||||
.dot_dot => "..",
|
.dot_dot => "..",
|
||||||
.dot_dot_eq => "..=",
|
.dot_dot_eq => "..=",
|
||||||
|
.dot_dot_lt => "..<",
|
||||||
|
.lt_dot_dot => "<..",
|
||||||
|
.lt_dot_dot_eq => "<..=",
|
||||||
|
.lt_dot_dot_lt => "<..<",
|
||||||
|
.eq_dot_dot => "=..",
|
||||||
|
.eq_dot_dot_eq => "=..=",
|
||||||
|
.eq_dot_dot_lt => "=..<",
|
||||||
.dollar => "$",
|
.dollar => "$",
|
||||||
.plus => "+",
|
.plus => "+",
|
||||||
.minus => "-",
|
.minus => "-",
|
||||||
|
|||||||
Reference in New Issue
Block a user