6.17.5.4 Defining translation tokens

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

translate: ( int-xt comp-xt post-xt "name" –  ) gforth-experimental “translate-colon”

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.


Footnotes

(27)

If there is no current definition when something is compiled, Gforth outputs a warning.