(10-06-2021 11:35 PM)Helix Wrote: [ -> ]I've noticed that if there is an error in a definition, the corresponding word cannot be deleted from the dictionary with FORGET. I have to delete the previous valid definition if I want to get rid of this false entry. Is this normal?
Good point! Yep, this is normal. A word cannot be found (with FORGET, ' tick etc.) if it is hidden. To unhide and delete the last definition, use REVEAL then FORGET. Incomplete definitions are hidden to prevent accidentally running them, which would lead to a crash obviously. WORDS still shows all hidden words but FORGET can't find them.
FORGET implementations in Forth may slightly differ in this respect, but FORGET is considered
obsolescent by the standard anyway. However, FORGET is still very useful as an easy way to redo a definition interactively. In general, placing a MARKER is preferred to delete code (see MARKER and ANEW). FORGET was removed by Sébastien from his pceForth (it was commented out). I rewrote parts of it to correct a bug in FORGET that caused a dictionary memory leak.
To go back to the question about improving error reporting, the word that caused the exception should be shown to the user with some context. This should be easy to implement by changing the last part of (ERROR) to report location of the error on the line by showing the line up to and including the word that caused the error:
SOURCE >IN @ UMIN TYPE ." << exception #" S>D (D.) TYPE
where SOURCE returns a pointer and size to the input buffer (TIB or FIB) and >IN is the location in this buffer of the next word after the last word executed. This will report the error in user input and in source files (albeit without a line number alas).
Closing a file after INCLUDE should be done as follows by catching INTERPRET exceptions in a new definition of INCLUDE-FILE:
Code:
: INCLUDE-FILE
save-input n>r
to source-id
begin
refill
while
['] interpret catch ?dup if
source-id close-file drop
n>r restore-input drop
throw
then
repeat
source-id close-file drop
nr> restore-input drop
This new definition uses updated SAVE-INPUT and RESTORE-INPUT combined with N>R and NR>, making the code of INCLUDE-FILE and EVALUATE more compact, thus saving some memory.
(10-06-2021 11:35 PM)Helix Wrote: [ -> ]I'm too in favor of simple systems. When I tried different Forth packages some years ago, I found that F-PC Forth has 1523 words! I think this defeats the purpose of Forth.
Adding optional definitions for those who are interested is a better solution.
Wow. 1523 words is way too much and unnecessary for most applications. However, at least we should incorporate the most useful standard word sets in Forth500 and include E500-specific words for graphics, sound and the file system. It is always possible to load extra words from source files.
I'd like to add that at this point anyone interested in this project can suggest improvements and additions to the Forth500 core, as long as there is sufficient space for user programs. Placing the additions in source files to load on demand is probably best.
As a note about standard Forth compliance, I noticed that there is no
REQUIRE and
REQUIRED implemented yet in Forth500. So I came up with the following quick-and-dirty implementation that simply stores the filename in the dictionary with a trailing blank in the name:
Code:
: REQUIRED
which-pocket dup>r swap dup>r cmove
bl 2r@ + c!
2r@ 1+ find-word nip if r>drop r>drop exit then
2r@ included
2r> 1+ (created) ;
: REQUIRE
parse-name required ;
Adding a trailing blank means that the filename word in the dictionary cannot clash with other words and cannot even be executed by accident. This word will be added after the file was successfully INCLUDED. ANEW and MARKER in the loaded file will also delete the filename, thus the next REQUIRE will load the file again if it was deleted from memory. It's a bit of a hack, but should work I believe. Note that the code above uses a new built-in system word (CREATED), which I added as a replacement of (LINK) and (NAME).
Updating the code and testing all of the additions and improvements will take a bit of time, but hopefully not too long.
I also would like to run the NQUEENS benchmark as suggested by xerxes. To this end, based on my understanding of the NQUEENS benchmarks I changed the NQUEENS Forth code slightly to use "nicer" standard Forth constructs, such as VALUE and by using POSTPONE to compile inlined versions of RCLAA and STOAA instead of calling them (disclaimer: this is yet untested on my end):
Code:
ANEW _NQUEENS_
8 CONSTANT RR
0 VALUE SS
0 VALUE XX
0 VALUE YY
CREATE AA RR 1+ ALLOT
: RCLAA POSTPONE AA POSTPONE + POSTPONE C@ ; IMMEDIATE
: STOAA POSTPONE AA POSTPONE + POSTPONE C! ; IMMEDIATE
: NQCORE
0 TO SS
0 TO XX
BEGIN
1 +TO XX RR XX STOAA
BEGIN
1 +TO SS
XX TO YY
BEGIN YY 1 > WHILE
-1 +TO YY
XX RCLAA YY RCLAA - DUP
0= SWAP ABS XX YY - = OR IF
0 TO YY
BEGIN XX RCLAA 1- DUP XX STOAA 0= WHILE
-1 +TO XX
REPEAT
THEN
REPEAT
YY 1 = UNTIL
RR XX = UNTIL
;
: NQUEENS
( STARTTIMER )
NQCORE
." S=" SS
( DISPLAYTIMER ) CR
;
NQCORE should be run in a loop to execute multiple times to get a manual stopwatch timing (the E500 has no RTC or system clock).
There are still a few speed and code size optimizations to make in Forth500, which can affect this benchmark. Safety versus performance is an important consideration. For example, I opted to test for the BREAK key (+15 CPU cycles) and to check stack overflows (+19 CPU cycles) but only when necessary and not too frequently. BREAK key tests are only done when a colon definition is called and in loops. Stack overflow checks are done only in loops and when interpreting Forth code from an input source to keep the overhead low. I also reduced the overhead of stack checking to just 19 cycles with some coding tricks. Removing these tests speeds things up, but at the cost of possible runaway programs when a coding mistake was made.
Inspiration for picking up pceForth to create an updated version Forth500 came from SuperForth and Forth for HP-71B. Back in the 80s my first encounter and tryout with Forth was
SuperForth for the QL by Garry Jackson. I learned the language, studied the Forth implementation in detail and wrote some stuff, but found the lack of file access to load files wanting. It supported blocks, which even at that time it felt like a huge step back to ancient times. So no blocks in Forth500, but blocks can be added from a source file if necessary.
There will be more to come soon, hopefully in a couple of days when I'm back to work on this project.
- Rob