#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { int32_t len; int32_t *data; } __glagol_vec_i32; typedef struct { int32_t len; int64_t *data; } __glagol_vec_i64; typedef struct { int32_t len; double *data; } __glagol_vec_f64; typedef struct { int32_t len; bool *data; } __glagol_vec_bool; typedef struct { int32_t len; char **data; } __glagol_vec_string; static int32_t __glagol_process_argc_value = 0; static char **__glagol_process_argv_value = NULL; static bool __glagol_time_base_initialized = false; static struct timespec __glagol_time_base; #define __GLAGOL_FILE_HANDLE_LIMIT 1024 static FILE *__glagol_file_handles[__GLAGOL_FILE_HANDLE_LIMIT]; #define __GLAGOL_NET_HANDLE_LIMIT 1024 static int __glagol_net_handles[__GLAGOL_NET_HANDLE_LIMIT]; static int64_t __glagol_result_i32_encode(uint32_t status, int32_t payload) { uint64_t encoded = ((uint64_t)status << 32) | (uint32_t)payload; return (int64_t)encoded; } static int64_t __glagol_result_i32_err(void) { return __glagol_result_i32_encode(1, 1); } static int32_t __glagol_net_handle_insert(int fd) { if (fd < 0 || fd == INT_MAX) { return 0; } for (int32_t handle = 1; handle < __GLAGOL_NET_HANDLE_LIMIT; handle++) { if (__glagol_net_handles[handle] == 0) { __glagol_net_handles[handle] = fd + 1; return handle; } } return 0; } static int __glagol_net_handle_get(int32_t handle) { if (handle <= 0 || handle >= __GLAGOL_NET_HANDLE_LIMIT) { return -1; } int stored = __glagol_net_handles[handle]; return stored == 0 ? -1 : stored - 1; } static int __glagol_net_handle_take(int32_t handle) { int fd = __glagol_net_handle_get(handle); if (fd >= 0) { __glagol_net_handles[handle] = 0; } return fd; } static bool __glagol_net_connect_port_is_valid(int32_t port) { return port > 0 && port <= 65535; } static bool __glagol_net_bind_port_is_valid(int32_t port) { return port >= 0 && port <= 65535; } static struct sockaddr_in __glagol_net_loopback_addr(int32_t port) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons((uint16_t)port); return addr; } void print_i32(int32_t value) { printf("%d\n", value); } void print_i64(int64_t value) { printf("%" PRId64 "\n", value); } void print_u32(uint32_t value) { printf("%" PRIu32 "\n", value); } void print_u64(uint64_t value) { printf("%" PRIu64 "\n", value); } void print_f64(double value) { printf("%.17g\n", value); } void print_string(const char *value) { puts(value); } void print_bool(bool value) { puts(value ? "true" : "false"); } int32_t string_len(const char *value) { return (int32_t)strlen(value); } void __glagol_io_eprint(const char *value) { fputs(value, stderr); } char *__glagol_io_read_stdin_result(void) { size_t capacity = 1024; size_t length = 0; char *value = malloc(capacity); if (value == NULL) { return NULL; } for (;;) { if (length == capacity) { if (capacity > SIZE_MAX / 2) { free(value); return NULL; } size_t next_capacity = capacity * 2; char *next = realloc(value, next_capacity); if (next == NULL) { free(value); return NULL; } value = next; capacity = next_capacity; } size_t available = capacity - length; size_t read_count = fread(value + length, 1, available, stdin); length += read_count; if (read_count < available) { if (ferror(stdin)) { free(value); return NULL; } break; } } if (length == capacity) { if (capacity == SIZE_MAX) { free(value); return NULL; } char *next = realloc(value, capacity + 1); if (next == NULL) { free(value); return NULL; } value = next; } value[length] = '\0'; return value; } void __glagol_process_init(int32_t argc, char **argv) { __glagol_process_argc_value = argc; __glagol_process_argv_value = argv; } int32_t __glagol_process_argc(void) { return __glagol_process_argc_value; } static void __glagol_process_arg_trap(void) { fputs("slovo runtime error: process argument index out of bounds\n", stderr); exit(1); } char *__glagol_process_arg(int32_t index) { if (index < 0 || index >= __glagol_process_argc_value || __glagol_process_argv_value == NULL) { __glagol_process_arg_trap(); } return __glagol_process_argv_value[index]; } char *__glagol_process_arg_result(int32_t index) { if (index < 0 || index >= __glagol_process_argc_value || __glagol_process_argv_value == NULL) { return NULL; } return __glagol_process_argv_value[index]; } char *__glagol_env_get(const char *name) { char *value = getenv(name); return value == NULL ? "" : value; } char *__glagol_env_get_result(const char *name) { return getenv(name); } static char *__glagol_read_remaining_text(FILE *file) { size_t capacity = 1024; size_t length = 0; char *value = malloc(capacity); if (value == NULL) { return NULL; } for (;;) { if (length == capacity) { if (capacity > SIZE_MAX / 2) { free(value); return NULL; } size_t next_capacity = capacity * 2; char *next = realloc(value, next_capacity); if (next == NULL) { free(value); return NULL; } value = next; capacity = next_capacity; } size_t available = capacity - length; size_t read_count = fread(value + length, 1, available, file); length += read_count; if (read_count < available) { if (ferror(file)) { free(value); return NULL; } break; } } if (length == capacity) { if (capacity == SIZE_MAX) { free(value); return NULL; } char *next = realloc(value, capacity + 1); if (next == NULL) { free(value); return NULL; } value = next; } value[length] = '\0'; return value; } static FILE *__glagol_file_handle_get(int32_t handle) { if (handle <= 0 || handle >= __GLAGOL_FILE_HANDLE_LIMIT) { return NULL; } return __glagol_file_handles[handle]; } static int32_t __glagol_file_handle_insert(FILE *file) { for (int32_t handle = 1; handle < __GLAGOL_FILE_HANDLE_LIMIT; handle++) { if (__glagol_file_handles[handle] == NULL) { __glagol_file_handles[handle] = file; return handle; } } return 0; } static void __glagol_fs_read_trap(void) { fputs("slovo runtime error: file read failed\n", stderr); exit(1); } char *__glagol_fs_read_text(const char *path) { FILE *file = fopen(path, "rb"); if (file == NULL) { __glagol_fs_read_trap(); } if (fseek(file, 0, SEEK_END) != 0) { fclose(file); __glagol_fs_read_trap(); } long length = ftell(file); if (length < 0) { fclose(file); __glagol_fs_read_trap(); } if (fseek(file, 0, SEEK_SET) != 0) { fclose(file); __glagol_fs_read_trap(); } size_t size = (size_t)length; char *value = malloc(size + 1); if (value == NULL) { fclose(file); __glagol_fs_read_trap(); } if (size > 0 && fread(value, 1, size, file) != size) { free(value); fclose(file); __glagol_fs_read_trap(); } if (fclose(file) != 0) { free(value); __glagol_fs_read_trap(); } value[size] = '\0'; return value; } char *__glagol_fs_read_text_result(const char *path) { FILE *file = fopen(path, "rb"); if (file == NULL) { return NULL; } if (fseek(file, 0, SEEK_END) != 0) { fclose(file); return NULL; } long length = ftell(file); if (length < 0) { fclose(file); return NULL; } if (fseek(file, 0, SEEK_SET) != 0) { fclose(file); return NULL; } size_t size = (size_t)length; char *value = malloc(size + 1); if (value == NULL) { fclose(file); return NULL; } if (size > 0 && fread(value, 1, size, file) != size) { free(value); fclose(file); return NULL; } if (fclose(file) != 0) { free(value); return NULL; } value[size] = '\0'; return value; } int32_t __glagol_fs_write_text(const char *path, const char *text) { FILE *file = fopen(path, "wb"); if (file == NULL) { return 1; } size_t len = strlen(text); if (len > 0 && fwrite(text, 1, len, file) != len) { fclose(file); return 1; } if (fclose(file) != 0) { return 1; } return 0; } int32_t __glagol_fs_write_text_result(const char *path, const char *text) { return __glagol_fs_write_text(path, text); } bool __glagol_fs_exists(const char *path) { struct stat info; return stat(path, &info) == 0; } bool __glagol_fs_is_file(const char *path) { struct stat info; return stat(path, &info) == 0 && S_ISREG(info.st_mode); } bool __glagol_fs_is_dir(const char *path) { struct stat info; return stat(path, &info) == 0 && S_ISDIR(info.st_mode); } int32_t __glagol_fs_remove_file_result(const char *path) { return unlink(path) == 0 ? 0 : 1; } int32_t __glagol_fs_create_dir_result(const char *path) { return mkdir(path, 0777) == 0 ? 0 : 1; } int64_t __glagol_fs_open_text_read_result(const char *path) { FILE *file = fopen(path, "rb"); if (file == NULL) { return __glagol_result_i32_err(); } int32_t handle = __glagol_file_handle_insert(file); if (handle == 0) { fclose(file); return __glagol_result_i32_err(); } return __glagol_result_i32_encode(0, handle); } char *__glagol_fs_read_open_text_result(int32_t handle) { FILE *file = __glagol_file_handle_get(handle); if (file == NULL) { return NULL; } return __glagol_read_remaining_text(file); } int32_t __glagol_fs_close_result(int32_t handle) { FILE *file = __glagol_file_handle_get(handle); if (file == NULL) { return 1; } __glagol_file_handles[handle] = NULL; return fclose(file) == 0 ? 0 : 1; } int64_t __glagol_net_tcp_connect_loopback_result(int32_t port) { if (!__glagol_net_connect_port_is_valid(port)) { return __glagol_result_i32_err(); } int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { return __glagol_result_i32_err(); } struct sockaddr_in addr = __glagol_net_loopback_addr(port); if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { close(fd); return __glagol_result_i32_err(); } int32_t handle = __glagol_net_handle_insert(fd); if (handle == 0) { close(fd); return __glagol_result_i32_err(); } return __glagol_result_i32_encode(0, handle); } int64_t __glagol_net_tcp_listen_loopback_result(int32_t port) { if (!__glagol_net_bind_port_is_valid(port)) { return __glagol_result_i32_err(); } int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { return __glagol_result_i32_err(); } int enabled = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) != 0) { close(fd); return __glagol_result_i32_err(); } struct sockaddr_in addr = __glagol_net_loopback_addr(port); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { close(fd); return __glagol_result_i32_err(); } if (listen(fd, 1) != 0) { close(fd); return __glagol_result_i32_err(); } int32_t handle = __glagol_net_handle_insert(fd); if (handle == 0) { close(fd); return __glagol_result_i32_err(); } return __glagol_result_i32_encode(0, handle); } int64_t __glagol_net_tcp_bound_port_result(int32_t handle) { int fd = __glagol_net_handle_get(handle); if (fd < 0) { return __glagol_result_i32_err(); } struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); if (getsockname(fd, (struct sockaddr *)&addr, &addr_len) != 0) { return __glagol_result_i32_err(); } return __glagol_result_i32_encode(0, (int32_t)ntohs(addr.sin_port)); } int64_t __glagol_net_tcp_accept_result(int32_t listener) { int fd = __glagol_net_handle_get(listener); if (fd < 0) { return __glagol_result_i32_err(); } int accepted; do { accepted = accept(fd, NULL, NULL); } while (accepted < 0 && errno == EINTR); if (accepted < 0) { return __glagol_result_i32_err(); } int32_t handle = __glagol_net_handle_insert(accepted); if (handle == 0) { close(accepted); return __glagol_result_i32_err(); } return __glagol_result_i32_encode(0, handle); } char *__glagol_net_tcp_read_all_result(int32_t handle) { int fd = __glagol_net_handle_get(handle); if (fd < 0) { return NULL; } size_t capacity = 1024; size_t length = 0; char *value = malloc(capacity); if (value == NULL) { return NULL; } for (;;) { if (length == capacity) { if (capacity > SIZE_MAX / 2) { free(value); return NULL; } size_t next_capacity = capacity * 2; char *next = realloc(value, next_capacity); if (next == NULL) { free(value); return NULL; } value = next; capacity = next_capacity; } size_t available = capacity - length; if (available > (size_t)SSIZE_MAX) { available = (size_t)SSIZE_MAX; } ssize_t read_count = recv(fd, value + length, available, 0); if (read_count < 0) { if (errno == EINTR) { continue; } free(value); return NULL; } if (read_count == 0) { break; } length += (size_t)read_count; } if (length == capacity) { if (capacity == SIZE_MAX) { free(value); return NULL; } char *next = realloc(value, capacity + 1); if (next == NULL) { free(value); return NULL; } value = next; } value[length] = '\0'; return value; } int32_t __glagol_net_tcp_write_text_result(int32_t handle, const char *text) { int fd = __glagol_net_handle_get(handle); if (fd < 0) { return 1; } size_t length = strlen(text); size_t written = 0; while (written < length) { size_t remaining = length - written; if (remaining > (size_t)SSIZE_MAX) { remaining = (size_t)SSIZE_MAX; } #ifdef MSG_NOSIGNAL ssize_t sent = send(fd, text + written, remaining, MSG_NOSIGNAL); #else ssize_t sent = send(fd, text + written, remaining, 0); #endif if (sent < 0) { if (errno == EINTR) { continue; } return 1; } if (sent == 0) { return 1; } written += (size_t)sent; } return 0; } int32_t __glagol_net_tcp_close_result(int32_t handle) { int fd = __glagol_net_handle_take(handle); if (fd < 0) { return 1; } return close(fd) == 0 ? 0 : 1; } static void __glagol_allocation_trap(void) { fputs("slovo runtime error: string allocation failed\n", stderr); exit(1); } char *__glagol_string_concat(const char *left, const char *right) { size_t left_len = strlen(left); size_t right_len = strlen(right); if (left_len > SIZE_MAX - right_len - 1) { __glagol_allocation_trap(); } char *value = malloc(left_len + right_len + 1); if (value == NULL) { __glagol_allocation_trap(); } memcpy(value, left, left_len); memcpy(value + left_len, right, right_len + 1); return value; } int64_t __glagol_string_byte_at_result(const char *value, int32_t index) { if (value == NULL || index < 0) { return __glagol_result_i32_err(); } size_t length = strlen(value); size_t offset = (size_t)index; if (offset >= length) { return __glagol_result_i32_err(); } return __glagol_result_i32_encode(0, (int32_t)(unsigned char)value[offset]); } char *__glagol_string_slice_result(const char *value, int32_t start, int32_t count) { if (value == NULL || start < 0 || count < 0) { return NULL; } size_t length = strlen(value); size_t offset = (size_t)start; size_t slice_len = (size_t)count; if (offset > length || slice_len > length - offset) { return NULL; } char *slice = malloc(slice_len + 1u); if (slice == NULL) { __glagol_allocation_trap(); } if (slice_len > 0) { memcpy(slice, value + offset, slice_len); } slice[slice_len] = '\0'; return slice; } bool __glagol_string_starts_with(const char *value, const char *prefix) { if (value == NULL || prefix == NULL) { return false; } size_t value_len = strlen(value); size_t prefix_len = strlen(prefix); return prefix_len <= value_len && memcmp(value, prefix, prefix_len) == 0; } bool __glagol_string_ends_with(const char *value, const char *suffix) { if (value == NULL || suffix == NULL) { return false; } size_t value_len = strlen(value); size_t suffix_len = strlen(suffix); return suffix_len <= value_len && memcmp(value + value_len - suffix_len, suffix, suffix_len) == 0; } static char __glagol_json_hex_digit(unsigned char value) { return value < 10u ? (char)('0' + value) : (char)('A' + (value - 10u)); } static void __glagol_json_add_size(size_t *total, size_t amount) { if (*total > SIZE_MAX - amount) { __glagol_allocation_trap(); } *total += amount; } char *__glagol_json_quote_string(const char *text) { if (text == NULL) { text = ""; } size_t escaped_len = 2u; const unsigned char *cursor = (const unsigned char *)text; while (*cursor != '\0') { switch (*cursor) { case '"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': __glagol_json_add_size(&escaped_len, 2u); break; default: __glagol_json_add_size(&escaped_len, *cursor < 0x20u ? 6u : 1u); break; } cursor++; } if (escaped_len == SIZE_MAX) { __glagol_allocation_trap(); } char *value = malloc(escaped_len + 1u); if (value == NULL) { __glagol_allocation_trap(); } char *out = value; *out++ = '"'; cursor = (const unsigned char *)text; while (*cursor != '\0') { unsigned char ch = *cursor; switch (ch) { case '"': *out++ = '\\'; *out++ = '"'; break; case '\\': *out++ = '\\'; *out++ = '\\'; break; case '\b': *out++ = '\\'; *out++ = 'b'; break; case '\f': *out++ = '\\'; *out++ = 'f'; break; case '\n': *out++ = '\\'; *out++ = 'n'; break; case '\r': *out++ = '\\'; *out++ = 'r'; break; case '\t': *out++ = '\\'; *out++ = 't'; break; default: if (ch < 0x20u) { *out++ = '\\'; *out++ = 'u'; *out++ = '0'; *out++ = '0'; *out++ = __glagol_json_hex_digit((unsigned char)(ch >> 4u)); *out++ = __glagol_json_hex_digit((unsigned char)(ch & 0x0Fu)); } else { *out++ = (char)ch; } break; } cursor++; } *out++ = '"'; *out = '\0'; return value; } char *__glagol_json_parse_string_value_result(const char *text) { if (text == NULL) { return NULL; } size_t len = strlen(text); if (len < 2u || text[0] != '"' || text[len - 1u] != '"') { return NULL; } char *value = malloc(len - 1u); if (value == NULL) { __glagol_allocation_trap(); } char *out = value; const unsigned char *cursor = (const unsigned char *)text + 1u; const unsigned char *end = (const unsigned char *)text + len - 1u; while (cursor < end) { unsigned char ch = *cursor++; if (ch < 0x20u || ch >= 0x80u || ch == '"') { free(value); return NULL; } if (ch != '\\') { *out++ = (char)ch; continue; } if (cursor >= end) { free(value); return NULL; } ch = *cursor++; switch (ch) { case '"': *out++ = '"'; break; case '\\': *out++ = '\\'; break; case '/': *out++ = '/'; break; case 'b': *out++ = '\b'; break; case 'f': *out++ = '\f'; break; case 'n': *out++ = '\n'; break; case 'r': *out++ = '\r'; break; case 't': *out++ = '\t'; break; default: free(value); return NULL; } } *out = '\0'; return value; } static char *__glagol_num_u64_to_string_impl(uint64_t magnitude, bool negative) { char reversed[20]; size_t digit_count = 0; do { reversed[digit_count] = (char)(0x30u + (magnitude % 10u)); digit_count++; magnitude /= 10u; } while (magnitude != 0u); size_t sign_count = negative ? 1u : 0u; char *value = malloc(sign_count + digit_count + 1u); if (value == NULL) { __glagol_allocation_trap(); } size_t cursor = 0; if (negative) { value[cursor] = (char)0x2du; cursor++; } for (size_t i = 0; i < digit_count; i++) { value[cursor + i] = reversed[digit_count - i - 1u]; } value[cursor + digit_count] = '\0'; return value; } static char *__glagol_num_i64_to_string_impl(int64_t value) { bool negative = value < 0; uint64_t magnitude = negative ? (uint64_t)(-(value + 1)) + 1u : (uint64_t)value; return __glagol_num_u64_to_string_impl(magnitude, negative); } char *__glagol_num_i32_to_string(int32_t value) { return __glagol_num_i64_to_string_impl((int64_t)value); } char *__glagol_num_u32_to_string(uint32_t value) { return __glagol_num_u64_to_string_impl((uint64_t)value, false); } char *__glagol_num_i64_to_string(int64_t value) { return __glagol_num_i64_to_string_impl(value); } char *__glagol_num_u64_to_string(uint64_t value) { return __glagol_num_u64_to_string_impl(value, false); } char *__glagol_num_f64_to_string(double value) { int length = snprintf(NULL, 0, "%.17f", value); if (length < 0) { __glagol_allocation_trap(); } char *text = malloc((size_t)length + 1u); if (text == NULL) { __glagol_allocation_trap(); } int written = snprintf(text, (size_t)length + 1u, "%.17f", value); if (written != length) { free(text); __glagol_allocation_trap(); } char *dot = strchr(text, '.'); if (dot != NULL) { char *last = text + strlen(text) - 1u; while (last > dot + 1 && *last == '0') { *last = '\0'; last--; } } return text; } bool __glagol_string_eq(const char *left, const char *right) { return strcmp(left, right) == 0; } static int64_t __glagol_string_parse_i32_result_encode(uint32_t status, int32_t payload) { uint64_t encoded = ((uint64_t)status << 32) | (uint32_t)payload; return (int64_t)encoded; } static int64_t __glagol_string_parse_i32_result_err(void) { return __glagol_string_parse_i32_result_encode(1, 1); } int64_t __glagol_string_parse_i32_result(const char *text) { if (text == NULL || text[0] == '\0') { return __glagol_string_parse_i32_result_err(); } const unsigned char *cursor = (const unsigned char *)text; bool negative = false; if (*cursor == '-') { negative = true; cursor++; if (*cursor == '\0') { return __glagol_string_parse_i32_result_err(); } } int64_t limit = negative ? 2147483648LL : 2147483647LL; int64_t value = 0; while (*cursor != '\0') { unsigned char byte = *cursor; if (byte < '0' || byte > '9') { return __glagol_string_parse_i32_result_err(); } int64_t digit = (int64_t)(byte - '0'); if (value > (limit - digit) / 10) { return __glagol_string_parse_i32_result_err(); } value = value * 10 + digit; cursor++; } int32_t parsed; if (negative) { parsed = value == 2147483648LL ? INT32_MIN : (int32_t)-value; } else { parsed = (int32_t)value; } return __glagol_string_parse_i32_result_encode(0, parsed); } static int64_t __glagol_string_parse_u32_result_err(void) { return __glagol_string_parse_i32_result_encode(1, 1); } int64_t __glagol_string_parse_u32_result(const char *text) { if (text == NULL || text[0] == '\0') { return __glagol_string_parse_u32_result_err(); } const unsigned char *cursor = (const unsigned char *)text; uint64_t value = 0; while (*cursor != '\0') { unsigned char byte = *cursor; if (byte < '0' || byte > '9') { return __glagol_string_parse_u32_result_err(); } uint64_t digit = (uint64_t)(byte - '0'); if (value > (((uint64_t)UINT32_MAX) - digit) / 10u) { return __glagol_string_parse_u32_result_err(); } value = value * 10u + digit; cursor++; } return __glagol_string_parse_i32_result_encode(0, (int32_t)(uint32_t)value); } int32_t __glagol_string_parse_i64_result(const char *text, int64_t *out) { if (out == NULL || text == NULL || text[0] == '\0') { return 1; } const unsigned char *cursor = (const unsigned char *)text; bool negative = false; if (*cursor == '-') { negative = true; cursor++; if (*cursor == '\0') { return 1; } } uint64_t limit = negative ? ((uint64_t)INT64_MAX + 1u) : (uint64_t)INT64_MAX; uint64_t value = 0; while (*cursor != '\0') { unsigned char byte = *cursor; if (byte < '0' || byte > '9') { return 1; } uint64_t digit = (uint64_t)(byte - '0'); if (value > (limit - digit) / 10u) { return 1; } value = value * 10u + digit; cursor++; } if (negative) { *out = value == ((uint64_t)INT64_MAX + 1u) ? INT64_MIN : -(int64_t)value; } else { *out = (int64_t)value; } return 0; } int32_t __glagol_string_parse_u64_result(const char *text, uint64_t *out) { if (out == NULL || text == NULL || text[0] == '\0') { return 1; } const unsigned char *cursor = (const unsigned char *)text; uint64_t value = 0; while (*cursor != '\0') { unsigned char byte = *cursor; if (byte < '0' || byte > '9') { return 1; } uint64_t digit = (uint64_t)(byte - '0'); if (value > (UINT64_MAX - digit) / 10u) { return 1; } value = value * 10u + digit; cursor++; } *out = value; return 0; } static size_t __glagol_consume_ascii_digits(const unsigned char *text, size_t index) { while (text[index] >= '0' && text[index] <= '9') { index++; } return index; } static bool __glagol_string_is_ascii_decimal_f64(const char *text) { if (text == NULL || text[0] == '\0') { return false; } const unsigned char *bytes = (const unsigned char *)text; size_t index = 0; if (bytes[index] == '-') { index++; if (bytes[index] == '\0') { return false; } } size_t whole_start = index; index = __glagol_consume_ascii_digits(bytes, index); size_t whole_digits = index - whole_start; if (whole_digits == 0 || bytes[index] != '.') { return false; } index++; size_t fractional_start = index; index = __glagol_consume_ascii_digits(bytes, index); size_t fractional_digits = index - fractional_start; if (fractional_digits == 0) { return false; } return bytes[index] == '\0'; } int32_t __glagol_string_parse_f64_result(const char *text, double *out) { if (out == NULL || !__glagol_string_is_ascii_decimal_f64(text)) { return 1; } errno = 0; char *end = NULL; double value = strtod(text, &end); if (errno == ERANGE || end == text || end == NULL || *end != '\0' || !isfinite(value)) { return 1; } *out = value; return 0; } int32_t __glagol_string_parse_bool_result(const char *text, bool *out) { if (out == NULL || text == NULL) { return 1; } if (strcmp(text, "true") == 0) { *out = true; return 0; } if (strcmp(text, "false") == 0) { *out = false; return 0; } return 1; } static bool __glagol_json_is_integer_token(const char *text, bool allow_negative) { if (text == NULL || text[0] == '\0') { return false; } const unsigned char *cursor = (const unsigned char *)text; if (*cursor == '-') { if (!allow_negative) { return false; } cursor++; if (*cursor == '\0') { return false; } } if (*cursor < '0' || *cursor > '9') { return false; } if (*cursor == '0' && cursor[1] != '\0') { return false; } cursor++; while (*cursor != '\0') { if (*cursor < '0' || *cursor > '9') { return false; } cursor++; } return true; } static bool __glagol_json_is_number_token(const char *text) { if (text == NULL || text[0] == '\0') { return false; } const unsigned char *bytes = (const unsigned char *)text; size_t index = 0; if (bytes[index] == '-') { index++; if (bytes[index] == '\0') { return false; } } if (bytes[index] == '0') { index++; } else if (bytes[index] >= '1' && bytes[index] <= '9') { index = __glagol_consume_ascii_digits(bytes, index); } else { return false; } if (bytes[index] == '.') { index++; size_t fractional_start = index; index = __glagol_consume_ascii_digits(bytes, index); if (index == fractional_start) { return false; } } if (bytes[index] == 'e' || bytes[index] == 'E') { index++; if (bytes[index] == '+' || bytes[index] == '-') { index++; } size_t exponent_start = index; index = __glagol_consume_ascii_digits(bytes, index); if (index == exponent_start) { return false; } } return bytes[index] == '\0'; } int32_t __glagol_json_parse_bool_value_result(const char *text, bool *out) { return __glagol_string_parse_bool_result(text, out); } int64_t __glagol_json_parse_i32_value_result(const char *text) { if (!__glagol_json_is_integer_token(text, true)) { return __glagol_string_parse_i32_result_err(); } return __glagol_string_parse_i32_result(text); } int64_t __glagol_json_parse_u32_value_result(const char *text) { if (!__glagol_json_is_integer_token(text, false)) { return __glagol_string_parse_u32_result_err(); } return __glagol_string_parse_u32_result(text); } int32_t __glagol_json_parse_i64_value_result(const char *text, int64_t *out) { if (!__glagol_json_is_integer_token(text, true)) { return 1; } return __glagol_string_parse_i64_result(text, out); } int32_t __glagol_json_parse_u64_value_result(const char *text, uint64_t *out) { if (!__glagol_json_is_integer_token(text, false)) { return 1; } return __glagol_string_parse_u64_result(text, out); } int32_t __glagol_json_parse_f64_value_result(const char *text, double *out) { if (out == NULL || !__glagol_json_is_number_token(text)) { return 1; } errno = 0; char *end = NULL; double value = strtod(text, &end); if (errno == ERANGE || end == text || end == NULL || *end != '\0' || !isfinite(value)) { return 1; } *out = value; return 0; } static void __glagol_vec_i32_allocation_trap(void) { fputs("slovo runtime error: vector allocation failed\n", stderr); exit(1); } static void __glagol_vec_i32_index_trap(void) { fputs("slovo runtime error: vector index out of bounds\n", stderr); exit(1); } __glagol_vec_i32 *__glagol_vec_i32_empty(void) { __glagol_vec_i32 *value = malloc(sizeof(__glagol_vec_i32)); if (value == NULL) { __glagol_vec_i32_allocation_trap(); } value->len = 0; value->data = NULL; return value; } __glagol_vec_i32 *__glagol_vec_i32_append(const __glagol_vec_i32 *source, int32_t element) { if (source->len < 0 || source->len == INT32_MAX) { __glagol_vec_i32_allocation_trap(); } size_t old_len = (size_t)source->len; size_t new_len = old_len + 1; if (new_len > SIZE_MAX / sizeof(int32_t)) { __glagol_vec_i32_allocation_trap(); } __glagol_vec_i32 *value = malloc(sizeof(__glagol_vec_i32)); if (value == NULL) { __glagol_vec_i32_allocation_trap(); } int32_t *data = malloc(new_len * sizeof(int32_t)); if (data == NULL) { free(value); __glagol_vec_i32_allocation_trap(); } if (old_len > 0) { memcpy(data, source->data, old_len * sizeof(int32_t)); } data[old_len] = element; value->len = (int32_t)new_len; value->data = data; return value; } int32_t __glagol_vec_i32_len(const __glagol_vec_i32 *value) { return value->len; } int32_t __glagol_vec_i32_index(const __glagol_vec_i32 *value, int32_t index) { if (index < 0 || index >= value->len) { __glagol_vec_i32_index_trap(); } return value->data[index]; } bool __glagol_vec_i32_eq(const __glagol_vec_i32 *left, const __glagol_vec_i32 *right) { if (left->len != right->len) { return false; } if (left->len == 0) { return true; } return memcmp(left->data, right->data, (size_t)left->len * sizeof(int32_t)) == 0; } static void __glagol_vec_i64_allocation_trap(void) { fputs("slovo runtime error: vector allocation failed\n", stderr); exit(1); } static void __glagol_vec_i64_index_trap(void) { fputs("slovo runtime error: vector index out of bounds\n", stderr); exit(1); } __glagol_vec_i64 *__glagol_vec_i64_empty(void) { __glagol_vec_i64 *value = malloc(sizeof(__glagol_vec_i64)); if (value == NULL) { __glagol_vec_i64_allocation_trap(); } value->len = 0; value->data = NULL; return value; } __glagol_vec_i64 *__glagol_vec_i64_append(const __glagol_vec_i64 *source, int64_t element) { if (source->len < 0 || source->len == INT32_MAX) { __glagol_vec_i64_allocation_trap(); } size_t old_len = (size_t)source->len; size_t new_len = old_len + 1; if (new_len > SIZE_MAX / sizeof(int64_t)) { __glagol_vec_i64_allocation_trap(); } __glagol_vec_i64 *value = malloc(sizeof(__glagol_vec_i64)); if (value == NULL) { __glagol_vec_i64_allocation_trap(); } int64_t *data = malloc(new_len * sizeof(int64_t)); if (data == NULL) { free(value); __glagol_vec_i64_allocation_trap(); } if (old_len > 0) { memcpy(data, source->data, old_len * sizeof(int64_t)); } data[old_len] = element; value->len = (int32_t)new_len; value->data = data; return value; } int32_t __glagol_vec_i64_len(const __glagol_vec_i64 *value) { return value->len; } int64_t __glagol_vec_i64_index(const __glagol_vec_i64 *value, int32_t index) { if (index < 0 || index >= value->len) { __glagol_vec_i64_index_trap(); } return value->data[index]; } bool __glagol_vec_i64_eq(const __glagol_vec_i64 *left, const __glagol_vec_i64 *right) { if (left->len != right->len) { return false; } if (left->len == 0) { return true; } return memcmp(left->data, right->data, (size_t)left->len * sizeof(int64_t)) == 0; } static void __glagol_vec_f64_allocation_trap(void) { fputs("slovo runtime error: vector allocation failed\n", stderr); exit(1); } static void __glagol_vec_f64_index_trap(void) { fputs("slovo runtime error: vector index out of bounds\n", stderr); exit(1); } __glagol_vec_f64 *__glagol_vec_f64_empty(void) { __glagol_vec_f64 *value = malloc(sizeof(__glagol_vec_f64)); if (value == NULL) { __glagol_vec_f64_allocation_trap(); } value->len = 0; value->data = NULL; return value; } __glagol_vec_f64 *__glagol_vec_f64_append(const __glagol_vec_f64 *source, double element) { if (source->len < 0 || source->len == INT32_MAX) { __glagol_vec_f64_allocation_trap(); } size_t old_len = (size_t)source->len; size_t new_len = old_len + 1; if (new_len > SIZE_MAX / sizeof(double)) { __glagol_vec_f64_allocation_trap(); } __glagol_vec_f64 *value = malloc(sizeof(__glagol_vec_f64)); if (value == NULL) { __glagol_vec_f64_allocation_trap(); } double *data = malloc(new_len * sizeof(double)); if (data == NULL) { free(value); __glagol_vec_f64_allocation_trap(); } if (old_len > 0) { memcpy(data, source->data, old_len * sizeof(double)); } data[old_len] = element; value->len = (int32_t)new_len; value->data = data; return value; } int32_t __glagol_vec_f64_len(const __glagol_vec_f64 *value) { return value->len; } double __glagol_vec_f64_index(const __glagol_vec_f64 *value, int32_t index) { if (index < 0 || index >= value->len) { __glagol_vec_f64_index_trap(); } return value->data[index]; } bool __glagol_vec_f64_eq(const __glagol_vec_f64 *left, const __glagol_vec_f64 *right) { if (left->len != right->len) { return false; } for (int32_t index = 0; index < left->len; index++) { if (left->data[index] != right->data[index]) { return false; } } return true; } static void __glagol_vec_bool_allocation_trap(void) { fputs("slovo runtime error: vector allocation failed\n", stderr); exit(1); } static void __glagol_vec_bool_index_trap(void) { fputs("slovo runtime error: vector index out of bounds\n", stderr); exit(1); } __glagol_vec_bool *__glagol_vec_bool_empty(void) { __glagol_vec_bool *value = malloc(sizeof(__glagol_vec_bool)); if (value == NULL) { __glagol_vec_bool_allocation_trap(); } value->len = 0; value->data = NULL; return value; } __glagol_vec_bool *__glagol_vec_bool_append(const __glagol_vec_bool *source, bool element) { if (source->len < 0 || source->len == INT32_MAX) { __glagol_vec_bool_allocation_trap(); } size_t old_len = (size_t)source->len; size_t new_len = old_len + 1; if (new_len > SIZE_MAX / sizeof(bool)) { __glagol_vec_bool_allocation_trap(); } __glagol_vec_bool *value = malloc(sizeof(__glagol_vec_bool)); if (value == NULL) { __glagol_vec_bool_allocation_trap(); } bool *data = malloc(new_len * sizeof(bool)); if (data == NULL) { free(value); __glagol_vec_bool_allocation_trap(); } if (old_len > 0) { memcpy(data, source->data, old_len * sizeof(bool)); } data[old_len] = element; value->len = (int32_t)new_len; value->data = data; return value; } int32_t __glagol_vec_bool_len(const __glagol_vec_bool *value) { return value->len; } bool __glagol_vec_bool_index(const __glagol_vec_bool *value, int32_t index) { if (index < 0 || index >= value->len) { __glagol_vec_bool_index_trap(); } return value->data[index]; } bool __glagol_vec_bool_eq(const __glagol_vec_bool *left, const __glagol_vec_bool *right) { if (left->len != right->len) { return false; } if (left->len == 0) { return true; } return memcmp(left->data, right->data, (size_t)left->len * sizeof(bool)) == 0; } static void __glagol_vec_string_allocation_trap(void) { fputs("slovo runtime error: vector allocation failed\n", stderr); exit(1); } static void __glagol_vec_string_index_trap(void) { fputs("slovo runtime error: vector index out of bounds\n", stderr); exit(1); } __glagol_vec_string *__glagol_vec_string_empty(void) { __glagol_vec_string *value = malloc(sizeof(__glagol_vec_string)); if (value == NULL) { __glagol_vec_string_allocation_trap(); } value->len = 0; value->data = NULL; return value; } __glagol_vec_string *__glagol_vec_string_append( const __glagol_vec_string *source, const char *element ) { if (source->len < 0 || source->len == INT32_MAX) { __glagol_vec_string_allocation_trap(); } size_t old_len = (size_t)source->len; size_t new_len = old_len + 1; if (new_len > SIZE_MAX / sizeof(char *)) { __glagol_vec_string_allocation_trap(); } __glagol_vec_string *value = malloc(sizeof(__glagol_vec_string)); if (value == NULL) { __glagol_vec_string_allocation_trap(); } char **data = malloc(new_len * sizeof(char *)); if (data == NULL) { free(value); __glagol_vec_string_allocation_trap(); } if (old_len > 0) { memcpy(data, source->data, old_len * sizeof(char *)); } data[old_len] = (char *)element; value->len = (int32_t)new_len; value->data = data; return value; } int32_t __glagol_vec_string_len(const __glagol_vec_string *value) { return value->len; } char *__glagol_vec_string_index(const __glagol_vec_string *value, int32_t index) { if (index < 0 || index >= value->len) { __glagol_vec_string_index_trap(); } return value->data[index]; } bool __glagol_vec_string_eq(const __glagol_vec_string *left, const __glagol_vec_string *right) { if (left->len != right->len) { return false; } for (int32_t index = 0; index < left->len; index++) { if (strcmp(left->data[index], right->data[index]) != 0) { return false; } } return true; } static void __glagol_time_monotonic_unavailable_trap(void) { fputs("slovo runtime error: monotonic time unavailable\n", stderr); exit(1); } static void __glagol_time_sleep_negative_trap(void) { fputs("slovo runtime error: sleep_ms negative duration\n", stderr); exit(1); } static void __glagol_time_sleep_failed_trap(void) { fputs("slovo runtime error: sleep failed\n", stderr); exit(1); } int32_t __glagol_time_monotonic_ms(void) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { __glagol_time_monotonic_unavailable_trap(); } if (!__glagol_time_base_initialized) { __glagol_time_base = now; __glagol_time_base_initialized = true; return 0; } time_t seconds = now.tv_sec - __glagol_time_base.tv_sec; long nanoseconds = now.tv_nsec - __glagol_time_base.tv_nsec; if (nanoseconds < 0) { seconds -= 1; nanoseconds += 1000000000L; } if (seconds < 0) { return 0; } if (seconds > INT32_MAX / 1000) { return INT32_MAX; } int64_t milliseconds = (int64_t)seconds * 1000 + nanoseconds / 1000000L; if (milliseconds > INT32_MAX) { return INT32_MAX; } return (int32_t)milliseconds; } void __glagol_time_sleep_ms(int32_t ms) { if (ms < 0) { __glagol_time_sleep_negative_trap(); } struct timespec remaining; remaining.tv_sec = ms / 1000; remaining.tv_nsec = (long)(ms % 1000) * 1000000L; while (nanosleep(&remaining, &remaining) != 0) { if (errno != EINTR) { __glagol_time_sleep_failed_trap(); } } } static void __glagol_random_i32_unavailable_trap(void) { fputs("slovo runtime error: random i32 unavailable\n", stderr); exit(1); } int32_t __glagol_random_i32(void) { int fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { __glagol_random_i32_unavailable_trap(); } uint32_t value = 0; unsigned char *cursor = (unsigned char *)&value; size_t remaining = sizeof(value); while (remaining > 0) { ssize_t n = read(fd, cursor, remaining); if (n < 0) { if (errno == EINTR) { continue; } close(fd); __glagol_random_i32_unavailable_trap(); } if (n == 0) { close(fd); __glagol_random_i32_unavailable_trap(); } cursor += n; remaining -= (size_t)n; } if (close(fd) != 0) { __glagol_random_i32_unavailable_trap(); } return (int32_t)(value & INT32_MAX); } void __glagol_array_bounds_trap(void) { fputs("slovo runtime error: array index out of bounds\n", stderr); exit(1); } void __glagol_unwrap_some_trap(void) { fputs("slovo runtime error: unwrap_some on none\n", stderr); exit(1); } void __glagol_unwrap_ok_trap(void) { fputs("slovo runtime error: unwrap_ok on err\n", stderr); exit(1); } void __glagol_unwrap_err_trap(void) { fputs("slovo runtime error: unwrap_err on ok\n", stderr); exit(1); }