Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions runtime/include/hemlock_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ typedef struct HmlExceptionContext {
HmlExceptionContext* hml_exception_push(void);
void hml_exception_pop(void);
__attribute__((noreturn)) void hml_throw(HmlValue exception_value);
__attribute__((noreturn)) void hml_throw_take(HmlValue exception_value);
HmlValue hml_exception_get_value(void);

// Runtime error helper - throws catchable exception with formatted message
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/builtins_async.c
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ HmlValue hml_join(HmlValue task_val) {
// Re-throw task exception in the joining thread (parity with interpreter)
if (had_exception) {
hml_release(&result);
hml_throw(exception);
hml_throw_take(exception);
}

return result;
Expand Down
7 changes: 3 additions & 4 deletions runtime/src/builtins_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ HmlValue hml_get_args(void) {
for (int i = 0; i < g_argc; i++) {
HmlValue str = hml_val_string(g_argv[i]);
hml_array_push(arr, str);
hml_release(&str); // array_push retained its own reference
}

return arr;
Expand Down Expand Up @@ -821,13 +822,11 @@ HmlValue hml_parse_string_to_type(HmlValue val, HmlValueType target_type) {
void hml_assert(HmlValue condition, HmlValue message) {
if (!hml_to_bool(condition)) {
// Throw catchable exception (match interpreter behavior)
HmlValue exception_msg;
if (message.type == HML_VAL_STRING && message.as.as_string) {
exception_msg = message;
hml_throw(message); // borrowed argument
} else {
exception_msg = hml_val_string("assertion failed");
hml_throw_take(hml_val_string("assertion failed"));
}
hml_throw(exception_msg);
}
}

Expand Down
12 changes: 9 additions & 3 deletions runtime/src/builtins_func.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,26 @@ void hml_exception_pop(void) {
}
}

void hml_throw(HmlValue exception_value) {
void hml_throw_take(HmlValue exception_value) {
if (!g_exception_stack || !g_exception_stack->is_active) {
// Uncaught exception
fprintf(stderr, "Uncaught exception: ");
print_value_to(stderr, exception_value);
fprintf(stderr, "\n");
hml_release(&exception_value);
exit(1);
}

hml_release(&g_exception_stack->exception_value);
g_exception_stack->exception_value = exception_value;
hml_retain(&g_exception_stack->exception_value);
longjmp(g_exception_stack->exception_buf, 1);
}

void hml_throw(HmlValue exception_value) {
hml_retain(&exception_value);
hml_throw_take(exception_value);
}

HmlValue hml_exception_get_value(void) {
if (g_exception_stack) {
HmlValue v = g_exception_stack->exception_value;
Expand All @@ -62,7 +68,7 @@ void hml_runtime_error(const char *format, ...) {
va_end(args);

HmlValue error_msg = hml_val_string(buffer);
hml_throw(error_msg);
hml_throw_take(error_msg);
}

// ========== DEFER SUPPORT ==========
Expand Down
68 changes: 34 additions & 34 deletions runtime/src/builtins_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ HmlValue hml_read_line(void) {

HmlValue hml_open(HmlValue path, HmlValue mode) {
if (path.type != HML_VAL_STRING) {
hml_throw(hml_val_string("open() expects string path"));
hml_throw_take(hml_val_string("open() expects string path"));
}

const char *path_str = path.as.as_string->data;
Expand All @@ -66,7 +66,7 @@ HmlValue hml_open(HmlValue path, HmlValue mode) {
char err_buf[512];
snprintf(err_buf, sizeof(err_buf), "Failed to open '%s' with mode '%s': %s",
path_str, mode_str, strerror(errno));
hml_throw(hml_val_string(err_buf));
hml_throw_take(hml_val_string(err_buf));
}

HmlFileHandle *fh = malloc(sizeof(HmlFileHandle));
Expand Down Expand Up @@ -99,7 +99,7 @@ static int hml_mode_to_open_flags(const char *mode) {
// open_fd(path, mode?) -> i32: open a file and return its raw POSIX fd.
HmlValue hml_open_fd(HmlValue path, HmlValue mode) {
if (path.type != HML_VAL_STRING) {
hml_throw(hml_val_string("open_fd() expects string path"));
hml_throw_take(hml_val_string("open_fd() expects string path"));
}

const char *path_str = path.as.as_string->data;
Expand All @@ -113,7 +113,7 @@ HmlValue hml_open_fd(HmlValue path, HmlValue mode) {
char err_buf[256];
snprintf(err_buf, sizeof(err_buf),
"open_fd() unsupported mode '%s' (use r, w, a, r+, w+, a+)", mode_str);
hml_throw(hml_val_string(err_buf));
hml_throw_take(hml_val_string(err_buf));
}

int is_write = (strchr(mode_str, 'w') != NULL || strchr(mode_str, 'a') != NULL ||
Expand All @@ -131,7 +131,7 @@ HmlValue hml_open_fd(HmlValue path, HmlValue mode) {
char err_buf[512];
snprintf(err_buf, sizeof(err_buf), "Failed to open '%s' with mode '%s': %s",
path_str, mode_str, strerror(errno));
hml_throw(hml_val_string(err_buf));
hml_throw_take(hml_val_string(err_buf));
}

return hml_val_i32(fd);
Expand All @@ -141,37 +141,37 @@ HmlValue hml_open_fd(HmlValue path, HmlValue mode) {
// file retains ownership of the fd; closing the file closes the fd.
HmlValue hml_fileno(HmlValue file) {
if (file.type != HML_VAL_FILE || !file.as.as_file) {
hml_throw(hml_val_string("fileno() expects file object"));
hml_throw_take(hml_val_string("fileno() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed || !fh->fp) {
char err_buf[512];
snprintf(err_buf, sizeof(err_buf), "fileno() called on closed file '%s'",
fh->path ? fh->path : "<unknown>");
hml_throw(hml_val_string(err_buf));
hml_throw_take(hml_val_string(err_buf));
}

int fd = fileno((FILE*)fh->fp);
if (fd < 0) {
char err_buf[256];
snprintf(err_buf, sizeof(err_buf), "fileno() failed: %s", strerror(errno));
hml_throw(hml_val_string(err_buf));
hml_throw_take(hml_val_string(err_buf));
}

return hml_val_i32(fd);
}

HmlValue hml_file_read(HmlValue file, HmlValue size) {
if (file.type != HML_VAL_FILE) {
hml_throw(hml_val_string("read() expects file object"));
hml_throw_take(hml_val_string("read() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot read from closed file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

int32_t read_size = 0;
Expand All @@ -195,14 +195,14 @@ HmlValue hml_file_read(HmlValue file, HmlValue size) {

HmlValue hml_file_read_all(HmlValue file) {
if (file.type != HML_VAL_FILE) {
hml_throw(hml_val_string("read() expects file object"));
hml_throw_take(hml_val_string("read() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot read from closed file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

FILE *fp = (FILE*)fh->fp;
Expand All @@ -223,7 +223,7 @@ HmlValue hml_file_read_all(HmlValue file) {

char *buffer = malloc(size + 1);
if (!buffer) {
hml_throw(hml_val_string("Memory allocation failed"));
hml_throw_take(hml_val_string("Memory allocation failed"));
}
size_t bytes_read = fread(buffer, 1, size, fp);
buffer[bytes_read] = '\0';
Expand All @@ -237,7 +237,7 @@ HmlValue hml_file_read_all(HmlValue file) {
size_t total_read = 0;
char *buffer = malloc(capacity);
if (!buffer) {
hml_throw(hml_val_string("Memory allocation failed"));
hml_throw_take(hml_val_string("Memory allocation failed"));
}

while (1) {
Expand All @@ -247,7 +247,7 @@ HmlValue hml_file_read_all(HmlValue file) {
char *new_buffer = realloc(buffer, capacity);
if (!new_buffer) {
free(buffer);
hml_throw(hml_val_string("Memory allocation failed"));
hml_throw_take(hml_val_string("Memory allocation failed"));
}
buffer = new_buffer;
}
Expand All @@ -261,7 +261,7 @@ HmlValue hml_file_read_all(HmlValue file) {
free(buffer);
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Read error on file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}
break; // EOF reached
}
Expand All @@ -277,14 +277,14 @@ HmlValue hml_file_read_all(HmlValue file) {

HmlValue hml_file_write(HmlValue file, HmlValue data) {
if (file.type != HML_VAL_FILE) {
hml_throw(hml_val_string("write() expects file object"));
hml_throw_take(hml_val_string("write() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot write to closed file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

const char *str = "";
Expand All @@ -298,14 +298,14 @@ HmlValue hml_file_write(HmlValue file, HmlValue data) {

HmlValue hml_file_seek(HmlValue file, HmlValue position) {
if (file.type != HML_VAL_FILE) {
hml_throw(hml_val_string("seek() expects file object"));
hml_throw_take(hml_val_string("seek() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot seek in closed file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

long pos = 0;
Expand All @@ -321,14 +321,14 @@ HmlValue hml_file_seek(HmlValue file, HmlValue position) {

HmlValue hml_file_tell(HmlValue file) {
if (file.type != HML_VAL_FILE) {
hml_throw(hml_val_string("tell() expects file object"));
hml_throw_take(hml_val_string("tell() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot tell position in closed file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

return hml_val_i32((int32_t)ftell((FILE*)fh->fp));
Expand All @@ -348,14 +348,14 @@ void hml_file_close(HmlValue file) {

HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) {
if (file.type != HML_VAL_FILE) {
hml_throw(hml_val_string("read_bytes() expects file object"));
hml_throw_take(hml_val_string("read_bytes() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot read from closed file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

int32_t read_size = 0;
Expand All @@ -364,7 +364,7 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) {
} else if (size.type == HML_VAL_I64) {
read_size = (int32_t)size.as.as_i64;
} else {
hml_throw(hml_val_string("read_bytes() expects integer size argument"));
hml_throw_take(hml_val_string("read_bytes() expects integer size argument"));
}

if (read_size <= 0) {
Expand All @@ -381,7 +381,7 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) {

void *data = malloc(read_size);
if (!data) {
hml_throw(hml_val_string("Memory allocation failed in read_bytes()"));
hml_throw_take(hml_val_string("Memory allocation failed in read_bytes()"));
}

size_t bytes_read = fread(data, 1, read_size, (FILE*)fh->fp);
Expand All @@ -390,7 +390,7 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) {
free(data);
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Read error on file '%s': %s", fh->path, strerror(errno));
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

HmlBuffer *buf = calloc(1, sizeof(HmlBuffer));
Expand All @@ -406,25 +406,25 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) {

HmlValue hml_file_write_bytes(HmlValue file, HmlValue data) {
if (file.type != HML_VAL_FILE) {
hml_throw(hml_val_string("write_bytes() expects file object"));
hml_throw_take(hml_val_string("write_bytes() expects file object"));
}

HmlFileHandle *fh = file.as.as_file;
if (fh->closed) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot write to closed file '%s'", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

// Check if file is writable
if (fh->mode[0] == 'r' && strchr(fh->mode, '+') == NULL) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Cannot write to file '%s' opened in read-only mode", fh->path);
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

if (data.type != HML_VAL_BUFFER || !data.as.as_buffer) {
hml_throw(hml_val_string("write_bytes() expects buffer argument"));
hml_throw_take(hml_val_string("write_bytes() expects buffer argument"));
}

HmlBuffer *buf = data.as.as_buffer;
Expand All @@ -433,7 +433,7 @@ HmlValue hml_file_write_bytes(HmlValue file, HmlValue data) {
if (ferror((FILE*)fh->fp)) {
char _err_buf[512];
snprintf(_err_buf, sizeof(_err_buf), "Write error on file '%s': %s", fh->path, strerror(errno));
hml_throw(hml_val_string(_err_buf));
hml_throw_take(hml_val_string(_err_buf));
}

return hml_val_i32((int32_t)written);
Expand Down Expand Up @@ -964,15 +964,15 @@ HmlValue hml_is_dir(HmlValue path) {

HmlValue hml_file_stat(HmlValue path) {
if (path.type != HML_VAL_STRING || !path.as.as_string) {
hml_throw(hml_val_string("file_stat() requires a string path"));
hml_throw_take(hml_val_string("file_stat() requires a string path"));
}

struct stat st;
if (stat(path.as.as_string->data, &st) != 0) {
char err_buf[512];
snprintf(err_buf, sizeof(err_buf), "Failed to stat '%s': %s",
path.as.as_string->data, strerror(errno));
hml_throw(hml_val_string(err_buf));
hml_throw_take(hml_val_string(err_buf));
}

HmlValue obj = hml_val_object();
Expand Down
Loading
Loading