feat: restructure repo, borrow checker, expanded stdlib

- Reorganize repository to Rust-style layout:
  compiler/bootstrap/  compiler/selfhost/  compiler/tests/
  library/std/  library/runtime/  tests/  tools/
- Add buxs/ Windows-compatible project root
- Add borrow checker tests and implement:
  - Alias analysis (double mutable borrow detection)
  - Use-after-move detection for own T
- Expand standard library:
  - Std::Os: Args, Env, Cwd, Chdir
  - Std::Time: NowMs, NowUs, SleepMs
  - Std::Process: Run, Output
  - Std::Io: PrintInt64 (fixes 32-bit truncation bug)
- Add examples: os_time.bux, process.bux
- Fix PrintInt to use int64_t in C runtime
This commit is contained in:
2026-06-05 20:08:17 +03:00
parent a45a2ecd49
commit 9c6b516453
75 changed files with 581 additions and 79 deletions
+44
View File
@@ -0,0 +1,44 @@
module Std::Array {
extern func bux_alloc(size: uint) -> *void;
extern func bux_realloc(ptr: *void, size: uint) -> *void;
extern func bux_free(ptr: *void);
extern func bux_bounds_check(index: uint, len: uint);
struct Array<T> {
data: *T,
len: uint,
cap: uint,
}
func Array_New<T>(cap: uint) -> Array<T> {
let data = bux_alloc(cap * sizeof(T)) as *T;
return Array<T> { data: data, len: 0, cap: cap };
}
func Array_Push<T>(self: *Array<T>, value: T) {
if self.len >= self.cap {
self.cap = self.cap * 2;
self.data = bux_realloc(self.data as *void, self.cap * sizeof(T)) as *T;
}
self.data[self.len] = value;
self.len = self.len + 1;
}
func Array_Get<T>(self: *Array<T>, index: uint) -> T {
bux_bounds_check(index, self.len);
return self.data[index];
}
func Array_Len<T>(self: *Array<T>) -> uint {
return self.len;
}
func Array_Free<T>(self: *Array<T>) {
bux_free(self.data as *void);
self.data = null as *T;
self.len = 0;
self.cap = 0;
}
}
+35
View File
@@ -0,0 +1,35 @@
module Std::Channel {
extern func bux_channel_new(capacity: int64, elem_size: int64) -> *void;
extern func bux_channel_send(handle: *void, elem: *void);
extern func bux_channel_recv(handle: *void, out: *void) -> int;
extern func bux_channel_close(handle: *void);
extern func bux_channel_free(handle: *void);
struct Channel<T> {
handle: *void;
}
func Channel_New<T>(capacity: int64) -> Channel<T> {
return Channel<T> { handle: bux_channel_new(capacity, sizeof(T)) };
}
func Channel_Send<T>(ch: *Channel<T>, value: T) {
bux_channel_send(ch.handle, &value);
}
func Channel_Recv<T>(ch: *Channel<T>) -> T {
var result: T;
bux_channel_recv(ch.handle, &result);
return result;
}
func Channel_Close<T>(ch: *Channel<T>) {
bux_channel_close(ch.handle);
}
func Channel_Free<T>(ch: *Channel<T>) {
bux_channel_free(ch.handle);
}
}
+19
View File
@@ -0,0 +1,19 @@
module Std::Fs {
extern func bux_dir_exists(path: String) -> int;
extern func bux_mkdir_if_needed(path: String) -> int;
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
func DirExists(path: String) -> bool {
return bux_dir_exists(path) != 0;
}
func Mkdir(path: String) -> bool {
return bux_mkdir_if_needed(path) != 0;
}
func ListDir(dir: String, ext: String, count: *int) -> *String {
return bux_list_dir(dir, ext, count);
}
}
+28
View File
@@ -0,0 +1,28 @@
module Std::Io {
extern func PrintLine(s: String);
extern func Print(s: String);
extern func PrintInt(n: int);
extern func PrintInt64(n: int64);
extern func PrintFloat(f: float64);
extern func PrintBool(b: bool);
extern func ReadLine() -> String;
extern func bux_read_file(path: String) -> String;
extern func bux_write_file(path: String, content: String) -> int;
extern func bux_file_exists(path: String) -> int;
func ReadFile(path: String) -> String {
return bux_read_file(path);
}
func WriteFile(path: String, content: String) -> bool {
let r: int = bux_write_file(path, content);
return r != 0;
}
func FileExists(path: String) -> bool {
let r: int = bux_file_exists(path);
return r != 0;
}
}
+169
View File
@@ -0,0 +1,169 @@
module Std::Map {
extern func bux_hash_bytes(ptr: *void, size: uint) -> uint;
extern func bux_hash_string(s: String) -> uint;
// ---------------------------------------------------------------------------
// Generic Map<K, V> — works with value-type keys (int, float, etc.)
// For String keys, use StringMap below.
// ---------------------------------------------------------------------------
struct MapEntry<K, V> {
key: K,
value: V,
occupied: bool,
}
struct Map<K, V> {
entries: *MapEntry<K, V>,
cap: uint,
len: uint,
}
func Map_New<K, V>(cap: uint) -> Map<K, V> {
let total: uint = cap * sizeof(MapEntry<K, V>);
let data: *MapEntry<K, V> = bux_alloc(total) as *MapEntry<K, V>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
}
return Map<K, V> { entries: data, cap: cap, len: 0 };
}
func Map_Set<K, V>(m: *Map<K, V>, key: K, value: V) {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
m.entries[idx].value = value;
return;
}
idx = (idx + 1) % m.cap;
}
m.entries[idx].key = key;
m.entries[idx].value = value;
m.entries[idx].occupied = true;
m.len = m.len + 1;
}
func Map_Get<K, V>(m: *Map<K, V>, key: K) -> V {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
return m.entries[idx].value;
}
idx = (idx + 1) % m.cap;
}
// Return zero value for missing key
var zero: V = 0 as V;
return zero;
}
func Map_Has<K, V>(m: *Map<K, V>, key: K) -> bool {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
return true;
}
idx = (idx + 1) % m.cap;
}
return false;
}
func Map_Len<K, V>(m: *Map<K, V>) -> uint {
return m.len;
}
func Map_Free<K, V>(m: *Map<K, V>) {
bux_free(m.entries as *void);
m.entries = null as *MapEntry<K, V>;
m.cap = 0;
m.len = 0;
}
// ---------------------------------------------------------------------------
// StringMap<V> — specialized Map for String keys, using strcmp
// ---------------------------------------------------------------------------
struct StringMapEntry<V> {
key: String,
value: V,
occupied: bool,
}
struct StringMap<V> {
entries: *StringMapEntry<V>,
cap: uint,
len: uint,
}
func StringMap_New<V>(cap: uint) -> StringMap<V> {
let total: uint = cap * sizeof(StringMapEntry<V>);
let data: *StringMapEntry<V> = bux_alloc(total) as *StringMapEntry<V>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
}
return StringMap<V> { entries: data, cap: cap, len: 0 };
}
func StringMap_Set<V>(m: *StringMap<V>, key: String, value: V) {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
m.entries[idx].value = value;
return;
}
idx = (idx + 1) % m.cap;
}
m.entries[idx].key = key;
m.entries[idx].value = value;
m.entries[idx].occupied = true;
m.len = m.len + 1;
}
func StringMap_Get<V>(m: *StringMap<V>, key: String) -> V {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
return m.entries[idx].value;
}
idx = (idx + 1) % m.cap;
}
var zero: V = 0 as V;
return zero;
}
func StringMap_Has<V>(m: *StringMap<V>, key: String) -> bool {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
return true;
}
idx = (idx + 1) % m.cap;
}
return false;
}
func StringMap_Len<V>(m: *StringMap<V>) -> uint {
return m.len;
}
func StringMap_Free<V>(m: *StringMap<V>) {
bux_free(m.entries as *void);
m.entries = null as *StringMapEntry<V>;
m.cap = 0;
m.len = 0;
}
}
+44
View File
@@ -0,0 +1,44 @@
module Std::Math {
extern func bux_sqrt(x: float64) -> float64;
extern func bux_pow(x: float64, y: float64) -> float64;
extern func bux_abs_i64(x: int64) -> int64;
extern func bux_abs_f64(x: float64) -> float64;
extern func bux_min_i64(a: int64, b: int64) -> int64;
extern func bux_max_i64(a: int64, b: int64) -> int64;
extern func bux_min_f64(a: float64, b: float64) -> float64;
extern func bux_max_f64(a: float64, b: float64) -> float64;
func Sqrt(x: float64) -> float64 {
return bux_sqrt(x);
}
func Pow(x: float64, y: float64) -> float64 {
return bux_pow(x, y);
}
func Abs(n: int64) -> int64 {
return bux_abs_i64(n);
}
func AbsF(f: float64) -> float64 {
return bux_abs_f64(f);
}
func Min(a: int64, b: int64) -> int64 {
return bux_min_i64(a, b);
}
func Max(a: int64, b: int64) -> int64 {
return bux_max_i64(a, b);
}
func MinF(a: float64, b: float64) -> float64 {
return bux_min_f64(a, b);
}
func MaxF(a: float64, b: float64) -> float64 {
return bux_max_f64(a, b);
}
}
+29
View File
@@ -0,0 +1,29 @@
module Std::Mem {
extern func bux_alloc(size: uint) -> *void;
extern func bux_realloc(ptr: *void, size: uint) -> *void;
extern func bux_free(ptr: *void);
extern func bux_mem_eq(a: *void, b: *void, size: uint) -> int;
func Alloc(size: uint) -> *void {
return bux_alloc(size);
}
func Realloc(ptr: *void, size: uint) -> *void {
return bux_realloc(ptr, size);
}
func Free(ptr: *void) {
bux_free(ptr);
}
func MemEq(a: *void, b: *void, size: uint) -> bool {
return bux_mem_eq(a, b, size) != 0;
}
func New<T>() -> *T {
let sz: uint = sizeof(T);
return bux_alloc(sz) as *T;
}
}
+34
View File
@@ -0,0 +1,34 @@
module Std::Os {
extern func bux_argc() -> int;
extern func bux_argv(index: int) -> String;
extern func bux_getenv(name: String) -> String;
extern func bux_setenv(name: String, value: String) -> int;
extern func bux_getcwd() -> String;
extern func bux_chdir(path: String) -> int;
func Os_ArgsCount() -> int {
return bux_argc();
}
func Os_Args(index: int) -> String {
return bux_argv(index);
}
func Os_GetEnv(name: String) -> String {
return bux_getenv(name);
}
func Os_SetEnv(name: String, value: String) -> bool {
return bux_setenv(name, value) == 0;
}
func Os_GetCwd() -> String {
return bux_getcwd();
}
func Os_Chdir(path: String) -> bool {
return bux_chdir(path) == 0;
}
}
+19
View File
@@ -0,0 +1,19 @@
module Std::Path {
extern func bux_path_join(a: String, b: String) -> String;
extern func bux_path_parent(path: String) -> String;
extern func bux_path_ext(path: String) -> String;
func Path_Join(a: String, b: String) -> String {
return bux_path_join(a, b);
}
func Path_Parent(path: String) -> String {
return bux_path_parent(path);
}
func Path_Ext(path: String) -> String {
return bux_path_ext(path);
}
}
+14
View File
@@ -0,0 +1,14 @@
module Std::Process {
extern func bux_process_run(cmd: String) -> int;
extern func bux_process_output(cmd: String) -> String;
func Process_Run(cmd: String) -> int {
return bux_process_run(cmd);
}
func Process_Output(cmd: String) -> String {
return bux_process_output(cmd);
}
}
+71
View File
@@ -0,0 +1,71 @@
module Std::Set {
extern func bux_hash_bytes(ptr: *void, size: uint) -> uint;
extern func bux_mem_eq(a: *void, b: *void, size: uint) -> int;
extern func bux_alloc(size: uint) -> *void;
extern func bux_free(ptr: *void);
struct SetEntry<T> {
value: T,
occupied: bool,
}
struct Set<T> {
entries: *SetEntry<T>,
cap: uint,
len: uint,
}
func Set_New<T>(cap: uint) -> Set<T> {
let total: uint = cap * sizeof(SetEntry<T>);
let data: *SetEntry<T> = bux_alloc(total) as *SetEntry<T>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
}
return Set<T> { entries: data, cap: cap, len: 0 };
}
func Set_Add<T>(s: *Set<T>, value: T) {
var valuePtr: *T = &value;
let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T));
var idx: uint = hash % s.cap;
while s.entries[idx].occupied {
var entryPtr: *T = &s.entries[idx].value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 {
return;
}
idx = (idx + 1) % s.cap;
}
s.entries[idx].value = value;
s.entries[idx].occupied = true;
s.len = s.len + 1;
}
func Set_Has<T>(s: *Set<T>, value: T) -> bool {
var valuePtr: *T = &value;
let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T));
var idx: uint = hash % s.cap;
while s.entries[idx].occupied {
var entryPtr: *T = &s.entries[idx].value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 {
return true;
}
idx = (idx + 1) % s.cap;
}
return false;
}
func Set_Len<T>(s: *Set<T>) -> uint {
return s.len;
}
func Set_Free<T>(s: *Set<T>) {
bux_free(s.entries as *void);
s.entries = null as *SetEntry<T>;
s.cap = 0;
s.len = 0;
}
}
+193
View File
@@ -0,0 +1,193 @@
module Std::String {
extern func bux_strlen(s: String) -> uint;
extern func bux_strcmp(a: String, b: String) -> int;
extern func bux_strncmp(a: String, b: String, n: uint) -> int;
extern func bux_strcpy(dest: *char8, src: String) -> *char8;
extern func bux_strcat(dest: *char8, src: String) -> *char8;
extern func bux_strncpy(dest: *char8, src: String, n: uint) -> *char8;
extern func bux_strstr(haystack: String, needle: String) -> String;
extern func bux_str_contains(haystack: String, needle: String) -> int;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func bux_str_trim_left(s: String) -> String;
extern func bux_str_trim_right(s: String) -> String;
extern func bux_str_trim(s: String) -> String;
extern func bux_int_to_str(n: int64) -> String;
extern func bux_str_to_int(s: String) -> int64;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_int(sb: *void, n: int64);
extern func bux_sb_append_float(sb: *void, f: float64);
extern func bux_sb_append_char(sb: *void, c: char8);
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
extern func bux_str_split_count(s: String, delim: String) -> uint;
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
extern func bux_str_join2(a: String, b: String, sep: String) -> String;
extern func bux_str_format(pattern: String, a0: String, a1: String, a2: String, a3: String, a4: String, a5: String, a6: String, a7: String) -> String;
func String_Len(s: String) -> uint {
return bux_strlen(s);
}
func String_Eq(a: String, b: String) -> bool {
return bux_strcmp(a, b) == 0;
}
func String_Concat(a: String, b: String) -> String {
let len_a: uint = bux_strlen(a);
let len_b: uint = bux_strlen(b);
let total: uint = len_a + len_b + 1;
let buf: *char8 = bux_alloc(total) as *char8;
bux_strcpy(buf, a);
bux_strcat(buf, b);
return buf;
}
func String_Copy(s: String) -> String {
let len: uint = bux_strlen(s);
let buf: *char8 = bux_alloc(len + 1) as *char8;
bux_strcpy(buf, s);
return buf;
}
func String_StartsWith(s: String, prefix: String) -> bool {
let s_len: uint = bux_strlen(s);
let p_len: uint = bux_strlen(prefix);
if p_len > s_len {
return false;
}
let r: int = bux_strncmp(s, prefix, p_len);
return r == 0;
}
func String_EndsWith(s: String, suffix: String) -> bool {
let s_len: uint = bux_strlen(s);
let suf_len: uint = bux_strlen(suffix);
if suf_len > s_len {
return false;
}
let start: uint = s_len - suf_len;
let tail: String = bux_str_slice(s, start, suf_len);
let eq: bool = bux_strcmp(tail, suffix) == 0;
return eq;
}
func String_Contains(s: String, substr: String) -> bool {
let r: int = bux_str_contains(s, substr);
return r != 0;
}
func String_Slice(s: String, start: uint, len: uint) -> String {
return bux_str_slice(s, start, len);
}
func String_Trim(s: String) -> String {
return bux_str_trim(s);
}
func String_TrimLeft(s: String) -> String {
return bux_str_trim_left(s);
}
func String_TrimRight(s: String) -> String {
return bux_str_trim_right(s);
}
func String_FromInt(n: int64) -> String {
return bux_int_to_str(n);
}
func String_ToInt(s: String) -> int64 {
return bux_str_to_int(s);
}
// String Builder — efficient string construction
struct StringBuilder {
handle: *void,
}
func StringBuilder_New() -> StringBuilder {
return StringBuilder { handle: bux_sb_new(64) };
}
func StringBuilder_NewCap(cap: uint) -> StringBuilder {
return StringBuilder { handle: bux_sb_new(cap) };
}
func StringBuilder_Append(sb: *StringBuilder, s: String) {
bux_sb_append(sb.handle, s);
}
func StringBuilder_AppendInt(sb: *StringBuilder, n: int64) {
bux_sb_append_int(sb.handle, n);
}
func StringBuilder_AppendFloat(sb: *StringBuilder, f: float64) {
bux_sb_append_float(sb.handle, f);
}
func StringBuilder_AppendChar(sb: *StringBuilder, c: char8) {
bux_sb_append_char(sb.handle, c);
}
func StringBuilder_Build(sb: *StringBuilder) -> String {
return bux_sb_build(sb.handle);
}
func StringBuilder_Free(sb: *StringBuilder) {
bux_sb_free(sb.handle);
}
// ---------------------------------------------------------------------------
// String split/join
// ---------------------------------------------------------------------------
func String_SplitCount(s: String, delim: String) -> uint {
return bux_str_split_count(s, delim);
}
func String_SplitPart(s: String, delim: String, index: uint) -> String {
return bux_str_split_part(s, delim, index);
}
func String_Join2(a: String, b: String, sep: String) -> String {
return bux_str_join2(a, b, sep);
}
// ---------------------------------------------------------------------------
// String find/replace/format
// ---------------------------------------------------------------------------
func String_Find(haystack: String, needle: String) -> String {
return bux_strstr(haystack, needle);
}
func String_Replace(s: String, old: String, new: String) -> String {
let pos: String = bux_strstr(s, old);
if pos as uint == 0 {
return s;
}
let oldLen: uint = bux_strlen(old);
let prefixLen: uint = pos as uint - s as uint;
let prefix: String = bux_str_slice(s, 0, prefixLen);
let suffix: String = bux_str_slice(s, prefixLen + oldLen, bux_strlen(s) - prefixLen - oldLen);
let temp: String = String_Concat(prefix, new);
let result: String = String_Concat(temp, suffix);
return result;
}
func String_Format1(pattern: String, a0: String) -> String {
return bux_str_format(pattern, a0, "", "", "", "", "", "", "");
}
func String_Format2(pattern: String, a0: String, a1: String) -> String {
return bux_str_format(pattern, a0, a1, "", "", "", "", "", "");
}
func String_Format3(pattern: String, a0: String, a1: String, a2: String) -> String {
return bux_str_format(pattern, a0, a1, a2, "", "", "", "", "");
}
}
+23
View File
@@ -0,0 +1,23 @@
module Std::Task {
extern func bux_task_spawn(fn: *void, arg: *void) -> *void;
extern func bux_task_join(handle: *void);
extern func bux_task_sleep(ms: int64);
struct TaskHandle {
handle: *void;
}
func Task_Spawn(fn: *void, arg: *void) -> TaskHandle {
return TaskHandle { handle: bux_task_spawn(fn, arg) };
}
func Task_Join(t: TaskHandle) {
bux_task_join(t.handle);
}
func Task_Sleep(ms: int64) {
bux_task_sleep(ms);
}
}
+19
View File
@@ -0,0 +1,19 @@
module Std::Time {
extern func bux_time_ms() -> int64;
extern func bux_time_us() -> int64;
extern func bux_sleep_ms(ms: int64);
func Time_NowMs() -> int64 {
return bux_time_ms();
}
func Time_NowUs() -> int64 {
return bux_time_us();
}
func Time_SleepMs(ms: int64) {
bux_sleep_ms(ms);
}
}