optionals
This commit is contained in:
@@ -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
97
examples/32-optionals.sx
Normal 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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user