Skip to content

Commit 61ad9a8

Browse files
gh-97: Pointer args bind for qualified imported function calls.
1 parent 852c571 commit 61ad9a8

File tree

5 files changed

+120
-12
lines changed

5 files changed

+120
-12
lines changed

src/env.c

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#define strdup _strdup
2323
#endif
2424

25+
/* Forward declarations for local helpers used before their definitions. */
26+
static EnvEntry* env_find_local(Env* env, const char* name);
27+
2528
/* ================================================================== */
2629
/* Thread-local snapshots for env_get_entry */
2730
/* ================================================================== */
@@ -53,6 +56,7 @@ static void env_entry_snap_clear(EnvEntry* e) {
5356
free(e->alias_target);
5457
e->alias_target = NULL;
5558
}
59+
e->alias_target_env = NULL;
5660
value_free(e->value);
5761
e->value = value_null();
5862
e->decl_type = TYPE_UNKNOWN;
@@ -80,9 +84,23 @@ static void env_entry_snap_from_raw(EnvEntry* dst, const EnvEntry* src) {
8084
dst->frozen = src->frozen;
8185
dst->permafrozen = src->permafrozen;
8286
dst->alias_target = src->alias_target ? strdup(src->alias_target) : NULL;
87+
dst->alias_target_env = src->alias_target_env;
8388
dst->value = value_copy(src->value);
8489
}
8590

91+
// Helper: like env_get_entry_raw but also returns the Env* where the
92+
// entry was found via out_env (if non-NULL).
93+
static EnvEntry* env_get_entry_raw_with_env(Env* env, const char* name, Env** out_env) {
94+
for (Env* e = env; e != NULL; e = e->parent) {
95+
EnvEntry* entry = env_find_local(e, name);
96+
if (entry) {
97+
if (out_env) *out_env = e;
98+
return entry;
99+
}
100+
}
101+
return NULL;
102+
}
103+
86104
/* ================================================================== */
87105
/* Helpers */
88106
/* ================================================================== */
@@ -143,11 +161,7 @@ static EnvEntry* env_find_local(Env* env, const char* name) {
143161
}
144162

145163
static EnvEntry* env_get_entry_raw(Env* env, const char* name) {
146-
for (Env* e = env; e != NULL; e = e->parent) {
147-
EnvEntry* entry = env_find_local(e, name);
148-
if (entry) return entry;
149-
}
150-
return NULL;
164+
return env_get_entry_raw_with_env(env, name, NULL);
151165
}
152166

153167
static bool env_get_raw(Env* env, const char* name,
@@ -158,10 +172,12 @@ static bool env_get_raw(Env* env, const char* name,
158172
if (entry) {
159173
/* Follow alias chain to the final target */
160174
EnvEntry* cur = entry;
175+
Env* cur_env = e;
161176
int depth = 0;
162177
while (cur && cur->alias_target) {
163178
if (depth++ > 256) return false; /* cycle or too deep */
164-
cur = env_get_entry_raw(env, cur->alias_target);
179+
Env* lookup_env = cur->alias_target_env ? cur->alias_target_env : cur_env;
180+
cur = env_get_entry_raw_with_env(lookup_env, cur->alias_target, &cur_env);
165181
}
166182
if (!cur) return false;
167183
if (out_value) *out_value = value_copy(cur->value);
@@ -231,6 +247,7 @@ bool env_define_direct(Env* env, const char* name, DeclType type) {
231247
entry->frozen = false;
232248
entry->permafrozen = false;
233249
entry->alias_target = NULL;
250+
entry->alias_target_env = NULL;
234251
entry->value = value_null();
235252
return true;
236253
}
@@ -243,7 +260,9 @@ bool env_assign_direct(Env* env, const char* name, Value value,
243260
/* Route through alias chain */
244261
if (entry->alias_target) {
245262
const char* target_name = entry->alias_target;
246-
EnvEntry* target = env_get_entry_raw(env, target_name);
263+
Env* lookup_env = entry->alias_target_env ? entry->alias_target_env : e;
264+
Env* target_env_found = NULL;
265+
EnvEntry* target = env_get_entry_raw_with_env(lookup_env, target_name, &target_env_found);
247266
if (!target) return false;
248267
if (type != TYPE_UNKNOWN && target->decl_type != type) return false;
249268
DeclType actual_type = env_decl_type_from_value(value);
@@ -298,6 +317,7 @@ bool env_delete_direct(Env* env, const char* name) {
298317
if (entry->alias_target) {
299318
free(entry->alias_target);
300319
entry->alias_target = NULL;
320+
entry->alias_target_env = NULL;
301321
}
302322
entry->initialized = false;
303323
entry->value = value_null();
@@ -312,17 +332,82 @@ bool env_set_alias_direct(Env* env, const char* name,
312332
bool declare_if_missing) {
313333
if (!env || !name || !target_name) return false;
314334

315-
/* Ensure the target exists */
316-
EnvEntry* target = env_get_entry_raw(env, target_name);
335+
/* Ensure the target exists; capture the Env where it was found */
336+
Env* target_env_found = NULL;
337+
EnvEntry* target = env_get_entry_raw_with_env(env, target_name, &target_env_found);
338+
if (!target) return false;
339+
340+
/* Resolve final target through alias chain; detect cycles */
341+
EnvEntry* cur = target;
342+
Env* cur_env = target_env_found;
343+
int depth = 0;
344+
while (cur && cur->alias_target) {
345+
if (depth++ > 256) return false;
346+
if (strcmp(cur->alias_target, name) == 0) return false; /* cycle */
347+
Env* lookup_env = cur->alias_target_env ? cur->alias_target_env : cur_env;
348+
cur = env_get_entry_raw_with_env(lookup_env, cur->alias_target, &cur_env);
349+
}
350+
if (!cur) return false;
351+
352+
/* Disallow aliasing to frozen / permafrozen target */
353+
if (cur->frozen || cur->permafrozen) return false;
354+
355+
/* Type compatibility */
356+
if (type != TYPE_UNKNOWN && type != cur->decl_type) return false;
357+
358+
/* Find existing local entry (but don't create it yet). Only create
359+
the local entry after all validation succeeds to avoid leaving a
360+
declared-but-uninitialized binding when alias setup fails. */
361+
EnvEntry* entry = env_find_local(env, name);
362+
if (!entry) {
363+
if (!declare_if_missing) return false;
364+
/* create now */
365+
if (!env_define_direct(env, name, type)) return false;
366+
entry = env_find_local(env, name);
367+
if (!entry) return false;
368+
}
369+
370+
/* Respect frozen state on the entry itself */
371+
if (entry->frozen || entry->permafrozen) return false;
372+
373+
/* Overwrite declared type with target's type */
374+
entry->decl_type = cur->decl_type;
375+
376+
/* Clear any stored value and set alias */
377+
if (entry->initialized) {
378+
value_free(entry->value);
379+
entry->initialized = false;
380+
entry->value = value_null();
381+
}
382+
if (entry->alias_target) {
383+
free(entry->alias_target);
384+
entry->alias_target = NULL;
385+
}
386+
entry->alias_target = strdup(cur->name);
387+
/* Record the Env where the final target was located so alias
388+
resolution works even when target lives in a different Env tree. */
389+
entry->alias_target_env = cur_env;
390+
entry->initialized = true; /* alias is considered initialised */
391+
return true;
392+
}
393+
394+
bool env_set_alias_cross(Env* env, const char* name, Env* target_env, const char* target_name, DeclType type, bool declare_if_missing) {
395+
if (!env || !name || !target_env || !target_name) return false;
396+
397+
/* Ensure the target exists; capture the Env where it was found */
398+
Env* target_env_found = NULL;
399+
EnvEntry* target = env_get_entry_raw_with_env(target_env, target_name, &target_env_found);
317400
if (!target) return false;
318401

319402
/* Resolve final target through alias chain; detect cycles */
320403
EnvEntry* cur = target;
404+
Env* cur_env = target_env_found;
321405
int depth = 0;
322406
while (cur && cur->alias_target) {
323407
if (depth++ > 256) return false;
324408
if (strcmp(cur->alias_target, name) == 0) return false; /* cycle */
325-
cur = env_get_entry_raw(env, cur->alias_target);
409+
Env* lookup_env = cur->alias_target_env ? cur->alias_target_env : cur_env;
410+
cur = env_get_entry_raw_with_env(lookup_env, cur->alias_target, &cur_env);
326411
}
327412
if (!cur) return false;
328413

@@ -361,6 +446,7 @@ bool env_set_alias_direct(Env* env, const char* name,
361446
entry->alias_target = NULL;
362447
}
363448
entry->alias_target = strdup(cur->name);
449+
entry->alias_target_env = cur_env;
364450
entry->initialized = true; /* alias is considered initialised */
365451
return true;
366452
}

src/env.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
#include "value.h"
55

6+
/* Forward-declare Env so EnvEntry can hold an Env* pointer. */
7+
typedef struct Env Env;
8+
69
typedef struct EnvEntry {
710
char* name;
811
DeclType decl_type;
@@ -12,6 +15,10 @@ typedef struct EnvEntry {
1215
bool permafrozen;
1316
// If non-NULL, this entry is an alias to another binding name in the environment
1417
char* alias_target;
18+
// If non-NULL, indicates the Env where alias_target should be resolved.
19+
// This allows aliasing to bindings that live in a different Env tree
20+
// (for example, caller environments when binding pointer arguments).
21+
Env* alias_target_env;
1522
} EnvEntry;
1623

1724
typedef struct Env {
@@ -41,6 +48,10 @@ EnvEntry* env_get_entry(Env* env, const char* name);
4148
// Create or update an alias (pointer) binding: `name` will become an alias to `target_name`.
4249
// If declare_if_missing is true, `name` will be defined if absent. Returns true on success.
4350
bool env_set_alias(Env* env, const char* name, const char* target_name, DeclType type, bool declare_if_missing);
51+
// Create or update an alias where the alias target should be resolved in a
52+
// specific environment (target_env). This supports pointer args binding
53+
// where the pointed-to symbol is visible in the caller's environment.
54+
bool env_set_alias_cross(Env* env, const char* name, Env* target_env, const char* target_name, DeclType type, bool declare_if_missing);
4455

4556
// Accessors for EnvEntry opaque use from other translation units
4657
// Returns true if the entry is initialized

src/interpreter.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1355,7 +1355,7 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
13551355
return value_null();
13561356
}
13571357

1358-
if (env_set_alias(call_env, param->name, target_name, param->type, true)) {
1358+
if (env_set_alias_cross(call_env, param->name, env, target_name, param->type, true)) {
13591359
if (arg_from_pos >= 0) {
13601360
value_free(pos_vals[arg_from_pos]);
13611361
pos_vals[arg_from_pos] = value_null();

tests/mod.pre

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ INT: sym = 0b101
22

33
FUNC INT: FUN(){
44
RETURN(0b0)
5-
}
5+
}
6+
7+
FUNC INT: SET_ZERO(INT: target){
8+
target = 0d0
9+
}

tests/test2.pre

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,13 @@ DEL(mod2.FUN)
10631063
ASSERT(NOT(EXIST(mod2.FUN)))
10641064
PRINT("IMPORT_PATH: PASS\n")
10651065

1066+
PRINT("Testing pointer args with imported functions...")
1067+
INT: x = 0d5
1068+
mod.SET_ZERO(@x)
1069+
ASSERT(EQ(x, 0d0))
1070+
DEL(x)
1071+
PRINT("Imported pointer args: PASS\n")
1072+
10661073
PRINT("Testing EXPORT...")
10671074
INT: ex_sym = 0d5
10681075
ASSERT(EQ(EXPORT(ex_sym, mod), 0d0))

0 commit comments

Comments
 (0)