You can create new defining words simply by wrapping defining-time code around existing defining words and putting the sequence in a colon definition.
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
Technically, this fragment defines a defining word def-word
, and
a word name
; 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
).
In other words, if you make the following definitions:
: def-word1 ( "name" -- ) Create code1 ; : action1 ( ... -- ... ) code2 ; def-word name1
Using name1 action1
is equivalent to using name
.
E.g., you can implement Constant
in this way:
: constant ( w "name" -- ) create , DOES> ( -- w ) @ ;
When you create a constant with 5 constant five
, first a new word
five
is created, then the value 5 is laid down in the body of
five
with ,
. When five
is invoked, the address of
the body is put on the stack, and @
retrieves the value 5.
In the example 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).
CREATE..DOES>
You may wonder how to use this feature. Here are some usage patterns:
When you see a sequence of code occurring several times, and you can
identify a meaning, you will factor it out as a colon definition. When
you see similar colon definitions, you can factor them using
CREATE..DOES>
. E.g., an assembler usually defines several words
that look very similar:
: ori, ( reg-taget reg-source n -- ) 0 asm-reg-reg-imm ; : andi, ( reg-taget reg-source n -- ) 1 asm-reg-reg-imm ;
This could be factored with:
: reg-reg-imm ( op-code -- ) create , DOES> ( reg-taget reg-source n -- ) @ asm-reg-reg-imm ; 0 reg-reg-imm ori, 1 reg-reg-imm andi,
Another view of CREATE..DOES>
is to consider it as a crude way to
supply a part of the parameters for a word (known as currying in
the functional language community). E.g., +
needs two
parameters. Creating versions of +
with one parameter fixed can
be done like this:
: curry+ ( n1 -- ) create , DOES> ( n2 -- n1+n2 ) @ + ; 3 curry+ 3+ -2 curry+ 2-
CREATE..DOES>
DOES>
compilation colon-sys1 -- colon-sys2 ; run-time nest-sys -- core "does"
This means that you need not use CREATE
and DOES>
in the
same definition; E.g., you can put the DOES>
-part in a separate
definition. This allows us to, e.g., select among different DOES>-parts:
: does1 DOES> ( ... -- ... ) ... ; : does2 DOES> ( ... -- ... ) ... ; : def-word ( ... -- ... ) create ... IF does1 ELSE does2 ENDIF ;
In a standard program you can apply a DOES>
-part only if the last
word was defined with CREATE
. In Gforth, the DOES>
-part
will override the behaviour of the last word defined in any case. In a
standard program, you can use DOES>
only in a colon
definition. In Gforth, you can also use it in interpretation state, in a
kind of one-shot mode:
CREATE name ( ... -- ... ) initialization DOES> code ;
This is equivalwent to the standard
:noname DOES> code ; CREATE name EXECUTE ( ... -- ... ) initialization
You can get the address of the body of a word with
>body
xt -- a-addr core "to-body"