immediate
Portable implementation of [IMMEDIATE] word
\ This file is in the public domain. NO WARRANTY.
\ Originally designed by Eric Blake, August 2025,
\ with revisions inspired by ruv.
\ Add the word [IMMEDIATE] which can be used to mark the intent of defining
\ an immediate word sooner than waiting until the closing ;.
\ The goal of this file is to permit the following:
\ : name [IMMEDIATE] ... ;
\ as syntax sugar for
\ : name ... ; IMMEDIATE
\ to satisfy the mantra "say what you are doing as soon as you can":
\ https://forth-standard.org/standard/core/IMMEDIATE#reply-313
\ First, note that there is an ambiguity observable between implementations
\ on the following code:
\ : foo ." foo " ;
\ : bar [ immediate ] ." bar " ;
\ where some implementations (gforth, SwitfForth, VFX) mark bar as
\ immediate, and others (iForth) mark foo as immediate.
\ https://forth-standard.org/standard/core/IMMEDIATE#contribution-112
\ Forth-2012 16.3.3 basically states that it is ambiguous if a program
\ modifies the compilation word list during compilation, but the purpose
\ of IMMEDIATE is to change the compilation list by making the latest
\ definition immediate. So, regardless of whether : is required to make
\ the latest definition be the ongoing compilation, actually executing
\ IMMEDIATE before ; triggered ambiguous behavior.
\ At the same time, the standard requires this test to pass:
\ T{ : IW9 CREATE , DOES> @ 2 + IMMEDIATE ; -> }T
\ : FIND-IW BL WORD FIND NIP ; ( -- 0 | 1 | -1 )
\ T{ 222 IW9 IW10 FIND-IW IW10 -> -1 }T \ IW10 is not immediate
\ T{ IW10 FIND-IW IW10 -> 224 1 }T \ IW10 becomes immediate
\ Basically, once the DOES> body is installed into iw10, then any future
\ execution of iw10 invokes IMMEDIATE on whatever happens to be the most
\ recently compiled word. Furthermore, while not explicitly shown in
\ the test, it is worth noting that IW9 was not marked immediate; an
\ implementation where IMMEDIATE is immediate is unlikely to pass this
\ test, and an immediate word [IMMEDIATE] must be sure that actually
\ invoking IMMEDIATE does not happen before ; completes.
\ https://forth-standard.org/standard/testsuite#test:core:IMMEDIATE
\ Furthermore, the fact that IMMEDIATE has traditionally been non-immediate
\ means that there is an existing corpus of code that expects to be able
\ to build meta-programming words, such as:
\ : nop : POSTPONE ; IMMEDIATE ;
\ nop filler
\ which should make filler, rather than nop, be immediate. This file does
\ not change that behavior. Although this file does not declare imm:, it
\ also makes it easy to declare
\ : imm: ( "name" -- colon-sys ) : POSTPONE [IMMEDIATE] ;
\ as a way to write "imm: name" instead of ": name [IMMEDIATE]" for an
\ alternative way to declare intent of defining an immediate macro.
\ The standard is clear that IMMEDIATE is ambiguous if used when the last
\ definition was via :NONAME or SYNONYM; this file does not remove that
\ ambiguity when there is no current definition (doing so would require
\ wrapping every defining word, which does not scale), so [IMMEDIATE] is
\ likewise ambiguous in those situations. However, this file does ensure
\ that [IMMEDIATE] between :NONAME and ; is well-defined to do nothing.
\ The reason for this is so that it becomes easier to copy and paste a
\ definition body without regards to whether the definition was started
\ by ": name" or ":NONAME".
\ Implementations that provide other modifiers that affect the most recent
\ definition, such as compile-only or restrict, should be able to do similar
\ wrappers for those words to declare intent at the name of a definition.
\ Likewise, if you rely on the implementation-defined behavior of CODE or
\ ;CODE, you may want those words to have similar treatment to what this
\ file does for :.
\ Standard forth does not provide an easy interface to see if a given nt or
\ xt is immediate, although several implementations provide a word
\ "immediate?" for that purpose. For debugging the effects of this file,
\ it is possible to write a parsing word that is portable:
\ \g Parsing word to display whether the next word is immediate.
\ [DEFINED] immediate? [IF] : imm ( "word" -- ) ' immediate? . ;
\ [ELSE] : imm BL WORD FIND NIP 1 = . ; [THEN]
\ And implementations that support SEARCH-WORDLIST can avoid the need to parse:
\ t{ S" [IMMEDIATE]" GET-CURRENT SEARCH-WORDLIST NIP 1 = -> TRUE }t
\ Dependencies:
\ Requires the following words from the Core word set
\ ! ( : ; ['] 0< ?DUP EXECUTE EXIT IF IMMEDIATE POSTPONE SWAP THEN VARIABLE
\ Requires the following words from the Core Extension word set
\ \ TRUE
\ Requires the following words from the Tools Extension word set
\ [DEFINED] [IF] [THEN] [UNDEFINED]
\ Compatibility stuff, inspired by gforth
\ document a word that is ambiguous to use outside of compilation semantics
[UNDEFINED] compile-only [IF] : compile-only ; [THEN]
\ specialized tag to distinguish documentation strings from internal comments
[UNDEFINED] \g [IF] : \g ['] \ EXECUTE ; IMMEDIATE [THEN]
\ convenience for altering the contents of a variable
[UNDEFINED] off [IF] : off 0 SWAP ! ; [THEN]
[UNDEFINED] on [IF] : on TRUE SWAP ! ; [THEN]
\ Implementation
\ Integer tracking whether there is an ongoing definition body. 1 after
\ :NONAME, -1 after :, and 0 after ;.
VARIABLE (defining) 0 (defining) !
\ Flag set when ; needs to perform a delayed IMMEDIATE.
VARIABLE (needs-immediate) (needs-immediate) off
\ Wrap ; to honor (needs-immediate) and clear (defining).
: ; ( C: colon-sys -- )
\g Complete a colon-sys after appending run-time semantics of
\g ( R: nest-sys -- ) to the current definition.
POSTPONE ;
(needs-immediate) @ IF
IMMEDIATE \ uses original non-immediate word
(needs-immediate) off
THEN
(defining) off
; IMMEDIATE compile-only
\ Wrap : to delay [IMMEDIATE] by setting (defining).
: : ( C: "<spaces>name" -- colon-sys )
\g Begin a colon definition for /name/.
:
-1 (defining) !
(needs-immediate) off \ resest the flag after errors, if any
;
\ Wrap :NONAME to ignore [IMMEDIATE] by setting (defining).
[DEFINED] :NONAME [IF]
: :NONAME ( -- xt colon-sys )
\g Create an anonymous colon definition.
:NONAME
1 (defining) !
(needs-immediate) off \ resest the flag after errors, if any
;
[THEN]
\ Provide [IMMEDIATE] which will trigger the effects of IMMEDIATE at the
\ next possible safe moment.
: [IMMEDIATE] ( -- )
\g Make the most recent definition (whether a word currently being
\g defined when between : and ;, or the word most recently defined
\g when after ;) an immediate word. Ignored during the body of
\g :NONAME, otherwise ambiguous if the most recent definition does not
\g have a name, or was defined as a SYNONYM.
(defining) @ ?DUP IF \ inside : or :NONAME
0< IF (needs-immediate) on THEN \ delay during :, ignore during :NONAME
EXIT THEN
IMMEDIATE \ not currently defining, so safe to do now
; IMMEDIATE
by EricBlake
Versions
Tags
forth-2012, macros, publicdomain, ansforth, immediate
Dependencies
None
Dependents
None