Compare commits
43 Commits
74607c4dbc
...
59f0aa7716
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59f0aa7716 | ||
|
|
ee00db849c | ||
|
|
2025bb361b | ||
|
|
83ec2536af | ||
|
|
12149eb548 | ||
|
|
67313e1dad | ||
|
|
fea5617e4e | ||
|
|
f513c11ea6 | ||
|
|
fd14ab5694 | ||
|
|
116af2359e | ||
|
|
c640e88513 | ||
|
|
3cc34d54c1 | ||
|
|
bf47146085 | ||
|
|
878c4226a6 | ||
|
|
e81780e32e | ||
|
|
2b8041a828 | ||
|
|
8b2a6598a9 | ||
|
|
5b304a29c1 | ||
|
|
82500931ce | ||
|
|
a7822d73cc | ||
|
|
89b3789973 | ||
|
|
35e35adb0a | ||
|
|
0592c9dc97 | ||
|
|
1790d808cc | ||
|
|
ef4059bdde | ||
|
|
6b07346d98 | ||
|
|
54db29e60a | ||
|
|
c38dd67b83 | ||
|
|
b163c4a3fc | ||
|
|
638a048cc5 | ||
|
|
1af80a4c38 | ||
|
|
e884e87f80 | ||
|
|
8990bd4978 | ||
|
|
c10aaa1482 | ||
|
|
5928d9f067 | ||
|
|
13f5fc57c1 | ||
|
|
1efafdbdf7 | ||
|
|
8adfc1dd50 | ||
|
|
3dbd3ce072 | ||
|
|
de6e826d21 | ||
|
|
856299ce36 | ||
|
|
91d01d0048 | ||
|
|
b240810b6f |
87
.github/workflows/build.yml
vendored
87
.github/workflows/build.yml
vendored
@@ -1,87 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
tags: ['v*']
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Zig
|
||||
uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: master
|
||||
|
||||
- name: Install LLVM 18
|
||||
run: sudo apt-get update && sudo apt-get install -y llvm-18-dev gcc
|
||||
|
||||
- name: Build
|
||||
run: zig build -Dllvm-prefix=/usr/lib/llvm-18
|
||||
|
||||
- name: Test
|
||||
run: zig build test -Dllvm-prefix=/usr/lib/llvm-18 --summary all
|
||||
|
||||
- name: Package
|
||||
run: tar czf sx-linux-x86_64.tar.gz -C zig-out/bin .
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sx-linux-x86_64
|
||||
path: zig-out/bin/
|
||||
|
||||
- name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: sx-linux-x86_64.tar.gz
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
LLVM_PREFIX: C:\LLVM\llvm-18.1.8-windows-amd64-msvc17-msvcrt
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Zig
|
||||
uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: master
|
||||
|
||||
- name: Download LLVM 18
|
||||
run: |
|
||||
Invoke-WebRequest -Uri "https://github.com/vovkos/llvm-package-windows/releases/download/llvm-18.1.8/llvm-18.1.8-windows-amd64-msvc17-msvcrt.7z" -OutFile "$env:TEMP\llvm.7z"
|
||||
7z x "$env:TEMP\llvm.7z" -oC:\LLVM
|
||||
|
||||
- name: Setup MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: zig build -Dstatic-llvm -Dllvm-prefix=%LLVM_PREFIX% -Dtarget=x86_64-windows-msvc
|
||||
|
||||
- name: Test
|
||||
shell: cmd
|
||||
continue-on-error: true
|
||||
run: zig build test -Dstatic-llvm -Dllvm-prefix=%LLVM_PREFIX% -Dtarget=x86_64-windows-msvc --summary all
|
||||
|
||||
- name: Package
|
||||
run: Compress-Archive -Path zig-out\bin\* -DestinationPath sx-windows-x86_64.zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sx-windows-x86_64
|
||||
path: zig-out/bin/
|
||||
|
||||
- name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: sx-windows-x86_64.zip
|
||||
@@ -1,5 +1,5 @@
|
||||
// Range-based for loops: `for start..end: (i) { }` (cursor optional, `end`
|
||||
// exclusive) is a runtime counting loop; `inline for start..end: (i) { }`
|
||||
// Range-based for loops: `for start..end (i) { }` (cursor optional, `end`
|
||||
// exclusive) is a runtime counting loop; `inline for start..end (i) { }`
|
||||
// is comptime-unrolled — the cursor is a compile-time constant each
|
||||
// iteration, so `xs[i]` over a heterogeneous pack substitutes the concrete
|
||||
// per-position element (this is what drives pack iteration).
|
||||
@@ -16,14 +16,14 @@ impl Show for B { show :: (self: *B) -> string => "B"; }
|
||||
|
||||
// Comptime-unrolled iteration over a pack; cursor `i` indexes the pack.
|
||||
each :: (..xs: Show) -> void {
|
||||
inline for 0..xs.len: (i) {
|
||||
inline for 0..xs.len (i) {
|
||||
print("[{}]={}\n", i, xs[i].show());
|
||||
}
|
||||
}
|
||||
|
||||
main :: () -> s32 {
|
||||
// Runtime range, cursor used.
|
||||
for 0..3: (i) { print("i={}\n", i); }
|
||||
for 0..3 (i) { print("i={}\n", i); }
|
||||
|
||||
// Runtime range, no cursor — body runs `end - start` times.
|
||||
n := 0;
|
||||
@@ -31,7 +31,7 @@ main :: () -> s32 {
|
||||
print("n={}\n", n);
|
||||
|
||||
// Non-zero start.
|
||||
for 2..5: (j) { print("j={}\n", j); }
|
||||
for 2..5 (j) { print("j={}\n", j); }
|
||||
|
||||
// Inline unroll over a heterogeneous pack.
|
||||
each(A.{ x = 1 }, B.{ s = "hi" }, A.{ x = 3 });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// `for xs: (*x)` binds each element by pointer — no per-element copy.
|
||||
// `for xs (*x)` binds each element by pointer — no per-element copy.
|
||||
// Mutations write back, and a pointer subject matches through the deref.
|
||||
#import "modules/std.sx";
|
||||
|
||||
@@ -10,12 +10,12 @@ Shape :: enum {
|
||||
main :: () -> s32 {
|
||||
// By-ref mutation writes back into the array (impossible with a value copy).
|
||||
xs : [3]s64 = .[1, 2, 3];
|
||||
for xs: (*x) { x.* = x + 100; }
|
||||
for xs (*x) { x.* = x + 100; }
|
||||
print("{} {} {}\n", xs[0], xs[1], xs[2]);
|
||||
|
||||
// Pointer subject matches through the deref; payload reads through the ref.
|
||||
shapes : [2]Shape = .[.circle(2.0), .none];
|
||||
for shapes: (*s) {
|
||||
for shapes (*s) {
|
||||
if s == {
|
||||
case .circle: (r) { print("circle {}\n", r); }
|
||||
case .none: { print("none\n"); }
|
||||
|
||||
@@ -10,7 +10,7 @@ Box :: struct {
|
||||
|
||||
sum_ptr :: (xs: *List(s64)) -> s64 {
|
||||
total : s64 = 0;
|
||||
for xs: (n) { total = total + n; } // iterate through a *List
|
||||
for xs (n) { total = total + n; } // iterate through a *List
|
||||
total
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ main :: () -> s32 {
|
||||
xs.append(30);
|
||||
|
||||
s : s64 = 0;
|
||||
for xs: (n) { s = s + n; } // value capture
|
||||
for xs (n) { s = s + n; } // value capture
|
||||
print("sum {}\n", s); // 60
|
||||
|
||||
for xs: (*n) { n.* = n + 100; } // by-ref: writes back
|
||||
for xs (*n) { n.* = n + 100; } // by-ref: writes back
|
||||
s = 0;
|
||||
for xs: (n) { s = s + n; }
|
||||
for xs (n) { s = s + n; }
|
||||
print("sum2 {}\n", s); // 360
|
||||
|
||||
print("via ptr {}\n", sum_ptr(@xs)); // 360
|
||||
@@ -34,7 +34,7 @@ main :: () -> s32 {
|
||||
bs := List(Box).{};
|
||||
bs.append(.{ v = 7 });
|
||||
bt : s64 = 0;
|
||||
for bs: (*b) { bt = bt + b.boxed(); } // *Box receiver, value-self method
|
||||
for bs (*b) { bt = bt + b.boxed(); } // *Box receiver, value-self method
|
||||
print("boxes {}\n", bt); // 7
|
||||
0
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Color :: enum { red; green; blue; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
add :: (a: s32, b: s32) -> s32 { a + b }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
@@ -118,7 +118,7 @@ main :: () {
|
||||
// For loop basic
|
||||
farr : [4]s32 = .[10, 20, 30, 40];
|
||||
out("for:");
|
||||
for farr: (it) {
|
||||
for farr (it) {
|
||||
out(" ");
|
||||
out(int_to_string(it));
|
||||
}
|
||||
@@ -126,14 +126,14 @@ main :: () {
|
||||
|
||||
// For with print
|
||||
out("for-print:");
|
||||
for farr: (it) {
|
||||
for farr (it) {
|
||||
print(" {}", it);
|
||||
}
|
||||
out("\n");
|
||||
|
||||
// For with index
|
||||
out("for-idx:");
|
||||
for farr: (_, ix) {
|
||||
for farr, 0.. (_, ix) {
|
||||
out(" ");
|
||||
out(int_to_string(ix));
|
||||
}
|
||||
@@ -141,14 +141,14 @@ main :: () {
|
||||
|
||||
// For with print two args
|
||||
out("for-2arg:");
|
||||
for farr: (it, ix) {
|
||||
for farr, 0.. (it, ix) {
|
||||
print(" {}@{}", it, ix);
|
||||
}
|
||||
out("\n");
|
||||
|
||||
// For with break
|
||||
out("for-break:");
|
||||
for farr: (it) {
|
||||
for farr (it) {
|
||||
if it == 30 { break; }
|
||||
print(" {}", it);
|
||||
}
|
||||
@@ -156,7 +156,7 @@ main :: () {
|
||||
|
||||
// For with continue
|
||||
out("for-continue:");
|
||||
for farr: (it) {
|
||||
for farr (it) {
|
||||
if it == 20 { continue; }
|
||||
print(" {}", it);
|
||||
}
|
||||
@@ -165,14 +165,14 @@ main :: () {
|
||||
// For on slice
|
||||
fsl : []s32 = .[10, 20, 30];
|
||||
out("for-slice:");
|
||||
for fsl: (it) {
|
||||
for fsl (it) {
|
||||
print(" {}", it);
|
||||
}
|
||||
out("\n");
|
||||
|
||||
// For on slice with index
|
||||
out("for-slice-idx:");
|
||||
for fsl: (it, ix) {
|
||||
for fsl, 0.. (it, ix) {
|
||||
print(" {}:{}", ix, it);
|
||||
}
|
||||
out("\n");
|
||||
@@ -181,8 +181,8 @@ main :: () {
|
||||
nf_a : [2]s32 = .[0, 1];
|
||||
nf_b : [2]s32 = .[0, 1];
|
||||
out("for-nested:");
|
||||
for nf_a: (oa) {
|
||||
for nf_b: (ob) {
|
||||
for nf_a (oa) {
|
||||
for nf_b (ob) {
|
||||
print(" ({},{})", oa, ob);
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ main :: () {
|
||||
// For with break preserving index
|
||||
fbi : [5]s32 = .[10, 20, 30, 40, 50];
|
||||
fbi_idx := 0;
|
||||
for fbi: (it, ix) {
|
||||
for fbi, 0.. (it, ix) {
|
||||
if it == 30 { fbi_idx = ix; break; }
|
||||
}
|
||||
print("for-break-idx: {}\n", fbi_idx);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
add :: (a: s32, b: s32) -> s32 { a + b }
|
||||
@@ -16,7 +16,7 @@ pair_add :: (a: $T, b: $U) -> s64 {
|
||||
|
||||
typed_sum :: (..args: []s32) -> s32 {
|
||||
result := 0;
|
||||
for args: (it) { result = result + it; }
|
||||
for args (it) { result = result + it; }
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
add :: (a: s32, b: s32) -> s32 { a + b }
|
||||
|
||||
@@ -24,7 +24,7 @@ clamp :: (x: s64) -> s64 { if x > 10 { return 10; } return x; }
|
||||
|
||||
main :: () -> s32 {
|
||||
print("const_one={}\n", const_one()); // 1
|
||||
print("raised={}\n", always_raise(5) catch e 0); // 0
|
||||
print("raised={}\n", always_raise(5) catch (e) 0); // 0
|
||||
print("clamp_hi={}\n", clamp(42)); // 10
|
||||
print("clamp_lo={}\n", clamp(7)); // 7
|
||||
|
||||
|
||||
26
examples/0047-basic-loop-local-stack-reuse.sx
Normal file
26
examples/0047-basic-loop-local-stack-reuse.sx
Normal file
@@ -0,0 +1,26 @@
|
||||
// Loop-body locals reuse one stack slot per frame: a body-declared local
|
||||
// (and every compiler temp) must not grow the stack per iteration, so
|
||||
// million-iteration loops run in constant stack. Covers body locals,
|
||||
// nested loops (the inner loop's hidden index slot), and element reads.
|
||||
// Regression (issue 0109): allocas were emitted at their use site, so each
|
||||
// iteration re-executed them — LLVM only reclaims allocas at `ret`, and
|
||||
// these loops segfaulted on stack exhaustion.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
sum := 0;
|
||||
for 0..1000000 (i) {
|
||||
buf : [128]s64 = ---;
|
||||
buf[0] = i;
|
||||
sum += buf[0];
|
||||
}
|
||||
print("sum={}\n", sum);
|
||||
|
||||
n := 0;
|
||||
for 0..3000000 (i) {
|
||||
for 0..1 (j) { n += 1; }
|
||||
}
|
||||
print("n={}\n", n);
|
||||
0
|
||||
}
|
||||
24
examples/0048-basic-for-array-large.sx
Normal file
24
examples/0048-basic-for-array-large.sx
Normal file
@@ -0,0 +1,24 @@
|
||||
// Collection-form `for` over an array, by-value capture: each iteration
|
||||
// reads ONE element from the array's storage (GEP + load), and the capture
|
||||
// stays a copy — mutating it never writes back to the array.
|
||||
// Regression (issue 0110): the element fetch was `index_get` on the array
|
||||
// VALUE, spilling a full copy of the array to a stack temp per iteration —
|
||||
// O(N²) bytes copied, and (pre-0109) per-iteration stack growth that made
|
||||
// this 4096-element loop segfault.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
arr : [4096]s64 = ---;
|
||||
i := 0;
|
||||
while i < 4096 { arr[i] = i; i += 1; }
|
||||
sum := 0;
|
||||
for arr (x) { sum += x; }
|
||||
print("sum={}\n", sum);
|
||||
|
||||
// By-value capture is a copy: mutating it leaves the array untouched.
|
||||
small : [3]s64 = .[10, 20, 30];
|
||||
for small (x) { x += 100; }
|
||||
print("copy-guard: {} {} {}\n", small[0], small[1], small[2]);
|
||||
0
|
||||
}
|
||||
46
examples/0049-basic-defer-break-continue.sx
Normal file
46
examples/0049-basic-defer-break-continue.sx
Normal file
@@ -0,0 +1,46 @@
|
||||
// `defer` runs on EVERY exit from the loop body's scope — fall-through,
|
||||
// `break`, and `continue` alike (LIFO, including entries from nested blocks
|
||||
// between the loop and the jump). Covers `for` ranges and `while`.
|
||||
// Regression (issue 0108): break/continue emitted a bare branch and the
|
||||
// breaking iteration's defers were silently skipped.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
for 0..3 (i) {
|
||||
defer print("cleanup {}\n", i);
|
||||
if i == 1 { break; }
|
||||
print("body {}\n", i);
|
||||
}
|
||||
print("after break loop\n");
|
||||
|
||||
for 0..3 (i) {
|
||||
defer print("c2 {}\n", i);
|
||||
if i == 1 { continue; }
|
||||
print("b2 {}\n", i);
|
||||
}
|
||||
print("done\n");
|
||||
|
||||
i := 0;
|
||||
while i < 3 {
|
||||
defer print("w{}\n", i);
|
||||
i += 1;
|
||||
if i == 2 { continue; }
|
||||
if i == 3 { break; }
|
||||
print("wbody{}\n", i);
|
||||
}
|
||||
print("while done\n");
|
||||
|
||||
// A break inside a nested block drains the nested block's defers AND the
|
||||
// loop body's, in LIFO order.
|
||||
for 0..2 (j) {
|
||||
defer print("outer {}\n", j);
|
||||
{
|
||||
defer print("inner {}\n", j);
|
||||
if j == 0 { break; }
|
||||
}
|
||||
print("unreached\n");
|
||||
}
|
||||
print("nested done\n");
|
||||
0
|
||||
}
|
||||
54
examples/0050-basic-for-multi-iterable.sx
Normal file
54
examples/0050-basic-for-multi-iterable.sx
Normal file
@@ -0,0 +1,54 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
pair_sum :: (xs: []s64, ys: []s64) -> s64 {
|
||||
total := 0;
|
||||
for xs, ys (x, y) { total += x * y; }
|
||||
total
|
||||
}
|
||||
|
||||
make :: () -> [3]s64 {
|
||||
r : [3]s64 = .[7, 8, 9];
|
||||
r
|
||||
}
|
||||
|
||||
main :: () -> s32 {
|
||||
// Agra's example: a 1..5 inclusive, b open-ended following along.
|
||||
for 1..=5, 0.. (a, b) { print("{}:{} ", a, b); }
|
||||
print("\n");
|
||||
|
||||
// Index idiom replacing the old (x, i) form.
|
||||
xs : [3]s64 = .[10, 20, 30];
|
||||
for xs, 0.. (x, i) { print("[{}]={} ", i, x); }
|
||||
print("\n");
|
||||
|
||||
// Parallel slices.
|
||||
a4 : [4]s64 = .[1, 2, 3, 4];
|
||||
b4 : [4]s64 = .[10, 20, 30, 40];
|
||||
print("dot={}\n", pair_sum(a4, b4));
|
||||
|
||||
// Arrow bodies.
|
||||
s := 0;
|
||||
for 0..4 (i) => s += i;
|
||||
print("arrow-range s={}\n", s);
|
||||
t := 0;
|
||||
for xs (x) => t += x;
|
||||
print("arrow-coll t={}\n", t);
|
||||
|
||||
// Call iterable + capture (first group = args, last group = capture).
|
||||
for make() (v) { print("v{} ", v); }
|
||||
print("\n");
|
||||
|
||||
// No-capture call iterable via leading-group escape.
|
||||
n := 0;
|
||||
for (make()) { n += 1; }
|
||||
print("escape n={}\n", n);
|
||||
|
||||
// Three-way zip: two collections + cursor.
|
||||
for a4, b4, 100.. (p, q, k) { print("{}/{}/{} ", p, q, k); }
|
||||
print("\n");
|
||||
|
||||
// By-ref capture in a multi-iterable header.
|
||||
for a4, 0.. (*p, i) { p.* += i; }
|
||||
print("after ref: {} {} {} {}\n", a4[0], a4[1], a4[2], a4[3]);
|
||||
0
|
||||
}
|
||||
63
examples/0051-basic-for-range-bounds.sx
Normal file
63
examples/0051-basic-for-range-bounds.sx
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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, arbitrary expressions at EITHER end (expression parsing
|
||||
// stops at the range token), 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");
|
||||
|
||||
// Arbitrary expressions at either end of the range token.
|
||||
x := 2;
|
||||
n := 0;
|
||||
sum := 0;
|
||||
for x+2..=42 (e) { n += 1; sum += e; } // expression start: 4 .. 42
|
||||
print("x+2..=42: n={} sum={}\n", n, sum);
|
||||
n2 := 0;
|
||||
for x+2<..<x*21 (e) => n2 += 1; // both ends: 5 .. 41
|
||||
print("x+2<..<x*21: n2={}\n", n2);
|
||||
n3 := 0;
|
||||
for 0..x*3 (i) => n3 += 1; // expression end: 0 .. 5
|
||||
print("0..x*3: n3={}\n", n3);
|
||||
|
||||
// Comparison operators still lex normally.
|
||||
a := 3;
|
||||
if a < 5 { print("cmp ok\n"); }
|
||||
b := a << 1;
|
||||
print("shl={}\n", b);
|
||||
0
|
||||
}
|
||||
32
examples/0052-basic-slice-range-bounds.sx
Normal file
32
examples/0052-basic-slice-range-bounds.sx
Normal file
@@ -0,0 +1,32 @@
|
||||
// Slice range bound markers — same matrix as for-header ranges: each side
|
||||
// of `..` takes `=` (inclusive) or `<` (exclusive), defaults 0-inclusive
|
||||
// start / exclusive end. Prefix form takes markers too ([..=2], [<..3]);
|
||||
// [..] is the whole slice; bounds are arbitrary expressions; strings slice
|
||||
// through the same path.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
dump :: (s: []s64, tag: string) {
|
||||
print("{}: ", tag);
|
||||
for s (v) { print("{} ", v); }
|
||||
print("(len {})\n", s.len);
|
||||
}
|
||||
|
||||
main :: () -> s32 {
|
||||
xs : [6]s64 = .[10, 11, 12, 13, 14, 15];
|
||||
full : []s64 = xs[0..6];
|
||||
|
||||
dump(full[1..=3], "1..=3"); // 11 12 13
|
||||
dump(full[0<..<4], "0<..<4"); // 11 12 13
|
||||
dump(full[..=2], "..=2"); // 10 11 12
|
||||
dump(full[<..3], "<..3"); // 11 12
|
||||
dump(full[2<..], "2<.."); // 13 14 15
|
||||
dump(full[..], ".."); // all six
|
||||
x := 3;
|
||||
dump(full[x-1..=x+1], "x-1..=x+1"); // 12 13 14
|
||||
|
||||
s := "abcdef";
|
||||
print("str 1..=3: {}\n", s[1..=3]); // bcd
|
||||
print("str 0<..<4: {}\n", s[0<..<4]); // bcd
|
||||
0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Perms :: enum flags { read; write; execute; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
@@ -16,7 +16,7 @@ main :: () {
|
||||
spts : [2]Point = .[Point.{1, 2}, Point.{3, 4}];
|
||||
spt2 := spts[1];
|
||||
print("arr-struct-x: {}\n", spt2.x);
|
||||
for spts: (it) {
|
||||
for spts (it) {
|
||||
print("for-struct: {}\n", it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
// ============================================================
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -63,6 +63,6 @@ main :: () {
|
||||
mk : Make(N, s64) = ---; mk[2] = 33; print("vp.typefn.expr: len={} v={}\n", mk.len, mk[2]);
|
||||
|
||||
// inline-for bound — expr const (3) and integral float (4)
|
||||
s := 0; inline for 0..N: (i) { s += i; } print("for.expr: {}\n", s); // 0+1+2 = 3
|
||||
t := 0; inline for 0..F: (i) { t += i; } print("for.float: {}\n", t); // 0+1+2+3 = 6
|
||||
s := 0; inline for 0..N (i) { s += i; } print("for.expr: {}\n", s); // 0+1+2 = 3
|
||||
t := 0; inline for 0..F (i) { t += i; } print("for.float: {}\n", t); // 0+1+2+3 = 6
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ main :: () -> s32 {
|
||||
|
||||
// for capture + index names
|
||||
xs := [3]s64.{ 10, 20, 30 };
|
||||
for xs: (`bool, `u16) {
|
||||
for xs, 0.. (`bool, `u16) {
|
||||
print("for = {} @ {}\n", `bool, `u16);
|
||||
}
|
||||
|
||||
|
||||
42
examples/0173-types-int-literal-default-s64.sx
Normal file
42
examples/0173-types-int-literal-default-s64.sx
Normal file
@@ -0,0 +1,42 @@
|
||||
// Integer literals default to s64 regardless of context: an unannotated
|
||||
// `x := <int literal>` local stays s64 even inside a function whose return
|
||||
// type is a narrower integer (the implicit-return target must not type the
|
||||
// body's declarations), and a large literal initializer keeps its value.
|
||||
// Also covers destructure decls (`a, b := ...`), which share the same rule.
|
||||
// Regression (issue 0111): these locals adopted the enclosing fn's return
|
||||
// type (s32/s8), silently wrapping `big := 3000000000` to -1294967296.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
f :: () -> s32 {
|
||||
x := 0;
|
||||
print("f.x: {}\n", type_name(type_of(x)));
|
||||
0
|
||||
}
|
||||
|
||||
g :: () -> s8 {
|
||||
x := 0;
|
||||
print("g.x: {}\n", type_name(type_of(x)));
|
||||
0
|
||||
}
|
||||
|
||||
big_host :: () -> s32 {
|
||||
big := 3000000000;
|
||||
print("big: {} = {}\n", type_name(type_of(big)), big);
|
||||
0
|
||||
}
|
||||
|
||||
d_host :: () -> s32 {
|
||||
a, b := (1, 2);
|
||||
print("a: {} b: {}\n", type_name(type_of(a)), type_name(type_of(b)));
|
||||
0
|
||||
}
|
||||
|
||||
main :: () {
|
||||
f();
|
||||
g();
|
||||
big_host();
|
||||
d_host();
|
||||
x := 0;
|
||||
print("main.x: {}\n", type_name(type_of(x)));
|
||||
}
|
||||
22
examples/0174-types-int-literal-boundaries.sx
Normal file
22
examples/0174-types-int-literal-boundaries.sx
Normal file
@@ -0,0 +1,22 @@
|
||||
// Boundary and exemption cases for the int-literal fits-check: extreme
|
||||
// in-range values compile (incl. negated literals via the constant fold);
|
||||
// width-64 types accept any representable literal; explicit `xx` / `cast`
|
||||
// still truncate on request; literal call args check against param types.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
clamp_s8 :: (v: s8) -> s8 { v }
|
||||
|
||||
main :: () {
|
||||
a : s8 = -128;
|
||||
b : s8 = 127;
|
||||
c : u8 = 0;
|
||||
d : u8 = 255;
|
||||
e : u64 = 0x7FFFFFFFFFFFFFFF;
|
||||
f : u32 = 0xFFFFFFFF;
|
||||
g : s16 = -32768;
|
||||
h : s8 = xx 300; // explicit truncation stays legal
|
||||
i := cast(s8) 300; // cast form too
|
||||
j : s8 = clamp_s8(-5);
|
||||
print("{} {} {} {} {} {} {} {} {} {}\n", a, b, c, d, e, f, g, h, i, j);
|
||||
}
|
||||
16
examples/0175-types-negative-literal-global.sx
Normal file
16
examples/0175-types-negative-literal-global.sx
Normal file
@@ -0,0 +1,16 @@
|
||||
// A negated literal is a compile-time constant for a global initializer:
|
||||
// ints serialize directly, an integral negative float narrows into an
|
||||
// integer global (non-integral errors), and boundary values fit exactly.
|
||||
// Out-of-range negatives get the literal fits-check, not "non-constant".
|
||||
// Regression (issue 0113): `g : s64 = -1;` was rejected as not a
|
||||
// compile-time constant (globalInitValue had no unary_op arm).
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
g1 : s64 = -1;
|
||||
g2 : s64 = -4.0;
|
||||
g3 : s8 = -128;
|
||||
|
||||
main :: () {
|
||||
print("{} {} {}\n", g1, g2, g3);
|
||||
}
|
||||
@@ -5,5 +5,5 @@ main :: () {
|
||||
s
|
||||
}
|
||||
|
||||
print("{}\n", fx(133));
|
||||
print("{}\n", fx(-3));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// first 4 bytes of malloc'd memory interpreted as `s32` (= 0 → null).
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx";
|
||||
#import "modules/std/mem.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
gpa := GPA.init();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
@@ -61,7 +61,7 @@ pair_add :: (a: $T, b: $U) -> s64 {
|
||||
|
||||
typed_sum :: (..args: []s32) -> s32 {
|
||||
result := 0;
|
||||
for args: (it) { result = result + it; }
|
||||
for args (it) { result = result + it; }
|
||||
result
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ sm_pair :: (a: s32, b: s32) -> (s32, s32, !) {
|
||||
|
||||
// `catch` block that diverges (logs the tag, then returns a fallback)
|
||||
sm_or_default :: (n: s32) -> s32 {
|
||||
return sm_parse(n) catch e {
|
||||
return sm_parse(n) catch (e) {
|
||||
print(" logged {}\n", e);
|
||||
return -1;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
sum :: (..args: []s32) -> s32 {
|
||||
result := 0;
|
||||
for args: (it) {
|
||||
for args (it) {
|
||||
result = result + it;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
print_all :: (..args: []s32) {
|
||||
for args: (it) {
|
||||
for args (it) {
|
||||
out(int_to_string(it));
|
||||
out(" ");
|
||||
}
|
||||
@@ -26,7 +26,7 @@ main :: () -> s32 {
|
||||
out(int_to_string(sum(..arr)));
|
||||
out("\n");
|
||||
|
||||
for arr: (it) {
|
||||
for arr (it) {
|
||||
out(int_to_string(it));
|
||||
out(" ");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ Point :: struct {
|
||||
|
||||
// Print all arguments — accepts any type, dispatches via type-switch
|
||||
print_any :: (..args: []Any) {
|
||||
for args: (it) {
|
||||
for args (it) {
|
||||
type := type_of(it);
|
||||
if type == {
|
||||
case int: out(int_to_string(cast(s32) it));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// constant (a literal, or an `inline for` cursor). A runtime index (here a
|
||||
// `while`-loop counter) must produce a clear diagnostic, not the confusing
|
||||
// "unresolved 'args'" the slice-index fall-through used to give. To walk a
|
||||
// pack, use `inline for 0..args.len: (i) { ... }`, which unrolls so each
|
||||
// pack, use `inline for 0..args.len (i) { ... }`, which unrolls so each
|
||||
// `args[i]` is a comptime index.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// match the impl's protocol type-args exactly.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx";
|
||||
#import "modules/std/mem.sx";
|
||||
|
||||
// User-defined parameterised protocol + an impl, so has_impl can
|
||||
// confirm parameterised matching works with a known-true case.
|
||||
|
||||
@@ -14,7 +14,7 @@ sink :: (v: s64) -> void { _ = v; }
|
||||
storage :: (..xs: Show) -> void { y := xs; _ = y; } // A: store
|
||||
call :: (..xs: Show) -> void { sink(xs); } // B: pass to a call
|
||||
ret :: (..xs: Show) -> s64 { return xs; } // C: return
|
||||
iter :: (..xs: Show) -> void { for xs : (x) { _ = x; } } // D: runtime iterate
|
||||
iter :: (..xs: Show) -> void { for xs (x) { _ = x; } } // D: runtime iterate
|
||||
|
||||
main :: () -> s32 {
|
||||
storage(A.{});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
add :: (a: s32, b: s32) -> s32 { a + b }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -14,10 +14,10 @@ M :: 3;
|
||||
|
||||
main :: () {
|
||||
s := 0;
|
||||
inline for 0..M: (i) { s += i; }
|
||||
inline for 0..M (i) { s += i; }
|
||||
print("sum 0..M = {}\n", s); // 0 + 1 + 2 = 3
|
||||
|
||||
t := 0;
|
||||
inline for 0..(M + 1): (i) { t += i; }
|
||||
inline for 0..(M + 1) (i) { t += i; }
|
||||
print("sum 0..(M+1) = {}\n", t); // 0 + 1 + 2 + 3 = 6
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ M :: 3.0;
|
||||
|
||||
main :: () {
|
||||
s := 0;
|
||||
inline for 0..M: (i) { s += i; }
|
||||
inline for 0..M (i) { s += i; }
|
||||
print("sum 0..M = {}\n", s); // 0 + 1 + 2 = 3
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
main :: () {
|
||||
s := 0;
|
||||
inline for -2..1: (i) { s += i; }
|
||||
inline for -2..1 (i) { s += i; }
|
||||
print("inline for -2..1 sum = {}\n", s); // -2 + -1 + 0 = -3
|
||||
|
||||
r := 0;
|
||||
for -2..1: (i) { r += i; }
|
||||
for -2..1 (i) { r += i; }
|
||||
print("for -2..1 sum = {}\n", r); // -2 + -1 + 0 = -3 (runtime parity)
|
||||
|
||||
e := 0;
|
||||
inline for 0..(-2.0): (i) { e += i; }
|
||||
inline for 0..(-2.0) (i) { e += i; }
|
||||
print("inline for 0..(-2.0) sum = {}\n", e); // empty range -> 0 iterations
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/hash.sx";
|
||||
#import "modules/fs.sx";
|
||||
#import "modules/std/fs.sx";
|
||||
|
||||
// 112-byte NIST multi-block vector — long enough that a 64-byte split is
|
||||
// a genuine block boundary and a 30-byte split lands mid-block.
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
// and freed in one `deinit`; the writer path allocates nothing.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/json.sx";
|
||||
#import "modules/fs.sx";
|
||||
#import "modules/std/fs.sx";
|
||||
|
||||
// The exact document the writer must produce (insertion order, escaping).
|
||||
EXPECT :: "{\"name\":\"a\\\"b\\n\",\"tab\":\"x\\ty\",\"bs\":\"c\\\\d\",\"ctrl\":\"\\u0001\",\"n\":-7,\"zero\":0,\"pos\":7,\"min\":-9223372036854775808,\"max\":9223372036854775807,\"ok\":true,\"nil\":null,\"xs\":[1,-2,3],\"nested\":{\"k\":\"v\"}}";
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// `JsonParseError` variant on the error channel, never a bogus value.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/json.sx";
|
||||
|
||||
// Canonical document: no insignificant whitespace, escapes in the writer's
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
// and decoded strings go through `alloc`, and the writer allocates nothing.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/json.sx";
|
||||
|
||||
// The writer's EXACT output for the PART A document (insertion order,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/cli.sx";
|
||||
log :: #import "modules/log.sx";
|
||||
log :: #import "modules/std/log.sx";
|
||||
|
||||
report :: (label: string, ok: bool) {
|
||||
if ok { print("{}: ok\n", label); } else { print("{}: FAIL\n", label); }
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// independent identities.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
// `cli` is imported BOTH flat (so its types — `FlagSpec` / `Command` / `Diag` —
|
||||
// are bare-visible) AND namespaced (so the same-name `cli.parse` stays a
|
||||
// distinct qualified identity from `json.parse`). Post-E1 a bare reference to a
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
combine :: (x: s64, y: s64) -> s64 { return x + y; }
|
||||
pick :: (..xs: []s64) -> s64 {
|
||||
result := 0;
|
||||
for xs: (it) { result = result + it; }
|
||||
for xs (it) { result = result + it; }
|
||||
result
|
||||
}
|
||||
from_a_combine :: () -> s64 { return combine(10, 20); }
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// pack, pick subtracts its two fixed args.
|
||||
combine :: (..xs: []s64) -> s64 {
|
||||
result := 0;
|
||||
for xs: (it) { result = result + it; }
|
||||
for xs (it) { result = result + it; }
|
||||
result
|
||||
}
|
||||
pick :: (a: s64, b: s64) -> s64 { return b - a; }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// that protocol. Exercises save/restore of the boxed context across the push.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx";
|
||||
#import "modules/std/mem.sx";
|
||||
|
||||
main :: () -> void {
|
||||
arena := Arena.init(context.allocator, 4096);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// 3. Static method with inline xx: `T.init(xx p, ...)` ← used to crash
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx";
|
||||
#import "modules/std/mem.sx";
|
||||
|
||||
Box :: struct {
|
||||
parent: Allocator;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// whether the recovery happens BEFORE or AFTER the first dispatch.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
|
||||
main :: () -> s32 {
|
||||
gpa := GPA.init();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// the operand's storage, so it never allocates and never reaches this
|
||||
// path. See specs.md §3 — Protocol value ownership and lifetime.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||
|
||||
Tracer :: struct {
|
||||
count: s64;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// and the local would stay at zero. With Option 3 the local sees the
|
||||
// increments because they ARE the local.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/allocators.sx";
|
||||
#import "modules/std/mem.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
gpa := GPA.init();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/test.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -21,7 +21,7 @@ IoErr :: error { Disk }
|
||||
|
||||
main :: () -> s32 {
|
||||
fail_own := closure(() -> !IoErr { raise error.Disk; });
|
||||
fail_own() catch e {
|
||||
fail_own() catch (e) {
|
||||
if e == error.Disk { print("own=Disk\n"); }
|
||||
};
|
||||
d := dep_err();
|
||||
|
||||
20
examples/0831-modules-namespace-alias-carry.sx
Normal file
20
examples/0831-modules-namespace-alias-carry.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
// Namespace aliases are module surface: a `ns :: #import` declared by a
|
||||
// module is usable by that module's DIRECT flat importers (the carry rule —
|
||||
// no `pub` keyword). Covers every qualified shape through BOTH a direct and
|
||||
// a carried alias: plain fn, struct static method + instance method, type
|
||||
// annotation, enum variant, module const, generic struct.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "0831-modules-namespace-alias-carry/facade.sx";
|
||||
|
||||
main :: () {
|
||||
print("{} ", r.helper()); // plain fn (carried)
|
||||
t := r.Thing.init(); // static method
|
||||
print("{} ", t.get());
|
||||
x : r.Thing = r.Thing.init(); // type annotation
|
||||
print("{} ", x.v);
|
||||
print("{} ", r.LIMIT); // module const
|
||||
print("{} ", r.Color.green); // enum variant
|
||||
b := r.Box(s64).{ item = 3 }; // generic struct
|
||||
print("{}\n", b.item);
|
||||
}
|
||||
1
examples/0831-modules-namespace-alias-carry/facade.sx
Normal file
1
examples/0831-modules-namespace-alias-carry/facade.sx
Normal file
@@ -0,0 +1 @@
|
||||
r :: #import "rich.sx";
|
||||
9
examples/0831-modules-namespace-alias-carry/rich.sx
Normal file
9
examples/0831-modules-namespace-alias-carry/rich.sx
Normal file
@@ -0,0 +1,9 @@
|
||||
Thing :: struct {
|
||||
v: s64;
|
||||
init :: () -> Thing { Thing.{ v = 5 } }
|
||||
get :: (self: *Thing) -> s64 { self.v }
|
||||
}
|
||||
Color :: enum { red; green; }
|
||||
LIMIT :s64: 99;
|
||||
Box :: struct ($T: Type) { item: T; }
|
||||
helper :: () -> s64 { 7 }
|
||||
@@ -20,7 +20,7 @@ must :: (n: s32) -> !E {
|
||||
|
||||
// Diverging body — returns from `classify` on error.
|
||||
classify :: (n: s32) -> s32 {
|
||||
must(n) catch e {
|
||||
must(n) catch (e) {
|
||||
if e == error.Bad { return 1; }
|
||||
if e == error.Empty { return 2; }
|
||||
return 9;
|
||||
@@ -28,9 +28,9 @@ classify :: (n: s32) -> s32 {
|
||||
return 0; // must(n) succeeded
|
||||
}
|
||||
|
||||
// Match-body form — sugar for `catch e { if e == { case ... } }`.
|
||||
// Match-body form — sugar for `catch (e) { if e == { case ... } }`.
|
||||
mclassify :: (n: s32) -> s32 {
|
||||
must(n) catch e == {
|
||||
must(n) catch (e) == {
|
||||
case .Bad: return 11;
|
||||
case .Empty: return 22;
|
||||
else: return 99;
|
||||
@@ -41,7 +41,7 @@ mclassify :: (n: s32) -> s32 {
|
||||
// Selective handle + re-raise (failable enclosing fn; `raise e` is the
|
||||
// variable form). Swallows Bad → success; re-raises everything else.
|
||||
handle_some :: (n: s32) -> !E {
|
||||
must(n) catch e {
|
||||
must(n) catch (e) {
|
||||
if e == error.Bad { return; } // swallow → success
|
||||
raise e; // re-raise the rest
|
||||
};
|
||||
@@ -50,7 +50,7 @@ handle_some :: (n: s32) -> !E {
|
||||
|
||||
main :: () -> s32 {
|
||||
r : s32 = 0;
|
||||
must(-1) catch e { if e == error.Bad { r = r + 1; } }; // Bad → +1
|
||||
must(-1) catch (e) { if e == error.Bad { r = r + 1; } }; // Bad → +1
|
||||
must(5) catch { r = r + 100; }; // success → body skipped
|
||||
r = r + classify(0); // Empty → 2
|
||||
r = r + classify(8); // success → 0
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
plain :: () -> s32 { return 0; }
|
||||
|
||||
main :: () -> s32 {
|
||||
plain() catch e { return 1; }; // error: operand has type s32 (not failable)
|
||||
plain() catch (e) { return 1; }; // error: operand has type s32 (not failable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// the consumer side of the error-channel tuple ABI). `try f()` on a
|
||||
// `-> (T, !E)` callee binds the value slot on success and propagates the error
|
||||
// on failure (a pure-failable caller returns the tag; a value-carrying caller
|
||||
// returns `{undef, tag}`). `f() catch e BODY` yields the value slot on success
|
||||
// returns `{undef, tag}`). `f() catch (e) BODY` yields the value slot on success
|
||||
// or the handler body's value on failure, merged through a block parameter.
|
||||
// The producer side is `examples/228-value-failable.sx`.
|
||||
|
||||
@@ -31,12 +31,12 @@ relay :: (n: s32) -> !E {
|
||||
|
||||
// value-carrying `catch`, bare-expression fallback.
|
||||
safe :: (n: s32) -> s32 {
|
||||
return parse(n) catch e 0;
|
||||
return parse(n) catch (e) 0;
|
||||
}
|
||||
|
||||
// value-carrying `catch`, match-body value.
|
||||
classify :: (n: s32) -> s32 {
|
||||
return parse(n) catch e == {
|
||||
return parse(n) catch (e) == {
|
||||
case .Bad: 1;
|
||||
case .Empty: 2;
|
||||
else: 3
|
||||
|
||||
@@ -14,6 +14,6 @@ parse :: (n: s32) -> (s32, !E) {
|
||||
}
|
||||
|
||||
main :: () -> s32 {
|
||||
x := parse(-1) catch e { print("oops\n") }; // error: body yields no value
|
||||
x := parse(-1) catch (e) { print("oops\n") }; // error: body yields no value
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// (ERR step E1.7). Unlike `defer` (which runs on every exit), `onfail` fires
|
||||
// on an error exit — a `raise` or a propagating `try` — and is skipped on
|
||||
// success. On an error exit `defer` and `onfail` run interleaved in reverse
|
||||
// declaration order. `onfail e { … }` binds the in-flight error tag.
|
||||
// declaration order. `onfail (e) { … }` binds the in-flight error tag.
|
||||
// (Per-attempt-`try` gating and `or`-chain absorption refine this in E2.4b.)
|
||||
|
||||
#import "modules/std.sx";
|
||||
@@ -25,7 +25,7 @@ run :: (n: s32) -> !E {
|
||||
|
||||
// `onfail e` binds the tag.
|
||||
classify :: (n: s32) -> !E {
|
||||
onfail e { if e == error.Bad { print("cleanup: bad\n"); } }
|
||||
onfail (e) { if e == error.Bad { print("cleanup: bad\n"); } }
|
||||
if n < 0 { raise error.Bad; }
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,13 +26,13 @@ inc :: (n: s32) -> (s32, s32, !E) {
|
||||
|
||||
// Multi-value `catch`, bare-expression tuple fallback (absorbs the failure).
|
||||
safe :: (n: s32) -> s32 {
|
||||
v, b := parse(n) catch e (40, 50);
|
||||
v, b := parse(n) catch (e) (40, 50);
|
||||
return v + b;
|
||||
}
|
||||
|
||||
// Multi-value `catch` match-body — per-tag dispatch, each arm a value-tuple.
|
||||
classify :: (n: s32) -> s32 {
|
||||
v, b := parse(n) catch e == {
|
||||
v, b := parse(n) catch (e) == {
|
||||
case .Bad: (1, 1);
|
||||
case .Empty: (2, 2);
|
||||
else: (9, 9);
|
||||
|
||||
@@ -17,8 +17,8 @@ g :: () -> !E { return; }
|
||||
f :: () -> !E {
|
||||
defer { return; } // ERROR: return in defer body
|
||||
onfail { try g(); } // ERROR: try in onfail body
|
||||
defer { for 0..1: (i) { break; } } // ERROR: break in defer body (transitive through loop)
|
||||
onfail e { if e == error.Bad { continue; } } // ERROR: continue in onfail body
|
||||
defer { for 0..1 (i) { break; } } // ERROR: break in defer body (transitive through loop)
|
||||
onfail (e) { if e == error.Bad { continue; } } // ERROR: continue in onfail body
|
||||
try g();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ main :: () -> s32 {
|
||||
print("a={} b={}\n", a, b); // a=BadDigit b=Overflow
|
||||
|
||||
// A tag bound by `catch` interpolates too (diverging handler).
|
||||
v := parse(0) catch e {
|
||||
v := parse(0) catch (e) {
|
||||
print("parse failed with {}\n", e); // parse failed with Empty
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -28,13 +28,13 @@ main :: () -> s32 {
|
||||
// cleared when the handler completes (a non-diverging exit), not on entry.
|
||||
// So inside the handler the frames are still visible (here: the `raise` in
|
||||
// `fail` + the `try fail` propagation in `propagate` = 2 frames)...
|
||||
propagate(-1) catch e {
|
||||
propagate(-1) catch (e) {
|
||||
print("in catch: len={}\n", sx_trace_len()); // 2 (handler sees the chain)
|
||||
};
|
||||
print("after catch: len={}\n", sx_trace_len()); // 0 (absorbed at handler exit)
|
||||
|
||||
// A success leaves the buffer empty (nothing pushed).
|
||||
propagate(1) catch e { };
|
||||
propagate(1) catch (e) { };
|
||||
print("after success: len={}\n", sx_trace_len()); // 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Error return-trace formatting (ERR step E3.3). `library/modules/trace.sx`
|
||||
// Error return-trace formatting (ERR step E3.3). `library/modules/std/trace.sx`
|
||||
// reads the trace buffer (E3.1, populated by E3.2's raise/try push wiring) and
|
||||
// renders it. `trace.print_current()` writes the trace to stderr; the catch
|
||||
// handler sees the full chain because the absorption clear fires at handler
|
||||
@@ -9,7 +9,7 @@
|
||||
// snapshot shows the trace lines interleaved with the `print` (stdout) lines.
|
||||
|
||||
#import "modules/std.sx";
|
||||
trace :: #import "modules/trace.sx";
|
||||
trace :: #import "modules/std/trace.sx";
|
||||
|
||||
// Buffer length probe (the runtime symbol; public read API is the trace module).
|
||||
sx_trace_len :: () -> u32 #foreign;
|
||||
@@ -27,7 +27,7 @@ mid :: (n: s32) -> !E {
|
||||
}
|
||||
|
||||
main :: () -> s32 {
|
||||
mid(-1) catch e {
|
||||
mid(-1) catch (e) {
|
||||
print("[stdout] caught {}\n", e); // tag name via the always-linked table
|
||||
trace.print_current(); // [stderr] the 2-frame trace
|
||||
};
|
||||
|
||||
@@ -30,8 +30,8 @@ main :: () -> (s32, !E) {
|
||||
r = r + (try fa(0) or try fa(7)); // a fails → b succeeds → 7
|
||||
r = r + (try fa(0) or try fa(0) or try fa(3)); // first two fail → third → +3 = 10
|
||||
r = r + (fa(0) or fa(0) or 96); // bare chain + value terminator → +96 = 106
|
||||
r = r + ((try fa(0) or try fa(0)) catch e 5); // both fail → catch handler → +5 = 111
|
||||
r = r + ((try fa(0) or try fa(9)) catch e 0); // second succeeds → catch skipped → +9 = 120
|
||||
r = r + ((try fa(0) or try fa(0)) catch (e) 5); // both fail → catch handler → +5 = 111
|
||||
r = r + ((try fa(0) or try fa(9)) catch (e) 0); // second succeeds → catch skipped → +9 = 120
|
||||
|
||||
try fv(0) or try fv(1); // void chain: first fails → second succeeds
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// single stdout line. Expected exit code: 0.
|
||||
|
||||
#import "modules/std.sx";
|
||||
log :: #import "modules/log.sx";
|
||||
log :: #import "modules/std/log.sx";
|
||||
|
||||
probe :: () -> s32 {
|
||||
if is_comptime() { return 1; } // comptime interpreter path
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// still appears despite `_exit` skipping the stdio flush.) Expected exit: 42.
|
||||
|
||||
#import "modules/std.sx";
|
||||
proc :: #import "modules/process.sx";
|
||||
proc :: #import "modules/std/process.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
print("starting\n");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Built on `process.exit`. Expected exit code: 1.
|
||||
|
||||
#import "modules/std.sx";
|
||||
proc :: #import "modules/process.sx";
|
||||
proc :: #import "modules/std/process.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
proc.assert(2 + 2 == 4, "arithmetic"); // passes → no-op
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// resolution, so only names print today. Expected exit: 0.
|
||||
|
||||
#import "modules/std.sx";
|
||||
trace :: #import "modules/trace.sx";
|
||||
trace :: #import "modules/std/trace.sx";
|
||||
|
||||
probe :: () {
|
||||
trace.print_interpreter_frames(); // dumps the chain: __run_0 → inner → probe
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Expected exit: 0 (the error is caught; the trace is printed during the build).
|
||||
|
||||
#import "modules/std.sx";
|
||||
trace :: #import "modules/trace.sx";
|
||||
trace :: #import "modules/std/trace.sx";
|
||||
|
||||
TErr :: error { Bad };
|
||||
|
||||
@@ -20,7 +20,7 @@ mid :: () -> !TErr {
|
||||
}
|
||||
|
||||
probe :: () {
|
||||
mid() catch e {
|
||||
mid() catch (e) {
|
||||
print("comptime caught {}\n", e);
|
||||
trace.print_current();
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ sm_pair :: (a: s32, b: s32) -> (s32, s32, !) {
|
||||
|
||||
// catch with a diverging block body
|
||||
sm_or_default :: (n: s32) -> s32 {
|
||||
return sm_parse(n) catch e {
|
||||
return sm_parse(n) catch (e) {
|
||||
print(" logged {}\n", e);
|
||||
return -1;
|
||||
};
|
||||
@@ -61,7 +61,7 @@ sm_run :: (cb: Closure(s32) -> (s32, !SmokeErr), n: s32) -> (s32, !SmokeErr) {
|
||||
// bare fn-type param: a NON-failable closure literal widens into the failable
|
||||
// slot (the ∅-widening adapter wraps `{value, 0}`)
|
||||
sm_widen :: (cb: (s32) -> (s32, !SmokeErr), n: s32) -> s32 {
|
||||
return cb(n) catch e -1;
|
||||
return cb(n) catch (e) -1;
|
||||
}
|
||||
|
||||
// generic ($T) value-carrying failable composition, monomorphized per call
|
||||
@@ -81,11 +81,11 @@ main :: () {
|
||||
if err2 == error.BadDigit { print("got: {}\n", err2); }
|
||||
|
||||
// catch — bare-expr body
|
||||
ce := sm_parse(0) catch e 100;
|
||||
ce := sm_parse(0) catch (e) 100;
|
||||
print("catch-expr: {}\n", ce);
|
||||
|
||||
// catch — match-body per-tag dispatch
|
||||
cm := sm_parse(200) catch e == {
|
||||
cm := sm_parse(200) catch (e) == {
|
||||
case .Overflow: 1;
|
||||
case .Empty: 2;
|
||||
else: 3;
|
||||
@@ -105,9 +105,9 @@ main :: () {
|
||||
if !gerr { print("or-chain: {}\n", g); }
|
||||
|
||||
// multi-value failable consumed by catch (tuple body)
|
||||
p, q := sm_pair(0, 3) catch e (0, 0);
|
||||
p, q := sm_pair(0, 3) catch (e) (0, 0);
|
||||
print("pair-catch: {} {}\n", p, q);
|
||||
p2, q2 := sm_pair(4, 5) catch e (0, 0);
|
||||
p2, q2 := sm_pair(4, 5) catch (e) (0, 0);
|
||||
print("pair-ok: {} {}\n", p2, q2);
|
||||
|
||||
// pure failable: absorb with no-binding catch
|
||||
@@ -120,15 +120,15 @@ main :: () {
|
||||
iv, ierr := sm_acquire(false);
|
||||
|
||||
// composition: inline failable closure literal through a Closure(...) param
|
||||
cl := sm_run(closure((x: s32) -> (s32, !SmokeErr) { if x < 0 { raise error.BadDigit; } return x * 2; }), 6) catch e -1;
|
||||
cl := sm_run(closure((x: s32) -> (s32, !SmokeErr) { if x < 0 { raise error.BadDigit; } return x * 2; }), 6) catch (e) -1;
|
||||
print("closure-run: {}\n", cl); // 12
|
||||
print("closure-run-err: {}\n", sm_run(closure((x: s32) -> (s32, !SmokeErr) { raise error.Empty; }), 1) catch e -9); // -9
|
||||
print("closure-run-err: {}\n", sm_run(closure((x: s32) -> (s32, !SmokeErr) { raise error.Empty; }), 1) catch (e) -9); // -9
|
||||
|
||||
// non-failable closure literal widened into the failable bare slot
|
||||
print("widen: {}\n", sm_widen(closure((x: s32) -> s32 => x + 1), 9)); // 10
|
||||
|
||||
// generic failable composition (monomorphized at s32)
|
||||
print("wrap: {}\n", sm_wrap(s32, closure(() -> (s32, !SmokeErr) { return 42; })) catch e 0); // 42
|
||||
print("wrap: {}\n", sm_wrap(s32, closure(() -> (s32, !SmokeErr) { return 42; })) catch (e) 0); // 42
|
||||
|
||||
print("errors ok\n");
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ guard :: (ok: bool) -> !E {
|
||||
}
|
||||
|
||||
ok_v :: #run parse(5); // success → 10 (value, error stripped)
|
||||
caught :: #run parse(-1) catch e 99; // Bad → 99
|
||||
caught :: #run parse(-1) catch (e) 99; // Bad → 99
|
||||
ored :: #run parse(0) or 55; // Empty → 55
|
||||
|
||||
#run guard(false) catch e { }; // onfail fires during the comptime unwind
|
||||
#run guard(false) catch (e) { }; // onfail fires during the comptime unwind
|
||||
|
||||
main :: () -> s32 {
|
||||
print("ok={} caught={} ored={}\n", ok_v, caught, ored);
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
E :: error { Neg }
|
||||
|
||||
runwith :: (cb: Closure(s64) -> (s64, !E), n: s64) -> s64 { return cb(n) catch e -1; }
|
||||
runwith :: (cb: Closure(s64) -> (s64, !E), n: s64) -> s64 { return cb(n) catch (e) -1; }
|
||||
|
||||
main :: () -> s32 {
|
||||
// block-body and arrow-body failable closures, called directly
|
||||
m := closure((x: s64) -> (s64, !E) { if x < 0 { raise error.Neg; } return x * 2; });
|
||||
n := closure((x: s64) -> (s64, !E) => x + 1);
|
||||
print("{} {} {} {}\n", m(5) catch e 0, m(-1) catch e 99, m(-1) or 7, n(40) catch e 0); // 10 99 7 41
|
||||
print("{} {} {} {}\n", m(5) catch (e) 0, m(-1) catch (e) 99, m(-1) or 7, n(40) catch (e) 0); // 10 99 7 41
|
||||
|
||||
// failable closure passed as a Closure(...) parameter
|
||||
print("param ok={} err={}\n", runwith(m, 5), runwith(m, -1)); // 10 -1
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
E :: error { Neg }
|
||||
|
||||
bare :: (cb: (s64) -> (s64, !E), n: s64) -> s64 { return cb(n) catch e -1; }
|
||||
bare :: (cb: (s64) -> (s64, !E), n: s64) -> s64 { return cb(n) catch (e) -1; }
|
||||
chain :: (cb: Closure(s64) -> (s64, !E), n: s64) -> (s64, !E) { return try cb(n); }
|
||||
|
||||
dbl :: (x: s64) -> (s64, !E) { if x < 0 { raise error.Neg; } return x * 2; }
|
||||
@@ -25,8 +25,8 @@ main :: () -> s32 {
|
||||
|
||||
// Closure(...) param, try-propagated, then caught at the call site
|
||||
print("chain ok={} err={}\n",
|
||||
chain(closure((x: s64) -> (s64, !E) => x + 6), 4) catch e 0, // 10
|
||||
chain(closure((x: s64) -> (s64, !E) { raise error.Neg; }), 1) catch e 0); // 0
|
||||
chain(closure((x: s64) -> (s64, !E) => x + 6), 4) catch (e) 0, // 10
|
||||
chain(closure((x: s64) -> (s64, !E) { raise error.Neg; }), 1) catch (e) 0); // 0
|
||||
|
||||
// NON-failable closure literal widened into the failable bare slot
|
||||
print("widen={}\n", bare(closure((x: s64) -> s64 => x + 1), 9)); // 10
|
||||
|
||||
@@ -24,13 +24,13 @@ main :: () -> s32 {
|
||||
handlers.append(closure((x: s32) -> (s32, !) { if x == 0 { raise error.Other; } return x + 100; }));
|
||||
|
||||
// success paths
|
||||
print("ok0={}\n", dispatch(handlers.items[0], 5) catch e 0); // 10
|
||||
print("ok1={}\n", dispatch(handlers.items[1], 7) catch e 0); // 107
|
||||
print("ok0={}\n", dispatch(handlers.items[0], 5) catch (e) 0); // 10
|
||||
print("ok1={}\n", dispatch(handlers.items[1], 7) catch (e) 0); // 107
|
||||
|
||||
// failure paths: each closure raises its own tag, which propagates
|
||||
// through `try` and is absorbed by the call-site `catch` fallback
|
||||
print("err0={}\n", dispatch(handlers.items[0], -1) catch e -1); // raised Negative → -1
|
||||
print("err1={}\n", dispatch(handlers.items[1], 0) catch e -2); // raised Other → -2
|
||||
print("err0={}\n", dispatch(handlers.items[0], -1) catch (e) -1); // raised Negative → -1
|
||||
print("err1={}\n", dispatch(handlers.items[1], 0) catch (e) -2); // raised Other → -2
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ main :: () -> s32 {
|
||||
handlers : List(Closure(s32) -> (s32, !)) = .{};
|
||||
handlers.append(closure((x: s32) -> (s32, !) { if x < 0 { raise error.Negative; } return x; }));
|
||||
handlers.append(closure((x: s32) -> (s32, !) { if x == 0 { raise error.Other; } return x; }));
|
||||
print("r={}\n", reject(handlers.items[0], 5) catch e 0);
|
||||
print("r={}\n", reject(handlers.items[0], 5) catch (e) 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
E :: error { Neg }
|
||||
|
||||
take :: (cb: Closure(s32) -> (s32, !E), x: s32) -> s32 { return cb(x) catch e -1; }
|
||||
take :: (cb: Closure(s32) -> (s32, !E), x: s32) -> s32 { return cb(x) catch (e) -1; }
|
||||
|
||||
main :: () -> s32 {
|
||||
// `-> s32` (non-failable) but the body raises → lambda-specific hint:
|
||||
|
||||
@@ -13,7 +13,7 @@ wrap :: ($T: Type, f: Closure() -> (T, !E)) -> (T, !E) { return try f(); }
|
||||
|
||||
main :: () -> s32 {
|
||||
// success, consumed by catch
|
||||
print("catch={}\n", wrap(s32, closure(() -> (s32, !E) { return 7; })) catch e -1); // 7
|
||||
print("catch={}\n", wrap(s32, closure(() -> (s32, !E) { return 7; })) catch (e) -1); // 7
|
||||
|
||||
// success, consumed by destructure (binds value + error slot); the value
|
||||
// slot is read only under an `if !err` guard (ERR E1.8 path-sensitivity)
|
||||
@@ -21,9 +21,9 @@ main :: () -> s32 {
|
||||
if !err { print("destr={} ok=true\n", r); } // destr=9 ok=true
|
||||
|
||||
// failure path: the raised tag propagates through the generic `try`
|
||||
print("fail={}\n", wrap(s32, closure(() -> (s32, !E) { raise error.Bad; }) ) catch e -1); // -1
|
||||
print("fail={}\n", wrap(s32, closure(() -> (s32, !E) { raise error.Bad; }) ) catch (e) -1); // -1
|
||||
|
||||
// a second monomorphization at a different T
|
||||
print("u8={}\n", wrap(u8, closure(() -> (u8, !E) { return 200; })) catch e 0); // 200
|
||||
print("u8={}\n", wrap(u8, closure(() -> (u8, !E) { return 200; })) catch (e) 0); // 200
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
E :: error { Z }
|
||||
|
||||
bare :: (cb: (s64) -> s64, n: s64) -> s64 { return cb(n); }
|
||||
baref :: (cb: (s64) -> (s64, !E), n: s64) -> s64 { return cb(n) catch e -1; }
|
||||
baref :: (cb: (s64) -> (s64, !E), n: s64) -> s64 { return cb(n) catch (e) -1; }
|
||||
|
||||
main :: () -> s32 {
|
||||
inc := closure((x: s64) -> s64 => x + 1); // capture-free closure var
|
||||
|
||||
@@ -53,7 +53,7 @@ main :: () -> s32 {
|
||||
// (4) early-return / raise helpers
|
||||
total = total + guarded(4); // +40
|
||||
total = total + guarded(-1); // -1
|
||||
total = total + (relay(2) catch e 0); // parse(2)=20 → +1 = 21
|
||||
total = total + (relay(2) catch (e) 0); // parse(2)=20 → +1 = 21
|
||||
|
||||
print("liveness total: {}\n", total); // 50+70+30+40-1+21 = 210
|
||||
return total;
|
||||
|
||||
@@ -13,7 +13,7 @@ recover :: () -> (s32, !E) { raise error.Bad; }
|
||||
|
||||
work :: (n: s32) -> !E {
|
||||
defer print("defer: always\n"); // plain cleanup
|
||||
onfail { failing() catch e print("onfail: caught (catch)\n"); } // catch absorbs
|
||||
onfail { failing() catch (e) print("onfail: caught (catch)\n"); } // catch absorbs
|
||||
onfail { x := recover() or 7; print("onfail: x={} (or)\n", x); } // or-value absorbs
|
||||
if n < 0 { raise error.Bad; }
|
||||
return;
|
||||
|
||||
@@ -16,7 +16,7 @@ run :: () {
|
||||
defer {
|
||||
v, e := probe(); // destructure decl
|
||||
if !e { print("defer: v={}\n", v); } // value live under the guard
|
||||
failing() catch x print("defer: caught\n"); // catch-statement absorbs
|
||||
failing() catch (x) print("defer: caught\n"); // catch-statement absorbs
|
||||
}
|
||||
print("body\n");
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ work :: () {
|
||||
if !err { print("defer closure: v={}\n", v); } // E1.8: live under guard
|
||||
try failing();
|
||||
};
|
||||
emit() catch e print("defer closure: raised\n");
|
||||
emit() catch (e) print("defer closure: raised\n");
|
||||
}
|
||||
print("body\n");
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user