feat: multi-instance closures, richer stdlib, and Rust-style diagnostics

Introduce fat function pointers (BuxFn {code, env}) so capturing closures
are heap-allocated per value in both bootstrap and selfhost. Expand
Array/Map/Set/String/Test/Result APIs, add proper tuple codegen and
error snippets with multi-char underlines, golden diagnostic tests, and
LSP diagnostics via buxc check.
This commit is contained in:
2026-07-15 16:00:21 +03:00
parent 94e6806dda
commit 61ac06ab5f
48 changed files with 2789 additions and 362 deletions
+46
View File
@@ -0,0 +1,46 @@
// Array_Contains / IndexOf / Extend + Iter_Collect / AnyEq
import Std::Io::{PrintLine, PrintInt};
import Std::Array::{
Array, Array_New, Array_Push, Array_Contains, Array_IndexOf,
Array_Extend, Array_Len, Array_Get, Array_Free
};
import Std::Iter::{Array_Iter, Iter, Iter_Collect, Iter_AnyEq, Iter_AllEq, Iter_HasNext, Iter_Next};
import Std::Test::{Test_AssertTrue, Test_AssertFalse, Test_AssertEqInt, Test_Pass};
func Main() -> int {
var a: Array<int> = Array_New<int>(4);
Array_Push<int>(&a, 10);
Array_Push<int>(&a, 20);
Array_Push<int>(&a, 30);
Test_AssertTrue(Array_Contains<int>(&a, 20));
Test_AssertFalse(Array_Contains<int>(&a, 99));
Test_AssertEqInt(Array_IndexOf<int>(&a, 30), 2);
Test_AssertEqInt(Array_IndexOf<int>(&a, 99), -1);
var b: Array<int> = Array_New<int>(2);
Array_Push<int>(&b, 40);
Array_Push<int>(&b, 50);
Array_Extend<int>(&a, &b);
Test_AssertEqInt(Array_Len<int>(&a) as int, 5);
Test_AssertEqInt(Array_Get<int>(&a, 4), 50);
var it: Iter<int> = Array_Iter<int>(&a);
Test_AssertTrue(Iter_AnyEq<int>(&it, 10));
Test_AssertFalse(Iter_AllEq<int>(&it, 10));
// Skip first two via advancing, collect rest
discard Iter_Next<int>(&it);
discard Iter_Next<int>(&it);
var rest: Array<int> = Iter_Collect<int>(&it);
Test_AssertEqInt(Array_Len<int>(&rest) as int, 3);
Test_AssertEqInt(Array_Get<int>(&rest, 0), 30);
Array_Free<int>(&a);
Array_Free<int>(&b);
Array_Free<int>(&rest);
PrintLine("array_iter_extra: ok");
Test_Pass("array + iter extras");
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
// Function pointer types: func(T) -> U
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};
func Apply(f: func(int) -> int, x: int) -> int {
return f(x);
}
func Double(n: int) -> int {
return n * 2;
}
func Inc(n: int) -> int {
return n + 1;
}
func Main() -> int {
let g: func(int) -> int = Double;
Test_AssertEqInt(Apply(g, 21), 42);
Test_AssertEqInt(Apply(Double, 7), 14);
Test_AssertEqInt(Apply(Inc, 99), 100);
PrintInt(Apply(Double, 5));
PrintLine("");
Test_Pass("func_ptr");
return 0;
}
+60
View File
@@ -0,0 +1,60 @@
// Map_Remove / Map_Clear / Set_Remove
import Std::Io::{PrintLine, PrintInt};
import Std::Map::{Map, Map_New, Map_Set, Map_Get, Map_Has, Map_Remove, Map_Clear, Map_Len, Map_IsEmpty, Map_Free};
import Std::Set::{Set, Set_New, Set_Add, Set_Has, Set_Remove, Set_Len, Set_IsEmpty, Set_Free};
import Std::Test::{Test_AssertTrue, Test_AssertFalse, Test_AssertEqInt, Test_Pass};
import Std::Result::{Result, Result_NewOk, Result_NewErr, Result_IsOk, Result_IsErr, Result_UnwrapOr, Result_Or, Result_UnwrapErr};
import Std::Option::{Option, Option_NewSome, Option_NewNone, Option_IsSome, Option_Or, Option_UnwrapOr};
import Std::String::{String_Eq};
func Main() -> int {
// --- Map ---
var m: Map<int, int> = Map_New<int, int>(16);
Map_Set<int, int>(&m, 1, 100);
Map_Set<int, int>(&m, 2, 200);
Map_Set<int, int>(&m, 3, 300);
Test_AssertEqInt(Map_Len<int, int>(&m) as int, 3);
Test_AssertTrue(Map_Has<int, int>(&m, 2));
Test_AssertTrue(Map_Remove<int, int>(&m, 2));
Test_AssertFalse(Map_Has<int, int>(&m, 2));
Test_AssertEqInt(Map_Len<int, int>(&m) as int, 2);
Test_AssertEqInt(Map_Get<int, int>(&m, 1), 100);
Test_AssertEqInt(Map_Get<int, int>(&m, 3), 300);
Test_AssertFalse(Map_Remove<int, int>(&m, 99));
Map_Clear<int, int>(&m);
Test_AssertTrue(Map_IsEmpty<int, int>(&m));
Map_Free<int, int>(&m);
// --- Set ---
var s: Set<int> = Set_New<int>(16);
Set_Add<int>(&s, 10);
Set_Add<int>(&s, 20);
Set_Add<int>(&s, 30);
Test_AssertTrue(Set_Remove<int>(&s, 20));
Test_AssertFalse(Set_Has<int>(&s, 20));
Test_AssertTrue(Set_Has<int>(&s, 10));
Test_AssertEqInt(Set_Len<int>(&s) as int, 2);
Test_AssertFalse(Set_IsEmpty<int>(&s));
Set_Free<int>(&s);
// --- Result helpers ---
let ok: Result = Result_NewOk(42);
let err: Result = Result_NewErr("boom");
Test_AssertTrue(Result_IsOk(ok));
Test_AssertTrue(Result_IsErr(err));
Test_AssertEqInt(Result_UnwrapOr(err, -1), -1);
let recovered: Result = Result_Or(err, Result_NewOk(7));
Test_AssertEqInt(Result_UnwrapOr(recovered, 0), 7);
Test_AssertTrue(String_Eq(Result_UnwrapErr(err), "boom"));
// --- Option helpers ---
let some: Option = Option_NewSome(5);
let none: Option = Option_NewNone();
Test_AssertTrue(Option_IsSome(some));
let o2: Option = Option_Or(none, some);
Test_AssertEqInt(Option_UnwrapOr(o2, 0), 5);
PrintLine("map_remove: ok");
Test_Pass("map_remove + result helpers");
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
// Multi-instance closures: each capturing closure gets its own heap env
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};
func MakeAdder(base: int) -> func(int) -> int {
return |a: int| -> int {
return a + base;
};
}
func Apply(f: func(int) -> int, x: int) -> int {
return f(x);
}
func Main() -> int {
let a10: func(int) -> int = MakeAdder(10);
let a20: func(int) -> int = MakeAdder(20);
// Independent instances — not a single global env
Test_AssertEqInt(a10(1), 11);
Test_AssertEqInt(a20(1), 21);
Test_AssertEqInt(a10(5), 15);
Test_AssertEqInt(Apply(a20, 3), 23);
// Capture-less still works
let add: func(int, int) -> int = |x: int, y: int| -> int {
return x + y;
};
Test_AssertEqInt(add(2, 3), 5);
PrintInt(a10(1));
PrintLine("");
PrintInt(a20(1));
PrintLine("");
Test_Pass("multi_closure");
return 0;
}
+55
View File
@@ -0,0 +1,55 @@
// Stdlib ergonomics demo: Array helpers, String_ReplaceAll, Test asserts
import Std::Io::{PrintLine, PrintInt};
import Std::Array::{
Array, Array_New, Array_Push, Array_Pop, Array_Clear, Array_IsEmpty,
Array_First, Array_Last, Array_Cap, Array_Reserve, Array_Len, Array_Get, Array_Free
};
import Std::String::{String_IsEmpty, String_ReplaceAll, String_Eq, String_Len};
import Std::Test::{
Test_AssertTrue, Test_AssertFalse, Test_AssertEqInt, Test_AssertEqString,
Test_AssertNeqInt, Test_AssertEqBool, Test_Pass
};
func Main() -> int {
// --- Array: Reserve / Push / First / Last / Pop / Clear ---
var arr: Array<int> = Array_New<int>(2);
Array_Reserve<int>(&arr, 8);
Test_AssertTrue(Array_Cap<int>(&arr) >= 8);
Test_AssertTrue(Array_IsEmpty<int>(&arr));
Array_Push<int>(&arr, 10);
Array_Push<int>(&arr, 20);
Array_Push<int>(&arr, 30);
Test_AssertFalse(Array_IsEmpty<int>(&arr));
Test_AssertEqInt(Array_Len<int>(&arr) as int, 3);
Test_AssertEqInt(Array_First<int>(&arr), 10);
Test_AssertEqInt(Array_Last<int>(&arr), 30);
let popped: int = Array_Pop<int>(&arr);
Test_AssertEqInt(popped, 30);
Test_AssertEqInt(Array_Len<int>(&arr) as int, 2);
Test_AssertEqInt(Array_Get<int>(&arr, 1), 20);
Array_Clear<int>(&arr);
Test_AssertTrue(Array_IsEmpty<int>(&arr));
Test_AssertTrue(Array_Cap<int>(&arr) >= 8); // capacity retained
Array_Free<int>(&arr);
// --- String: IsEmpty / ReplaceAll ---
Test_AssertTrue(String_IsEmpty(""));
Test_AssertFalse(String_IsEmpty("x"));
let multi: String = String_ReplaceAll("a-b-a-b-a", "a", "X");
Test_AssertEqString(multi, "X-b-X-b-X");
Test_AssertNeqInt(String_Len(multi) as int, 0);
// Safe when replacement contains the needle (no infinite loop)
let safe: String = String_ReplaceAll("..", ".", "x.");
Test_AssertEqString(safe, "x.x.");
Test_AssertEqBool(true, true);
Test_Pass("stdlib ergonomics");
PrintLine("stdlib_ergonomics: all checks passed");
return 0;
}
+21
View File
@@ -0,0 +1,21 @@
// String_IsBlank / String_Repeat
import Std::Io::{PrintLine};
import Std::String::{String_IsBlank, String_IsEmpty, String_Repeat, String_Eq, String_Len};
import Std::Test::{Test_AssertTrue, Test_AssertFalse, Test_AssertEqInt, Test_AssertEqString, Test_Pass};
func Main() -> int {
Test_AssertTrue(String_IsEmpty(""));
Test_AssertTrue(String_IsBlank(""));
Test_AssertTrue(String_IsBlank(" \t\n"));
Test_AssertFalse(String_IsBlank(" x "));
let dots: String = String_Repeat(".", 5);
Test_AssertEqString(dots, ".....");
Test_AssertEqInt(String_Len(String_Repeat("ab", 3)) as int, 6);
Test_AssertEqString(String_Repeat("x", 0), "");
Test_AssertEqString(String_Repeat("ok", 1), "ok");
PrintLine(String_Repeat("Bux ", 2));
Test_Pass("string_extra");
return 0;
}
+32
View File
@@ -0,0 +1,32 @@
// Tuple types: (T, U), return, field access via .0 / .1
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};
func MakePair(a: int, b: int) -> (int, int) {
return (a, b);
}
func Swap(t: (int, int)) -> (int, int) {
return (t.1, t.0);
}
func Main() -> int {
let t: (int, int) = MakePair(10, 20);
Test_AssertEqInt(t.0, 10);
Test_AssertEqInt(t.1, 20);
let s: (int, int) = Swap(t);
Test_AssertEqInt(s.0, 20);
Test_AssertEqInt(s.1, 10);
let lit: (int, int) = (7, 8);
Test_AssertEqInt(lit.0, 7);
Test_AssertEqInt(lit.1, 8);
PrintInt(t.0);
PrintLine("");
PrintInt(t.1);
PrintLine("");
Test_Pass("tuples");
return 0;
}