#include "lang/env.h"
#include "core/runtime.h"
#include "core/platform.h"
#include "table/sym.h"
#include "table/dict.h"
#include "ops/temporal.h"
#include "ops/linkop.h"
#include "mem/sys.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
static void fn_set_name(ray_t* obj, const char* name) {
memset(obj->aux, 0, 16);
size_t len = strlen(name);
if (len > 13) len = 13;
memcpy(obj->aux + 2, name, len);
}
ray_t* ray_fn_unary(const char* name, uint8_t fn_attrs, ray_unary_fn fn) {
ray_t* obj = ray_alloc(0);
if (!obj) return ray_error("oom", NULL);
obj->type = RAY_UNARY;
obj->attrs = fn_attrs;
obj->i64 = (int64_t)(uintptr_t)fn;
fn_set_name(obj, name);
return obj;
}
ray_t* ray_fn_binary(const char* name, uint8_t fn_attrs, ray_binary_fn fn) {
ray_t* obj = ray_alloc(0);
if (!obj) return ray_error("oom", NULL);
obj->type = RAY_BINARY;
obj->attrs = fn_attrs;
obj->i64 = (int64_t)(uintptr_t)fn;
fn_set_name(obj, name);
return obj;
}
ray_t* ray_fn_vary(const char* name, uint8_t fn_attrs, ray_vary_fn fn) {
ray_t* obj = ray_alloc(0);
if (!obj) return ray_error("oom", NULL);
obj->type = RAY_VARY;
obj->attrs = fn_attrs;
obj->i64 = (int64_t)(uintptr_t)fn;
fn_set_name(obj, name);
return obj;
}
static _Atomic(int) g_env_lock = 0;
static inline void env_lock(void) {
while (atomic_exchange_explicit(&g_env_lock, 1, memory_order_acquire)) {
RAY_CPU_RELAX();
}
}
static inline void env_unlock(void) {
atomic_store_explicit(&g_env_lock, 0, memory_order_release);
}
#define ENV_CAP 1024
static struct {
int64_t keys[ENV_CAP];
ray_t* vals[ENV_CAP];
uint8_t user[ENV_CAP];
int32_t count;
} g_env;
int32_t ray_env_scope_depth(void) { return __VM ? __VM->scope_depth : 0; }
int32_t ray_env_global_count(void) { return g_env.count; }
int64_t ray_env_builtin_sym(const ray_t* fn) {
if (!fn) return -1;
for (int32_t i = 0; i < g_env.count; i++)
if (g_env.vals[i] == fn) return g_env.keys[i];
return -1;
}
ray_t* ray_env_get_lexical_local(int64_t sym_id) {
if (!__VM) return NULL;
for (int32_t d = __VM->scope_depth - 1; d >= 0; d--) {
ray_scope_frame_t* f = &__VM->scope_stack[d];
if (f->kind == RAY_SCOPE_QUERY) continue;
for (int32_t i = 0; i < f->count; i++)
if (f->keys[i] == sym_id) return f->vals[i];
}
return NULL;
}
bool ray_env_has_lexical_local(int64_t sym_id) {
return ray_env_get_lexical_local(sym_id) != NULL;
}
ray_err_t ray_env_set_query_local(int64_t sym_id, ray_t* val) {
return ray_env_has_lexical_local(sym_id) ? RAY_OK
: ray_env_set_local(sym_id, val);
}
ray_t* ray_env_capture_locals(void) {
if (!__VM || __VM->scope_depth == 0) return NULL;
int64_t capacity = 0;
for (int32_t d = 0; d < __VM->scope_depth; d++)
capacity += __VM->scope_stack[d].count;
if (capacity == 0) return NULL;
ray_t* keys = ray_sym_vec_new(RAY_SYM_W64, capacity);
ray_t* vals = ray_list_new(capacity);
if (!keys || RAY_IS_ERR(keys) || !vals || RAY_IS_ERR(vals)) {
if (keys && !RAY_IS_ERR(keys)) ray_release(keys);
if (vals && !RAY_IS_ERR(vals)) ray_release(vals);
return ray_error("oom", NULL);
}
int64_t* key_ids = (int64_t*)ray_data(keys);
for (int32_t d = __VM->scope_depth - 1; d >= 0; d--) {
ray_scope_frame_t* f = &__VM->scope_stack[d];
for (int32_t i = 0; i < f->count; i++) {
if (!f->vals[i]) continue;
bool seen = false;
for (int64_t k = 0; k < keys->len; k++)
if (key_ids[k] == f->keys[i]) { seen = true; break; }
if (seen) continue;
key_ids[keys->len++] = f->keys[i];
vals = ray_list_append(vals, f->vals[i]);
if (!vals || RAY_IS_ERR(vals)) {
ray_release(keys);
return vals ? vals : ray_error("oom", NULL);
}
}
}
if (keys->len == 0) {
ray_release(keys);
ray_release(vals);
return NULL;
}
return ray_dict_new(keys, vals);
}
ray_err_t ray_env_push_capture(ray_t* capture) {
if (ray_env_push_scope() != RAY_OK) return RAY_ERR_OOM;
if (!capture) return RAY_OK;
if (capture->type != RAY_DICT) {
ray_env_pop_scope();
return RAY_ERR_TYPE;
}
ray_t* keys = ray_dict_keys(capture);
ray_t* vals = ray_dict_vals(capture);
if (!keys || keys->type != RAY_SYM || !vals || vals->type != RAY_LIST ||
keys->len != vals->len) {
ray_env_pop_scope();
return RAY_ERR_TYPE;
}
ray_t** value_items = (ray_t**)ray_data(vals);
for (int64_t i = 0; i < keys->len; i++) {
int64_t sym = ray_read_sym(ray_data(keys), i, RAY_SYM, keys->attrs);
ray_err_t err = ray_env_set_local(sym, value_items[i]);
if (err != RAY_OK) {
ray_env_pop_scope();
return err;
}
}
return RAY_OK;
}
static int64_t g_ipc_hook_syms[5] = {0};
static bool g_ipc_hook_syms_ready = false;
static int64_t g_self_sym = -1;
static void ipc_hook_syms_ensure(void) {
if (g_ipc_hook_syms_ready) return;
g_ipc_hook_syms[0] = ray_sym_intern(".ipc.on.open", strlen(".ipc.on.open"));
g_ipc_hook_syms[1] = ray_sym_intern(".ipc.on.close", strlen(".ipc.on.close"));
g_ipc_hook_syms[2] = ray_sym_intern(".ipc.on.sync", strlen(".ipc.on.sync"));
g_ipc_hook_syms[3] = ray_sym_intern(".ipc.on.async", strlen(".ipc.on.async"));
g_ipc_hook_syms[4] = ray_sym_intern(".ipc.on.auth", strlen(".ipc.on.auth"));
g_ipc_hook_syms_ready = true;
}
bool ray_sym_is_ipc_hook(int64_t sym_id) {
ipc_hook_syms_ensure();
for (int i = 0; i < 5; i++)
if (g_ipc_hook_syms[i] == sym_id) return true;
return false;
}
int64_t ray_sym_ipc_hook(int idx) {
if (idx < 0 || idx >= 5) return -1;
ipc_hook_syms_ensure();
return g_ipc_hook_syms[idx];
}
ray_err_t ray_env_init(void) {
memset(&g_env, 0, sizeof(g_env));
__VM->scope_depth = 0;
return RAY_OK;
}
void ray_env_destroy(void) {
while (__VM->scope_depth > 0) ray_env_pop_scope();
for (int32_t i = 0; i < g_env.count; i++) {
if (g_env.vals[i]) ray_release(g_env.vals[i]);
}
memset(&g_env, 0, sizeof(g_env));
g_ipc_hook_syms_ready = false;
g_self_sym = -1;
}
static ray_t* env_lookup_flat(int64_t sym_id) {
for (int32_t d = __VM->scope_depth - 1; d >= 0; d--) {
ray_scope_frame_t* f = &__VM->scope_stack[d];
for (int32_t i = 0; i < f->count; i++) {
if (f->keys[i] == sym_id) return f->vals[i];
}
}
for (int32_t i = 0; i < g_env.count; i++) {
if (g_env.keys[i] == sym_id) return g_env.vals[i];
}
return NULL;
}
ray_t* ray_env_get_local(int64_t sym_id) {
if (!__VM) return NULL;
for (int32_t d = __VM->scope_depth - 1; d >= 0; d--) {
ray_scope_frame_t* f = &__VM->scope_stack[d];
for (int32_t i = 0; i < f->count; i++)
if (f->keys[i] == sym_id) return f->vals[i];
}
return NULL;
}
ray_t* ray_env_get(int64_t sym_id) {
ray_t* flat = env_lookup_flat(sym_id);
if (flat) return flat;
if (!ray_sym_is_dotted(sym_id)) return NULL;
const int64_t* segs;
int n = ray_sym_segs(sym_id, &segs);
if (n < 2) return NULL;
ray_t* v = env_lookup_flat(segs[0]);
for (int i = 1; v && i < n; i++) {
v = ray_container_probe_sym(v, segs[i]);
}
return v;
}
ray_t* ray_env_resolve(int64_t sym_id) {
ray_t* flat = env_lookup_flat(sym_id);
if (flat) { ray_retain(flat); return flat; }
if (!ray_sym_is_dotted(sym_id)) return NULL;
const int64_t* segs;
int n = ray_sym_segs(sym_id, &segs);
if (n < 2) return NULL;
ray_t* v = env_lookup_flat(segs[0]);
bool fresh = false;
for (int i = 1; v && i < n; i++) {
ray_t* next = NULL;
bool next_fresh = false;
if (ray_link_has(v)) {
next = ray_link_deref(v, segs[i]);
if (next && RAY_IS_ERR(next)) {
if (fresh) ray_release(v);
return next;
}
next_fresh = (next != NULL);
}
if (!next) next = ray_container_probe_sym(v, segs[i]);
if (next) {
if (fresh) ray_release(v);
v = next;
fresh = next_fresh;
continue;
}
ray_t* fn = NULL;
for (int32_t d = __VM->scope_depth - 1; d >= 0 && !fn; d--) {
ray_scope_frame_t* f = &__VM->scope_stack[d];
for (int32_t k = 0; k < f->count; k++) {
if (f->keys[k] == segs[i] && f->vals[k]
&& f->vals[k]->type == RAY_UNARY) {
fn = f->vals[k];
break;
}
}
}
if (!fn) {
for (int32_t k = 0; k < g_env.count; k++) {
if (g_env.keys[k] == segs[i] && g_env.vals[k]
&& g_env.vals[k]->type == RAY_UNARY) {
fn = g_env.vals[k];
break;
}
}
}
if (fn) {
ray_unary_fn f = (ray_unary_fn)(uintptr_t)fn->i64;
ray_t* r = f(v);
if (fresh) ray_release(v);
if (!r || RAY_IS_ERR(r)) return NULL;
v = r;
fresh = true;
continue;
}
if (fresh) ray_release(v);
return NULL;
}
if (!v) return NULL;
if (!fresh) ray_retain(v);
return v;
}
static ray_err_t env_bind_global_impl(int64_t sym_id, ray_t* val, int is_user) {
env_lock();
for (int32_t i = 0; i < g_env.count; i++) {
if (g_env.keys[i] == sym_id) {
if (val == NULL) {
if (g_env.vals[i]) ray_release(g_env.vals[i]);
for (int32_t j = i; j + 1 < g_env.count; j++) {
g_env.keys[j] = g_env.keys[j + 1];
g_env.vals[j] = g_env.vals[j + 1];
g_env.user[j] = g_env.user[j + 1];
}
g_env.count--;
env_unlock();
return RAY_OK;
}
if (g_env.vals[i]) ray_release(g_env.vals[i]);
ray_retain(val);
g_env.vals[i] = val;
if (is_user) g_env.user[i] = 1;
env_unlock();
return RAY_OK;
}
}
if (val == NULL) {
env_unlock();
return RAY_OK;
}
if (g_env.count >= ENV_CAP) {
env_unlock();
return RAY_ERR_OOM;
}
g_env.keys[g_env.count] = sym_id;
ray_retain(val);
g_env.vals[g_env.count] = val;
g_env.user[g_env.count] = is_user ? 1 : 0;
g_env.count++;
env_unlock();
return RAY_OK;
}
static ray_err_t env_bind_global(int64_t sym_id, ray_t* val) {
return env_bind_global_impl(sym_id, val, 0);
}
static ray_err_t env_bind_global_user(int64_t sym_id, ray_t* val) {
return env_bind_global_impl(sym_id, val, 1);
}
static ray_err_t env_bind_local(int64_t sym_id, ray_t* val) {
ray_scope_frame_t* f = &__VM->scope_stack[__VM->scope_depth - 1];
for (int32_t i = 0; i < f->count; i++) {
if (f->keys[i] == sym_id) {
if (val == NULL) {
if (f->vals[i]) ray_release(f->vals[i]);
for (int32_t j = i; j + 1 < f->count; j++) {
f->keys[j] = f->keys[j + 1];
f->vals[j] = f->vals[j + 1];
}
f->count--;
return RAY_OK;
}
if (f->vals[i]) ray_release(f->vals[i]);
ray_retain(val);
f->vals[i] = val;
return RAY_OK;
}
}
if (val == NULL) return RAY_OK;
if (f->count >= f->cap) {
int32_t new_cap = f->cap * 2;
int64_t* nk = (int64_t*)ray_sys_alloc(sizeof(int64_t) * (size_t)new_cap);
ray_t** nv = (ray_t**)ray_sys_alloc(sizeof(ray_t*) * (size_t)new_cap);
if (!nk || !nv) {
if (nk) ray_sys_free(nk);
if (nv) ray_sys_free(nv);
return RAY_ERR_OOM;
}
memcpy(nk, f->keys, sizeof(int64_t) * (size_t)f->count);
memcpy(nv, f->vals, sizeof(ray_t*) * (size_t)f->count);
if (f->keys != f->keys_inline) ray_sys_free(f->keys);
if (f->vals != f->vals_inline) ray_sys_free(f->vals);
f->keys = nk;
f->vals = nv;
f->cap = new_cap;
}
f->keys[f->count] = sym_id;
ray_retain(val);
f->vals[f->count] = val;
f->count++;
return RAY_OK;
}
static ray_err_t env_set_dotted(int64_t sym_id, ray_t* val,
ray_t* (*base_lookup)(int64_t),
ray_err_t (*bind_fn)(int64_t, ray_t*)) {
const int64_t* segs;
int n = ray_sym_segs(sym_id, &segs);
if (n < 2) return RAY_ERR_TYPE;
ray_t* parents[256];
parents[0] = base_lookup(segs[0]);
if (parents[0] && parents[0]->type != RAY_DICT)
return RAY_ERR_TYPE;
for (int i = 1; i < n - 1; i++) {
if (!parents[i - 1]) { parents[i] = NULL; continue; }
ray_t* child = ray_dict_probe_sym_borrowed(parents[i - 1], segs[i]);
if (child && child->type != RAY_DICT)
return RAY_ERR_TYPE;
parents[i] = child;
}
int start_i;
ray_t* cur;
bool deleting = (val == NULL);
if (deleting) {
ray_t* leaf_parent = parents[n - 2];
if (!leaf_parent) return RAY_OK;
if (!ray_dict_probe_sym_borrowed(leaf_parent, segs[n - 1])) return RAY_OK;
ray_retain(leaf_parent);
ray_t* k = ray_sym(segs[n - 1]);
cur = ray_dict_remove(leaf_parent, k);
ray_release(k);
if (!cur || RAY_IS_ERR(cur)) return RAY_ERR_OOM;
start_i = n - 2;
} else {
ray_retain(val);
cur = val;
start_i = n - 1;
}
for (int i = start_i; i >= 1; i--) {
ray_t* parent = parents[i - 1];
if (deleting && cur && cur->type == RAY_DICT && ray_dict_len(cur) == 0) {
ray_release(cur);
if (!parent) { cur = NULL; break; }
ray_retain(parent);
ray_t* k = ray_sym(segs[i]);
cur = ray_dict_remove(parent, k);
ray_release(k);
if (!cur || RAY_IS_ERR(cur)) return RAY_ERR_OOM;
continue;
}
ray_t* dict_in;
if (parent) {
ray_retain(parent);
dict_in = parent;
} else {
ray_t* keys = ray_sym_vec_new(RAY_SYM_W64, 1);
ray_t* vals = ray_list_new(1);
dict_in = ray_dict_new(keys, vals);
if (!dict_in || RAY_IS_ERR(dict_in)) { ray_release(cur); return RAY_ERR_OOM; }
}
ray_t* k = ray_sym(segs[i]);
ray_t* next = ray_dict_upsert(dict_in, k, cur);
ray_release(k);
ray_release(cur);
if (!next || RAY_IS_ERR(next)) return RAY_ERR_OOM;
cur = next;
}
ray_t* to_bind = cur;
if (deleting && cur && cur->type == RAY_DICT && ray_dict_len(cur) == 0) {
to_bind = NULL;
}
ray_err_t err = bind_fn(segs[0], to_bind);
if (cur) ray_release(cur);
return err;
}
static ray_t* lookup_global(int64_t sym_id) {
for (int32_t i = 0; i < g_env.count; i++) {
if (g_env.keys[i] == sym_id) return g_env.vals[i];
}
return NULL;
}
static ray_t* lookup_top_frame(int64_t sym_id) {
if (__VM->scope_depth <= 0) return NULL;
ray_scope_frame_t* f = &__VM->scope_stack[__VM->scope_depth - 1];
for (int32_t i = 0; i < f->count; i++) {
if (f->keys[i] == sym_id) return f->vals[i];
}
return NULL;
}
bool ray_sym_is_reserved(int64_t sym_id) {
ray_t* s = ray_sym_str(sym_id);
if (!s) return false;
const char* p = ray_str_ptr(s);
size_t n = ray_str_len(s);
return n > 0 && p && p[0] == '.';
}
ray_err_t ray_env_bind(int64_t sym_id, ray_t* val) {
if (ray_sym_is_dotted(sym_id)) {
return env_set_dotted(sym_id, val, lookup_global, env_bind_global);
}
return env_bind_global(sym_id, val);
}
ray_err_t ray_env_bind_flat(int64_t sym_id, ray_t* val) {
return env_bind_global(sym_id, val);
}
ray_err_t ray_env_set(int64_t sym_id, ray_t* val) {
if (ray_sym_is_reserved(sym_id) && !ray_sym_is_ipc_hook(sym_id))
return RAY_ERR_RESERVED;
if (ray_sym_is_dotted(sym_id))
return env_set_dotted(sym_id, val, lookup_global, env_bind_global_user);
return env_bind_global_user(sym_id, val);
}
static ray_err_t env_push_scope(uint8_t kind) {
if (__VM->scope_depth >= RAY_SCOPE_CAP) return RAY_ERR_OOM;
ray_scope_frame_t* f = &__VM->scope_stack[__VM->scope_depth];
f->keys = f->keys_inline;
f->vals = f->vals_inline;
f->cap = RAY_FRAME_CAP;
f->count = 0;
f->kind = kind;
__VM->scope_depth++;
return RAY_OK;
}
ray_err_t ray_env_push_scope(void) {
return env_push_scope(RAY_SCOPE_LEXICAL);
}
ray_err_t ray_env_push_query_scope(void) {
return env_push_scope(RAY_SCOPE_QUERY);
}
void ray_env_pop_scope(void) {
if (__VM->scope_depth <= 0) return;
__VM->scope_depth--;
ray_scope_frame_t* f = &__VM->scope_stack[__VM->scope_depth];
for (int32_t i = 0; i < f->count; i++) {
if (f->vals[i]) ray_release(f->vals[i]);
}
if (f->keys != f->keys_inline) ray_sys_free(f->keys);
if (f->vals != f->vals_inline) ray_sys_free(f->vals);
f->keys = f->keys_inline;
f->vals = f->vals_inline;
f->cap = RAY_FRAME_CAP;
f->count = 0;
f->kind = RAY_SCOPE_LEXICAL;
}
ray_err_t ray_env_scope_bind(const int64_t* syms, int32_t n, ray_t** slots,
ray_t* self_obj) {
ray_err_t err = ray_env_push_scope();
if (err != RAY_OK) return err;
if (self_obj) {
if (g_self_sym < 0) g_self_sym = ray_sym_intern("self", 4);
(void)ray_env_set_local(g_self_sym, self_obj);
}
for (int32_t i = 0; i < n; i++) {
if (!slots[i]) continue;
ray_err_t e = ray_env_set_local(syms[i], slots[i]);
if (e != RAY_OK && e != RAY_ERR_RESERVED) {
ray_env_pop_scope();
return e;
}
}
return RAY_OK;
}
void ray_env_scope_sync(const int64_t* syms, int32_t n, ray_t** slots) {
if (__VM->scope_depth > 0) {
ray_scope_frame_t* f = &__VM->scope_stack[__VM->scope_depth - 1];
for (int32_t i = 0; i < n; i++) {
for (int32_t k = 0; k < f->count; k++) {
if (f->keys[k] != syms[i]) continue;
if (f->vals[k] != slots[i]) {
ray_retain(f->vals[k]);
if (slots[i]) ray_release(slots[i]);
slots[i] = f->vals[k];
}
break;
}
}
}
ray_env_pop_scope();
}
int32_t ray_env_list(int64_t* sym_ids, ray_t** vals, int32_t max_entries) {
int32_t n = g_env.count < max_entries ? g_env.count : max_entries;
for (int32_t i = 0; i < n; i++) {
sym_ids[i] = g_env.keys[i];
vals[i] = g_env.vals[i];
}
return n;
}
int32_t ray_env_list_user(int64_t* sym_ids, ray_t** vals, int32_t max_entries) {
int32_t out = 0;
for (int32_t i = 0; i < g_env.count && out < max_entries; i++) {
if (!g_env.user[i]) continue;
sym_ids[out] = g_env.keys[i];
vals[out] = g_env.vals[i];
out++;
}
return out;
}
static const char* s_keywords[] = {
"def", "do", "false", "fn", "if", "let", "set", "true", NULL
};
static int cmp_str_ptr(const void* a, const void* b) {
return strcmp(*(const char**)a, *(const char**)b);
}
bool ray_env_has_name(const char* name, int64_t len) {
if (!name || len <= 0) return false;
for (int32_t i = 0; i < g_env.count; i++) {
ray_t* s = ray_sym_str(g_env.keys[i]);
if (!s) continue;
const char* n = ray_str_ptr(s);
if (!n) continue;
if ((int64_t)strlen(n) == len && memcmp(n, name, (size_t)len) == 0)
return true;
}
for (const char** kw = s_keywords; *kw; kw++) {
if ((int64_t)strlen(*kw) == len && memcmp(*kw, name, (size_t)len) == 0)
return true;
}
return false;
}
int64_t ray_env_lookup_prefix(const char* prefix, int64_t len,
const char** results, int64_t max_results) {
int64_t count = 0;
for (int32_t i = 0; i < g_env.count && count < max_results; i++) {
ray_t* s = ray_sym_str(g_env.keys[i]);
if (!s) continue;
const char* name = ray_str_ptr(s);
if (!name) continue;
int64_t nlen = (int64_t)strlen(name);
if (nlen >= len && strncmp(name, prefix, (size_t)len) == 0) {
int dup = 0;
for (int64_t j = 0; j < count; j++) {
if (strcmp(results[j], name) == 0) { dup = 1; break; }
}
if (!dup) results[count++] = name;
}
}
for (const char** kw = s_keywords; *kw && count < max_results; kw++) {
int64_t klen = (int64_t)strlen(*kw);
if (klen >= len && strncmp(*kw, prefix, (size_t)len) == 0) {
int dup = 0;
for (int64_t j = 0; j < count; j++) {
if (strcmp(results[j], *kw) == 0) { dup = 1; break; }
}
if (!dup) results[count++] = *kw;
}
}
if (count > 1) {
qsort((void*)results, (size_t)count, sizeof(const char*), cmp_str_ptr);
}
return count;
}
ray_err_t ray_env_set_local(int64_t sym_id, ray_t* val) {
if (ray_sym_is_reserved(sym_id) && !ray_sym_is_ipc_hook(sym_id))
return RAY_ERR_RESERVED;
if (__VM->scope_depth <= 0) return ray_env_set(sym_id, val);
if (ray_sym_is_dotted(sym_id)) {
return env_set_dotted(sym_id, val, lookup_top_frame, env_bind_local);
}
return env_bind_local(sym_id, val);
}