Dynamically‐Scoped Variables in zeptoforth - tabemann/zeptoforth GitHub Wiki
zeptoforth as of release 1.5.3 has support for dynamically-scoped, task-local variables. The dynamically-scoped variables' words live in the dynamic
module.
Dynamically-scoped variables are declared with dyn
( "name" -- ), for single-cell dynamically-scoped variables, and with 2dyn
( "name" -- ), for double-cell dynamically-scoped variables. Their declarations may be compiled to RAM or to flash, in the latter case persisting across reboots. Note that declaring dynamic-scoped variables merely assigns them an id code by which they may be referred to later.
Dynamically-scoped variables are read with dyn@
( variable -- x|xd ), which fetches the most recently assigned value of dynamically-scoped variable variable within the current task and returns it as x, if it is a single-cell dynamically-scoped variable, or xd, if it is a double-cell dynamically-scoped variable. If variable has not been set in the current scope in the current task, x-dyn-variable-not-set
is raised.
Dynamically-scoped variables are set within a given scope with dyn!
( xt x|xd variable -- ) which calls the execution token xt with the dynamically-scoped variable variable set to x, if it is a single-cell dynamically-scoped variable, or xd, if it is a double-cell dynamically-scoped variable.
Dynamically-scoped variables are permanently set in the current task with dyn-no-scope!
( x|xd variable -- ) which sets the dynamically-scoped variable variable to x, if it is a single-cell dynamically-scoped variable, or xd, if it is a double-cell dynamically-scoped variable. Note that this permanently uses up space in the current task's dictionary, so it is recommended for only initializing the value of dynamically-scoped variable on a one-time basis per task.
Note that unlike user
variables these only take up space when they are used, and they are either set within a particular scope, or are set permanently throughout an entire task. These variables live in a task's RAM dictionary, so make sure enough space is available for each time they are set. The space for set dynamically-scoped variables with scopes is freed once the scope is exited, either normally or through the raising of an exception.
Do note that dynamically-scoped variables in child tasks do not inherit their values from their parent tasks. Rather, all dynamically-scoped variables start off as being unset in any given task. One may globally initialize the value of a dynamically-scoped variable within a task with dyn-no-scope!
, or else one may initially set a dynamically-scoped variable to a value within a scope with dyn!
.
Another note is that fetching the value of a dynamically-scoped variable involves a linear search along a singly-linked list, so if one would be repeatedly fetching a value from one, and its value would not have changed, one should instead fetch its value once and store it either on the data stack or in a local variable.
For an example of dynamically-scoped variables in action, consider the following:
dynamic import ok
Here we import the dynamic
module.
dyn foobar ok
Here we define a single-cell dynamically-scoped variable named foobar
.
$DEADBEEF foobar dyn-no-scope! ok
Here we initialize foobar
in the current task to the value $DEADBEEF
.
: read-foobar foobar dyn@ h.8 space ; ok
Here we define a word read-foobar
which fetches the current value of foobar
and prints it in hexadecimal.
read-foobar DEADBEEF ok
Here we call read-foobar
and get the current value of foobar
, $DEADBEEF
.
' read-foobar $CAFEBABE foobar dyn! CAFEBABE ok
Here we specify read-foobar
as a scope in which foobar
is set to $CAFEBABE
and get that value.
read-foobar DEADBEEF ok
Here we call read-foobar
again and confirm that the current value of foobar
is again $DEADBEEF
now that we have exited the scope specified by dyn!
.
begin ok
[: ['] read-foobar $BAADF00D foobar dyn! read-foobar ;] $CAFEBABE foobar dyn! ok
end BAADF00D CAFEBABE ok
Here we specify a scope in which foobar
is set to $CAFEBABE
, and then specify another scope in which foobar
is set to $BAADF00D
; afterwards, we get the value of foobar
, which is $BAADF00D
, then exit that scope and get the value of foobar
, which is now $CAFEBABE
as we had previously set it.