Constant array

12 views
Skip to first unread message

rickman

unread,
Jan 18, 2009, 2:57:39 AM1/18/09
to
I have been searching for code on creating an array of constants. I
have done a little work with arrays using then like value variables.
But I can't quite figure out how to make an array of constants without
being wordy using , to put values into the definition. Also, one of
the constant array types I want to make is an array of words to
execute.

\ Define constant array
: :constarray \ Usage: :constarray value value ... ;
CREATE ( -- ) 0 , ['] ; 0 BEGIN OVER ' TUCK <> WHILE , 1+ REPEAT DROP
SWAP !
DOES> ( n -- run code ) SWAP 1+ CELLS + @ execute ;

: test1 ." test #1" CR ;
: test2 ." test #2" CR ;
:constarray test test1 test2 ;
0 test 1 test

The result of the above is correct with test1 and test2 both
displaying their data. But if I use forget to remove the definitions
and reload, I get an error.

Any ideas on what is wrong? This is under Win32Forth. Or any ideas
on a better way to do this?

Rick

Alex McDonald

unread,
Jan 18, 2009, 7:46:38 PM1/18/09
to

FORGET is extremely difficult to implement on a modern OS. Use MARKER
instead.

--
Regards
Alex McDonald

rickman

unread,
Jan 18, 2009, 7:58:08 PM1/18/09
to

Same result.

MARKER Restart

\ Define constant array
: :constarray \ Usage: :constarray value value ... ;
CREATE ( -- ) 0 , ['] ; 0 BEGIN OVER ' TUCK <> WHILE , 1+ REPEAT DROP
SWAP !
DOES> ( n -- run code ) SWAP 1+ CELLS + @ execute ;

: test1 ." test #1" CR ;
: test2 ." test #2" CR ;
:constarray test test1 test2 ;
0 test 1 test

EXCEPTION 0xC0000005 ACCESS_VIOLATION
Version: 6.12.00 Build: 2
Registers:
Eax: 0050BD10
Ebx: TOS 00000000 top of stack
Ecx: 00000081
Edx: USER 00127F70 user area
Edi: 00000000
Esi: IP 0040E4BC Forth ip
Esp: SP@ 00126F44 stack ptr
Ebp: RP@ 0012FDE8 rstack ptr
Eip: PC 00000002 machine ip
Access addr: 00000002 READ violation
Backtracking: CONSOLE-STATUSBAR-INTERPRET+0
INCLUDING?+1C [UNKNOWN]+FFF00008 [UNKNOWN]+FFEFFFFE

Data stack: 0 0 0 0
Primitive "0x400000" loaded from: SRC\KERNEL\FKER
NEL.F at line: 2843
Invoking word CONSOLE-STATUSBAR-INTERPRET loaded from
: C:\PROGRAM FILES\WIN32FORTH\SRC\CONSOLE\CONSOLESTATB
AR.F at line: 121
fails at word _INTERPRET loaded from: SRC\KERNEL\FKER
NEL.F at line: 4699

Press any key to exit...


DOES> ( n -- run code ) SWAP 1+ CELLS + @ execute ;

^
Error(9998): ; Windows exception trapped in file C:\AR
IUS\BOARDS\IRIG-B-TESTBED\FORTHAPPS\CONST.F at line 6

It says it doesn't like line 6, but it highlights line 5 in the IDE.

Rick

Coos Haak

unread,
Jan 18, 2009, 8:02:58 PM1/18/09
to
Op Sat, 17 Jan 2009 23:57:39 -0800 (PST) schreef rickman:

> I have been searching for code on creating an array of constants. I
> have done a little work with arrays using then like value variables.
> But I can't quite figure out how to make an array of constants without
> being wordy using , to put values into the definition. Also, one of
> the constant array types I want to make is an array of words to
> execute.
>
> \ Define constant array
>::constarray \ Usage: :constarray value value ... ;
> CREATE ( -- ) 0 , ['] ; 0 BEGIN OVER ' TUCK <> WHILE , 1+ REPEAT DROP
> SWAP !

Here you are messing up the code of `;' !!


> DOES> ( n -- run code ) SWAP 1+ CELLS + @ execute ;
>
>: test1 ." test #1" CR ;
>: test2 ." test #2" CR ;
>:constarray test test1 test2 ;
> 0 test 1 test
>
> The result of the above is correct with test1 and test2 both
> displaying their data. But if I use forget to remove the definitions
> and reload, I get an error.
>
> Any ideas on what is wrong? This is under Win32Forth. Or any ideas
> on a better way to do this?
>

Why not simply:
: :constarray
CREATE BEGIN ' DUP [;] <> WHILE , REPEAT DROP
DOES> SWAP CELLS + @ EXECUTE ;

Or even
VARIABLE aux \ somewhere to store a value
HERE aux ! ' test1 , ' test2 ,
: test CELLS [ aux @ ] LITERAL + @ EXECUTE ;

--
Coos

CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html

Coos Haak

unread,
Jan 18, 2009, 8:05:00 PM1/18/09
to
Op Mon, 19 Jan 2009 02:02:58 +0100 schreef Coos Haak:

>::constarray


> CREATE BEGIN ' DUP [;] <> WHILE , REPEAT DROP
CREATE BEGIN ' DUP ['] ; <> WHILE , REPEAT DROP

--

rickman

unread,
Jan 18, 2009, 9:50:49 PM1/18/09
to
On Jan 18, 8:02 pm, Coos Haak <chfo...@hccnet.nl> wrote:
> Op Sat, 17 Jan 2009 23:57:39 -0800 (PST) schreef rickman:
>
> > I have been searching for code on creating an array of constants.  I
> > have done a little work with arrays using then like value variables.
> > But I can't quite figure out how to make an array of constants without
> > being wordy using , to put values into the definition.  Also, one of
> > the constant array types I want to make is an array of words to
> > execute.
>
> > \ Define constant array
> >::constarray \ Usage: :constarray value value ... ;
> > CREATE ( -- ) 0 , ['] ; 0 BEGIN OVER ' TUCK <> WHILE , 1+ REPEAT DROP
> > SWAP !
>
> Here you are messing up the code of `;' !!
>
> > DOES> ( n -- run code ) SWAP 1+ CELLS + @  execute ;

Yes, I see that now. I was trying to store the count in the first
location, but I munged that up. How do I get the address of the
location where I stored the 0? Do I need to use HERE before I
use , ? Or maybe I can use HERE at the end and then subtract the n
cells?

> >: test1 ." test #1" CR ;
> >: test2 ." test #2" CR ;
> >:constarray test test1 test2 ;
> > 0 test 1 test
>
> > The result of the above is correct with test1 and test2 both
> > displaying their data.  But if I use forget to remove the definitions
> > and reload, I get an error.
>
> > Any ideas on what is wrong?  This is under Win32Forth.  Or any ideas
> > on a better way to do this?
>
> Why not simply:
> : :constarray
>    CREATE   BEGIN ' DUP [;] <> WHILE , REPEAT DROP
>    DOES>   SWAP CELLS + @ EXECUTE ;

I don't know what [;] is. I don't see it in the DPANS document and it
does not show up using see in Win32Forth. This also does not store
the count. I would like to have the DOES> check to make sure the n is
in range.


> Or even
> VARIABLE aux  \ somewhere to store a value
> HERE aux ! ' test1 , ' test2 ,
> : test  CELLS [ aux @ ] LITERAL + @ EXECUTE ;

All the ticking is a bit crude although it would work. I would like
to have a word that is more general purpose.

Thanks for the ideas.

Rick

rickman

unread,
Jan 18, 2009, 10:00:05 PM1/18/09
to
On Jan 18, 8:05 pm, Coos Haak <chfo...@hccnet.nl> wrote:
> Op Mon, 19 Jan 2009 02:02:58 +0100 schreef Coos Haak:
>
> >::constarray
> >    CREATE   BEGIN ' DUP [;] <> WHILE , REPEAT DROP
>
>      CREATE   BEGIN ' DUP ['] ; <> WHILE , REPEAT DROP

I tried this and it seems to work now.

: :constarray \ Usage: :constarray value value ... ;
CREATE ( -- )
0 , ['] ; 0 ( -- xt 0 )


BEGIN OVER ' TUCK <> WHILE , 1+ REPEAT

DROP NIP DUP CELLS HERE + !


DOES> ( n -- run code ) SWAP 1+ CELLS + @ execute
;

Thanks,

Rick

Alex McDonald

unread,
Jan 18, 2009, 10:40:51 PM1/18/09
to
> Rick- Hide quoted text -
>
> - Show quoted text -

Using FORGET and MARKER after a serious error, as in the example, can
lead to further problems. Given the quick startup of Win32Forth, the
best advice is to start the program again.

Others have noted the errors in the code.

The error routine tries its best to identify the line and column thta
caused the error, which was correctly identified at the end of line 6.
Line 5 is the start of the definition itself, so that's where it
places you in the IDE.

--
Regards
Alex McDonald

Alex McDonald

unread,
Jan 19, 2009, 2:02:26 AM1/19/09
to
> Rick- Hide quoted text -
>
> - Show quoted text -- Hide quoted text -

>
> - Show quoted text -

Range checking version.

: xtarray ( <-name-> <-xt-> [...] <;> )
create here 0 ,
begin
' dup ['] ; <>
while , repeat drop
here cell- swap !
does> ( index -- ? )
over 0< >r \ check lower bound index
swap 1+ cells over +
swap @ over < \ check upper bound address
r> or abort" index out of range"
@ execute ;

xtarray xa dup swap drop ;

I'm not terribly fond of the above; it looks too like a standard
definition. Perhaps something like this is marginally better.

: xtarray ( <-name-> -- addr )
create here dup ,
does> ( index -- ? )
over 0< >r \ check lower bound index
swap 1+ cells over +
swap @ over < \ check upper bound address
r> or abort" index out of range"
@ execute ;

: ]xtlist ;
: xtlist[ ( addr -- )
begin
' dup ['] ]xtlist <>
while , repeat drop
here cell- swap ! \ points at last entry
;

xtarray xa xtlist[ dup swap drop ]xtlist

--
Regards
Alex McDonald

rickman

unread,
Jan 19, 2009, 3:21:23 AM1/19/09
to

What's wrong with looking like a standard definition? I was actually
trying to find a way to use the regular compilation words to do this.
The idea of building a list of xt addresses is what a standard
definition does. The only difference is that my word will define an N
way conditional like a case statement that then invokes the
appropriate definition instead of executing them all senquentially. I
don't remember all my Forth education such as the difference between
ITC and DTC, but I know one of them uses a list of xt addresses and
the cfa points to the code for next. If instead the cfa points to
code to select just one of the words compiled, that would do what I
want. I guess if the Forth is not the right type, such as STC, then
it couldn't be compiled directly.

How about this?

MARKER Restart

\ Define array of xt and select one for execution
: :xtarray \ Usage: :constarray value value ... ;
CREATE ( -- )


0 , ['] ; 0 ( -- xt 0 )

BEGIN OVER ' TUCK <> WHILE , 1+ REPEAT

DROP NIP DUP CELLS CELL+ HERE SWAP - !


DOES> ( n -- run code )

2DUP @ 0 WITHIN ABORT" array out of range"
CELL+ SWAP CELLS + @ execute
;

: test1 ." test #1" CR ;
: test2 ." test #2" CR ;

:xtarray test test1 test2 ;
0 test 1 test 2 test

The test cases seem to work. I also tried it with -1 test and that
also flags an error.

Jonah Thomas

unread,
Jan 19, 2009, 9:53:28 AM1/19/09
to
rickman <gnu...@gmail.com> wrote:

> Coos Haak <chfo...@hccnet.nl> wrote:
> > schreef rickman:
> >
> > > I have been searching for code on creating an array of constants.
> > >  I have done a little work with arrays using then like value
> > > variables. But I can't quite figure out how to make an array of
> > > constants without being wordy using , to put values into the
> > > definition.  Also, one of the constant array types I want to make
> > > is an array of words to execute.
> >
> > > \ Define constant array
> > >::constarray \ Usage: :constarray value value ... ;
> > > CREATE ( -- ) 0 , ['] ; 0 BEGIN OVER ' TUCK <> WHILE , 1+ REPEAT
> > > DROP SWAP !
> >
> > Here you are messing up the code of `;' !!
> >
> > > DOES> ( n -- run code ) SWAP 1+ CELLS + @  execute ;
>
> Yes, I see that now. I was trying to store the count in the first
> location, but I munged that up. How do I get the address of the
> location where I stored the 0? Do I need to use HERE before I
> use , ? Or maybe I can use HERE at the end and then subtract the n
> cells?

When you have trouble doing something complicated, why not do it
simpler? For example,

: :constarray ( x1 x2 ... xn n -- )
CREATE DUP , 0 DO , LOOP
DOES> 2DUP @ U< IF CELL+ SWAP CELLS + @ ELSE handle-error THEN ;

This only works when the constants don't change after compile-time, but
it's simple. And you can put in numbers that don't have to be dictionary
lookups.

test1 test2 19 3 :constarray test

Or if you need the xts, you could do

' test1 ' test2 2 :constarray test

and do EXECUTE inside the DOES>

If you don't want to count the constants yourself you could add some
sugar.

VARIABLE my-count
: start-count
0DEPTH my-count ! ;
: end-count
DEPTH my-count @ - ;

start-count ' test1 ' test2 end-count :constarray test

Maybe it's easy enough to let the intepreter do the work. But if you're
doing it for users, maybe you don't want them to have to type in a
special syntax. In that case, maybe use TCL or HTML or something, and
give them a window with a field they can type constant names into, or
better yet give them a set of icons with the available constant names
and they can click on the ones they want.

When you write your own interpreter it's bound to be more complex. But
it can be done, just notice what you're trying to get done.

You used ; to end the list, that's a good choice because it will never
be an xt that's on the list, though it might possibly confuse somebody.

So you use your interpreter to get a list of xt's, and you make a jump
table.

: not-iast-item? ( "name" -- flag )
>in @ bl parse-name S" ;" compare if >in ! true else drop 0 then ;

: get-list ( -- xtn ... xt1 n )
0 begin not-last-item? while 1+ ' swap repeat ;

: make-jump-table ( xtn ... xt1 n -- addr )
HERE >R dup , 0 do , loop R> ;
: jump-table-bounds? ( n addr -- n addr' flag )
2dup @ U< ;
: jump-table-jump ( n addr -- x*j )
swap 1+ cells + @ execute ;

: :constarray
CREATE get-list make-jump-table
DOES> jump-table-bounds? IF jump-table-jump ELSE bounds-error THEN ;

This is all untested. One issue is that the whole list has to be on one
line. You could fix that with REFILL and make it more complicated. The
lessons I get are:

1. Try to factor. Easier to get things right when you do them in
isolation. Harder to test code that's buried inside a loop.

2. Try to minimise the massaging. When you do ' you're stuck with what
your interpreter gives you. Does your interpreter give you the right
result for ' ; ? You have to know, and it might not port.

3. It's simpler to use an existing interpreter than build your own. But
if you do build your own, add just the complications that make it do
just what you need and no more. If it's for your own use, be careful to
use it as you intend and you can be sparing of error-checking for the
inputs.

jacko

unread,
Jan 19, 2009, 10:35:04 AM1/19/09
to
hi

: ONEXECUTE ( compile execution semantics + branch and call with no
return )
CELLS R> + @EXECUTE ;

use

: blah blah blah ONEXECUTE test0 test1 test2 ;

it may not work on all forths, but it gets the return stack item, and
adds to get the address of the xt. assumes constant list only used
once, or is a factor of many words ;-)

cheers jacko

Ed

unread,
Jan 20, 2009, 5:41:42 AM1/20/09
to
Alex McDonald wrote:
> ...

> FORGET is extremely difficult to implement on a modern OS. Use MARKER
> instead.
> ...

That claim was made in the ANS rationale A.6.2.1850. I've yet to
see a case where FORGET was either onerous to implement, or
more complicated than MARKER. Has anyone?

It's quite possible that MARKER is a better word simply because
it does more.

jacko

unread,
Jan 20, 2009, 8:28:08 AM1/20/09
to

May need a 1+ or cell+ if subrotine threaded code, has big potential
problems with immediate words.

: ON CELLS R> + PERFORM ; \ going to put this in gforth port for nibz

: ontest ON test0 test1 ['] POSTPONE test3 ;

This would not postpone test3 on 2, but would postpone the word
following the call to ontest on 2.

cheers jacko

Leo Wong

unread,
Jan 20, 2009, 11:42:45 AM1/20/09
to
On Jan 18, 2:57 am, rickman <gnu...@gmail.com> wrote:
> . . . one of the constant array types I want to make is an array of words to
> execute.

\ xarray.f, Leo Wong, Presidential Inauguration Day 2009 +

: end-xarray ;

: xarray ( <name> . . . <end-array> )
CREATE HERE >R 0 ,
0 BEGIN ' DUP ['] end-xarray <>
WHILE , 1+
REPEAT DROP R> !
DOES> ( n -- ) 2DUP @ U<
IF SWAP 1+ cells + @ EXECUTE
ELSE CR ." Out of range" 2DROP THEN ;

: test1 CR ." Test 1" ;
: test2 CR ." Test 2" ;

xarray test test1 test2 end-xarray

0 test 1 test 2 test -1 test

Bernd Paysan

unread,
Jan 20, 2009, 12:20:13 PM1/20/09
to
Leo Wong wrote:
> \ xarray.f, Leo Wong, Presidential Inauguration Day 2009 +

Hm, should we now name the year "year 1 of Barack"? There are contries which
never had a continuous calendar, but just king + year of his reign (and for
sure, only the first name is used). The king's name were uniquified by
adding numbers (so after year 8 of George III, the chimp follows year 1 of
Barack I, the great ;-).

--
Bernd Paysan
"If you want it done right, you have to do it yourself"
http://www.jwdt.com/~paysan/

Leo Wong

unread,
Jan 20, 2009, 3:12:17 PM1/20/09
to
On Jan 20, 11:42 am, Leo Wong <Mary....@gmail.com> wrote:

> : xarray ( <name> . . . <end-array> )
>    CREATE HERE >R 0 ,
>    0 BEGIN ' DUP ['] end-xarray <>
>    WHILE , 1+
>    REPEAT DROP R> !
>    DOES> ( n -- ) 2DUP @ U<
>       IF SWAP 1+ cells + @ EXECUTE
>       ELSE CR ." Out of range" 2DROP THEN ;


: xarray ( . . . CONSTANT <name> )
HERE >R 0 ,
0 BEGIN ' DUP ['] CONSTANT <>
WHILE , 1+
REPEAT R@ SWAP EXECUTE R> !
DOES> ( n -- ) @ 2DUP @ U<
IF SWAP 1+ CELLS + @ EXECUTE


ELSE CR ." Out of range" 2DROP THEN ;

: test1 CR ." Test 1" ;
: test2 CR ." Test 2" ;

xarray test1 test2 CONSTANT test

Elizabeth D Rather

unread,
Jan 20, 2009, 5:44:59 PM1/20/09
to

The issue is how much system state do you want to restore? FORGET just
cuts back the dictionary. Depending on the platform & implementation,
it may be necessary to reset some other pointers (e.g. to code being
deleted), interrupt handlers or call-backs, wordlist info, etc. MARKER
provides an opportunity to save necessary state information so you can
restore not only the dictionary but also other state info to a known
condition.

Cheers,
Elizabeth

--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================

rickman

unread,
Jan 21, 2009, 12:22:27 AM1/21/09
to

Using CONSTANT as the terminator is interesting. I thought about
putting the ['] <name> inside the loop, but isn't ['] a processing
intensive search of the vocabulary. But maybe that is not important.
I'm still kinda new at this and I don't recall the details of [']. I
think the search is done at compile time while the loop is at
execution time of the defining word. So in the loop this is just
another literal loaded onto the stack. If the dictionary search is
done when the defining word is executed, the search for CONSTANT is
repeated for every word in the new definition. I guess in the grand
scheme of things that is still not such a big deal.

I think you have a mistake in your DOES> code. The first @ does a
fetch of the first cell in the data section and removed the address of
the data field which is needed later. But I also see an EXECUTE in
what should be the create part which is no longer there... I guess
this is a lot more complex than I realized. Did you test this? Will
it really work??? I can't say I understand this.

Rick

Ed

unread,
Jan 21, 2009, 8:13:58 AM1/21/09
to
Elizabeth D Rather wrote:
> Ed wrote:
> > Alex McDonald wrote:
> >> ...
> >> FORGET is extremely difficult to implement on a modern OS. Use MARKER
> >> instead.
> >> ...
> >
> > That claim was made in the ANS rationale A.6.2.1850. I've yet to
> > see a case where FORGET was either onerous to implement, or
> > more complicated than MARKER. Has anyone?
> >
> > It's quite possible that MARKER is a better word simply because
> > it does more.
>
> The issue is how much system state do you want to restore? FORGET just
> cuts back the dictionary. Depending on the platform & implementation,
> it may be necessary to reset some other pointers (e.g. to code being
> deleted), interrupt handlers or call-backs, wordlist info, etc. MARKER
> provides an opportunity to save necessary state information so you can
> restore not only the dictionary but also other state info to a known
> condition.

No, the issue is that ANS claimed FORGET was prohibitive to implement
on modern systems.

Win32Forth and VFX (two modern complex systems) have elected to
provide both FORGET and MARKER, andso calls the claim into question.

Whether MARKER is more useful than FORGET is a different question.

Andrew Haley

unread,
Jan 21, 2009, 8:22:55 AM1/21/09
to

> provide both FORGET and MARKER, and so calls the claim into
> question.

Illogical, captain. The fact that Win32Forth and VFX do this doesn't
call anything into question.

A Forth with separate headers, code and data has three dictionary
pointers, and if FORGET is going to peel back all three of them it has
to know, for every word in the dictionary, the values of those
pointers before a definition was created. While it is _possible_ to
do that, the TC didn't see fit to require it; eminently sensible IMO.

Andrew.

Stephen Pelc

unread,
Jan 21, 2009, 9:17:49 AM1/21/09
to
On Thu, 22 Jan 2009 00:13:58 +1100, "Ed" <nos...@invalid.com> wrote:

>No, the issue is that ANS claimed FORGET was prohibitive to implement
>on modern systems.
>
>Win32Forth and VFX (two modern complex systems) have elected to
>provide both FORGET and MARKER, andso calls the claim into question.

In VFX Forth, FORGET is documented as obsolescent. It was provided
to ease porting of applications from other Forths. It's probably
time to retire this word.

The real point is that just unhooking the dictionary chains is not
enough to restore all state. Most "big" Forths permit you to add
to the behaviour of MARKERs (e.g. PRUNE and REMEMBER) so that the
application programmer can add save/restore actions.

The design of MARKER is fine. The real problem is that, in my
experience, most application programmers do not implement enough
additional behaviour, and so just restart the application -
it's quicker than thinking.

There is very little call for FORGET or MARKER in embedded systems
except for web scripting.

Stephen


--
Stephen Pelc, steph...@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads

Leo Wong

unread,
Jan 21, 2009, 9:27:20 AM1/21/09
to

Using CONSTANT was eye candy, and probably a bad idea. Your
definition is probably better:

: xarray:


CREATE HERE >R 0 ,

0 BEGIN ' DUP ['] ; <>


WHILE , 1+ REPEAT DROP R> !
DOES> ( n -- ) 2DUP @ U<

IF SWAP 1+ CELLS + @ EXECUTE

ELSE CR ." Not in range 0-" @ 1- 0 .R ." : " . THEN ;

: test1 CR ." Test 1" ;
: test2 CR ." Test 2" ;

xarray: test test1 test2 ;

0 test 1 test 2 test -1 test

W32Forth:

include xarray.f
Test 1
Test 2
Not in range 0-1: 2
Not in range 0-1: -1 ok
see xarray:
: XARRAY:


CREATE HERE >R 0 , 0

BEGIN ' DUP lit ; <>


WHILE , 1+
REPEAT DROP R> !

DOES> 2DUP @ U<
IF SWAP 1+ CELLS + @ METHODEXECUTE
ELSE CR ." Not in range 0-" @ 1- 0 .R ." :
"
.
THEN ; ok


Note, however, that Gforth didn't like ['] ; , so for that Forth
some other ending besides ; would be required.

Leo Wong

jacko

unread,
Jan 21, 2009, 9:59:49 AM1/21/09
to
Hi

: ONCASE CELLS R> + PERFORM ; \ going to put this in gforth port for
nibz

This is a much better name, due to ON being used for port control in
some forths.
Will work with ITC forths, When followed by a list of compiled xts (as
ITC does).

There are good embedded lower power reasons for doing it small. There
is no bounds checking!! I do not really use ['] so you may have
noticed the lack of a [,] to compile POSTPONE as an xt. This may be
useful in some programming situations. I am not sure if CASE et al.
compiles into the EC standard dictionary, but it would take more space
if index to action is a sequential ordered number, like for a OS file
function table.

cheers jacko


Helmar

unread,
Jan 21, 2009, 10:13:50 AM1/21/09
to
On Jan 21, 9:27 am, Leo Wong <Mary....@gmail.com> wrote:

> Using CONSTANT was eye candy, and probably a bad idea.

It was a nice idea. I would implement:

--------------------
: ...constant ' ['] constant over <> if , recurse exit then drop ;
: xarray here ...constant create ,
does> dup @ rot cells +
tuck u<= abort" xarray outside bounds"
@ execute ;

: a ." A" ;
: b ." B" ;
: c ." C" ;

xarray a b c constant z

0 z 1 z 2 z cr

3 z
--------------------

Regards,
-Helmar

rickman

unread,
Jan 21, 2009, 11:42:52 AM1/21/09
to

That is an interesting use of recursion. I have to say I find your
use of EXIT inside the IF to be confusing. I know that Forth users
tend to toss out most established ideas of structured programming, but
I don't see where this deviation is at all useful. Wouldn't it be
more clear and just as easy to use "IF , RECURSE ELSE DROP THEN"? To
me this is very easy to see that the two branches are equal in the
usage of the stack which is a common cause of errors.

I don't understand how the u<= checks the range of the input
parameter. It seems to be comparing the value of the PFA of the
constant word to the calculated address of the element in the list to
be executed. That does not account for the header created by create,
right? Also, by comparing two pointers as unsigned does not work when
the calculated address wraps around the top of the number space. That
is one reason why I did the range check on the index and used WITHIN
rather than u<=.

Rick

Helmar

unread,
Jan 21, 2009, 12:17:04 PM1/21/09
to
On Jan 21, 11:42 am, rickman <gnu...@gmail.com> wrote:
> On Jan 21, 10:13 am, Helmar <hel...@gmail.com> wrote:
>
>
>
>
>
> > On Jan 21, 9:27 am, Leo Wong <Mary....@gmail.com> wrote:
>
> > > Using CONSTANT was eye candy, and probably a bad idea.
>
> > It was a nice idea. I would implement:
>
> > --------------------
> > : ...constant   ' ['] constant over <> if , recurse exit then drop ;
> > : xarray        here ...constant create ,
> >                 does> dup @ rot cells +
> >                 tuck u<= abort" xarray outside bounds"
> >                 @ execute ;
>
> > : a ." A" ;
> > : b ." B" ;
> > : c ." C" ;
>
> > xarray a b c constant z
>
> > 0 z 1 z 2 z cr
>
> > 3 z
>
> That is an interesting use of recursion.  I have to say I find your
> use of EXIT inside the IF to be confusing.

OK. But it leaves the system the possibility to change the RECURSE
into a jump. Systems that do "tail call"-optimization should do this
by default in this situation. For ANS-Forth it's best not to implement
such things, since otherwise you come into problem with a lot of non-
standard programs that claim to be ANS-comformant but in reality they
depend on return stack manipulations that are not covered by ANS.

>  I know that Forth users
> tend to toss out most established ideas of structured programming, but
> I don't see where this deviation is at all useful.

Imagine it as a "jump" on some modern systems.

>  Wouldn't it be
> more clear and just as easy to use "IF , RECURSE ELSE DROP THEN"?  To
> me this is very easy to see that the two branches are equal in the
> usage of the stack which is a common cause of errors.

I made it as ANS comforming as the idea allowed.
What I did was to utilize 1/2 of the ideas of IF/THEN-less
programming. The idea behind is that words that need IF/ELSE/THEN are
usually not well factored.

> I don't understand how the u<= checks the range of the input
> parameter.

It checks only if the resulting pointer is above the boundary. But
that is 1/2 of the problem noticed by the system ;) Here it is visible
that it does only these checks.

>  It seems to be comparing the value of the PFA of the
> constant word to the calculated address of the element in the list to
> be executed.

PFA is no question here. Gforth will not work here for too small
values, since it will waste data space when defining a word.

>  That does not account for the header created by create,
> right?

Sure, why it should? The header is an implementation detail. Sure - I
should take that into account when claiming 100% ANS. But I do an
implementation that assumes data is for data and not for maintainance
of the system.

>  Also, by comparing two pointers as unsigned does not work when
> the calculated address wraps around the top of the number space.

Sure. This a always a problem of addition to pointers. I do not want
to make debugging tools bullet-proof. A lot of possible cases that
could be wrong are covered. That is all I wanted - negative values or
much too high values you can simply find by adding a .S to your source
while debugging it. Ever be aware that the checks are done on runtime
and should be as simple as possible. I choosed the simples one that
seems to show me enough errors.

>  That
> is one reason why I did the range check on the index and used WITHIN
> rather than u<=.

Be careful ;)

Regards,
-Helmar

rickman

unread,
Jan 21, 2009, 12:40:05 PM1/21/09
to

Actually it only checks if the calculated address falls in the range
above the pfa. A too large index (not unlikely if a stack error is
made) can give a result that wraps around the space and ends up below
the pfa.


> >  It seems to be comparing the value of the PFA of the
> > constant word to the calculated address of the element in the list to
> > be executed.
>
> PFA is no question here. Gforth will not work here for too small
> values, since it will waste data space when defining a word.

I don't know what this means. My point is that many implementations
will compile the xt data, then the header, then the pfa. Your code
won't work on these systems. It will only work if the pfa is just
past the end of the xt data.


> >  That does not account for the header created by create,
> > right?
>
> Sure, why it should? The header is an implementation detail. Sure - I
> should take that into account when claiming 100% ANS. But I do an
> implementation that assumes data is for data and not for maintainance
> of the system.

Yes, it is am implementation detail that will break your code if not
done in the "right" way. So the code won't work on many systems.
What system did you test it on?


> >  Also, by comparing two pointers as unsigned does not work when
> > the calculated address wraps around the top of the number space.
>
> Sure. This a always a problem of addition to pointers. I do not want
> to make debugging tools bullet-proof. A lot of possible cases that
> could be wrong are covered. That is all I wanted - negative values or
> much too high values you can simply find by adding a .S to your source
> while debugging it. Ever be aware that the checks are done on runtime
> and should be as simple as possible. I choosed the simples one that
> seems to show me enough errors.

I see what you are saying and if it required much to do a proper check
I would agree. But it is so simple to properly check the index using
WITHIN before using it to calculate the address. Then you don't have
to do anything to find the problem, the error check will flag it.

Your approach may be simpler, but a solution should be as simple as
possible and no simpler. ;^)


> >  That
> > is one reason why I did the range check on the index and used WITHIN
> > rather than u<=.
>
> Be careful ;)

Thanks for your comments. I have learned a bit (or relearned) from
this. I only use Forth once every couple of years and so I tend to
forget a lot of the details. I wish Win32Forth had a better
glossary. That could help a lot.

Rick

Helmar

unread,
Jan 21, 2009, 1:04:12 PM1/21/09
to
On Jan 21, 12:40 pm, rickman <gnu...@gmail.com> wrote:

Hi again,

> > It checks only if the resulting pointer is above the boundary. But
> > that is 1/2 of the problem noticed by the system ;) Here it is visible
> > that it does only these checks.
>
> Actually it only checks if the calculated address falls in the range
> above the pfa.  A too large index (not unlikely if a stack error is
> made) can give a result that wraps around the space and ends up below
> the pfa.

"below the pfa" - it checks if it falls below the "PFA" and then it
lets it pass. The error is always the other kind of thing that might
happen ;) Just for philosophical mindset.

> > >  It seems to be comparing the value of the PFA of the
> > > constant word to the calculated address of the element in the list to
> > > be executed.
>
> > PFA is no question here. Gforth will not work here for too small
> > values, since it will waste data space when defining a word.
>
> I don't know what this means.

It means I do not know what a PFA is? Can you show me the definition
in DPANS that is needed to implement ANS-Forth? It is not there. I see
http://maschenwerk.de/HelFORTH/DPANS/dpans3.htm#3.3.1 - there it is
left up to the implementation how this is organized. But since it's
fairly random how what needs to be - for example if EXECUTE is
disturbed by a THROW in case a word was not found - I think it's
random to do. The program might have some environmental dependencies -
and what? You can with low efforts implement the same thing and
fullfill ANS and ANS is: random in selection of the "fully ANS"-
system.

>  My point is that many implementations
> will compile the xt data, then the header, then the pfa.  Your code
> won't work on these systems.  It will only work if the pfa is just
> past the end of the xt data.

Hey, "PFA" is not even a part of the language here - CREATE gives you
inside DOES> ... what HERE was at the moment of CREATE. That is all
you know. You do not know about "PFA" or similar.

> > >  That does not account for the header created by create,
> > > right?
>
> > Sure, why it should? The header is an implementation detail. Sure - I
> > should take that into account when claiming 100% ANS. But I do an
> > implementation that assumes data is for data and not for maintainance
> > of the system.
>
> Yes, it is am implementation detail that will break your code if not
> done in the "right" way.  So the code won't work on many systems.

You've to learn that you'll do it right, if you've done it by
yourself. It's impossible to fullfill all systems requirements without
beeing a Lunatic.

> What system did you test it on?

Actually with the download from http://maschenwerk.de/4p/forum/index?msg_id=481
But I guess older version would work too.

> > >  Also, by comparing two pointers as unsigned does not work when
> > > the calculated address wraps around the top of the number space.
>
> > Sure. This a always a problem of addition to pointers. I do not want
> > to make debugging tools bullet-proof. A lot of possible cases that
> > could be wrong are covered. That is all I wanted - negative values or
> > much too high values you can simply find by adding a .S to your source
> > while debugging it. Ever be aware that the checks are done on runtime
> > and should be as simple as possible. I choosed the simples one that
> > seems to show me enough errors.
>
> I see what you are saying and if it required much to do a proper check
> I would agree.  But it is so simple to properly check the index using
> WITHIN before using it to calculate the address.  Then you don't have
> to do anything to find the problem, the error check will flag it.
>
> Your approach may be simpler, but a solution should be as simple as
> possible and no simpler.  ;^)

No, a solution is as simple as your system allows.

> > >  That
> > > is one reason why I did the range check on the index and used WITHIN
> > > rather than u<=.
>
> > Be careful ;)
>
> Thanks for your comments.  I have learned a bit (or relearned) from
> this.  I only use Forth once every couple of years and so I tend to
> forget a lot of the details.  I wish Win32Forth had a better
> glossary.  That could help a lot.

Hehe, my system has no glossary. If I would have more interested
people that help to make one (it's simply very complicated to write
one without user interaction), I'd do one. If you are able to read
german, I'll soon publish a book about the system and its history.

Regards,
-Helmar

Coos Haak

unread,
Jan 21, 2009, 1:43:02 PM1/21/09
to
Op Wed, 21 Jan 2009 09:17:04 -0800 (PST) schreef Helmar:


<snip>


> OK. But it leaves the system the possibility to change the RECURSE
> into a jump. Systems that do "tail call"-optimization should do this
> by default in this situation. For ANS-Forth it's best not to implement
> such things, since otherwise you come into problem with a lot of non-
> standard programs that claim to be ANS-comformant but in reality they
> depend on return stack manipulations that are not covered by ANS.

Huh?
A non-standard program is allowed to use return stack manipulations.
A standard program is not.
How can an ANS-conformant program be non-conformant?

Helmar

unread,
Jan 21, 2009, 3:12:30 PM1/21/09
to
On Jan 21, 1:43 pm, Coos Haak <chfo...@hccnet.nl> wrote:
> Op Wed, 21 Jan 2009 09:17:04 -0800 (PST) schreef Helmar:
>
> <snip>
>
> > OK. But it leaves the system the possibility to change the RECURSE
> > into a jump. Systems that do "tail call"-optimization should do this
> > by default in this situation. For ANS-Forth it's best not to implement
> > such things, since otherwise you come into problem with a lot of non-
> > standard programs that claim to be ANS-comformant but in reality they
> > depend on return stack manipulations that are not covered by ANS.
>
> Huh?
> A non-standard program is allowed to use return stack manipulations.
> A standard program is not.
> How can an ANS-conformant program be non-conformant?

You should know it better: a program claimed to be ANS-conformant is
something different than a program that *is* conformant. Same for
claims of conformance of systems. I would never claim something to be
conformant. I'd claim something is a solution. If the solution needs
fixes for some special or even alian cases does not matter if it does
the job. A standard of Forth can only something below the lines people
want to have. Everybody wants something else.

Regards,
-Helmar

rickman

unread,
Jan 21, 2009, 3:25:52 PM1/21/09
to
On Jan 21, 1:04 pm, Helmar <hel...@gmail.com> wrote:
> On Jan 21, 12:40 pm, rickman <gnu...@gmail.com> wrote:
>
> Hi again,
>
> > > It checks only if the resulting pointer is above the boundary. But
> > > that is 1/2 of the problem noticed by the system ;) Here it is visible
> > > that it does only these checks.
>
> > Actually it only checks if the calculated address falls in the range
> > above the pfa.  A too large index (not unlikely if a stack error is
> > made) can give a result that wraps around the space and ends up below
> > the pfa.
>
> "below the pfa" - it checks if it falls below the "PFA" and then it
> lets it pass. The error is always the other kind of thing that might
> happen ;) Just for philosophical mindset.

Ok, the check is to see whether the calculated address is above (or
equal to) or below the pfa. The point is that there are a number of
errors that this will not catch; ones where the index is above the
range of the entries and creates an address that wraps around the
address space and ones where the index xis less than zero. If you
treat the index as an usigned, then these are the same error. The
point is that it is very easy to check for all cases of error in the
index range. So there is not much advantage to reducing the utility
of the check.


> > > >  It seems to be comparing the value of the PFA of the
> > > > constant word to the calculated address of the element in the list to
> > > > be executed.
>
> > > PFA is no question here. Gforth will not work here for too small
> > > values, since it will waste data space when defining a word.
>
> > I don't know what this means.
>
> It means I do not know what a PFA is? Can you show me the definition

> in DPANS that is needed to implement ANS-Forth? It is not there. I seehttp://maschenwerk.de/HelFORTH/DPANS/dpans3.htm#3.3.1- there it is


> left up to the implementation how this is organized. But since it's
> fairly random how what needs to be - for example if EXECUTE is
> disturbed by a THROW in case a word was not found - I think it's
> random to do. The program might have some environmental dependencies -
> and what? You can with low efforts implement the same thing and
> fullfill ANS and ANS is: random in selection of the "fully ANS"-
> system.

Again, I don't understand your reply. I don't know what DPANS says
about the pfa or whatever it is called. The point is that you can't
count on your calculation matching the structure of the dictionary
while a test of the index *will* always work and is not any more
work.


> >  My point is that many implementations
> > will compile the xt data, then the header, then the pfa.  Your code
> > won't work on these systems.  It will only work if the pfa is just
> > past the end of the xt data.
>
> Hey, "PFA" is not even a part of the language here - CREATE gives you
> inside DOES> ... what HERE was at the moment of CREATE. That is all
> you know. You do not know about "PFA" or similar.

Exactly. That is why your approach will not work on some systems.
Your definition uses HERE *before* CREATE to mark a location before
the header is defined. If the header is defined in the same address
space as the data field, then it has to be accounted for in the
calculations. Allocating the data space after using CREATE eliminates
that issue.


> > > >  That does not account for the header created by create,
> > > > right?
>
> > > Sure, why it should? The header is an implementation detail. Sure - I
> > > should take that into account when claiming 100% ANS. But I do an
> > > implementation that assumes data is for data and not for maintainance
> > > of the system.
>
> > Yes, it is am implementation detail that will break your code if not
> > done in the "right" way.  So the code won't work on many systems.
>
> You've to learn that you'll do it right, if you've done it by
> yourself. It's impossible to fullfill all systems requirements without
> beeing a Lunatic.
>
> > What system did you test it on?
>

> Actually with the download fromhttp://maschenwerk.de/4p/forum/index?msg_id=481


> But I guess older version would work too.

I don't know what 4p is.

If I were another 10 years older I would read German. I was a chemist
and a one time all the famous chemists were German. So all students
of chemistry had to learn to read German in order to read the
important research publications. But that changed sometime in the
50's or 60's, IIRC. I was in college in the 70's.

Rick

Ian Osgood

unread,
Jan 21, 2009, 3:45:09 PM1/21/09
to
On Jan 17, 11:57 pm, rickman <gnu...@gmail.com> wrote:
> I have been searching for code on creating an array of constants.  I
> have done a little work with arrays using them like value variables.

> But I can't quite figure out how to make an array of constants without
> being wordy using , to put values into the definition.

I find "," to define constant arrays and structures to be one of the
least wordy things about Forth. I guess this is a matter of taste.

> Also, one of


> the constant array types I want to make is an array of words to
> execute.

One mechanism I have used is called "vector".

: vector
create ( n -- ) 0 do , loop
does> ( n -- ) swap cells + @ execute ;

which takes n XTs on the stack when created. The XTs can be a mix
of :nonames and ticked words.
An example of its use for printing roman numerals is here:

http://www.rosettacode.org/wiki/Roman_Numerals#Forth

In my chess program, I just used CREATE raw. Partly because I was
unfamiliar with CREATE-DOES> at the time and partly because regular
create and tick syntax is already pretty clear. (This mechanism can't
work with :noname.)

create gen
' noop , ' genP , ' genN , ' genB , \ etc.

... ( piece ) cells gen + @ execute ...

Ian

jacko

unread,
Jan 21, 2009, 4:33:38 PM1/21/09
to
Hi

CFA = Code Field Address
PFA = Parameter field Address
I also sometimes use DFA = Do Does Field Address

The code field address is the address that an xt useually points to,
and it contains a pointer to machine code on ITC threading models, and
actual machine code on DTC threading models.

The parameter field address is the address (usuually following) that
contains the parameter e.g. constants value. And is the address place
on stack by DOES>.

The do does field address sometimes follows the CFA to make CREATE
DOES> easier to implement, but it does use one extra cell per
definition, even when there is no DOES>.

So in ITC we have CFA DFA PFA cell order for simplist implementation.
This ordering is not fixed by any standard document, but fixed by
certain well used 'standard' fast (in terms of boot-strapping
(bringing a compiler to new hardware)) methods of implementation. I
hope this clarification helps.

cheers jacko

Helmar

unread,
Jan 21, 2009, 5:52:25 PM1/21/09
to
On Jan 21, 3:25 pm, rickman <gnu...@gmail.com> wrote:

> Exactly.  That is why your approach will not work on some systems.
> Your definition uses HERE *before* CREATE to mark a location before
> the header is defined.

Yes, that is intentionally.

>  If the header is defined in the same address
> space as the data field, then it has to be accounted for in the
> calculations.

If X happens, Y would be true - same argument as mine.

>  Allocating the data space after using CREATE eliminates
> that issue.

But that cripples the possibilities to use the language in a terse
way. Be sure it would be possible to implement what I wrote and to
skip the maintainance informations some informations pollute into the
data space ;) This is simply one more @ at least and a little bit CELL
+ magic.

Regards,
-Helmar


Ed

unread,
Jan 21, 2009, 11:32:05 PM1/21/09
to
> ...

Are they not complex systems? Have they found FORGET prohibitive
to implement?

> A Forth with separate headers, code and data has three dictionary
> pointers, and if FORGET is going to peel back all three of them it has
> to know, for every word in the dictionary, the values of those
> pointers before a definition was created. While it is _possible_ to
> do that, the TC didn't see fit to require it; eminently sensible IMO.

I don't understand that. I have a forth with the dictionary in three
segments. MARKER isn't any easier to implement than FORGET.
The notion that MARKER can save/restore a few easily-got pointers
and bingo! the system is trimmed sounds nice. Unfortunately I've
never seen such a forth. I asked in an earlier post and apparently
no-one else has either. It may just be a myth :)

rickman

unread,
Jan 22, 2009, 1:53:36 AM1/22/09
to

It seems pretty clear to me that MARKER is easier to implement than
FORGET. With MARKER any state that needs to be restored is saved in
the word defined with MARKER. But FORGET has no opportunity to save
context. It has to restore a system just by finding a word in the
dictionary and deleting it and everything after. I guess it is more
accurate to say that MARKER is easier to implement so that it works
well than FORGET.

Rick

Andrew Haley

unread,
Jan 22, 2009, 6:33:25 AM1/22/09
to

Yes.

> Have they found FORGET prohibitive to implement?

Well, they've done it: I don't know how much pain it caused. Clearly
a complex system more easily absorbs the pain of the work required to
make this happen than a simple one, and it's not just the complex
systems that have to deal with multiple address spaces.

> > A Forth with separate headers, code and data has three dictionary
> > pointers, and if FORGET is going to peel back all three of them it has
> > to know, for every word in the dictionary, the values of those
> > pointers before a definition was created. While it is _possible_ to
> > do that, the TC didn't see fit to require it; eminently sensible IMO.

> I don't understand that. I have a forth with the dictionary in three
> segments. MARKER isn't any easier to implement than FORGET.
> The notion that MARKER can save/restore a few easily-got pointers
> and bingo! the system is trimmed sounds nice.

How are they easily got? Would you have *every* definition remember
the values of all dictionary pointers at the time they were created.

> Unfortunately I've never seen such a forth. I asked in an earlier
> post and apparently no-one else has either. It may just be a myth
> :)

They exist. Heh, I even wrote one: 8051 chipFORTH has HERE on the
host, HERE, THERE, and IHERE on the target. Sure, it would have been
possible to record target HERE, THERE, and IHERE for every word just
in case someone wanted to FORGET it, but it's extra code for no
reason.

Andrew.

Bernd Paysan

unread,
Jan 22, 2009, 7:50:27 AM1/22/09
to
Andrew Haley wrote:
>> I don't understand that. I have a forth with the dictionary in three
>> segments. MARKER isn't any easier to implement than FORGET.
>> The notion that MARKER can save/restore a few easily-got pointers
>> and bingo! the system is trimmed sounds nice.
>
> How are they easily got? Would you have *every* definition remember
> the values of all dictionary pointers at the time they were created.

You don't do that. The definition has a dictionary pointer all by itself.
You throw away all definitions that were defined *after* that definition,
plus those that were defined in wordlists created later. That works
reasonably well. MARKER in Gforth is not so much less complicated than
FORGET in bigFORTH, the one central loop that goes through lists to find
what to keep and what to forget is not a big deal.

In some cases, FORGET doesn't produce the same result as MARKER. E.g. if you
have two separate dictionaries in two separate wordlists, and you call
FORGET for a word in one of them, the other dictionary and wordlist will
remain untouched.

Andrew Haley

unread,
Jan 22, 2009, 8:57:24 AM1/22/09
to
Bernd Paysan <bernd....@gmx.de> wrote:
> Andrew Haley wrote:
> >> I don't understand that. I have a forth with the dictionary in three
> >> segments. MARKER isn't any easier to implement than FORGET.
> >> The notion that MARKER can save/restore a few easily-got pointers
> >> and bingo! the system is trimmed sounds nice.
> >
> > How are they easily got? Would you have *every* definition remember
> > the values of all dictionary pointers at the time they were created.

> You don't do that. The definition has a dictionary pointer all by
> itself. You throw away all definitions that were defined *after*
> that definition, plus those that were defined in wordlists created
> later.

That doesn't address the issue at all. What about all the other
dictionary pointers?

Andrew.

Bernd Paysan

unread,
Jan 22, 2009, 10:09:20 AM1/22/09
to
Andrew Haley wrote:
>> You don't do that. The definition has a dictionary pointer all by
>> itself. You throw away all definitions that were defined *after*
>> that definition, plus those that were defined in wordlists created
>> later.
>
> That doesn't address the issue at all. What about all the other
> dictionary pointers?

Don't be so thick ;-). You walk the wordlist until you find the definition
to forget - while doing so, you have to keep track of dictionary pointers
in other dictionaries, too. E.g. I define something like this:

ram : foo ." Hello!" ;
rom : bar ." Goodbye" ;

and then I say

forget foo

Now this means I have to forget both foo and bar - and when I do that for
bar, I have to set the ROM dictionary pointer back, because bar is in ROM,
while foo is in RAM.

The only possibility when this fails is when I can't find out which one has
been defined first, like here

vocabulary ram-stuff
vocabulary rom-stuff

ram ram-stuff definitions : foo ." Hello!" ;
rom rom-stuff definitions : bar ." Goodbye" ;

forget foo

In this case I find only one wordlist (or vocabulary) with a definition late
or later than foo, that's ram-stuff. rom-stuff has no words in a wordlist
later than foo, so the system can't decide in which order foo and bar have
been defined.

Andrew Haley

unread,
Jan 22, 2009, 10:46:22 AM1/22/09
to
Bernd Paysan <bernd....@gmx.de> wrote:
> Andrew Haley wrote:
> >> You don't do that. The definition has a dictionary pointer all by
> >> itself. You throw away all definitions that were defined *after*
> >> that definition, plus those that were defined in wordlists created
> >> later.
> >
> > That doesn't address the issue at all. What about all the other
> > dictionary pointers?

> Don't be so thick ;-).

Eh? Even with the smiley that's out of line.

> You walk the wordlist until you find the definition to forget -
> while doing so, you have to keep track of dictionary pointers in
> other dictionaries, too. E.g. I define something like this:

> ram : foo ." Hello!" ;
> rom : bar ." Goodbye" ;

> and then I say

> forget foo

What about all the memory spaces? There's a number of memory spaces
in use, and you need to peel back all of them. For example, say you
have an internal fast memory space allocated with IHERE and IALLOT.
When you want some internal memory, you just say

IHERE 4 CELLS ALLOT CONSTANT BUF

Someone then says

FORGET FOO

How can you know the value of IHERE when FOO was compiled? It's not
worth keeping extra houskeeping data just for FORGET. With MARKER,
you snapshot all of the memory pointers. The same applies for user
variables and +USER, etc...

Andrew.

rickman

unread,
Jan 22, 2009, 12:28:38 PM1/22/09
to
On Jan 22, 10:46 am, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:

Doesn't FOO have a pointer to the part of IHERE space that was alloted
for it? The pointer to the empty space can be adjusted to this value,
no? If FOO doesn't have a pointed into the IHERE space, then how is
that memory used?

I think the real issue with FORGET vs. MARKER is that whatever the
system does when words are compiled can be remembered with MARKER
since it is implemented to do whatever is needed. But FORGET is
stated to only roll back the dictionary. What happens if you FORGET a
portion of the dictionary that is in use by interrupt code or
something similar? MARKER can be smart enough to disable those
interrupts and not crash the system. Or maybe the FORGOTten code
contains some words that were hooked into some processing of the
keyboard or display? Same thing, MARKER can unhook before it
forgets.

Rick

jacko

unread,
Jan 22, 2009, 1:01:14 PM1/22/09
to
hi

FORGET should find the word supplied, and then nest search to find all
vocabularies defined before this word. All vocabulary lastword
pointers of these pre-vocabs should be reset to point to the the word
latest defined in the vocabulary, which is defined before the word to
be forgot. The definitions dictionary and search dictionary should be
set to values that pop the search order of any later vocabulary. The
system last work pointer should be set to the word that forget first
found.

This has a vocabulary side effect of maybe changing the current search
and definitions vocabularies and that is all. You should have the same
free memory as you had just before the word forget first found was
defined.

Vocabularies complicate forget, by causing the need for a nested
context search. If you leave any definitions on active search chains
which are defined after the forgotten word, an eventual crash when
more definitions are made will reseult. FORGET is pre-vocabulary
words, and used to simply mean set HERE and the system last definition
pointer.

cheers jacko

jacko

unread,
Jan 22, 2009, 1:11:22 PM1/22/09
to
: FORGET ABORT" Yes. Most DEFINATLY. Use MARKER. Ooh You FORGOT. Silly
Ass." ; \ now that wasn't too hard to define.

Andrew Haley

unread,
Jan 22, 2009, 1:15:32 PM1/22/09
to
rickman <gnu...@gmail.com> wrote:
> On Jan 22, 10:46?am, Andrew Haley <andre...@littlepinkcloud.invalid>

> wrote:
> > Bernd Paysan <bernd.pay...@gmx.de> wrote:
> > > Andrew Haley wrote:
> > > >> You don't do that. The definition has a dictionary pointer all by
> > > >> itself. ?You throw away all definitions that were defined *after*

> > > >> that definition, plus those that were defined in wordlists created
> > > >> later.
> >
> > > > That doesn't address the issue at all. ?What about all the other

> > > > dictionary pointers?
> > > Don't be so thick ;-).
> >
> > Eh? ?Even with the smiley that's out of line.

> >
> > > You walk the wordlist until you find the definition to forget -
> > > while doing so, you have to keep track of dictionary pointers in
> > > other dictionaries, too. E.g. I define something like this:
> > > ram : foo ." Hello!" ;
> > > rom : bar ." Goodbye" ;
> > > and then I say
> > > forget foo
> >
> > What about all the memory spaces? ?There's a number of memory spaces
> > in use, and you need to peel back all of them. ?For example, say you

> > have an internal fast memory space allocated with IHERE and IALLOT.
> > When you want some internal memory, you just say
> >
> > ? IHERE ?4 CELLS ALLOT ?CONSTANT BUF
^ IALLOT

> >
> > Someone then says
> >
> > ? FORGET FOO
> >
> > How can you know the value of IHERE when FOO was compiled? ?It's
> > not worth keeping extra houskeeping data just for FORGET. ?With
> > MARKER, you snapshot all of the memory pointers. ?The same applies


> > for user variables and +USER, etc...

> Doesn't FOO have a pointer to the part of IHERE space that was
> alloted for it?

No, of course not: FOO is just a colon definition, in code space. It
doesn't use IHERE space at all. On a microcontroller, IHERE space may
well only be 128 bytes in total. It's very precious: you really do
need to make sure it gets recycled.

> The pointer to the empty space can be adjusted to this value, no?
> If FOO doesn't have a pointed into the IHERE space, then how is that
> memory used?

IHERE 4 CELLS IALLOT CONSTANT BUF

BUF is now a buffer, to be used in the usual way.

> I think the real issue with FORGET vs. MARKER is that whatever the
> system does when words are compiled can be remembered with MARKER
> since it is implemented to do whatever is needed. But FORGET is
> stated to only roll back the dictionary. What happens if you FORGET
> a portion of the dictionary that is in use by interrupt code or
> something similar? MARKER can be smart enough to disable those
> interrupts and not crash the system. Or maybe the FORGOTten code
> contains some words that were hooked into some processing of the
> keyboard or display? Same thing, MARKER can unhook before it
> forgets.

That too.

Andrew.

Elizabeth D Rather

unread,
Jan 22, 2009, 3:07:18 PM1/22/09
to

Yeah, and what do you do about interrupts and vectors (DEFERs and
similar) that were set to words that are now gone?

That's the kind of problem that MARKER and its cousins can handle.

Cheers,
Elizabeth

--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================

Albert van der Horst

unread,
Jan 22, 2009, 3:41:46 PM1/22/09
to
In article <ab29bcd0-fa59-455d...@d32g2000yqe.googlegroups.com>,
rickman <gnu...@gmail.com> wrote:

>On Jan 21, 11:32=A0pm, "Ed" <nos...@invalid.com> wrote:
>> Andrew Haley wrote:
>> > Ed <nos...@invalid.com> wrote:
>> > > Elizabeth D Rather wrote:
>> > > > Ed wrote:
>> > > > > Alex McDonald wrote:
>> > > > >> ...
>> > > > >> FORGET is extremely difficult to implement on a modern OS. Use M=
>ARKER
>> > > > >> instead.
>> > > > >> ...
>>
>> > > > > That claim was made in the ANS rationale A.6.2.1850. =A0I've yet =

>to
>> > > > > see a case where FORGET was either onerous to implement, or
>> > > > > more complicated than MARKER. =A0Has anyone?

>>
>> > > > > It's quite possible that MARKER is a better word simply because
>> > > > > it does more.
>>
>> > > > The issue is how much system state do you want to restore? =A0FORGE=
>T just
>> > > > cuts back the dictionary. =A0Depending on the platform & implementa=
>tion,
>> > > > it may be necessary to reset some other pointers (e.g. to code bein=
>g
>> > > > deleted), interrupt handlers or call-backs, wordlist info, etc. =A0=
>MARKER
>> > > > provides an opportunity to save necessary state information so you =
>can
>> > > > restore not only the dictionary but also other state info to a know=

>n
>> > > > condition.
>>
>> > > No, the issue is that ANS claimed FORGET was prohibitive to implement
>> > > on modern systems.
>>
>> > > Win32Forth and VFX (two modern complex systems) have elected to
>> > > provide both FORGET and MARKER, and so calls the claim into
>> > > question.
>>
>> > Illogical, captain. =A0The fact that Win32Forth and VFX do this doesn't

>> > call anything into question.
>> > ...
>>
>> Are they not complex systems? =A0Have they found FORGET prohibitive

>> to implement?
>>
>> > A Forth with separate headers, code and data has three dictionary
>> > pointers, and if FORGET is going to peel back all three of them it has
>> > to know, for every word in the dictionary, the values of those
>> > pointers before a definition was created. =A0While it is _possible_ to

>> > do that, the TC didn't see fit to require it; eminently sensible IMO.
>>
>> I don't understand that. =A0I have a forth with the dictionary in three
>> segments. =A0MARKER isn't any easier to implement than FORGET.

>> The notion that MARKER can save/restore a few easily-got pointers
>> and bingo! the system is trimmed sounds nice. =A0Unfortunately I've
>> never seen such a forth. =A0I asked in an earlier post and apparently
>> no-one else has either. =A0It may just be a myth :)

>
>It seems pretty clear to me that MARKER is easier to implement than
>FORGET. With MARKER any state that needs to be restored is saved in
>the word defined with MARKER. But FORGET has no opportunity to save
>context. It has to restore a system just by finding a word in the
>dictionary and deleting it and everything after. I guess it is more
>accurate to say that MARKER is easier to implement so that it works
>well than FORGET.

On simple system it is a draw, e.g. lina.

WANT MARKER SEE MARKER

: MARKER
HERE CREATE , DOES> @ 'FORGET-VOC FOR-VOCS DP !
;
OK
SEE FORGET

: FORGET
POSTPONE ' DUP FENCE @ < 15 ?ERROR 'FORGET-VOC FOR-VOCS >NFA @ DP !
;


>
>Rick

Groetjes Albert

--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- like all pyramid schemes -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Alex McDonald

unread,
Jan 22, 2009, 4:33:58 PM1/22/09
to
On Jan 21, 2:17 pm, stephen...@mpeforth.com (Stephen Pelc) wrote:

> On Thu, 22 Jan 2009 00:13:58 +1100, "Ed" <nos...@invalid.com> wrote:
> >No, the issue is that ANS claimed FORGET was prohibitive to implement
> >on modern systems.
>
> >Win32Forth and VFX (two modern complex systems) have elected to
> >provide both FORGET and MARKER, andso calls the claim into question.
>
> In VFX Forth, FORGET is documented as obsolescent. It was provided
> to ease porting of applications from other Forths. It's probably
> time to retire this word.
>
> The real point is that just unhooking the dictionary chains is not
> enough to restore all state. Most "big" Forths permit you to add
> to the behaviour of MARKERs (e.g. PRUNE and REMEMBER) so that the
> application programmer can add save/restore actions.
>
> The design of MARKER is fine. The real problem is that, in my
> experience, most application programmers do not implement enough
> additional behaviour, and so just restart the application -
> it's quicker than thinking.
>
> There is very little call for FORGET or MARKER in embedded systems
> except for web scripting.
>
> Stephen
>
> --
> Stephen Pelc, stephen...@mpeforth.com
> MicroProcessor Engineering Ltd - More Real, Less Time
> 133 Hill Lane, Southampton SO15 5AF, England
> tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
> web:http://www.mpeforth.com- free VFX Forth downloads

To follow on from Ed's comment (and apologies for taking so long to
reply), my Win32Forth code has

\ NOTE
\ 1) Forget no longer works.
\ 2) Markers now reset the user area.

Other version note

\ FORGET limitations:
\ 1) You cannot directly forget an IN-SYSTEM word since
\ it would require extra work to determine the new DP.


FORGET looks easy to code, but the end result with Win32Forth is
usually an unstable system in an inconsistent state once it has been
executed.

--
Regards
Alex McDonald

George Hubert

unread,
Jan 22, 2009, 5:53:07 PM1/22/09
to
> > web:http://www.mpeforth.com-free VFX Forth downloads

>
> To follow on from Ed's comment (and apologies for taking so long to
> reply), my Win32Forth code has
>
> \ NOTE
> \ 1) Forget no longer works.
> \ 2) Markers now reset the user area.

That's the STC version

> Other version note

i.e. the ITC version

> \ FORGET limitations:
> \ 1) You cannot directly forget an IN-SYSTEM word since
> \ it would require extra work to determine the new DP.
>

Also (since V6.07) you cannot directly forget a code
definition (they throw an error the same as words below
the variable FENCE). Also when forgetting or executing
a marker only the names of code words are removed, the
definitions then becoming anonymous, since the code
pointer is never reset.

> FORGET looks easy to code, but the end result with Win32Forth is
> usually an unstable system in an inconsistent state once it has been
> executed.
>
> --
> Regards
> Alex McDonald

George Hubert

Ed

unread,
Jan 23, 2009, 12:54:18 AM1/23/09
to

Before including Win32F in my comment I had a quick browse of
the source (ITC) and it looked like FORGET and MARKER shared
essentially the same code. A SEE of VFX's words revealed it does
much the same. As does my forth.

I presume VFX's FORGET isn't broken. Also I've had no issues
with FORGET in my forth which has the dictionary split over three
distinct segments. While two examples may not be definitive
proof that FORGET is do-able on all systems, it is suggestive.
However since FORGET has been declared obsolete, many forths
won't bother supporting it.

For myself, I rarely use FORGET or MARKER - but implementing
them was an experience! :)


Ed

unread,
Jan 23, 2009, 1:53:17 AM1/23/09
to
Stephen Pelc wrote:
> On Thu, 22 Jan 2009 00:13:58 +1100, "Ed" <nos...@invalid.com> wrote:
>
> >No, the issue is that ANS claimed FORGET was prohibitive to implement
> >on modern systems.
> >
> >Win32Forth and VFX (two modern complex systems) have elected to
> >provide both FORGET and MARKER, andso calls the claim into question.
>
> In VFX Forth, FORGET is documented as obsolescent. It was provided
> to ease porting of applications from other Forths. It's probably
> time to retire this word.

When MPE eventually discards FORGET it will save maybe 30 bytes
plus header. A trivial amount. For other systems it's likely to be similar.

The point I wish to make - which most respondents have done their
best to evade - is that FORGET was never the hard-to-implement
monster that ANS made it out to be. Nor was there any evidence
that MARKER would be any easier.

TC's are in the position of being able to introduce words that never
previously existed. They have a responsibility to state their claims
in a fair and balanced manner. It's something I consciously try to do.

If the ANS assertions are false or misleading then let's clear the
record and move on - instead of propagating a myth.

> The design of MARKER is fine. The real problem is that, in my
> experience, most application programmers do not implement enough
> additional behaviour, and so just restart the application -
> it's quicker than thinking.

If it's quicker than thinking and no real downside, what's wrong
with that :)

MARKER and FORGET are system tools and probably don't
deserve to be in the Standard. Apps aren't likely to use them.

In these days of cheap memory, fast desktops and bloated forths,
who needs fancy dictionary trimming tools? What do they save?
The most useful word I know is EMPTY. It gets things back to
a clean state ready for a recompile. No need to dream up names
for FORGET, MARKER or ANEW. Just type EMPTY and the
job is done.

If during app development, system vectors change and EMPTY
would cause a crash then I can do a COLD or reboot forth.
I do have a "prunes" list, which I can add to and runs when the
dictionary is trimmed, but I only use it under exceptional
circumstances. The easier course usually is: "don't".

Andrew Haley

unread,
Jan 23, 2009, 4:15:31 AM1/23/09
to
Ed <nos...@invalid.com> wrote:

> The point I wish to make - which most respondents have done their
> best to evade - is that FORGET was never the hard-to-implement
> monster that ANS made it out to be. Nor was there any evidence that
> MARKER would be any easier.

Several people, me included, have patiently explained to you why
FORGET was problematic and you have either ignored them or dismissed
without justification what they said. You have also grossly
misrepresented what ANS actually wrote, which was

"FORGET assumes that all the information needed to restore the
dictionary to its previous state is inferable somehow from the
forgotten word. While this may be true in simple linear dictionary
models, it is difficult to implement in other Forth systems; e.g.,
those with multiple address spaces. For example, if Forth is embedded
in ROM, how does FORGET know how much RAM to recover when an array is
forgotten? A general and preferred solution is provided by MARKER."

This paragraph is perfectly clear to me. With definitions in ROM and
variables, arrays etc in RAM, a Forth has no way to know how much RAM
to recover when a word is forgotten unless extra data structures are
created just for FORGET to use.

Andrew.

Bernd Paysan

unread,
Jan 23, 2009, 4:28:07 AM1/23/09
to
Elizabeth D Rather wrote:
> Yeah, and what do you do about interrupts and vectors (DEFERs and
> similar) that were set to words that are now gone?

And you do that in MARKER? We don't. Standard programs can't rely on that
kind of behavior. MARKER is just for the dictionary, like FORGET, and not
for all states of the system.

Aleksej Saushev

unread,
Jan 23, 2009, 5:26:34 AM1/23/09
to
"Ed" <nos...@invalid.com> writes:

> The point I wish to make - which most respondents have done their
> best to evade - is that FORGET was never the hard-to-implement
> monster that ANS made it out to be. Nor was there any evidence
> that MARKER would be any easier.

Sure, patch work is simpler to implement than right thing.

>> The design of MARKER is fine. The real problem is that, in my
>> experience, most application programmers do not implement enough
>> additional behaviour, and so just restart the application -
>> it's quicker than thinking.
>
> If it's quicker than thinking and no real downside, what's wrong
> with that :)
>
> MARKER and FORGET are system tools and probably don't
> deserve to be in the Standard. Apps aren't likely to use them.

I strongly disagree.


--
HE CE3OH...

Helmar

unread,
Jan 23, 2009, 5:58:54 AM1/23/09
to
On Jan 23, 5:26 am, Aleksej Saushev <a...@inbox.ru> wrote:

> > MARKER and FORGET are system tools and probably don't
> > deserve to be in the Standard.  Apps aren't likely to use them.
>
> I strongly disagree.

I strongly disagree.
MARKER and FORGET are something like an author of a book would write
somewhere:
"Sorry, people. The last chapter was written by accident. Please
forget. I was that proud of my expressions that I did not want to take
from the print, but be sure it does not help my story."

If MARKER is about doing a systems task and then forget about the
implementation, I do not understand why not using an instance of the
implementation to do it (like with fork() or similar). Well, in some
systems this might not be possible, these might need something like
MARKER and FORGET, but I too do not see why it needs to be written in
the standard.

Regards,
-Helmar

Aleksej Saushev

unread,
Jan 23, 2009, 6:44:44 AM1/23/09
to
Helmar <hel...@gmail.com> writes:

> On Jan 23, 5:26 am, Aleksej Saushev <a...@inbox.ru> wrote:
>
>> > MARKER and FORGET are system tools and probably don't
>> > deserve to be in the Standard.  Apps aren't likely to use them.
>>
>> I strongly disagree.
>
> I strongly disagree.
> MARKER and FORGET are something like an author of a book would write
> somewhere:
> "Sorry, people. The last chapter was written by accident. Please
> forget. I was that proud of my expressions that I did not want to take
> from the print, but be sure it does not help my story."

"Really, people. The last chapter was written that way to make
things clear, in practice it is better to follow another way,
e.g. use precomputed results."

> If MARKER is about doing a systems task and then forget about the
> implementation, I do not understand why not using an instance of the
> implementation to do it (like with fork() or similar). Well, in some
> systems this might not be possible, these might need something like
> MARKER and FORGET, but I too do not see why it needs to be written in
> the standard.

Because it is very useful and enough common to be standard tool.

Evey underspecified standard leads to major pains in future.
For example take what happened to C and Scheme, where you have all sort
of compatibility problems due to so-called "simplicity of design".


--
HE CE3OH...

Helmar

unread,
Jan 23, 2009, 7:06:32 AM1/23/09
to
On Jan 23, 6:44 am, Aleksej Saushev <a...@inbox.ru> wrote:
> Helmar <hel...@gmail.com> writes:
> > On Jan 23, 5:26 am, Aleksej Saushev <a...@inbox.ru> wrote:
>
> >> > MARKER and FORGET are system tools and probably don't
> >> > deserve to be in the Standard.  Apps aren't likely to use them.
>
> >> I strongly disagree.
>
> > I strongly disagree.
> > MARKER and FORGET are something like an author of a book would write
> > somewhere:
> > "Sorry, people. The last chapter was written by accident. Please
> > forget. I was that proud of my expressions that I did not want to take
> > from the print, but be sure it does not help my story."
>
> "Really, people. The last chapter was written that way to make
> things clear, in practice it is better to follow another way,
> e.g. use precomputed results."

Why not simply keep the later unused things? I know for example the mm-
rtcg benchmark does a lot of compilations depending on data to process
something (but does not use MARKER imho). Such uses of a system might
be OK for some tasks, but usually I do not see much sense in it.
Especially when only NONAMES are involved, there would be simpler
solutions than to access namespace. Imagine hashed names: in this case
you've to invalidate the particular hash-structures for the later
defined words. Or use new hashes after MARKER. Or better: you've to
recalculate the hashes - what a mess.

> > If MARKER is about doing a systems task and then forget about the
> > implementation, I do not understand why not using an instance of the
> > implementation to do it (like with fork() or similar). Well, in some
> > systems this might not be possible, these might need something like
> > MARKER and FORGET, but I too do not see why it needs to be written in
> > the standard.
>
> Because it is very useful and enough common to be standard tool.

I do not think it's very useful at all. At least if your system
provides something like fork() or similar.

> Evey underspecified standard leads to major pains in future.

The MARKER word is not that interesting.

> For example take what happened to C and Scheme, where you have all sort
> of compatibility problems due to so-called "simplicity of design".

And there are no compatiblity problems in FORTH? Bah.

-Helmar

Dennis

unread,
Jan 23, 2009, 7:08:10 AM1/23/09
to

I would contend that a non-extensible MARKER is just as broken as
FORGET ever was. As has been pointed out here, the basic
implementation of MARKER is nearly identical to FORGET. The
difference is that MAKER can be implemented to be extended.
Unfortunately, the standard was written when there was not much know
about how those extensions would be implemented and thus, the
extensions are not standard, and perhaps, since the extensions are
system specific, they can not be standardized.

DaR

Bernd Paysan

unread,
Jan 23, 2009, 7:39:32 AM1/23/09