Before you define a translation token, you should think about the
interpreting run-time, compiling run-time, and postponing run-time
that the corresponding actions should perform for translations with
this translation token. Also, you should think about whether the
translation actions should perform additional scanning (like
scan-translate-string
).
You then need to define three words, one for the interpreting action, one for the compiling action, and one for the postponing action.
Each of these words will eventually be called after removing the translation token from the stack (but the remainder of the translation is still on the stack(s)). It should remove this remainder, perform the additional scanning (if any), and then perform the appropriate run-time.
Once you have these three words, you can define a translation token by passing the xts of these words to
Defines name, a translation token (see Defining recognizers).
name execution: ( – translation-token )
name interpreting action: ( ... translation – ... )
Remove the translation token from the stack and execute int-xt.
name compiling action: ( ... translation – ... )
Remove the translation token from the stack and execute comp-xt.
name postponing action: ( translation – )
Remove the translation token from the stack and execute post-xt.
To make this a little more concrete, here is an implementation for
translate-cell
:
' noop ( x -- x ) \ int-xt ' lit, ( compilation: x -- ; run-time: -- x ) \ comp-xt :noname lit, postpone lit, ; ( postponing: x -- ; run-time: -- x ) \ post-xt translate: translate-cell
If a recognizer recognizes something as a single-cell literal x,
it pushes x and then calls translate-cell
. Later the text
interpreter (or postpone
or some other consumer of
translations) removes the translation token and executes one of the
three xts above (depending on what translation action is desired).
When the interpretation semantics is needed, int-xt is executed, and x stays on the stack.
For the compilation semantics, x is compiled into the current definition as literal.
For postponing, more time levels are involved: at text-interpretation
time (when the recognizer runs and the translation token action is performed)
the current definition is d1. When d1 runs, the current
definition is d227. The post-xt of
the translate-cell
implementation above first compiles x
into d1 and also compiles lit,
into d1 (that’s the
postpone lit,
part). When d1 runs, it pushes x and
then the lit,
compiles x into d2.
Many literal translation tokens follow this scheme.
A translation token that is quite different is translate-name
.
Here’s an implementation:
: name-intsem ( ... nt -- ... ) name>interpret execute-exit ; : name-compsem ( ... nt -- ... ) name>compile execute-exit ; : name-compcompsem ( nt -- ) lit, postpone name-compsem ; ' name-intsem ' name-compsem ' name-compcompsem translate: translate-name
Name-intsem
performs the interpretation semantics of nt, by
getting the xt of the interpretation semantics and executing it. Here
execute-exit
is used, in order for return-stack words to work
(that’s a Gforth 1.0 feature). Also, in Gforth 1.0 all words have
interpretation semantics, so the result of name-interpret
is
not tested for 0.
Name-compsem
performs the compilation semantics of nt.
Name-compcompsem
compiles the compilation semantics of nt.
This is achieved by compiling nt and name-compsem
into the
current definition d1. When d1 runs, the result performs the
compilation semantics of nt at that time.
If there is no current definition when something is compiled, Gforth outputs a warning.