Reputation: 13701
John Heyes' ANS Forth test suite contains the following definition:
: IFFLOORED [ -3 2 / -2 = INVERT ] LITERAL IF POSTPONE \ THEN ;
This is then used to conditionally define various words depending on whether we're using floored or symmetric division:
IFFLOORED : T/MOD >R S>D R> FM/MOD ;
So IFFLOORED
acts like either a noop or a \
depending on the result of the expression. Fine. That's easily implementable on my threaded interpreter by doing this:
: POSTPONE ' , ; IMMEDIATE
...and now IFFLOORED
works; the definition is equivalent to : IFFLOORED -1 IF ['] \ EXECUTE THEN ;
.
Unfortunately, further down the test suite is the following code:
: GT1 123 ;
: GT4 POSTPONE GT1 ; IMMEDIATE
: GT5 GT4 ;
\ assertion here that the stack is empty
The same implementation doesn't work here. If POSTPONE
compiles a reference to its word, then GT4
becomes the equivalent of : GT4 123 ;
... but GT4
is immediate. So when GT5
is defined, 123 is pushed onto the compiler's stack and GT5
becomes a noop. But that's not right; the test suite expects calling GT5
to leave 123 on the stack. So for this to work, POSTPONE
must generate code which generates code:
: POSTPONE ' LITERAL ['] , LITERAL ;
And, indeed, if I play with gForth, I see that POSTPONE
actually works like this:
: GT1 123 ;
: GT4 POSTPONE GT1 ; IMMEDIATE
SEE GT4
<long number> compile, ;
But these two definitions are not compatible. If I use the second definition, the first test fails (because now IFFLOORED
tries to compile \
rather than executing it). If I use the first definition, the second test fails (because GT4
pushes onto the compiler stack rather than compiling a literal push).
...but both tests pass in gForth.
So what's going on?
Upvotes: 9
Views: 2007
Reputation: 8564
The behavior of postpone
word in compilation state is to determine compilation semantics for its parsed argument and append these semantics to the current definition.
Compilation semantics for a word can be either special or ordinary (see the section 3.4.3.3 Compilation semantics of Forth-2012). To work correctly, postpone
should distinguish these cases and generate code according to the different patterns.
A problem of your implementations is that they are correct either for ordinary compilation semantics or for special compilation semantics.
A standard compliant implementation is as follows:
: state-on ( -- ) 1 state ! ;
: state-off ( -- ) 0 state ! ;
: execute-compiling ( i*x xt --j*x )
state @ if execute exit then
state-on execute state-off
;
: postpone ( "name" -- )
bl word find dup 0= -13 and throw 1 = ( xt flag-special )
swap lit, if ['] execute-compiling else ['] compile, then compile,
; immediate
See more details in my post How POSTPONE should work.
Upvotes: 0
Reputation: 1884
Let me answer here, as the question changed considerably. I am still not sure I understand the question, though :)
In your example, you define
: GT4 POSTPONE GT1 ; IMMEDIATE
What happens here, is the following:
:
is executed, reading GT4
and creating the new wordPOSTPONE
's compilation semantics is executed, which is to compile the compilation semantics of GT1
- as you have seen in GForth.;
is executed, ending the definitionIMMEDIATE
is executed, marking the last defined word as immediate.POSTPONE
is called only when compiling GT4
, and it does not appear in the compiled code. So when later using this immediate word in the definition of GT5
, the interpretation semantics of POSTPONE
is not needed.
By the way, according to the standard, POSTPONE
has only compilation semantics, and the interpretation semantics is undefined.
See also the POSTPONE
tutorial in the GForth manual.
EDIT Examples of interpretation and compilation semantics:
: TEST1 ." interpretation" ; => ok
: TEST2 ." compilation" ; IMMEDIATE => ok
: TEST3 TEST1 TEST2 ; => compilation ok
TEST3 => interpretation ok
: TEST4 POSTPONE TEST1 ; IMMEDIATE => ok
: TEST5 TEST4 ; => ok
TEST5 => interpretation ok
: TEST6 POSTPONE TEST2 ; IMMEDIATE => ok
TEST6 => compilation ok
If you have any more questions, you can reference these tests.
Upvotes: 9
Reputation: 1884
The snippet you've quoted does the following things:
IFFLOORED
, so when it is evaluated, it will put this value on the stack. (This is the effect of LITERAL
.)IFFLOORED
, after pushing the value on the stack, comes an IF
- THEN
expression. When the value is true, it means that we are not in a floored environment, so we want to comment out the rest of the line, and that is what \
does.So here comes the tricky part - \
is IMMEDIATE
, i.e., you cannot use it inside a colon definition, because it will comment out the rest of the line. You have to explicitly tell the compiler that you want to compile this function, and not execute it, which is what POSTPONE
does.
Upvotes: 1