This commit is contained in:
agra
2026-03-02 21:00:55 +02:00
parent 2f4f898d54
commit bbb5426777
42 changed files with 483 additions and 9023 deletions

View File

@@ -0,0 +1,45 @@
#import "modules/std.sx";
#import "modules/compiler.sx";
// --- #run build configuration ---
// build_options() returns a BuildOptions struct at compile time.
// Methods on it (add_link_flag, set_output_path) are compiler builtins
// that configure the build without runtime cost.
configure_build :: () {
opts := build_options();
// These calls are intercepted by the compiler at compile time.
// On a normal (non-wasm) target, inline if gates them off.
inline if OS == .wasm {
opts.set_output_path("sx-out/wasm/test.html");
opts.add_link_flag("-sUSE_SDL=3");
}
}
#run configure_build();
// --- inline if with compiler constants ---
main :: () {
// Verify #run configure_build() executed without error
print("build config: ok\n");
// Verify compiler constants are available
print("pointer size: {}\n", POINTER_SIZE);
// Verify inline if with OS/ARCH works
inline if OS == {
case .macos: { print("os: macos\n"); }
case .linux: { print("os: linux\n"); }
case .windows: { print("os: windows\n"); }
case .wasm: { print("os: wasm\n"); }
else: { print("os: unknown\n"); }
}
// Verify POINTER_SIZE is usable in inline if
inline if POINTER_SIZE == 8 {
print("64-bit platform\n");
}
inline if POINTER_SIZE == 4 {
print("32-bit platform\n");
}
}

View File

@@ -1,25 +0,0 @@
#import "modules/std.sx";
// Issue: nested field assignment through pointer
// self.inner.field = value should work when self is a pointer
Inner :: struct {
len: s64;
cap: s64;
}
Outer :: struct {
inner: Inner;
count: s64;
reset :: (self: *Outer) {
self.inner.len = 0; // error: field assignment target must be a variable
self.count += 1;
}
}
main :: () {
o := Outer.{ inner = Inner.{ len = 5, cap = 10 }, count = 0 };
o.reset();
print("{}\n", o.inner.len);
}

View File

@@ -1,25 +0,0 @@
// Issue: enum literal inference in match expression used as assignment RHS
// When a match expression is assigned to a field with a known enum type,
// the enum literals in case arms should infer their type from the assignment target.
Color :: enum {
red;
green;
blue;
none;
}
Thing :: struct {
color: Color;
}
main :: () {
t : Thing = ---;
value : u8 = 1;
t.color = if value == {
case 1: .red; // error: cannot infer enum type for literal
case 2: .green;
case 3: .blue;
else: .none;
};
}

View File

@@ -1,18 +0,0 @@
#import "modules/std.sx";
Color :: struct {
r, g, b, a: u8;
}
COLOR_WHITE :: Color.{ r = 255, g = 255, b = 255, a = 255 };
// Additional case: struct constant with enum-typed fields
HAlign :: enum { leading; center; trailing; }
VAlign :: enum { top; center; bottom; }
Alignment :: struct {
h: HAlign;
v: VAlign;
}
ALIGN_CENTER :: Alignment.{ h = .center, v = .center };

View File

@@ -1,20 +0,0 @@
// Issue: top-level constants from imported files are not visible
// COLOR_WHITE works after fix, but ALIGN_CENTER (struct with enum fields) does not.
// Error: undefined identifier 'ALIGN_CENTER'
#import "modules/std.sx";
#import "examples/issue-0004-defs.sx";
Thing :: struct {
color: Color;
alignment: Alignment;
make :: () -> Thing {
Thing.{ color = COLOR_WHITE, alignment = ALIGN_CENTER };
}
}
main :: () {
t := Thing.make();
print("{}\n", t.color.r);
}

View File

@@ -1,23 +0,0 @@
// Issue: match on u8 value with enum result assigned to typed field
// The switch value is u8 but case constants are s64 (default int literal type).
// Compiler should cast case constants to match the switch value type.
// LLVM error: Switch constants must all be same type as switch value!
out :: (str: string) -> void #builtin;
Button :: enum {
none;
left;
middle;
right;
}
main :: () {
val : u8 = 2;
result : Button = if val == {
case 1: .left;
case 2: .middle;
case 3: .right;
else: .none;
};
}

View File

@@ -1,39 +0,0 @@
// Issue: chained method call on struct field operates on a copy
// `a.field.method()` where method takes *Self creates a temporary copy of `field`
// instead of borrowing `a.field` as a pointer.
// The mutation is lost because it modifies the copy, not the original.
out :: (str: string) -> void #builtin;
Counter :: struct {
value: s64;
inc :: (self: *Counter) {
self.value += 1;
}
}
Parent :: struct {
counter: Counter;
}
main :: () {
p := Parent.{ counter = Counter.{ value = 0 } };
// This should increment p.counter.value, but the mutation is lost:
p.counter.inc();
if p.counter.value == 0 {
out("BUG: p.counter.value is still 0 after inc()\n");
} else {
out("OK: p.counter.value is 1\n");
}
// Workaround: take explicit pointer
cp := @p.counter;
cp.inc();
if p.counter.value == 1 {
out("OK: workaround via pointer works\n");
}
}

View File

@@ -1,26 +0,0 @@
// Issue 0008: Chained ?? (null coalescing) doesn't work
//
// `a ?? b ?? c` where a: ?f32, b: ?f32, c: f32 fails with:
// "narrowing conversion from '?f32' to 'f32' requires explicit 'xx' cast"
//
// It parses as (a ?? b) ?? c, and the first ?? rejects ?f32 as the rhs.
//
// Expected: ?? should either be right-associative so it parses as a ?? (b ?? c),
// or allow ?T as the rhs (returning ?T when rhs is optional, T when rhs is concrete).
//
// Workaround: use parentheses — a ?? (b ?? c)
Foo :: struct {
x: ?f32;
y: ?f32;
}
main :: () -> void {
f := Foo.{ x = 1.0, y = 2.0 };
// This works:
ok := f.x ?? (f.y ?? 0.0);
// This should also work but fails:
bad := f.x ?? f.y ?? 0.0;
}

View File

@@ -1,20 +0,0 @@
// Issue 0009: Struct-level constant declarations
//
// Constants declared inside a struct body with `NAME :Type: value;` syntax
// fail with "expected field name in struct".
//
// Expected: structs should support constant declarations alongside fields and methods.
Foo :: struct {
x: f32;
// This method works:
get_x :: (self: *Foo) -> f32 { self.x; }
// This constant should work but fails:
DEFAULT_X :f32: 42.0;
}
main :: () -> void {
f := Foo.{ x = Foo.DEFAULT_X };
}

View File

@@ -1,38 +0,0 @@
// Issue 0010: inline if-else in struct literal field produces type error
// The `null` branch is typed as `*void` instead of being coerced to `?f32`
//
// Error: narrowing conversion from '*void' to 'f32' requires explicit 'xx' cast
#import "modules/std.sx";
Foo :: struct {
width: ?f32;
}
main :: () -> void {
x :f32: 10.0;
// null in then branch, value in else
f1 := Foo.{ width = if true then null else x };
print("{}\n", f1.width ?? 99.0);
// value in then branch, null in else
f2 := Foo.{ width = if true then x else null };
print("{}\n", f2.width ?? 99.0);
// both branches are values
f3 := Foo.{ width = if false then 5.0 else x };
print("{}\n", f3.width ?? 99.0);
// standalone variable, not just struct fields
val: ?f32 = if true then null else 42.0;
print("{}\n", val ?? 0.0);
val2: ?f32 = if false then null else 42.0;
print("{}\n", val2 ?? 0.0);
// negation in condition
cond := false;
val3: ?f32 = if !cond then null else 42.0;
print("{}\n", val3 ?? 0.0);
}

View File

@@ -1,41 +0,0 @@
#import "modules/std.sx";
// Forward references: types, struct fields, methods, and free functions
// can reference types declared later in the file.
// Free function referencing types declared later
make_frame :: (e: EdgeInsets) -> Frame {
Frame.{ x = e.left, y = e.top,
w = 100.0 - e.left - e.right,
h = 100.0 - e.top - e.bottom };
}
// Struct with a field whose type is declared later
Container :: struct {
frame: Frame;
insets: EdgeInsets;
}
Frame :: struct {
x, y, w, h: f32;
inset :: (self: Frame, insets: EdgeInsets) -> Frame {
Frame.{ x = self.x + insets.left, y = self.y + insets.top,
w = self.w - insets.left - insets.right,
h = self.h - insets.top - insets.bottom };
}
}
EdgeInsets :: struct {
top, left, bottom, right: f32;
}
main :: () {
e := EdgeInsets.{ top = 10.0, left = 10.0, bottom = 10.0, right = 10.0 };
f := make_frame(e);
r := f.inset(e);
c := Container.{ frame = f, insets = e };
print("{}", r.x);
print(" {}", r.w);
print(" {}", c.frame.x);
}

View File

@@ -1,6 +1,19 @@
OperatingSystem :: enum { macos; linux; windows; wasm; unknown; }
Architecture :: enum { aarch64; x86_64; wasm32; unknown; }
Architecture :: enum { aarch64; x86_64; wasm32; wasm64; unknown; }
OS : OperatingSystem = .unknown;
ARCH : Architecture = .unknown;
POINTER_SIZE : s64 = 8;
BuildOptions :: struct {
add_link_flag :: (self: BuildOptions, flag: [:0]u8) {
// Compiler builtin — intercepted at compile time
}
set_output_path :: (self: BuildOptions, path: [:0]u8) {
// Compiler builtin — intercepted at compile time
}
}
build_options :: () -> BuildOptions {
return BuildOptions.{};
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
#import "modules/std.sx";
Phys :: struct {
x, y: f32;
GRAVITY :f32: 9.81;
MAX_SPEED :: 100;
}
main :: () {
print("gravity: {}\n", Phys.GRAVITY);
print("max speed: {}\n", Phys.MAX_SPEED);
}

View File

@@ -1,80 +0,0 @@
#import "modules/std.sx";
main :: () {
n1 : s32 = 1;
n2 : s32 = 2;
n3 : s32 = 3;
f1 := closure((x: s32) -> s32 => x + n1);
f2 := closure((x: s32) -> s32 => x + n2);
f3 := closure((x: s32) -> s32 => x + n3);
print("f1: {}\n", f1(10));
print("f2: {}\n", f2(10));
print("f3: {}\n", f3(10));
// closure struct field
Button :: struct {
label: string;
on_press: Closure(s32) -> void;
}
btn_val := 99;
btn_cb := closure((id: s32) {
print("btn: {} {}\n", id, btn_val);
});
btn := Button.{ label = "OK", on_press = btn_cb };
btn.on_press(1);
// optional closure
f_none : ?Closure(s64) -> s64 = null;
if f_none != null { print("should not print\n"); }
else { print("opt-closure: none\n"); }
// closure factory
make_adder :: (n: s32) -> Closure(s32) -> s32 {
closure((x: s32) -> s32 => x + n);
}
add5 := make_adder(5);
add10 := make_adder(10);
print("factory: {} {}\n", add5(100), add10(100));
// HOF compose
compose :: (f: Closure(s32) -> s32, g: Closure(s32) -> s32) -> Closure(s32) -> s32 {
closure((x: s32) -> s32 => f(g(x)));
}
double :: (x: s32) -> s32 { return x * 2; }
cf := compose(add5, double);
print("compose: {}\n", cf(10));
// closure with array
sort_bubble :: (arr: [*]s32, cnt: s64, less: Closure(s32, s32) -> bool) {
i : s64 = 0;
while i < cnt {
j : s64 = 0;
while j < cnt - 1 {
if less(arr[j + 1], arr[j]) {
tmp := arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
j += 1;
}
i += 1;
}
}
sort_arr : [5]s32 = .[5, 3, 1, 4, 2];
sort_bubble(xx @sort_arr, 5, closure((a: s32, b: s32) -> bool {
return a < b;
}));
print("sort: {} {} {} {} {}\n", sort_arr[0], sort_arr[1], sort_arr[2], sort_arr[3], sort_arr[4]);
// Many closures with string captures
tag1 := "hello";
tag2 := "world";
sf1 := closure((x: s32) { print("sf1: {} {}\n", tag1, x); });
sf2 := closure((x: s32) { print("sf2: {} {}\n", tag2, x); });
sf1(1);
sf2(2);
print("=== DONE ===\n");
}

View File

@@ -1,8 +0,0 @@
#import "modules/std.sx";
main :: () {
n := 42;
f := closure((x: s64) -> s64 { x + n; });
r := f(10);
print("r: {}\n", r);
}

View File

@@ -1,5 +0,0 @@
#import "modules/std.sx";
greet :: () -> string { format("hello"); }
main :: () {
print("{}\n", greet());
}

View File

@@ -1,6 +0,0 @@
#import "modules/std.sx";
main :: () -> s32 {
v := Vector(3,f32).[1,2,3];
print("{}
", v);
}

View File

@@ -1,24 +0,0 @@
#import "modules/std.sx";
Point :: struct { x: s32; y: s32; }
Eq :: protocol {
eq :: (other: Self) -> bool;
}
impl Eq for Point {
eq :: (self: *Point, other: Point) -> bool {
self.x == other.x and self.y == other.y;
}
}
are_equal :: ($T: Type/Eq, a: T, b: T) -> bool {
a.eq(b);
}
main :: () {
p1 := Point.{ x = 1, y = 2 };
p2 := Point.{ x = 1, y = 2 };
p3 := Point.{ x = 3, y = 4 };
print("P6.1: {} {}\n", are_equal(p1, p2), are_equal(p1, p3));
}

View File

@@ -1,5 +0,0 @@
#import "modules/std.sx";
main :: () {
s := "";
print("{}\n", s);
}

View File

@@ -1,14 +0,0 @@
#import "modules/std.sx";
Vec2 :: union {
data: [2]f32;
struct { x, y: f32; };
}
main :: () {
uv : Vec2 = ---;
uv.x = 1.0;
uv.y = 2.0;
print("promoted-x: {}\n", uv.x);
print("promoted-data0: {}\n", uv.data[0]);
}