Inspired by the sub-thread 'Alternatives to S" and TO' here
https://groups.google.com/forum/#!topic/comp.lang.forth/tJG_Wpb_4l4
(Google Groups won't let me quote any more exactly, and I think this deserves its own thread for all readers.
Anton Ertl wrote:
Another take on your statement is that we should get rid of words like
S" and TO (and not design more of the kind) which some implementations
implement with STATE-smartness, for which other implementations
provide implementations with better properties, but with repercussions
that are far more trouble than these words are worth.
Instead, we would only have normal words (like +) and IMMEDIATE words
like ( and IF (the latter maybe compile-only), but no access to STATE
and no words that have independent interpretation and compilation
semantics.
We would have to replace S" and TO with something else. The classic
approach would be the ' ['] solution, but that's not very popular.
What is popular is the solution taken for numbers: The text
interpreter deals with them as appropriate and everyone is happy. We
don't use NUM 123 and [NUM] 123 for numbers, we let the text
interpreter deal with them.
Probably most of the parsing words (the main driver behind STATE-smart
words) can be eliminated by having a few additions to the text
interpreter. E.g., instead of writing ." bla" or .( bla) one could
write "bla" TYPE, and have code that works both interpretively and
compiled as intended.
There are a few parsing words where the replacement requires its own
treatment in the text interpreter, in particular TO. Instead of
TO v
we could write
->v
and the text interpreter would do the appropriate thing with V.
Albert van der Horst used to call this feature "denotations", but
lately I have seen him use "prefixes".
Many will cry out against this complication of the text interpreter.
Some may also find the idea bad because up to now the text interpreter
is a black box so any additions there are only done by the compiler
vendor. However:
1) I think that this feature adds relatively little complexity to the
text interpreter, and it gets rid of a huge amount of baggage
elsewhere (STATE-smart words and the restrictions that their defenders
want to put upon us, or the complexities of the alternatives and their
ramifications).
2) One could make it possible for "mere" users to add further
prefixes/denotations (or, as Gforth calls them, recognizers) to the
text interpreter, just like it is possible to add further words now.
Of course some people will fear giving this much power to the users,
and it's certainly a feature that should be used sparingly, but that's
a management issue.
/endquote
That makes sense. The tricky definitions are those that, like literals, parse something from the input stream and then have to do something different with it according to the current state of STATE. It's tricky to POSTPONE literals, and it makes no sense to ' them.
Berndt then showed how this is done in Gforth using recognizers:
We have only one recognizer chain, and instead of passing a flag, we
return a vtable, which contains three methods: how to interpret the
thing, how to compile the thing, and how to serialize the thing
(serializing is for POSTPONE,). "Thing" is the rest the recognizer
returns (token, number, float, whatever).
/endquote
A recognizer is like a wordlist for literals; if the parsed text fits a certain pattern it tells you what to do with it, otherwise it passes it on to the next recognizer in the chain. So, if we have a recognizer chain called FIND-LITERAL that ends with an always-successful throw for unmatched patterns and the vtable methods are INTERPRETED, COMPILED and POSTPONED, then the text interpreter might look like:
... FIND DUP IF \ ... no surprises here
ELSE
find-literal STATE @ IF compiled ELSE interpreted THEN
THEN
With all the STATE-smartness handled in the FIND-LITERAL chain, the specifications for ' and FIND become simplified.
' always returns the definition's execution token, even if its semantics are
compile-only
FIND always returns the execution token and a flag to say whether it is
normally executed while compiling
A programmer can still write classical STATE-smart words and take the consequences if they are ticked or POSTPONEd, or they can write words like S" and TO as prefixes, which cannot be ticked but can be supplied with a sensible POSTPONED method.
Is it possible to exorcise STATE entirely?
Quite easily. Just have a FIND-replacement that returns a vtable.
' EXECUTE
' COMPILE
:NONAME POSTPONE LITERAL POSTPONE COMPILE, ;
CREATE DEFAULT-XT
' EXECUTE
' EXECUTE
' COMPILE,
CREATE IMMEDIATE-XT
: FIND-XT \ c-addr u -- xt vtable | 0
... FIND CASE
1 OF IMMEDIATE-XT ENDOF
-1 OF DEFAULT-XT ENDOF
DUP
ENDCASE
And the text interpreter becomes:
... FIND-XT DUP 0= IF FIND-LITERAL THEN
STATE @ IF COMPILED ELSE INTERPRETED THEN ;
Even that use of STATE can be eliminated:
DEFER DO-IT
: [ ['] INTERPRETED IS DO-IT ;
: ] ['] COMPILED IS DO-IT ;
... FIND-XT DUP 0= IF FIND-LITERAL THEN DO-IT