Some may wonder why I wrote VOCABULARY the way I did. Since Fleet Forth has a tree-like vocabulary structure, wouldn't it have been easier to implement vocabulary like FIG-Forth with a false name in it where (FIND) , the find primitive, would race through a vocabulary, follow the false name's link field to the latest word in the parent vocabulary until the word was found or it ran out of parent vocabularies and the search failed? The reason has to do with the desired behavior. To that end, Fleet Forth has two find primitives. (FIND) searches a vocabulary and, if necessary, any parent vocabularies. VFIND only searches the given vocabulary. CREATE , Fleet Forths word used by all defining words, and FORGET use VFIND to only search the CURRENT vocabulary. It's a good idea to warn of a redefinition but with a tree structured vocabulary implemented like FIG-Forth, a warning about a redefinition will occur even if the word being redefined is in a parent vocabulary. One way to deal with this is to have a variable WARNING, like Blazin' Forth, which is used to allow or suppress warnings about redefinitions. I personally prefer to always be warned about redefinitions but, only when they occur in the CURRENT vocabulary, the one receiving new definitions.
I built a test kernel with the FIG-Forth style vocabulary structure just to see the size difference. Including WARNING and the test for it added 20 bytes, changing the definition of VOCABULARY added 6 bytes, and modifying FORGET so the CURRENT vocabulary isn't forgotten ( Fleet Forth's version only searches the CURRENT vocabulary for the word to be forgotten, so the CURRENT vocabulary is never found )added 10 more bytes for a total of 36 bytes. Eliminating VFIND and streamlining (FIND) saved 39 bytes. 3 bytes saved in the kernel. The overall system for this test build was larger. A word used by WORDS needed another 14 bytes to test for and avoid following the link to a parent vocabulary ( I feel that WORDS should *only* display the words in the CONTEXT vocabulary, not every vocabulary from it back to and including FORTH ). ORDER and VOCS would need a complete rewrite and even more code to work properly. So, not such a great savings after all.
How did having two find primitives only add 39 bytes? Simple, here is the source for (FIND) and VFIND:
Code:
SCR# 13
// (FIND)
HEX
CODE (FIND) ( ADR VOC -- ADR2 F )
DEY, N 1- STY, SEC,
2 ,X LDA, 2 # SBC, N 2+ STA,
3 ,X LDA, 0 # SBC, N 3 + STA,
0 ,X LDY, 1 ,X LDA,
INX, INX, XSAVE STX,
BEGIN,
N STY, N 4 + STY,
N 1+ STA, N 5 + STA,
BEGIN,
0 # LDY,
N )Y LDA, TAX, INY,
N )Y LDA,
SCR# 14
// (FIND) JAB
0= NOT WHILE,
N 1+ STA, N STX, INY,
N )Y LDA, N 2+ )Y EOR,
3F # AND,
CS-DUP 0= UNTIL, // CONTINUE
BEGIN,
INY,
N )Y LDA, N 2+ )Y EOR,
0= NOT UNTIL,
7F # AND,
0= UNTIL,
XSAVE LDX,
SCR# 15
// (FIND) JAB
SEC,
TYA, N ADC, 0 ,X STA,
0 # LDA,
N 1+ ADC, 1 ,X STA,
2 # LDY,
N )Y LDA, 40 # AND,
0= IF,
LABEL PUSH.TRUE // -1
0FF # LDA, PHA,
PUSH JMP,
THEN,
LABEL PUSH.ONE // 1
1 # LDA, HERE TIS APUSH
PHA, 0 # LDA, PUSH JMP,
THEN,
SCR# 16
// (FIND) JAB
3 # LDY,
N 4 + )Y LDA,
N 1- AND,
0= NOT WHILE,
TAX, DEY,
N 4 + )Y LDA,
TAY, TXA,
0= UNTIL, // ALWAYS BRANCH
THEN,
XSAVE LDX,
LABEL PUSH.FALSE // 0
0 # LDA, PHA,
PUSH JMP,
END-CODE
SCR# 82
// VFIND FIND NAME '
HEX
CODE VFIND ( ADR VOC -- ADR2 F )
-2 ALLOT
' (FIND) @ 1+ ,
END-CODE
(FIND) first decrements the Y-register, it is zero upon entering a code word, to $FF and stores it in N-1 ( one of the scratch pad locations in zero page ). VFIND is a word without a body of its own. It's code field points one byte into the body of (FIND), just past the DEY instruction therefore, a zero is stored in N-1.
(FIND)'s outer BEGIN loop handles the transition from a vocabulary to its parent, if it has one. The BEGIN loop inside that handles the search in a vocabulary. Notice that if the value stored in N-1 is $FF
and the high byte of the address for the parent vocabulary is not zero, then the parent vocabulary is searched.
Since VFIND stores a zero in N-1, when the code reaches the
N 1- AND, on screen 16 the result is always zero so the parent vocabulary, if there is one, is never searched by VFIND, only (FIND). (FIND) and VFIND take the address of the name to be searched for and the address of the VOCABULARY
NOT the address of the latest word in the VOCABULARY. Here is the code for FIND:
Code:
: FIND ( ADR -- ADR2 F )
CONTEXT @ (FIND)
?DUP ?EXIT
CURRENT @ VFIND ;
By the way, notice that when testing to see if the link field, or the parent vocabulary field, is a valid address or zero, only the high byte is tested.
cheers,
Jim