6.10.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 addr action-of is
to-table: d!-table 2! d+!  n/a    n/a    n/a 

This defines a table d!-table with a to and a +to action, and no action for addr, action-of and is; i.e., for our x defined with dvalue, if you addr x, you will get an error message. At the end of the line you can leave trailing n/as away, but here we show them for completeness.

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 and +to (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.

You may want to define another defining word dvarue that is like dvalue, but also supports addr (see Varues), usually using [noop] as implementation for the part of addr that already receives the address on the stack. Gforth provides >to+addr-table; it takes a table address on the stack and creates a new table with the same entries, except that the addr entry is replaced by [noop]. So you can now define dvarue as follows:

d!-table >to+addr-table d!a-table
`>body d!a-table to-class: dvarue-to
: dvarue ( d "name" -- )
  create 2,
  `2@ set-does>
  `dvarue-to set-to ;

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, ADDR, ACTION-OF, and IS. 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. Unsupported operations can be left away at the end of the line.

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.

>to+addr-table: ( table-addr "name" –  ) gforth-experimental “to-to-plus-addr-table-colon”

Name is a copy of the table at table-addr, but in name the ADDR-method is 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.