If you want the words defined with your defining words to behave differently from words defined with standard defining words, you can write your defining word like this:
: def-word ( "name" -- ) CREATE code1 DOES> ( ... -- ... ) code2 ; def-word name
This fragment defines a defining word def-word
and then
executes it. When def-word
executes, it CREATE
s a new
word name
, and executes the code code1. The code code2
is not executed at this time. The word name
is sometimes called a
child of def-word
.
When you execute name
, the address of the body of name
is
put on the data stack and code2 is executed (the address of the body
of name
is the address HERE
returns immediately after the
CREATE
, i.e., the address a create
d word returns by
default).
You can use def-word
to define a set of child words that behave
similarly; they all have a common run-time behaviour determined by
code2. Typically, the code1 sequence builds a data area in the
body of the child word. The structure of the data is common to all
children of def-word
, but the data values are specific – and
private – to each child word. When a child word is executed, the
address of its private data area is passed as a parameter on TOS to be
used and manipulated18 by code2.
The two fragments of code that make up the defining words act (are executed) at two completely separate times:
Another way of understanding the behaviour of def-word
and
name
is to say that, if you make the following definitions:
: def-word1 ( "name" -- ) CREATE code1 ; : action1 ( ... -- ... ) code2 ; def-word1 name1
Then using name1 action1
is equivalent to using name
.
Another way of writing def-word
is (see Quotations):
: def-word ( "name" -- ; name execution: ... -- ... ) create code1 [: code2 ;] set-does> ;
Gforth actually compiles the code using does>
into code
equivalent to the latter code. An advantage of the set-does>
approach is that you can put other code behind it and you can use it
inside control structures without needing workarounds. A disadvantage
is that it is Gforth-specific.
A classic example is that you can define CONSTANT
in this way:
: CONSTANT ( w "name" -- ) CREATE , DOES> ( -- w ) @ ;
or equivalently
: CONSTANT ( w "name" -- ; name execution: -- w ) create , ['] @ set-does> ;
When you create a constant with 5 CONSTANT five
, a set of
define-time actions take place; first a new word five
is
created, then the value 5 is laid down in the body of five
with
,
. When five
is executed, the address of the body is put
on the stack, and @
retrieves the value 5. The word
five
has no code of its own; it simply contains a data field
and the xt of the quotation or of @
.
The final example in this section is intended to remind you that space
reserved in CREATE
d words is data space and therefore can be
both read and written by a Standard program19:
: foo ( "name" -- ) CREATE -1 , DOES> ( -- ) @ . ; foo first-word foo second-word 123 ' first-word >BODY !
If first-word
had been a CREATE
d word, we could simply
have executed it to get the address of its data field. However, since it
was defined to have DOES>
actions, its execution semantics are to
perform those DOES>
actions. To get the address of its data field
it’s necessary to use '
to get its xt, then >BODY
to
translate the xt into the address of the data field. When you execute
first-word
, it will display 123
. When you execute
second-word
it will display -1
.
In the examples above the stack comment after the DOES>
specifies
the stack effect of the defined words, not the stack effect of the
following code (the following code expects the address of the body on
the top of stack, which is not reflected in the stack comment). This is
the convention that I use and recommend (it clashes a bit with using
locals declarations for stack effect specification, though).
It is legitimate both to read and write to this data area.
Exercise: use this
example as a starting point for your own implementation of Value
and TO
– if you get stuck, investigate the behaviour of '
and
[']
.