@@ -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+
156172FuncTable * 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
207223bool 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 ;
0 commit comments