#include "lang/env.h"
#include "table/sym.h"
#include "table/dict.h"
#include "ops/temporal.h"
#include "ops/linkop.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
static void fn_set_name(ray_t* obj, const char* name) {
memset(obj->nullmap, 0, 16);
size_t len = strlen(name);
if (len > 13) len = 13;
memcpy(obj->nullmap + 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)) {
#if defined(__x86_64__) || defined(__i386__)
__builtin_ia32_pause();
#endif
}
}
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;
#define SCOPE_CAP 64
#define FRAME_CAP 64
typedef struct {
int64_t keys[FRAME_CAP];
ray_t* vals[FRAME_CAP];
int32_t count;
} ray_scope_frame_t;
static _Thread_local ray_scope_frame_t scope_stack[SCOPE_CAP];
static _Thread_local int32_t scope_depth = 0;
int32_t ray_env_scope_depth(void) { return scope_depth; }
int32_t ray_env_global_count(void) { return g_env.count; }
ray_err_t ray_env_init(void) {
memset(&g_env, 0, sizeof(g_env));
scope_depth = 0;
return RAY_OK;
}
void ray_env_destroy(void) {
while (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));
}
static ray_t* env_lookup_flat(int64_t sym_id) {
for (int32_t d = scope_depth - 1; d >= 0; d--) {
ray_scope_frame_t* f = &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(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 = scope_depth - 1; d >= 0 && !fn; d--) {
ray_scope_frame_t* f = &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 = &scope_stack[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 >= FRAME_CAP) return RAY_ERR_OOM;
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 (scope_depth <= 0) return NULL;
ray_scope_frame_t* f = &scope_stack[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)) 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);
}
ray_err_t ray_env_push_scope(void) {
if (scope_depth >= SCOPE_CAP) return RAY_ERR_OOM;
scope_stack[scope_depth].count = 0;
scope_depth++;
return RAY_OK;
}
void ray_env_pop_scope(void) {
if (scope_depth <= 0) return;
scope_depth--;
ray_scope_frame_t* f = &scope_stack[scope_depth];
for (int32_t i = 0; i < f->count; i++) {
if (f->vals[i]) ray_release(f->vals[i]);
}
f->count = 0;
}
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)) return RAY_ERR_RESERVED;
if (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);
}