Fleet Forth has been revised since I last posted how it handles the text stream. This is how the current version of Fleet Forth handles the text stream.
First, Fleet Forth's QUIT loop.
Fleet Forth has a word WHERE which shows where an error occurred. The first thing WHERE does is store the address of EXIT in its first cell. This prevents recursive error handling if WHERE should cause an error; however, this means QUIT must restore the first cell of WHERE .
Code: Select all
: QUIT ( -- )
[COMPILE] [
BEGIN
RP! // CLEAR RETURN STACK
['] LIT (IS) WHERE // RESTORE WHERE
CR QUERY INTERPRET
STATE @ 0=
CS-DUP UNTIL // CONTINUE IF COMPILING
." OK"
AGAIN -; // LOOP FOREVER
QUERY is the Forth-83 Standard word to read text from the input device (usually the keyboard) and store it in the text input buffer.
QUERY is the only word which places text in the text input buffer. Fleet Forth's QUERY has support for any word which may point TIB to an address other than the text input buffer by pointing TIB to the actual text input buffer before using TIB .
Code: Select all
: QUERY ( -- )
'TIB IS TIB TIB #80 EXPECT
SPAN C@ #TIB !
0 0
LABEL BLK.2!
BLK 2! ;
'TIB is a metacompiler macro which evaluates to $2A7, the address for the text input buffer.
BLK is actually a double variable. The first cell is normally accessed by words using BLK . >IN is a constant which points to the second cell of BLK . The ability to fetch the values of both BLK and >IN with a single 2@ and store both values with a single 2! results in a smaller Fleet Forth kernel.
QUERY uses EXPECT to read text into the text input buffer ( TIB ) and stores the value of SPAN in #TIB . EXPECT does not store a terminating zero byte.
QUERY then stores a zero in both BLK and >IN .
LOAD and LINELOAD .
Code: Select all
: LINELOAD ( LINE# BLK# -- )
DUP 0=
ABORT" CAN'T LOAD 0"
BLK 2@ 2>R
BLK ! C/L * >IN !
INTERPRET 2R>
BRANCH [ BLK.2! , ] -;
: LOAD ( U -- )
0 SWAP BRANCH
[ ' LINELOAD >BODY , ] -;
LINELOAD aborts if the block to load is zero. For any other block number, LINELOAD saves the contents of BLK and >IN to the return stack. It then stores the block number on the data stack in BLK then multiplies the line number by C/L and stores that value in >IN . LINELOAD uses INTERPRET to interpret the contents of the block before restoring the previous contents of BLK and >IN .
LOAD places zero on the stack, swaps this number with the block number and branches to LINELOAD .
EVALUATE is a word in ANSI Forth which will interpret the contents of a string. It takes an address and the length of a string. I have found it to be a useful addition to the Fleet Forth system.
Code: Select all
: EVALUATE ( ADR CNT -- )
TIB #TIB @ 2>R
LIT [ >MARK ] ENTER
0 0 LIT
[ ' LINELOAD >BODY DUP TRUE
" LOAD 0" COUNT MATCH ?HUH
+ , ]
ENTER
2R>
[ >RESOLVE ]
#TIB ! (IS) TIB ;
This source is for a very compact version of EVALUATE ; however, it is relatively straight forward. Save the contents of TIB and #TIB to the return stack. Point TIB at the string to be evaluated and save the string length in #TIB . Place two zeroes on the data stack and use ENTER to perform the equivalent of a GOSUB into the body of LINELOAD just past LINELOAD's inline error string "CAN'T LOAD 0." LINELOAD will save and restore the contents of BLK and >IN . Since EVALUATE supplies a block number of zero, INTERPRET will actually interpret the contents of the string pointed to by TIB and #TIB .
Finally, restore the original contents of TIB and #TIB .
INTERPRET interprets (and/or compiles) the text from the text stream. It exits when the text stream is exhausted. INTERPRET uses NAME which uses WORD to process strings from the text stream one blank delimited string at a time.
WORD uses 'STREAM to get the address and length of the remaining text stream.
Code: Select all
: 'STREAM ( -- ADR N )
BLK @ ?DUP
IF
BLOCK B/BUF
ELSE
TIB #TIB @
THEN
>IN @
OVER UMIN /STRING ;
'STREAM places the address of a block buffer and $400 on the data stack if a non zero block number is stored in BLK or it will place the address of the text input buffer and its length on the data stack.
'STREAM then duplicates the length of the text stream. The unsigned minimum of the length and the value of >IN is used with /STRING to return the address and length of the portion of the text stream which has not yet been processed. If the value of >IN is equal to or greater than the length of the text stream, the text stream has been exhausted and 'STREAM will return an address just past the text stream and a length of zero.
WORD uses 'STREAM so it has access to the address of the unprocessed portion of the text stream as well as the remaining length. WORD stores a string at HERE as a count byte followed by the text of the string and a trailing blank. The trailing blank is needed by NUMBER? .
Fleet Forth has no word called ENCLOSE . Fleet Forth's WORD uses two primitives named SKIP and SCAN . Both words take the address of a string, its length and a character (the delimiter). SKIP returns the address of the first character which is not the delimiter and the remaining length of the string. SCAN returns the address of the first delimiter encountered and the remaining length of the string. If SKIP only encounters delimiter characters or SCAN can not find a delimiter, the address just past the string and a length of zero is returned. Neither word alters the string. Both SKIP and SCAN can handle strings of arbitrary size.
The source for INTERPRET .
Code: Select all
: INTERPRET
BEGIN
PAUSE NAME C@ 0EXIT
HERE I/C
AGAIN -;
PAUSE is the task switcher, which is set to NOOP when not multitasking. PAUSE is included in INTERPRET's loop to allow multitasking while the text stream is being interpreted.
There is no special word with a blank name to stop interpretation. If the text stream is exhausted, WORD returns a string with a length of zero. INTERPRET checks the length of the string and exits if it is zero.
If the length of the string is not zero, INTERPRET places the address of HERE on the stack for I/C , the word which interprets or compiles one word.
This is why Fleet Forth's text input buffer, block buffers and evaluated strings do not need a trailing zero.