optionals

This commit is contained in:
agra
2026-02-22 22:16:30 +02:00
parent d3e574eae5
commit 1cc67f9b5a
17 changed files with 1952 additions and 32 deletions

View File

@@ -16,7 +16,7 @@ main :: () {
ptr := @v;
copy := ptr.*;
print("copy: {}\n", copy);
// null pointer
np : *Vec2 = null;

97
examples/32-optionals.sx Normal file
View File

@@ -0,0 +1,97 @@
#import "modules/std.sx";
// --- Type declarations ---
OptNode :: struct { value: s32; next: ?s32; }
OptInner :: struct { val: s32; }
OptOuter :: struct { inner: ?OptInner; }
// --- Comptime optionals ---
ct_sum :: () -> s32 {
x: ?s32 = 42;
y: ?s32 = null;
return (x ?? 0) + (y ?? 99);
}
CT_RESULT :: #run ct_sum();
main :: () -> s32 {
// Basic optional creation
x: ?s32 = 42;
y: ?s32 = null;
print("x = {}\n", x);
print("y = {}\n", y);
// Force unwrap
print("x! = {}\n", x!);
// Null coalescing
print("x ?? 0 = {}\n", x ?? 0);
print("y ?? 99 = {}\n", y ?? 99);
// If-binding (safe unwrap)
if val := x {
print("if-bind x: {}\n", val);
}
if val := y {
print("should not print\n");
} else {
print("if-bind y: none\n");
}
// Pattern matching
check :: (v: ?s32) -> s32 {
return if v == {
case .some: (val) { val; }
case .none: { 0; }
};
}
print("match some: {}\n", check(42));
print("match none: {}\n", check(null));
// Optional chaining
p: ?OptNode = OptNode.{ value = 10, next = 20 };
q: ?OptNode = null;
print("p?.value = {}\n", p?.value ?? 0);
print("q?.value = {}\n", q?.value ?? 0);
// Deep chaining
o1 := OptOuter.{ inner = OptInner.{ val = 99 } };
o2 := OptOuter.{ inner = null };
print("o1.inner?.val = {}\n", o1.inner?.val ?? 0);
print("o2.inner?.val = {}\n", o2.inner?.val ?? 0);
// Flow-sensitive narrowing
a: ?s32 = 10;
b: ?s32 = 20;
if a != null {
print("narrowed a: {}\n", a);
}
// Guard narrowing
guard :: (v: ?s32) -> s32 {
if v == null { return 0; }
return v;
}
print("guard 42: {}\n", guard(42));
print("guard null: {}\n", guard(null));
// Compound narrowing
if a != null and b != null {
print("both: {} {}\n", a, b);
}
// Compound guard
guard2 :: (a: ?s32, b: ?s32) -> s32 {
if a == null or b == null { return 0; }
return a + b;
}
print("guard2: {}\n", guard2(3, 4));
// Struct field defaults
n := OptNode.{ value = 10 };
print("default next: {}\n", n.next);
// Comptime result
print("comptime: {}\n", CT_RESULT);
return 0;
}

View File

@@ -1,4 +1,5 @@
#import "modules/std.sx";
#import "modules/math";
pkg :: #import "modules/testpkg";
// ============================================================
@@ -33,6 +34,14 @@ Defaults :: struct {
c: s32 = ---;
}
OptNode :: struct {
value: s32;
next: ?s32;
}
OptInner :: struct { val: s32; }
OptOuter :: struct { inner: ?OptInner; }
MyFloat :: f64;
Perms :: enum flags { read; write; execute; }
@@ -86,6 +95,25 @@ CT_VAL :: #run add(10, 15);
CT_MUL :: #run mul(6, 7);
CT_CHAIN :: #run add(CT_VAL, 5);
// #run compile-time optional tests
ct_opt_coalesce :: () -> s32 {
x: ?s32 = 42;
y: ?s32 = null;
return (x ?? 0) + (y ?? 99);
}
ct_opt_unwrap :: () -> s32 {
x: ?s32 = 77;
return x!;
}
ct_opt_guard :: () -> s32 {
x: ?s32 = 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
gen_code :: () -> string {
return "print(\"insert-ok\\n\");";
@@ -1019,6 +1047,11 @@ END;
// #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();
@@ -1480,5 +1513,211 @@ END;
}
}
// ========================================================
// OPTIONALS
// ========================================================
print("--- optionals ---\n");
// Basic optional creation and null
{
x: ?s32 = 42;
y: ?s32 = null;
print("opt x: {}\n", x); // opt x: 42
print("opt y: {}\n", y); // opt y: null
}
// Force unwrap
{
x: ?s32 = 10;
val := x!;
print("unwrap: {}\n", val); // unwrap: 10
}
// Null coalescing
{
x: ?s32 = 42;
y: ?s32 = null;
a := x ?? 0;
b := y ?? 99;
print("coalesce a: {}\n", a); // coalesce a: 42
print("coalesce b: {}\n", b); // coalesce b: 99
}
// If-binding (safe unwrap)
{
x: ?s32 = 7;
y: ?s32 = null;
if val := x {
print("if-bind x: {}\n", val); // if-bind x: 7
}
if val := y {
print("if-bind y: should not print\n");
} else {
print("if-bind y: none\n"); // if-bind y: none
}
}
// Pattern matching on optionals
{
check :: (v: ?s32) -> s32 {
return if v == {
case .some: (val) { val; }
case .none: { 0; }
};
}
a: ?s32 = 55;
b: ?s32 = null;
print("match some: {}\n", check(a)); // match some: 55
print("match none: {}\n", check(b)); // match none: 0
}
// Optional with implicit wrapping
{
opt_wrap :: (n: s32) -> ?s32 {
if n > 0 {
return n;
}
return null;
}
r1 := opt_wrap(5);
r2 := opt_wrap(0);
print("wrap pos: {}\n", r1); // wrap pos: 5
print("wrap neg: {}\n", r2); // wrap neg: null
}
// Struct field defaults for ?T
{
n := OptNode.{ value = 10 };
print("opt field default: {}\n", n.next); // opt field default: null
m := OptNode.{ value = 20, next = 42 };
print("opt field set: {}\n", m.next); // opt field set: 42
}
// ?T as function parameter
{
opt_process :: (val: ?s32) -> s32 {
return val ?? 0;
}
a: ?s32 = 42;
b: ?s32 = null;
print("opt param a: {}\n", opt_process(a)); // opt param a: 42
print("opt param b: {}\n", opt_process(b)); // opt param b: 0
print("opt param 7: {}\n", opt_process(7)); // opt param 7: 7
}
// Generic function with ?T return
{
first_pos :: ($T: Type, a: T, b: T) -> ?T {
if a > 0 { return a; }
if b > 0 { return b; }
return null;
}
print("generic opt 1: {}\n", first_pos(s32, 5, 10)); // generic opt 1: 5
print("generic opt 2: {}\n", first_pos(s32, 0, 7)); // generic opt 2: 7
print("generic opt 3: {}\n", first_pos(s32, 0, 0)); // generic opt 3: null
}
// Optional chaining (?.)
{
p: ?OptNode = OptNode.{ value = 10, next = 20 };
q: ?OptNode = null;
print("chain some: {}\n", p?.value ?? 0); // chain some: 10
print("chain none: {}\n", q?.value ?? 0); // chain none: 0
print("chain print: {}\n", p?.next); // chain print: 20
print("chain null: {}\n", q?.next); // chain null: null
// Chained: obj.field?.field
o1 := OptOuter.{ inner = OptInner.{ val = 99 } };
o2 := OptOuter.{ inner = null };
print("deep chain 1: {}\n", o1.inner?.val ?? 0); // deep chain 1: 99
print("deep chain 2: {}\n", o2.inner?.val ?? 0); // deep chain 2: 0
}
// Flow-sensitive narrowing
{
x: ?s32 = 42;
y: ?s32 = null;
// if x != null → x is narrowed to s32
if x != null {
print("narrow x: {}\n", x); // narrow x: 42
}
// if y != null → not entered
if y != null {
print("should not print\n");
} else {
print("narrow y else: null\n"); // narrow y else: null
}
// if x == null ... else → else-branch narrowed
if x == null {
print("should not print\n");
} else {
print("narrow else x: {}\n", x); // narrow else x: 42
}
}
// Guard narrowing
{
guard_fn :: (v: ?s32) -> s32 {
if v == null { return 0; }
return v;
}
print("guard some: {}\n", guard_fn(42)); // guard some: 42
print("guard none: {}\n", guard_fn(null)); // guard none: 0
}
// Compound narrowing: && chains
{
a: ?s32 = 10;
b: ?s32 = 20;
c: ?s32 = null;
if a != null and b != null {
print("and both: {} {}\n", a, b); // and both: 10 20
}
if a != null and c != null {
print("should not print\n");
} else {
print("and one null\n"); // and one null
}
}
// Compound guard narrowing: || chains
{
guard2 :: (a: ?s32, b: ?s32) -> s32 {
if a == null or b == null { return 0; }
return a + b;
}
print("or guard: {}\n", guard2(3, 4)); // or guard: 7
print("or guard null: {}\n", guard2(3, null)); // or guard null: 0
}
// Nested if narrowing
{
a: ?s32 = 10;
b: ?s32 = 20;
if a != null {
if b != null {
print("nested narrow: {} {}\n", a, b); // nested narrow: 10 20
}
}
}
// Guard narrowing used in loop
{
guard_loop :: (v: ?s32) -> s32 {
if v == null { return 0; }
sum := 0;
i := 0;
while i < v {
sum = sum + 1;
i = i + 1;
}
return sum;
}
print("guard loop: {}\n", guard_loop(3)); // guard loop: 3
}
print("=== DONE ===\n");
}