6.11.10.6 Words with user-defined to etc.

When you define a word x, you can set its execution semantics with set-does> (see User-defined defining words using create) or set-execute (see Header methods). But you can also change the semantics of

to x        \ aka ->x
+to x       \ aka +>x
addr x
action-of x \ aka `x defer@
is x        \ aka `x defer!

This is all achieved through a common mechanism described in this section. As an example, let’s define dvalue (it behaves in Gforth exactly like 2value, see Values). First, we need a table of the various to-like actions:

: d+! ( d addr -- )
  dup >r 2@ d+ r> 2! ;

\                  to +to action-of is  addr
to-table: d!-table 2! d+!    n/a    n/a [noop] 

This defines a table d!-table with a to and a +to action, no action for action-of and is, and an addr action. I.e., for our x defined with dvalue, if you is x, you will get an error message. Note that a word using this table must still be declared addressable: in order to legitimately use the addr action.

At the end of the line you can leave trailing [noop] for the addr entry and any trailing n/as for the other entries away, but in the above we show them for completeness. I.e., the table could also have been defined as

\                  to +to action-of is  addr
to-table: d!-table 2! d+!

The entries in the table are words that get an address on the top-of-stack. They possibly also expect some additional data deeper in the stack, but that is data that is provided in the usual use of the word. E.g., in the case of dvalue, the expectation is that you put a double-cell d on the stack before you do a to x, and that d and the address where it should be stored is eventually passed to 2!.

In the case of dvalue, the address is computed from the xt of x with >body. In order to let that be known to the system, you write

`>body d!-table to-class: dvalue-to

This defines the method implementations of the methods behind to, +to, addr, action-of, and is, and corresponding the methods for value!, value+!, >addr, defer@, and defer!. The reason for defining the methods in two steps (by first defining the table) is that the same table can be used for several to-classes; e.g., !-table is used for defining value-to (used for value), but also for uvalue-to used for defining uvalue (see Task-local data) and to-w: (used for the default (w:) locals).

>body is appropriate for words with storage in the dictionary, such as value. But, e.g., for storage in user (task-local) storage (e.g., uvalue), you use >uvalue instead. The general case is that the system pushes the xt of x, and then executes the xt that has been passed to to-class:. This xt may also consume one or more additional values passed on the stack (e.g., for value-flavoured struct fields, xt consumes the address of the struct right below the xt); its overall stack effect is ( ... xt -- addr ).

Now you can define

: dvalue ( d "name" -- )
  create 2,
  `2@ set-does>
  `dvalue-to set-to ;

Here the set-to changes the created word name to use the methods from dvalue-to for implementing to, +to and addr (and the others, but they are defined to deliver errors).

Now you can define words with dvalue and use them:

#5. dvalue x
#2. +to x
x d. \ prints "7 "

The +to x first pushes the xt of x, then performs >body (provided in the definition of dvalue-to), and finally performs the d+! provided in the d!-table.

In order to use addr x, you should have defined x with

#5. addressable: dvalue x

These are the words mentioned above:

to-table: ( "name" "to-word" "+to-word" "addr-word" "action-of-word" "is-word" –  ) gforth-experimental “to-table-colon”

Create a table name with entries for TO, +TO, ACTION-OF, IS, and ADDR. The words for these entries are called with xt on the stack, where xt belongs to the word behind to (or +to etc.). Use n/a to mark unsupported operations. Default entries operations can be left away at the end of the line; the default is for the addr entry is [noop] while the default for the other entries is n/a.

n/a ( ) gforth-experimental “not-available”

This word can be ticked, but throws an “Operation not supported” exception on interpretation and compilation. Use this for methods etc. that aren’t supported.

[noop] ( ) gforth-experimental “bracket-noop”

Does nothing, both when executed and when compiled.

to-class: ( xt table "name" –  ) gforth-experimental “to-class-colon”

Create a to-class implementation name, where xt ( ... xt -- addr ) computes the address to access the data, and table (created with to-table:) contains the words for accessing it.

>uvalue ( xt – addr  ) gforth-internal “to-uvalue”

Xt is the xt of a word x defined with uvalue; addr is the address of the data of x in the current task. This word is useful for building, e.g., uvalue. Do not use it to circumvent that you cannot get the address of a uvalue with addr; in the future Gforth may perform optimizations that assume that uvalues can only be accessed through their name.

set-to ( to-xt –  ) gforth-1.0 “set-to”

Changes the implementations of the to-class methods of the most recently defined word to come from the to-class that has the xt to-xt.