Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly implement POSTPONE in a Forth system?

Tags:

forth

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?

like image 536
David Given Avatar asked Jul 26 '15 12:07

David Given


1 Answers

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:

  1. : is executed, reading GT4 and creating the new word
  2. POSTPONE's compilation semantics is executed, which is to compile the compilation semantics of GT1 - as you have seen in GForth.
  3. ; is executed, ending the definition
  4. IMMEDIATE 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.

like image 193
vukung Avatar answered Nov 03 '22 00:11

vukung