summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 17:47:30 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 17:47:30 +0000
commitba5ee94871e670fbe1ea091dd5731e593df0b29f (patch)
tree3b706a9ab11effa4acb094482f3d657c986ef501
parentaba9191ab3ef0699b0f9507ee3d03161f9ee7771 (diff)
Some std for you
-rw-r--r--std.zc16
-rw-r--r--std/core.zc6
-rw-r--r--std/fs.zc162
-rw-r--r--std/io.zc43
-rw-r--r--std/json.zc250
-rw-r--r--std/map.zc182
-rw-r--r--std/mem.zc77
-rw-r--r--std/net.zc128
-rw-r--r--std/option.zc53
-rw-r--r--std/path.zc117
-rw-r--r--std/result.zc42
-rw-r--r--std/string.zc112
-rw-r--r--std/thread.zc130
-rw-r--r--std/time.zc44
-rw-r--r--std/vec.zc161
15 files changed, 1523 insertions, 0 deletions
diff --git a/std.zc b/std.zc
new file mode 100644
index 0000000..b67b3cf
--- /dev/null
+++ b/std.zc
@@ -0,0 +1,16 @@
+
+struct String;
+struct File;
+
+import "std/core.zc"
+import "std/vec.zc"
+import "std/string.zc"
+import "std/io.zc"
+import "std/fs.zc"
+import "std/time.zc"
+import "std/result.zc"
+import "std/option.zc"
+import "std/map.zc"
+import "std/json.zc"
+import "std/path.zc"
+import "std/mem.zc"
diff --git a/std/core.zc b/std/core.zc
new file mode 100644
index 0000000..daf6a96
--- /dev/null
+++ b/std/core.zc
@@ -0,0 +1,6 @@
+
+include <stdlib.h>
+include <string.h>
+include <stdio.h>
+include <stdbool.h>
+include <stdarg.h>
diff --git a/std/fs.zc b/std/fs.zc
new file mode 100644
index 0000000..4223054
--- /dev/null
+++ b/std/fs.zc
@@ -0,0 +1,162 @@
+
+import "./core.zc"
+import "./result.zc"
+import "./string.zc"
+import "./vec.zc"
+
+raw {
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+
+ // Helper to get file size for stat
+ int _z_fs_get_metadata(char* path, uint64_t* size, int* is_dir, int* is_file) {
+ struct stat st;
+ if (stat(path, &st) != 0) return -1;
+ *size = st.st_size;
+ *is_dir = S_ISDIR(st.st_mode);
+ *is_file = S_ISREG(st.st_mode);
+ return 0;
+ }
+
+ int _z_fs_read_entry(void* dir, char* out_name, int* is_dir) {
+ struct dirent* ent = readdir((DIR*)dir);
+ if (!ent) return 0;
+ strcpy(out_name, ent->d_name);
+ *is_dir = (ent->d_type == DT_DIR);
+ return 1;
+ }
+
+ int _z_fs_mkdir(char* path) {
+ #ifdef _WIN32
+ return mkdir(path);
+ #else
+ return mkdir(path, 0777);
+ #endif
+ }
+}
+
+extern fn _z_fs_mkdir(path: char*) -> int;
+extern fn _z_fs_get_metadata(path: char*, size: U64*, is_dir: int*, is_file: int*) -> int;
+extern fn _z_fs_read_entry(dir: void*, out_name: char*, is_dir: int*) -> int;
+
+struct File {
+ handle: void*; // FILE*
+}
+
+struct Metadata {
+ size: U64;
+ is_dir: bool;
+ is_file: bool;
+}
+
+@derive(Eq)
+struct DirEntry {
+ name: String;
+ is_dir: bool;
+}
+
+impl File {
+ fn open(path: char*, mode: char*) -> Result<File> {
+ var h = fopen(path, mode);
+ if (h == NULL) {
+ return Result<File>::Err("Failed to open file");
+ }
+ return Result<File>::Ok(File { handle: h });
+ }
+
+ fn close(self) {
+ if (self.handle) {
+ fclose(self.handle);
+ self.handle = NULL;
+ }
+ }
+
+ fn read_to_string(self) -> Result<String> {
+ if (self.handle == NULL) return Result<String>::Err("File not open");
+
+ fseek(self.handle, 0, SEEK_END);
+ var size = ftell(self.handle);
+ fseek(self.handle, 0, SEEK_SET);
+
+ var buffer: char* = malloc((usize)size + 1);
+ if (buffer == NULL) return Result<String>::Err("Out of memory");
+
+ var read = fread(buffer, 1, size, self.handle);
+ buffer[read] = 0;
+
+ var s = String::new(buffer);
+ free(buffer);
+ return Result<String>::Ok(s);
+ }
+
+ fn read_all(path: char*) -> Result<String> {
+ var res = File::open(path, "rb");
+ if (res.is_err()) return Result<String>::Err(res.err);
+
+ var f: File = res.unwrap();
+ var s_res = f.read_to_string();
+ f.close();
+ return s_res;
+ }
+
+ fn write_string(self, content: char*) -> Result<bool> {
+ if (self.handle == NULL) return Result<bool>::Err("File not open");
+ var len = strlen(content);
+ var written = fwrite(content, 1, len, self.handle);
+ if (written != len) return Result<bool>::Err("Write incomplete");
+ return Result<bool>::Ok(true);
+ }
+
+ fn exists(path: char*) -> bool {
+ return access(path, F_OK) == 0;
+ }
+
+ fn metadata(path: char*) -> Result<Metadata> {
+ var size: uint64_t;
+ var is_d: int;
+ var is_f: int;
+ if (_z_fs_get_metadata(path, &size, &is_d, &is_f) != 0) {
+ return Result<Metadata>::Err("Failed to get metadata");
+ }
+ return Result<Metadata>::Ok(Metadata { size: (U64)size, is_dir: is_d != 0, is_file: is_f != 0 });
+ }
+
+ fn create_dir(path: char*) -> Result<bool> {
+ if (_z_fs_mkdir(path) != 0) return Result<bool>::Err("Failed to create directory");
+ return Result<bool>::Ok(true);
+ }
+
+ fn remove_file(path: char*) -> Result<bool> {
+ if (unlink(path) != 0) return Result<bool>::Err("Failed to remove file");
+ return Result<bool>::Ok(true);
+ }
+
+ fn remove_dir(path: char*) -> Result<bool> {
+ if (rmdir(path) != 0) return Result<bool>::Err("Failed to remove directory");
+ return Result<bool>::Ok(true);
+ }
+
+ fn read_dir(path: char*) -> Result< Vec<DirEntry> > {
+ var dir = opendir(path);
+ if (dir == NULL) return Result< Vec<DirEntry> >::Err("Failed to open directory");
+
+ var entries = Vec<DirEntry>::new();
+ var name_buf = malloc(256);
+ var is_d: int = 0;
+
+ while (_z_fs_read_entry(dir, name_buf, &is_d)) {
+ // Skip . and ..
+ if (strcmp(name_buf, ".") == 0 || strcmp(name_buf, "..") == 0) continue;
+
+ entries.push(DirEntry {
+ name: String::new(name_buf),
+ is_dir: is_d != 0
+ });
+ }
+
+ free(name_buf);
+ closedir(dir);
+ return Result< Vec<DirEntry> >::Ok(entries);
+ }
+}
diff --git a/std/io.zc b/std/io.zc
new file mode 100644
index 0000000..e7b6300
--- /dev/null
+++ b/std/io.zc
@@ -0,0 +1,43 @@
+
+import "./core.zc"
+
+raw {
+ char* format(const char* fmt, ...) {
+ static char buffer[1024];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+ return buffer;
+ }
+
+ char* format_new(const char* fmt, ...) {
+ char* buffer = malloc(1024);
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buffer, 1024, fmt, args);
+ va_end(args);
+ return buffer;
+ }
+}
+
+raw {
+ char* readln() {
+ char* line = NULL;
+ size_t len = 0;
+ ssize_t read;
+
+ read = getline(&line, &len, stdin);
+
+ if (read != -1) {
+ // Remove newline if present
+ if (line[read - 1] == '\n') {
+ line[read - 1] = '\0';
+ }
+ return line;
+ }
+
+ if (line) free(line);
+ return NULL;
+ }
+}
diff --git a/std/json.zc b/std/json.zc
new file mode 100644
index 0000000..ee2d43d
--- /dev/null
+++ b/std/json.zc
@@ -0,0 +1,250 @@
+
+import "./core.zc"
+import "./vec.zc"
+import "./map.zc"
+import "./string.zc"
+import "./result.zc"
+
+@derive(Eq)
+enum JsonType {
+ JSON_NULL,
+ JSON_BOOL,
+ JSON_NUMBER,
+ JSON_STRING,
+ JSON_ARRAY,
+ JSON_OBJECT
+}
+
+@derive(Eq)
+struct JsonValue {
+ kind: JsonType;
+ string_val: char*;
+ number_val: double;
+ bool_val: bool;
+ array_val: Vec<JsonValue*>*;
+ object_val: Map<JsonValue*>*;
+}
+
+raw {
+ Vec_JsonValuePtr Vec_JsonValuePtr_new();
+ void Vec_JsonValuePtr_push(Vec_JsonValuePtr* self, JsonValue* item);
+ Map_JsonValuePtr Map_JsonValuePtr_new();
+ void Map_JsonValuePtr_put(Map_JsonValuePtr* self, char* key, JsonValue* val);
+
+ static void _json_skip_ws(const char** p) {
+ while (**p == ' ' || **p == '\t' || **p == '\n' || **p == '\r') (*p)++;
+ }
+
+ static char* _json_parse_string(const char** p) {
+ if (**p != '"') return NULL;
+ (*p)++;
+ char buf[4096]; int len = 0;
+ while (**p && **p != '"') {
+ if (**p == '\\' && (*p)[1]) {
+ (*p)++;
+ char c = **p;
+ if (c == 'n') buf[len++] = '\n';
+ else if (c == 't') buf[len++] = '\t';
+ else if (c == 'r') buf[len++] = '\r';
+ else if (c == '"') buf[len++] = '"';
+ else if (c == '\\') buf[len++] = '\\';
+ else buf[len++] = c;
+ } else {
+ buf[len++] = **p;
+ }
+ (*p)++;
+ }
+ if (**p == '"') (*p)++;
+ buf[len] = '\0';
+ return strdup(buf);
+ }
+
+ static struct JsonValue* _json_parse_value(const char** p);
+
+ static struct JsonValue* _json_parse_array(const char** p) {
+ if (**p != '[') return NULL;
+ (*p)++;
+ struct JsonValue* arr = malloc(sizeof(struct JsonValue));
+ arr->kind = JsonType_JSON_ARRAY();
+ arr->string_val = 0; arr->number_val = 0; arr->bool_val = 0; arr->object_val = 0;
+ arr->array_val = malloc(sizeof(Vec_JsonValuePtr));
+ *(arr->array_val) = Vec_JsonValuePtr_new();
+
+ _json_skip_ws(p);
+ if (**p == ']') { (*p)++; return arr; }
+
+ while (1) {
+ _json_skip_ws(p);
+ struct JsonValue* val = _json_parse_value(p);
+ if (!val) return NULL;
+ Vec_JsonValuePtr_push(arr->array_val, val);
+ _json_skip_ws(p);
+ if (**p == ']') { (*p)++; return arr; }
+ if (**p != ',') return NULL;
+ (*p)++;
+ }
+ }
+
+ static struct JsonValue* _json_parse_object(const char** p) {
+ if (**p != '{') return NULL;
+ (*p)++;
+ struct JsonValue* obj = malloc(sizeof(struct JsonValue));
+ obj->kind = JsonType_JSON_OBJECT();
+ obj->string_val = 0; obj->number_val = 0; obj->bool_val = 0; obj->array_val = 0;
+ obj->object_val = malloc(sizeof(Map_JsonValuePtr));
+ *(obj->object_val) = Map_JsonValuePtr_new();
+
+ _json_skip_ws(p);
+ if (**p == '}') { (*p)++; return obj; }
+
+ while (1) {
+ _json_skip_ws(p);
+ char* key = _json_parse_string(p);
+ if (!key) return NULL;
+ _json_skip_ws(p);
+ if (**p != ':') { free(key); return NULL; }
+ (*p)++;
+ _json_skip_ws(p);
+ struct JsonValue* val = _json_parse_value(p);
+ if (!val) { free(key); return NULL; }
+ Map_JsonValuePtr_put(obj->object_val, key, val);
+ free(key);
+ _json_skip_ws(p);
+ if (**p == '}') { (*p)++; return obj; }
+ if (**p != ',') return NULL;
+ (*p)++;
+ }
+ }
+
+ static struct JsonValue* _json_parse_value(const char** p) {
+ _json_skip_ws(p);
+ if (**p == '\0') return NULL;
+
+ if (strncmp(*p, "null", 4) == 0) {
+ *p += 4;
+ struct JsonValue* v = malloc(sizeof(struct JsonValue));
+ v->kind = JsonType_JSON_NULL();
+ v->string_val = 0; v->number_val = 0; v->bool_val = 0; v->array_val = 0; v->object_val = 0;
+ return v;
+ }
+ if (strncmp(*p, "true", 4) == 0) {
+ *p += 4;
+ struct JsonValue* v = malloc(sizeof(struct JsonValue));
+ v->kind = JsonType_JSON_BOOL();
+ v->string_val = 0; v->number_val = 0; v->bool_val = 1; v->array_val = 0; v->object_val = 0;
+ return v;
+ }
+ if (strncmp(*p, "false", 5) == 0) {
+ *p += 5;
+ struct JsonValue* v = malloc(sizeof(struct JsonValue));
+ v->kind = JsonType_JSON_BOOL();
+ v->string_val = 0; v->number_val = 0; v->bool_val = 0; v->array_val = 0; v->object_val = 0;
+ return v;
+ }
+ if (**p == '"') {
+ char* s = _json_parse_string(p);
+ if (!s) return NULL;
+ struct JsonValue* v = malloc(sizeof(struct JsonValue));
+ v->kind = JsonType_JSON_STRING();
+ v->string_val = s; v->number_val = 0; v->bool_val = 0; v->array_val = 0; v->object_val = 0;
+ return v;
+ }
+ if (**p == '-' || (**p >= '0' && **p <= '9')) {
+ char* end;
+ double num = strtod(*p, &end);
+ if (end == *p) return NULL;
+ *p = end;
+ struct JsonValue* v = malloc(sizeof(struct JsonValue));
+ v->kind = JsonType_JSON_NUMBER();
+ v->string_val = 0; v->number_val = num; v->bool_val = 0; v->array_val = 0; v->object_val = 0;
+ return v;
+ }
+ if (**p == '[') return _json_parse_array(p);
+ if (**p == '{') return _json_parse_object(p);
+ return NULL;
+ }
+
+ struct JsonValue* _json_do_parse(const char* json) {
+ const char* ptr = json;
+ return _json_parse_value(&ptr);
+ }
+}
+
+impl JsonValue {
+ fn null() -> JsonValue {
+ return JsonValue { kind: JsonType::JSON_NULL(), string_val: 0, number_val: 0, bool_val: false, array_val: 0, object_val: 0 };
+ }
+
+ fn bool(b: bool) -> JsonValue {
+ return JsonValue { kind: JsonType::JSON_BOOL(), string_val: 0, number_val: 0, bool_val: b, array_val: 0, object_val: 0 };
+ }
+
+ fn number(n: double) -> JsonValue {
+ return JsonValue { kind: JsonType::JSON_NUMBER(), string_val: 0, number_val: n, bool_val: false, array_val: 0, object_val: 0 };
+ }
+
+ fn string(s: char*) -> JsonValue {
+ return JsonValue { kind: JsonType::JSON_STRING(), string_val: strdup(s), number_val: 0, bool_val: false, array_val: 0, object_val: 0 };
+ }
+
+ fn array() -> JsonValue {
+ var v: Vec<JsonValue*>* = malloc(sizeof(Vec_JsonValuePtr));
+ *v = Vec_JsonValuePtr::new();
+ return JsonValue { kind: JsonType::JSON_ARRAY(), string_val: 0, number_val: 0, bool_val: false, array_val: v, object_val: 0 };
+ }
+
+ fn object() -> JsonValue {
+ var m: Map<JsonValue*>* = malloc(sizeof(Map_JsonValuePtr));
+ *m = Map_JsonValuePtr::new();
+ return JsonValue { kind: JsonType::JSON_OBJECT(), string_val: 0, number_val: 0, bool_val: false, array_val: 0, object_val: m };
+ }
+
+ fn push(self, val: JsonValue) {
+ if (self.kind.tag != JsonType::JSON_ARRAY().tag) return;
+ var p: JsonValue* = malloc(sizeof(JsonValue));
+ *p = val;
+ self.array_val.push(p);
+ }
+
+ fn set(self, key: char*, val: JsonValue) {
+ if (self.kind.tag != JsonType::JSON_OBJECT().tag) return;
+ var p: JsonValue* = malloc(sizeof(JsonValue));
+ *p = val;
+ self.object_val.put(key, p);
+ }
+
+ fn parse(json: char*) -> Result<JsonValue*> {
+ var result: JsonValue* = _json_do_parse(json);
+ if (result != NULL) {
+ return Result<JsonValue*>::Ok(result);
+ }
+ return Result<JsonValue*>::Err("JSON parse error");
+ }
+
+ fn free(self) {
+ if (self.kind.tag == JsonType::JSON_STRING().tag) free(self.string_val);
+ if (self.kind.tag == JsonType::JSON_ARRAY().tag) {
+ var v: Vec<JsonValue*>* = self.array_val;
+ for (var i: usize = 0; i < v.length(); i = i + 1) {
+ var item: JsonValue* = v.get(i);
+ item.free();
+ free(item);
+ }
+ v.clear();
+ free(v.data);
+ free(v);
+ }
+ if (self.kind.tag == JsonType::JSON_OBJECT().tag) {
+ var m: Map<JsonValue*>* = self.object_val;
+ for (var i: usize = 0; i < m.capacity(); i = i + 1) {
+ if (m.is_slot_occupied(i)) {
+ var child: JsonValue* = m.val_at(i);
+ child.free();
+ free(child);
+ }
+ }
+ m.free();
+ free(m);
+ }
+ }
+}
diff --git a/std/map.zc b/std/map.zc
new file mode 100644
index 0000000..cd54f3d
--- /dev/null
+++ b/std/map.zc
@@ -0,0 +1,182 @@
+
+import "./core.zc"
+import "./option.zc"
+
+raw {
+ size_t _map_hash_str(const char* str) {
+ size_t hash = 14695981039346656037UL;
+ while (*str) {
+ hash ^= (unsigned char)*str++;
+ hash *= 1099511628211UL;
+ }
+ return hash;
+ }
+}
+
+struct Map<V> {
+ keys: char**;
+ vals: V*;
+ occupied: bool*;
+ deleted: bool*;
+ len: usize;
+ cap: usize;
+}
+
+impl Map<V> {
+ fn new() -> Map<V> {
+ return Map<V> { keys: 0, vals: 0, occupied: 0, deleted: 0, len: 0, cap: 0 };
+ }
+
+ fn _resize(self, new_cap: usize) {
+ var old_keys = self.keys;
+ var old_vals = self.vals;
+ var old_occupied = self.occupied;
+ var old_deleted = self.deleted;
+ var old_cap = self.cap;
+
+ self.cap = new_cap;
+ self.keys = calloc(new_cap, sizeof(char*));
+ self.vals = calloc(new_cap, sizeof(V));
+ self.occupied = calloc(new_cap, sizeof(bool));
+ self.deleted = calloc(new_cap, sizeof(bool));
+ self.len = 0;
+
+ for (var i: usize = 0; i < old_cap; i = i + 1) {
+ if (old_occupied[i] && !old_deleted[i]) {
+ self.put(old_keys[i], old_vals[i]);
+ }
+ }
+
+ // Free old arrays (use explicit braces to avoid parser bug)
+ if (old_keys != NULL) { free(old_keys); }
+ if (old_vals != NULL) { free(old_vals); }
+ if (old_occupied != NULL) { free(old_occupied); }
+ if (old_deleted != NULL) { free(old_deleted); }
+ }
+
+ fn put(self, key: char*, val: V) {
+ if (self.len >= self.cap * 0.75) {
+ var new_cap = self.cap * 2;
+ if (new_cap < 8) new_cap = 8;
+ self._resize(new_cap);
+ }
+
+ var hash = _map_hash_str(key);
+ var idx = hash % self.cap;
+
+ while (true) {
+ if (!self.occupied[idx] || (self.occupied[idx] && !self.deleted[idx] && strcmp(self.keys[idx], key) == 0)) {
+ if (!self.occupied[idx]) self.len = self.len + 1;
+
+ if (!self.occupied[idx] || self.deleted[idx]) {
+ self.keys[idx] = strdup(key);
+ }
+
+ self.vals[idx] = val;
+ self.occupied[idx] = true;
+ self.deleted[idx] = false;
+ return;
+ }
+
+ idx = (idx + 1) % self.cap;
+ }
+ }
+
+ fn get(self, key: char*) -> Option<V> {
+ if (self.cap == 0) {
+ return Option<V>::None();
+ }
+
+ var hash = _map_hash_str(key);
+ var idx = hash % self.cap;
+ var start_idx = idx;
+
+ while (true) {
+ if (!self.occupied[idx]) {
+ return Option<V>::None();
+ }
+
+ if (!self.deleted[idx] && strcmp(self.keys[idx], key) == 0) {
+ return Option<V>::Some(self.vals[idx]);
+ }
+
+ idx = (idx + 1) % self.cap;
+ if (idx == start_idx) {
+ return Option<V>::None();
+ }
+ }
+ }
+
+ fn contains(self, key: char*) -> bool {
+ var opt: Option<V> = self.get(key);
+ return opt.is_some();
+ }
+
+ fn remove(self, key: char*) {
+ if (self.cap == 0) return;
+
+ var hash = _map_hash_str(key);
+ var idx = hash % self.cap;
+
+ while (true) {
+ if (!self.occupied[idx]) return;
+
+ if (!self.deleted[idx] && strcmp(self.keys[idx], key) == 0) {
+ self.deleted[idx] = true;
+ self.len = self.len - 1;
+ free(self.keys[idx]);
+ return;
+ }
+
+ idx = (idx + 1) % self.cap;
+ }
+ }
+
+ fn length(self) -> usize {
+ return self.len;
+ }
+
+ fn is_empty(self) -> bool {
+ return self.len == 0;
+ }
+
+ fn free(self) {
+ if (self.keys) {
+ for (var i: usize = 0; i < self.cap; i = i + 1) {
+ if (self.occupied[i] && !self.deleted[i]) {
+ free(self.keys[i]);
+ }
+ }
+ free(self.keys);
+ free(self.vals);
+ free(self.occupied);
+ free(self.deleted);
+ }
+ self.keys = 0;
+ self.vals = 0;
+ self.occupied = 0;
+ self.deleted = 0;
+ self.len = 0;
+ self.cap = 0;
+ }
+
+ fn capacity(self) -> usize {
+ return self.cap;
+ }
+
+ fn is_slot_occupied(self, idx: usize) -> bool {
+ if (idx >= self.cap) return false;
+ return self.occupied[idx] && !self.deleted[idx];
+ }
+
+ fn key_at(self, idx: usize) -> char* {
+ if (idx >= self.cap || !self.occupied[idx] || self.deleted[idx]) {
+ return NULL;
+ }
+ return self.keys[idx];
+ }
+
+ fn val_at(self, idx: usize) -> V {
+ return self.vals[idx];
+ }
+}
diff --git a/std/mem.zc b/std/mem.zc
new file mode 100644
index 0000000..3e08e8b
--- /dev/null
+++ b/std/mem.zc
@@ -0,0 +1,77 @@
+
+fn alloc<T>() -> T* {
+ return malloc(sizeof(T));
+}
+
+fn zalloc<T>() -> T* {
+ return calloc(1, sizeof(T));
+}
+
+fn alloc_n<T>(n: usize) -> T* {
+ return malloc(sizeof(T) * n);
+}
+
+struct Box<T> {
+ ptr: T*;
+}
+
+impl Box<T> {
+ fn new() -> Self {
+ return Self { ptr: calloc(1, sizeof(T)) };
+ }
+
+ fn from_ptr(p: T*) -> Self {
+ return Self { ptr: p };
+ }
+
+ fn get(self) -> T* {
+ return self.ptr;
+ }
+
+ fn is_null(self) -> bool {
+ return self.ptr == NULL;
+ }
+
+ fn free(self) {
+ if self.ptr != NULL {
+ free(self.ptr);
+ }
+ }
+}
+
+struct Slice<T> {
+ data: T*;
+ len: usize;
+}
+
+impl Slice<T> {
+ fn new(data: T*, len: usize) -> Self {
+ return Self { data: data, len: len };
+ }
+
+ fn get(self, i: usize) -> T {
+ return self.data[i];
+ }
+
+ fn set(self, i: usize, val: T) {
+ self.data[i] = val;
+ }
+
+ fn is_empty(self) -> bool {
+ return self.len == 0;
+ }
+}
+
+fn mem_zero<T>(ptr: T*, count: usize) {
+ memset(ptr, 0, sizeof(T) * count);
+}
+
+fn mem_copy<T>(dst: T*, src: T*, count: usize) {
+ memcpy(dst, src, sizeof(T) * count);
+}
+
+fn swap<T>(a: T*, b: T*) {
+ var tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
diff --git a/std/net.zc b/std/net.zc
new file mode 100644
index 0000000..996ddeb
--- /dev/null
+++ b/std/net.zc
@@ -0,0 +1,128 @@
+
+include <sys/socket.h>;
+include <netinet/in.h>;
+include <arpa/inet.h>;
+include <unistd.h>;
+include <errno.h>;
+
+import "./core.zc"
+import "./result.zc"
+import "./string.zc"
+
+const Z_AF_INET = 2;
+const Z_SOCK_STREAM = 1;
+
+raw {
+ static int _z_net_bind(int fd, char *host, int port) {
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (inet_pton(AF_INET, host, &addr.sin_addr) <= 0) return -1; // Invalid addr
+
+ int opt = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) return -2; // Bind fail
+ if (listen(fd, 10) < 0) return -3; // Listen fail
+ return 0;
+ }
+
+ static int _z_net_connect(int fd, char *host, int port) {
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (inet_pton(AF_INET, host, &addr.sin_addr) <= 0) return -1;
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) return -2;
+ return 0;
+ }
+
+ static int _z_net_accept(int fd) {
+ return accept(fd, NULL, NULL);
+ }
+
+ static ssize_t _z_net_read(int fd, char* buf, size_t n) {
+ return read(fd, (void*)buf, n);
+ }
+
+ static ssize_t _z_net_write(int fd, char* buf, size_t n) {
+ return write(fd, (const void*)buf, n);
+ }
+}
+
+extern fn socket(domain: int, type: int, proto: int) -> int;
+extern fn close(fd: int) -> int;
+
+extern fn _z_net_bind(fd: int, host: char*, port: int) -> int;
+extern fn _z_net_connect(fd: int, host: char*, port: int) -> int;
+extern fn _z_net_accept(fd: int) -> int;
+extern fn _z_net_read(fd: int, buf: char*, n: usize) -> isize;
+extern fn _z_net_write(fd: int, buf: char*, n: usize) -> isize;
+
+
+struct TcpStream {
+ fd: int;
+}
+
+impl TcpStream {
+ fn read(self, buf: char*, len: usize) -> Result<usize> {
+ var n = _z_net_read(self.fd, buf, len);
+ if (n < 0) return Result<usize>::Err("Read failed");
+ return Result<usize>::Ok((usize)n);
+ }
+
+ fn write(self, buf: char*, len: usize) -> Result<usize> {
+ var n = _z_net_write(self.fd, buf, len);
+ if (n < 0) return Result<usize>::Err("Write failed");
+ return Result<usize>::Ok((usize)n);
+ }
+
+ fn close(self) {
+ if (self.fd >= 0) {
+ close(self.fd);
+ self.fd = -1;
+ }
+ }
+
+ fn connect(host: char*, port: int) -> Result<TcpStream> {
+ var fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0);
+ if (fd < 0) return Result<TcpStream>::Err("Failed to create socket");
+
+ var res = _z_net_connect(fd, host, port);
+ if (res == -1) { close(fd); return Result<TcpStream>::Err("Invalid address"); }
+ if (res == -2) { close(fd); return Result<TcpStream>::Err("Connection failed"); }
+
+ return Result<TcpStream>::Ok(TcpStream { fd: fd });
+ }
+}
+
+struct TcpListener {
+ fd: int;
+}
+
+impl TcpListener {
+ fn bind(host: char*, port: int) -> Result<TcpListener> {
+ var fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0);
+ if (fd < 0) return Result<TcpListener>::Err("Failed to create socket");
+
+ var res = _z_net_bind(fd, host, port);
+ if (res == -1) { close(fd); return Result<TcpListener>::Err("Invalid address"); }
+ if (res == -2) { close(fd); return Result<TcpListener>::Err("Bind failed"); }
+ if (res == -3) { close(fd); return Result<TcpListener>::Err("Listen failed"); }
+
+ return Result<TcpListener>::Ok(TcpListener { fd: fd });
+ }
+
+ fn accept(self) -> Result<TcpStream> {
+ var client_fd = _z_net_accept(self.fd);
+ if (client_fd < 0) return Result<TcpStream>::Err("Accept failed");
+ return Result<TcpStream>::Ok(TcpStream { fd: client_fd });
+ }
+
+ fn close(self) {
+ if (self.fd >= 0) {
+ close(self.fd);
+ self.fd = -1;
+ }
+ }
+}
diff --git a/std/option.zc b/std/option.zc
new file mode 100644
index 0000000..5652bd7
--- /dev/null
+++ b/std/option.zc
@@ -0,0 +1,53 @@
+
+import "./core.zc"
+
+struct Option<T> {
+ is_some: bool;
+ val: T;
+}
+
+impl Option<T> {
+ fn Some(v: T) -> Option<T> {
+ return Option<T> { is_some: true, val: v };
+ }
+
+ fn None() -> Option<T> {
+ return Option<T> { is_some: false, val: 0 };
+ }
+
+ fn is_some(self) -> bool {
+ return self.is_some;
+ }
+
+ fn is_none(self) -> bool {
+ return !self.is_some;
+ }
+
+ fn unwrap(self) -> T {
+ if (!self.is_some) {
+ !"Panic: unwrap called on None";
+ exit(1);
+ }
+ return self.val;
+ }
+
+ fn unwrap_or(self, def_val: T) -> T {
+ if (self.is_some) {
+ return self.val;
+ }
+ return def_val;
+ }
+
+ fn expect(self, msg: char*) -> T {
+ if (!self.is_some) {
+ !"Panic: {msg}";
+ exit(1);
+ }
+ return self.val;
+ }
+
+ fn or_else(self, other: Option<T>) -> Option<T> {
+ if self.is_some { return *self; }
+ return other;
+ }
+}
diff --git a/std/path.zc b/std/path.zc
new file mode 100644
index 0000000..6a9531d
--- /dev/null
+++ b/std/path.zc
@@ -0,0 +1,117 @@
+
+import "./core.zc"
+import "./string.zc"
+import "./option.zc"
+import "./vec.zc"
+
+struct Path {
+ str: String;
+}
+
+impl Path {
+ fn new(s: char*) -> Path {
+ return Path { str: String::new(s) };
+ }
+
+ fn from_string(s: String) -> Path {
+ return Path { str: s };
+ }
+
+ fn c_str(self) -> char* {
+ return self.str.c_str();
+ }
+
+ fn clone(self) -> Path {
+ return Path { str: String::new(self.str.c_str()) };
+ }
+
+ fn join(self, other: char*) -> Path {
+ var base_len = self.str.length();
+ var new_s = String::from(self.str.c_str());
+
+ if (base_len > 0) {
+ var last = self.str.vec.get(base_len - 1);
+ if (last != '/' && last != '\\') {
+ var sep = String::new("/");
+ new_s.append(sep);
+ }
+ }
+
+ var other_str = String::new(other);
+ new_s.append(other_str);
+
+ return Path { str: new_s };
+ }
+
+
+ fn extension(self) -> Option<String> {
+ var s = self.c_str();
+ var len = self.str.length();
+
+ for (var i: usize = len; i > 0; i = i - 1) {
+ var c = s[i-1];
+ if (c == '.') {
+ // Check if we hit a separator before element
+ // e.g. /d.ir/file - found dot
+ // /d.ir/file.txt - found last dot
+ return Option<String>::Some(self.str.substring(i, len - i));
+ }
+ if (c == '/' || c == '\\') {
+ return Option<String>::None();
+ }
+ }
+ return Option<String>::None();
+ }
+
+ fn file_name(self) -> Option<String> {
+ var s = self.c_str();
+ var len = self.str.length();
+
+ if (len == 0) return Option<String>::None();
+
+ // Remove trailing separators
+ var end = len;
+ while (end > 0 && (s[end-1] == '/' || s[end-1] == '\\')) {
+ end = end - 1;
+ }
+ if (end == 0) return Option<String>::None(); // Root
+
+ var start = end;
+ while (start > 0 && s[start-1] != '/' && s[start-1] != '\\') {
+ start = start - 1;
+ }
+
+ return Option<String>::Some(self.str.substring(start, end - start));
+ }
+
+ fn parent(self) -> Option<Path> {
+ var s = self.c_str();
+ var len = self.str.length();
+ if (len == 0) return Option<Path>::None();
+
+ // Trim trailing separators
+ var end = len;
+ while (end > 0 && (s[end-1] == '/' || s[end-1] == '\\')) {
+ end = end - 1;
+ }
+
+ // Scan back to separator
+ while (end > 0 && s[end-1] != '/' && s[end-1] != '\\') {
+ end = end - 1;
+ }
+
+ if (end == 0) return Option<Path>::None();
+
+ while (end > 0 && (s[end-1] == '/' || s[end-1] == '\\')) {
+ end = end - 1;
+ }
+
+ if (end == 0) {
+ // Must be root
+ return Option<Path>::Some(Path::new("/"));
+ }
+
+ var parent_str = self.str.substring(0, end);
+ return Option<Path>::Some(Path { str: parent_str });
+ }
+}
diff --git a/std/result.zc b/std/result.zc
new file mode 100644
index 0000000..b043162
--- /dev/null
+++ b/std/result.zc
@@ -0,0 +1,42 @@
+
+import "./core.zc"
+
+struct Result<T> {
+ is_ok: bool;
+ val: T;
+ err: char*;
+}
+
+impl Result<T> {
+ fn Ok(v: T) -> Result<T> {
+ return Result<T> { is_ok: true, val: v, err: 0 };
+ }
+
+ fn Err(e: char*) -> Result<T> {
+ return Result<T> { is_ok: false, val: 0, err: e };
+ }
+
+ fn is_ok(self) -> bool {
+ return self.is_ok;
+ }
+
+ fn is_err(self) -> bool {
+ return !self.is_ok;
+ }
+
+ fn unwrap(self) -> T {
+ if (!self.is_ok) {
+ !"Panic: unwrap called on Err: {self.err}";
+ exit(1);
+ }
+ return self.val;
+ }
+
+ fn expect(self, msg: char*) -> T {
+ if (!self.is_ok) {
+ !"Panic: {msg}: {self.err}";
+ exit(1);
+ }
+ return self.val;
+ }
+}
diff --git a/std/string.zc b/std/string.zc
new file mode 100644
index 0000000..5c0327a
--- /dev/null
+++ b/std/string.zc
@@ -0,0 +1,112 @@
+
+import "./core.zc"
+import "./vec.zc"
+import "./option.zc"
+
+struct String {
+ vec: Vec<char>;
+}
+
+impl String {
+ fn new(s: char*) -> String {
+ var len = strlen(s);
+ var v = Vec<char>::new();
+ // Manual copy for now
+ for (var i = 0; i < len; i = i + 1) {
+ v.push(s[i]);
+ }
+ v.push(0);
+ return String { vec: v };
+ }
+
+ fn from(s: char*) -> String {
+ return String::new(s);
+ }
+
+ fn c_str(self) -> char* {
+ return self.vec.data;
+ }
+
+ fn append(self, other: String) {
+ self.vec.len = self.vec.len - 1;
+ var other_len = other.vec.len;
+ for (var i = 0; i < other_len; i = i + 1) {
+ self.vec.push(other.vec.get(i));
+ }
+ }
+
+ fn add(self, other: String) -> String {
+ var new_s = String::from(self.c_str());
+ new_s.append(other);
+ return new_s;
+ }
+
+ fn eq(self, other: String) -> bool {
+ return strcmp(self.c_str(), other.c_str()) == 0;
+ }
+
+ fn length(self) -> usize {
+ if (self.vec.len == 0) { return 0;
+ }
+ return self.vec.len - 1;
+ // Exclude null terminator
+ }
+
+ fn substring(self, start: usize, len: usize) -> String {
+ if (start + len > self.length()) {
+ printf("Panic: substring out of bounds\n");
+ exit(1);
+ }
+ var v = Vec<char>::new();
+ // Manual copy
+ for (var i: usize = 0; i < len; i = i + 1) {
+ v.push(self.vec.get(start + i));
+ }
+ v.push(0);
+ return String { vec: v };
+ }
+
+ fn find(self, target: char) -> Option<usize> {
+ var len = self.length();
+ for (var i: usize = 0; i < len; i = i + 1) {
+ if (self.vec.get(i) == target) {
+ return Option<usize>::Some(i);
+ }
+ }
+ return Option<usize>::None();
+ }
+
+ fn print(self) {
+ "{self.c_str()}"..;
+ }
+
+ fn println(self) {
+ "{self.c_str()}";
+ }
+
+ fn is_empty(self) -> bool {
+ return self.length() == 0;
+ }
+
+ fn contains(self, target: char) -> bool {
+ return self.find(target).is_some();
+ }
+
+ fn starts_with(self, prefix: char*) -> bool {
+ var plen = strlen(prefix);
+ if plen > self.length() { return false; }
+ return strncmp(self.c_str(), prefix, plen) == 0;
+ }
+
+ fn ends_with(self, suffix: char*) -> bool {
+ var slen = strlen(suffix);
+ var len = self.length();
+ if slen > len { return false; }
+ var offset = (int)(len - slen);
+ return strcmp(self.c_str() + offset, suffix) == 0;
+ }
+
+ fn free(self) {
+ self.vec.free();
+ }
+}
diff --git a/std/thread.zc b/std/thread.zc
new file mode 100644
index 0000000..f1aae69
--- /dev/null
+++ b/std/thread.zc
@@ -0,0 +1,130 @@
+
+include <pthread.h>;
+include <time.h>;
+include <unistd.h>;
+
+import "./core.zc"
+import "./result.zc"
+
+raw {
+ typedef void (*ZenThreadFunc)(void*);
+
+ struct ZenThreadCtx {
+ void *func_ptr;
+ void *ctx;
+ };
+
+ static void* _z_thread_trampoline(void *arg) {
+ struct ZenThreadCtx *c = (struct ZenThreadCtx*)arg;
+ z_closure_T *closure = (z_closure_T*)c;
+ void (*f)(void*) = (void(*)(void*))closure->func;
+ f(closure->ctx);
+ free(c);
+ return NULL;
+ }
+
+ static int _z_thread_spawn(void *ctx_copy, size_t *out_handle) {
+ pthread_t pt;
+ int ret = pthread_create(&pt, NULL, _z_thread_trampoline, ctx_copy);
+ if (ret == 0) {
+ *out_handle = (size_t)pt;
+ }
+ return ret;
+ }
+
+ static int _z_thread_join(void *handle) {
+ return pthread_join((pthread_t)handle, NULL);
+ }
+
+ static void _z_mutex_init(void *ptr) {
+ pthread_mutex_init((pthread_mutex_t*)ptr, NULL);
+ }
+
+ static void _z_mutex_lock(void *ptr) {
+ pthread_mutex_lock((pthread_mutex_t*)ptr);
+ }
+
+ static void _z_mutex_unlock(void *ptr) {
+ pthread_mutex_unlock((pthread_mutex_t*)ptr);
+ }
+
+ static void _z_mutex_destroy(void *ptr) {
+ pthread_mutex_destroy((pthread_mutex_t*)ptr);
+ }
+
+ static void _z_usleep(int micros) {
+ usleep(micros);
+ }
+}
+
+extern fn _z_thread_spawn(ctx: void*, out: usize*) -> int;
+extern fn _z_thread_join(handle: void*) -> int;
+extern fn _z_mutex_init(ptr: void*);
+extern fn _z_mutex_lock(ptr: void*);
+extern fn _z_mutex_unlock(ptr: void*);
+extern fn _z_mutex_destroy(ptr: void*);
+extern fn _z_usleep(micros: int);
+
+
+
+struct Thread {
+ handle: void*;
+}
+
+impl Thread {
+ fn spawn(func: fn()) -> Result<Thread> {
+ var t: usize = 0;
+
+ var ctx_copy = malloc(16); // z_closure_T is 16 bytes
+ if (ctx_copy == NULL) return Result<Thread>::Err("OOM");
+
+ memcpy(ctx_copy, &func, 16);
+
+ var ret = _z_thread_spawn(ctx_copy, &t);
+
+ if (ret != 0) {
+ free(ctx_copy);
+ return Result<Thread>::Err("Failed to create thread");
+ }
+
+ return Result<Thread>::Ok(Thread { handle: (void*)t });
+ }
+
+ fn join(self) -> Result<bool> {
+ var ret = _z_thread_join(self.handle);
+ if (ret != 0) return Result<bool>::Err("Join failed");
+ return Result<bool>::Ok(true);
+ }
+}
+
+struct Mutex {
+ handle: void*;
+}
+
+impl Mutex {
+ fn new() -> Mutex {
+ var ptr = malloc(64);
+ _z_mutex_init(ptr);
+ return Mutex { handle: ptr };
+ }
+
+ fn lock(self) {
+ _z_mutex_lock(self.handle);
+ }
+
+ fn unlock(self) {
+ _z_mutex_unlock(self.handle);
+ }
+
+ fn free(self) {
+ if (self.handle) {
+ _z_mutex_destroy(self.handle);
+ free(self.handle);
+ }
+ }
+}
+
+fn sleep_ms(ms: int) {
+ _z_usleep(ms * 1000);
+}
+
diff --git a/std/time.zc b/std/time.zc
new file mode 100644
index 0000000..72e611a
--- /dev/null
+++ b/std/time.zc
@@ -0,0 +1,44 @@
+
+import "./core.zc"
+
+raw {
+ #include <time.h>
+ #include <unistd.h>
+ #include <sys/time.h>
+
+ static uint64_t _time_now_impl(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint64_t)(tv.tv_sec) * 1000 + (uint64_t)(tv.tv_usec) / 1000;
+ }
+}
+
+struct Duration {
+ millis: U64;
+}
+
+impl Duration {
+ fn from_ms(ms: U64) -> Duration {
+ return Duration { millis: ms };
+ }
+
+ fn from_secs(s: U64) -> Duration {
+ return Duration { millis: s * (U64)1000 };
+ }
+}
+
+struct Time {}
+
+impl Time {
+ fn now() -> U64 {
+ return _time_now_impl();
+ }
+
+ fn sleep(d: Duration) {
+ usleep((U32)(d.millis * (U64)1000));
+ }
+
+ fn sleep_ms(ms: U64) {
+ usleep((U32)(ms * (U64)1000));
+ }
+}
diff --git a/std/vec.zc b/std/vec.zc
new file mode 100644
index 0000000..dc57563
--- /dev/null
+++ b/std/vec.zc
@@ -0,0 +1,161 @@
+
+import "./core.zc"
+
+struct Vec<T> {
+ data: T*;
+ len: usize;
+ cap: usize;
+}
+
+impl Vec<T> {
+ fn new() -> Vec<T> {
+ return Vec<T> { data: 0, len: 0, cap: 0 };
+ }
+
+ fn push(self, item: T) {
+ if (self.len >= self.cap) {
+ if (self.cap == 0) { self.cap = 8;
+ }
+ else { self.cap = self.cap * 2;
+ }
+ self.data = realloc(self.data, self.cap * sizeof(T));
+ }
+ self.data[self.len] = item;
+ self.len = self.len + 1;
+ }
+
+ fn insert(self, idx: usize, item: T) {
+ if (idx > self.len) {
+ !"Panic: Insert index out of bounds";
+ exit(1);
+ }
+ if (self.len >= self.cap) {
+ if (self.cap == 0) { self.cap = 8;
+ }
+ else { self.cap = self.cap * 2;
+ }
+ self.data = realloc(self.data, self.cap * sizeof(T));
+ }
+ // Shift elements right
+ if (idx < self.len) {
+ memmove(self.data + idx + 1, self.data + idx, (self.len - idx) * sizeof(T));
+ }
+ self.data[idx] = item;
+ self.len = self.len + 1;
+ }
+
+ fn pop(self) -> T {
+ if (self.len == 0) {
+ !"Panic: pop called on empty Vec";
+ exit(1);
+ }
+ self.len = self.len - 1;
+ return self.data[self.len];
+ }
+
+ fn remove(self, idx: usize) -> T {
+ if (idx >= self.len) {
+ !"Panic: Remove index out of bounds";
+ exit(1);
+ }
+ var item = self.data[idx];
+ // Shift elements left
+ if (idx < self.len - 1) {
+ memmove(self.data + idx, self.data + idx + 1, (self.len - idx - 1) * sizeof(T));
+ }
+ self.len = self.len - 1;
+ return item;
+ }
+
+ fn get(self, idx: usize) -> T {
+ if (idx >= self.len) {
+ !"Panic: Index out of bounds";
+ exit(1);
+ }
+ return self.data[idx];
+ }
+
+ fn last(self) -> T {
+ if (self.len == 0) {
+ !"Panic: last called on empty Vec";
+ exit(1);
+ }
+ return self.data[self.len - 1];
+ }
+
+ fn length(self) -> usize {
+ return self.len;
+ }
+
+ fn contains(self, item: T) -> bool {
+ var i: usize = 0;
+ while i < self.len {
+ if self.data[i] == item { return true; }
+ i++;
+ }
+ return false;
+ }
+
+ fn is_empty(self) -> bool {
+ return self.len == 0;
+ }
+
+ fn clear(self) {
+ self.len = 0;
+ }
+
+ fn free(self) {
+ if (self.data) free(self.data);
+ self.data = 0;
+ self.len = 0;
+ self.cap = 0;
+ }
+
+ fn first(self) -> T {
+ if (self.len == 0) {
+ !"Panic: first called on empty Vec";
+ exit(1);
+ }
+ return self.data[0];
+ }
+
+ fn set(self, idx: usize, item: T) {
+ if (idx >= self.len) {
+ !"Panic: set index out of bounds";
+ exit(1);
+ }
+ self.data[idx] = item;
+ }
+
+ fn reverse(self) {
+ var i: usize = 0;
+ var j = self.len - 1;
+ while i < j {
+ var tmp = self.data[i];
+ self.data[i] = self.data[j];
+ self.data[j] = tmp;
+ i++;
+ j--;
+ }
+ }
+
+ fn eq(self, other: Vec<T>) -> bool {
+ if self.len != other.len { return false; }
+ var i: usize = 0;
+ while i < self.len {
+ if self.data[i] != other.data[i] { return false; }
+ i = i + 1;
+ }
+ return true;
+ }
+
+ fn clone(self) -> Vec<T> {
+ var new_vec: Vec<T> = Vec<T>::new();
+ var i: usize = 0;
+ while i < self.len {
+ new_vec.push(self.data[i]);
+ i = i + 1;
+ }
+ return new_vec;
+ }
+}