test: group examples into per-category folders
Move examples/*.sx and their expected/ snapshots into per-category subfolders (examples/<category>/...). Folder = leading filename token, with ffi-objc/ffi-jni kept whole; filenames are unchanged. The corpus runner and LSP sweep now discover each category's expected/ dir, while issues/ stays flat. Example 1058's repo-root-relative companion import is made file-relative. Path strings embedded in 164 snapshots were regenerated (path-only changes). Test-layout docs in CLAUDE.md updated.
This commit is contained in:
20
examples/comptime/0600-comptime-run.sx
Normal file
20
examples/comptime/0600-comptime-run.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
#import "modules/std.sx";
|
||||
// this will bake x to be 7 as a global constant
|
||||
x :: #run compute(5);
|
||||
|
||||
compute :: (v: i32) -> i32 => v + 2;
|
||||
|
||||
main :: () {
|
||||
//test
|
||||
y :: #run compute(7);
|
||||
c :: 2;
|
||||
print("hello {}\n", x + y * c);
|
||||
}
|
||||
|
||||
#run main();
|
||||
|
||||
// ** stdout after build **
|
||||
// hello 25
|
||||
|
||||
// ** stdout after run **
|
||||
// hello 25
|
||||
29
examples/comptime/0601-comptime-meta.sx
Normal file
29
examples/comptime/0601-comptime-meta.sx
Normal file
@@ -0,0 +1,29 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math";
|
||||
|
||||
test :: () -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
Vec4 :: Vector(4,f32);
|
||||
|
||||
main :: () {
|
||||
x : Type = f64;
|
||||
print("{}\n", x);
|
||||
|
||||
v:f64 = 3.2;
|
||||
print("{}\n", v);
|
||||
|
||||
x = Vec4;
|
||||
print("{}\n", x);
|
||||
|
||||
x = test;
|
||||
print("{}\n", x);
|
||||
|
||||
}
|
||||
|
||||
// ** stdout **
|
||||
// f64
|
||||
// 3.200000
|
||||
// Vector(4,f32)
|
||||
// () -> i32
|
||||
35
examples/comptime/0602-comptime-interp-cast-ptr-cmp.sx
Normal file
35
examples/comptime/0602-comptime-interp-cast-ptr-cmp.sx
Normal file
@@ -0,0 +1,35 @@
|
||||
// `cast(T) val` inside a conditional, evaluated by the IR interpreter
|
||||
// during a post-link callback. The `cast` syntax lowers the type arg
|
||||
// (`i64`) as a `placeholder` IR op; the interpreter treats placeholders
|
||||
// as undef so the comparison runs through unchanged.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
libc :: #library "c";
|
||||
popen :: (cmd: [:0]u8, mode: [:0]u8) -> *void extern libc;
|
||||
puts :: (s: [:0]u8) -> i32 extern libc;
|
||||
|
||||
R :: struct { x: i32; }
|
||||
|
||||
bug :: (cmd: [:0]u8) -> ?R {
|
||||
f := popen(cmd, "r");
|
||||
if cast(i64) f == 0 { return null; }
|
||||
R.{ x = 1 }
|
||||
}
|
||||
|
||||
post_link :: (opt: BuildOptions) -> bool abi(.compiler) {
|
||||
if r := bug("echo hi") { puts("ok"); } else { puts("null"); }
|
||||
true
|
||||
}
|
||||
|
||||
// The registrar is itself compiler-domain (`abi(.compiler)`): it runs in the
|
||||
// comptime evaluator (never the binary), so its `build_options()` /
|
||||
// `set_post_link_callback()` compiler-API calls are permitted.
|
||||
configure :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
on_build(post_link);
|
||||
}
|
||||
#run configure();
|
||||
|
||||
main :: () { print("rt\n"); }
|
||||
32
examples/comptime/0603-comptime-interp-variadic-any.sx
Normal file
32
examples/comptime/0603-comptime-interp-variadic-any.sx
Normal file
@@ -0,0 +1,32 @@
|
||||
// IR interpreter — variadic `..Any` indexing inside post-link callback.
|
||||
//
|
||||
// `format(fmt, ..args: []Any)` lowers to `any_to_string(args[i])` calls.
|
||||
// The interpreter must be able to read every element of the packed
|
||||
// `[N x Any]` slice from within a `#run`/post-link callback, not just
|
||||
// the first two — and not just via JIT.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
puts :: (s: [:0]u8) -> i32 extern libc;
|
||||
|
||||
cb :: (opt: BuildOptions) -> bool abi(.compiler) {
|
||||
a := format("{}", "x");
|
||||
puts("1-arg ok");
|
||||
b := format("{} {}", "x", "y");
|
||||
puts("2-arg ok");
|
||||
c := format("{} {} {}", "x", "y", "z");
|
||||
puts("3-arg ok");
|
||||
true
|
||||
}
|
||||
|
||||
// The registrar is itself compiler-domain (`abi(.compiler)`): it runs in the
|
||||
// comptime evaluator (never the binary), so its `build_options()` /
|
||||
// `set_post_link_callback()` compiler-API calls are permitted.
|
||||
configure :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
on_build(cb);
|
||||
}
|
||||
#run configure();
|
||||
|
||||
main :: () { print("rt\n"); }
|
||||
190
examples/comptime/0604-comptime-typed-store-widths.sx
Normal file
190
examples/comptime/0604-comptime-typed-store-widths.sx
Normal file
@@ -0,0 +1,190 @@
|
||||
// Lock down the interp's raw-pointer store width per primitive type.
|
||||
//
|
||||
// Each helper allocates a 32-byte buffer through `context.allocator`,
|
||||
// fills it with a sentinel byte (0xAA), writes ONE typed value at
|
||||
// offset 8, then sums every byte back. A correctly-sized store touches
|
||||
// exactly `sizeof(T)` bytes, so the sum equals
|
||||
// 31 * 0xAA + sum-of-bytes-in-the-written-value.
|
||||
// A wrong width (e.g. an 8-byte store at a 1-byte slot) clobbers
|
||||
// neighbors with zeros and the sum drops.
|
||||
//
|
||||
// Each test computes its expected sum at COMPTIME (the value is baked
|
||||
// into a `#run` constant — the interp's `storeAtRawPtr` runs). The
|
||||
// runtime program prints the same checksum computed by codegen
|
||||
// (LLVM-emitted typed stores). The two MUST match — that's the
|
||||
// regression assertion.
|
||||
//
|
||||
// To pin the test: every helper returns its checksum; main prints
|
||||
// "ok" iff every comptime-baked checksum equals the runtime-recomputed
|
||||
// one. Failure prints which width diverged.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
SENTINEL :u8: 0xAA; // 170 — neighbor pattern
|
||||
BUF_SIZE :i64: 32;
|
||||
TARGET :i64: 8; // offset where the typed store lands
|
||||
|
||||
// ── per-width helpers ───────────────────────────────────────────────
|
||||
|
||||
fill :: (buf: [*]u8) {
|
||||
i : i64 = 0;
|
||||
while i < BUF_SIZE { buf[i] = SENTINEL; i += 1; }
|
||||
}
|
||||
|
||||
sum_bytes :: (buf: [*]u8) -> i64 {
|
||||
s : i64 = 0;
|
||||
i : i64 = 0;
|
||||
while i < BUF_SIZE { s += xx buf[i]; i += 1; }
|
||||
s
|
||||
}
|
||||
|
||||
run_u8 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *u8 = xx @buf[TARGET];
|
||||
p.* = 0x42;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_u16 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *u16 = xx @buf[TARGET];
|
||||
p.* = 0x0102;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_u32 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *u32 = xx @buf[TARGET];
|
||||
p.* = 0x01020304;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_u64 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *u64 = xx @buf[TARGET];
|
||||
p.* = 0x0102030405060708;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_i8 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *i8 = xx @buf[TARGET];
|
||||
p.* = 0x42;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_i16 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *i16 = xx @buf[TARGET];
|
||||
p.* = 0x0102;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_i32 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *i32 = xx @buf[TARGET];
|
||||
p.* = 0x01020304;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_i64 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *i64 = xx @buf[TARGET];
|
||||
p.* = 0x0102030405060708;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_bool :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *bool = xx @buf[TARGET];
|
||||
p.* = true;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_f32 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *f32 = xx @buf[TARGET];
|
||||
p.* = 1.0;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
run_f64 :: () -> i64 {
|
||||
buf : [*]u8 = xx libc_malloc(BUF_SIZE);
|
||||
fill(buf);
|
||||
p : *f64 = xx @buf[TARGET];
|
||||
p.* = 1.0;
|
||||
s := sum_bytes(buf);
|
||||
libc_free(xx buf);
|
||||
s
|
||||
}
|
||||
|
||||
// ── comptime-baked expected checksums ───────────────────────────────
|
||||
// `#run` evaluates each helper via the interp, so its
|
||||
// `storeAtRawPtr(addr, val, val_ty)` honors the declared width.
|
||||
|
||||
EXP_U8 :: #run run_u8();
|
||||
EXP_U16 :: #run run_u16();
|
||||
EXP_U32 :: #run run_u32();
|
||||
EXP_U64 :: #run run_u64();
|
||||
EXP_S8 :: #run run_i8();
|
||||
EXP_S16 :: #run run_i16();
|
||||
EXP_S32 :: #run run_i32();
|
||||
EXP_S64 :: #run run_i64();
|
||||
EXP_BOOL :: #run run_bool();
|
||||
EXP_F32 :: #run run_f32();
|
||||
EXP_F64 :: #run run_f64();
|
||||
|
||||
// ── runtime comparison ──────────────────────────────────────────────
|
||||
|
||||
check :: (label: string, got: i64, want: i64) -> bool {
|
||||
if got == want { return true; }
|
||||
print("FAIL {}: comptime={} runtime={}\n", label, want, got);
|
||||
false
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
ok := true;
|
||||
if !check("u8", run_u8(), EXP_U8) { ok = false; }
|
||||
if !check("u16", run_u16(), EXP_U16) { ok = false; }
|
||||
if !check("u32", run_u32(), EXP_U32) { ok = false; }
|
||||
if !check("u64", run_u64(), EXP_U64) { ok = false; }
|
||||
if !check("i8", run_i8(), EXP_S8) { ok = false; }
|
||||
if !check("i16", run_i16(), EXP_S16) { ok = false; }
|
||||
if !check("i32", run_i32(), EXP_S32) { ok = false; }
|
||||
if !check("i64", run_i64(), EXP_S64) { ok = false; }
|
||||
if !check("bool", run_bool(), EXP_BOOL) { ok = false; }
|
||||
if !check("f32", run_f32(), EXP_F32) { ok = false; }
|
||||
if !check("f64", run_f64(), EXP_F64) { ok = false; }
|
||||
if ok { print("ok\n"); return 0; }
|
||||
return 1;
|
||||
}
|
||||
31
examples/comptime/0605-comptime-aggregate-global.sx
Normal file
31
examples/comptime/0605-comptime-aggregate-global.sx
Normal file
@@ -0,0 +1,31 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
// MEM Phase 1.4 regression: an aggregate (struct) returned from a `#run`
|
||||
// initializer must serialize correctly into the static binary, not
|
||||
// silently collapse to a zero-init constant.
|
||||
//
|
||||
// Before Phase 1.4 the LLVM `valueToLLVMConst` only handled int/float/bool
|
||||
// and dropped everything else into `LLVMConstNull` — so a global like
|
||||
// `POINT :: #run make_point();` ended up emitting `{0, 0}` regardless of
|
||||
// what the interp computed. Reading POINT.x would give 0, hiding the bug.
|
||||
//
|
||||
// Also exercises type inference at the `NAME :: #run expr;` binding site:
|
||||
// without an explicit annotation, the global's type is taken from the
|
||||
// comptime expression's return shape (here, `Point`).
|
||||
|
||||
Point :: struct {
|
||||
x: i32;
|
||||
y: i32;
|
||||
}
|
||||
|
||||
make_point :: () -> Point {
|
||||
return Point.{ x = 7, y = 13 };
|
||||
}
|
||||
|
||||
POINT :: #run make_point();
|
||||
|
||||
main :: () -> i32 {
|
||||
print("POINT.x = {}\n", POINT.x);
|
||||
print("POINT.y = {}\n", POINT.y);
|
||||
return 0;
|
||||
}
|
||||
26
examples/comptime/0606-comptime-string-global.sx
Normal file
26
examples/comptime/0606-comptime-string-global.sx
Normal file
@@ -0,0 +1,26 @@
|
||||
// Phase 1.4a — a `#run` that returns a string (or any aggregate
|
||||
// containing a heap-allocated buffer) must serialize correctly into
|
||||
// the static binary. The interp computes the string at build time,
|
||||
// allocating its backing through `context.allocator` (which bottoms
|
||||
// out at libc_malloc in the default context). The serializer reads
|
||||
// the resulting `{addr, len}` aggregate, captures the bytes from
|
||||
// host memory, emits them as a private global byte array, and
|
||||
// rebuilds the aggregate to point at that array.
|
||||
//
|
||||
// Before Phase 1.4a this segfaulted at runtime — the pointer field
|
||||
// in the static const ended up as `i0 0` (malformed) because the
|
||||
// interp's host-address `.int` value can't be lowered as `ptr` by
|
||||
// `LLVMConstInt`.
|
||||
#import "modules/std.sx";
|
||||
|
||||
build_greeting :: () -> string {
|
||||
return concat("hello", " world");
|
||||
}
|
||||
|
||||
GREETING :: #run build_greeting();
|
||||
|
||||
main :: () -> i32 {
|
||||
print("greeting = '{}'\n", GREETING);
|
||||
print("greeting.len = {}\n", GREETING.len);
|
||||
return 0;
|
||||
}
|
||||
31
examples/comptime/0607-comptime-nested-comptime-return.sx
Normal file
31
examples/comptime/0607-comptime-nested-comptime-return.sx
Normal file
@@ -0,0 +1,31 @@
|
||||
// Nested comptime call + return — a comptime fn (`$x: i32`) whose
|
||||
// body contains BOTH a nested comptime call (`print`) AND a
|
||||
// `return X;` statement must lower cleanly. The wrapper fn built
|
||||
// by `createComptimeFunction` saves/restores `inline_return_target`
|
||||
// so it doesn't inherit a slot belonging to the OUTER caller's
|
||||
// basic block. Pre-fix: interp executed the wrapper with a
|
||||
// stale-frame inline-return slot and tripped a null-pointer store
|
||||
// at `storeAtRawPtr`.
|
||||
//
|
||||
// Repro: comptime fn (`$x: i32`) whose body has BOTH a nested
|
||||
// comptime call (`print`) AND a `return X;` statement. Pre-fix:
|
||||
// interp panics. Post-fix: prints "inside" then "n=42".
|
||||
//
|
||||
// The pack-fn variant of the same bug (filed in the original
|
||||
// issue as face 2) was fixed earlier when step-2b moved pack-fn
|
||||
// calls off the inline path into the mono path. Plain comptime
|
||||
// fns stay on the inline path; their fix is the
|
||||
// `createComptimeFunction` state save/restore.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
helper :: ($x: i32) -> i64 {
|
||||
print("inside\n");
|
||||
return 42;
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
n := helper(7);
|
||||
print("n={}\n", n);
|
||||
return 0;
|
||||
}
|
||||
83
examples/comptime/0608-comptime-comptime.sx
Normal file
83
examples/comptime/0608-comptime-comptime.sx
Normal file
@@ -0,0 +1,83 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
add :: (a: i32, b: i32) -> i32 { a + b }
|
||||
|
||||
mul :: (a: i32, b: i32) -> i32 { a * b }
|
||||
|
||||
// #run compile-time constants
|
||||
CT_VAL :: #run add(10, 15);
|
||||
|
||||
CT_MUL :: #run mul(6, 7);
|
||||
|
||||
CT_CHAIN :: #run add(CT_VAL, 5);
|
||||
|
||||
// #run compile-time optional tests
|
||||
|
||||
// #run compile-time optional tests
|
||||
ct_opt_coalesce :: () -> i32 {
|
||||
x: ?i32 = 42;
|
||||
y: ?i32 = null;
|
||||
return (x ?? 0) + (y ?? 99);
|
||||
}
|
||||
|
||||
ct_opt_unwrap :: () -> i32 {
|
||||
x: ?i32 = 77;
|
||||
return x!;
|
||||
}
|
||||
|
||||
ct_opt_guard :: () -> i32 {
|
||||
x: ?i32 = 10;
|
||||
if x == null { return -1; }
|
||||
return x;
|
||||
}
|
||||
|
||||
CT_OPT_COALESCE :: #run ct_opt_coalesce();
|
||||
|
||||
CT_OPT_UNWRAP :: #run ct_opt_unwrap();
|
||||
|
||||
CT_OPT_GUARD :: #run ct_opt_guard();
|
||||
|
||||
// #insert helpers
|
||||
|
||||
// #insert helpers
|
||||
gen_code :: () -> string {
|
||||
return "print(\"insert-ok\\n\");";
|
||||
}
|
||||
|
||||
gen_val :: () -> string {
|
||||
return "print(\"insert-gen: {}\\n\", 42);";
|
||||
}
|
||||
|
||||
// --- Error handling (failable functions: sets, raise/try/catch/or/onfail) ---
|
||||
|
||||
main :: () {
|
||||
|
||||
// ========================================================
|
||||
// 8. COMPILE-TIME
|
||||
// ========================================================
|
||||
print("=== 8. Comptime ===\n");
|
||||
|
||||
// #run constant
|
||||
print("run-const: {}\n", CT_VAL);
|
||||
|
||||
// #run with expression
|
||||
print("run-expr: {}\n", CT_MUL);
|
||||
|
||||
// #run chained dependency
|
||||
print("run-chain: {}\n", CT_CHAIN);
|
||||
|
||||
// #run comptime optionals
|
||||
print("ct-opt-coalesce: {}\n", CT_OPT_COALESCE); // ct-opt-coalesce: 141
|
||||
print("ct-opt-unwrap: {}\n", CT_OPT_UNWRAP); // ct-opt-unwrap: 77
|
||||
print("ct-opt-guard: {}\n", CT_OPT_GUARD); // ct-opt-guard: 10
|
||||
|
||||
// #insert with function
|
||||
#insert gen_code();
|
||||
|
||||
// #insert additional
|
||||
#insert gen_val();
|
||||
}
|
||||
46
examples/comptime/0609-comptime-inline-if.sx
Normal file
46
examples/comptime/0609-comptime-inline-if.sx
Normal file
@@ -0,0 +1,46 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
// --- inline if (compile-time conditionals) ---
|
||||
print("=== inline if ===\n");
|
||||
{
|
||||
// POINTER_SIZE is 8 on desktop (64-bit)
|
||||
inline if POINTER_SIZE == 8 {
|
||||
print("64-bit\n");
|
||||
} else {
|
||||
print("32-bit\n");
|
||||
}
|
||||
|
||||
// OS enum comparison
|
||||
inline if OS == .wasm {
|
||||
print("wasm\n");
|
||||
} else {
|
||||
print("not wasm\n");
|
||||
}
|
||||
|
||||
// != comparison
|
||||
inline if OS != .unknown {
|
||||
print("known os\n");
|
||||
} else {
|
||||
print("unknown os\n");
|
||||
}
|
||||
|
||||
// nested inline if
|
||||
inline if POINTER_SIZE != 4 {
|
||||
inline if OS != .wasm {
|
||||
print("desktop 64-bit\n");
|
||||
} else {
|
||||
print("wasm 64-bit??\n");
|
||||
}
|
||||
}
|
||||
|
||||
// POINTER_SIZE in regular (non-inline) if expression
|
||||
ps := if POINTER_SIZE == 8 then "8" else "4";
|
||||
print("pointer size via if: {}\n", ps);
|
||||
}
|
||||
}
|
||||
23
examples/comptime/0610-comptime-inline-for-const-bound.sx
Normal file
23
examples/comptime/0610-comptime-inline-for-const-bound.sx
Normal file
@@ -0,0 +1,23 @@
|
||||
// `inline for 0..K` with a named-const or constant-foldable bound unrolls at
|
||||
// compile time, just like a literal bound.
|
||||
//
|
||||
// Regression (issue 0083): the inline-for bound folder (`evalComptimeInt`) only
|
||||
// handled literals, comptime cursors, and `<pack>.len`, so `inline for 0..M`
|
||||
// (M a module const) and `inline for 0..(M + 1)` (a const expression) both
|
||||
// failed with "range end is not a compile-time integer". `evalComptimeInt` now
|
||||
// delegates to the single shared const-int evaluator
|
||||
// (`program_index.evalConstIntExpr`), so the inline-for bound and an array
|
||||
// dimension fold the same shapes to the same value.
|
||||
#import "modules/std.sx";
|
||||
|
||||
M :: 3;
|
||||
|
||||
main :: () {
|
||||
s := 0;
|
||||
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; }
|
||||
print("sum 0..(M+1) = {}\n", t); // 0 + 1 + 2 + 3 = 6
|
||||
}
|
||||
14
examples/comptime/0611-comptime-integral-float-inline-for.sx
Normal file
14
examples/comptime/0611-comptime-integral-float-inline-for.sx
Normal file
@@ -0,0 +1,14 @@
|
||||
// An `inline for 0..M` bound accepts an integral float constant — `M :: 3.0`
|
||||
// unrolls the same three iterations as `M :: 3`. The inline-for bound folder
|
||||
// (`evalComptimeInt`) delegates to the shared const-int evaluator, so the
|
||||
// integral-float rule (issue 0083 / F0.4 attempt 8, Agra ruling) applies here
|
||||
// too.
|
||||
#import "modules/std.sx";
|
||||
|
||||
M :: 3.0;
|
||||
|
||||
main :: () {
|
||||
s := 0;
|
||||
inline for 0..M (i) { s += i; }
|
||||
print("sum 0..M = {}\n", s); // 0 + 1 + 2 = 3
|
||||
}
|
||||
24
examples/comptime/0612-comptime-inline-for-range-bounds.sx
Normal file
24
examples/comptime/0612-comptime-inline-for-range-bounds.sx
Normal file
@@ -0,0 +1,24 @@
|
||||
// An `inline for` / `for` range bound is a range ENDPOINT, not a count, so the
|
||||
// count negative-rejection rule does NOT apply to it: negative endpoints are
|
||||
// valid and an empty/inverted range simply runs zero iterations.
|
||||
//
|
||||
// Regression (F0.4 attempt 11, Agra ruling): the spec wrongly lumped inline-for
|
||||
// bounds with counts (array dim / Vector lane / value-param), which reject
|
||||
// negatives. Bounds are exempt — `inline for -2..1` iterates -2,-1,0 and an
|
||||
// integral-float empty range `0..(-2.0)` runs zero iterations. Comptime and
|
||||
// runtime loops must agree.
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () {
|
||||
s := 0;
|
||||
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; }
|
||||
print("for -2..1 sum = {}\n", r); // -2 + -1 + 0 = -3 (runtime parity)
|
||||
|
||||
e := 0;
|
||||
inline for 0..(-2.0) (i) { e += i; }
|
||||
print("inline for 0..(-2.0) sum = {}\n", e); // empty range -> 0 iterations
|
||||
}
|
||||
32
examples/comptime/0613-comptime-print-any-type.sx
Normal file
32
examples/comptime/0613-comptime-print-any-type.sx
Normal file
@@ -0,0 +1,32 @@
|
||||
// Comptime `#run` formatting of an `Any` that holds a `Type`.
|
||||
//
|
||||
// `print("{}", at)` where `at: Any` holds a `Type` value routes through
|
||||
// `format` → `any_to_string`, whose `type := type_of(val)` lowers (for an
|
||||
// `.any` operand) to `struct_get(val, 0)` — reading the Any-Type's tag.
|
||||
// At runtime a `Type` value is the aggregate `{ tag=.any, value=tid }`,
|
||||
// so the read works and the type's name prints. The comptime interpreter
|
||||
// stores a first-class `Type` as a bare `.type_tag`, so the struct_get
|
||||
// must mirror that same `{ .any, tid }` layout — otherwise it bails and
|
||||
// the `#run` truncates. Reflection over the same `Any` (`type_name`,
|
||||
// `type_is_unsigned`) already works; the value-print must match.
|
||||
//
|
||||
// Regression (issue 0096): a comptime `#run` print of an `Any`-held
|
||||
// `Type` silently stopped (omitted `value=` + every later line) yet still
|
||||
// built with exit 0.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
ct_probe :: () {
|
||||
print("before\n");
|
||||
x : u64 = 1;
|
||||
t : Type = type_of(x);
|
||||
at : Any = t;
|
||||
print("name={}\n", type_name(at));
|
||||
print("unsigned={}\n", type_is_unsigned(at));
|
||||
print("value={}\n", at);
|
||||
print("after\n");
|
||||
}
|
||||
|
||||
#run ct_probe();
|
||||
|
||||
main :: () {}
|
||||
30
examples/comptime/0614-comptime-metatype-enum.sx
Normal file
30
examples/comptime/0614-comptime-metatype-enum.sx
Normal file
@@ -0,0 +1,30 @@
|
||||
// Comptime type construction: mint a NEW nominal enum from a `TypeInfo` value
|
||||
// via the `define(declare("E"), info)` primitives, then construct one of its
|
||||
// variants and match on it — exercising that a programmatically-built enum
|
||||
// (with NO backing AST decl) flows through enum codegen unmodified (layout /
|
||||
// construct / match), byte-identical to a hand-written enum.
|
||||
//
|
||||
// The enum has two variants: `value` carrying an i64 payload, and `closed` with
|
||||
// no payload (`payload = void`).
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
E :: define(declare("E"), .enum(.{ variants = .[
|
||||
EnumVariant.{ name = "value", payload = i64 },
|
||||
EnumVariant.{ name = "closed", payload = void },
|
||||
] }));
|
||||
|
||||
main :: () -> i32 {
|
||||
e := E.value(3);
|
||||
if e == {
|
||||
case .value: (v) { print("value {}\n", v); }
|
||||
case .closed: { print("closed\n"); }
|
||||
}
|
||||
|
||||
c : E = .closed;
|
||||
if c == {
|
||||
case .value: (v) { print("value {}\n", v); }
|
||||
case .closed: { print("closed\n"); }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
34
examples/comptime/0615-comptime-metatype-typefn-identity.sx
Normal file
34
examples/comptime/0615-comptime-metatype-typefn-identity.sx
Normal file
@@ -0,0 +1,34 @@
|
||||
// Comptime type construction — identity: a type-fn that builds a type with
|
||||
// `define(declare("Box"), ...)` must memoize by the instantiation's mangled name, so
|
||||
// `Box(i64)` resolved at two INDEPENDENT sites (here: a return type and a
|
||||
// parameter type) is ONE `TypeId`. A value built at one site is therefore
|
||||
// assignable / matchable at the other — nominal identity. If the minted result
|
||||
// were not registered under the mangled instantiation name, the two sites would
|
||||
// mint distinct types and `consume(build())` would be a type error.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
Box :: ($T: Type) -> Type {
|
||||
return define(declare("Box"), .enum(.{ variants = .[
|
||||
EnumVariant.{ name = "some", payload = T },
|
||||
EnumVariant.{ name = "none", payload = void },
|
||||
] }));
|
||||
}
|
||||
|
||||
build :: () -> Box(i64) { // site A resolves Box(i64)
|
||||
return Box(i64).some(7);
|
||||
}
|
||||
|
||||
consume :: (b: Box(i64)) { // site B resolves Box(i64) independently
|
||||
if b == {
|
||||
case .some: (n) { print("some {}\n", n); }
|
||||
case .none: { print("none\n"); }
|
||||
}
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
consume(build()); // typechecks ONLY if site A == site B
|
||||
b : Box(i64) = .none;
|
||||
consume(b);
|
||||
return 0;
|
||||
}
|
||||
30
examples/comptime/0616-comptime-field-type.sx
Normal file
30
examples/comptime/0616-comptime-field-type.sx
Normal file
@@ -0,0 +1,30 @@
|
||||
// Comptime reflection: `field_type($T, i) -> Type` — the type-only field
|
||||
// projection the value-level reflection (`field_value` / `type_of`) couldn't
|
||||
// express.
|
||||
// Reflect a struct's fields by name (`field_name`) AND by type (`field_type`),
|
||||
// and a tagged-union's variant payloads. It folds at lower time, so it composes
|
||||
// inside `type_eq` / `type_name` / any type-arg slot.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
Point :: struct { x: i64; y: f64; on: bool; }
|
||||
|
||||
Msg :: enum { num: i64; tag: bool; quit; }
|
||||
|
||||
main :: () -> i32 {
|
||||
// Struct fields: name + type, position by position.
|
||||
print("Point has {} fields\n", field_count(Point));
|
||||
print(" 0: {} : {}\n", field_name(Point, 0), type_name(field_type(Point, 0)));
|
||||
print(" 1: {} : {}\n", field_name(Point, 1), type_name(field_type(Point, 1)));
|
||||
print(" 2: {} : {}\n", field_name(Point, 2), type_name(field_type(Point, 2)));
|
||||
|
||||
// field_type composes in a type-arg slot (type_eq folds at comptime).
|
||||
print("field 0 is i64: {}\n", type_eq(field_type(Point, 0), i64));
|
||||
print("field 1 is f64: {}\n", type_eq(field_type(Point, 1), f64));
|
||||
|
||||
// Tagged-union variant payloads (the tagless `quit` → void).
|
||||
print("Msg.num payload: {}\n", type_name(field_type(Msg, 0)));
|
||||
print("Msg.tag payload: {}\n", type_name(field_type(Msg, 1)));
|
||||
print("Msg.quit payload: {}\n", type_name(field_type(Msg, 2)));
|
||||
return 0;
|
||||
}
|
||||
38
examples/comptime/0617-comptime-metatype-channel-results.sx
Normal file
38
examples/comptime/0617-comptime-metatype-channel-results.sx
Normal file
@@ -0,0 +1,38 @@
|
||||
// Comptime type construction — channel result types: RecvResult($T) /
|
||||
// TryResult($T), built ENTIRELY in sx library code as type-fns over the
|
||||
// `define(declare(), ...)` primitives (no compiler machinery). A blocking recv
|
||||
// yields a value or a `closed` marker; a non-blocking try-recv adds `empty` —
|
||||
// three states a bool can't express. This locks that they construct and match
|
||||
// like any enum, and that `RecvResult(i64)` is one nominal type across sites
|
||||
// (the type-fn identity path).
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
main :: () -> i32 {
|
||||
r := RecvResult(i64).value(42);
|
||||
if r == {
|
||||
case .value: (v) { print("recv value {}\n", v); }
|
||||
case .closed: { print("recv closed\n"); }
|
||||
}
|
||||
|
||||
rc : RecvResult(i64) = .closed;
|
||||
if rc == {
|
||||
case .value: (v) { print("recv value {}\n", v); }
|
||||
case .closed: { print("recv closed\n"); }
|
||||
}
|
||||
|
||||
t := TryResult(i64).value(7);
|
||||
if t == {
|
||||
case .value: (v) { print("try value {}\n", v); }
|
||||
case .empty: { print("try empty\n"); }
|
||||
case .closed: { print("try closed\n"); }
|
||||
}
|
||||
|
||||
te : TryResult(i64) = .empty;
|
||||
if te == {
|
||||
case .value: (v) { print("try value {}\n", v); }
|
||||
case .empty: { print("try empty\n"); }
|
||||
case .closed: { print("try closed\n"); }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
50
examples/comptime/0618-comptime-metatype-self-reference.sx
Normal file
50
examples/comptime/0618-comptime-metatype-self-reference.sx
Normal file
@@ -0,0 +1,50 @@
|
||||
// Comptime type construction — SELF-REFERENCE: a recursive enum minted via
|
||||
// declare/define. `declare("List")` names a forward type the compiler registers
|
||||
// at compile time, so the body can reference it as `*List` (a pointer to a type
|
||||
// that isn't defined yet — legal, since a pointer needs no layout). `define`
|
||||
// then fills the body in place. A by-VALUE self-reference would be infinite-size
|
||||
// and rejected; `*List` is the idiom.
|
||||
//
|
||||
// Builds a 3-node cons-list (c -> b -> a -> nil), matches through the pointer
|
||||
// payload directly (`if p ==`) and via deref (`n.*`), and counts it recursively.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
make_list :: () -> Type {
|
||||
h := declare("List");
|
||||
return define(h, .enum(.{ variants = .[
|
||||
EnumVariant.{ name = "cons", payload = *List }, // self-reference
|
||||
EnumVariant.{ name = "nil", payload = void },
|
||||
] }));
|
||||
}
|
||||
|
||||
List :: make_list();
|
||||
|
||||
// Recursive traversal: `count` matches `n.*` (deref) into the same nominal type.
|
||||
count :: (n: *List) -> i64 {
|
||||
if n.* == {
|
||||
case .cons: (next) { return 1 + count(next); }
|
||||
case .nil: { return 0; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
a : List = .nil;
|
||||
b : List = .cons(@a);
|
||||
c : List = .cons(@b);
|
||||
|
||||
// Match the payload pointer directly (match auto-derefs).
|
||||
if c == {
|
||||
case .cons: (p) {
|
||||
if p == {
|
||||
case .cons: { print("c -> cons\n"); }
|
||||
case .nil: { print("c -> nil\n"); }
|
||||
}
|
||||
}
|
||||
case .nil: { print("c is nil\n"); }
|
||||
}
|
||||
|
||||
print("len = {}\n", count(@c));
|
||||
return 0;
|
||||
}
|
||||
34
examples/comptime/0619-comptime-metatype-type-info.sx
Normal file
34
examples/comptime/0619-comptime-metatype-type-info.sx
Normal file
@@ -0,0 +1,34 @@
|
||||
// Comptime reflection — `type_info($T)`: reflect a SOURCE enum INTO a `TypeInfo`
|
||||
// value, then feed that value straight back to `define` to mint a byte-identical
|
||||
// copy. This is the inverse of `define`'s decode: `type_info` reads a type's
|
||||
// variants (name + payload type) out of the type table and constructs the same
|
||||
// `.enum(EnumInfo{ variants })` value the `define` examples write by hand.
|
||||
//
|
||||
// Round-trip: `ShapeCopy` is reconstructed purely from `type_info(Shape)` — no
|
||||
// literal variant list — and constructs/matches like the original.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
Shape :: enum {
|
||||
circle: f64;
|
||||
rect: i64;
|
||||
empty;
|
||||
}
|
||||
|
||||
// Reflect Shape → TypeInfo, then reconstruct an identical nominal enum.
|
||||
ShapeCopy :: define(declare("ShapeCopy"), type_info(Shape));
|
||||
|
||||
describe :: (s: ShapeCopy) {
|
||||
if s == {
|
||||
case .circle: (r) { print("circle r={}\n", r); }
|
||||
case .rect: (n) { print("rect n={}\n", n); }
|
||||
case .empty: { print("empty\n"); }
|
||||
}
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
describe(.circle(2.5));
|
||||
describe(.rect(7));
|
||||
describe(.empty);
|
||||
return 0;
|
||||
}
|
||||
41
examples/comptime/0620-comptime-metatype-make-enum.sx
Normal file
41
examples/comptime/0620-comptime-metatype-make-enum.sx
Normal file
@@ -0,0 +1,41 @@
|
||||
// make_enum — the GENERAL comptime enum constructor over declare/define: mint a
|
||||
// nominal enum from a `[]EnumVariant` VALUE, rather than a hardcoded literal
|
||||
// (the channel-result constructors hardcode theirs). The variant list is an
|
||||
// ordinary comptime value, so a builder ASSEMBLES it in a local before minting —
|
||||
// here `build_level` constructs `vs` (a local array, which could be filled
|
||||
// conditionally / in a loop), then mints `Level` from it.
|
||||
//
|
||||
// This exercises `define` decoding a value-arg SLICE (`decodeVariantElements`'s
|
||||
// slice-fat-pointer branch), as opposed to the inline `.[ … ]` array the
|
||||
// 0614–0618 examples pass directly into `.enum(...)`.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
build_level :: () -> Type {
|
||||
// The variant list lives in a local (not inlined into `define`): a non-
|
||||
// generic `() -> Type` builder has its whole body comptime-evaluated, so
|
||||
// `vs` is in scope when `make_enum` mints from it.
|
||||
vs := EnumVariant.[
|
||||
EnumVariant.{ name = "info", payload = void },
|
||||
EnumVariant.{ name = "warn", payload = void },
|
||||
EnumVariant.{ name = "fatal", payload = i64 }, // carries an exit code
|
||||
];
|
||||
return make_enum("Level", vs);
|
||||
}
|
||||
|
||||
Level :: build_level();
|
||||
|
||||
show :: (l: Level) {
|
||||
if l == {
|
||||
case .info: { print("info\n"); }
|
||||
case .warn: { print("warn\n"); }
|
||||
case .fatal: (c) { print("fatal code={}\n", c); }
|
||||
}
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
show(.info);
|
||||
show(.warn);
|
||||
show(.fatal(70));
|
||||
return 0;
|
||||
}
|
||||
30
examples/comptime/0621-comptime-metatype-make-enum-sliced.sx
Normal file
30
examples/comptime/0621-comptime-metatype-make-enum-sliced.sx
Normal file
@@ -0,0 +1,30 @@
|
||||
// Comptime slice over a non-string AGGREGATE: assemble a pool of candidate
|
||||
// variants in a local array, then mint an enum from a SUBSLICE of it. This
|
||||
// exercises the interp's `subslice` op on an array VALUE (`dirs[0..2]`) — which
|
||||
// used to bail ("slice over non-string aggregates not yet supported") — now
|
||||
// yielding a real `[]EnumVariant` the `define` decoder reads.
|
||||
//
|
||||
// `Axis` is built from only the first two of four directions, so `.south` /
|
||||
// `.west` are NOT variants of it.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
build_axis :: () -> Type {
|
||||
dirs := EnumVariant.[
|
||||
EnumVariant.{ name = "north", payload = void },
|
||||
EnumVariant.{ name = "east", payload = i64 }, // carries a bearing
|
||||
EnumVariant.{ name = "south", payload = void },
|
||||
EnumVariant.{ name = "west", payload = void },
|
||||
];
|
||||
return make_enum("Axis", dirs[0..2]); // first two only — a comptime subslice
|
||||
}
|
||||
|
||||
Axis :: build_axis();
|
||||
|
||||
main :: () -> i32 {
|
||||
a : Axis = .north;
|
||||
b : Axis = .east(90);
|
||||
if a == { case .north: { print("north\n"); } case .east: (d) { print("east {}\n", d); } }
|
||||
if b == { case .north: { print("north\n"); } case .east: (d) { print("east {}\n", d); } }
|
||||
return 0;
|
||||
}
|
||||
27
examples/comptime/0622-comptime-metatype-struct.sx
Normal file
27
examples/comptime/0622-comptime-metatype-struct.sx
Normal file
@@ -0,0 +1,27 @@
|
||||
// Comptime STRUCT metaprogramming — `define` constructs a struct (not just an
|
||||
// enum), and `type_info` reflects one. Two paths:
|
||||
// 1. Programmatic build: `define(declare("Vec2"), .struct(.{ fields = … }))`
|
||||
// mints a nominal struct from a `[]StructField` value.
|
||||
// 2. Round-trip: `define(declare("RowCopy"), type_info(Row))` reflects a source
|
||||
// struct INTO a `.struct(StructInfo)` value and reconstructs an identical
|
||||
// one — no literal field list.
|
||||
// Both constructed structs build + read like any hand-written struct.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
Vec2 :: define(declare("Vec2"), .struct(.{ fields = .[
|
||||
StructField.{ name = "x", type = f64 },
|
||||
StructField.{ name = "y", type = f64 },
|
||||
] }));
|
||||
|
||||
Row :: struct { id: i64; score: f64; }
|
||||
RowCopy :: define(declare("RowCopy"), type_info(Row));
|
||||
|
||||
main :: () -> i32 {
|
||||
v := Vec2.{ x = 1.5, y = -2.0 };
|
||||
print("v = {} {}\n", v.x, v.y);
|
||||
|
||||
r := RowCopy.{ id = 42, score = 9.5 };
|
||||
print("r = {} {}\n", r.id, r.score);
|
||||
return 0;
|
||||
}
|
||||
23
examples/comptime/0623-comptime-metatype-tuple.sx
Normal file
23
examples/comptime/0623-comptime-metatype-tuple.sx
Normal file
@@ -0,0 +1,23 @@
|
||||
// Comptime TUPLE metaprogramming — `define` constructs a tuple and `type_info`
|
||||
// reflects one, completing the reflect/construct triad (enum 0619, struct 0622,
|
||||
// tuple here). Tuples are POSITIONAL, so `TupleInfo` is just a `[]Type` (no field
|
||||
// names). Two paths:
|
||||
// 1. Programmatic build: `define(declare("Pair"), .tuple(.{ elements = … }))`.
|
||||
// 2. Round-trip: `define(declare("TripleCopy"), type_info((i64, bool, f64)))`
|
||||
// reflects a source tuple type INTO a `.tuple(TupleInfo)` value and
|
||||
// reconstructs it — no literal element list.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
Pair :: define(declare("Pair"), .tuple(.{ elements = .[ i64, f64 ] }));
|
||||
|
||||
TripleCopy :: define(declare("TripleCopy"), type_info((i64, bool, f64)));
|
||||
|
||||
main :: () -> i32 {
|
||||
p : Pair = .{ 3, 2.5 };
|
||||
print("p = {} {}\n", p.0, p.1);
|
||||
|
||||
t : TripleCopy = .{ 7, true, 1.5 };
|
||||
print("t = {} {} {}\n", t.0, t.1, t.2);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// A GENERIC type-fn (`($T) -> Type`) may use LOCALS before its `return` — the
|
||||
// full body is comptime-evaluated, not just the return expression. Here
|
||||
// `make_status` assembles a variant list in a local `vs` (whose `ok` payload is
|
||||
// the type parameter `T`), then mints `Status` from it via `make_enum`. The
|
||||
// equivalent in a non-generic builder already worked (examples/0620); this
|
||||
// extends it to the generic case.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
make_status :: ($T: Type) -> Type {
|
||||
vs := EnumVariant.[
|
||||
EnumVariant.{ name = "ok", payload = T }, // payload = the type arg
|
||||
EnumVariant.{ name = "pending", payload = void },
|
||||
EnumVariant.{ name = "failed", payload = i64 }, // error code
|
||||
];
|
||||
return make_enum("Status", vs);
|
||||
}
|
||||
|
||||
Status :: make_status(f64);
|
||||
|
||||
show :: (s: Status) {
|
||||
if s == {
|
||||
case .ok: (v) { print("ok={}\n", v); }
|
||||
case .pending: { print("pending\n"); }
|
||||
case .failed: (e) { print("failed code={}\n", e); }
|
||||
}
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
show(.ok(2.5));
|
||||
show(.pending);
|
||||
show(.failed(404));
|
||||
return 0;
|
||||
}
|
||||
23
examples/comptime/0626-comptime-weld-fn-intern-text-of.sx
Normal file
23
examples/comptime/0626-comptime-weld-fn-intern-text-of.sx
Normal file
@@ -0,0 +1,23 @@
|
||||
// Comptime compiler API — welded compiler FUNCTIONS over the host-call bridge.
|
||||
//
|
||||
// `intern` / `text_of` are bound to the `compiler` library via
|
||||
// `abi(.compiler)`. They have no real symbol — under the comptime
|
||||
// interpreter the call dispatches to the compiler's registered Zig handler
|
||||
// (the string pool), never dlsym. Comptime-only: here they run inside `#run`,
|
||||
// folding to a plain string constant the runtime `main` prints.
|
||||
//
|
||||
// Round-trip: `text_of(intern(s)) == s` — interning a string yields a handle,
|
||||
// and resolving the handle gives the text back.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
|
||||
StringId :: u32;
|
||||
intern :: (s: string) -> StringId abi(.compiler);
|
||||
text_of :: (id: StringId) -> string abi(.compiler);
|
||||
|
||||
greeting :: #run text_of(intern("hello, compiler"));
|
||||
|
||||
main :: () {
|
||||
print("{}\n", greeting);
|
||||
}
|
||||
27
examples/comptime/0627-comptime-enum-value-param.sx
Normal file
27
examples/comptime/0627-comptime-enum-value-param.sx
Normal file
@@ -0,0 +1,27 @@
|
||||
// Comptime ENUM value parameter: `$o: <EnumType>` binds the enum-literal
|
||||
// argument to its variant tag, monomorphizes the inlined body per distinct
|
||||
// ordering value, and resolves `o` in the body as a compile-time-known enum
|
||||
// constant — usable both in `if o == .a` comparisons AND as a comptime-readable
|
||||
// variant tag during lowering (a downstream lowerer reads it via
|
||||
// `comptime_value_bindings`, exercised here as the `[o]i64` array dimension).
|
||||
#import "modules/std.sx";
|
||||
|
||||
Ord :: enum { a; b; c; }
|
||||
|
||||
pick :: ($o: Ord) -> i64 {
|
||||
if o == .a { return 10; }
|
||||
if o == .b { return 20; }
|
||||
return 30;
|
||||
}
|
||||
|
||||
// `o` read as a compile-time integer (its variant tag) in a TYPE position.
|
||||
tag_dim :: ($o: Ord) -> i64 {
|
||||
arr : [o]i64 = ---;
|
||||
return arr.len;
|
||||
}
|
||||
|
||||
main :: () {
|
||||
print("{}\n", pick(.b)); // 20
|
||||
print("{} {} {}\n", pick(.a), pick(.b), pick(.c)); // 10 20 30
|
||||
print("{} {} {}\n", tag_dim(.a), tag_dim(.b), tag_dim(.c)); // 0 1 2
|
||||
}
|
||||
36
examples/comptime/0628-comptime-compiler-find-type.sx
Normal file
36
examples/comptime/0628-comptime-compiler-find-type.sx
Normal file
@@ -0,0 +1,36 @@
|
||||
// Comptime compiler API — read-only reflection readers (Phase 3).
|
||||
//
|
||||
// `find_type` / `type_field_count` are bound to the `compiler` library via
|
||||
// `abi(.compiler)`, joining the `intern` / `text_of` seed. They are
|
||||
// the first REFLECTION readers: the compiler exposes its own type table to
|
||||
// comptime sx as plain handles (a `TypeId` is a u32, like a `StringId`), so the
|
||||
// calls are clean scalar host-calls — handle in, scalar out, no marshaling.
|
||||
//
|
||||
// find_type(name) → the named type's handle (0 / `unresolved` if absent)
|
||||
// type_field_count(t) → its member count (struct fields here)
|
||||
//
|
||||
// Comptime-only: they run inside `#run`, folding to plain int constants the
|
||||
// runtime `main` prints. Chains `intern` → `find_type` → `type_field_count`.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
|
||||
StringId :: u32;
|
||||
TypeId :: u32;
|
||||
|
||||
intern :: (s: string) -> StringId abi(.compiler);
|
||||
find_type :: (name: StringId) -> TypeId abi(.compiler);
|
||||
type_field_count :: (t: TypeId) -> i64 abi(.compiler);
|
||||
|
||||
Point :: struct { x: i64; y: i64; z: i64; }
|
||||
|
||||
// Look the struct up by name and count its fields, all at comptime.
|
||||
point_fields :: #run type_field_count(find_type(intern("Point")));
|
||||
|
||||
// A name with no matching type folds to the `unresolved` sentinel (0).
|
||||
missing_id :: #run find_type(intern("NoSuchType"));
|
||||
|
||||
main :: () {
|
||||
print("Point has {} fields\n", point_fields);
|
||||
print("missing type id = {}\n", missing_id);
|
||||
}
|
||||
44
examples/comptime/0629-comptime-compiler-field-reflect.sx
Normal file
44
examples/comptime/0629-comptime-compiler-field-reflect.sx
Normal file
@@ -0,0 +1,44 @@
|
||||
// Comptime compiler API — field-level reflection readers (Phase 3).
|
||||
//
|
||||
// Builds on the `find_type` / `type_field_count` readers (example 0628) with the
|
||||
// per-member readers, all on the same plain-`u32`-handle shape (scalar in,
|
||||
// handle out — no marshaling):
|
||||
//
|
||||
// type_field_name(t, i) → the i-th member's name handle (StringId)
|
||||
// type_field_type(t, i) → the i-th member's type handle (TypeId)
|
||||
// type_nominal_name(t) → a named type's own name handle (StringId)
|
||||
//
|
||||
// Reflecting `Pair { lo: Point; hi: Point; }`: read each field's name, and the
|
||||
// nominal name of each field's type. Chains
|
||||
// intern → find_type → type_field_{name,type} → (type_nominal_name) → text_of,
|
||||
// all folded at comptime, all serviced natively by the flat-memory VM.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
|
||||
StringId :: u32;
|
||||
TypeId :: u32;
|
||||
|
||||
intern :: (s: string) -> StringId abi(.compiler);
|
||||
text_of :: (id: StringId) -> string abi(.compiler);
|
||||
find_type :: (name: StringId) -> TypeId abi(.compiler);
|
||||
type_field_count :: (t: TypeId) -> i64 abi(.compiler);
|
||||
type_nominal_name :: (t: TypeId) -> StringId abi(.compiler);
|
||||
type_field_name :: (t: TypeId, idx: i64) -> StringId abi(.compiler);
|
||||
type_field_type :: (t: TypeId, idx: i64) -> TypeId abi(.compiler);
|
||||
|
||||
Point :: struct { x: i64; y: i64; }
|
||||
Pair :: struct { lo: Point; hi: Point; }
|
||||
|
||||
pair :: #run find_type(intern("Pair"));
|
||||
n :: #run type_field_count(find_type(intern("Pair")));
|
||||
f0_name :: #run text_of(type_field_name(find_type(intern("Pair")), 0));
|
||||
f1_name :: #run text_of(type_field_name(find_type(intern("Pair")), 1));
|
||||
// field 0's type is `Point` — read its nominal name through the type handle.
|
||||
f0_type :: #run text_of(type_nominal_name(type_field_type(find_type(intern("Pair")), 0)));
|
||||
|
||||
main :: () {
|
||||
print("Pair has {} fields\n", n);
|
||||
print("field 0 = {} : {}\n", f0_name, f0_type);
|
||||
print("field 1 = {} : {}\n", f1_name, f0_type);
|
||||
}
|
||||
46
examples/comptime/0630-comptime-compiler-type-kind.sx
Normal file
46
examples/comptime/0630-comptime-compiler-type-kind.sx
Normal file
@@ -0,0 +1,46 @@
|
||||
// Comptime compiler API — type-kind + enum-value reflection readers (Phase 3).
|
||||
//
|
||||
// Completes the READ side the metatype needs to re-express `type_info(T)` as sx:
|
||||
//
|
||||
// type_kind(t) → a stable kind discriminant, to branch on the shape
|
||||
// (0 other · 1 struct · 2 enum · 3 tagged_union ·
|
||||
// 4 tuple · 5 union · 6 array · 7 vector · 8 error_set)
|
||||
// type_field_value(t, i) → enum variant i's integer value (explicit or ordinal)
|
||||
//
|
||||
// Together with find_type / type_field_count / type_field_name / type_field_type
|
||||
// / type_nominal_name (examples 0628–0629), a comptime sx function can now fully
|
||||
// reflect a struct/enum/tuple into data — no `#builtin` needed. All folded at
|
||||
// `#run`, all serviced natively by the flat-memory VM.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
|
||||
StringId :: u32;
|
||||
TypeId :: u32;
|
||||
|
||||
intern :: (s: string) -> StringId abi(.compiler);
|
||||
text_of :: (id: StringId) -> string abi(.compiler);
|
||||
find_type :: (name: StringId) -> TypeId abi(.compiler);
|
||||
type_kind :: (t: TypeId) -> i64 abi(.compiler);
|
||||
type_field_name :: (t: TypeId, idx: i64) -> StringId abi(.compiler);
|
||||
type_field_value :: (t: TypeId, idx: i64) -> i64 abi(.compiler);
|
||||
|
||||
Color :: enum { red; green; blue; }
|
||||
WindowFlags :: enum flags u32 { vsync :: 64; resizable :: 4; hidden :: 128; }
|
||||
Point :: struct { x: i64; y: i64; }
|
||||
|
||||
color_kind :: #run type_kind(find_type(intern("Color"))); // 2 = enum
|
||||
point_kind :: #run type_kind(find_type(intern("Point"))); // 1 = struct
|
||||
|
||||
// Plain enum → ordinal values.
|
||||
green_val :: #run type_field_value(find_type(intern("Color")), 1); // 1
|
||||
|
||||
// Flags enum → explicit values, read by name + value.
|
||||
vsync_name :: #run text_of(type_field_name(find_type(intern("WindowFlags")), 0)); // "vsync"
|
||||
vsync_val :: #run type_field_value(find_type(intern("WindowFlags")), 0); // 64
|
||||
|
||||
main :: () {
|
||||
print("Color kind = {}, Point kind = {}\n", color_kind, point_kind);
|
||||
print("Color.green = {}\n", green_val);
|
||||
print("WindowFlags.{} = {}\n", vsync_name, vsync_val);
|
||||
}
|
||||
87
examples/comptime/0631-comptime-compiler-register-graph.sx
Normal file
87
examples/comptime/0631-comptime-compiler-register-graph.sx
Normal file
@@ -0,0 +1,87 @@
|
||||
// Comptime compiler API — the WRITE side: one kind-branching `register_type`
|
||||
// minting an actual enum AND a graph of mutually-recursive types (Phase 3).
|
||||
//
|
||||
// `declare_type` / `pointer_to` / `register_type` are bound to the `compiler`
|
||||
// library. They MINT into the type table, so they run at LOWERING time (lazily,
|
||||
// on demand) — when a `-> Type` builder's result is first referenced — where the
|
||||
// compiler still resolves references to the new types. (`#run` is too late: it
|
||||
// runs at emit time, after the type table is frozen.) They take/return real
|
||||
// `Type` values (like the metatype's declare/define), and `register_type`
|
||||
// branches on the `kind` arg IN THE COMPILER — the codes match the read-side
|
||||
// `type_kind`: 1 struct · 2 enum · 3 tagged_union · 4 tuple.
|
||||
//
|
||||
// Suit :: enum { hearts; spades; diamonds; } (actual, payloadless)
|
||||
// GraphA :: enum { self_ref: *A; to_b: B; tag: u32; } (payloads → tagged_union)
|
||||
// GraphB :: enum { back_a: *A; self_b: *B; num: u32; }
|
||||
//
|
||||
// Forward `declare_type` handles + `pointer_to` make the A<->B cycle expressible
|
||||
// before either body is filled.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
|
||||
Member :: struct { name: string; ty: Type; }
|
||||
|
||||
StringId :: u32;
|
||||
TypeId :: u32;
|
||||
|
||||
intern :: (s: string) -> StringId abi(.compiler);
|
||||
find_type :: (name: StringId) -> TypeId abi(.compiler);
|
||||
type_kind :: (t: TypeId) -> i64 abi(.compiler);
|
||||
declare_type :: (name: string) -> Type abi(.compiler);
|
||||
pointer_to :: (t: Type) -> Type abi(.compiler);
|
||||
register_type :: (handle: Type, kind: i64, members: []Member) -> Type abi(.compiler);
|
||||
|
||||
KIND_ENUM :: 2; // an ACTUAL payloadless enum
|
||||
KIND_TAGGED_UNION :: 3; // a payload-carrying enum
|
||||
|
||||
// An actual enum: variants are names, no payloads (ty = void).
|
||||
make_suit :: () -> Type {
|
||||
return register_type(declare_type("Suit"), KIND_ENUM, .[
|
||||
Member.{ name = "hearts", ty = void },
|
||||
Member.{ name = "spades", ty = void },
|
||||
Member.{ name = "diamonds", ty = void },
|
||||
]);
|
||||
}
|
||||
Suit :: make_suit();
|
||||
|
||||
// The mutually-recursive A <-> B graph (payload variants → tagged_union).
|
||||
build_graph :: () -> Type {
|
||||
hA := declare_type("GraphA");
|
||||
hB := declare_type("GraphB");
|
||||
register_type(hA, KIND_TAGGED_UNION, .[
|
||||
Member.{ name = "self_ref", ty = pointer_to(hA) }, // *A — self-reference
|
||||
Member.{ name = "to_b", ty = hB }, // B by value (forward)
|
||||
Member.{ name = "tag", ty = u32 }, // a plain payload
|
||||
]);
|
||||
register_type(hB, KIND_TAGGED_UNION, .[
|
||||
Member.{ name = "back_a", ty = pointer_to(hA) }, // *A — back-reference
|
||||
Member.{ name = "self_b", ty = pointer_to(hB) }, // *B — self-reference
|
||||
Member.{ name = "num", ty = u32 },
|
||||
]);
|
||||
return hA;
|
||||
}
|
||||
GraphA :: build_graph();
|
||||
|
||||
// Reflect the minted types (read side, at #run) to confirm their kinds.
|
||||
suit_kind :: #run type_kind(find_type(intern("Suit"))); // 2 = actual enum
|
||||
grapha_kind :: #run type_kind(find_type(intern("GraphA"))); // 3 = tagged_union
|
||||
|
||||
main :: () -> i32 {
|
||||
// Suit is a real, usable enum.
|
||||
s := Suit.spades;
|
||||
if s == {
|
||||
case .hearts: { print("hearts\n"); }
|
||||
case .spades: { print("spades\n"); }
|
||||
case .diamonds: { print("diamonds\n"); }
|
||||
}
|
||||
// GraphA is a real, usable tagged union.
|
||||
a := GraphA.tag(7);
|
||||
if a == {
|
||||
case .tag: (n) { print("tag={}\n", n); }
|
||||
case .self_ref: (p) { print("self_ref\n"); }
|
||||
case .to_b: (b) { print("to_b\n"); }
|
||||
}
|
||||
print("Suit kind={}, GraphA kind={}\n", suit_kind, grapha_kind);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Regression (issue 0142): a comptime-minted FULLY payloadless enum (every
|
||||
// variant tagless) must mint as a real `.@"enum"`, not an all-void tagged_union
|
||||
// — the latter has an IR/LLVM size mismatch that tripped `verifySizes` at
|
||||
// codegen. `make_enum` (declare/define) with an all-void variant list now
|
||||
// produces an ordinary enum, usable like a hand-written one.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
make_suit :: () -> Type {
|
||||
return make_enum("Suit", EnumVariant.[
|
||||
EnumVariant.{ name = "hearts", payload = void },
|
||||
EnumVariant.{ name = "spades", payload = void },
|
||||
EnumVariant.{ name = "diamonds", payload = void },
|
||||
]);
|
||||
}
|
||||
Suit :: make_suit();
|
||||
|
||||
show :: (s: Suit) {
|
||||
if s == {
|
||||
case .hearts: { print("hearts\n"); }
|
||||
case .spades: { print("spades\n"); }
|
||||
case .diamonds: { print("diamonds\n"); }
|
||||
}
|
||||
}
|
||||
|
||||
main :: () {
|
||||
show(.spades); // leading-dot, typed context
|
||||
x := Suit.diamonds; // qualified construction
|
||||
show(x);
|
||||
}
|
||||
15
examples/comptime/0633-comptime-compiler-namespaced-type.sx
Normal file
15
examples/comptime/0633-comptime-compiler-namespaced-type.sx
Normal file
@@ -0,0 +1,15 @@
|
||||
// A comptime-minted type (built via the compiler API in `shapes.sx`) reached
|
||||
// through a NAMESPACED import: `s.Suit`, with qualified variant construction
|
||||
// `s.Suit.spades`. The bare-import form is example 0634; in-file minting +
|
||||
// reflection is 0631.
|
||||
|
||||
#import "modules/std.sx";
|
||||
s :: #import "0633-comptime-compiler-namespaced-type/shapes.sx";
|
||||
|
||||
main :: () {
|
||||
x := s.Suit.spades;
|
||||
if x == {
|
||||
case .hearts: { print("hearts\n"); }
|
||||
case .spades: { print("spades\n"); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Reaches shapes.sx via a NAMESPACED import; re-exports a helper over its Suit.
|
||||
#import "modules/std.sx";
|
||||
s :: #import "shapes.sx";
|
||||
|
||||
name_of :: (x: s.Suit) -> string {
|
||||
if x == { case .hearts: { return "hearts"; } case .spades: { return "spades"; } }
|
||||
return "?";
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// A module that MINTS a comptime enum via the compiler API and exports it.
|
||||
#import "modules/std.sx";
|
||||
|
||||
|
||||
Member :: struct { name: string; ty: Type; }
|
||||
declare_type :: (name: string) -> Type abi(.compiler);
|
||||
register_type :: (handle: Type, kind: i64, members: []Member) -> Type abi(.compiler);
|
||||
|
||||
build_suit :: () -> Type {
|
||||
return register_type(declare_type("Suit"), 2, .[ // kind 2 = actual enum
|
||||
Member.{ name = "hearts", ty = void },
|
||||
Member.{ name = "spades", ty = void },
|
||||
]);
|
||||
}
|
||||
|
||||
Suit :: build_suit();
|
||||
14
examples/comptime/0634-comptime-compiler-bare-import-type.sx
Normal file
14
examples/comptime/0634-comptime-compiler-bare-import-type.sx
Normal file
@@ -0,0 +1,14 @@
|
||||
// A comptime-minted type (built via the compiler API in 0633's `shapes.sx`)
|
||||
// reached through a BARE import: the minted `Suit` is in scope flat, with
|
||||
// qualified variant construction `Suit.spades`. Namespaced form is 0633.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "0633-comptime-compiler-namespaced-type/shapes.sx";
|
||||
|
||||
main :: () {
|
||||
x := Suit.spades;
|
||||
if x == {
|
||||
case .hearts: { print("hearts\n"); }
|
||||
case .spades: { print("spades\n"); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// A comptime-minting module (0633's `shapes.sx`) reached via TWO import edges in
|
||||
// one build: directly (bare) here, and indirectly through `indirect.sx` (which
|
||||
// imports it namespaced as `s`). The minted `Suit` must be ONE type across both
|
||||
// edges — `name_of` (typed `s.Suit` in indirect.sx) accepts the bare `Suit`
|
||||
// constructed here. Exercises that re-evaluating the type-fn across import paths
|
||||
// is idempotent (same TypeId), not a re-mint conflict.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "0633-comptime-compiler-namespaced-type/shapes.sx"; // bare edge → `Suit`
|
||||
#import "0633-comptime-compiler-namespaced-type/indirect.sx"; // edge via `s :: shapes`
|
||||
|
||||
main :: () {
|
||||
a := Suit.spades; // bare Suit
|
||||
print("{}\n", name_of(a)); // passed where indirect expects s.Suit (same type)
|
||||
b := Suit.hearts;
|
||||
print("{}\n", name_of(b));
|
||||
}
|
||||
17
examples/comptime/0636-comptime-extern-libc.sx
Normal file
17
examples/comptime/0636-comptime-extern-libc.sx
Normal file
@@ -0,0 +1,17 @@
|
||||
// Comptime host-FFI: a `#run` that calls real libc functions (`toupper`/`tolower`)
|
||||
// at compile time. The comptime VM resolves the symbol via dlsym and dispatches
|
||||
// through the host_ffi trampolines (Phase 4D) — a scalar arg in, a scalar return
|
||||
// out — folding the result into a constant. (The legacy interpreter does the same
|
||||
// via its own dlsym path; both agree.)
|
||||
#import "modules/std.sx";
|
||||
|
||||
toupper :: (c: i32) -> i32 extern libc;
|
||||
tolower :: (c: i32) -> i32 extern libc;
|
||||
|
||||
UP :: #run toupper(97); // 'a' -> 'A' = 65
|
||||
LO :: #run tolower(90); // 'Z' -> 'z' = 122
|
||||
|
||||
main :: () -> i32 {
|
||||
print("toupper(97)={} tolower(90)={}\n", UP, LO);
|
||||
return 0;
|
||||
}
|
||||
14
examples/comptime/0637-comptime-extern-slice-arg.sx
Normal file
14
examples/comptime/0637-comptime-extern-slice-arg.sx
Normal file
@@ -0,0 +1,14 @@
|
||||
// Comptime host-FFI with a SLICE argument: a `#run` calling a libc function whose
|
||||
// parameter is a `[:0]u8` (a `{ptr,len}` fat pointer), not a bare `cstring`. The VM
|
||||
// marshals the fat pointer to a NUL-terminated `char*` before the call (Phase 4D.2),
|
||||
// mirroring the legacy interpreter. (A bare `cstring` arg already passes as a word.)
|
||||
#import "modules/std.sx";
|
||||
|
||||
strlen :: (s: [:0]u8) -> usize extern libc;
|
||||
|
||||
LEN :: #run strlen("hello, world");
|
||||
|
||||
main :: () -> i32 {
|
||||
print("len={}\n", LEN);
|
||||
return 0;
|
||||
}
|
||||
16
examples/comptime/0638-comptime-domain-fn-not-emitted.sx
Normal file
16
examples/comptime/0638-comptime-domain-fn-not-emitted.sx
Normal file
@@ -0,0 +1,16 @@
|
||||
// A BODIED `abi(.compiler)` function is a compiler-domain function: the comptime
|
||||
// evaluator runs its sx body, but it is NEVER lowered into the shipped binary
|
||||
// (emit_llvm skips it, like an extern). Here `double` folds the `#run` const at
|
||||
// compile time; `main` only ever sees the folded constant, and no `double` symbol
|
||||
// exists in the binary. (Regression for the S3 step of the abi(.compiler) work —
|
||||
// see current/PLAN-COMPILER-VM.md.)
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
double :: (x: i64) -> i64 abi(.compiler) { x * 2 }
|
||||
|
||||
answer :: #run double(42);
|
||||
|
||||
main :: () {
|
||||
print("answer = {}\n", answer);
|
||||
}
|
||||
20
examples/comptime/0639-comptime-bitwise-shift.sx
Normal file
20
examples/comptime/0639-comptime-bitwise-shift.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
// Comptime bitwise + shift ops on the VM: AND/OR/XOR/NOT and shl/shr all fold
|
||||
// at compile time. Regression for the VM op port (P5.6) — these were unported in
|
||||
// `comptime_vm` and bailed (`shr` surfaced via the iOS-device bundler). `shr` is
|
||||
// an arithmetic right shift (sign-extending), mirroring the legacy interp's i64
|
||||
// model; the shift amount clamps to 63.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
AND :: 0b1100 & 0b1010; // 0b1000 = 8
|
||||
OR :: 0b1100 | 0b1010; // 0b1110 = 14
|
||||
XOR :: 0b1100 ^ 0b1010; // 0b0110 = 6
|
||||
NOT :: ~0; // -1
|
||||
SHL :: 1 << 10; // 1024
|
||||
SHR :: 1024 >> 3; // 128
|
||||
ASHR :: (-8) >> 1; // -4 (arithmetic, sign-extending)
|
||||
|
||||
main :: () {
|
||||
print("and={} or={} xor={} not={}\n", AND, OR, XOR, NOT);
|
||||
print("shl={} shr={} ashr={}\n", SHL, SHR, ASHR);
|
||||
}
|
||||
38
examples/comptime/0640-comptime-list-grown-variant-define.sx
Normal file
38
examples/comptime/0640-comptime-list-grown-variant-define.sx
Normal file
@@ -0,0 +1,38 @@
|
||||
// Comptime type construction from a List grown at compile time: assemble the
|
||||
// variant set in a `List(EnumVariant)` via `.append` (which allocates + grows
|
||||
// its backing through the comptime `context.allocator`), then mint an enum from
|
||||
// the grown backing sliced to its length — `vs.items[0..vs.len]`. The comptime
|
||||
// VM evaluates the List growth, the slice, and `define`/`declare` end-to-end.
|
||||
//
|
||||
// A `[*]T` many-pointer (the List's bare `items` field) carries no length, so it
|
||||
// must be sliced with `[0..len]` to form a `[]T`; passing `vs.items` bare is a
|
||||
// rejected coercion (see the diagnostic example for that path).
|
||||
//
|
||||
// Regression (issue 0141): the List-grown form used to segfault in the comptime
|
||||
// VM's slice decoder.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
make_enum :: (name: string, variants: []EnumVariant) -> Type {
|
||||
return define(declare(name), .enum(.{ variants = variants }));
|
||||
}
|
||||
|
||||
build_color :: () -> Type {
|
||||
vs : List(EnumVariant) = .{};
|
||||
vs.append(EnumVariant.{ name = "red", payload = void });
|
||||
vs.append(EnumVariant.{ name = "green", payload = i64 });
|
||||
vs.append(EnumVariant.{ name = "blue", payload = void });
|
||||
return make_enum("Color", vs.items[0..vs.len]);
|
||||
}
|
||||
|
||||
Color :: build_color();
|
||||
|
||||
main :: () -> i32 {
|
||||
c : Color = .green(7);
|
||||
if c == {
|
||||
case .red: { print("red\n"); }
|
||||
case .green: (v) { print("green={}\n", v); }
|
||||
case .blue: { print("blue\n"); }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
43
examples/comptime/0640-comptime-tagged-union-value-param.sx
Normal file
43
examples/comptime/0640-comptime-tagged-union-value-param.sx
Normal file
@@ -0,0 +1,43 @@
|
||||
// Comptime TAGGED-UNION value parameter: `$s: <TaggedUnion>` generalizes the
|
||||
// enum value-param mechanism (examples/0627) to a payload-bearing enum. The
|
||||
// argument is a constant variant literal — either bare (`.point`) or with a
|
||||
// payload (`.circle(5.0)`) — and binds the param so it resolves in the body:
|
||||
// * `if s == .circle` lowers as an ordinary tag comparison,
|
||||
// * the variant tag is comptime-readable in a TYPE position (`[s]i64`),
|
||||
// * a payload read off the bound value (`s.rect`) resolves to the
|
||||
// compile-time-known payload.
|
||||
// Distinct variant args monomorphize the inlined body per value.
|
||||
#import "modules/std.sx";
|
||||
|
||||
Shape :: enum {
|
||||
circle: f64;
|
||||
rect: i64;
|
||||
point;
|
||||
}
|
||||
|
||||
// Tag comparison in the body — bare AND payload variants both bind.
|
||||
classify :: ($s: Shape) -> i64 {
|
||||
if s == .circle { return 1; }
|
||||
if s == .rect { return 2; }
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Variant tag read as a compile-time integer in a TYPE position (the array
|
||||
// dimension), exercising the `comptime_value_bindings` / `comptimeIntNamed`
|
||||
// integration contract for a tagged union.
|
||||
tag_dim :: ($s: Shape) -> i64 {
|
||||
arr : [s]i64 = ---;
|
||||
return arr.len;
|
||||
}
|
||||
|
||||
// Payload read off the bound comptime value.
|
||||
rect_payload :: ($s: Shape) -> i64 {
|
||||
if s == .rect { return s.rect; }
|
||||
return -1;
|
||||
}
|
||||
|
||||
main :: () {
|
||||
print("{} {} {}\n", classify(.circle(5.0)), classify(.rect(7)), classify(.point)); // 1 2 3
|
||||
print("{} {} {}\n", tag_dim(.circle(0.0)), tag_dim(.rect(0)), tag_dim(.point)); // 0 1 2
|
||||
print("{}\n", rect_payload(.rect(42))); // 42
|
||||
}
|
||||
36
examples/comptime/0641-comptime-empty-types-valid.sx
Normal file
36
examples/comptime/0641-comptime-empty-types-valid.sx
Normal file
@@ -0,0 +1,36 @@
|
||||
// A comptime-constructed type with NO members is VALID for every kind:
|
||||
// - an empty struct `struct {}` and empty tuple `()` are zero-size aggregates
|
||||
// you can instantiate (`.{}`),
|
||||
// - an empty enum and an empty tagged_union are valid uninhabited / zero-member
|
||||
// types — legitimate to NAME and reference even though they have no
|
||||
// constructible value (no variant to construct).
|
||||
//
|
||||
// This mirrors normal sx, where `struct {}` and `enum {}` already codegen fine;
|
||||
// the metatype `define`/`declare` path now agrees (the old blanket
|
||||
// "a type with no members is never valid" rejection is gone).
|
||||
//
|
||||
// The ONLY thing still rejected on this path is a bare `declare("X")` that is
|
||||
// never completed by a matching `define` — an INCOMPLETE forward slot that would
|
||||
// panic codegen. That case is exercised by examples/1179.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/meta.sx";
|
||||
|
||||
// Explicitly-defined empty types of every kind.
|
||||
EmptyStruct :: define(declare("EmptyStruct"), .struct(.{ fields = .[] }));
|
||||
EmptyTuple :: define(declare("EmptyTuple"), .tuple(.{ elements = .[] }));
|
||||
EmptyEnum :: define(declare("EmptyEnum"), .enum(.{ variants = .[] }));
|
||||
// An empty tagged_union (kind 3): no variants, but a valid named type. (The
|
||||
// `define` DSL maps an all-void variant set to a payloadless enum, so reach for
|
||||
// the register_type primitive directly to mint a 0-variant tagged_union.)
|
||||
EmptyUnion :: register_type(declare("EmptyUnion"), 3, .[]);
|
||||
|
||||
main :: () -> i32 {
|
||||
// Instantiate the constructible ones.
|
||||
s : EmptyStruct = .{};
|
||||
t : EmptyTuple = .{};
|
||||
_ = s;
|
||||
_ = t;
|
||||
// EmptyEnum / EmptyUnion are uninhabited — valid as types, no value to make.
|
||||
print("empty struct/tuple/enum/tagged_union are all valid\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Comptime VALUE params bind on methods of GENERIC structs.
|
||||
//
|
||||
// A free function's `$o: Ord` comptime value param binds via
|
||||
// lowerComptimeCall → bindComptimeValueParams (see 0627 / 0640). The
|
||||
// generic-struct-instance method path (`b.pick(.b)`) takes a different
|
||||
// dispatch route: the struct monomorphizes for `T`, but the method's `$o`
|
||||
// must STILL bind by inlining the body — a plain call to the monomorphized
|
||||
// FuncId would leave `o` unresolved. This locks the composition: `T` is bound
|
||||
// from the struct instantiation, `$o` from the comptime value arg, and a
|
||||
// `self.field` read through the pointer receiver works inside the inlined body.
|
||||
//
|
||||
// Regression: composition gap — comptime value params on generic-struct methods.
|
||||
#import "modules/std.sx";
|
||||
|
||||
Ord :: enum { a; b; c; }
|
||||
|
||||
Box :: struct ($T: Type) {
|
||||
value: i64;
|
||||
// Enum comptime value param: `if o == .x` lowers as a tag comparison; the
|
||||
// receiver `self` is bound so `self.value` reads correctly.
|
||||
pick :: (self: *Box(T), $o: Ord) -> i64 {
|
||||
if o == .a { return self.value + 1; }
|
||||
if o == .b { return self.value + 2; }
|
||||
return self.value + 3;
|
||||
}
|
||||
// The bound tag is also readable in a TYPE position ([o]i64), via
|
||||
// comptimeIntNamed — the same accessor the atomics stream uses for
|
||||
// `Atomic($T).load($o)`.
|
||||
size_for :: (self: *Box(T), $o: Ord) -> i64 {
|
||||
arr : [o]i64 = ---;
|
||||
return arr.len;
|
||||
}
|
||||
}
|
||||
|
||||
Shape :: enum { circle: f64; point; }
|
||||
|
||||
Drawer :: struct ($T: Type) {
|
||||
// Tagged-union comptime value param on a generic-struct method.
|
||||
area :: (self: *Drawer(T), $s: Shape) -> i64 {
|
||||
if s == .circle { return 1; }
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
main :: () {
|
||||
b := Box(i64).{ value = 10 };
|
||||
print("{}\n", b.pick(.a)); // 11
|
||||
print("{}\n", b.pick(.b)); // 12
|
||||
print("{}\n", b.pick(.c)); // 13
|
||||
print("{}\n", b.size_for(.a)); // tag 0 → [0]i64 → 0
|
||||
print("{}\n", b.size_for(.b)); // tag 1 → [1]i64 → 1
|
||||
print("{}\n", b.size_for(.c)); // tag 2 → [2]i64 → 2
|
||||
|
||||
d := Drawer(i64).{};
|
||||
print("{}\n", d.area(.circle(2.0))); // 1
|
||||
print("{}\n", d.area(.point)); // 0
|
||||
}
|
||||
1
examples/comptime/expected/0600-comptime-run.exit
Normal file
1
examples/comptime/expected/0600-comptime-run.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/comptime/expected/0600-comptime-run.stderr
Normal file
1
examples/comptime/expected/0600-comptime-run.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
3
examples/comptime/expected/0600-comptime-run.stdout
Normal file
3
examples/comptime/expected/0600-comptime-run.stdout
Normal file
@@ -0,0 +1,3 @@
|
||||
hello 25
|
||||
--- build done ---
|
||||
hello 25
|
||||
1
examples/comptime/expected/0601-comptime-meta.exit
Normal file
1
examples/comptime/expected/0601-comptime-meta.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/comptime/expected/0601-comptime-meta.stderr
Normal file
1
examples/comptime/expected/0601-comptime-meta.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
4
examples/comptime/expected/0601-comptime-meta.stdout
Normal file
4
examples/comptime/expected/0601-comptime-meta.stdout
Normal file
@@ -0,0 +1,4 @@
|
||||
f64
|
||||
3.200000
|
||||
Vector(4,f32)
|
||||
() -> i32
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
--- build done ---
|
||||
rt
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
--- build done ---
|
||||
rt
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ok
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
POINT.x = 7
|
||||
POINT.y = 13
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
greeting = 'hello world'
|
||||
greeting.len = 11
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
inside
|
||||
n=42
|
||||
1
examples/comptime/expected/0608-comptime-comptime.exit
Normal file
1
examples/comptime/expected/0608-comptime-comptime.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/comptime/expected/0608-comptime-comptime.stderr
Normal file
1
examples/comptime/expected/0608-comptime-comptime.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
9
examples/comptime/expected/0608-comptime-comptime.stdout
Normal file
9
examples/comptime/expected/0608-comptime-comptime.stdout
Normal file
@@ -0,0 +1,9 @@
|
||||
=== 8. Comptime ===
|
||||
run-const: 25
|
||||
run-expr: 42
|
||||
run-chain: 30
|
||||
ct-opt-coalesce: 141
|
||||
ct-opt-unwrap: 77
|
||||
ct-opt-guard: 10
|
||||
insert-ok
|
||||
insert-gen: 42
|
||||
1
examples/comptime/expected/0609-comptime-inline-if.exit
Normal file
1
examples/comptime/expected/0609-comptime-inline-if.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
=== inline if ===
|
||||
64-bit
|
||||
not wasm
|
||||
known os
|
||||
desktop 64-bit
|
||||
pointer size via if: 8
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
sum 0..M = 3
|
||||
sum 0..(M+1) = 6
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
sum 0..M = 3
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
inline for -2..1 sum = -3
|
||||
for -2..1 sum = -3
|
||||
inline for 0..(-2.0) sum = 0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
before
|
||||
name=u64
|
||||
unsigned=true
|
||||
value=u64
|
||||
after
|
||||
--- build done ---
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
value 3
|
||||
closed
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
some 7
|
||||
none
|
||||
1
examples/comptime/expected/0616-comptime-field-type.exit
Normal file
1
examples/comptime/expected/0616-comptime-field-type.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
Point has 3 fields
|
||||
0: x : i64
|
||||
1: y : f64
|
||||
2: on : bool
|
||||
field 0 is i64: true
|
||||
field 1 is f64: true
|
||||
Msg.num payload: i64
|
||||
Msg.tag payload: bool
|
||||
Msg.quit payload: void
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
recv value 42
|
||||
recv closed
|
||||
try value 7
|
||||
try empty
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user