Skip to content

Commit c8dd49f

Browse files
gh-18: Allow function overriding.
1 parent 466f3b4 commit c8dd49f

File tree

4 files changed

+55
-21
lines changed

4 files changed

+55
-21
lines changed

lib/csprng.pre

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,4 @@ FUNC RANGE_MIN_MAX(INT: min, INT: max):INT{
161161
ASSERT( GT(range, 0) )
162162
INT: offset = RANGE(range)
163163
RETURN( ADD(offset, min) )
164-
}
164+
}

src/builtins.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,7 +2213,7 @@ static Value deser_val(JsonValue* obj, UnserCtx* ctx, Interpreter* interp, const
22132213

22142214
JsonValue* nm = json_obj_get(obj, "name");
22152215
if (nm && nm->type == JSON_STR) {
2216-
Func* existing = func_table_lookup(interp->functions, nm->as.str);
2216+
Func* existing = func_table_lookup(interp->functions, nm->as.str, NULL);
22172217
if (existing) {
22182218
if (id) unser_func_set(ctx, id, existing);
22192219
return value_func(existing);
@@ -4959,7 +4959,7 @@ static Value builtin_signature(Interpreter* interp, Value* args, int argc, Expr*
49594959

49604960
// If not in environment or not a function there, check the interpreter function table
49614961
Func* ff = NULL;
4962-
if (interp && interp->functions) ff = func_table_lookup(interp->functions, name);
4962+
if (interp && interp->functions) ff = func_table_lookup(interp->functions, name, env);
49634963
if (ff != NULL) {
49644964
struct Func* f = ff;
49654965
size_t cap = 256;

src/interpreter.c

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,22 @@ static int label_map_find(LabelMap* map, Value key) {
153153

154154
// ============ Function table ============
155155

156+
// Walk to the root of an environment chain.
157+
static Env* env_root(Env* env) {
158+
while (env && env->parent) {
159+
env = env->parent;
160+
}
161+
return env;
162+
}
163+
164+
// Returns true when `ancestor` appears in the parent chain of `env`.
165+
static bool env_is_ancestor(Env* ancestor, Env* env) {
166+
for (Env* e = env; e != NULL; e = e->parent) {
167+
if (e == ancestor) return true;
168+
}
169+
return false;
170+
}
171+
156172
FuncTable* func_table_create(void) {
157173
FuncTable* table = safe_malloc(sizeof(FuncTable));
158174
return table;
@@ -205,16 +221,17 @@ void func_table_free(FuncTable* table) {
205221
}
206222

207223
bool func_table_add(FuncTable* table, const char* name, Func* func) {
208-
// Check if already exists
209-
FuncEntry* entry = table->entries;
210-
while (entry) {
211-
if (strcmp(entry->name, name) == 0) {
212-
return false; // Already exists
224+
for (FuncEntry* entry = table->entries; entry != NULL; entry = entry->next) {
225+
if (strcmp(entry->name, name) != 0) continue;
226+
// If either the existing entry or the new registration is an
227+
// operator (func == NULL), do not allow the registration.
228+
if (entry->func == NULL || func == NULL) {
229+
return false; // Operator names cannot be overridden or shadowed
213230
}
214-
entry = entry->next;
231+
// Otherwise both are user functions: allow shadowing by inserting
232+
// the new entry at the head (no rejection).
215233
}
216-
217-
// Add new entry
234+
218235
FuncEntry* new_entry = safe_malloc(sizeof(FuncEntry));
219236
new_entry->name = strdup(name);
220237
new_entry->func = func;
@@ -224,15 +241,32 @@ bool func_table_add(FuncTable* table, const char* name, Func* func) {
224241
return true;
225242
}
226243

227-
Func* func_table_lookup(FuncTable* table, const char* name) {
228-
FuncEntry* entry = table->entries;
229-
while (entry) {
230-
if (strcmp(entry->name, name) == 0) {
231-
return entry->func;
244+
Func* func_table_lookup(FuncTable* table, const char* name, Env* caller_env) {
245+
Func* fallback = NULL;
246+
Env* caller_root = env_root(caller_env);
247+
248+
for (FuncEntry* entry = table->entries; entry != NULL; entry = entry->next) {
249+
if (strcmp(entry->name, name) != 0) continue;
250+
Func* f = entry->func;
251+
if (!f) continue;
252+
253+
// Prefer functions whose closure is an ancestor of the caller's env.
254+
if (f->closure && caller_env && env_is_ancestor(f->closure, caller_env)) {
255+
return f;
256+
}
257+
258+
// Next preference: matching environment roots (same module/global tree).
259+
Env* closure_root = env_root(f->closure);
260+
if (!fallback && closure_root && caller_root && closure_root == caller_root) {
261+
fallback = f;
262+
continue;
232263
}
233-
entry = entry->next;
264+
265+
// As a final option, remember the first match.
266+
if (!fallback) fallback = f;
234267
}
235-
return NULL;
268+
269+
return fallback;
236270
}
237271

238272
// ============ Value truthiness ============
@@ -402,7 +436,7 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
402436
bool initialized;
403437
if (!env_get(env, expr->as.ident, &v, &dtype, &initialized)) {
404438
// Check if it's a function name
405-
Func* func = func_table_lookup(interp->functions, expr->as.ident);
439+
Func* func = func_table_lookup(interp->functions, expr->as.ident, env);
406440
if (func) {
407441
return value_func(func);
408442
}
@@ -624,7 +658,7 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
624658
}
625659

626660
// Check user-defined functions
627-
user_func = func_table_lookup(interp->functions, func_name);
661+
user_func = func_table_lookup(interp->functions, func_name, env);
628662
if (!user_func) {
629663
// If not found, check if it's a variable holding a function
630664
Value v;

src/interpreter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ int interpreter_restart_thread(Interpreter* interp, Value thr_val, int line, int
113113
FuncTable* func_table_create(void);
114114
void func_table_free(FuncTable* table);
115115
bool func_table_add(FuncTable* table, const char* name, Func* func);
116-
Func* func_table_lookup(FuncTable* table, const char* name);
116+
Func* func_table_lookup(FuncTable* table, const char* name, Env* caller_env);
117117

118118
// Module registry helpers
119119
// Register a module name and create its isolated Env. Returns 0 on success, -1 on error.

0 commit comments

Comments
 (0)