Digest #276 2024-08-12
Contributions
comment - Resolve the forward reference orig1 using the location following the appended run-time semantics.
Seems to me that ELSE wastes a BRANCH and its target/offset. ELSE compiles this branch so THEN can have a branch target to patch at compile time.
This branch and offset might be 16 bytes wasted for a 64 bit Forth. Wasted for each ELSE,
If you keep an 8 byte stack of if/else/then state the extra branch can be eliminated.
IF pushes a token representing IF on this stack. ELSE will patch the branch added to the dictionary by IF. ELSE pops the IF token and pushes an ELSE token. THEN pops the token and if it’s an ELSE token, it doesn’t need to patch anything at run time.
: ELSE ( C: orig1 -- orig2 )
POSTPONE AHEAD 1 CS-ROLL POSTPONE THEN
; IMMEDIATE
Replies
requestClarification - Temporary removing system-compilation items
Given that a part or all of the control-flow stack items may be elsewhere, a standard program cannot consume them with n>r
.
If the committee wants to allow using depth
-based techniques for accessing data stack items below control-flow stack items, AFAICS the way to go would be to delete the second sentence that you cited above.
The existence of non-standard code that uses such techniques can be seen as existing practice.
requestClarification - State of other stacks after ABORT
Yes, I think that abort
should empty the control-flow stack and the floating-point stack, and the document should be amended to specify that. The former is easy to specify, the latter has the complication that the core system has no floating-point stack. I still would specify it with the core abort
rather than having an additional FP version of abort
; such redefinitions have produced confusion among readers of the standard document in the past.
referenceImplementation - The reference implementation is incorrect
[r327] still misses the clearing of the data stack as discussed in [r1154].
In the [r327] variant some items can be cleared from the data stack, since when an error occurs, CATCH
restores depth of all stacks, including the data stack.
But it's not quite correct since user's items can be there before CATCH
.
The following addition:
SP0 @ SP! ( perform the "clear the data stack" action of ABORT )
is not correct since it empties the data stack unconditionally, even when no error occurs.
One more implementation variant, I hope more correct:
\ A system should have ordinary words for these actions:
: ENTER-COMPILATION ( -- ) ] ;
: LEAVE-COMPILATION ( -- ) POSTPONE [ ;
: COMPILATION ( -- flag ) STATE @ 0<> ;
: QUIT ( i*x -- ⊥ ) ( C: j*x -- ⊥ ) ( F: k*r -- ⊥ )
RP0 @ SET-RP \ empty the return stack
... \ set the input source to the user input device
BEGIN
LEAVE-COMPILATION \ (if any) - enter interpretation state
[: BEGIN REFILL WHILE INTERPRET COMPILATION 0= IF ." OK " THEN CR REPEAT ;] CATCH
DUP WHILE ( ior ) CR
\ NB: in this point the input source specifications are restored (formally, by `THROW`)
CASE
-1 OF ENDOF \ abort, no any message
-2 OF ( display message from ABORT" ) ENDOF
( default ) DUP ." Exception # " . CR
ENDCASE
DEPTH NDROP \ empty the data stack and the control-flow stack
FDEPTH FNDROP \ empty the floating point stack
... \ ensure that all processing has been completed, and no ambiguous condition exists
REPEAT BYE \ no error, the end of the input stream has been reached
;
The symbol "⊥
" in a stack diagram (the bottom type) means that the caller never gets the control back.
requestClarification - Temporary removing system-compilation items
Given that a part or all of the control-flow stack items may be elsewhere, a standard program cannot consume them with
n>r
.
Yes, another part may be elsewhere, and n>r
consumes only the part that is on the data stack. But, the stack parameters that were on the data stack are unavailable only due to this part on the data stack.
Another question is, why might this code work incorrectly on a standard Forth system? I can only guess that :
stores the depth, and ;
checks that the depth is not changed. But it would be very strange behavior, since ;
is not allowed to fail due to changes in the stack depth, but only due to colon-sys.
AFAICS the way to go would be to delete the second sentence that you cited above.
I would interpret that sentence as an explanation to the reader.
What about the following wording:
The possible presence of such items on the data stack means that any items already there
shall beis unavailable to a program (exceptpick
androll
words) until the control-flow-stack items are removed from the data stack.
requestClarification - Temporary removing system-compilation items
I have realized that if a program is allowed to change the stack depth due to consuming or producing stack parameter under system-compilation items, then a system cannot implement leave
as I described in the contribution 185.
Conversely, if a system is allowed to implement leave
in that way, then a program is not allowed to change the number of stack parameters under system-compilation items.
At the moment, in this dilemma, I would prefer to give programs more options and tighten systems.
Exception handling
The standard specifies a behavior for the case when an exception is not caught by a program (functions of abort
and quit
). This behavior is not suitable for other threads/tasks.
So, another behavior should be specified: the thread is terminated, and the throw code is saved. This throw code should be obtainable via a valid task-id.
Shared resources
The user input devise is a shared resource too. What behavior is expected when two tasks call quit
in parallel?
comment - Resolve the forward reference orig1 using the location following the appended run-time semantics.
Seems to me that ELSE wastes a BRANCH and its target/offset.
The specification does not dictate any particular implementation, only the behavior that a standard program can observe (or depends on).
You can even resolve references in run-time (and/or use something like a computed goto under the hood), as long as it does not affect the behavior of a standard program.
requestClarification - Why do we use +n and not u in the stack diagram for n>r and nr>
A fact of historical curiosity.
Elizabeth D Rather <erather@forth.com>
wrote on 2010-03-14 12:11:15 -10:00
, in comp.lang.forth
, with subject Re: RfD: N>R and NR>
, message-id <iaidnUCzzNCZwgDWnZ2dnUVZ_h6dnZ2d@supernews.com>
Peter Knaggs wrote:
...I also agree with Anton in that "n" should be either "u" or "+n" as "n" allows for a negative value.
Yes, this is important. And I certainly favor +n over u. God forbid there should be thousands of cells of this stuff!
I.e, there were arguments to use +n in 2010.
comment - Resolve the forward reference orig1 using the location following the appended run-time semantics.
The specification of else
is in terms of two origs. These can be cs-roll
ed during compilation (and sometimes the cs-roll
is implicit). An example is
begin ... while ... while ... repeat ... else ... then
Maybe it's possible to still implement something along the lines of mykesx' idea by having one stack item on the additional stack per control-flow stack item and then perform a roll of that stack for every cs-roll during compilation. One would also have to care for cleaning up this run-time stack on exit
and throw
, and doing the manipulations of the additional stack even when jumping across some control-flow word. Overall this looks complicated and fraught with pitfalls. If you want to save program memory on a 64-bit machine (why?), use native-code compilation; native-code jumps typically cost 2-5 bytes on AMD64.
comment - Resolve the forward reference orig1 using the location following the appended run-time semantics.
Am I not reading the paragraph for compilation correctly?
It talks about then patching the branch added by else…