There have been some header ideas for Gforth from Anton around for
years, featuring an intelligent COMPILE, and some sort of quite-unified
token, but not-quite-so, as this unified token was the nt, and would not
directly EXECUTE. The discussion over mono-tokenizm has proceeded in
the meantime, and during a long e-mail discussion over the last week,
another new header design idea emerged.
The main idea I have about this header design is, that the actions you
want to do with a word - apart from the trivial EXECUTE - should be
methods in a mini-oof style (because that's small enough to implement
even on a Gforth EC target - the only deviation I'm going to make is
that the VT will be one cell before the "object pointer" - our object is
an xt). The virtual table approach also saves space, as most words will
have the same virtual method table (vt). All selectors take the xt from
the top of stack, and pass it on to the method.
As this should be the only token a word has (no separate NT), it should
also reliably give the name of the word. This is a bit tricky.
The header structure looks like this and the pointer points to the code
field (in threaded code, this is an address, in native code, this is the
code).
the xt points here
|
v
name-string | count | link | vt | code field (doer) | body
The vt itself contains the following methods, all with the stack effect
( x*i xt -- x*j )
COMPILE, this compiles the execution semantics ("intelligent
compile,")
INTERPRET-XT this is the interpretation semantics
COMPILE-XT this is the compilation semantics
TICK-XT tick action, to implement true aliases and compile-
only words which can't be '-ed
RUN-PRELUDE this is the parse-time action for Manfred Mahlow's
prelude concept
DODOES This points to the code executed by the dodoes doer
we need this in Gforth
The link field also may point to the xt, so there may be changes to
WORDS (>LINK for each traversal? - there will be changes anyways, as
TRAVERSE-WORDLIST is worth to implement). The reason why the name part
is reversed - count last - (the string itself is still in normal order)
is that this way, it is reliable to get the name of a word from its XT.
:NONAME words will have a zero count element, clearly indicating that
there's no name there.
To save space for the vt, identical vts are shared. The sharing is done
as follows: The vt of the currently constructed word (LASTXT) is in a
buffer for vt construction. When the next word header is started, the
system looks if that vt has already been used, and in that case, reuses
the found vt (e.g. the default vt or the vt of immediate words). If
it's unique, new space for a vt is allocated on the dictionary, and the
buffered vt is copied there.
There are no flags, everything currently done with flags in the count
field are done by modifying the vt:
: immediate ['] execute lastxt [defines] compile-xt ;
: compile-only
['] compile-only-error lastxt [defines] interpret-xt
['] ticking-compile-only-error lastxt [defines] tick-xt ;
An alias or synonym is essentially a proxy using DODEFER as doer, and
passing all messages to the xt in the body (which also means that
ticking such a word returns the original xt, unless that's prevented by
a ticking-compile-only-error there, whereas FIND-XT returns a unique
xt).
This design feels quite right, it allows to implement all the backwards
compatibility needed for Gforth's name token (which then is identical to
the xt, but NAME>INT and NAME>COMP can be implemented), it allows to
implement Stephen Pelc's INT-COMP, and COMP-COMP,, and the stuff
necessary for a OOP extension with local contexts but without parsing
words - inspired by Manfred Mahlow - is also feasible. The replacement
for FIND is FIND-XT ( addr u -- xt / 0 ). Just an xt, nothing else.
It will remove some ugly code in Gforth, especially the heuristics to go
from the xt to the name, and all the special-casing for different
headers like aliases and interpret/compile: we have now.
--
Bernd Paysan
"If you want it done right, you have to do it yourself"
http://bernd-paysan.de/