From ba5ee94871e670fbe1ea091dd5731e593df0b29f Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sun, 11 Jan 2026 17:47:30 +0000 Subject: Some std for you --- std.zc | 16 ++++ std/core.zc | 6 ++ std/fs.zc | 162 +++++++++++++++++++++++++++++++++++++ std/io.zc | 43 ++++++++++ std/json.zc | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ std/map.zc | 182 ++++++++++++++++++++++++++++++++++++++++++ std/mem.zc | 77 ++++++++++++++++++ std/net.zc | 128 ++++++++++++++++++++++++++++++ std/option.zc | 53 +++++++++++++ std/path.zc | 117 +++++++++++++++++++++++++++ std/result.zc | 42 ++++++++++ std/string.zc | 112 ++++++++++++++++++++++++++ std/thread.zc | 130 ++++++++++++++++++++++++++++++ std/time.zc | 44 +++++++++++ std/vec.zc | 161 +++++++++++++++++++++++++++++++++++++ 15 files changed, 1523 insertions(+) create mode 100644 std.zc create mode 100644 std/core.zc create mode 100644 std/fs.zc create mode 100644 std/io.zc create mode 100644 std/json.zc create mode 100644 std/map.zc create mode 100644 std/mem.zc create mode 100644 std/net.zc create mode 100644 std/option.zc create mode 100644 std/path.zc create mode 100644 std/result.zc create mode 100644 std/string.zc create mode 100644 std/thread.zc create mode 100644 std/time.zc create mode 100644 std/vec.zc 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 +include +include +include +include 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 + #include + #include + + // 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 { + var h = fopen(path, mode); + if (h == NULL) { + return Result::Err("Failed to open file"); + } + return Result::Ok(File { handle: h }); + } + + fn close(self) { + if (self.handle) { + fclose(self.handle); + self.handle = NULL; + } + } + + fn read_to_string(self) -> Result { + if (self.handle == NULL) return Result::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::Err("Out of memory"); + + var read = fread(buffer, 1, size, self.handle); + buffer[read] = 0; + + var s = String::new(buffer); + free(buffer); + return Result::Ok(s); + } + + fn read_all(path: char*) -> Result { + var res = File::open(path, "rb"); + if (res.is_err()) return Result::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 { + if (self.handle == NULL) return Result::Err("File not open"); + var len = strlen(content); + var written = fwrite(content, 1, len, self.handle); + if (written != len) return Result::Err("Write incomplete"); + return Result::Ok(true); + } + + fn exists(path: char*) -> bool { + return access(path, F_OK) == 0; + } + + fn metadata(path: char*) -> Result { + 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::Err("Failed to get metadata"); + } + return Result::Ok(Metadata { size: (U64)size, is_dir: is_d != 0, is_file: is_f != 0 }); + } + + fn create_dir(path: char*) -> Result { + if (_z_fs_mkdir(path) != 0) return Result::Err("Failed to create directory"); + return Result::Ok(true); + } + + fn remove_file(path: char*) -> Result { + if (unlink(path) != 0) return Result::Err("Failed to remove file"); + return Result::Ok(true); + } + + fn remove_dir(path: char*) -> Result { + if (rmdir(path) != 0) return Result::Err("Failed to remove directory"); + return Result::Ok(true); + } + + fn read_dir(path: char*) -> Result< Vec > { + var dir = opendir(path); + if (dir == NULL) return Result< Vec >::Err("Failed to open directory"); + + var entries = Vec::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 >::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*; + object_val: Map*; +} + +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* = 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* = 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 { + var result: JsonValue* = _json_do_parse(json); + if (result != NULL) { + return Result::Ok(result); + } + return Result::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* = 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* = 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 { + keys: char**; + vals: V*; + occupied: bool*; + deleted: bool*; + len: usize; + cap: usize; +} + +impl Map { + fn new() -> Map { + return Map { 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 { + if (self.cap == 0) { + return Option::None(); + } + + var hash = _map_hash_str(key); + var idx = hash % self.cap; + var start_idx = idx; + + while (true) { + if (!self.occupied[idx]) { + return Option::None(); + } + + if (!self.deleted[idx] && strcmp(self.keys[idx], key) == 0) { + return Option::Some(self.vals[idx]); + } + + idx = (idx + 1) % self.cap; + if (idx == start_idx) { + return Option::None(); + } + } + } + + fn contains(self, key: char*) -> bool { + var opt: Option = 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* { + return malloc(sizeof(T)); +} + +fn zalloc() -> T* { + return calloc(1, sizeof(T)); +} + +fn alloc_n(n: usize) -> T* { + return malloc(sizeof(T) * n); +} + +struct Box { + ptr: T*; +} + +impl Box { + 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 { + data: T*; + len: usize; +} + +impl Slice { + 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(ptr: T*, count: usize) { + memset(ptr, 0, sizeof(T) * count); +} + +fn mem_copy(dst: T*, src: T*, count: usize) { + memcpy(dst, src, sizeof(T) * count); +} + +fn swap(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 ; +include ; +include ; +include ; +include ; + +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 { + var n = _z_net_read(self.fd, buf, len); + if (n < 0) return Result::Err("Read failed"); + return Result::Ok((usize)n); + } + + fn write(self, buf: char*, len: usize) -> Result { + var n = _z_net_write(self.fd, buf, len); + if (n < 0) return Result::Err("Write failed"); + return Result::Ok((usize)n); + } + + fn close(self) { + if (self.fd >= 0) { + close(self.fd); + self.fd = -1; + } + } + + fn connect(host: char*, port: int) -> Result { + var fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0); + if (fd < 0) return Result::Err("Failed to create socket"); + + var res = _z_net_connect(fd, host, port); + if (res == -1) { close(fd); return Result::Err("Invalid address"); } + if (res == -2) { close(fd); return Result::Err("Connection failed"); } + + return Result::Ok(TcpStream { fd: fd }); + } +} + +struct TcpListener { + fd: int; +} + +impl TcpListener { + fn bind(host: char*, port: int) -> Result { + var fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0); + if (fd < 0) return Result::Err("Failed to create socket"); + + var res = _z_net_bind(fd, host, port); + if (res == -1) { close(fd); return Result::Err("Invalid address"); } + if (res == -2) { close(fd); return Result::Err("Bind failed"); } + if (res == -3) { close(fd); return Result::Err("Listen failed"); } + + return Result::Ok(TcpListener { fd: fd }); + } + + fn accept(self) -> Result { + var client_fd = _z_net_accept(self.fd); + if (client_fd < 0) return Result::Err("Accept failed"); + return Result::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 { + is_some: bool; + val: T; +} + +impl Option { + fn Some(v: T) -> Option { + return Option { is_some: true, val: v }; + } + + fn None() -> Option { + return Option { 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) -> Option { + 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 { + 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::Some(self.str.substring(i, len - i)); + } + if (c == '/' || c == '\\') { + return Option::None(); + } + } + return Option::None(); + } + + fn file_name(self) -> Option { + var s = self.c_str(); + var len = self.str.length(); + + if (len == 0) return Option::None(); + + // Remove trailing separators + var end = len; + while (end > 0 && (s[end-1] == '/' || s[end-1] == '\\')) { + end = end - 1; + } + if (end == 0) return Option::None(); // Root + + var start = end; + while (start > 0 && s[start-1] != '/' && s[start-1] != '\\') { + start = start - 1; + } + + return Option::Some(self.str.substring(start, end - start)); + } + + fn parent(self) -> Option { + var s = self.c_str(); + var len = self.str.length(); + if (len == 0) return Option::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::None(); + + while (end > 0 && (s[end-1] == '/' || s[end-1] == '\\')) { + end = end - 1; + } + + if (end == 0) { + // Must be root + return Option::Some(Path::new("/")); + } + + var parent_str = self.str.substring(0, end); + return Option::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 { + is_ok: bool; + val: T; + err: char*; +} + +impl Result { + fn Ok(v: T) -> Result { + return Result { is_ok: true, val: v, err: 0 }; + } + + fn Err(e: char*) -> Result { + return Result { 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; +} + +impl String { + fn new(s: char*) -> String { + var len = strlen(s); + var v = Vec::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::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 { + var len = self.length(); + for (var i: usize = 0; i < len; i = i + 1) { + if (self.vec.get(i) == target) { + return Option::Some(i); + } + } + return Option::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 ; +include ; +include ; + +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 { + var t: usize = 0; + + var ctx_copy = malloc(16); // z_closure_T is 16 bytes + if (ctx_copy == NULL) return Result::Err("OOM"); + + memcpy(ctx_copy, &func, 16); + + var ret = _z_thread_spawn(ctx_copy, &t); + + if (ret != 0) { + free(ctx_copy); + return Result::Err("Failed to create thread"); + } + + return Result::Ok(Thread { handle: (void*)t }); + } + + fn join(self) -> Result { + var ret = _z_thread_join(self.handle); + if (ret != 0) return Result::Err("Join failed"); + return Result::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 + #include + #include + + 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 { + data: T*; + len: usize; + cap: usize; +} + +impl Vec { + fn new() -> Vec { + return Vec { 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) -> 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 { + var new_vec: Vec = Vec::new(); + var i: usize = 0; + while i < self.len { + new_vec.push(self.data[i]); + i = i + 1; + } + return new_vec; + } +} -- cgit v1.2.3