Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Extended Flow Control Structures

440 views
Skip to first unread message

Krishna Myneni

unread,
Jun 22, 2022, 10:32:44 PM6/22/22
to
I've long put off introducing extended flow control structures in
kForth, primarily for the reason that I've not needed them before (I
remember one instance of porting Forth code which used two WHILEs within
a BEGIN ... REPEAT structure, but it was easy to work around it.
Extended flow control structures are of the type shown in Fig. A.3 in
the Forth-2012 standard:

BEGIN ... WHILE ... WHILE ... REPEAT ... THEN

BEGIN ... WHILE ... UNTIL ... ELSE ... THEN

and others. Andrew Haley mentioned at one time, in a discussion on
c.l.f. about 21 years ago, that Forth should also be able to do

DO ... WHILE ... LOOP ... ELSE ... THEN

I admit I don't know if the standard guarantees the above extended DO
... LOOP structure. There are probably other variants of extended flow
control structures, beyond those shown in Fig. A3, which are compatible
with the Forth-2012 standard, and an enumeration of all of these would
be useful to have in the standard.

The topic comes up again for me when converting old unstructured Fortran
77 code into structured Forth. In the Fortran 77 code, line numbers and
GO TOs are used to create IF ... ELSE ... THEN and BEGIN ... WHILE ...
REPEAT structures, and also the extended versions containing multiple
WHILEs within a BEGIN ... REPEAT structure. It's often not obvious which
extended flow control pattern is being created with GO TOs, but Forth
patterns such as the one below do show up in the old code:

BEGIN ... WHILE ... WHILE ... WHILE ... REPEAT ... ELSE ... THEN ...
ELSE ... THEN

It's not immediately obvious when one has not used such a structure what
the branching is, so I wrote a non-trivial case to understand the flow
better. The problem is to loop over a range of integers and test each
integer to see if it falls within one of several intervals. If so, we
branch out of the loop and return the appropriate interval number. While
this problem can be trivially handled by examining the overlap of the
loop range with the intervals, it is nevertheless illustrative to do --
a less trivial problem would be to loop over an array of unsorted
integers and break out of the loop when one integer falls into an interval.

The code is given below and has been tested under Gforth. It does what I
expect based on the flow control diagram in Fig. A.3 of the Forth 2012
standard. Note which WHILE maps to which ELSE in this example. What is
your experience with extended flow control structures? They do appear to
be useful when converting unstructured code to structured code.

--
Krishna Myneni


--- start of code ---
\ Loop over a range of integers, from nlow to nhigh,
\ and stop whenever the current integer falls within
\ one of the specified intervals. Return the interval
\ number; 0 if no interval encountered.
\
\ # interval
\ 1 [-10, -6]
\ 2 [ -3, 1]
\ 3 [ 3, 16]
\ 4 [ 24, 29]
\ 5 [114,127]
\

-10 -6 2constant i1
-3 1 2constant i2
3 16 2constant i3
24 29 2constant i4
114 127 2constant i5

: test-ec1 ( nlow nhigh -- intvl )
2dup > IF swap THEN
BEGIN
over i5 1+ rot within 0= \ not in i5?
WHILE
over i4 1+ rot within 0= \ not in i4?
WHILE
over i3 1+ rot within 0= \ not in i3?
WHILE
over i2 1+ rot within 0= \ not in i2?
WHILE
over i1 1+ rot within 0= \ not in i1?
WHILE
2dup <=
WHILE
swap 1+ swap
REPEAT
0 \ no interval encountered
ELSE 1 THEN
ELSE 2 THEN
ELSE 3 THEN
ELSE 4 THEN
ELSE 5 THEN
>r 2drop r>
;
--- end of code ---

--- examples ---
13 17 test-ec1 . 3 ok
17 23 test-ec1 . 0 ok
17 24 test-ec1 . 4 ok
16 24 test-ec1 . 3 ok
44 88 test-ec1 . 0 ok
--- end of examples ---


dxforth

unread,
Jun 23, 2022, 12:37:54 AM6/23/22
to
On 23/06/2022 12:32, Krishna Myneni wrote:
>
> Andrew Haley mentioned at one time, in a discussion on
> c.l.f. about 21 years ago, that Forth should also be able to do
>
> DO ... WHILE ... LOOP ... ELSE ... THEN
>
> I admit I don't know if the standard guarantees the above extended DO
> ... LOOP structure.

No, since it intermixes two different control structures.
Usual way of handling it is IF ... UNLOOP EXIT THEN.
Rule-of-thumb: anything that encourages ELSE is best avoided.
Same for ENDOF (ELSE by another name).

minf...@arcor.de

unread,
Jun 23, 2022, 3:39:15 AM6/23/22
to
Krishna Myneni schrieb am Donnerstag, 23. Juni 2022 um 04:32:44 UTC+2:
> I've long put off introducing extended flow control structures in
> kForth, primarily for the reason that I've not needed them before (I
> remember one instance of porting Forth code which used two WHILEs within
> a BEGIN ... REPEAT structure, but it was easy to work around it.

I find readability of control flows very important. Therefore in my code I abstain
from Forth's "extended" control flow structures ("poor man's" control flows would
describe them better).

Therefore I added four C-like elements to my Forth: BREAK CONTINUE GOTO LABEL
and never looked back.

Within the same pot: (?)DO loops. Eg UNLOOP is a language dinosaur that any decent
compiler should handle automatically. Therefore I use my own FOR..NEXT variants mostly.

none albert

unread,
Jun 23, 2022, 4:00:35 AM6/23/22
to
In article <t90jc8$kjq$1...@dont-email.me>,
Python has introduced stuff much like this.
It requires much in the realm of getting used to and there seems no
reasonable/intuitive way to express this stuff.
The way do..loop uses the return stack makes it akward to
have WHILE in loop.

Otoh, using infix it can be done, in algol68 :

FOR i FROM x STEP stride UNTIL y WHILE condition DO .. OD

is easy to remember because it is always the same and each
member can be left out.

FROM 1900 UNTIL 2000 DO accumulate_harvest OD

Groetjes Albert
--
"in our communism country Viet Nam, people are forced to be
alive and in the western country like US, people are free to
die from Covid 19 lol" duc ha
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Anton Ertl

unread,
Jun 23, 2022, 4:14:11 AM6/23/22
to
Krishna Myneni <krishna...@ccreweb.org> writes:
>Andrew Haley mentioned at one time, in a discussion on
>c.l.f. about 21 years ago, that Forth should also be able to do
>
>DO ... WHILE ... LOOP ... ELSE ... THEN
>
>I admit I don't know if the standard guarantees the above extended DO
>... LOOP structure.

DO leaves a do-sys on the control-flow stack; WHILE expects a dest.
So no, the above construct cannot occur in a standard program.

>The topic comes up again for me when converting old unstructured Fortran
>77 code into structured Forth. In the Fortran 77 code, line numbers and
>GO TOs are used to create IF ... ELSE ... THEN and BEGIN ... WHILE ...
>REPEAT structures, and also the extended versions containing multiple
>WHILEs within a BEGIN ... REPEAT structure. It's often not obvious which
>extended flow control pattern is being created with GO TOs,

In one case I had created a program with gotos (in C), because that
appeared more natural to me at the time. My boss insisted that I
convert it to structured code, so I drew a control-flow diagram, and
then it became clear to me how to reorganize the code into the
well-known structured constructs.

>but Forth
>patterns such as the one below do show up in the old code:
>
>BEGIN ... WHILE ... WHILE ... WHILE ... REPEAT ... ELSE ... THEN ...
>ELSE ... THEN
>
>It's not immediately obvious when one has not used such a structure what
>the branching is,

Even if one has used it, it's hard to follow. If you feel the need
for special handling of different exits, better use some other
construction. We have discussed several over the years. I am pretty
happy with

case
code1 cond1 0= ?of special-handling1 endof
code2 cond2 0= ?of special-handling2 endof
code3 cond3 0= ?of special-handling3 endof
code4
next-case

instead of

begin
code1 cond1 while
code2 cond3 while
code3 cond3 while
code4 repeat
special-handling3
else
special-handling2 then
else
special-handling1
then

but other options have been presented.
The uniform cases and the uniform results suggest a more data-driven
approach. Also, binary search is probably more efficient than linear
search even for such a small number of cases.

- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: https://forth-standard.org/
EuroForth 2022: http://www.euroforth.org/ef22/cfp.html

Stephen Pelc

unread,
Jun 23, 2022, 5:58:37 AM6/23/22
to
On 23 Jun 2022 at 04:32:39 CEST, "Krishna Myneni" <krishna...@ccreweb.org>
wrote:

> I've long put off introducing extended flow control structures in
> kForth, primarily for the reason that I've not needed them before
The problem for me is the readability. The multiple WHILES are no problem. The
multiple THENs are no problem until you add the incomprehensible ELSE
clauses.

When writing code that must last a while and be maintained and edited, the
maintenance programmer has to understand the code.

"The competent programmer is fully aware of the strictly limited size of his
own skull; therefore he approaches the programming task in full humility, and
"We know about as much about software quality problems as they knew about
the Black Plague in the 1600s. We've seen the victims' agonies and helped
burn the corpses. We don't know what causes it; we don't really know if there
is only one disease. We just suffer - and keep pouring our sewage into our
Looking at my own practice, I occasionally use multiple WHILEs, and the
THENs always follow directly after the REPEAT. There are no ELSE
clauses.

Stephen
--
Stephen Pelc, ste...@vfxforth.com
MicroProcessor Engineering, Ltd. - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, +44 (0)78 0390 3612, +34 649 662 974
http://www.mpeforth.com - free VFX Forth downloads

Marcel Hendrix

unread,
Jun 23, 2022, 7:10:04 AM6/23/22
to
On Thursday, June 23, 2022 at 11:58:37 AM UTC+2, Stephen Pelc wrote:
> On 23 Jun 2022 at 04:32:39 CEST, "Krishna Myneni" <krishna...@ccreweb.org>
> wrote:
> > I've long put off introducing extended flow control structures in
> > kForth, primarily for the reason that I've not needed them before
> > The code is given below and has been tested under Gforth. It does what I
> > expect based on the flow control diagram in Fig. A.3 of the Forth 2012
> > standard. Note which WHILE maps to which ELSE in this example. What is
> > your experience with extended flow control structures? They do appear to
> > be useful when converting unstructured code to structured code.
[..]

Agree with Anton and Stephen. Here the horror starts:
> > REPEAT
> > 0 \ no interval encountered
> > ELSE 1 THEN
> > ELSE 2 THEN
> > ELSE 3 THEN
> > ELSE 4 THEN
> > ELSE 5 THEN

The multi-WHILE is clear and OK, the ELSE THENs decide from condensed electrons.
I would use xx EXIT instead (and use the intervals verbatim).

-marcel

Krishna Myneni

unread,
Jun 23, 2022, 7:38:15 AM6/23/22
to
I had coded it originally without ELSEs, only THENs and EXITs,

REPEAT
2drop 0 EXIT
THEN 2drop 1 EXIT
THEN 2drop 2 EXIT
\ etc.

but the redundant stack cleanup code of 2DROP caused me to switch to
using ELSE for the branch point of the WHILEs in the loop. Also, one
exit from the word is generally considered better than multiple exits,
for analyzing program flow.

I guess I don't see the problem with using ELSEs, but then again I
haven't tried to comprehend someone else's code using this type of flow
control.

--
Krishna


Krishna Myneni

unread,
Jun 23, 2022, 7:47:07 AM6/23/22
to
The big danger in the type of flow control used above is mismatching the
branch points with the conditional branches, i.e. the code following
ELSE (or THEN if no ELSE is present) with the corresponding WHILE. My
purpose in writing the example code above was to see how the branch
points corresponded with the WHILEs.

Offhand, I don't see too much problem in using such a construct, but
Anton suggests there are alternate flow control structures which are
more transparent about the branching. In the early unstructured code
which I'm porting, with equivalence to the above but using THENs without
ELSEs, was to allow multiple branch points to execute the same
subsequent code, e.g.

REPEAT
...
THEN
...
THEN
...

In this way, multiple branches can share the same code, which was
important in those days for reducing program memory.

--
Krishna



Krishna Myneni

unread,
Jun 23, 2022, 7:53:37 AM6/23/22
to
On 6/23/22 02:39, minf...@arcor.de wrote:
> Krishna Myneni schrieb am Donnerstag, 23. Juni 2022 um 04:32:44 UTC+2:
>> I've long put off introducing extended flow control structures in
>> kForth, primarily for the reason that I've not needed them before (I
>> remember one instance of porting Forth code which used two WHILEs within
>> a BEGIN ... REPEAT structure, but it was easy to work around it.
>
> I find readability of control flows very important. Therefore in my code I abstain
> from Forth's "extended" control flow structures ("poor man's" control flows would
> describe them better).
>

The homily about teaching a poor man to fish comes to mind -- in the
current context, it's about enabling a poor man to solve linear systems
of equations (have you seen the licensing costs for matlab?).

> Therefore I added four C-like elements to my Forth: BREAK CONTINUE GOTO LABEL
> and never looked back.
>

After looking at the unstructured Fortran 77 code for days, I'd be happy
if I never saw another GOTO again. Wil Baden argued against the
knee-jerk reaction to using GOTO, when used in moderation. I don't know ...

> Within the same pot: (?)DO loops. Eg UNLOOP is a language dinosaur that any decent
> compiler should handle automatically. Therefore I use my own FOR..NEXT variants mostly.

What do you mean handle automatically? What is your substitute for
"UNLOOP EXIT" ?

--
Krishna

Krishna Myneni

unread,
Jun 23, 2022, 8:00:37 AM6/23/22
to
On 6/23/22 02:41, Anton Ertl wrote:
> Krishna Myneni <krishna...@ccreweb.org> writes:
>> Andrew Haley mentioned at one time, in a discussion on
>> c.l.f. about 21 years ago, that Forth should also be able to do
>>
>> DO ... WHILE ... LOOP ... ELSE ... THEN
>>
>> I admit I don't know if the standard guarantees the above extended DO
>> ... LOOP structure.
>
> DO leaves a do-sys on the control-flow stack; WHILE expects a dest.
> So no, the above construct cannot occur in a standard program.
>

Ah, thanks.

>> The topic comes up again for me when converting old unstructured Fortran
>> 77 code into structured Forth. In the Fortran 77 code, line numbers and
>> GO TOs are used to create IF ... ELSE ... THEN and BEGIN ... WHILE ...
>> REPEAT structures, and also the extended versions containing multiple
>> WHILEs within a BEGIN ... REPEAT structure. It's often not obvious which
>> extended flow control pattern is being created with GO TOs,
>
> In one case I had created a program with gotos (in C), because that
> appeared more natural to me at the time. My boss insisted that I
> convert it to structured code, so I drew a control-flow diagram, and
> then it became clear to me how to reorganize the code into the
> well-known structured constructs.
>

I'm able to figure out the equivalent structured flow after staring at
the F77 program long enough, and making notes interspersed with the
code, but it is a tedious process.

>> but Forth
>> patterns such as the one below do show up in the old code:
>>
>> BEGIN ... WHILE ... WHILE ... WHILE ... REPEAT ... ELSE ... THEN ...
>> ELSE ... THEN
>>
>> It's not immediately obvious when one has not used such a structure what
>> the branching is,
>
> Even if one has used it, it's hard to follow. If you feel the need
> for special handling of different exits, better use some other
> construction. We have discussed several over the years. I am pretty
> happy with

One issue is that the special handling for different conditional tests
share code by falling through from one branch to the next. We can handle
this in Forth with factoring, or careful use of THEN and ELSE ... THEN
flow, but I think factoring is easier to understand.
For scaling to large problems, a large array of integers to test or an
arbitrary number of intervals, there will be more efficient ways to do
this. This was an exercise to understand the branching of this type of
extended flow control structure.


--
Krishna

Krishna Myneni

unread,
Jun 23, 2022, 8:06:22 AM6/23/22
to
It's not the return stack that poses the problem with the standard --
it's the control stack, as Anton pointed out.

I think the BEGIN WHILE ... WHILE ... structures can become
comprehensible through use. They are so little used in much of the Forth
code I have encountered that I never saw it as priority to implement.
However, I think it is important that such flow control structures work
per the Forth 2012 standard, within a Forth system. I do plan to fix
this shortcoming in my own system.

> Otoh, using infix it can be done, in algol68 :
>
> FOR i FROM x STEP stride UNTIL y WHILE condition DO .. OD
>
> is easy to remember because it is always the same and each
> member can be left out.
>
> FROM 1900 UNTIL 2000 DO accumulate_harvest OD
>

Yes, Andrew Haley referenced this capability of Algol for DO loops.

--
Krishna

Hans Bezemer

unread,
Jun 23, 2022, 8:18:42 AM6/23/22
to
On Thursday, June 23, 2022 at 9:39:15 AM UTC+2, minf...@arcor.de wrote:
> Therefore I added four C-like elements to my Forth: BREAK CONTINUE GOTO LABEL
> and never looked back.
I considered those - but there are numerous problems with these:
- BREAK and CONTINUE are only useful if combined with "IF". Now a reference to the WHILE
loop is buried under this one. Bonus points if you embed them in several IF's;
- There is a significant difference if the LABEL if defined before or after the first GOTO.
I suppose don't have to spell them out.

> Within the same pot: (?)DO loops. Eg UNLOOP is a language dinosaur that any decent
> compiler should handle automatically. Therefore I use my own FOR..NEXT variants mostly.
I wonder how:

: TEST 10 0 DO 10 0 DO j i * 25 = IF EXIT THEN LOOP LOOP ;

Well, clean this one up without UNLOOP..

I've always favored a "BEGIN WHILE WHILE UNTIL|REPEAT|AGAIN" syntax - simply because it
is easy. You simply gobble up WHILE references until you hit a BEGIN reference. I've also got a
DONE (which deals with a failed WHILE), but I've never needed to use that one IRL. BUT..
since it is positioned near the failed WHILE it's easier to figure out:

In this example ”-TRAILING” tells you why it terminated:

: -trailing ( a n1 -- a n2)
begin
dup
while \ quit if length is zero
done ." All spaces or NULL string" cr done
2dup 1- chars + c@ bl =
while \ quit if it is not a space
1- \ decrement length
done ." String trimmed" cr done
repeat
;

Hans Bezemer

Hans Bezemer

unread,
Jun 23, 2022, 8:26:40 AM6/23/22
to
On Thursday, June 23, 2022 at 2:06:22 PM UTC+2, Krishna Myneni wrote:
> I think the BEGIN WHILE ... WHILE ... structures can become
> comprehensible through use. They are so little used in much of the Forth
> code I have encountered that I never saw it as priority to implement.
You might be right - because I use them quite a lot! But like I said: 4tH's
DO..REPEAT is a very transparent construct. If I present code here I always
have to put in those pesky THEN's - which is kind of ugly.

> > Otoh, using infix it can be done, in algol68 :
> > FOR i FROM x STEP stride UNTIL y WHILE condition DO .. OD
It's almost uBasic/4tH code:
FOR i TO x STEP stride
UNTIL y
WHILE condition
(..)
NEXT

Can't say it does the same thing as the Algol snippet, but you can construct
such a thing.

Hans Bezemer

Anton Ertl

unread,
Jun 23, 2022, 11:10:30 AM6/23/22
to
That's just as bad, for the same reason: the code the WHILE jumps to
is far from the WHILE, and it takes work (and is error-prone to
associate these code pieces with the corresponding WHILEs).

If you are prepared to have multiple EXITs, put the exit code directly
beside the condition:

: test-ec1 ( nlow nhigh -- intvl )
2dup > IF swap THEN
BEGIN
over i5 1+ rot within if 2drop 5 exit then
over i4 1+ rot within if 2drop 4 exit then
over i3 1+ rot within if 2drop 3 exit then
over i2 1+ rot within if 2drop 2 exit then
over i1 1+ rot within if 2drop 1 exit then
2dup > if 2drop 0 exit then
swap 1+ swap
again ;

>but the redundant stack cleanup code of 2DROP caused me to switch to
>using ELSE for the branch point of the WHILEs in the loop.

Can be dealt with by wrapping the code.

>Also, one
>exit from the word is generally considered better than multiple exits,
>for analyzing program flow.

I find it particularly cumbersome in debugging: With multiple EXITs, I
have to put a tracer before every EXIT to see what the stack effect
behind the word is. Without EXITs, I only have to put one tracer in
front of the ";".

S Jack

unread,
Jun 23, 2022, 1:04:00 PM6/23/22
to
On Thursday, June 23, 2022 at 7:06:22 AM UTC-5, Krishna Myneni wrote:
> I think the BEGIN WHILE ... WHILE ... structures can become
> comprehensible through use. They are so little used in much of the Forth

A common structure in the arsenal of old Forths. Not difficult to understand
or use if used moderately. Seldom is the need for more than three WHILEs.
Should you need more then you have choice, find another method or
roll up you sleeves and do it. A one-off complication is no big deal;
leave comments.
--
me

minf...@arcor.de

unread,
Jun 23, 2022, 3:51:08 PM6/23/22
to
Krishna Myneni schrieb am Donnerstag, 23. Juni 2022 um 13:53:37 UTC+2:
> On 6/23/22 02:39, minf...@arcor.de wrote:
> > Krishna Myneni schrieb am Donnerstag, 23. Juni 2022 um 04:32:44 UTC+2:
> >> I've long put off introducing extended flow control structures in
> >> kForth, primarily for the reason that I've not needed them before (I
> >> remember one instance of porting Forth code which used two WHILEs within
> >> a BEGIN ... REPEAT structure, but it was easy to work around it.
> >
> > I find readability of control flows very important. Therefore in my code I abstain
> > from Forth's "extended" control flow structures ("poor man's" control flows would
> > describe them better).
> >
> The homily about teaching a poor man to fish comes to mind -- in the
> current context, it's about enabling a poor man to solve linear systems
> of equations (have you seen the licensing costs for matlab?).

There is no "good or bad" flow control syntax. When it works it works. As to
linear systems I am playing nowadays more with Julia than with NumPy. For
in-Forth calculations I use my own numeric matrix wordsets. But of course
sometimes you have longer CASE constructs.

> > Therefore I added four C-like elements to my Forth: BREAK CONTINUE GOTO LABEL
> > and never looked back.
> >
> After looking at the unstructured Fortran 77 code for days, I'd be happy
> if I never saw another GOTO again. Wil Baden argued against the
> knee-jerk reaction to using GOTO, when used in moderation. I don't know ...

"The horrors of GOTO-made spaghetti code" had been semi-religiously evoked
during the times of teaching structured programming. I think we have become
wiser and more flexible today and just don't overuse GOTOs in our own code.

> > Within the same pot: (?)DO loops. Eg UNLOOP is a language dinosaur that any decent
> > compiler should handle automatically. Therefore I use my own FOR..NEXT variants mostly.
> What do you mean handle automatically? What is your substitute for
> "UNLOOP EXIT" ?

There is no substitute for UNLOOP EXIT because you might need it when using standard
Forth code or some legacy code.
Today for counted loops I mostly use a non-standard FOR..NEXT construct that does not put
anything on the return stack. Here UNLOOP would even trigger an exception.

minf...@arcor.de

unread,
Jun 23, 2022, 4:03:23 PM6/23/22
to
the.bee...@gmail.com schrieb am Donnerstag, 23. Juni 2022 um 14:18:42 UTC+2:
> On Thursday, June 23, 2022 at 9:39:15 AM UTC+2, minf...@arcor.de wrote:
> > Therefore I added four C-like elements to my Forth: BREAK CONTINUE GOTO LABEL
> > and never looked back.
> I considered those - but there are numerous problems with these:
> - BREAK and CONTINUE are only useful if combined with "IF". Now a reference to the WHILE
> loop is buried under this one. Bonus points if you embed them in several IF's;
BREAK/CONTINUE must only be used within loops or case constructs. They
compile a jump to the loop tail/head. Try them within IF's: if there is no exception
your complier is flawed.

> - There is a significant difference if the LABEL if defined before or after the first GOTO.
> I suppose don't have to spell them out.
You don't need a two-pass compiler for resolving jumps to LABELs. The algorithm is very
similar to the one in ENDCASE. It is executed during compile-time of ; (SEMIS) .

> > Within the same pot: (?)DO loops. Eg UNLOOP is a language dinosaur that any decent
> > compiler should handle automatically. Therefore I use my own FOR..NEXT variants mostly.
> I wonder how:
>
> : TEST 10 0 DO 10 0 DO j i * 25 = IF EXIT THEN LOOP LOOP ;
>
> Well, clean this one up without UNLOOP..

Under standard Forth this is legal code. ;-)

Gerry Jackson

unread,
Jun 23, 2022, 4:06:57 PM6/23/22
to
On 23/06/2022 03:32, Krishna Myneni wrote:
> The topic comes up again for me when converting old unstructured Fortran
> 77 code into structured Forth. In the Fortran 77 code, line numbers and
> GO TOs are used to create IF ... ELSE ... THEN and BEGIN ... WHILE ...
> REPEAT structures, and also the extended versions containing multiple
> WHILEs within a BEGIN ... REPEAT structure. It's often not obvious which
> extended flow control pattern is being created with GO TOs, but Forth
> patterns such as the one below do show up in the old code:
>
> BEGIN ... WHILE ... WHILE ... WHILE ... REPEAT ... ELSE ... THEN ...
> ELSE ... THEN
>
> It's not immediately obvious when one has not used such a structure what
> the branching is, so I wrote a non-trivial case to understand the flow
> better. The problem is to loop over a range of integers and test each
> integer to see if it falls within one of several intervals. If so, we
> branch out of the loop and return the appropriate interval number. While
> this problem can be trivially handled by examining the overlap of the
> loop range with the intervals, it is nevertheless illustrative to do --
> a less trivial problem would be to loop over an array of unsorted
> integers and break out of the loop when one integer falls into an interval.
>
> The code is given below and has been tested under Gforth. It does what I
> expect based on the flow control diagram in Fig. A.3 of the Forth 2012
> standard. Note which WHILE maps to which ELSE in this example. What is
> your experience with extended flow control structures? They do appear to
> be useful when converting unstructured code to structured code.

I use such a loop with 2 WHILEs fairly often and only remember using 3
on one occasion, mostly without an ELSE but if I do use ELSE I have to
think very carefully about what goes where.

It's worth pointing out that Michael Gassanenko's CHOOSE statement is
suited to selection of integer ranges as long as the ranges don't have
large gaps between them. See
https://groups.google.com/g/comp.lang.forth/c/HwxAddZ54mg/m/6RLvtl7uBQAJ

--
Gerry

dxforth

unread,
Jun 23, 2022, 7:01:57 PM6/23/22
to
On 23/06/2022 12:32, Krishna Myneni wrote:
>
> --- examples ---
> 13 17 test-ec1 . 3 ok
> 17 23 test-ec1 . 0 ok
> 17 24 test-ec1 . 4 ok
> 16 24 test-ec1 . 3 ok
> 44 88 test-ec1 . 0 ok
> --- end of examples ---


-10 -6 2constant i1
-3 1 2constant i2
3 16 2constant i3
24 29 2constant i4
114 127 2constant i5

: <= > not ;

: not-in ( lo hi range result -- lo hi ; result )
>r 1+ 3 pick within if 2drop r> unnest exit then
r> drop ;

: test-ec1
2dup > IF swap THEN
begin
i1 1 not-in
i2 2 not-in
i3 3 not-in
i4 4 not-in
i5 5 not-in
2dup <=
while
swap 1+ swap
repeat 2drop 0 ;

13 17 test-ec1 .
17 23 test-ec1 .
17 24 test-ec1 .
16 24 test-ec1 .
44 88 test-ec1 .

include \forth\dx\test-ecl 3 0 4 3 0 ok

Krishna Myneni

unread,
Jun 23, 2022, 8:22:36 PM6/23/22
to
While this code looks clever, it would not be transparent to a
programmer familiar with standard (Forth-94 or Forth-2012) programmer.
First, looking at your definition of TEST-EC1 in isolation, it is not
possible to follow the program flow. It would look as though there were
a cascade of tests within the BEGIN ... WHILE section.

Next, when NOT-IN is examined, one sees a non-standard UNNEST. What does
this word do? Does it unwind the return stack one level (assuming that
nothing was pushed to the return stack by the calling word), or does it
empty the return stack altogether? If it's the former, then it seems
like it would cause a crash if the falling word pushed anything onto the
return stack temporarily. If it's the latter, then TEST-EC1 would not be
usable as a factor for defining other words.

--
Krishna



dxforth

unread,
Jun 23, 2022, 8:34:01 PM6/23/22
to
On 24/06/2022 10:22, Krishna Myneni wrote:
>
> Next, when NOT-IN is examined, one sees a non-standard UNNEST. What does
> this word do? Does it unwind the return stack one level

That's it. Avoids having to deal with flags and such as Moore explained
in Thinking FORTH.


Krishna Myneni

unread,
Jun 23, 2022, 8:50:18 PM6/23/22
to
I don't know. After having gone through the example, I feel fairly
confident that if I saw a BEGIN ... REPEAT structure which included
multiple WHILEs, I can tell whether or not the flow control structure is
properly closed and also precisely where the branch points are for each
WHILE statement.

> If you are prepared to have multiple EXITs, put the exit code directly
> beside the condition:
>
> : test-ec1 ( nlow nhigh -- intvl )
> 2dup > IF swap THEN
> BEGIN
> over i5 1+ rot within if 2drop 5 exit then
> over i4 1+ rot within if 2drop 4 exit then
> over i3 1+ rot within if 2drop 3 exit then
> over i2 1+ rot within if 2drop 2 exit then
> over i1 1+ rot within if 2drop 1 exit then
> 2dup > if 2drop 0 exit then
> swap 1+ swap
> again ;
>
>> but the redundant stack cleanup code of 2DROP caused me to switch to
>> using ELSE for the branch point of the WHILEs in the loop.
>
> Can be dealt with by wrapping the code.
>
>> Also, one
>> exit from the word is generally considered better than multiple exits,
>> for analyzing program flow.
>
> I find it particularly cumbersome in debugging: With multiple EXITs, I
> have to put a tracer before every EXIT to see what the stack effect
> behind the word is. Without EXITs, I only have to put one tracer in
> front of the ";".
>

Yep, which is why I want to minimize EXITs within a word, with the ideal
being none at all.

--
Krishna




David Schultz

unread,
Jun 23, 2022, 9:18:04 PM6/23/22
to
On 6/22/22 21:32, Krishna Myneni wrote:
> I've long put off introducing extended flow control structures in
> kForth, primarily for the reason that I've not needed them before (I
> remember one instance of porting Forth code which used two WHILEs within
> a BEGIN ... REPEAT structure, but it was easy to work around it.
> Extended flow control structures are of the type shown in Fig. A.3 in
> the Forth-2012 standard:
>

Recently I have been working on a fix for the ENCLOSE bug in 1802
FIG-Forth. My first step was to replicate the code in Forth assembly so
I could then modify it. The problem was that the structure did not fit
the usual structured flow control. Mostly two exits from a loop going to
different places.

My solution was to abuse the conditionals. Adding an IF within the BEGIN
loop and immediately pushing its information to the return stack.
Followed with similar abuse after the loop exit to get a backwards
branch. It worked as a one off but I wouldn't want to make a habit of it.


--
http://davesrocketworks.com
David Schultz

Krishna Myneni

unread,
Jun 23, 2022, 9:20:38 PM6/23/22
to
On 6/23/22 15:06, Gerry Jackson wrote:
> On 23/06/2022 03:32, Krishna Myneni wrote:
...
>>
>> BEGIN ... WHILE ... WHILE ... WHILE ... REPEAT ... ELSE ... THEN ...
>> ELSE ... THEN
>>
...
>> What is your experience with extended flow control structures? They do
>> appear to be useful when converting unstructured code to structured code.
>
> I use such a loop with 2 WHILEs fairly often and only remember using 3
> on one occasion, mostly without an ELSE but if I do use ELSE I have to
> think very carefully about what goes where.
>

Each WHILE, not including the final WHILE which precedes the REPEAT, has
either an ELSE ... THEN counterpart, or just a THEN to close the branch.
It's use of THEN only which creates issues. I think the following should
always be safe to do,

BEGIN
...
WHILE \ W1
...
WHILE \ W2
...
WHILE \ W3
...
WHILE \ precedes REPEAT
...
REPEAT
ELSE ... THEN \ closes W3
ELSE ... THEN \ closes W2
ELSE ... THEN \ closes W1

\ flow from each WHILE branch resumes here ... carry on

Even if there is no action which needs to be taken on a particular ELSE
, using ELSE ... THEN guarantees that the WHILE branch execution on
false will resume at an easily predictable place in the code, namely
after all closures of the WHILE branches. The only drawback is not being
able to cascade execution of code for the different WHILE branches if we
always use ELSE ... THEN, but that's a small price to pay for
comprehensibility. Proper indentation is a must when the code between
ELSE ... THEN contains flow control structures.

--
Krishna

Krishna Myneni

unread,
Jun 24, 2022, 8:11:46 AM6/24/22
to
On 6/23/22 20:20, Krishna Myneni wrote:

> I think the following should
> always be safe to do,
>
> BEGIN
>  ...
> WHILE  \ W1
>  ...
> WHILE  \ W2
>  ...
> WHILE  \ W3
>  ...
> WHILE  \ precedes REPEAT
>  ...
> REPEAT
> ELSE ... THEN  \ closes W3
> ELSE ... THEN  \ closes W2
> ELSE ... THEN  \ closes W1
>
> \ flow from each WHILE branch resumes here ... carry on
>
> Even if there is no action which needs to be taken on a particular ELSE
> , using ELSE ... THEN guarantees that the WHILE branch execution on
> false will resume at an easily predictable place in the code, namely
> after all closures of the WHILE branches. ...

The above example needs one revision, namely the "fall through" case,
when the branch point follows REPEAT (the usual case for a single
WHILE). For n WHILE uses, we have

BEGIN
...
cond_1
WHILE \ W_1
...
cond_2
WHILE \ W_2
...
cond_n-1 \ W_n-1
WHILE
...
cond_n \ W_n
WHILE
...
REPEAT
... \ special handling for fall through (cond_n = FALSE)
ELSE ... THEN \ special handling for W_n-1 (cond_n-1 = FALSE)
ELSE ... THEN \ special handling for W_n-2 (cond_n-2 = FALSE)
...
ELSE ... THEN \ special handling for W_1 (cond_1 = FALSE)
\ flow from all WHILE branches resumes here

I submit that the above flow control structure is perfectly
comprehensible and predictable when ELSE ... THEN is consistently used
for W1 -- W_n-1, even when no special handling is required for branching
out of the loop from these WHILEs.

The other perfectly safe way to use multiple WHILEs in an extended flow
control structure is when no special case handling is necessary for
branching out of the indefinite loop for any of the WHILEs branches, as
has been stated by Stephen Pelc and others:

BEGIN
...
cond_1
WHILE \ W_1
...
cond_2
WHILE \ W_2
...
cond_n-1
WHILE \ W_n-1
...
cond_n
WHILE \ W_n
...
REPEAT
\ branch for W_n (cond_n = FALSE)
THEN \ branch for W_n-1 (cond_n-1 = FALSE)
THEN \ branch for W_n-2 (cond n-2 = FALSE)
...
THEN \ branch for W_1 (cond_1 = FALSE)

\ flow from all WHILE branches resumes here

Note that for n uses of WHILE there are n-1 THENs following REPEAT. The
sole purpose of each use of WHILE is simply to permit conditional
branching out of the indefinite loop, with no additional handling required.

A safe and comprehensible minor variant of the above flow control
structure would be to include special handling code *only* for the fall
through case,

...
REPEAT
\ branch for W_n (cond_n = FALSE)
... \ special handling only for cond_n = FALSE
THEN \ branch for W_n-1 (cond_n-1 = FALSE)
THEN \ branch for W_n-2 (cond n-2 = FALSE)
...
THEN \ branch for W_1 (cond_1 = FALSE)

\ flow from all WHILE branches resumes here

While others have proposed flow control alternatives to using multiple
WHILEs within an indefinite loop, the use of multiple WHILEs has the
advantage that it is already supported by the Forth-94 and Forth-2012
standards. I believe their use can be made comprehensible and safe if we
follow practices such as the ones shown above.

--
Krishna

Hans Bezemer

unread,
Jun 24, 2022, 8:14:10 AM6/24/22
to
On Thursday, June 23, 2022 at 10:03:23 PM UTC+2, minf...@arcor.de wrote:
> > : TEST 10 0 DO 10 0 DO j i * 25 = IF EXIT THEN LOOP LOOP ;
> >
> > Well, clean this one up without UNLOOP..
> Under standard Forth this is legal code. ;-)

Given that Forth doesn't have a true syntax - sure. Still, it barfs - and rightly so:
: TEST 10 0 DO 10 0 DO j i * 25 = IF EXIT THEN LOOP LOOP ; ok
test
*the terminal*:2:1: error: Invalid memory address
>>>test<<<
Backtrace:
*terminal*:1:38: 0 $7FE11B98E048 ;s
1 $A
2 $5
3 $A

Hans Bezemer

minf...@arcor.de

unread,
Jun 24, 2022, 8:34:31 AM6/24/22
to
the.bee...@gmail.com schrieb am Freitag, 24. Juni 2022 um 14:14:10 UTC+2:
> On Thursday, June 23, 2022 at 10:03:23 PM UTC+2, minf...@arcor.de wrote:
> > > : TEST 10 0 DO 10 0 DO j i * 25 = IF EXIT THEN LOOP LOOP ;
> > >
> > > Well, clean this one up without UNLOOP..
> > Under standard Forth this is legal code. ;-)
> Given that Forth doesn't have a true syntax - sure. Still, it barfs - and rightly so:
> : TEST 10 0 DO 10 0 DO j i * 25 = IF EXIT THEN LOOP LOOP ; ok
> test
> *the terminal*:2:1: error: Invalid memory address

Since you are insisting
: TEST 10 FOR 10 FOR ^N N * 25 = IF EXIT THEN NEXT NEXT ; ok
test ok
;-)

Hans Bezemer

unread,
Jun 24, 2022, 8:46:48 AM6/24/22
to
On Thursday, June 23, 2022 at 4:32:44 AM UTC+2, Krishna Myneni wrote:
> --- start of code ---
> \ Loop over a range of integers, from nlow to nhigh,
> \ and stop whenever the current integer falls within
> \ one of the specified intervals. Return the interval
> \ number; 0 if no interval encountered.
Well, you can always overcomplicate things:

---8<---
: le over min ;
: ge over max ;
: lt 1- le ;
: gt 1- ge ;

: range
case
-10 lt of 0 endof
-6 le of 1 endof

-3 lt of 0 endof
1 le of 2 endof

3 lt of 0 endof
16 le of 3 endof

24 lt of 0 endof
29 le of 4 endof

114 lt of 0 endof
127 le of 5 endof
0
endcase
;

: test-ec1
2dup < if swap then range dup if nip else drop range then
;
---8<---

> --- examples ---
> 13 17 test-ec1 . 3 ok
> 17 23 test-ec1 . 0 ok
> 17 24 test-ec1 . 4 ok
> 16 24 test-ec1 . 3 ok
> 44 88 test-ec1 . 0 ok
> --- end of examples ---
QED:

13 17 test-ec1 . 3 ok
17 23 test-ec1 . 0 ok
17 24 test-ec1 . 4 ok
16 24 test-ec1 . 3 ok
44 88 test-ec1 . 0 ok

Hans Bezemer

Hans Bezemer

unread,
Jun 24, 2022, 8:48:00 AM6/24/22
to
On Friday, June 24, 2022 at 2:34:31 PM UTC+2, minf...@arcor.de wrote:
> Since you are insisting
> : TEST 10 FOR 10 FOR ^N N * 25 = IF EXIT THEN NEXT NEXT ; ok
> test ok
> ;-)

Error: ANS94 violation detected: code does not compile.

Hans Bezemer

Hans Bezemer

unread,
Jun 24, 2022, 9:34:57 AM6/24/22
to
On Friday, June 24, 2022 at 2:50:18 AM UTC+2, Krishna Myneni wrote:
> Yep, which is why I want to minimize EXITs within a word, with the ideal
> being none at all.
I embraced the motto "a function may have only ONE entry point and ONE
exit point" for a LONG time. Until I realized it's pure nonsense when you're writing
Forth. If you're writing Forth properly, you got very compact functions.

If you pursue this motto, you'll be confronted with very muddy logic in order to
achieve that - after all, the flow logic must end up JUST THERE.

However, if your function ("word") is compact it doesn't disturb the logic, it makes
it much clearer:

: get_singlechar ( a -- v)
c@ dup is-alpha if >index variables + ;then
[char] @ = if 0 ['] (array) exec_function ;then
E.NOTVAR throw
;

This word checks whether a variable is alphabetic. If so, it calculates its address
and it's done. No ELSE required..

If not, It checks whether it is an "AT" sign. If so, it calculates its address
and it's done. No ELSE required..

Otherwise throw an error. It's that easy. It doesn't require a "Christmas tree" of code
and superfluous ELSEs. It's even promoted now in more "classical" languages like C.

Hans Bezemer

Krishna Myneni

unread,
Jun 24, 2022, 11:23:03 AM6/24/22
to
On 6/24/22 08:34, Hans Bezemer wrote:
> On Friday, June 24, 2022 at 2:50:18 AM UTC+2, Krishna Myneni wrote:
>> Yep, which is why I want to minimize EXITs within a word, with the ideal
>> being none at all.
> I embraced the motto "a function may have only ONE entry point and ONE
> exit point" for a LONG time. Until I realized it's pure nonsense when you're writing
> Forth. If you're writing Forth properly, you got very compact functions.
>

I use EXIT when it makes sense to do so, for example an check on input
parameters at the beginning of the word reveals that no computation need
be done and we can simply return,

: foo ( a b c -- )
dup 0= IF 2drop EXIT THEN \ for c=0 nothing is required
...
;

For short one or two line definitions it may or may not make sense.

> If you pursue this motto, you'll be confronted with very muddy logic in order to
> achieve that - after all, the flow logic must end up JUST THERE.
>
> However, if your function ("word") is compact it doesn't disturb the logic, it makes
> it much clearer:
>
> : get_singlechar ( a -- v)
> c@ dup is-alpha if >index variables + ;then
> [char] @ = if 0 ['] (array) exec_function ;then
> E.NOTVAR throw
> ;

I assume ;THEN just compiles EXIT THEN into the code. I'm wary of the
use of off-the-cuff shorthand flow control words, particularly when they
are trivial. They present an obstacle to people who are not experienced
in Forth and have to figure out what they mean. That's why I avoid them
in my own code.

So, let's write the above using ELSE

: get_singlechar ( a -- v )
c@
dup is-alpha IF
>index variables +
ELSE
[char] @ = IF
0 ['] (array) exec_function
ELSE
E.NOTVAR THROW
THEN
THEN
;

(If you're using Google Groups to view this message, all of the
indentation in the above code will be destroyed and it won't illustrate
my point very well). My opinion is that in your example the "classical"
style is easier to follow for a less experienced Forth programmer,
easier to debug, and easier to maintain than the original.

Not all words which perform a substantial computation can be expressed
in two or three lines of code. Then, it's important to be able to follow
the flow of the code. Furthermore, having multiple EXITs (even if they
are hidden behind custom flow control words) means that one has to
ensure that the data stack (and the fp stack) have to be placed in the
proper states indicated by the stack comment. Also, anything pushed onto
the return stack temporarily by code preceding the EXIT has to be
popped. This needs to be done for every EXIT or the code will break. In
my FOO example, it was easy to ensure that the stack contents on EXIT
would meet these requirements.

>
> This word checks whether a variable is alphabetic. If so, it calculates its address
> and it's done. No ELSE required..
>
> If not, It checks whether it is an "AT" sign. If so, it calculates its address
> and it's done. No ELSE required..
>
> Otherwise throw an error. It's that easy. It doesn't require a "Christmas tree" of code
> and superfluous ELSEs. It's even promoted now in more "classical" languages like C.
>

--
Krishna

Hans Bezemer

unread,
Jun 24, 2022, 6:51:12 PM6/24/22
to
On Friday, June 24, 2022 at 5:23:03 PM UTC+2, Krishna Myneni wrote:
> I assume ;THEN just compiles EXIT THEN into the code. I'm wary of the
> use of off-the-cuff shorthand flow control words, particularly when they
> are trivial. They present an obstacle to people who are not experienced
> in Forth and have to figure out what they mean. That's why I avoid them
> in my own code.
It helps to do tail call optimization in 4tH. And I got sick of writing EXIT THEN -
it looks terrible IMHO. BTW, I also got an ;ENDOF for that reason.

> My opinion is that in your example the "classical"
> style is easier to follow for a less experienced Forth programmer,
> easier to debug, and easier to maintain than the original.
Like I said: even other programming languages are now embracing multiple
EXITs instead of the embedded checkpoint method. Simply because drilling
down a tree is not quite so easy to follow either and it makes horrible code:

test a IF
__test b IF
____test c IF
______test d IF
________." You made it!" CR
______ELSE
________." You failed at d" CR
______THEN
____ELSE
______." You failed at c" CR
____THEN
__ELSE
____." You failed at b" CR
__THEN
ELSE
__." You failed at a"
THEN

Instead of:

test a 0= IF ." You failed at a" CR ;THEN
test b 0= IF ." You failed at b" CR ;THEN
test c 0= IF ." You failed at c" CR ;THEN
test d 0= IF ." You failed at d" CR ;THEN
." You made it!" CR

You'll be the judge of what is easier to understand or maintain..

> Not all words which perform a substantial computation can be expressed
> in two or three lines of code. Then, it's important to be able to follow
> the flow of the code. Furthermore, having multiple EXITs (even if they
> are hidden behind custom flow control words) means that one has to
> ensure that the data stack (and the fp stack) have to be placed in the
> proper states indicated by the stack comment. Also, anything pushed onto
> the return stack temporarily by code preceding the EXIT has to be
> popped. This needs to be done for every EXIT or the code will break.
Yes - and cows drink water. You ALWAYS have to ensure an identical stack
diagram at an exit point - at least I do. That's why I never use ?DUP.

BTW, I'm not out here to fight out "who is right". I'm just curious to hear the
arguments behind people who have an opposing style, to answer the question:
"What am I missing here"?

So I thank you for the trouble you went through going into this.

Hans Bezemer

Paul Rubin

unread,
Jun 24, 2022, 7:10:10 PM6/24/22
to
Hans Bezemer <the.bee...@gmail.com> writes:
> BTW, I'm not out here to fight out "who is right". I'm just curious to
> hear the arguments behind people who have an opposing style, to answer
> the question: "What am I missing here"?

A more realistic example of the nested style is when you actually want
to do some computations between the tests, but then end up returning an
error code if any of the tests (including later ones) fail. In Forth or
Python this is probably more readably implemented with catch/throw.
Haskell uses the Error and Maybe monads. In C it is just messy. C++
has a proposal for deterministic exceptions that I think are similar to
Haskell's Error monad, but I don't know what the proposal's current
status is. I wonder whether Rust has anything for this purpose.

dxforth

unread,
Jun 24, 2022, 10:22:34 PM6/24/22
to
Forth programmers would appreciate seeing the tricks you used to achieve
this magic. It wouldn't come cheap.

dxforth

unread,
Jun 24, 2022, 11:07:06 PM6/24/22
to
On 25/06/2022 08:51, Hans Bezemer wrote:
> On Friday, June 24, 2022 at 5:23:03 PM UTC+2, Krishna Myneni wrote:
>> I assume ;THEN just compiles EXIT THEN into the code. I'm wary of the
>> use of off-the-cuff shorthand flow control words, particularly when they
>> are trivial. They present an obstacle to people who are not experienced
>> in Forth and have to figure out what they mean. That's why I avoid them
>> in my own code.

> It helps to do tail call optimization in 4tH.

Of importance to AVR Flashforth. Compiling RET to replace ELSE improved
speed but not code so I changed it to call optimize. I now worry what I
may have broken. So far so good.

dxforth

unread,
Jun 25, 2022, 12:10:20 AM6/25/22
to
'realistic' would be going along with the mainstream. OTOH

"My goal is to develop tools that augment my abilities. If others can use
them, fine. It would be foolish to lose an opportunity to explore or excel
just to conform to some equalitarian philosophy. Too often our culture
seeks the lowest common denominator." - C.M.

minf...@arcor.de

unread,
Jun 25, 2022, 3:18:02 AM6/25/22
to
C is now half a century old and it shows. Still its little assert(..) function/macro
can be quite handy. It is more than what standard Forth offers.

none albert

unread,
Jun 25, 2022, 4:45:22 AM6/25/22
to
In article <30bcb8e4-0e3f-45ef...@googlegroups.com>,
It is a very long time ago that Marcel Hendrix explained to me
that you have to put an UNLOOP before the EXIT.
I found it unexpected.

>Hans Bezemer

Groetjes Albert
--
"in our communism country Viet Nam, people are forced to be
alive and in the western country like US, people are free to
die from Covid 19 lol" duc ha
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Hans Bezemer

unread,
Jun 25, 2022, 8:40:18 AM6/25/22
to
On Saturday, June 25, 2022 at 9:18:02 AM UTC+2, minf...@arcor.de wrote:
> C is now half a century old and it shows. Still its little assert(..) function/macro
> can be quite handy. It is more than what standard Forth offers.
4tH has got it. GForth has got it - but I never used it. Never used it in C as well.
To me, it's more like debugging code - and in production code it looks REALLY
UGLY IMHO.

The main reason is that all you get is "Assertion failed" with some technical stuff
trailing behind. I can't see how it benefits an ordinary user.

But again: my feelings about it..

Hans Bezemer

Krishna Myneni

unread,
Jun 25, 2022, 10:30:55 AM6/25/22
to
On 6/22/22 21:32, Krishna Myneni wrote:
> I've long put off introducing extended flow control structures in
> kForth, primarily for the reason that I've not needed them before ...
> Extended flow control structures are of the type shown in Fig. A.3 in
> the Forth-2012 standard:
>
> BEGIN ... WHILE ... WHILE ... REPEAT ... THEN
>
> BEGIN ... WHILE ... UNTIL ... ELSE ... THEN
>
...

kForth-64 now supports the extended flow control structures (master
branch, currently at v 0.4.0 on github). One or more WHILE statements
may be used within the following flow control structures:

BEGIN ... WHILE ... REPEAT
BEGIN ... UNTIL
BEGIN ... AGAIN

The extended structures conform to the documented behavior in the
Forth-94 and Forth-2012 standards. It turned out to be fairly simple to
extend the basic versions of these flow control structures. Porting
unstructured Fortran code to structured Forth forced me to do something
I had intended to do for a long time. The extensions will appear in
kForth-32 and kForth-Win32 after a period of testing.

--
Krishna Myneni


S Jack

unread,
Jun 25, 2022, 11:14:19 AM6/25/22
to
On Saturday, June 25, 2022 at 7:40:18 AM UTC-5, the.bee...@gmail.com wrote:

> 4tH has got it. GForth has got it - but I never used it. Never used it in C as well.
> To me, it's more like debugging code - and in production code it looks REALLY
> UGLY IMHO.

I use assortment of asserts in my testings programs that print in
pretty colors. Think my testing would be better if I bothered to implement
the Hays type testing. I have the luxury to get by with field and smoke
tests. Amazing how years can go by before hitting some obvious, once
seen, problems. Always better to let someone else write the tests; they'll
find them quick.
--
me

S Jack

unread,
Jun 25, 2022, 12:29:10 PM6/25/22
to
On Saturday, June 25, 2022 at 9:30:55 AM UTC-5, Krishna Myneni wrote:
> I had intended to do for a long time. The extensions will appear in
> kForth-32 and kForth-Win32 after a period of testing.

Good, nice to see another box checked in the do-list.
Last night I did one myself, a cheap history. I didn't want C library
in my Forth. I tried to COPROC with the system to let it do the
command input and pass it to the Forth but that didn't work well so I
just built a simple history in the Forth using one block screen of
virtual memory. I limit my command line to 128 bytes so I can keep
8 commands in the block buffer; don't write to the screen but can
LIST them using null terminated strings that don't bother LIST display
and my terminal input is still old Fig QUERY with EXPECT which
provides null terminated strings so it all fits nicely. It turned out
better than I thought it would; I can use it as. It provides the desired
function and still is simple. Can make some improvement but have no need
for additional features that would add complexity.

:) frogd
ELF32X86_64 Frog Version 1.0d

"f/history.f" fload
--- XQUERY installed ok ok
.( hello world )
hello world ok
.( hello muther )
hello muther ok
.( hello pappa )
hello pappa ok
.( hello sam )
hello sam ok
.( hello dean )
hello dean ok
10: .( hello dean )
08: .( hello sam )
06: .( hello pappa )
04: .( hello muther )
hello muther ok
history

SCR # 490
0 "f/history.f" fload
1
2 .( hello world )
3
4 .( hello muther )
5
6 .( hello pappa )
7
8 .( hello sam )
9
10 .( hello dean )
11
12 .( hello muther )
13
14
15
ok
bye

Not seen are the key strokes, UPARROW to enter the history , "k" to step down the
list, CR to select and execute an entry or "q" to abandon the history.
--
me

Anton Ertl

unread,
Jun 25, 2022, 12:35:08 PM6/25/22
to
Hans Bezemer <the.bee...@gmail.com> writes:
>4tH has got it. GForth has got it - but I never used it. Never used it in C as well.

I use assertions regularly, basically whenever there is a property
that I rely on in the following code and that I think follows from the
preceding code, but is not so obvious, and/or where I fear that it
might be destroyed in maintenance.

>To me, it's more like debugging code - and in production code it looks REALLY
>UGLY IMHO.
>
>The main reason is that all you get is "Assertion failed" with some technical stuff
>trailing behind. I can't see how it benefits an ordinary user.

Normally the non-programming user should not be seeing a failed
assertion. But if he sees it, the direct benefit is that the
application stops rather than producing a bogus result. If you (the
programmer does not like that, turn off the assertion checking for
production.

The indirect benefit is more relevant, though: The assertion documents
things the programmer is relying on, which avoids blind alleys in
maintenance. It also checks these assumptions (unless the checking is
turned off), so ideally the assertions will catch any cases during
testing where these things do not (or do no longer) hold. If the
non-programmer user ever sees them, the testing has not been thorough
enough.

- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: https://forth-standard.org/
EuroForth 2022: http://www.euroforth.org/ef22/cfp.html

minf...@arcor.de

unread,
Jun 25, 2022, 3:39:12 PM6/25/22
to
the.bee...@gmail.com schrieb am Samstag, 25. Juni 2022 um 14:40:18 UTC+2:
> On Saturday, June 25, 2022 at 9:18:02 AM UTC+2, minf...@arcor.de wrote:
> > C is now half a century old and it shows. Still its little assert(..) function/macro
> > can be quite handy. It is more than what standard Forth offers.
> 4tH has got it. GForth has got it - but I never used it. Never used it in C as well.
> To me, it's more like debugging code - and in production code it looks REALLY
> UGLY IMHO.

Sure, it is no rocket science, so everybody rolls his own assertion functions and
divergeant syntax.

When your own assertions hurt your aesthetic feelings, make them prettier.
Colorizing might be an idea perhaps.

none albert

unread,
Jun 25, 2022, 3:40:11 PM6/25/22
to
In article <16416e97-6b62-408b...@googlegroups.com>,
minf...@arcor.de <minf...@arcor.de> wrote:
<SNIP>
>
>C is now half a century old and it shows. Still its little assert(..) function/macro
>can be quite handy. It is more than what standard Forth offers.

How for Pete's sake is assert() any different than ABORT" ?

minf...@arcor.de

unread,
Jun 25, 2022, 6:21:46 PM6/25/22
to
none albert schrieb am Samstag, 25. Juni 2022 um 21:40:11 UTC+2:
> In article <16416e97-6b62-408b...@googlegroups.com>,
> minf...@arcor.de <minf...@arcor.de> wrote:
> <SNIP>
> >
> >C is now half a century old and it shows. Still its little assert(..) function/macro
> >can be quite handy. It is more than what standard Forth offers.
> How for Pete's sake is assert() any different than ABORT" ?

It is true that you could build a Forth clone of assert() with
ABORT" and non-standard equivalents of NDEBUG _LINE_ and _FILE_.
No rocket science.

dxforth

unread,
Jun 26, 2022, 1:16:15 AM6/26/22
to
'Extended structures' implies something additional to the 'normal' ones.
While it is 'extended' relative to what FIG or 83 could do, I don't view
them as additional. Neither did ANS call them "Extended structures".
The story how they came to be is, however, is interesting. Originally
WHILE and REPEAT were implemented as:

\ Fig, 78, 83
: WHILE IF ;
: REPEAT cs-swap AGAIN THEN ;

In the early 80's someone at Forth Inc (Dean Sanderson?) noticed that
if it were re-arranged to:

\ ForthInc, ANS
: WHILE IF cs-swap ;
: REPEAT AGAIN THEN ;

suddenly WHILE became more useful and one could more stuff AKA tricks
- if it were ever needed.

Like most tricks, over-use results in a horrible mess - which I believe
has been demonstrated.

Marcel Hendrix

unread,
Jun 26, 2022, 3:08:17 AM6/26/22
to
On Friday, June 24, 2022 at 2:46:48 PM UTC+2, the.bee...@gmail.com wrote:
> On Thursday, June 23, 2022 at 4:32:44 AM UTC+2, Krishna Myneni wrote:
> > --- start of code ---
> > \ Loop over a range of integers, from nlow to nhigh,
> > \ and stop whenever the current integer falls within
> > \ one of the specified intervals. Return the interval
> > \ number; 0 if no interval encountered.
> Well, you can always overcomplicate things:
>
[..]
> : test-ec1
> 2dup < if swap then range dup if nip else drop range then
> ;
> ---8<---
> > --- examples ---
> > 13 17 test-ec1 . 3 ok
> > 17 23 test-ec1 . 0 ok
> > 17 24 test-ec1 . 4 ok
> > 16 24 test-ec1 . 3 ok
> > 44 88 test-ec1 . 0 ok
> > --- end of examples ---
> QED:
> 13 17 test-ec1 . 3 ok
> 17 23 test-ec1 . 0 ok
> 17 24 test-ec1 . 4 ok
> 16 24 test-ec1 . 3 ok
> 44 88 test-ec1 . 0 ok
> Hans Bezemer

Have stack diagrams become unfashionable?

And what about a small explanation about the why
instead of a description of the how? Without that
it is not possible to come up with a better implementation.
It does not seem to make sense to return the interval index
if there can be more than one. What happens when the
intervals overlap, or when the input range overlaps more
than a single interval?

FORTH> 13 25 test-ec1 . 3 ok

-marcel

Hans Bezemer

unread,
Jun 26, 2022, 8:30:41 AM6/26/22
to
On Sunday, June 26, 2022 at 9:08:17 AM UTC+2, Marcel Hendrix wrote:
> > : test-ec1
> > 2dup < if swap then range dup if nip else drop range then
> > ;
> Have stack diagrams become unfashionable?
No -- but in the original code only one was given - TEST-EC1 - so I
assumed that would be clear.

> And what about a small explanation about the why
> instead of a description of the how?
I gave it - it's simpler than the original code. The logic is maintained
in one single place. Applying ;ENDOF or inlining LE, LT could even
speed it up a bit more, but I bet it's already faster. But that's more
your department ;-)

> It does not seem to make sense to return the interval index
> if there can be more than one. What happens when the
> intervals overlap, or when the input range overlaps more
> than a single interval?
That behavior is IMHO given in this test:

16 24 test-ec1 . 3 ok

\ interval
\ 1 [-10, -6]
\ 2 [ -3, 1]
\ 3 [ 3, 16]
\ 4 [ 24, 29]
\ 5 [114,127]

It overlaps two categories in the original code (AFAIK). It returns
the lower bounds. So did I.

If not - it would be easy to fix. You push the upper limit through
the "range routine". Depending on how you want to interpret a "limit"
it would either return "0", the same or a higher interval. You seem to consider
a higher interval a big "nono".

"0" means: it fell in limbo AFTER the limit (want to include that one?)
or the same and then we agree in all cases we have a hit.

Embedding the required behavior in TEST-EC1 would be trivial.

Hans Bezemer

Krishna Myneni

unread,
Jun 26, 2022, 1:26:15 PM6/26/22
to
On 6/26/22 02:08, Marcel Hendrix wrote:
...
> And what about a small explanation about the why
> instead of a description of the how? Without that
> it is not possible to come up with a better implementation.
> It does not seem to make sense to return the interval index
> if there can be more than one. What happens when the
> intervals overlap, or when the input range overlaps more
> than a single interval?
>
...

This example was devised to test the BEGIN ... WHILE ... WHILE ...
REPEAT ... ELSE ... THEN ... THEN flow control structure. It's not
intended to be an algorithm for an application. However, it's not hard
to envision some sort of application. For example, one can imagine a
sensor input which is repeatedly sampled (and used in some feedback
loop), with non-overlapping intervals of sample values corresponding to
different emergency actions needed. For the example given, a loop range
input allows for testing to see that the flow control structure has the
correct behavior.

--
Krishna

Krishna Myneni

unread,
Jun 26, 2022, 1:34:39 PM6/26/22
to
On 6/25/22 11:29, S Jack wrote:
> On Saturday, June 25, 2022 at 9:30:55 AM UTC-5, Krishna Myneni wrote:
>> I had intended to do for a long time. The extensions will appear in
>> kForth-32 and kForth-Win32 after a period of testing.
>
> Good, nice to see another box checked in the do-list.
> Last night I did one myself, a cheap history. I didn't want C library
> in my Forth. I tried to COPROC with the system to let it do the
> command input and pass it to the Forth but that didn't work well so I
> just built a simple history in the Forth using one block screen of
> virtual memory. I limit my command line to 128 bytes so I can keep
> 8 commands in the block buffer ...

I think it's very hard to get away from the C library for a modern, full
functional desktop Forth system. However, I sympathize with the idea. I
would like to see more Forth source libraries which may be used
directly, rather than compiled C libraries accessible through an
external library interface. Examples include Forth libraries which
provide the ability to retrieve image and metadata from different image
file types, write .wav and .mp3 audio files, write .mpg4 video files,
i/o through usb interface, etc.

--
Krishna


Krishna Myneni

unread,
Jun 26, 2022, 1:40:39 PM6/26/22
to
On 6/26/22 00:16, dxforth wrote:
> On 26/06/2022 00:30, Krishna Myneni wrote:
...
>> kForth-64 now supports the extended flow control structures (master
>> branch, currently at v 0.4.0 on github). One or more WHILE statements
>> may be used within the following flow control structures:
>>
>> BEGIN ... WHILE ... REPEAT
>> BEGIN ... UNTIL
>> BEGIN ... AGAIN
>>
>> The extended structures conform to the documented behavior in the
>> Forth-94 and Forth-2012 standards. ...
>
> 'Extended structures' implies something additional to the 'normal' ones.
> While it is 'extended' relative to what FIG or 83 could do, I don't view
> them as additional. ...

Fig A.3 of both the Forth-94 and Forth-2012 standards, which depicts the
flow control for

BEGIN ... WHILE ... WHILE ... REPEAT ... THEN and
BEGIN ... WHILE ... UNTIL ... ELSE ... THEN

is captioned, "Extended control-flow patterns." This is the language
used in the two standards, not my language.

--
Krishna

minf...@arcor.de

unread,
Jun 26, 2022, 1:49:25 PM6/26/22
to
Costs&time for development, debugging, documentation and maintenance
covered, there is not much left over for applications.

If I want batteries included in a language, there are numereous other choices.

Marcel Hendrix

unread,
Jun 26, 2022, 2:55:19 PM6/26/22
to
On Sunday, June 26, 2022 at 7:34:39 PM UTC+2, Krishna Myneni wrote:
[..]
> I think it's very hard to get away from the C library for a modern, full
> functional desktop Forth system. However, I sympathize with the idea. I
> would like to see more Forth source libraries which may be used
> directly, rather than compiled C libraries accessible through an
> external library interface. Examples include Forth libraries which
> provide the ability to retrieve image and metadata from different image
> file types, write .wav and .mp3 audio files, write .mpg4 video files,
> i/o through usb interface, etc.

For me, an existing library is good enough when I am not intensely
occupied with the application area. E.g., why bother with analysing
MIDI files when one doesn't have a tingel-tangel in the living-room,
why bother with .WAV when one doesn't fancy to trigger .wav files with
a MIDI command from within MANX ?

Today I found a reason to want to edit a mpeg4 file because I needed to
record myself for a internal conference. Boy, those standard video
editing 'tools' in Windows suck, even when one only wants to cut out
small mistakes or speed up the talk without increasing the pitch.
However, one reason twice a year is not enough.

-marcel

S Jack

unread,
Jun 26, 2022, 7:19:35 PM6/26/22
to
Most everything can be dealt with by a decent host; little need for Forth.
I'm not so ambitious to try and replace a Matlab with a home brew Forth app.
My use of Forth is just to consolidate general programming to one program
instead of having to deal with different syntax and behaviors in the
sundry programming programs that run on the host.
Unfortunate for me Linux is all C which I don't like, so my Forth won't
be getting intimate with it. I keep at shell level and use pipes
for data exchange. Exploring mostly, don't have any programming needs
nor do I really want any. Just play with pretty colors and tinker.
--
me

dxforth

unread,
Jun 26, 2022, 10:47:06 PM6/26/22
to
Even with access to libraries, a single project can consume all of one's
time. Avidemux, VLC player etc aren't one afternoon projects, nor even
single-person projects. So daunting in scope are they, members come and
go. Some journeys never end, so one needs to consider whether to embark
on them. Forth is one for sure.

dxforth

unread,
Jun 27, 2022, 12:15:26 AM6/27/22
to
Would you refer to the following as "Additional structures" because ANS
captioned the flow chart "Additional basic control-flow patterns"?

IF ... ELSE ... THEN
BEGIN ... WHILE ... REPEAT

It's not the terminology that concerns as what it promotes. AFAIK ANS
didn't. Multiple WHILEs resulting in a string of dislocated ELSE ...
THENs isn't easy to follow. Were there a compiler trick that hid all
this from the user then maybe. Even if such existed, factoring is
almost always a better solution to convoluted control structures - and
something Forth does promote. Hans gave a solution that was ANS assuming
that's important. There may be others.

minf...@arcor.de

unread,
Jun 27, 2022, 2:15:44 AM6/27/22
to
S Jack schrieb am Montag, 27. Juni 2022 um 01:19:35 UTC+2:
> On Sunday, June 26, 2022 at 12:34:39 PM UTC-5, Krishna Myneni wrote:
> > On 6/25/22 11:29, S Jack wrote:
> > file types, write .wav and .mp3 audio files, write .mpg4 video files,
> > i/o through usb interface, etc.
> Most everything can be dealt with by a decent host; little need for Forth.
> I'm not so ambitious to try and replace a Matlab with a home brew Forth app.

Reinventing Matlab in Forth is out of question. OTOH doing numerical linear
algebra really is. See Krishna Myneni's recent tridiagonalization algorithms
based on FSL. Another example: my own signal processing stuff is based on
a linear algebra library in Forth and C which provides a DSL with many similarities
to Matlab syntax.

You can't shrink Matlab but you can expand Forth.

none albert

unread,
Jun 27, 2022, 4:29:46 AM6/27/22
to
In article <6cbc178e-3778-4e16...@googlegroups.com>,
S Jack <sdwj...@gmail.com> wrote:
>On Saturday, June 25, 2022 at 9:30:55 AM UTC-5, Krishna Myneni wrote:
>> I had intended to do for a long time. The extensions will appear in
>> kForth-32 and kForth-Win32 after a period of testing.
>
>Good, nice to see another box checked in the do-list.
>Last night I did one myself, a cheap history. I didn't want C library
>in my Forth. I tried to COPROC with the system to let it do the
>command input and pass it to the Forth but that didn't work well so I
>just built a simple history in the Forth using one block screen of
>virtual memory.

Writing a command history in Forth is a complete waste of time.

In MS-Windows all console applications have a command history
courtesy Bill Gates.
In Linux there is a small program that transparently provides it.
Instead of
lina <options&parameters>
you type
rlwrap lina <options&parameters>

And yes you can write a perfect gui turnkey program using
a console Forth.

>--
>me

Hans Bezemer

unread,
Jun 27, 2022, 4:40:35 AM6/27/22
to
On Monday, June 27, 2022 at 10:29:46 AM UTC+2, none albert wrote:
> In article <6cbc178e-3778-4e16...@googlegroups.com>,
> S Jack <sdwj...@gmail.com> wrote:
> >On Saturday, June 25, 2022 at 9:30:55 AM UTC-5, Krishna Myneni wrote:
> >> I had intended to do for a long time. The extensions will appear in
> >> kForth-32 and kForth-Win32 after a period of testing.
> >
> >Good, nice to see another box checked in the do-list.
> >Last night I did one myself, a cheap history. I didn't want C library
> >in my Forth. I tried to COPROC with the system to let it do the
> >command input and pass it to the Forth but that didn't work well so I
> >just built a simple history in the Forth using one block screen of
> >virtual memory.
> Writing a command history in Forth is a complete waste of time.
>
> In MS-Windows all console applications have a command history
> courtesy Bill Gates.
> In Linux there is a small program that transparently provides it.
> Instead of
> lina <options&parameters>
> you type
> rlwrap lina <options&parameters>
>
> And yes you can write a perfect gui turnkey program using
> a console Forth.
Agreed. A user pointed this out to me, so I added it to the manual. Documented, done.

HB

minf...@arcor.de

unread,
Jun 27, 2022, 5:32:59 AM6/27/22
to
none albert schrieb am Montag, 27. Juni 2022 um 10:29:46 UTC+2:
> In article <6cbc178e-3778-4e16...@googlegroups.com>,
> S Jack <sdwj...@gmail.com> wrote:
> >On Saturday, June 25, 2022 at 9:30:55 AM UTC-5, Krishna Myneni wrote:
> >> I had intended to do for a long time. The extensions will appear in
> >> kForth-32 and kForth-Win32 after a period of testing.
> >
> >Good, nice to see another box checked in the do-list.
> >Last night I did one myself, a cheap history. I didn't want C library
> >in my Forth. I tried to COPROC with the system to let it do the
> >command input and pass it to the Forth but that didn't work well so I
> >just built a simple history in the Forth using one block screen of
> >virtual memory.
> Writing a command history in Forth is a complete waste of time.
>
> In MS-Windows all console applications have a command history
> courtesy Bill Gates.

How do you access the history from within ACCEPT? Assuming that
you use ACCEPT within your QUIT ie outer interpreter loop.

Hans Bezemer

unread,
Jun 27, 2022, 7:59:36 AM6/27/22
to
On Monday, June 27, 2022 at 6:15:26 AM UTC+2, dxforth wrote:
> It's not the terminology that concerns as what it promotes. AFAIK ANS
> didn't. Multiple WHILEs resulting in a string of dislocated ELSE ...
> THENs isn't easy to follow. Were there a compiler trick that hid all
> this from the user then maybe. Even if such existed, factoring is
> almost always a better solution to convoluted control structures - and
> something Forth does promote. Hans gave a solution that was ANS assuming
> that's important. There may be others.
I agree fully with you. It took me ages to wrap my head around the ANS structures.
First, ELSE..THEN seems related to the IF statement rather than WHILE. Second,
If you add multiple WHILEs the connection to that WHILE is completely at the
bottom of the construct.

Another "thingy" - there is NO ELSE for the first WHILE, because REPEAT kills
that chance AFAIK. That's horribly inconsistent. My "DONE" construct *always*
belongs to the previous WHILE - no exceptions. I also have no need for REPEAT,
since it's identical to AGAIN. It resolves all open WHILEs - zero ones if need be.
AGAIN/REPEAT compiles a BRANCH, while UNTIL compiles a 0BRANCH.

It seemed so neat and logical that way that I never doubted this solution. It has
been in 4tH for so long, it's hard to remember where it came from. I can vaguely
remember a FD (?) post on this subject - but I can't tell you whether it gave rise
to this solution or merely was a recognition of what I'd done before.

The only documentation I have it was introduced in v3.50 around 2005. That's a long
time ago. I've briefly browsed the FD archives, but it didn't pop up right away.

"DONE", however was my own invention. I designed that one when I found out this
one actually works:

: a if ." if " else ." 1st " else ." 2nd " else ." 3rd " then cr ;
ok
0 a 1st 3rd
ok
1 a if 2nd
ok

Hans Bezemer

Krishna Myneni

unread,
Jun 27, 2022, 8:42:52 AM6/27/22
to
> ...

Correct. The purpose of introducing a Forth version of a package like
EISPACK is two-fold:

1. Provide essential foundational code for scientific computing, which
are presently not generally available, to Forth systems. The
foundational algorithms are encoded in other languages, so the problem
is one of porting the code, unencumbered by proprietary licenses, to Forth.

2. Provide Forth source code which may be studied and modified for use
in special purpose applications.

This is a reasonable goal which can be accomplished in a reasonable time.

For numerical linear algebra, two types of problems are common:

1. Solve a set of linear equations of the form, Ax = b, where A is a
specified matrix, b is a specified vector (one-column matrix). The
problem is to solve for x.

The FSL provides algorithms in Forth source, ported by Skip Carter and
Marcel Hendrix for tackling this type of problem. LINPACK provides a
more comprehensive set of algorithms for solving these problems and
their variants.

2. Solve a set of linear equations of the form, Au = lambda*u, where A
is a specified matrix, lambda is an unknown scalar and u is an unknown
vector. There is no Forth code within the FSL for solving these types of
"eigensystem" problems. Marcel Hendrix has written some unpublished
Forth code and I have ported some of the EISPACK routines for handling a
subset of these types of problems.

Such goals are far from grandiose and based on user need.

Other types of libraries, e.g. low-level access to image files, audio
files, and video files, are not any more complex. They do take time, but
the benefit of having standard Forth source code available for these
tasks are that they can be easily modified for individual needs and used
across Forth systems on different platforms.

--
Krishna







Anton Ertl

unread,
Jun 27, 2022, 9:03:02 AM6/27/22
to
"minf...@arcor.de" <minf...@arcor.de> writes:
>If I want batteries included in a language, there are numereous other choices.

The language that advertizes "batteries included" (Python) includes
lots of C batteries, and the non-included stuff also often calls C
libraries.

minf...@arcor.de

unread,
Jun 27, 2022, 9:36:58 AM6/27/22
to
Anton Ertl schrieb am Montag, 27. Juni 2022 um 15:03:02 UTC+2:
> "minf...@arcor.de" <minf...@arcor.de> writes:
> >If I want batteries included in a language, there are numereous other choices.
> The language that advertizes "batteries included" (Python) includes
> lots of C batteries, and the non-included stuff also often calls C
> libraries.

Ok, I forgot that this is a Python ad. OTOH recently I played with Golang and had the
impresssion that its packages were mostly coded in Golang without C libraries behind.
I don't know if there is perhaps a licensing aspect behind.

dxforth

unread,
Jun 27, 2022, 9:50:14 AM6/27/22
to
On 27/06/2022 21:59, Hans Bezemer wrote:
>
> Another "thingy" - there is NO ELSE for the first WHILE, because REPEAT kills
> that chance AFAIK. That's horribly inconsistent.

ELSE after REPEAT ? REPEAT is equivalent to AGAIN-THEN so no opportunity.
As an experiment I tried:

: test ( n -- )
begin ?dup while dup . 1- again ." foo" else ." bar" then ;

It's not clear to me what that should do. Most forths I tried:

cr 5 test
5 4 3 2 1 bar
cr 0 test
bar

While mine gave:

cr 5 test
5 4 3 2 1 foo
cr 0 test
bar

One for the Twilight Zone?

Anton Ertl

unread,
Jun 27, 2022, 9:52:13 AM6/27/22
to
Hans Bezemer <the.bee...@gmail.com> writes:
>Another "thingy" - there is NO ELSE for the first WHILE, because REPEAT kills
>that chance AFAIK. That's horribly inconsistent.

If you prefer to have an ELSE for every WHILE, you can write

BEGIN
... WHILE
... WHILE
... AGAIN
ELSE ... THEN
ELSE ... THEN



My "DONE" construct *always*
>belongs to the previous WHILE - no exceptions. I also have no need for REPEAT,
>since it's identical to AGAIN. It resolves all open WHILEs - zero ones if need be.
>AGAIN/REPEAT compiles a BRANCH, while UNTIL compiles a 0BRANCH.
>
>It seemed so neat and logical that way that I never doubted this solution. It has
>been in 4tH for so long, it's hard to remember where it came from. I can vaguely
>remember a FD (?) post on this subject - but I can't tell you whether it gave rise
>to this solution or merely was a recognition of what I'd done before.
>
>The only documentation I have it was introduced in v3.50 around 2005. That's a long
>time ago. I've briefly browsed the FD archives, but it didn't pop up right away.

Gordon Charlton proposed END as universal control-struction finishing
word in the early 1990s. I don't remember the details, but it looked
quite elegant.

MPE (so probably Stephen Pelc) generalized CASE to also cover loops,
where NEXT-CASE loops back and finishes a CASE with arbitrarily many
OF/?OF...ENDOF clauses.

Anton Ertl

unread,
Jun 27, 2022, 10:51:32 AM6/27/22
to
dxforth <dxf...@gmail.com> writes:
>On 27/06/2022 21:59, Hans Bezemer wrote:
>>
>> Another "thingy" - there is NO ELSE for the first WHILE, because REPEAT kills
>> that chance AFAIK. That's horribly inconsistent.
>
>ELSE after REPEAT ? REPEAT is equivalent to AGAIN-THEN so no opportunity.
>As an experiment I tried:
>
>: test ( n -- )
> begin ?dup while dup . 1- again ." foo" else ." bar" then ;
>
>It's not clear to me what that should do.

The stuff between AGAIN and ELSE is unreachable (in standard Forth),
so you would normally not put anything there; instead, someone
desiring to match the WHILEs with ELSEs might write:

begin ... while ... while ... again else <code2> then else <code1> then

where <code2> is performed if the second while exited, and <code1> if
the first while exited.

Anton Ertl

unread,
Jun 27, 2022, 10:58:01 AM6/27/22
to
"minf...@arcor.de" <minf...@arcor.de> writes:
>OTOH recently I played with Golang and had the
>impresssion that its packages were mostly coded in Golang without C libraries behind.

Looking at the Debian package golang-1.18-go, it depends on libc6
>=2.32, i.e. a C library. If you want to avoid C, better go with
ciforth or colorForth.

dxforth

unread,
Jun 27, 2022, 12:58:46 PM6/27/22
to
On 28/06/2022 00:45, Anton Ertl wrote:
> dxforth <dxf...@gmail.com> writes:
>>On 27/06/2022 21:59, Hans Bezemer wrote:
>>>
>>> Another "thingy" - there is NO ELSE for the first WHILE, because REPEAT kills
>>> that chance AFAIK. That's horribly inconsistent.
>>
>>ELSE after REPEAT ? REPEAT is equivalent to AGAIN-THEN so no opportunity.
>>As an experiment I tried:
>>
>>: test ( n -- )
>> begin ?dup while dup . 1- again ." foo" else ." bar" then ;
>>
>>It's not clear to me what that should do.
>
> The stuff between AGAIN and ELSE is unreachable (in standard Forth),

Yes. What I got after executing the correct test :(

> so you would normally not put anything there; instead, someone
> desiring to match the WHILEs with ELSEs might write:
>
> begin ... while ... while ... again else <code2> then else <code1> then
>
> where <code2> is performed if the second while exited, and <code1> if
> the first while exited.

While the above has purpose and works, it looks convoluted thanks to ELSE.
In practice I've found the following sufficient:

begin ... while ... while ... repeat then
begin ... while ... while ... repeat ... then
begin ... while ... while ... repeat ... exit then

Similarly BEGIN WHILE UNTIL

Keeps confusion to a minimum.

S Jack

unread,
Jun 27, 2022, 3:41:18 PM6/27/22
to
On Friday, June 24, 2022 at 9:22:34 PM UTC-5, dxforth wrote:
> On 24/06/2022 22:34, minf...@arcor.de wrote:
> Forth programmers would appreciate seeing the tricks you used to achieve
> this magic. It wouldn't come cheap.

Not the same subject but close:
:) frogd
ELF32X86_64 Frog Version 1.0d

"job" /go
: foo pro
10 4 do
10 4 do
cr j . i .
i. j i * dup .
25 = if abandon fi
loop
loop
;
cr foo .( boo )

4 4 ==> 16
4 5 ==> 20
4 6 ==> 24
4 7 ==> 28
4 8 ==> 32
4 9 ==> 36
5 4 ==> 20
5 5 ==> 25 boo
--
me

Anton Ertl

unread,
Jun 27, 2022, 4:15:01 PM6/27/22
to
dxforth <dxf...@gmail.com> writes:
>On 28/06/2022 00:45, Anton Ertl wrote:
>> someone desiring to match the WHILEs with ELSEs might write:
>>
>> begin ... while ... while ... again else <code2> then else <code1> then
>>
>> where <code2> is performed if the second while exited, and <code1> if
>> the first while exited.
>
>While the above has purpose and works, it looks convoluted thanks to ELSE.
>In practice I've found the following sufficient:
>
>begin ... while ... while ... repeat then

Fine

>begin ... while ... while ... repeat ... then

Not so obvious how the stuff between REPEAT and THEN comes to be
executed.

>begin ... while ... while ... repeat ... exit then

Easier to understand and no disadvantages relative to the one above.

begin ... while ... 0= if ... exit then ... repeat

Krishna Myneni

unread,
Jun 27, 2022, 7:26:43 PM6/27/22
to
On 6/27/22 15:10, Anton Ertl wrote:
> dxforth <dxf...@gmail.com> writes:
>> On 28/06/2022 00:45, Anton Ertl wrote:
>>> someone desiring to match the WHILEs with ELSEs might write:
>>>
>>> begin ... while ... while ... again else <code2> then else <code1> then
>>>
>>> where <code2> is performed if the second while exited, and <code1> if
>>> the first while exited.
>>
>> While the above has purpose and works, it looks convoluted thanks to ELSE.
>> In practice I've found the following sufficient:
>>
>> begin ... while ... while ... repeat then
>
> Fine
>
>> begin ... while ... while ... repeat ... then
>
> Not so obvious how the stuff between REPEAT and THEN comes to be
> executed.
> ...

This wasn't obvious to me either until I wrote an example and looked at
the flow control diagram. All one needs to understand is that for
multiple WHILEs in BEGIN ... WHILE ... REPEAT structure, the WHILE
closest to REPEAT is the one capable of branching to the point
immediately after REPEAT. After you use it once, it's quite
straightforward to understand.

--
Krishna

dxforth

unread,
Jun 27, 2022, 9:33:24 PM6/27/22
to
Your magic word is 'abandon'. Same question applies.

dxforth

unread,
Jun 27, 2022, 10:35:51 PM6/27/22
to
On 28/06/2022 06:10, Anton Ertl wrote:
> dxforth <dxf...@gmail.com> writes:
>>On 28/06/2022 00:45, Anton Ertl wrote:
>>> someone desiring to match the WHILEs with ELSEs might write:
>>>
>>> begin ... while ... while ... again else <code2> then else <code1> then
>>>
>>> where <code2> is performed if the second while exited, and <code1> if
>>> the first while exited.
>>
>>While the above has purpose and works, it looks convoluted thanks to ELSE.
>>In practice I've found the following sufficient:
>>
>>begin ... while ... while ... repeat then
>
> Fine
>
>>begin ... while ... while ... repeat ... then
>
> Not so obvious how the stuff between REPEAT and THEN comes to be
> executed.

Every forther already knows what this does:

... while ... repeat ...

From there it's not hard to imagine what an additional WHILE that needs
a THEN would do. And if one doesn't, it's trivial enough to test.
One or two uses and it becomes second nature.

>
>>begin ... while ... while ... repeat ... exit then
>
> Easier to understand and no disadvantages relative to the one above.
>
> begin ... while ... 0= if ... exit then ... repeat

I would see that as a missed opportunity. Just recently I rewrote code
I'd encountered from this:

: sy? ( word table -- address )
begin
@+ 1+
while
2dup n=
if sy+
else nip 2- exit
then
repeat
drop c@+ type 0 huh? ;

to this:

: sy? ( word table -- address )
begin
@+ 1+
while
2dup n=
while
sy+
repeat nip 2- end
drop c@+ type 0 huh? ;

Understanding one's tools, things become simpler. That said I consider
this the practical limit. Which is ok because good forth and long
definitions don't mix.

S Jack

unread,
Jun 27, 2022, 11:45:49 PM6/27/22
to
On Monday, June 27, 2022 at 8:33:24 PM UTC-5, dxforth wrote:
> On 28/06/2022 05:41, S Jack wrote:
> > On Friday, June 24, 2022 at 9:22:34 PM UTC-5, dxforth wrote:

(
Abandon word by saving copy of the return on entry and
popping return to that point on abandonment.
)
: -pro "rp@ here ! cell dp +!" eval ; imm
: abandon "-cell dp +! here @ rp! exit" eval ; imm

: foo -pro
10 1 do
10 4 do
cr i j 2dup . . * dup ." --> " .
10 = if abandon fi
loop
loop
;
cr foo .( BOO )

(
Several ways to save return. I settled on the dictionary.
Earlier I used the L-stack (pro) but not a good choice.
For standard Forth which doesn't know DP (?) you could use
a work variable.
)
--
me

minf...@arcor.de

unread,
Jun 28, 2022, 2:11:46 AM6/28/22
to
Anton Ertl schrieb am Montag, 27. Juni 2022 um 16:58:01 UTC+2:
> "minf...@arcor.de" <minf...@arcor.de> writes:
> >OTOH recently I played with Golang and had the
> >impresssion that its packages were mostly coded in Golang without C libraries behind.
> Looking at the Debian package golang-1.18-go, it depends on libc6
> >=2.32, i.e. a C library. If you want to avoid C, better go with
> ciforth or colorForth.

I guess that any Forth riding on an OS has a C binding.

dxforth

unread,
Jun 28, 2022, 3:35:48 AM6/28/22
to
So not unlike CATCH THROW. I use CATCH THROW when the return is far
away or problematic. Otherwise there's UNLOOP (a factor of LOOP) and
UNNEST. These can be as short one machine instruction.

none albert

unread,
Jun 28, 2022, 3:39:57 AM6/28/22
to
In article <eb18ef1e-1f80-45c8...@googlegroups.com>,
I use (ACCEPT) that leaves a string constant ( adr n ) but that is not
relevant. It ultimately uses REFILL-TIB that plays by the rules
for a console application, i.e. using "standard in" and READ-FILE.

Then you can use cursor up cursor down, and other cursor movements
for editing.
It boils down to:
: REFILL-TIB TIB @ 400 STDIN READ-FILE ... ;

(In ciforth it is more sophisticated because it handles redirection
transparently.)

none albert

unread,
Jun 28, 2022, 3:45:29 AM6/28/22
to
In article <t9766t$3iklu$1...@dont-email.me>,
Krishna Myneni <krishna...@ccreweb.org> wrote:
>On 6/22/22 21:32, Krishna Myneni wrote:
>> I've long put off introducing extended flow control structures in
>> kForth, primarily for the reason that I've not needed them before ...
>> Extended flow control structures are of the type shown in Fig. A.3 in
>> the Forth-2012 standard:
>>
>> BEGIN ... WHILE ... WHILE ... REPEAT ... THEN
>>
>> BEGIN ... WHILE ... UNTIL ... ELSE ... THEN
>>
>...
>
>kForth-64 now supports the extended flow control structures (master
>branch, currently at v 0.4.0 on github). One or more WHILE statements
>may be used within the following flow control structures:
>
>BEGIN ... WHILE ... REPEAT
>BEGIN ... UNTIL
>BEGIN ... AGAIN
>
>The extended structures conform to the documented behavior in the
>Forth-94 and Forth-2012 standards. It turned out to be fairly simple to
>extend the basic versions of these flow control structures. Porting
>unstructured Fortran code to structured Forth forced me to do something
>I had intended to do for a long time. The extensions will appear in
>kForth-32 and kForth-Win32 after a period of testing.
>
>--
>Krishna Myneni
>
>

I take the Pascal view.
Introducing complicated control structures is not worth it,
in the rare cases you introduces labels and goto's,
they are more clear then a one-off control structures that
you must study in a case by case bases.
Serves as a big red flag too.

none albert

unread,
Jun 28, 2022, 3:47:23 AM6/28/22
to
In article <afed1955-b115-404d...@googlegroups.com>,
All respectable languages code their libraries in the language
itself. There is always a clash if you are forced to use C libraries.

minf...@arcor.de

unread,
Jun 28, 2022, 4:26:21 AM6/28/22
to
Thanks, one never stops learning.

Stephen Pelc

unread,
Jun 28, 2022, 4:38:02 AM6/28/22
to
On 27 Jun 2022 at 15:40:58 CEST, "Anton Ertl" <Anton Ertl> wrote:

> MPE (so probably Stephen Pelc) generalized CASE to also cover loops,
> where NEXT-CASE loops back and finishes a CASE with arbitrarily many
> OF/?OF...ENDOF clauses.

Guilty as charged. However, it fill the hole for a general purpose loop
in which the exit tests and the exit actions are together, so making
the whole thing much more readable than the multiple WHILE and
ELSE equivalent.

However NEXT-CASE is very rarely used as once you get to
needing it, refactoring the code is probably a good idea.

Stephen
--
Stephen Pelc, ste...@vfxforth.com
MicroProcessor Engineering, Ltd. - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, +44 (0)78 0390 3612, +34 649 662 974
http://www.mpeforth.com - free VFX Forth downloads

none albert

unread,
Jun 28, 2022, 5:52:09 AM6/28/22
to
In article <t9eel8$11o0v$1...@dont-email.me>,
Stephen Pelc <ste...@vfxforth.com> wrote:
>On 27 Jun 2022 at 15:40:58 CEST, "Anton Ertl" <Anton Ertl> wrote:
>
>> MPE (so probably Stephen Pelc) generalized CASE to also cover loops,
>> where NEXT-CASE loops back and finishes a CASE with arbitrarily many
>> OF/?OF...ENDOF clauses.
>
>Guilty as charged. However, it fill the hole for a general purpose loop
>in which the exit tests and the exit actions are together, so making
>the whole thing much more readable than the multiple WHILE and
>ELSE equivalent.
>
>However NEXT-CASE is very rarely used as once you get to
>needing it, refactoring the code is probably a good idea.

I ditched CASE in favour of Anton Ertl's (?)

: CONDS 0 ; IMMEDIATE
: THENS BEGIN DUP WHILE POSTPONE THEN REPEAT
DROP ; IMMEDIATE

Never looked back. E.g.

CONDS
xchg-aa-pattern DUP matches? IF replace TRUE ELSE
lealea-pattern DUP matches? IF ?lealea-replace? ELSE
lea0-pattern DUP matches? IF ?lea0-replace? ELSE
movrspxo-pattern DUP matches? IF ?movrspxo-replace? ELSE
movxorsp-pattern DUP matches? IF ?movxorsp-replace? ELSE
xchg-pattern DUP matches? IF ?xchg-replace? ELSE
oprnotest-pattern DUP matches? IF ?oprnotest-replace? ELSE
noclc-pattern DUP matches? IF ?noclc-replace? ELSE
movinope-pattern DUP matches? IF ?movinope-replace? ELSE
\ DUP is-movdecrsp IF go-movdecrsp TRUE ELSE
FALSE
THENS
>
>Stephen

dxforth

unread,
Jun 28, 2022, 6:02:04 AM6/28/22
to
On 28/06/2022 19:52, albert wrote:
> In article <t9eel8$11o0v$1...@dont-email.me>,
> Stephen Pelc <ste...@vfxforth.com> wrote:
>>On 27 Jun 2022 at 15:40:58 CEST, "Anton Ertl" <Anton Ertl> wrote:
>>
>>> MPE (so probably Stephen Pelc) generalized CASE to also cover loops,
>>> where NEXT-CASE loops back and finishes a CASE with arbitrarily many
>>> OF/?OF...ENDOF clauses.
>>
>>Guilty as charged. However, it fill the hole for a general purpose loop
>>in which the exit tests and the exit actions are together, so making
>>the whole thing much more readable than the multiple WHILE and
>>ELSE equivalent.
>>
>>However NEXT-CASE is very rarely used as once you get to
>>needing it, refactoring the code is probably a good idea.
>
> I ditched CASE in favour of Anton Ertl's (?)
>
> : CONDS 0 ; IMMEDIATE
> : THENS BEGIN DUP WHILE POSTPONE THEN REPEAT
> DROP ; IMMEDIATE
>
> Never looked back. E.g.

Wil Baden (COND THENS)

I didn't like THENS and renamed it CONT (short for: condition ... continue)

Hans Bezemer

unread,
Jun 28, 2022, 6:26:07 AM6/28/22
to
On Tuesday, June 28, 2022 at 9:35:48 AM UTC+2, dxforth wrote:
> So not unlike CATCH THROW. I use CATCH THROW when the return is far
> away or problematic. Otherwise there's UNLOOP (a factor of LOOP) and
> UNNEST. These can be as short one machine instruction.

Completely the same here. In processing programs I fire up a CATCH when calling
a stage, so I can approximately see what is going on. In others (like uBasic/4tH)
I fire away THROWs when problems pop up, because it's hard to guide them across
so many layers of code. And they're caught at the base, invoking the interpreter.

I hardly ever though use CATCH/THROW trivially - like the trend is with so many
languages nowadays. There are such things as return values (like flags) to see whether
a routine succeeded or not. It's up to the application itself how to react on that.

E.g. when I read an .INI file - and it's not there, one app might be like "Well, that's fine. We
take the defaults", while another app might absolutely require those settings. But
I think those considerations are obvious to this audience.

Hans Bezemer

Anton Ertl

unread,
Jun 28, 2022, 8:02:55 AM6/28/22
to
It's not that I don't understand it if I invest time into
understanding it, it's that it's not obvious. Yes, I can figure it
out, but the fact that I have to figure it out when alternatives work
without figuring-out damns this style.

Anton Ertl

unread,
Jun 28, 2022, 8:12:09 AM6/28/22
to
dxforth <dxf...@gmail.com> writes:
>On 28/06/2022 06:10, Anton Ertl wrote:
>>>begin ... while ... while ... repeat ... then
>>
>> Not so obvious how the stuff between REPEAT and THEN comes to be
>> executed.
>
>Every forther already knows what this does:
>
> ... while ... repeat ...
>
>From there it's not hard to imagine what an additional WHILE that needs
>a THEN would do.

>>>begin ... while ... while ... repeat ... exit then
>>
>> Easier to understand and no disadvantages relative to the one above.
>>
>> begin ... while ... 0= if ... exit then ... repeat
>
>I would see that as a missed opportunity. Just recently I rewrote code
>I'd encountered from this:
>
> : sy? ( word table -- address )
> begin
> @+ 1+
> while
> 2dup n=
> if sy+
> else nip 2- exit
> then
> repeat
> drop c@+ type 0 huh? ;

That's perverse. Better:

: sy? ( word table -- address )
begin
@+ 1+
while
2dup n= 0= if nip 2- exit then
sy+
repeat
drop c@+ type 0 huh? ;

>to this:
>
> : sy? ( word table -- address )
> begin
> @+ 1+
> while
> 2dup n=
> while
> sy+
> repeat nip 2- end
> drop c@+ type 0 huh? ;

That's also perverse. In both cases: If you are going to EXIT anyway
(hidden in END in your example), why separate the exiting branch from
the condition?

Anton Ertl

unread,
Jun 28, 2022, 8:48:35 AM6/28/22
to
Stephen Pelc <ste...@vfxforth.com> writes:
>However NEXT-CASE is very rarely used as once you get to
>needing it, refactoring the code is probably a good idea.

I am not sure that refactoring really helps when you have multiple
exit conditions in a loop.

As for usage frequency:

In Gforth's image there are the following usages:

2 NEXT-CASE
19 CONTOF (another looping word used with CASE)
8 ?OFs (another non-standard word used with CASE)
16 CASE standard word
74 OF standard word
133 WHILE
103 REPEAT

So obviously 30 more WHILE's than REPEATs, so there is quite a bit of
usage of "extended flow control structures".

Almost all uses of non-standard CASE words are in locate1.fs, a
relatively recent part of Gforth (just as we added NEXT-CASE
relatively recently).

The two NEXT-CASE usages are:

: l2 ( -- c-addr u lineno )
located-buffer 1 case ( c-addr u lineno1 )
over 0= ?of endof
dup located-bottom @ >= ?of endof
locate-lines# @ located-diff >= ?of endof
dup located-top @ >= ?of locate-print-line contof
locate-next-line
next-case ;

: .whereline {: view u -- :}
\ print the part of the source line around view that fits in the
\ current line, of which u characters have already been used
view view>buffer
1 case ( c-addr u lineno1 )
over 0= ?of endof
dup view view>line = ?of locate-line view u .wheretype1 endof
locate-next-line
next-case
drop 2drop ;

The three uses of ?OF CONTOF indicate that a WHILE would have been ok
for that case, but note that both loops had a case where WHILE would
have required something beyond adding a THEN after the REPEAT. How
would these word be improved with factoring?

You see one of the CONTOFs in L2 above. The other 18 are in:

: fancy-after-l ( c-addr1 u1 lineno1 -- c-addr2 u2 lineno2 )
\ allow to scroll around right after LOCATE and friends:
case
ekey \ k-winch will only be visible with ekey
ctrl p of 1 prepend-locate-lines contof
ctrl n of 1 append-locate-lines contof
ctrl u of rows 2/ prepend-locate-lines contof
ctrl d of rows 2/ append-locate-lines contof
'k' of 1 prepend-locate-lines contof
'j' of 1 append-locate-lines contof
'l' of located-diff >r index++
r> located-diff - append-locate-lines contof
'h' of located-diff >r index--
r> located-diff - append-locate-lines contof
ctrl b of rows 2 - prepend-locate-lines contof
bl of rows 2 - append-locate-lines contof
ctrl l of 0 append-locate-lines contof
'q' of endof
#esc of endof
ekey>xchar ?of ['] xemit $tmp unkeys endof
k-up of 1 prepend-locate-lines contof
k-down of 1 append-locate-lines contof
k-prior of rows 2/ prepend-locate-lines contof
k-next of rows 2/ append-locate-lines contof
k-winch of 0 append-locate-lines contof
k-right of located-diff >r index++
r> located-diff - append-locate-lines contof
k-left of located-diff >r index--
r> located-diff - append-locate-lines contof
endcase ;

This could obviously done in a more data-driven way, but I think that
this way is more readable, dogma be damned. Here's a similar thing in
a data-driven approach:

Create xchar-ctrlkeys ( -- )
' false , ' xfirst-pos , ' xback , ' false ,
' xeof , ' xend-pos , ' xforw , ' false ,
' ?xdel , ' xtab-expand , ' (xenter) , ' xclear-rest ,
' xreformat , ' (xenter) , ' next-line , ' false ,

' prev-line , ' false , ' false , ' setsel ,
' xtranspose , ' xclear-first , ' xpaste , ' false ,
' <xdel> , ' xpaste , ' xhide , ' xchar-altkey ,
' false , ' false , ' false , ' false ,

Create std-ekeys
' xback , ' xforw , ' prev-line , ' next-line ,
' xfirst-pos , ' xend-pos , ' prev-line , ' next-line ,
' false , ' <xdel> , ' (xenter) , ' false ,
' false , ' false , ' false , ' false ,
' false , ' false , ' false , ' false ,
' false , ' false , ' false , ' xreformat ,
' xhide , ' false , ' prev-line , ' next-line ,
' ?xdel , ' xtab-expand , ' setsel , ' xeof' ,

' xchar-ctrlkeys IS ctrlkeys
' std-ekeys IS ekeys

: bindkey ( xt key -- )
dup bl u>= abort" Ctrl codes only!"
cells ctrlkeys + ! ;
: ebindkey ( xt key -- )
dup keycode-limit keycode-start within abort" Ekeys only!"
keycode-start - cells ekeys + ! ;

Disadvantage of the data-driven approach: It's not obvious which key,
e.g., xclear-first is bound to.

Anton Ertl

unread,
Jun 28, 2022, 8:51:26 AM6/28/22
to
albert@cherry.(none) (albert) writes:
>I ditched CASE in favour of Anton Ertl's (?)
>
> : CONDS 0 ; IMMEDIATE
> : THENS BEGIN DUP WHILE POSTPONE THEN REPEAT
> DROP ; IMMEDIATE

Not mine. I use CASE for that, and also use NEXT-CASE.

dxforth

unread,
Jun 28, 2022, 9:13:58 AM6/28/22
to
On 28/06/2022 21:53, Anton Ertl wrote:
> Krishna Myneni <krishna...@ccreweb.org> writes:
>>On 6/27/22 15:10, Anton Ertl wrote:
>>> dxforth <dxf...@gmail.com> writes:
>>>> begin ... while ... while ... repeat ... then
>>>
>>> Not so obvious how the stuff between REPEAT and THEN comes to be
>>> executed.
>>> ...
>>
>>This wasn't obvious to me either until I wrote an example and looked at
>>the flow control diagram. All one needs to understand is that for
>>multiple WHILEs in BEGIN ... WHILE ... REPEAT structure, the WHILE
>>closest to REPEAT is the one capable of branching to the point
>>immediately after REPEAT. After you use it once, it's quite
>>straightforward to understand.
>
> It's not that I don't understand it if I invest time into
> understanding it, it's that it's not obvious. Yes, I can figure it
> out, but the fact that I have to figure it out when alternatives work
> without figuring-out damns this style.

Are you saying ANS made an error enabling WHILE for exactly that use?
If so, how do you explain gforth using it? I stopped counting instances
after finding ten files. Here's a sample:

: s>unumber? ( addr u -- ud flag )
over ''' =
IF \ a ' alone is rather unusual :-)
drop char+ c@ 0 true EXIT
THEN
base @ >r dpl on getbase
0. 2swap
BEGIN ( d addr len )
dup >r >number dup
WHILE \ there are characters left
dup r> -
WHILE \ the last >number parsed something
dup 1- dpl ! over c@ '.' =
WHILE \ the current char is '.'
1 /string
REPEAT THEN \ there are unparseable characters left
2drop false
ELSE
rdrop 2drop true
THEN
r> base ! ;

Looking at that I'd say the author was determined not to factor,
readability be damned.
Message has been deleted

Marcel Hendrix

unread,
Jun 28, 2022, 4:24:14 PM6/28/22
to
On Tuesday, June 28, 2022 at 2:48:35 PM UTC+2, Anton Ertl wrote:
[..]
> As for usage frequency:
>
> In Gforth's image there are the following usages:
>
> 2 NEXT-CASE
> 19 CONTOF (another looping word used with CASE)
> 8 ?OFs (another non-standard word used with CASE)
> 16 CASE standard word
> 74 OF standard word
> 133 WHILE
> 103 REPEAT

Interesting statistics!

In iForth64 (47 files) I find no extensions of CASE, and
202 CASE
1064 OF
201 ENDCASE
150 BEGIN
126 WHILE
99 REPEAT
11 REPEATED
4 ?REPEATED
899 EXIT
4760 IF
746 ELSE
1892 ENDIF
1781 DO
98 ?DO
256 LOOP
56 +LOOP
21 UNLOOP

-marcel

dxforth

unread,
Jun 28, 2022, 10:04:37 PM6/28/22
to
So the perfect loop is one that embeds all the exit code to be jumped
over:

: sy? ( word table -- address )
begin
@+ 1+
0= if drop c@+ type 0 huh? end
2dup n= 0= if nip 2- end
sy+
again ;

All the CS textbooks will need to be rewritten.

dxforth

unread,
Jun 29, 2022, 12:15:17 AM6/29/22
to
REPEATED ?REPEATED ???


Marcel Hendrix

unread,
Jun 29, 2022, 1:39:52 AM6/29/22
to
On Wednesday, June 29, 2022 at 6:15:17 AM UTC+2, dxforth wrote:
> On 29/06/2022 06:24, Marcel Hendrix wrote:
> > On Tuesday, June 28, 2022 at 2:48:35 PM UTC+2, Anton Ertl wrote:
> > [..]
[..]
> > 150 BEGIN
> > 126 WHILE
> > 99 REPEAT
> > 11 REPEATED
> > 4 ?REPEATED
[..]
> REPEATED ?REPEATED ???

REPEAT THEN ... THEN (any amount)
UNTIL ... THEN (any amount)

-marcel

Anton Ertl

unread,
Jun 29, 2022, 11:52:55 AM6/29/22
to
dxforth <dxf...@gmail.com> writes:
>On 28/06/2022 21:53, Anton Ertl wrote:
>> Krishna Myneni <krishna...@ccreweb.org> writes:
>>>On 6/27/22 15:10, Anton Ertl wrote:
>>>> dxforth <dxf...@gmail.com> writes:
>>>>> begin ... while ... while ... repeat ... then
>>>>
>>>> Not so obvious how the stuff between REPEAT and THEN comes to be
>>>> executed.
>>>> ...
>>>
>>>This wasn't obvious to me either until I wrote an example and looked at
>>>the flow control diagram. All one needs to understand is that for
>>>multiple WHILEs in BEGIN ... WHILE ... REPEAT structure, the WHILE
>>>closest to REPEAT is the one capable of branching to the point
>>>immediately after REPEAT. After you use it once, it's quite
>>>straightforward to understand.
>>
>> It's not that I don't understand it if I invest time into
>> understanding it, it's that it's not obvious. Yes, I can figure it
>> out, but the fact that I have to figure it out when alternatives work
>> without figuring-out damns this style.
>
>Are you saying ANS made an error enabling WHILE for exactly that use?

No. Forth is no nanny language that tries to eliminate all ways to
write bad programs. E.g., you can write

: 5 4 ;

in standard Forth. Forth relies on programmers to use good
programming practices. There is a way to use multiple WHILEs that I
consider fine: REPEAT/AGAIN followed by a THEN for every (extra)
WHILE, without any other code in between.

>If so, how do you explain gforth using it? I stopped counting instances
>after finding ten files. Here's a sample:
>
>: s>unumber? ( addr u -- ud flag )
> over ''' =
> IF \ a ' alone is rather unusual :-)
> drop char+ c@ 0 true EXIT
> THEN
> base @ >r dpl on getbase
> 0. 2swap
> BEGIN ( d addr len )
> dup >r >number dup
> WHILE \ there are characters left
> dup r> -
> WHILE \ the last >number parsed something
> dup 1- dpl ! over c@ '.' =
> WHILE \ the current char is '.'
> 1 /string
> REPEAT THEN \ there are unparseable characters left
> 2drop false
> ELSE
> rdrop 2drop true
> THEN
> r> base ! ;

And this is an example of bad usage. I don't know which version you
have that from, but this word looks even worse in the current
development version. You can see from the comments that (probably) I
tried to make it understandable. At one time I also tried to refactor
it, but apparently gave up eventually.

>Looking at that I'd say the author was determined not to factor,
>readability be damned.

One could indulge a little bit into the archaeology of this code, and
see who changed what, and how it arrived at the current state, but
for me other things are more urgent.

Anton Ertl

unread,
Jun 29, 2022, 12:11:06 PM6/29/22
to
Interesting relations. The WHILE and REPEAT numbers are similar to
Gforth, but some other numbers and relations are quite different:

iForth Gforth
source image
202 16 CASE
1064 74 OF
201 14 ENDCASE
150 130 BEGIN
126 133 WHILE
99 103 REPEAT
11 REPEATED
4 ?REPEATED
899 271 EXIT
4760 917 IF
746 340 ELSE
1892 74 ENDIF
934 THEN
1781 18 DO
98 68 ?DO
5 +do
22 u+do
2 -do
4 u-do
256 65 LOOP
56 48 +LOOP
6 -loop
21 15 UNLOOP

The difference in the counted-loop usage is remarkable. How do you
explain the difference between IF and ENDIF in iForth? Do you use
THEN after all?

Anton Ertl

unread,
Jun 29, 2022, 1:44:20 PM6/29/22
to
dxforth <dxf...@gmail.com> writes:
>On 28/06/2022 22:03, Anton Ertl wrote:
>> Better:
>>
>> : sy? ( word table -- address )
>> begin
>> @+ 1+
>> while
>> 2dup n= 0= if nip 2- exit then
>> sy+
>> repeat
>> drop c@+ type 0 huh? ;
...
>> If you are going to EXIT anyway
>> (hidden in END in your example), why separate the exiting branch from
>> the condition?
>
>So the perfect loop is one that embeds all the exit code to be jumped
>over:
>
>: sy? ( word table -- address )
> begin
> @+ 1+
> 0= if drop c@+ type 0 huh? end
> 2dup n= 0= if nip 2- end
> sy+
> again ;

If you have two conditions with different handlers, something in that
vein is ok. Of course, the EXITs mean that you complicate dealing
with stuff after the handlers. This can be avoided by using
CASE...NEXT-CASE:

: sy? ( word table -- address )
case
@+ 1+
0= ?of drop c@+ type 0 huh? endof
2dup n= 0= ?of nip 2- endof
sy+
next-case ;

Now you can insert, say, a tracer between NEXT-CASE and ";", or
application logic.

>All the CS textbooks will need to be rewritten.

Dijkstra's guarded commands are similar in spirit, but his "do"
switches the behaviour of the inner and outer clauses around, like
Gforth does with

case
... ?of ... contof
... ?of ... contof
...
endcase

But guarded command "do", and all combinations of Gforth's extended
CASE have a single exit (unless you use EXIT), like structured
programming dogma requires.
It is loading more messages.
0 new messages