break
BREAK/CONTINUE-Statements for Standard Forth
- raw README.md
- raw break-tests.fs
- raw break.fs
- raw glossary.md
- raw mit-license
- raw package.4th
- raw ttester-simple.fs
BREAK/CONTINUE-Statements for Standard Forth
Ulrich Hoffmann <uho@xlerb.de>
Version 1.0.0 - 2017-07-25
This package provides an implementation of C like BREAK
and CONTINUE
statements
on top of Forth-94 (ANS Forth).
BREAK
allows to exit the innermost textual enclosing loop. It works for
BEGIN
-loops as well as for DO
-loops. CONTINUE
stops executing the current
loop cycle and restarts the loop in its next cycle.
If no surrounding loop is present then both BREAK and CONTINUE exit the current definition.
Example
Display whether or not a given number is a prime number.
: .prime ( n -- )
dup 1 =
IF false swap
ELSE dup 2 =
IF true swap
ELSE dup 2 mod
IF true swap 3
BEGIN ( f n u )
2dup dup * >=
WHILE ( f n u )
2dup mod 0= IF >r >r drop false r> r> BREAK THEN
2 +
REPEAT ( f n u )
drop
ELSE
false swap
THEN THEN THEN
. ." is " 0= IF ." not " THEN ." a prime number."
;
1 .prime 1 is not a prime number. ok
2 .prime 2 is a prime number. ok
3 .prime 3 is a prime number. ok
4 .prime 4 is not a prime number. ok
5 .prime 5 is a prime number. ok
6 .prime 6 is not a prime number. ok
7 .prime 7 is a prime number. ok
8 .prime 8 is not a prime number. ok
9 .prime 9 is not a prime number. ok
Background
Some people ask for definitions to provide such a BREAK
feature for Forth:
Newsgroups: comp.lang.forth
Date: Wed, 19 Jul 2017 10:22:39 -0700 (PDT)
In-Reply-To: 0e5dc25f-2fc5-4c27-8587-feb4e6b952e8@googlegroups.com
NNTP-Posting-Host: 92.83.227.96
User-Agent: G2/1.0
Message-ID: ac821e97-23ab-4edd-a741-a4429e3991cd@googlegroups.com
Subject: Re: A Start With Forth 2017 – new eBook
From: Raimond Dragomir raimond....@gmail.com
Injection-Date: Wed, 19 Jul 2017 17:22:40 +0000
Content-Type: text/plain; charset="UTF-8"[...]
But I can propose onemyself: make a BREAK word in Swiftforth, VFX and gforth. a BREAK that's working for EVERY/ANY loop (including the DO LOOPs). It can be a primitive written in assembler or C or whatever. No need to be written in ANS forth (chicken or egg problem? no, make it a primitive). How hard is it? Really hard isn't it? Too hard to really DO IT ?.
So here it is - based on Forth-94 (ANS Forth).
You can use this implementation as a guideline to incorporate BREAK
and
CONTINUE
into specific systems of your choice if your have "carnal knowledge"
about their inner working.
This implementation only assumes what is ensured by a standard system.
See section Implementation below for details.
Others are not convinced that BREAK
(and even less CONTINUE
) is necessary and
favor a style that leaves loops in definitions by exiting the definition
(via EXIT
or UNLOOP EXIT
).
For example the above definition of .prime
could have been (separating
calculation and output) facorized as:
: divides? ( n u -- f ) mod 0= ;
: even? ( n -- f ) 2 divides? ;
: square ( n -- n^2 ) dup * ;
: prime? ( n -- f )
dup 1 = IF drop false EXIT THEN
dup 2 = IF drop true EXIT THEN
dup even? IF drop false EXIT THEN
3 BEGIN ( n u )
2dup square >=
WHILE ( n u )
2dup divides? IF 2drop false EXIT THEN
2 +
REPEAT ( n u )
2drop true ;
: .prime ( n -- ) dup . ." is " prime? 0= IF ." not " THEN ." a prime number" cr ;
If you dare - consult the discussion on comp.lang.forth (Subject: BREAK proposal) of July 2017...
Your milage may vary. Choose wisely.
Installation
To use the BREAK
or CONTINUE
, just S" break.fs" INCLUDED
on any standard system. This makes
the new words available and redefines the existing control structures accordingly. After loading
you don't have a standard system anymore as e.g. the compilation semantics of IF
is no
longer the standard effect on the control flow stack:
Compilation: ( C: -- orig )
but is now:
Compilation: ( C: orig_1 ... orig_u|dest -- orig orig_1 ... orig_u|dest ) ( u v loopID -- u v loopID )
to handle branches implied by BREAK
and CONTINUE
and to supply information about the surrounding loop.
The package redefines the standard CORE words BEGIN
UNTIL
AGAIN
WHILE
REPEAT
DO
IF
ELSE
THEN
:
;
DOES>
in a non standard way. Compiler extensions that assume their standard
behavior are likely to fail. Their use in ordinary programs should however remain uneffected.
The package also redefines the standard CORE EXT words :NONAME
?DO
CASE
OF
ENDOF
ENDCASE
and
AHEAD
from the TOOLS EXT word set if they have already been defined before loading the package.
Documentation
See the file glossary.md for a description of the defined words.
Tests and more example usage
The file break-tests.fs
contains test definitions based on John Hayes test framework tester.fs
.
They show additional ways to use BREAK
and CONTINUE
and their expected results.
To run the test suite issue S" break-tests.fs" INCLUDED
. The expected output should look like this
S" break-tests.fs" INCLUDED
some messages warning about redefinitions of standard words
break control structure
break tests done
ok
Tests have been run successfully on
- gforth, 0.7.3 and 0.7.9,
- SwiftForth 3.6.3 and
- VFX-Forth 4.7.2
which all use the data stack as control flow stack and also on
- FLK 1.3.2
that has a separate control flow stack.
Pitfalls
The following pitfalls have been identified:
+LOOP
When using CONTINUE
inside DO
... +LOOP
be aware that control flow is transfered directly
from CONTINUE
to +LOOP
in order to start the next loop iteration. Any calculation for the
loop increment value n
(see 6.1.0140 +LOOP in the Forth-94 standards document) in front of +LOOP
is skipped. An appropriate calculation has to be supplied in front of CONTINUE
already.
Example:
: continue-+loop ( -- )
10 0 DO I 8 = IF BREAK THEN
I 4 = IF 2 CONTINUE THEN
I
2 +LOOP 99 ;
t{ continue-+loop -> 0 2 6 99 }t
Implementation
The core idea of this implementation is to hold appropriate branch origins for BREAK
s and CONTINUE
s as well as the branch destination
of the beginning of the loop (along with the already existing items there) on top of the control flow stack.
Additional information about the loop is put on top of the data stack. This information is
( u v loopID ) where
u specifies the number of items additionally put on the control flow stack,
v specifies the number of used
OF
-ENDOF
pairs when using aCASE
structure, andloopID is a loop identifier distinguishing between
BEGIN
-loops (loopID=1),DO
-loops (loopID=2), and- no loop (loopID=0).
Both BREAK
and CONTINUE
compile different code, depending on the kind of loop
they appear in.
BREAK
inside a
BEGIN
-loopBREAK
compiles an unconditionals forward branch past the end of the loop.inside a
DO
-loopBREAK
compiles aLEAVE
statement (which will unravel the loop parameters and branch past the particularLOOP
or+LOOP
).outside of any loop
BREAK
compilesEXIT
leaving the current definition.
CONTINUE
inside a
BEGIN
-loopCONTINUE
compiles an unconditional branch to the beginning of the loop.inside a
DO
-loopCONTINUE
compiles an unconditional branch just before the correspondingLOOP
or+LOOP
.outside of any loop
CONTINUE
compilesEXIT
leaving the current definition.
The package assumes that branches are handled by the underlying standard system:
- unconditional forward branches by
AHEAD
(resolved byTHEN
) - unconditional backward branches by
AGAIN
(to location ofBEGIN
)
As both BREAK
and CONTINUE
can occur nested inside (nested) IF
or CASE
structures
the information about the surrounding loop must be moved to the top of control flow and
data stack even inside these structures.
So in essence the package
modifies the behavior of
:
:noname
DOES>
BEGIN
DO
and?DO
to put appropriate loop information on the data stack,modifies the behavior of
;
UNTIL
AGAIN
LOOP
+LOOP
to resolveBREAK
andCONTINUE
branches and to clean up loop information,modifies the behavior of
IF
ELSE
THEN
WHILE
REPEAT
AHEAD
and redefinesCASE
OF
ENDOF
ENDCASE
so they can handle the additional items on the control flow and data stack,defines
BREAK
andCONTINUE
with the compilation semantics described above.
During compilation of control structures a standard system uses the control flow stack. It might use the data stack as control flow stack or a separate stack for this purpose.
Thus a standard program (such as this package) must assume the control flow stack
could be either. It must clear the data stack of any items it puts there by itself
before using control flow stack dependent words such as the control structures themselves
or the control flow stack manipulation words CS-ROLL
and CS-PICK
.
Note that CS-ROLL
or CS-PICK
can only work on dest
or orig
items on
the control flow stack but DO
-loops also put do-sys
items there that CS-ROLL
and CS-PICK
cannot handle.
This effectively also inhibits access to items below do-sys
. The same holds for
CASE
/OF
and case-sys
/of-sys
items. This restricts the manipulation that you can
do on the control flow stack in presence of DO
or CASE
.
Please consult the source code break.fs
for exact details.
Bug Reports
Please send bug reports, improvements and suggestions to Ulrich Hoffmann <uho@xlerb.de>
Conformance
This program conforms to Forth-94 and Forth-2012
Standard conformant labeling
This is an ANS Forth Program with the environmental dependency of using lower case for standard definition names,
Requiring
] [ WORD WHILE UNTIL THEN SWAP REPEAT R@ R> POSTPONE OVER LOOP LEAVE IMMEDIATE IF EXIT ELSE
DUP DROP DOES> DO CR BL BEGIN >R = ; : 2DROP 1- 1+ 0= +LOOP FIND (
from the Core word set.Requiring
NIP 0<> .( \
from the Core Extensions word set.Requiring
[THEN] [IF] CS-ROLL CS-PICK AHEAD
from the Programming-Tools Extensions word set.Redefining
OF ENDOF ENDCASE CASE AGAIN ?DO :NONAME
from the Core Extensions word set if available.After loading this program, a Standard System does no longer exist: The program redefines
BEGIN
UNTIL
AGAIN
WHILE
REPEAT
DO
LOOP
+LOOP
IF
ELSE
THEN
:
;
DOES>
and possibly:NONAME
AHEAD
?DO
CASE
OF
ENDOF
ENDCASE
in a non standard way adding additional information to the data stack and the control flow stack during compilation to reflect the uses ofBREAK
andCONTINUE
. Programs that explicitly access the control flow stack or use these words in compiler extensions assuming their standard compilation semantics will most likely fail. Programs that just use the control flow structures will remain uneffected.
May the Forth be with you!
by UlrichHoffmann
Versions
Tags
forth-94, forth-2012, ansforth, controlstructures
Dependencies
None
Dependents
None