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.
58 lines
2.0 KiB
Plaintext
58 lines
2.0 KiB
Plaintext
// Backtick raw identifier across every control-flow / capture / binding form,
|
|
// plus bare later uses. A reserved type-name spelling (`s2`, `u8`, …) works as a
|
|
// binding name in a destructure, an `if`/`while` optional binding, a `for`
|
|
// capture + index, and a match-arm capture; a backtick-named function is
|
|
// bare-callable; and a backtick struct field is bare- or backtick-accessible.
|
|
// The escape is needed only at the binding site — a later BARE reference / call
|
|
// / member access resolves to the binding. A *bare* binding name is still the
|
|
// reserved type (see examples/1121), so the escape is the only way to spell
|
|
// these as values.
|
|
// Regression (issue 0089 — attempt-2 completeness across binding forms).
|
|
#import "modules/std.sx";
|
|
|
|
pair :: () -> (s64, s64) { (1, 2) }
|
|
maybe :: () -> ?s64 { return 42; }
|
|
|
|
// Function named with a reserved spelling — bare-callable (no backtick at call).
|
|
`s2 :: (n: s64) -> s64 { return n + 1; }
|
|
|
|
Quad :: struct { `s1: s32; `s2: s32; }
|
|
|
|
main :: () -> s32 {
|
|
// destructure binding names
|
|
`u8, rest := pair();
|
|
print("dstr = {} {}\n", `u8, rest);
|
|
|
|
// if optional binding + bare-position reference inside the branch
|
|
if `s16 := maybe() {
|
|
print("if = {}\n", `s16);
|
|
}
|
|
|
|
// while optional binding (name only — the while binding isn't body-exposed)
|
|
while `s32 := maybe() {
|
|
break;
|
|
}
|
|
|
|
// for capture + index names
|
|
xs := [3]s64.{ 10, 20, 30 };
|
|
for xs, 0.. (`bool, `u16) {
|
|
print("for = {} @ {}\n", `bool, `u16);
|
|
}
|
|
|
|
// match-arm capture
|
|
opt: ?s64 = 5;
|
|
m := if opt == {
|
|
case .some: (`string) { `string * 2 }
|
|
case .none: { 0 }
|
|
};
|
|
print("match = {}\n", m);
|
|
|
|
// backtick function called BARE and via backtick — both resolve to the fn
|
|
print("call = {} {}\n", s2(10), `s2(10));
|
|
|
|
// struct field named with a reserved spelling: bare + backtick member access
|
|
q := Quad.{ `s1 = 7, `s2 = 9 };
|
|
print("field = {} {} | {} {}\n", q.s1, q.s2, q.`s1, q.`s2);
|
|
return 0;
|
|
}
|