6.1.2033 POSTPONE CORE

Interpretation:

Interpretation semantics for this word are undefined.

Compilation:

( "<spaces>name" -- )

Skip leading space delimiters. Parse name delimited by a space. Find name. Append the compilation semantics of name to the current definition. An ambiguous condition exists if name is not found.

See:

Rationale:

Typical use:

   : ENDIF POSTPONE THEN ; IMMEDIATE

   : X ... IF ... ENDIF ... ;

POSTPONE replaces most of the functionality of COMPILE and [COMPILE]. COMPILE and [COMPILE] are used for the same purpose: postpone the compilation behavior of the next word in the parse area. COMPILE was designed to be applied to non-immediate words and [COMPILE] to immediate words. This burdens the programmer with needing to know which words in a system are immediate. Consequently, Forth standards have had to specify the immediacy or non-immediacy of all words covered by the standard. This unnecessarily constrains implementors.

A second problem with COMPILE is that some programmers have come to expect and exploit a particular implementation, namely:

   : COMPILE R> DUP @ , CELL+ >R ;

This implementation will not work on native code Forth systems. In a native code Forth using inline code expansion and peephole optimization, the size of the object code produced varies; this information is difficult to communicate to a "dumb" COMPILE. A "smart" (i.e., immediate) COMPILE would not have this problem, but this was forbidden in previous standards.

For these reasons, COMPILE has not been included in the standard and [COMPILE] has been moved in favor of POSTPONE. Additional discussion can be found in Hayes, J.R., "Postpone", Proceedings of the 1989 Rochester Forth Conference.

Testing:

T{ : GT4 POSTPONE GT1 ; IMMEDIATE -> }T
T{ : GT5 GT4 ; -> }T
T{ GT5 -> 123 }T

T{ : GT6 345 ; IMMEDIATE -> }T
T{ : GT7 POSTPONE GT6 ; -> }T
T{ GT7 -> 345 }T

ContributeContributions

ruvavatar of ruv [98] Ambiguous condition could be removedComment2019-07-19 15:08:09

Applying POSTPONE to some words is an ambiguous condition. Actually, it is just a concession to some Forth system implementations (see discussion wrt TO). Since the compilation semantics for these words are well defined, there is no normative cause for an ambiguous condition.

These words are: TO, IS, ACTION-OF.

Another two words are: S" and S" (the variants from FILE word set).

All these five words are "dual-semantics" words (and there are no other such standard words). It is not clear what is the difference that applying POSTPONE to the last two is not an ambiguous condition. In any case, it seems that in any standard Forth system, POSTPONE can be correctly implemented for these five words.

If these words are implemented as STATE-smart immediate words then it is enough to put a special flag to them and implement POSTPONE as the following:

: [ STATE OFF ; IMMEDIATE
: ] STATE ON ;

: EXECUTE-COMPILING ( i*x xt --j*x )
  STATE @ IF  EXECUTE  EXIT  THEN
  STATE 1+!   EXECUTE  STATE @ 1 = IF STATE OFF THEN
;

: POSTPONE
    \ ...
    \ ( xt flags )
    DUP MASK-DUALSMART AND IF DROP LIT, ['] EXECUTE-COMPILING COMPILE, EXIT THEN
    \ ...
; IMMEDIATE

The [COMPILE] can be implemented in the similar way too.

JennyBrienavatar of JennyBrien

I would agree that the ambiguous condition be removed, and istead a note be added to the specification of POSTPONE and [COMPILE] to the effect that, unless the compilation semantics of XYY are specified as in e.g. ( 'Perform the execution semantics given below' then it is an error for the code compiled by POSTPONE xyz to be executed other than during compilation.

ruvavatar of ruv

So you have suggested to make the following code non-standard:

: foo 123 . ;
: bar postpone foo ;
: baz [ bar ] ;

I don't think that it is a good idea. This code does not contain any error.

In general case, an ambiguous condition exist if you try to append something to the current definition when it is absent.

JennyBrienavatar of JennyBrien

So you have suggested to make the following code non-standard:

: foo 123 . ;
: bar postpone foo ;
: baz [ bar ] ;

I don't think that it is a good idea. This code does not contain any error.

But does it serve any useful purpose? Why not just write : baz foo ; ?

In general case, an ambiguous condition exist if you try to append something to the current definition when it is absent.

To which this is an exception because you are interpreting in the middle of a current definition. But why would you switch from compiling to interpreting in order to compile? Normally you do that for one reason only - to calculate a literal whose value will be known at compile time. Or is there some other reason that I am missing?

AntonErtlavatar of AntonErtl

@ruv: Your implementation sketch is not complete enough to be sure what the plan is.

If your plan is to special-case some system-defined words (in particular, TO, IS, ACTION-OF) in POSTPONE and append their compilation semantics rather than STATE-smart semantics, yes, this can work. A system implementor can even do it by temporarily setting STATE at run-time around the execution of the STATE-smart semantics, because the implementation of these words are the job of the system implementor (i.e., even if the source code looks like the word is STATE-smart, it does not have to behave like a STATE-smart word, while a user-defined STATE-smart word actually has to behave as a STATE-smart word). You should then also special-case dual words in ' and ['].

Does this mean we can remove the ambiguous condition? That depends on whether we want system implementors who have implemented TO, IS and ACTION-OF as STATE-smart words without this special-casing to change their implementation. Looking at the behaviour of S" in SwiftForth (it behaves as a STATE-smart rather than dual word), I don't expect SwiftForth to change even if you remove the ambiguous condition; unless a customer complains, but they apparently have not about S".

@JennyBrien: You want to replace a restriction on POSTPONEing three words with a restriction on POSTPONEing all but a very few words. No thanks.

As for the useful purpose: If you write code generators with parameters, it's more convenient to call the code generator interpretively. Say, something like

: sort [ ' < ' swap ' @ ' ! gen-quicksort ] ;
: fsort [ ' f< ' fswap ' f@ ' f! gen-quicksort ] ;

In any case, existing standard code such as my garbage collector does this. You would have to give a good reason to destandardize this code.

, but it seems that you want to support STATE-smart implementations of TO etc. by

JennyBrienavatar of JennyBrien

@JennyBrien: You want to replace a restriction on POSTPONEing three words with a restriction on POSTPONEing all but a very few words. No thanks.

As for the useful purpose: If you write code generators with parameters, it's more convenient to call the code generator interpretively. Say, something like

: sort [ ' < ' swap ' @ ' ! gen-quicksort ] ;
: fsort [ ' f< ' fswap ' f@ ' f! gen-quicksort ] ;

```

In any case, existing standard code such as my garbage collector does this. You would have to give a good reason to destandardize this code.

I understand now. It wasn't one of my brighter ideas. There's not a great deal of use for POSTPONE TO or POSTPONE S" , but I was trying to see if there were sensible restrictions that would make STATE-smart implementations of dual words unproblematic. I can see that if you do this sort of thing regularly, you would come to hate STATE-smartness with a passion.

PeterKnaggsavatar of PeterKnaggs

We have discussed this at the standards meeting and have come to the conclusion that proper implementation of dual semantics words are not sufficiently common to change the ambiguous condition.

ruvavatar of ruv

@ruv: Your implementation sketch is not complete enough to be sure what the plan is.

I published a proof of concept, a working example that can be tested on a standard Forth system, see at GitHub.

Looking at the behaviour of S" in SwiftForth (it behaves as a STATE-smart rather than dual word), I don't expect SwiftForth to change even if you remove the ambiguous condition; unless a customer complains, but they apparently have not about S".

My proposal does not affect S" word. Applying POSTPONE to S" was never ambiguous. So, behavior of POSTPONE S" is already non standard in SwiftForth. I just show a simple way how to fix this issue, and also make POSTPONE proper working for other words with minimal effort.

I agree that it is reasonable to wait until the proper implementation will be sufficiently common. OTOH, there is a sense to take into account only those Forth systems in which POSTPONE correctly works for all words except TO, IS and ACTION-OF (that are only affected by my proposal). Since if POSTPONE incorrectly works for some other words in a Forth system, changing the ambiguous condition regarding these three words does not change the compliance status of this Forth system. Don't sure that we know even a few such Forth systems.

ruvavatar of ruv

behavior of POSTPONE S" is already non standard in SwiftForth

I was wrong on that.

According to the TC reply to RFI Q99-027, an ambiguous condition exists if a program performs compilation semantics in interpretation state.

So the behavior of SwiftForth in this case does not meet the common expectations, but it is still standard compliant.

Reply New Version

MitraArdronavatar of MitraArdron [175] GT1 not defined Request for clarification2021-01-21 06:32:53

In the testing above GT1 is not defined.

PeterKnaggsavatar of PeterKnaggs

Correct see Annex F.

MitraArdronavatar of MitraArdron

In which case shouldn't GT1 be linked to the definition on that page ?

GeraldWodniavatar of GeraldWodni

The committee discussed that issue:

It would blow up the testcases if we add all required test-words for every word making the document too big.

We could do this on the website itself though: the best solution would be to write a forth program which finds all required references and updates the test definitions for the words. However this is a lot of work. If you are interested, we would welcome a pull request for this feature on the forth-standard.org repository.

Closed
Reply New Version

MitraArdronavatar of MitraArdron [177] Needs an example of replacing COMPILEExample2021-01-22 03:42:24

I don't know about anyone else, but how POSTPONE replaces [COMPILE] is clear, much as I've tried I can't figure out how POSTPONE replaces COMPILE.

For example with something like : AGAIN COMPILE branch ; ( a -- ) how is that intended to be replaced by POSTPONE is unclear.

AntonErtlavatar of AntonErtl

Just use POSTPONE instead of COMPILE; adding the <RESOLVE that is missing from your AGAIN, this results in:

: again postpone branch <resolve ;

MitraArdronavatar of MitraArdron

Thanks for catching my mistake - I think that clarification should be added in the text above, because this is two VERY different behaviors, i.e. in

: ENDIF POSTPONE THEN ; IMMEDIATE
: AGAIN POSTPONE branch <RESOLVE ; IMMEDIATE
: foo ....ENDIF .... AGAIN ; 

The first POSTPONE causes THEN to be compiled into ENDIF, while the second causes branch to be compiled into foo, i.e. not only does the programmer still have to be very cognisant of the immediateness or otherwise of THEN and branch, but the implementer of POSTPONE (me in this case) has to write a definition that checks if THEN or branch are immediate and behave differently for the two cases.

While it could be argued that this is precisely explained by the standard as written above, to anyone coming from a eForth or similar environment, familiar with COMPILE and [COMPILE], I think it is highly ambiguous, and would strongly suggest some explanation in the standard.

AntonErtlavatar of AntonErtl

Both cause the compilation semantics of the POSTPONEd word to be performed during the compilation of FOO.

The programmer does not have to know the immediacy of the POSTPONEd word at all, but has to know the compilation semantics (admittedly, if you think about words in terms of "immediate" or not, you need to know the immediacy in order to know the compilation semantics, but that's a result of framing the whole issue in these terms, not something innate in POSTPONE). The whole reason for POSTPONE's standardization is that systems like cmForth have, e.g., "immediate" implementations of, e.g., +, and classical COMPILE would not work for them (here "immediate" is used in an implementation-specific way; in the standard sense + is not immediate).

The system implementor does not need to check for immediacy, either. E.g., here's a definition of POSTPONE that does not check:

: ?parse-name ( "name" -- c-addr u )
  parse-name dup 0= -16 and throw ;

: ?find-name ( c-addr u -- nt )
  find-name dup 0= -13 and throw ;

: postpone ( "name" -- )
  ?parse-name ?find-name name>compile
  ( xt1 xt2 ) swap postpone literal compile, ;

Sure, if your system uses a header flag for "immediate", NAME>COMPILE will check that flag, but other systems are possible. E.g., in Gforth NAME>COMPILE is equivalent to

: NAME>COMPILE ( nt -- x xt ) dup cell- @ >vt>comp @ execute ;

I made two attempts to explain semantics: one in 2000 (slight revision in 2004) and one in 2010. But there is no consensus on the Forth200x committee about adding such explanations, so nothing in this direction has been added to the standards document yet.

MitraArdronavatar of MitraArdron

Sure - as I said, precisely explained but essentially incomprehensible.

Yes, I agree there are multiple ways of implementing this, but this word explicitly replaces COMPILE in a way that is VERY hard to follow, with no examples. My example referred to cases where the code is implemented as a threaded dictionary (old-style) and in that case I assert you do still need to know whether a word is immediate both in the use of POSTPONE (i.e. I want it to compile "branch" into foo, but to execute THEN) and have to check this in POSTPONE (you've used NAME>COMPILE to effectively do that checking, and NAME>COMPILE is, as far as I can tell, not relevant in systems that only use a single XT.

Its a pity if, as you suggest, the Standards Committee doesn't really want this standard to be clear and implementable, that might suggest why a lot of implementations still use older standards or eForth :-(

AntonErtlavatar of AntonErtl

I think it's the other way round: Because many people think in terms of immediate-flag systems, the standard appears hard to understand to them. Maybe we need another Bill Muench and another C.H.Ting to code up and produce educational material for a system that's more in line with standard concepts.

NAME>COMPILE is a standard word and is relevant to single-xt+immediate-flag systems, too, but yes, on such a system it will check the immediate flag.

ruvavatar of ruv

the implementer of POSTPONE (me in this case) has to write a definition that checks if THEN or branch are immediate and behave differently for the two cases.

If you implement immediacy via the corresponding flag then yes, you have to check it. Perhaps not in the definition of POSTPONE directly, but in some place it should be checked. And that was the initial point — to move the problem of choosing between COMPILE and [COMPILE] from the programmers to the system. This solution has some edge cases, but they are very rare in practice.

: ENDIF POSTPONE THEN ; IMMEDIATE
: AGAIN POSTPONE branch <RESOLVE ; IMMEDIATE
: foo .... ENDIF .... AGAIN ; 

The first POSTPONE causes THEN to be compiled into ENDIF, while the second causes branch to be compiled into foo

I can suggest another point of view. In Forth we can (with some obvious reservations) replace the use of a word by its definition. If a word is immediate then its definition is placed in square brackets when it's used in compilation state (with reservation concerning control flow and setting compilation state).

Let's assume that the interpretation semantics of POSTPONE is to take the word in the argument and perform its compilation semantics.

Then your definition of foo

: foo .... ENDIF .... AGAIN ; 

is equivalent to

: foo .... [ POSTPONE THEN ] ....  [ POSTPONE branch <RESOLVE ] ; 

That's equivalent to

: foo .... THEN ....  branch  [ <RESOLVE ] ; 

<small>(since performing compilation semantics is what the Forth text interpreter does when it encounter a word in compilation state)

It looks like the both words are just syntactically placed into foo. And what is compiled into foo depends on these words.

So we can think about it as if the argument of POSTPONE (or better say, its uniquely named synonym) is always syntactically placed instead of the containing word (e.g., into the definition of the target word), and is encountered by the Forth text interpreter. And what is compiled depends on this word. If it's a regular word then it's compiled as is, if it's an immediate word then it's executed and it can compile some other words.

MatteoVitturiavatar of MatteoVitturi

Hello,

POSTPONE 's Interpretation semantics are undefined and [ is an immediate word used to enter interpretation state, this implies that in the example

: foo .... [ POSTPONE THEN ] ....  [ POSTPONE branch <RESOLVE ] ; 

POSTPONE used within square brackets leads to an ambigous condition.

Where am I wrong ?

ruvavatar of ruv

POSTPONE used within square brackets leads to an ambigous condition. Where am I wrong?

It's wrong to assume that it's a real code. In general, it's a pseudo-code, and just a kind of algebra, when you substitute one expression by another expression according to some rules (and this code fragment is an intermediate step in an algebraic transformation). It's aimed to better understand something or prove.

In some cases, when the certain assumptions are met, such a pseudo-code can be a correct real code too.

According to the standard, a Forth system is allowed to provide implementation-defined interpretation semantics for a word for which the standard undefines interpretation semantics (see A.3.4.3.2).

In this particular case, the assumption is that the interpretation semantics for postpone are to take the word/name in the argument and perform the compilation semantics for it (NB: this assumption was mentioned!). Moreover, if you hold the equivalence of compile, and postpone literal postpone execute, and provide the ordinary compile, word, then these interpretation semantics for postpone are the only possible option.

AntonErtlavatar of AntonErtl

The question has been answered.

Closed
Reply New Version

ruvavatar of ruv [198] Portable implementation for POSTPONESuggested reference implementation2021-05-06 09:19:58

One simple and almost portable implementation of POSTPONE is following:

: POSTPONE ( "name" -- )
  BL WORD FIND DUP 0= -13 AND THROW 1 = ( xt flag-compilation )
  SWAP LIT,  IF ['] EXECUTE  ELSE ['] COMPILE,  THEN  COMPILE,
; IMMEDIATE

This implementation uses the non-standard LIT, word (that is factor of LITERAL), and applies Tick to COMPILE, (that is ambiguous in the general case due to undefined interpretation semantics for COMPILE, at the moment). Also this implementation relies on the TC reply to RFI Q99-027 and doesn't meet the common expectations concerning a system behavior in this regard (a more completed implementation can be found in my gist on GitHub).

LIT, execution: ( x -- ) append the run-time semantics "place x on the stack" to the current definition.

If LIT, is absent, it can be replaced by the phrase 0 <# #S #> EVALUATE in the definition above.

LITERAL can be defined via LIT, and vise versa:

: LITERAL ( x -- ) LIT, ; IMMEDIATE

: LIT, ( x -- ) POSTPONE LITERAL ;

To make applying Tick to COMPILE, compliant, it's enough to redefined it:

: COMPILE, ( xt -- ) COMPILE, ;

ruvavatar of ruvNew Version: [198] Portable implementation for POSTPONE

Hide differences

One simple and almost portable implementation of POSTPONE is following:

One simple and almost portable implementation of POSTPONE is following (now slightly shorter):

: POSTPONE ( "name" -- )
  BL WORD FIND DUP 0= -13 AND THROW 1 = ( xt flag-compilation )

SWAP LIT, IF ['] EXECUTE ELSE ['] COMPILE, THEN COMPILE,

IF COMPILE, EXIT THEN LIT, ['] COMPILE, COMPILE,

; IMMEDIATE


This implementation uses the non-standard `LIT,` word (that is factor of `LITERAL`), and applies Tick to `COMPILE,` (that is ambiguous in the general case due to undefined interpretation semantics for `COMPILE,` at the moment).  Also this implementation relies on the [TC reply to RFI Q99-027](https://groups.google.com/forum/message/raw?msg=comp.lang.forth/RmsDuen7YkY/xDvW74uzi30J/) and doesn't meet the common expectations concerning a system behavior in this regard (a more completed implementation can be found in my [_gist_](https://gist.github.com/ruv/fe2256dde1ca304f31ed925c8b998259) on GitHub).


`LIT, ` execution: _( x -- )_ append the run-time semantics "place _x_ on the stack" to the current definition.

If `LIT,` is absent, it can be replaced by the phrase `0 <# #S #> EVALUATE` in the definition above.

`LITERAL` can be defined via `LIT,` and vise versa:

: LITERAL ( x -- ) LIT, ; IMMEDIATE

: LIT, ( x -- ) POSTPONE LITERAL ;



To make applying Tick to `COMPILE,` compliant, it's enough to redefined it:

: COMPILE, ( xt -- ) COMPILE, ;


agsbavatar of agsb

POSTPONE must always occur between : and ; ?

ruvavatar of ruv

In general, POSTPONE may occur (may be referenced to) outside of a colon definition (: name ... ;).

For example, with synonym:

synonym p postpone

But when it's encountered by the Forth text interpreter in a standard program, it may occur only inside a colon definition.

Reply New Version

LSchmidtavatar of LSchmidt [221] I suggest to complete the testComment2022-02-27 14:26:42

add a word : GT1 123 ; as first line.
Currently it is necessary to extrapolate from context that a non-immediate word with that name and semantics is required.

ruvavatar of ruv

add a word : GT1 123 ; as first line.

The testcase fragments of code are automatically generated from the full test suite. And some words are reused in many places. And it's not easily to automatically detect and include such words in the fragment.

Probably the problem can be mitigated by adding a link to the full testcase, in this case: https://forth-standard.org/standard/testsuite#test:core:POSTPONE So, a definition missed in the place can be found somewhere above.

Reply New Version