feat: stdlib daily APIs, macro tt/type paste, riscv64 cross smoke
ci / build (ubuntu) (push) Has been cancelled
ci / unit + fmt (push) Has been cancelled
ci / examples (push) Has been cancelled
ci / goldens + tools (push) Has been cancelled
ci / apps (push) Has been cancelled
ci / selfhost smoke (push) Has been cancelled
ci / macos smoke (push) Has been cancelled
ci / windows smoke (push) Has been cancelled
ci / CI gate (push) Has been cancelled
selfhost-loop / bootstrap determinism (push) Has been cancelled

Sessions 83–87: grow Array/Map/String/Test ergonomics; delimiter-balanced
and juxta :tt macros plus $t:type fragments; expression-level $(…),* in
templates; selfhost slice lits; riscv64/aarch64 cross smoke helper and
freestanding docs. Null-safe CBE type names and String_StartsWith.
This commit is contained in:
2026-07-27 21:40:11 +03:00
parent a785747c37
commit d60ce2bc3f
23 changed files with 1263 additions and 86 deletions
+72
View File
@@ -0,0 +1,72 @@
// Session 83 — Array RemoveAt/Insert/SwapRemove/Clone + String Cmp/IndexOf/case
import Std::Io::{PrintLine, PrintInt};
import Std::Array::{
Array, Array_New, Array_Push, Array_Get, Array_Len, Array_Free,
Array_RemoveAt, Array_Insert, Array_SwapRemove, Array_Clone
};
import Std::String::{
String_Eq, String_Cmp, String_IndexOf, String_ToUpper, String_ToLower
};
import Std::Map::{Map, Map_New, Map_Set, Map_GetOr, Map_Free};
import Std::Test::{
Test_AssertTrue, Test_AssertFalse, Test_AssertEqInt, Test_AssertEqString, Test_Pass
};
func Main() -> int {
// --- Array_Insert / RemoveAt ---
var arr: Array<int> = Array_New<int>(4);
Array_Push<int>(&arr, 10);
Array_Push<int>(&arr, 30);
Array_Insert<int>(&arr, 1, 20); // [10, 20, 30]
Array_Insert<int>(&arr, 3, 40); // append via insert at len
Test_AssertEqInt(Array_Len<int>(&arr) as int, 4);
Test_AssertEqInt(Array_Get<int>(&arr, 0), 10);
Test_AssertEqInt(Array_Get<int>(&arr, 1), 20);
Test_AssertEqInt(Array_Get<int>(&arr, 2), 30);
Test_AssertEqInt(Array_Get<int>(&arr, 3), 40);
let rem: int = Array_RemoveAt<int>(&arr, 1); // remove 20 → [10, 30, 40]
Test_AssertEqInt(rem, 20);
Test_AssertEqInt(Array_Len<int>(&arr) as int, 3);
Test_AssertEqInt(Array_Get<int>(&arr, 1), 30);
// --- Array_SwapRemove (order not preserved) ---
let swapped: int = Array_SwapRemove<int>(&arr, 0); // remove 10, last→front
Test_AssertEqInt(swapped, 10);
Test_AssertEqInt(Array_Len<int>(&arr) as int, 2);
// --- Array_Clone ---
var clone: Array<int> = Array_Clone<int>(&arr);
Test_AssertEqInt(Array_Len<int>(&clone) as int, Array_Len<int>(&arr) as int);
Test_AssertEqInt(Array_Get<int>(&clone, 0), Array_Get<int>(&arr, 0));
Array_Push<int>(&clone, 99);
Test_AssertEqInt(Array_Len<int>(&clone) as int, 3);
Test_AssertEqInt(Array_Len<int>(&arr) as int, 2); // original unchanged
Array_Free<int>(&arr);
Array_Free<int>(&clone);
// --- String_Cmp / IndexOf / ToUpper / ToLower ---
Test_AssertEqInt(String_Cmp("abc", "abc"), 0);
Test_AssertTrue(String_Cmp("a", "b") < 0);
Test_AssertTrue(String_Cmp("z", "a") > 0);
Test_AssertEqInt(String_IndexOf("hello world", "world"), 6);
Test_AssertEqInt(String_IndexOf("hello", "xyz"), -1);
Test_AssertEqInt(String_IndexOf("aaa", "a"), 0);
Test_AssertEqString(String_ToUpper("Hello, Bux!"), "HELLO, BUX!");
Test_AssertEqString(String_ToLower("Hello, Bux!"), "hello, bux!");
Test_AssertEqString(String_ToUpper("123"), "123");
Test_AssertEqString(String_ToLower(""), "");
// --- Map_GetOr ---
var m: Map<int, int> = Map_New<int, int>(8);
Map_Set<int, int>(&m, 1, 100);
Test_AssertEqInt(Map_GetOr<int, int>(&m, 1, -1), 100);
Test_AssertEqInt(Map_GetOr<int, int>(&m, 99, -1), -1);
Map_Free<int, int>(&m);
PrintLine("collections_extra: all checks passed");
Test_Pass("collections_extra");
return 0;
}
+126
View File
@@ -0,0 +1,126 @@
// Session 8486 — delimiter-balanced :tt + expr-level $(…),* + juxta free-form
//
// 1) `$args:tt` multi-element group flattens when spliced as sole call arg:
// `(a, b)` tuple and `[a, b]` slice lit → `$f($args)` becomes `f(a, b)`.
// 2) Expression-level `$( $a ),*` inside call templates expands to N args.
// 3) Nested `id_tt!` rewrite still works (deeper nested expand).
// 4) Free-form juxta (session 86): pattern `$f:ident $args:tt` matches a single
// call arg `F(a, b)` as ident + arg-list group (no comma required at call site).
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_Pass};
// Parenthesized group as argument list (delimiter-balanced tt)
macro! apply_tt {
( $f:ident, $args:tt ) => {
$f($args)
}
}
// Juxtaposition pattern (no comma between fragments) — free-form paste
macro! apply_juxta {
( $f:ident $args:tt ) => {
$f($args)
}
}
// Expression-level repetition inside a call
macro! apply_rep {
( $f:ident, $($a:expr),* ) => {
$f( $($a),* )
}
}
// Nested rewrite of tt through another macro
macro! id_tt {
( $x:tt ) => { $x }
}
macro! outer_tt {
( $x:tt ) => { id_tt!($x) }
}
// Non-tuple tt still works as a single value
macro! wrap_tt {
( $x:tt ) => { ( $x ) + 1 }
}
// Contrast: :expr keeps the tuple as one argument
macro! apply_expr {
( $f:ident, $args:expr ) => {
$f($args)
}
}
func Add(a: int, b: int) -> int {
return a + b;
}
func Add3(a: int, b: int, c: int) -> int {
return a + b + c;
}
func SumPair(t: (int, int)) -> int {
return t.0 + t.1;
}
func Main() -> int {
// 1) tt group flatten: (3, 4) → Add(3, 4)
let a: int = apply_tt!(Add, (3, 4));
PrintInt(a);
PrintLine("");
// 2) expression-level rep: Add3(1, 2, 3)
let b: int = apply_rep!(Add3, 1, 2, 3);
PrintInt(b);
PrintLine("");
// empty rep → zero-arg call is invalid for Add3; use Add with 2 via rep
let b2: int = apply_rep!(Add, 10, 20);
PrintInt(b2);
PrintLine("");
// 3) nested tt rewrite
let c: int = outer_tt!(7 + 4);
PrintInt(c);
PrintLine("");
// 4) non-group tt value
let d: int = wrap_tt!(41);
PrintInt(d);
PrintLine("");
// 5) :expr keeps tuple as one arg
let e: int = apply_expr!(SumPair, (5, 6));
PrintInt(e);
PrintLine("");
// 6) nested group through outer_tt then apply
let f: int = apply_tt!(Add, (100, 1));
PrintInt(f);
PrintLine("");
// 7) slice-lit group flatten (session 85)
let g: int = apply_tt!(Add, [8, 9]);
PrintInt(g);
PrintLine("");
// 8) free-form juxta: single call site arg Add(2, 5) → $f + $args (session 86)
let h: int = apply_juxta!(Add(2, 5));
PrintInt(h);
PrintLine("");
// zero-arg juxta still works with empty group
// (skip — no zero-arg test func required)
// 9) juxta with three args
let i: int = apply_juxta!(Add3(1, 2, 4));
PrintInt(i);
PrintLine("");
if a != 7 || b != 6 || b2 != 30 || c != 11 || d != 42 || e != 11 || f != 101 || g != 17 || h != 7 || i != 7 {
PrintLine("FAIL macro_tt_raw");
return 1;
}
PrintLine("PASS macro_tt_raw");
Test_Pass("macro_tt_raw");
return 0;
}
+55
View File
@@ -0,0 +1,55 @@
// Session 87 — `$t:type` fragment for sizeof / cast type positions
import Std::Io::{PrintLine, PrintInt};
import Std::Array::{Array, Array_New, Array_Push, Array_Get, Array_Len, Array_Reverse, Array_Free};
import Std::Test::{
Test_AssertEqInt, Test_AssertEqString, Test_AssertNeqString, Test_Pass
};
import Std::String::{String_ToUpper};
macro! size_of {
( $t:type ) => { sizeof($t) as int }
}
macro! cast_zero {
( $t:type ) => { 0 as $t }
}
func Main() -> int {
let a: int = size_of!(int);
let b: int = size_of!(*int);
PrintInt(a);
PrintLine("");
PrintInt(b);
PrintLine("");
// 4 or 8 depending on ABI
if a != 4 && a != 8 {
PrintLine("FAIL size_of int");
return 1;
}
if b != 4 && b != 8 {
PrintLine("FAIL size_of *int");
return 1;
}
let z: int = cast_zero!(int);
Test_AssertEqInt(z, 0);
// Array_Reverse + Test_AssertNeqString
var arr: Array<int> = Array_New<int>(4);
Array_Push<int>(&arr, 1);
Array_Push<int>(&arr, 2);
Array_Push<int>(&arr, 3);
Array_Reverse<int>(&arr);
Test_AssertEqInt(Array_Get<int>(&arr, 0), 3);
Test_AssertEqInt(Array_Get<int>(&arr, 1), 2);
Test_AssertEqInt(Array_Get<int>(&arr, 2), 1);
Test_AssertEqInt(Array_Len<int>(&arr) as int, 3);
Array_Free<int>(&arr);
Test_AssertEqString(String_ToUpper("ok"), "OK");
Test_AssertNeqString("a", "b");
PrintLine("PASS macro_type");
Test_Pass("macro_type");
return 0;
}