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.