-
Notifications
You must be signed in to change notification settings - Fork 3
feat(datalog): aggregates, float constants, between sugar, and typed head constants #7
Changes from all commits
2665998
717fab8
875e102
6a5dc03
6a56026
d37388d
279a6d3
d211a5b
c78a34b
faaee1f
e5c156a
0c3652d
6793bda
e8c43b7
c1933f8
c783977
7351910
4724a6e
a626ccb
a3fe8f3
8c68967
086a41a
4e9b4de
f80b228
0a32bd7
2478ab4
ed48c3e
5696f04
a75a6a8
2a2b8c2
b2de655
6d0929a
ecdb0d9
06ddbc1
ed2824b
e294723
2b1846e
3a025ce
c321670
dc73dbd
e0bf053
973ee76
2759d34
86880ab
916d84a
5b7949b
3ff6a7b
d348677
2cd5741
2ecae48
23a0cb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,6 +27,8 @@ | |||||||||||
| #include <stdarg.h> | ||||||||||||
| #include <stdio.h> | ||||||||||||
| #include <string.h> | ||||||||||||
| #include <sys/stat.h> | ||||||||||||
| #include <errno.h> | ||||||||||||
| #ifdef RAY_OS_WINDOWS | ||||||||||||
| #include <windows.h> | ||||||||||||
| #else | ||||||||||||
|
|
@@ -137,6 +139,21 @@ ray_t* ray_error(const char* code, const char* fmt, ...) { | |||||||||||
| return err; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| void ray_error_free(ray_t* err) { | ||||||||||||
| /* Skip NULL and anything that isn't actually a RAY_ERROR — callers | ||||||||||||
| * often pass a result that might be either an error or a real value. */ | ||||||||||||
| if (!err || !RAY_IS_ERR(err)) return; | ||||||||||||
| /* Both ray_free and ray_release_owned_refs short-circuit on RAY_IS_ERR | ||||||||||||
| * as a safety default (the refcount system deliberately does not track | ||||||||||||
| * error objects). Retype the block to a leaf atom (-RAY_I64) so those | ||||||||||||
| * guards don't fire — an atom with no owned children is the safest | ||||||||||||
| * shape to pass through the standard free path. The rc was already | ||||||||||||
| * 1 from ray_alloc, so ray_free will reclaim the block via the buddy | ||||||||||||
| * allocator. From this point the caller must not touch err again. */ | ||||||||||||
| err->type = -RAY_I64; | ||||||||||||
| ray_free(err); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| const char* ray_err_code(ray_t* err) { | ||||||||||||
| if (!err || err->type != RAY_ERROR) return NULL; | ||||||||||||
| /* sdata is 7 bytes and may not be null-terminated when full */ | ||||||||||||
|
|
@@ -157,14 +174,17 @@ void ray_error_clear(void) { | |||||||||||
|
|
||||||||||||
| /* ===== Lifecycle ===== */ | ||||||||||||
|
|
||||||||||||
| ray_runtime_t* ray_runtime_create(int argc, char** argv) { | ||||||||||||
| (void)argc; (void)argv; | ||||||||||||
| static ray_runtime_t* runtime_create_impl(const char* sym_path, | ||||||||||||
| ray_err_t* out_sym_err) { | ||||||||||||
| if (out_sym_err) *out_sym_err = RAY_OK; | ||||||||||||
|
|
||||||||||||
| /* Init subsystems */ | ||||||||||||
| ray_heap_init(); | ||||||||||||
| ray_sym_init(); | ||||||||||||
|
|
||||||||||||
| /* Allocate runtime via system allocator */ | ||||||||||||
| /* Allocate runtime and set __VM + mem_budget BEFORE any file I/O so | ||||||||||||
| * that ray_error() has a live VM to record diagnostics against and | ||||||||||||
| * allocations are bounded by the budget. */ | ||||||||||||
| ray_runtime_t* rt = (ray_runtime_t*)ray_sys_alloc(sizeof(ray_runtime_t)); | ||||||||||||
| if (!rt) return NULL; | ||||||||||||
| memset(rt, 0, sizeof(*rt)); | ||||||||||||
|
|
@@ -196,13 +216,70 @@ ray_runtime_t* ray_runtime_create(int argc, char** argv) { | |||||||||||
| rt->mem_budget = (int64_t)(4ULL << 30); | ||||||||||||
| #endif | ||||||||||||
|
|
||||||||||||
| /* Init language (env + builtins) — must be after __VM is set */ | ||||||||||||
| /* __RUNTIME must be visible before ray_sym_load so mem_budget checks | ||||||||||||
| * and ray_error() both operate against the live runtime. */ | ||||||||||||
| __RUNTIME = rt; | ||||||||||||
|
|
||||||||||||
| /* Load persisted symbol table BEFORE ray_lang_init interns builtins. | ||||||||||||
| * Ordering: __VM + mem_budget are live so file I/O errors surface via | ||||||||||||
| * ray_error() and allocations are budget-bounded. Still before | ||||||||||||
| * ray_lang_init so persisted user symbol IDs keep their slots and | ||||||||||||
| * builtins append afterwards. */ | ||||||||||||
| if (sym_path) { | ||||||||||||
| /* Pre-flight size check: reject files that would blow past the | ||||||||||||
| * memory budget before ever touching ray_col_load. | ||||||||||||
| * | ||||||||||||
| * errno handling: ENOENT is the normal first-run case and stays | ||||||||||||
| * RAY_OK; any *other* stat failure (EACCES, ENOTDIR, EIO, …) is | ||||||||||||
| * a real problem and must be surfaced as RAY_ERR_IO, otherwise | ||||||||||||
| * the caller would silently continue with an empty sym table | ||||||||||||
| * and later hit the "divergence" class of bugs this entrypoint | ||||||||||||
| * was added to avoid. */ | ||||||||||||
| struct stat st; | ||||||||||||
| if (stat(sym_path, &st) == 0) { | ||||||||||||
| /* Allow the sym file itself plus some working headroom (2x). | ||||||||||||
| * A well-formed sym file is a list of interned strings; the | ||||||||||||
| * in-memory footprint is bounded by file size within a small | ||||||||||||
| * constant factor. */ | ||||||||||||
| if (st.st_size > 0 && | ||||||||||||
| (int64_t)st.st_size > rt->mem_budget / 2) { | ||||||||||||
| if (out_sym_err) *out_sym_err = RAY_ERR_OOM; | ||||||||||||
| /* Continue startup with empty sym table; caller decides | ||||||||||||
| * whether to treat this as fatal. */ | ||||||||||||
| } else { | ||||||||||||
| ray_err_t sym_err = ray_sym_load(sym_path); | ||||||||||||
| if (out_sym_err) *out_sym_err = sym_err; | ||||||||||||
| /* RAY_ERR_CORRUPT and I/O errors are non-fatal here: | ||||||||||||
| * caller inspects out_sym_err to decide recovery. */ | ||||||||||||
| } | ||||||||||||
| } else if (errno != ENOENT) { | ||||||||||||
| if (out_sym_err) *out_sym_err = RAY_ERR_IO; | ||||||||||||
| } | ||||||||||||
| /* ENOENT: leave out_sym_err = RAY_OK — absent sym file is the | ||||||||||||
| * normal first-run case. */ | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+228
to
+260
|
||||||||||||
|
|
||||||||||||
| /* Init language (env + builtins) — must be after __VM is set and | ||||||||||||
| * after sym_load so persisted user IDs keep their slots. */ | ||||||||||||
| ray_lang_init(); | ||||||||||||
|
|
||||||||||||
| __RUNTIME = rt; | ||||||||||||
| return rt; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| ray_runtime_t* ray_runtime_create(int argc, char** argv) { | ||||||||||||
| (void)argc; (void)argv; | ||||||||||||
| return runtime_create_impl(NULL, NULL); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| ray_runtime_t* ray_runtime_create_with_sym(const char* sym_path) { | ||||||||||||
| return runtime_create_impl(sym_path, NULL); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| ray_runtime_t* ray_runtime_create_with_sym_err(const char* sym_path, | ||||||||||||
| ray_err_t* out_sym_err) { | ||||||||||||
| return runtime_create_impl(sym_path, out_sym_err); | ||||||||||||
| } | ||||||||||||
|
singaraiona marked this conversation as resolved.
|
||||||||||||
|
|
||||||||||||
|
||||||||||||
| /* NOTE: These public entrypoints must also be declared in src/core/runtime.h | |
| * and include/rayforce.h to keep the API surface synchronized. | |
| */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,10 +103,20 @@ extern _Thread_local ray_vm_t *__VM; | |
| ray_runtime_t* ray_runtime_create(int argc, char** argv); | ||
| void ray_runtime_destroy(ray_runtime_t* rt); | ||
|
|
||
| /* Persistent-consumer lifecycle: load the sym table from `sym_path` (if | ||
| * present) before builtins register, so user-interned IDs keep the same | ||
| * slots across process restarts. The _err variant surfaces the load | ||
| * result via `out_sym_err` (RAY_OK / RAY_ERR_CORRUPT / I/O errors) so | ||
| * callers can decide recovery policy; the plain variant discards it. */ | ||
| ray_runtime_t* ray_runtime_create_with_sym(const char* sym_path); | ||
| ray_runtime_t* ray_runtime_create_with_sym_err(const char* sym_path, | ||
| ray_err_t* out_sym_err); | ||
|
Comment on lines
+106
to
+113
|
||
|
|
||
| /* Error API — allocates ray_t with type=RAY_ERROR, sets __VM->err.msg */ | ||
| ray_t* ray_error(const char* code, const char* fmt, ...); | ||
| /* Read error code from a RAY_ERROR object (returns pointer to sdata) */ | ||
| const char* ray_err_code(ray_t* err); | ||
| /* ray_error_free() is published in include/rayforce.h */ | ||
|
|
||
| /* Read VM error detail message (NULL if empty) */ | ||
| const char* ray_error_msg(void); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
runtime_create_impl treats any stat() failure as “no sym file” (out_sym_err stays RAY_OK) and skips ray_sym_load entirely. That hides real I/O problems (e.g., EACCES) and defeats the whole "load sym before builtins" guarantee for persistent consumers. Consider distinguishing ENOENT from other errors and either (a) attempt ray_sym_load directly and rely on its error codes, or (b) set out_sym_err appropriately for non-ENOENT stat failures.