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:
agra
2026-06-10 20:30:55 +03:00
parent c640e88513
commit 116af2359e
75 changed files with 701 additions and 391 deletions

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,9 @@
1:0 2:1 3:2 4:3 5:4
[0]=10 [1]=20 [2]=30
dot=300
arrow-range s=6
arrow-coll t=60
v7 v8 v9
escape n=3
1/10/100 2/20/101 3/30/102 4/40/103
after ref: 1 3 5 7

View File

@@ -1,5 +1,5 @@
error: pack 'xs' has no runtime value — a pack is comptime-only and can't be used as a value here
--> /Users/agra/projects/sx/examples/0536-packs-pack-as-value.sx:14:40
--> examples/0536-packs-pack-as-value.sx:14:40
|
14 | storage :: (..xs: Show) -> void { y := xs; _ = y; } // A: store
| ^^
@@ -10,7 +10,7 @@ help: to store it, materialize a tuple: `(..xs)`
| ^^
error: pack 'xs' has no runtime value — a pack is comptime-only and can't be used as a value here
--> /Users/agra/projects/sx/examples/0536-packs-pack-as-value.sx:15:40
--> examples/0536-packs-pack-as-value.sx:15:40
|
15 | call :: (..xs: Show) -> void { sink(xs); } // B: pass to a call
| ^^
@@ -21,7 +21,7 @@ help: materialize a tuple `(..xs)` to store it, or `xx xs` to convert it to an e
| ^^
error: pack 'xs' has no runtime value — a pack is comptime-only and can't be used as a value here
--> /Users/agra/projects/sx/examples/0536-packs-pack-as-value.sx:16:42
--> examples/0536-packs-pack-as-value.sx:16:42
|
16 | ret :: (..xs: Show) -> s64 { return xs; } // C: return
| ^^
@@ -32,12 +32,12 @@ help: to return it, return a tuple `(..xs)` and make the return type that tuple
| ^^
error: pack 'xs' has no runtime value — a pack is comptime-only and can't be used as a value here
--> /Users/agra/projects/sx/examples/0536-packs-pack-as-value.sx:17:39
--> examples/0536-packs-pack-as-value.sx:17:39
|
17 | iter :: (..xs: Show) -> void { for xs : (x) { _ = x; } } // D: runtime iterate
17 | iter :: (..xs: Show) -> void { for xs (x) { _ = x; } } // D: runtime iterate
| ^^
help: to iterate at comptime use `inline for 0..xs.len (i)`; for a runtime loop declare it as `..xs: []P` (a protocol slice) instead of a pack
|
17 | iter :: (..xs: Show) -> void { for xs : (x) { _ = x; } } // D: runtime iterate
17 | iter :: (..xs: Show) -> void { for xs (x) { _ = x; } } // D: runtime iterate
| ^^

View File

@@ -17,16 +17,16 @@ error: 's16' is a reserved type name and cannot be used as an identifier
| ^^^
error: 'bool' is a reserved type name and cannot be used as an identifier
--> examples/1121-diagnostics-reserved-name-control-flow.sx:22:14
--> examples/1121-diagnostics-reserved-name-control-flow.sx:22:13
|
22 | for xs: (bool) { } // for capture name
| ^^^^
22 | for xs (bool) { } // for capture name
| ^^^^
error: 's32' is a reserved type name and cannot be used as an identifier
--> examples/1121-diagnostics-reserved-name-control-flow.sx:23:17
--> examples/1121-diagnostics-reserved-name-control-flow.sx:23:21
|
23 | for xs: (v, s32) { } // for index name
| ^^^
23 | for xs, 0.. (v, s32) { } // for index name
| ^^^
error: 'string' is a reserved type name and cannot be used as an identifier
--> examples/1121-diagnostics-reserved-name-control-flow.sx:26:22

View File

@@ -1,5 +1,5 @@
error: inline for: range end is not a compile-time integer
--> examples/1138-diagnostics-inline-for-non-integral-bound.sx:12:19
|
12 | inline for 0..4.5: (i) { s += i; }
12 | inline for 0..4.5 (i) { s += i; }
| ^^^

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: for-loop syntax: the ':' before the capture was removed — write `for xs (x) { }` (index via `for xs, 0.. (x, i)`)
--> examples/1149-diagnostics-for-colon-removed.sx:8:11
|
8 | for xs: (x) { }
| ^

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: for capture count must match the iterable count — one capture per iterable
--> examples/1150-diagnostics-for-capture-arity.sx:8:20
|
8 | for xs, ys (x) { }
| ^

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: the first iterable must have a bounded length (it drives the loop) — an open range 'a..' may only follow it
--> examples/1151-diagnostics-for-open-first.sx:7:17
|
7 | for 0.. (i) { }
| ^

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: '..=' requires an end expression — the open form is 'a..'
--> examples/1152-diagnostics-for-inclusive-open.sx:6:14
|
6 | for 0..= (i) { }
| ^

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: a range element cannot be captured by reference
--> examples/1153-diagnostics-for-range-by-ref.sx:7:19
|
7 | for 0..3 (*i) { }
| ^

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: expected capture variable name (a call iterable also needs a capture: `for f(n) (x) { }`)
--> examples/1154-diagnostics-for-call-needs-capture.sx:10:11
|
10 | for g() { }
| ^

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: cannot iterate this expression — if the parens were call arguments, a call iterable also needs a capture (`for f(n) (x) { }`) or parentheses (`for (f(n)) { }`)
--> examples/1155-diagnostics-for-not-iterable.sx:11:9
|
11 | for f(n) { }
| ^

View File

@@ -0,0 +1 @@