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>
This commit is contained in:
@@ -159,9 +159,30 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_environment_stop_waiting(akbasic_Envi
|
||||
* runtime's currently active environment auto-creates. A lookup that misses in a
|
||||
* non-active environment returns NULL through `dest` without error, matching the
|
||||
* reference.
|
||||
*
|
||||
* **A host wanting a variable the script will still see later wants
|
||||
* akbasic_runtime_global() instead.** This one lands the variable in whatever
|
||||
* scope happens to be active, which during a suspended run is usually a `FOR` or
|
||||
* `GOSUB` body -- and it dies when that scope pops.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_environment_get(akbasic_Environment *obj, const char *varname, akbasic_Variable **dest);
|
||||
|
||||
/**
|
||||
* @brief Find a variable in exactly this scope, creating it here if it is absent.
|
||||
*
|
||||
* No walk up the parent chain in either direction: the caller has said which
|
||||
* scope it means, and no active-environment check stands in the way. This is
|
||||
* what akbasic_runtime_global() calls against the root.
|
||||
*
|
||||
* @param obj The scope to search and, on a miss, to create in.
|
||||
* @param varname Name including its type suffix.
|
||||
* @param dest Output destination populated by the function; never NULL on success.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When any argument is NULL.
|
||||
* @throws AKBASIC_ERR_BOUNDS When the name is too long, or no variable slot is free.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_environment_create(akbasic_Environment *obj, const char *varname, akbasic_Variable **dest);
|
||||
|
||||
/**
|
||||
* @brief Resolve a label to the line number it marks.
|
||||
* @param obj Scope to search; the parent chain is walked.
|
||||
|
||||
@@ -373,6 +373,37 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_start(akbasic_Runtime *obj, i
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_load(akbasic_Runtime *obj, const char *source);
|
||||
|
||||
/**
|
||||
* @brief Find or create a variable in the script's outermost scope.
|
||||
*
|
||||
* **This is the entry point a host exchanging values with a script should use.**
|
||||
* akbasic_environment_get() lands a new variable in whatever scope is active,
|
||||
* and a script suspended part-way through a bounded akbasic_runtime_run() is
|
||||
* usually inside a `FOR` or `GOSUB` body -- so the script reads the value
|
||||
* correctly inside the loop and gets `0` immediately after it, with nothing
|
||||
* raised anywhere. Reaching for the root by hand does not help either: that
|
||||
* function only auto-creates in the *active* environment, so with a child active
|
||||
* it returns NULL through `dest` without raising and an unchecked host
|
||||
* dereferences it.
|
||||
*
|
||||
* Here the root is found by walking `obj->environment` to the environment with
|
||||
* no parent, and the variable is created there unconditionally. That is the
|
||||
* right answer inside a user function's scope too: a funcdef's environment is
|
||||
* initialized with the caller's as its parent, so the walk terminates at the
|
||||
* same root.
|
||||
*
|
||||
* Seeding before akbasic_runtime_start() and reading after the script stops both
|
||||
* still work through this, so a host has no reason to use anything else.
|
||||
*
|
||||
* @param obj Object to initialize, inspect, or modify.
|
||||
* @param name Variable name including its type suffix -- `A#`, `B%`, `C$`.
|
||||
* @param dest Output destination populated by the function; never NULL on success.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When any argument is NULL, or the runtime has no environment.
|
||||
* @throws AKBASIC_ERR_BOUNDS When the name is too long, or no variable slot is free.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_global(akbasic_Runtime *obj, const char *name, akbasic_Variable **dest);
|
||||
|
||||
/* --- Internal API: exposed for the scanner, parser and verb handlers only. --- */
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user