lang: multi-iterable for loops — drop ':', add '..=', open ranges, arrow bodies
The for header is now a comma-separated list of iterables with a
positional capture group and no ':' separator:
for xs (x) { } // collection
for 0..n (i) { } // range (end exclusive)
for 1..=5 (a) { } // ..= inclusive end
for xs, 0.. (x, i) { } // index idiom (replaces (x, i))
for xs, ys (x, y) { } // parallel (zip) iteration
for xs (x) => sum += x; // arrow body (full statement)
First-iterable-wins: the first iterable's length drives the loop and
must be bounded; the other positions follow by their own cursors (a
non-first range's end is not consulted or evaluated; a shorter
non-first collection is read past its length on mismatch). The old
single-iterable index capture is replaced by the trailing open range.
Capture/call disambiguation is positional: the paren group immediately
before '{' or '=>' is the capture, every earlier top-level group is a
call. 'for zip(a, b) (x, y)' calls zip; 'for f(n) { }' reads (n) as
the capture and errors with a parenthesize/add-capture hint. The old
':' form errors with a migration hint.
Lowering is unified across forms: one cursor slot per position (ranges
start at their start, collections at 0), all advanced together, the
first position's bound terminating. inline for keeps the single
bounded comptime range.
Migrated the full corpus (examples, library modules, issue repros,
in-source test strings). New coverage: examples/0050 (the full feature
surface) and examples/1149-1155 (seven diagnostic faces). specs.md For
Loop section + grammar rewritten; readme teaser updated.
This commit is contained in:
54
src/ast.zig
54
src/ast.zig
@@ -642,29 +642,41 @@ pub const WhileExpr = struct {
|
||||
binding_is_raw: bool = false,
|
||||
};
|
||||
|
||||
pub const ForExpr = struct {
|
||||
iterable: *Node,
|
||||
body: *Node,
|
||||
capture_name: []const u8,
|
||||
capture_span: ?Span = null, // span of `capture_name` (null when omitted, e.g. `for 0..N { }`)
|
||||
/// True when `capture_name` was a backtick raw identifier
|
||||
/// (`` for xs: (`s2) ``) — exempt from the reserved-type-name check.
|
||||
capture_is_raw: bool = false,
|
||||
index_name: ?[]const u8 = null,
|
||||
index_span: ?Span = null, // span of `index_name` (set iff `index_name` is)
|
||||
/// True when `index_name` was a backtick raw identifier
|
||||
/// (`` for xs: (x, `s2) ``) — exempt from the reserved-type-name check.
|
||||
index_is_raw: bool = false,
|
||||
/// Range form `for start..end (i) { }`: `iterable` is the start, `range_end`
|
||||
/// the (exclusive) end. Null for the iterate-a-collection form
|
||||
/// (`for coll : (x) { }`). For the range form `capture_name` is the cursor
|
||||
/// (empty when omitted, `for 0..N { }`).
|
||||
/// One position of a (possibly multi-iterable) `for` header.
|
||||
pub const ForIterable = struct {
|
||||
/// Collection expression, or the range START for the range forms.
|
||||
expr: *Node,
|
||||
/// `a..b` / `a..=b` end. Null for a plain collection AND for the
|
||||
/// open-ended range `a..` (distinguished by `is_range`).
|
||||
range_end: ?*Node = null,
|
||||
/// `inline for` — comptime-unrolled (range bounds must be comptime).
|
||||
/// True for any range form (`a..`, `a..b`, `a..=b`).
|
||||
is_range: bool = false,
|
||||
/// `a..=b` — end is inclusive.
|
||||
inclusive: bool = false,
|
||||
};
|
||||
|
||||
/// One capture of a `for` header: `(x)`, `(*x)`, `(x, y, ...)`.
|
||||
pub const ForCapture = struct {
|
||||
name: []const u8,
|
||||
span: ?Span = null,
|
||||
/// True when the name was a backtick raw identifier (`` for xs (`s2) ``)
|
||||
/// — exempt from the reserved-type-name check.
|
||||
is_raw: bool = false,
|
||||
/// `(*x)` — bind a pointer into the collection (no per-element copy).
|
||||
by_ref: bool = false,
|
||||
};
|
||||
|
||||
/// `for it1, it2, ... (c1, c2, ...) { }` — parallel iteration. The FIRST
|
||||
/// iterable's length drives the loop (first-iterable-wins); the others are
|
||||
/// indexed along it, and a non-first range's end is not consulted. The
|
||||
/// capture group is positional: empty (no bindings) or one capture per
|
||||
/// iterable. The body is a block or an `=> expr;` arrow body.
|
||||
pub const ForExpr = struct {
|
||||
iterables: []ForIterable,
|
||||
captures: []ForCapture,
|
||||
body: *Node,
|
||||
/// `inline for` — comptime-unrolled (single bounded range, comptime bounds).
|
||||
is_inline: bool = false,
|
||||
/// `for xs: (*x)` — bind `x` to a pointer into the collection (no per-element
|
||||
/// copy) rather than a value copy of each element.
|
||||
capture_by_ref: bool = false,
|
||||
};
|
||||
|
||||
pub const SpreadExpr = struct {
|
||||
|
||||
Reference in New Issue
Block a user