Forward Declarations

Forward declarations are used to create recursive calls if recurse cannot be used.

: bar foo ;
: foo bar ;

One solution for this task is Deferred Words.

Edefer foo
: bar foo ;
:noname ... ; is foo

This works usually. Furthermore they are based upon standard techniques.

Another solution for forward declarations uses a Just-In-Time (JIT) approach. With it a forward declaration resolves itself when called without further user (or programmer) interaction:

1
2
3
4
5
6
7
8
> forward: foo
> : bar foo ;
> bar
 found only forward declaration.
> : foo ." hey" ;
> bar
  hey
>

Line 1 declares foo to be defined later. This foo must not be called directly! The next line defines a word bar that uses foo. Note that foo is not yet a code definition. The word bar can be safely executed however. When the program execution of bar arrives at foo, the JIT module starts. This module first gets the name of the forwardly defined word (foo) and looks it up in the dictionary If find-name gets an XT for foo it is checked whether it is the XT of the forward: declaration or another one. If it is the XT of the forward: declaration, execution is aborted with an error message.

If an XT is found, that fulfils the requirements, two things happen: First the call to foo in the callee (bar) is changed from the original one (that goes to the forward declaration) to the new one that is found by find-name. Plus the XT is executed itself. As a result, a repeated call to bar will not call the JIT runtime checks again but hands over directly to the new foo.

foo can be redefined again. Any already resolved references will remain, still not resolved references will resolve to the new definition:

> forward: foo
> : bar foo ;
> : baz foo ;
> bar
  found only forward declaration.
> : foo ." I'm number 1" ;
> bar
  I'm number 1 ok
> : foo ." I'm number 2" ;
> baz
  I'm number 2 ok
> bar
  I'm number 1 ok
> baz
  I'm number 2 ok
>

The implementation uses internal data structure knowhow. The word forward: creates words that performs the above discussed runtime behaviour when called inside another definition. It is assumed that they are only called within a colon definition.

: forward:
 dp create ,
 does>
 dup 1- swap @i here iplace here count ( copy to temporary ram)
 find-name if \ unless some wordlist voodoo ...
   swap over = abort" found only forward declaration."
   dup r@ 1- !i execute
 else
   \ can only happen if search wordlist has been changed
   true abort" unresolved forward declaration"
 then
;

Late Binding

A similiar definition to forward: can be used to implement late binding. In this case a forward reference will not get permanently resolved but looks up the dictionary every time it get called.

: execute-late:
 dp create ,
 does>
 dup 1- swap @i here iplace here count ( copy to temporary ram)
 find-name if \ unless some wordlist voodoo...
   swap over = abort" found only forward declaration."
   execute
 else
   \ can only happen if search wordlist has changed
   true abort" unresolved forward declaration"
 then
;

This has a huge runtime penalty since on every invocation a dictionary lookup will be made. An option would be the use of search-wordlist command instead of find-name if a proper (short) word list exists.

> execute-late: foo
> : bar foo ;
> bar
  found only forward declaration.
> : foo ." I'm number 1" ;
> bar
  I'm number 1 ok
> : foo ." I'm number 2" ;
> bar
  I'm number 2 ok
>