Add akbasic_runtime_global: a host variable lands in the script's root scope

A host creating a variable while a script was suspended got it in whatever
scope was active -- usually a FOR or GOSUB body -- and it died when the body
popped, silently. Reaching for the root by hand returned NULL without raising,
because environment_get only auto-creates in the active environment.

Both are still true of environment_get, which is correct for what the
interpreter uses it for. The README and the example now point somewhere else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
This commit is contained in:
2026-07-31 11:53:05 -04:00
parent 3edbf75547
commit e7ef48f1ca
9 changed files with 425 additions and 87 deletions

View File

@@ -249,8 +249,6 @@ akerr_ErrorContext *akbasic_environment_get(akbasic_Environment *obj, const char
{
PREPARE_ERROR(errctx);
akbasic_Environment *walk = NULL;
akbasic_Variable *variable = NULL;
int64_t sizes[1] = { 1 };
void *slot = NULL;
FAIL_ZERO_RETURN(errctx, (obj != NULL && varname != NULL && dest != NULL), AKERR_NULLPOINTER,
@@ -276,6 +274,36 @@ akerr_ErrorContext *akbasic_environment_get(akbasic_Environment *obj, const char
SUCCEED_RETURN(errctx);
}
PASS(errctx, akbasic_environment_create(obj, varname, dest));
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akbasic_environment_create(akbasic_Environment *obj, const char *varname, akbasic_Variable **dest)
{
PREPARE_ERROR(errctx);
akbasic_Variable *variable = NULL;
int64_t sizes[1] = { 1 };
void *slot = NULL;
akerr_ErrorContext *found = NULL;
FAIL_ZERO_RETURN(errctx, (obj != NULL && varname != NULL && dest != NULL), AKERR_NULLPOINTER,
"NULL argument in environment create");
/*
* This scope only. Unlike akbasic_environment_get() there is no walk up the
* parent chain: the caller has already said *which* scope it means, and
* finding an outer one would put the variable somewhere other than where it
* was asked for.
*/
*dest = NULL;
found = akbasic_symtab_get(&obj->variables, varname, &slot, NULL);
if ( found == NULL ) {
*dest = (akbasic_Variable *)slot;
SUCCEED_RETURN(errctx);
}
found->handled = true;
IGNORE(akerr_release_error(found));
PASS(errctx, akbasic_runtime_new_variable(obj->runtime, &variable));
FAIL_ZERO_RETURN(errctx, (strlen(varname) < sizeof(variable->name)), AKBASIC_ERR_BOUNDS,
"Variable name '%s' is too long", varname);