lang F1: range-based for + inline-for unroll over packs
Add range loop syntax:
- runtime for start..end (i) { } counting loop, cursor optional, end exclusive
- comptime inline for start..end (i) { } comptime-unrolled body
The inline form binds the cursor as an int_val comptime constant per
iteration, so xs[i] over a heterogeneous pack substitutes the concrete
per-position element -- the canonical's pack-iteration vehicle
(inline for 0..sources.len (i) { sources[i].addListener(...) }).
- AST: ForExpr.range_end, ForExpr.is_inline
- parser: parseForExpr range vs collection form; suppress_call flag so
N (i) is not read as a call N(i) while parsing a range bound
- lower: lowerRuntimeRangeFor / lowerInlineRangeFor; evalComptimeInt;
comptimeIndexOf extends pack-index resolution beyond int literals
Revises spec's inline for i in 0..N to the no-in, range-first, paren-cursor
form. Regression: examples/200-for-range.sx.
This commit is contained in:
22
specs.md
22
specs.md
@@ -1006,8 +1006,7 @@ may do, regardless of the concrete arg types at any particular call site.
|
||||
|---|---|---|
|
||||
| Length | `xs.len` | comptime int (field-style, not `len(xs)`) |
|
||||
| Index | `xs[i]` | i-th element; `i` must be comptime |
|
||||
| Comptime unroll (index) | `inline for i in 0..xs.len { ... }` | unrolled loop; not `#for` |
|
||||
| Comptime unroll (element) | `inline for x in xs { ... }` | desugars to index form; `x`'s type varies per iteration |
|
||||
| Comptime unroll (index) | `inline for 0..xs.len (i) { ... }` | unrolled loop; cursor `i` is a comptime constant per iteration; not `#for` |
|
||||
| Projection | `xs.field` | see "Pack projection" |
|
||||
| Spread → call args | `..xs` / `..xs.field` | expands to N positional args |
|
||||
| Spread → tuple value | `(..xs)` / `(..xs.field)` | materializes a tuple |
|
||||
@@ -1057,7 +1056,7 @@ value-requiring position is a compile error with a tailored suggestion:
|
||||
- storing/binding it (`let x = xs;`, `self.f = xs;`) → suggest `(..xs)`;
|
||||
- passing to a non-pack-taking call (`f(xs)`) → suggest `..xs`;
|
||||
- returning it (`return xs;`) → suggest a tuple return with `(..xs)`;
|
||||
- iterating at runtime (`for x in xs`, `xs[runtime_i]`) → suggest `inline for`.
|
||||
- iterating at runtime (`for xs : (x)`, `xs[runtime_i]`) → suggest `inline for`.
|
||||
|
||||
#### Storage and protocol conformance
|
||||
|
||||
@@ -1089,7 +1088,7 @@ map :: (mapper: Closure(..sources.T) -> $R, ..sources: ValueListenable)
|
||||
c.own_allocator = context.allocator;
|
||||
c.mapper = mapper;
|
||||
c.sources = (..sources); // pack-to-tuple materialization
|
||||
inline for i in 0..sources.len { // comptime unroll over the pack
|
||||
inline for 0..sources.len (i) { // comptime unroll over the pack
|
||||
sources[i].addListener((_) => c.recompute());
|
||||
}
|
||||
c.value = mapper(..sources.value); // pack spread + projection in a call
|
||||
@@ -1510,6 +1509,21 @@ while i < 10 {
|
||||
```
|
||||
|
||||
### For Loop
|
||||
|
||||
#### Range form
|
||||
```sx
|
||||
for start..end (i) { } // counting loop, cursor `i` (s64), `end` exclusive
|
||||
for start..end { } // no cursor — body runs `end - start` times
|
||||
inline for start..end (i) { } // comptime-unrolled; `i` is a comptime constant per iteration
|
||||
```
|
||||
`start` and `end` are `s64` expressions; the loop counts `start, start+1, …, end-1`.
|
||||
The cursor parens are optional — omit them when the body doesn't need the index.
|
||||
The `inline` variant requires comptime-known bounds and unrolls the body once per
|
||||
value, binding the cursor as a compile-time constant (so it can index a pack:
|
||||
`inline for 0..xs.len (i) { xs[i].m() }`). `break;` / `continue;` work in the
|
||||
runtime form.
|
||||
|
||||
#### Collection form
|
||||
```sx
|
||||
for iterable: (elem) { } // element alias (no copy)
|
||||
for iterable: (elem, ix) { } // element + index
|
||||
|
||||
Reference in New Issue
Block a user