[net.lang.forth] CREATE <BUILDS

carr@utah-cs.UUCP (Harold Carr) (10/12/84)

Could someone enlighten me on why one would use CREATE ... DOES> in
some instances and <BUILDS .. DOES> in others?  Also, I am very interested
in Forth's meta-defining words and would appreciate examples of interesting
and powerful ways of using them.

Here are a couple of examples of using <BUILDS and CREATE.  They are
both written in Bill's VAXFORTH.  This first example creates an
"execution array" to contain words to be executed when accessed:

: xeq-array	( number-of-slots -- )
   <builds	( Executing xeq-array in the form: )
		(  10 xeq-array keys )
		( uses <BUILDS to create a dictionary entry for KEYS. )
   2* 2* dup here swap 0 fill allot ( Make a vector initialized to zero)
   does>	( index -- )
		( DOES> alters the code field and first parameter of )
		( word defined by <BUILDS to execute the sequence of )
		( words following DOES>.  When the DOES> part executes )
		( it begins with the address of the first parameter of )
		( the new word on the stack. )
   swap 2* 2* + @ execute ; ( Execute the function in the indexed slot. )

<BUILDS will take a word from the input stream and create a dictionary
header for it.  Then the code up to DOES> is run.  In this case, the
code allocates space in the new word for a vector initialized to zeros.
The DOES> part defines the run-time words for the newly created word.
When the new word is run its parameter field address is pushed on Forth's
parameter stack (the user has already pushed an index before it).  The
parameter address is the base of the vector.  It swaps these two items,
leaving the index on top, which is then multiplied by four to create
a thirty-two bit word index.  This index is then added to the base.
The @ operator gets the contents of the indexed slot and leaves it
on the stack.  The contents should be the address of another executable
word.  Execute simply executes the word whose address is on the top
of the stack.

By defining the following auxilary words (shown without comment):

: put
   [compile] ' ;

: offset-in
   2* 2* [compile] ' + ! ;

We can create a key-stroke dispatch table:

26 xeq-array control-keys
...
put backward  2 offset-in control-keys
put forward   6 offset-in control-keys
put next     14 offset-in control-keys
put previous 21 offset-in control-keys
...

This creates an execution array for control keys.  Then we install
the functions associated with each key.  Note, that by using the
execution array rather than a case statement, we can dynamically
redefine the keys.  To run any of the functions in the array we simply
do:

14 control-keys

Control-keys takes its index from the stack, fetches the function and
executes it.  A simple loop will now listen to the keyboard:

: keys
   begin
    key
    dup 27 <=
    if
     control-keys
    else
     regular-keys
    then
   again ;


A second example (sent to me by Mitch Bradley) uses CREATE to create
an automatically indexed byte array:

: carray    ( array-size --name)
   create   ( Create a dictionary entry for the next word in
              the input stream, without allocating any parameter
              field memory. )
   allot    ( Add array-size bytes to the parameter field of the
              most recently defined word. )
   does>    ( index -- byte-address )
            ( Marks the termination of the defining part of a high-level
              defining word and begins the definition of the run-time
              actions.  On execution of words defined with CARRAY, the
              sequence of words between DOES> and ; will be executed,
              with the address of the word's parameter field on the stack. )
   + ;      ( Add index to base. )

I am still not sure why one would use CREATE rather than DOES> here.

I'll be waiting to hear from you.

Forth programmers remind me of Lisp
hackers: fearless, creative and free - The environment is so open
and powerful (no secret black boxes called compilers), we just can't
wait to get our hands on it an change it!

Harold Carr      arpa: CARR@UTAH-20  uucp: harpo!utah-cs!carr

wmb@sun.uucp (Mitch Bradley) (10/14/84)

> Could someone enlighten me on why one would use CREATE ... DOES> in
> some instances and <BUILDS .. DOES> in others?

<BUILDS is an anachronism.  The name was changed from <BUILDS to CREATE
in the 79 standard.  There might have been some subtle difference
between the semantics of the old <BUILDS and the new CREATE, but I
can't remember what it was.  In any case, <BUILDS is no longer part
of the current standard, having been replaced by CREATE.

CREATE ought to work fine in place of <BUILDS in your example.

Your example of an execution array to dispatch control keys is
a good one.  Instead of initializing the array with zero, it would
have been better to initialize it with the address of a word that
prints an "unitialized vector" message.

There is a spiffier way to do the control key handling, to wit:

Instead of making an array indexed by the control key typed,
you convert each control character into a string of the form
"^P", for instance, and then search the dictionary for a word
with that name.  If one is found, execute it.  This scheme make
it very easy to define and modify the behavior of various control
keys.  For instance, if you want to define control-H to erase
the previous character, all you have to do is

: ^H erase-previous-character ;

assuming you already have a word "erase-previous-character" which
does the work.

It is also easy to handle escape sequences with this scheme.
You define ^[ ( which is ESCAPE ) to append the next key to
the current string, then search the dictionary again.  For example,

: ^[  key append-char-to-string search-and-execute ;
: ^[H erase-previous-word ;

again assuming you already have the words that do the work.

I wrote a paper describing this scheme and its use to implement
an Emacs-style command-line editor with command-completion.
It is being published in the Proceedings of the 1984 Rochester
Forth Conference.  Code is included.  The Proceedings are being
published now and should be out pretty soon.  The code will require
some hacking for use with Bill's VAX Forth, because it is written
for Forth-83, whereas Bill's Forth is Forth-79.  Shouldn't be too
difficult, though.  The command completion may be difficult, due
to the highly-encoded 5-character + count names that Bill uses.
The line editing should be easy to port.

I will mail the code to anybody that wants it, but it would be a
nice gesture to buy the Proceedings anyway (Support your Institute for
Applied Forth Research!).

Mitch Bradley