|
32 | 32 | // Forward declarations for interpreter functions we need |
33 | 33 | Value eval_expr(Interpreter* interp, Expr* expr, Env* env); |
34 | 34 | int value_truthiness(Value v); |
| 35 | +static int module_export_bindings(Interpreter* interp, Env* caller_env, Env* mod_env, const char* alias, int line, int col, const char* fail_msg); |
35 | 36 |
|
36 | 37 | // Helper macros |
37 | 38 | #define RUNTIME_ERROR(interp, msg, line, col) \ |
@@ -5211,22 +5212,9 @@ static Value builtin_import_path(Interpreter* interp, Value* args, int argc, Exp |
5211 | 5212 | free(found_path); |
5212 | 5213 | free(canonical_path); |
5213 | 5214 |
|
5214 | | - // Expose module symbols into caller env under alias prefix: alias.name -> value |
5215 | | - size_t alias_len = strlen(alias); |
5216 | | - for (size_t i = 0; i < mod_env->count; i++) { |
5217 | | - EnvEntry* e = &mod_env->entries[i]; |
5218 | | - if (!e->initialized) continue; |
5219 | | - if (e->name && e->name[0] == '_' && e->name[1] == '_') continue; |
5220 | | - size_t qlen = alias_len + 1 + strlen(e->name) + 1; |
5221 | | - char* qualified = malloc(qlen); |
5222 | | - if (!qualified) { if (alias_dup) free(alias_dup); RUNTIME_ERROR(interp, "Out of memory", line, col); } |
5223 | | - snprintf(qualified, qlen, "%s.%s", alias, e->name); |
5224 | | - if (!env_assign(env, qualified, e->value, e->decl_type, true)) { |
5225 | | - free(qualified); |
5226 | | - if (alias_dup) free(alias_dup); |
5227 | | - RUNTIME_ERROR(interp, "IMPORT_PATH failed to assign qualified name", line, col); |
5228 | | - } |
5229 | | - free(qualified); |
| 5215 | + if (module_export_bindings(interp, env, mod_env, alias, line, col, "IMPORT_PATH failed to assign qualified name") != 0) { |
| 5216 | + if (alias_dup) free(alias_dup); |
| 5217 | + return value_null(); |
5230 | 5218 | } |
5231 | 5219 |
|
5232 | 5220 | // Ensure the module name itself exists in caller env |
@@ -7178,13 +7166,146 @@ static Value builtin_extend(Interpreter* interp, Value* args, int argc, Expr** a |
7178 | 7166 | // Expose the extension namespace symbol in the current module environment. |
7179 | 7167 | if (loaded_name && loaded_name[0] != '\0') { |
7180 | 7168 | (void)env_assign(env, loaded_name, value_str(""), TYPE_STR, true); |
| 7169 | + |
| 7170 | + EnvEntry* namespaces_entry = env_get_entry(env, "__EXTEND_NAMES__"); |
| 7171 | + const char* existing_namespaces = (namespaces_entry && namespaces_entry->initialized && namespaces_entry->value.type == VAL_STR && namespaces_entry->value.as.s) |
| 7172 | + ? namespaces_entry->value.as.s |
| 7173 | + : ""; |
| 7174 | + size_t loaded_len = strlen(loaded_name); |
| 7175 | + int already_present = 0; |
| 7176 | + const char* cursor = existing_namespaces; |
| 7177 | + while (cursor && *cursor != '\0') { |
| 7178 | + while (*cursor == '|') cursor++; |
| 7179 | + if (*cursor == '\0') break; |
| 7180 | + const char* start = cursor; |
| 7181 | + while (*cursor != '\0' && *cursor != '|') cursor++; |
| 7182 | + size_t token_len = (size_t)(cursor - start); |
| 7183 | + if (token_len == loaded_len && strncmp(start, loaded_name, loaded_len) == 0) { |
| 7184 | + already_present = 1; |
| 7185 | + break; |
| 7186 | + } |
| 7187 | + } |
| 7188 | + if (!already_present) { |
| 7189 | + size_t existing_len = strlen(existing_namespaces); |
| 7190 | + size_t combined_len = existing_len + loaded_len + 2; |
| 7191 | + char* combined = malloc(combined_len + 1); |
| 7192 | + if (!combined) { |
| 7193 | + if (interp->error) free(interp->error); |
| 7194 | + interp->error = strdup("Out of memory"); |
| 7195 | + interp->error_line = line; |
| 7196 | + interp->error_col = col; |
| 7197 | + free(loaded_name); |
| 7198 | + return value_null(); |
| 7199 | + } |
| 7200 | + if (existing_len > 0) { |
| 7201 | + memcpy(combined, existing_namespaces, existing_len); |
| 7202 | + combined[existing_len] = '|'; |
| 7203 | + memcpy(combined + existing_len + 1, loaded_name, loaded_len); |
| 7204 | + combined[existing_len + loaded_len + 1] = '|'; |
| 7205 | + combined[existing_len + loaded_len + 2] = '\0'; |
| 7206 | + } else { |
| 7207 | + combined[0] = '|'; |
| 7208 | + memcpy(combined + 1, loaded_name, loaded_len); |
| 7209 | + combined[loaded_len + 1] = '|'; |
| 7210 | + combined[loaded_len + 2] = '\0'; |
| 7211 | + } |
| 7212 | + (void)env_assign(env, "__EXTEND_NAMES__", value_str(combined), TYPE_STR, true); |
| 7213 | + free(combined); |
| 7214 | + } |
7181 | 7215 | } |
7182 | 7216 |
|
7183 | 7217 | free(loaded_name); |
7184 | 7218 | free(ext_err); |
7185 | 7219 | return value_bool(false); |
7186 | 7220 | } |
7187 | 7221 |
|
| 7222 | +static int module_export_bindings(Interpreter* interp, Env* caller_env, Env* mod_env, const char* alias, int line, int col, const char* fail_msg) { |
| 7223 | + if (!interp || !caller_env || !mod_env || !alias || alias[0] == '\0') return -1; |
| 7224 | + |
| 7225 | + size_t alias_len = strlen(alias); |
| 7226 | + |
| 7227 | + for (size_t i = 0; i < mod_env->count; i++) { |
| 7228 | + EnvEntry* e = &mod_env->entries[i]; |
| 7229 | + if (!e->initialized) continue; |
| 7230 | + if (e->name && e->name[0] == '_' && e->name[1] == '_') continue; |
| 7231 | + size_t qlen = alias_len + 1 + strlen(e->name) + 1; |
| 7232 | + char* qualified = malloc(qlen); |
| 7233 | + if (!qualified) return -1; |
| 7234 | + snprintf(qualified, qlen, "%s.%s", alias, e->name); |
| 7235 | + if (!env_assign(caller_env, qualified, e->value, e->decl_type, true)) { |
| 7236 | + free(qualified); |
| 7237 | + if (interp->error) free(interp->error); |
| 7238 | + interp->error = strdup(fail_msg); |
| 7239 | + interp->error_line = line; |
| 7240 | + interp->error_col = col; |
| 7241 | + return -1; |
| 7242 | + } |
| 7243 | + free(qualified); |
| 7244 | + } |
| 7245 | + |
| 7246 | + EnvEntry* namespaces_entry = env_get_entry(mod_env, "__EXTEND_NAMES__"); |
| 7247 | + if (namespaces_entry && namespaces_entry->initialized && namespaces_entry->value.type == VAL_STR && namespaces_entry->value.as.s && namespaces_entry->value.as.s[0] != '\0') { |
| 7248 | + const char* namespaces = namespaces_entry->value.as.s; |
| 7249 | + const char* cursor = namespaces; |
| 7250 | + while (cursor && *cursor != '\0') { |
| 7251 | + while (*cursor == '|') cursor++; |
| 7252 | + if (*cursor == '\0') break; |
| 7253 | + |
| 7254 | + const char* start = cursor; |
| 7255 | + while (*cursor != '\0' && *cursor != '|') cursor++; |
| 7256 | + size_t ns_len = (size_t)(cursor - start); |
| 7257 | + if (ns_len == 0) continue; |
| 7258 | + |
| 7259 | + char* ns = malloc(ns_len + 1); |
| 7260 | + if (!ns) return -1; |
| 7261 | + memcpy(ns, start, ns_len); |
| 7262 | + ns[ns_len] = '\0'; |
| 7263 | + |
| 7264 | + size_t ns_qual_len = alias_len + 1 + ns_len + 1; |
| 7265 | + char* ns_qualified = malloc(ns_qual_len); |
| 7266 | + if (!ns_qualified) { free(ns); return -1; } |
| 7267 | + snprintf(ns_qualified, ns_qual_len, "%s.%s", alias, ns); |
| 7268 | + if (!env_get_entry(caller_env, ns_qualified)) { |
| 7269 | + if (!env_assign(caller_env, ns_qualified, value_str(""), TYPE_STR, true)) { |
| 7270 | + free(ns_qualified); |
| 7271 | + free(ns); |
| 7272 | + if (interp->error) free(interp->error); |
| 7273 | + interp->error = strdup(fail_msg); |
| 7274 | + interp->error_line = line; |
| 7275 | + interp->error_col = col; |
| 7276 | + return -1; |
| 7277 | + } |
| 7278 | + } |
| 7279 | + |
| 7280 | + for (size_t j = 0; j < mod_env->count; j++) { |
| 7281 | + EnvEntry* e = &mod_env->entries[j]; |
| 7282 | + if (!e->initialized) continue; |
| 7283 | + if (e->name && e->name[0] == '_' && e->name[1] == '_') continue; |
| 7284 | + size_t qlen = ns_qual_len + 1 + strlen(e->name) + 1; |
| 7285 | + char* qualified = malloc(qlen); |
| 7286 | + if (!qualified) { free(ns_qualified); free(ns); return -1; } |
| 7287 | + snprintf(qualified, qlen, "%s.%s", ns_qualified, e->name); |
| 7288 | + if (!env_assign(caller_env, qualified, e->value, e->decl_type, true)) { |
| 7289 | + free(qualified); |
| 7290 | + free(ns_qualified); |
| 7291 | + free(ns); |
| 7292 | + if (interp->error) free(interp->error); |
| 7293 | + interp->error = strdup(fail_msg); |
| 7294 | + interp->error_line = line; |
| 7295 | + interp->error_col = col; |
| 7296 | + return -1; |
| 7297 | + } |
| 7298 | + free(qualified); |
| 7299 | + } |
| 7300 | + |
| 7301 | + free(ns_qualified); |
| 7302 | + free(ns); |
| 7303 | + } |
| 7304 | + } |
| 7305 | + |
| 7306 | + return 0; |
| 7307 | +} |
| 7308 | + |
7188 | 7309 | // Stubs for operations requiring TNS/MAP/THD |
7189 | 7310 | static Value builtin_import(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) { |
7190 | 7311 | (void)args; (void)argc; |
@@ -7432,22 +7553,8 @@ static Value builtin_import(Interpreter* interp, Value* args, int argc, Expr** a |
7432 | 7553 | free(found_path); |
7433 | 7554 | free(canonical_path); |
7434 | 7555 |
|
7435 | | - // Expose module symbols into caller env under alias prefix: alias.name -> value |
7436 | | - size_t alias_len = strlen(alias); |
7437 | | - |
7438 | | - for (size_t i = 0; i < mod_env->count; i++) { |
7439 | | - EnvEntry* e = &mod_env->entries[i]; |
7440 | | - if (!e->initialized) continue; |
7441 | | - if (e->name && e->name[0] == '_' && e->name[1] == '_') continue; // skip magic |
7442 | | - size_t qlen = alias_len + 1 + strlen(e->name) + 1; |
7443 | | - char* qualified = malloc(qlen); |
7444 | | - if (!qualified) { RUNTIME_ERROR(interp, "Out of memory", line, col); } |
7445 | | - snprintf(qualified, qlen, "%s.%s", alias, e->name); |
7446 | | - if (!env_assign(env, qualified, e->value, e->decl_type, true)) { |
7447 | | - free(qualified); |
7448 | | - RUNTIME_ERROR(interp, "IMPORT failed to assign qualified name", line, col); |
7449 | | - } |
7450 | | - free(qualified); |
| 7556 | + if (module_export_bindings(interp, env, mod_env, alias, line, col, "IMPORT failed to assign qualified name") != 0) { |
| 7557 | + return value_null(); |
7451 | 7558 | } |
7452 | 7559 |
|
7453 | 7560 | // Ensure the module name itself exists in caller env (avoid undefined identifier errors) |
|
0 commit comments